diff --git a/.github/DISCUSSION_TEMPLATE/ideas.yml b/.github/DISCUSSION_TEMPLATE/ideas.yml new file mode 100644 index 000000000000..e004231f38ac --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/ideas.yml @@ -0,0 +1,54 @@ +title: "[Suggestion] " +body: + - type: markdown + attributes: + value: | + ## Before We Start + + Please provide reasonably detailed responses to the question below to help the Core Team and maintainers + to understand how you run RabbitMQ and why you'd like to see the suggested changes. + - type: markdown + attributes: + value: | + ## Relevant Details + - type: dropdown + id: rabbitmq_series + attributes: + label: RabbitMQ series + options: + - 4.0.x + - 4.1.x + validations: + required: true + - type: input + id: os + attributes: + label: Operating system (distribution) used + description: What OS or distribution do you run RabbitMQ on? + validations: + required: true + - type: dropdown + id: deployment_type + attributes: + label: How is RabbitMQ deployed? + options: + - Community Docker image + - Debian package + - RPM package + - Generic binary package + - Kubernetes Operator(s) from Team RabbitMQ + - Bitnami Helm chart + - Chocolatey package + - Windows installer + - Windows binary package + - RabbitMQ-as-a-Service from a public cloud provider + - Other + validations: + required: true + - type: textarea + id: details + attributes: + label: What would you like to suggest for a future version of RabbitMQ? + description: Please take the time to explain how you use RabbitMQ and why this change is important + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/other.yml b/.github/DISCUSSION_TEMPLATE/other.yml new file mode 100644 index 000000000000..204e307a8cff --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/other.yml @@ -0,0 +1,54 @@ +title: "[Other] " +body: + - type: markdown + attributes: + value: | + ## Before We Start + + This category exists for free form questions where deployment details are less relevant, e.g. application and topology + design kind of questions. Please provide a reasonably detailed description of what you are trying to do with RabbitMQ. + - type: checkboxes + attributes: + label: Community Support Policy + description: + options: + - label: I have read [RabbitMQ's Community Support Policy](https://github.com/rabbitmq/rabbitmq-server/blob/main/COMMUNITY_SUPPORT.md) + required: true + - type: markdown + attributes: + value: | + ## Relevant Details + - type: dropdown + id: rabbitmq_version + attributes: + label: RabbitMQ version used + options: + - 4.0.3 + - 3.13.7 or older + validations: + required: true + - type: dropdown + id: deployment_type + attributes: + label: How is RabbitMQ deployed? + options: + - Community Docker image + - Debian package + - RPM package + - Generic binary package + - Kubernetes Operator(s) from Team RabbitMQ + - Bitnami Helm chart + - Chocolatey package + - Windows installer + - Windows binary package + - RabbitMQ-as-a-Service from a public cloud provider + - Other + validations: + required: true + - type: textarea + id: details + attributes: + label: Steps to reproduce the behavior in question + description: What specific steps need to be performed in order to reproduce this behavior? Why? + validations: + required: true diff --git a/.github/DISCUSSION_TEMPLATE/questions.yml b/.github/DISCUSSION_TEMPLATE/questions.yml new file mode 100644 index 000000000000..2afa9bd49df5 --- /dev/null +++ b/.github/DISCUSSION_TEMPLATE/questions.yml @@ -0,0 +1,209 @@ +title: "[Questions] " +body: + - type: markdown + attributes: + value: | + ## Before We Start + + Please provide reasonably detailed responses to the question below to help others help you. + + If you omit relevant information, those trying to reproduce what you are about to report will have to guess. + Guessing is a very time consuming, and therefore expensive, approach to troubleshooting distributed messaging infrastructure. + - type: checkboxes + attributes: + label: Community Support Policy + description: + options: + - label: I have read [RabbitMQ's Community Support Policy](https://github.com/rabbitmq/rabbitmq-server/blob/main/COMMUNITY_SUPPORT.md) + required: true + - label: I run RabbitMQ 4.x, the only series currently covered by [community support](https://www.rabbitmq.com/release-information) + required: true + - label: I promise to provide all relevant information (versions, logs from all nodes, rabbitmq-diagnostics output, detailed reproduction steps) + required: true + - type: markdown + attributes: + value: | + ## Relevant Details + - type: dropdown + id: rabbitmq_version + attributes: + label: RabbitMQ version used + options: + - 4.0.4 + - 4.0.3 + validations: + required: true + - type: dropdown + id: erlang_version + attributes: + label: Erlang version used + options: + - 26.2.x + - 26.1.x + - 26.0.x + validations: + required: true + - type: input + id: os + attributes: + label: Operating system (distribution) used + description: What OS or distribution do you run RabbitMQ on? + validations: + required: true + - type: dropdown + id: deployment_type + attributes: + label: How is RabbitMQ deployed? + options: + - Community Docker image + - Debian package + - RPM package + - Generic binary package + - Kubernetes Operator(s) from Team RabbitMQ + - Bitnami Helm chart + - Chocolatey package + - Windows installer + - Windows binary package + - RabbitMQ-as-a-Service from a public cloud provider + - Other + validations: + required: true + - type: textarea + id: diagnostics_status + attributes: + label: rabbitmq-diagnostics status output + value: | + See https://www.rabbitmq.com/docs/cli to learn how to use rabbitmq-diagnostics +
+ + ``` + # PASTE OUTPUT HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: true + - type: textarea + id: rabbitmq_logs + attributes: + label: Logs from node 1 (with sensitive values edited out) + description: Relevant RabbitMQ logs with sensitive values edited out + value: | + See https://www.rabbitmq.com/docs/logging to learn how to collect logs +
+ + ``` + # PASTE LOG HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: true + - type: textarea + id: logs_node_2 + attributes: + label: Logs from node 2 (if applicable, with sensitive values edited out) + description: Relevant RabbitMQ logs with sensitive values edited out + value: | + See https://www.rabbitmq.com/docs/logging to learn how to collect logs +
+ + ``` + # PASTE LOG HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: false + - type: textarea + id: logs_node_3 + attributes: + label: Logs from node 3 (if applicable, with sensitive values edited out) + description: Relevant RabbitMQ logs with sensitive values edited out + value: | + See https://www.rabbitmq.com/docs/logging to learn how to collect logs +
+ + ``` + # PASTE LOG HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: false + - type: textarea + id: rabbitmq_conf + attributes: + label: rabbitmq.conf + description: rabbitmq.conf contents + value: | + See https://www.rabbitmq.com/docs/configure#config-location to learn how to find rabbitmq.conf file location +
+ + ``` + # PASTE rabbitmq.conf HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: true + - type: textarea + id: deployment_steps + attributes: + label: Steps to deploy RabbitMQ cluster + description: How would you explain how you deploy RabbitMQ to a new colleague? + validations: + required: true + - type: textarea + id: reproduction_steps + attributes: + label: Steps to reproduce the behavior in question + description: What specific steps need to be performed in order to reproduce this behavior? Why? + validations: + required: true + - type: textarea + id: advanced_config + attributes: + label: advanced.config + description: advanced.config contents (if applicable) + value: | + See https://www.rabbitmq.com/docs/configure#config-location to learn how to find advanced.config file location +
+ + ``` + # PASTE advanced.config HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: false + - type: textarea + id: app_code + attributes: + label: Application code + description: Relevant messaging-related parts of application code + value: | +
+ + ```python + # PASTE CODE HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: false + - type: textarea + id: k8s_deployment + attributes: + label: Kubernetes deployment file + description: Kubernetes deployment YAML that demonstrates how RabbitMQ is deployed (if applicable) + value: | +
+ + ```yaml + # Relevant parts of K8S deployment that demonstrate how RabbitMQ is deployed + # PASTE YAML HERE, BETWEEN BACKTICKS + ``` +
+ validations: + required: false + - type: textarea + id: question + attributes: + label: What problem are you trying to solve? + description: and why? + validations: + required: true \ No newline at end of file diff --git a/.github/workflows/check-build-system-equivalence.yaml b/.github/workflows/check-build-system-equivalence.yaml index d79d8297340f..2458c7fee903 100644 --- a/.github/workflows/check-build-system-equivalence.yaml +++ b/.github/workflows/check-build-system-equivalence.yaml @@ -23,7 +23,11 @@ on: elixir_version: description: 'Elixir version to build with' required: true +<<<<<<< HEAD default: "1.17" +======= + default: "1.15" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) project_version: description: 'PROJECT_VERSION used for make' required: true diff --git a/.github/workflows/gazelle-scheduled.yaml b/.github/workflows/gazelle-scheduled.yaml index b6bb0648434c..f80a97f6af12 100644 --- a/.github/workflows/gazelle-scheduled.yaml +++ b/.github/workflows/gazelle-scheduled.yaml @@ -12,9 +12,15 @@ jobs: matrix: target_branch: - main +<<<<<<< HEAD - v3.12.x - v3.11.x - v3.10.x +======= + - v4.0.x + - v3.13.x + - v3.12.x +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) timeout-minutes: 10 steps: - name: CHECKOUT REPOSITORY diff --git a/.github/workflows/oci-arm64-make.yaml b/.github/workflows/oci-arm64-make.yaml index 0e4dbf212645..26331d8c1bd3 100644 --- a/.github/workflows/oci-arm64-make.yaml +++ b/.github/workflows/oci-arm64-make.yaml @@ -47,7 +47,11 @@ jobs: - name: make package-generic-unix if: steps.authorized.outputs.authorized == 'true' run: | +<<<<<<< HEAD make package-generic-unix PROJECT_VERSION=4.0.0 +======= + make package-generic-unix PROJECT_VERSION=4.1.0-alpha.1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload package-generic-unix if: steps.authorized.outputs.authorized == 'true' uses: actions/upload-artifact@v4.3.1 diff --git a/.github/workflows/oci-make.yaml b/.github/workflows/oci-make.yaml index 6c1a2ba61583..2d335deb6743 100644 --- a/.github/workflows/oci-make.yaml +++ b/.github/workflows/oci-make.yaml @@ -40,7 +40,11 @@ jobs: - name: make package-generic-unix if: steps.authorized.outputs.authorized == 'true' run: | +<<<<<<< HEAD make package-generic-unix PROJECT_VERSION=4.0.0 +======= + make package-generic-unix PROJECT_VERSION=4.1.0-alpha.1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload package-generic-unix if: steps.authorized.outputs.authorized == 'true' uses: actions/upload-artifact@v4.3.1 @@ -65,7 +69,11 @@ jobs: - name: Prepare run: | platform=${{ matrix.platform }} +<<<<<<< HEAD echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV +======= + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Checkout uses: actions/checkout@v4 - name: Download package-generic-unix @@ -116,7 +124,11 @@ jobs: run: | mkdir -p /tmp/digests digest="${{ steps.build.outputs.digest }}" +<<<<<<< HEAD touch "/tmp/digests/${digest#sha256:}" +======= + touch "/tmp/digests/${digest#sha256:}" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload digest uses: actions/upload-artifact@v4 with: @@ -157,10 +169,17 @@ jobs: working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ +<<<<<<< HEAD $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} +======= + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) summary-oci: needs: diff --git a/.github/workflows/peer-discovery-aws.yaml b/.github/workflows/peer-discovery-aws.yaml new file mode 100644 index 000000000000..2e94da990fcd --- /dev/null +++ b/.github/workflows/peer-discovery-aws.yaml @@ -0,0 +1,105 @@ +name: Peer Discovery AWS Integration Test +on: + push: + paths: + - "deps/rabbitmq_peer_discovery_aws/**" + - "deps/rabbitmq_peer_discovery_common/**" + - "deps/rabbit/src/rabbit_peer_discovery.erl" + schedule: + - cron: "4 0 * * MON" + workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true +jobs: + peer-discovery-aws-integration-test: + name: Integration Test + runs-on: ubuntu-22.04 + timeout-minutes: 45 + steps: + - name: CHECK IF IMAGE WILL PUSH + id: authorized + run: | + if [ -n "${{ secrets.DOCKERHUB_PASSWORD }}" ]; then + echo "authorized=true" | tee -a $GITHUB_OUTPUT + else + echo "authorized=false" | tee -a $GITHUB_OUTPUT + fi + - name: CHECKOUT REPOSITORY + if: steps.authorized.outputs.authorized == 'true' + uses: actions/checkout@v4 + - uses: docker/metadata-action@v5 + if: steps.authorized.outputs.authorized == 'true' + id: metadata + with: + images: pivotalrabbitmq/rabbitmq + tags: | + type=sha,format=long + - uses: int128/wait-for-docker-image-action@v1 + if: steps.authorized.outputs.authorized == 'true' + with: + tags: ${{ steps.metadata.outputs.tags }} + timeout-seconds: 3600 + polling-seconds: 60 + - name: COMPUTE REPO CACHE KEY + if: steps.authorized.outputs.authorized == 'true' + id: repo-cache-key + run: | + echo "value=bazel-repo-cache-${{ hashFiles('MODULE.bazel') }}" | tee -a $GITHUB_OUTPUT + - name: LOAD REPO CACHE + if: steps.authorized.outputs.authorized == 'true' + uses: actions/cache/restore@v4 + with: + key: ${{ steps.repo-cache-key.outputs.value }} + path: /home/runner/repo-cache/ + - name: CONFIGURE OTP & ELIXIR + if: steps.authorized.outputs.authorized == 'true' + uses: erlef/setup-beam@v1.17 + with: + otp-version: 26 + elixir-version: 1.15 + - name: SETUP ecs-cli + if: steps.authorized.outputs.authorized == 'true' + env: + ECS_CLI_VERSION: 1.21.0 + run: | + curl -Lo /usr/local/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-linux-amd64-v${ECS_CLI_VERSION} && \ + chmod +x /usr/local/bin/ecs-cli && \ + ecs-cli --version + - name: AUTHENTICATE TO GOOGLE CLOUD + if: steps.authorized.outputs.authorized == 'true' + uses: google-github-actions/auth@v2.1.7 + with: + credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} + - name: CONFIGURE BAZEL + if: steps.authorized.outputs.authorized == 'true' + run: | + if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then + cat << EOF >> user.bazelrc + build --remote_cache=https://storage.googleapis.com/${{ secrets.REMOTE_CACHE_BUCKET_NAME }} + build --google_default_credentials + + build --experimental_guard_against_concurrent_changes + EOF + fi + cat << EOF >> user.bazelrc + build --repository_cache=/home/runner/repo-cache/ + build --color=yes + EOF + + bazelisk info release + #! - name: Setup tmate session + #! uses: mxschmitt/action-tmate@v3 + - name: RUN INTEGRATION TESTS + if: steps.authorized.outputs.authorized == 'true' + run: | + branch_or_tag="${GITHUB_REF##*/}" + bazelisk test //deps/rabbitmq_peer_discovery_aws:integration_SUITE \ + --test_tag_filters=aws \ + --build_tests_only \ + --test_env AWS_ACCESS_KEY_ID=${{ secrets.CONCOURSE_AWS_ACCESS_KEY_ID }} \ + --test_env AWS_SECRET_ACCESS_KEY=${{ secrets.CONCOURSE_AWS_SECRET_ACCESS_KEY }} \ + --test_env RABBITMQ_IMAGE="pivotalrabbitmq/rabbitmq:sha-${{ github.sha }}" \ + --test_env AWS_ECS_CLUSTER_NAME="rabbitmq-peer-discovery-aws-actions-${branch_or_tag//[._]/-}" \ + --test_output=streamed \ + --verbose_failures diff --git a/.github/workflows/release-4.1.x-alphas.yaml b/.github/workflows/release-4.1.x-alphas.yaml new file mode 100644 index 000000000000..2c1f44ed2ed4 --- /dev/null +++ b/.github/workflows/release-4.1.x-alphas.yaml @@ -0,0 +1,36 @@ +name: "Trigger a 4.1.x alpha release build" +on: + workflow_dispatch: + push: + branches: + # 4.1.x + - "main" + paths: + - "deps/*/src/**" + - 'deps/rabbitmq_management/priv/**' + - ".github/workflows/**" + - "rabbitmq-components.mk" +env: + DEV_WORKFLOW_REPOSITORY: "rabbitmq/server-packages" +jobs: + trigger_alpha_build: + runs-on: ubuntu-latest + steps: + - name: Compute prerelease identifier from commit SHA + run: echo "PRERELEASE_IDENTIFIER=`echo ${{ github.sha }} | cut -c1-8`" >> $GITHUB_ENV + - name: Trigger a 4.0.x alpha build in ${{ env.DEV_WORKFLOW_REPOSITORY }} + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.RABBITMQCI_BOT_TOKEN }} + repository: ${{ env.DEV_WORKFLOW_REPOSITORY }} + event-type: "new_4.1.x_alpha" + client-payload: |- + { + "release_repository": "${{ env.DEV_WORKFLOW_REPOSITORY }}", + "release_description": "Commit: https://github.com/rabbitmq/rabbitmq-server/commit/${{ github.sha }}, pushed at: ${{ github.event.repository.pushed_at }}", + "prerelease": true, + "prerelease_kind": "alpha", + "prerelease_identifier": "${{ env.PRERELEASE_IDENTIFIER }}", + "release_title": "RabbitMQ ${{ vars.SERVER_41_NEXT_PATCH_VERSION }}-alpha.${{ env.PRERELEASE_IDENTIFIER }} (from ${{ github.event.repository.pushed_at }})", + "base_version": "${{ vars.SERVER_41_NEXT_PATCH_VERSION }}" + } diff --git a/.github/workflows/templates/test.template.yaml b/.github/workflows/templates/test.template.yaml index bbb361518df6..e8a59293feb5 100644 --- a/.github/workflows/templates/test.template.yaml +++ b/.github/workflows/templates/test.template.yaml @@ -23,7 +23,11 @@ on: push: branches: #! - main +<<<<<<< HEAD #! - v4.0.x +======= + - v4.0.x +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - v3.13.x - v3.12.x - v3.11.x diff --git a/.github/workflows/test-authnz.yaml b/.github/workflows/test-authnz.yaml index e652fee19879..ce317690499a 100644 --- a/.github/workflows/test-authnz.yaml +++ b/.github/workflows/test-authnz.yaml @@ -10,7 +10,11 @@ on: - 'deps/rabbitmq_auth_**' - 'deps/rabbitmq_management/src/**' - 'deps/rabbitmq_management/priv/**' +<<<<<<< HEAD - 'selenium/**' +======= + - 'deps/rabbitmq_management/selenium/**' +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - 'scripts/**' - .bazelrc - .bazelversion @@ -20,12 +24,19 @@ on: - .github/workflows/test-authnz.yaml pull_request: paths: +<<<<<<< HEAD - 'deps/rabbit/**' - 'deps/rabbitmq_auth_/**' - 'deps/rabbitmq_mqtt/**' - 'selenium/full-suite-authnz-messaging' - 'selenium/suites/authnz-messaging' - 'selenium/test/authnz-msg-protocols' +======= + - 'selenium/**' + - 'deps/rabbit/**' + - 'deps/rabbitmq_auth_/**' + - 'deps/rabbitmq_mqtt/**' +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - .github/workflows/test-authnz.yaml concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} @@ -64,6 +75,7 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} +<<<<<<< HEAD - name: Configure Bazel run: | if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then @@ -81,6 +93,12 @@ jobs: - name: Build & Load RabbitMQ OCI run: | bazelisk run packaging/docker-image:rabbitmq-amd64 +======= + - name: Build & Load RabbitMQ OCI + run: | + make package-generic-unix + make docker-image +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Configure Docker Network run: | @@ -93,9 +111,18 @@ jobs: - name: Run Suites run: | +<<<<<<< HEAD RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ ${SELENIUM_DIR}/run-suites.sh full-suite-authnz-messaging +======= + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ + ${SELENIUM_DIR}/run-suites.sh full-suite-authnz-messaging + mkdir -p /tmp/full-suite-authnz-messaging + mv /tmp/selenium/* /tmp/full-suite-authnz-messaging + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload Test Artifacts if: always() uses: actions/upload-artifact@v4.3.2 diff --git a/.github/workflows/test-make-target.yaml b/.github/workflows/test-make-target.yaml index 8b57eb5044b0..b6f5fc49dbf5 100644 --- a/.github/workflows/test-make-target.yaml +++ b/.github/workflows/test-make-target.yaml @@ -57,35 +57,66 @@ jobs: uses: dsaltares/fetch-gh-release-asset@master if: inputs.mixed_clusters with: +<<<<<<< HEAD regex: true file: "rabbitmq-server-generic-unix-[\\d.]*\\.tar.xz" +======= + version: 'tags/v4.0.3' + regex: true + file: "rabbitmq-server-generic-unix-\\d.+\\.tar\\.xz" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) target: ./ - name: MIXED CLUSTERS - SETUP SECONDARY_DIST if: inputs.mixed_clusters run: | +<<<<<<< HEAD gpg --import rabbitmq-release-signing-key.asc gpg --verify rabbitmq-server-generic-unix-*.asc rabbitmq-server-generic-unix-*.tar.xz tar xf rabbitmq-server-generic-unix-*.tar.xz echo "SECONDARY_DIST=${GITHUB_WORKSPACE}/rabbitmq_server-`echo -n ${{ steps.fetch_secondary_dist.outputs.version }} | sed s/v//`" >> $GITHUB_ENV +======= + ls -l rabbitmq-server-generic-unix-*.tar.xz* + + archive_name=$(echo rabbitmq-server-generic-unix-*.tar.xz) + archive_version=$(echo $archive_name | sed -E -e 's/^rabbitmq-server-generic-unix-//' -e 's/\.tar\.xz$//') + + gpg --import rabbitmq-release-signing-key.asc + gpg --verify $archive_name.asc $archive_name + tar xf $archive_name + + echo "SECONDARY_DIST=${GITHUB_WORKSPACE}/rabbitmq_server-$archive_version" >> $GITHUB_ENV +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: SETUP DOTNET (rabbit) uses: actions/setup-dotnet@v4 if: inputs.plugin == 'rabbit' with: +<<<<<<< HEAD dotnet-version: '3.1.x' +======= + dotnet-version: '8.0' +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: SETUP SLAPD (rabbitmq_auth_backend_ldap) if: inputs.plugin == 'rabbitmq_auth_backend_ldap' run: | sudo apt-get update && \ sudo apt-get install -y \ +<<<<<<< HEAD apparmor-utils \ ldap-utils \ slapd sudo aa-complain `which slapd` +======= + ldap-utils \ + slapd + + sudo systemctl is-active --quiet apparmor.service && sudo systemctl stop apparmor.service + sudo systemctl disable apparmor.service +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: RUN TESTS if: inputs.plugin != 'rabbitmq_cli' diff --git a/.github/workflows/test-make.yaml b/.github/workflows/test-make.yaml index 60d61dad9fc4..ab82f89bd053 100644 --- a/.github/workflows/test-make.yaml +++ b/.github/workflows/test-make.yaml @@ -3,7 +3,10 @@ on: push: branches: - main +<<<<<<< HEAD - v4.0.x +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) paths: - deps/** - scripts/** diff --git a/.github/workflows/test-management-ui-for-pr.yaml b/.github/workflows/test-management-ui-for-pr.yaml index 76c0181a14f2..9409f4466ec3 100644 --- a/.github/workflows/test-management-ui-for-pr.yaml +++ b/.github/workflows/test-management-ui-for-pr.yaml @@ -42,6 +42,7 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} +<<<<<<< HEAD - name: Configure Bazel run: | if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then @@ -59,6 +60,12 @@ jobs: - name: Build & Load RabbitMQ OCI run: | bazelisk run packaging/docker-image:rabbitmq-amd64 +======= + - name: Build & Load RabbitMQ OCI + run: | + make package-generic-unix + make docker-image +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Configure Docker Network run: | @@ -71,6 +78,7 @@ jobs: - name: Run short ui suites on a standalone rabbitmq server run: | +<<<<<<< HEAD RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ ADDON_PROFILES=cluster ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui mkdir -p /tmp/short-suite @@ -79,6 +87,13 @@ jobs: mv ${SELENIUM_DIR}/logs/* /tmp/short-suite/logs mkdir -p /tmp/short-suite/screens mv ${SELENIUM_DIR}/screens/* /tmp/short-suite/screens +======= + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ + ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + mkdir -p /tmp/short-suite + mv /tmp/selenium/* /tmp/short-suite +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload Test Artifacts if: always() diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml index 53902913d8c0..1099f672924b 100644 --- a/.github/workflows/test-management-ui.yaml +++ b/.github/workflows/test-management-ui.yaml @@ -57,6 +57,7 @@ jobs: with: credentials_json: ${{ secrets.REMOTE_CACHE_CREDENTIALS_JSON }} +<<<<<<< HEAD - name: Configure Bazel run: | if [ -n "${{ secrets.REMOTE_CACHE_BUCKET_NAME }}" ]; then @@ -74,6 +75,12 @@ jobs: - name: Build & Load RabbitMQ OCI run: | bazelisk run packaging/docker-image:rabbitmq-amd64 +======= + - name: Build & Load RabbitMQ OCI + run: | + make package-generic-unix + make docker-image +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Configure Docker Network run: | @@ -86,6 +93,7 @@ jobs: - name: Run short ui suite on a 3-node rabbitmq cluster run: | +<<<<<<< HEAD RABBITMQ_DOCKER_IMAGE=bazel/packaging/docker-image:rabbitmq-amd64 \ ADDON_PROFILES=cluster ${SELENIUM_DIR}/run-suites.sh full-suite-management-ui mkdir -p /tmp/full-suite @@ -95,12 +103,24 @@ jobs: mkdir -p /tmp/full-suite/screens mv ${SELENIUM_DIR}/screens/* /tmp/full-suite/screens +======= + IMAGE_TAG=$(find PACKAGES/rabbitmq-server-generic-unix-*.tar.xz | awk -F 'PACKAGES/rabbitmq-server-generic-unix-|.tar.xz' '{print $2}') + RABBITMQ_DOCKER_IMAGE=pivotalrabbitmq/rabbitmq:$IMAGE_TAG \ + ${SELENIUM_DIR}/run-suites.sh short-suite-management-ui + mkdir -p /tmp/short-suite + mv /tmp/selenium/* /tmp/short-suite + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - name: Upload Test Artifacts if: always() uses: actions/upload-artifact@v4.3.2 with: name: test-artifacts-${{ matrix.browser }}-${{ matrix.erlang_version }} path: | +<<<<<<< HEAD +======= + /tmp/full-suite +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) /tmp/short-suite summary-selenium: diff --git a/.github/workflows/test-mixed-versions.yaml b/.github/workflows/test-mixed-versions.yaml index bf2ed9ae2fdb..49726be6b373 100644 --- a/.github/workflows/test-mixed-versions.yaml +++ b/.github/workflows/test-mixed-versions.yaml @@ -2,6 +2,10 @@ name: Test Mixed Version Clusters on: push: branches: +<<<<<<< HEAD +======= + - v4.0.x +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - v3.13.x - bump-otp-* - bump-elixir-* @@ -467,6 +471,27 @@ jobs: repo_cache_key: ${{ needs.check-workflow.outputs.repo_cache_key }} plugin: rabbitmq_auth_backend_http secrets: inherit +<<<<<<< HEAD +======= + test-rabbitmq_auth_backend_ldap-mixed: + needs: + - check-workflow + - test-rabbit-0-mixed + - test-rabbit-1-mixed + - test-rabbit-2-mixed + - test-rabbit-3-mixed + - test-rabbit-4-mixed + - test-rabbit-5-mixed + - test-rabbit-6-mixed + - test-rabbit-7-mixed + - test-rabbit-8-mixed + - test-rabbit-9-mixed + uses: ./.github/workflows/test-plugin-mixed.yaml + with: + repo_cache_key: ${{ needs.check-workflow.outputs.repo_cache_key }} + plugin: rabbitmq_auth_backend_ldap + secrets: inherit +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test-rabbitmq_auth_backend_oauth2-mixed: needs: - check-workflow @@ -1130,6 +1155,10 @@ jobs: - test-rabbitmq_amqp1_0-mixed - test-rabbitmq_auth_backend_cache-mixed - test-rabbitmq_auth_backend_http-mixed +<<<<<<< HEAD +======= + - test-rabbitmq_auth_backend_ldap-mixed +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - test-rabbitmq_auth_backend_oauth2-mixed - test-rabbitmq_auth_mechanism_ssl-mixed - test-rabbitmq_aws-mixed diff --git a/.github/workflows/test-plugin-mixed.yaml b/.github/workflows/test-plugin-mixed.yaml index 416db6f0745f..de5b9ca2f084 100644 --- a/.github/workflows/test-plugin-mixed.yaml +++ b/.github/workflows/test-plugin-mixed.yaml @@ -29,10 +29,14 @@ jobs: - 26 metadata_store: - mnesia +<<<<<<< HEAD # Khepri is currently skipped because Khepri is an unstable feature: we don't guarantee upgrability. # Mixed-version tests currently fail with Khepri because of a new machine version introduced in # Khepri v0.14.0. # - khepri +======= + - khepri +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) include: - erlang_version: 26 elixir_version: 1.17 diff --git a/.github/workflows/test-plugin.yaml b/.github/workflows/test-plugin.yaml index 3c163ba059a3..8f5de886a151 100644 --- a/.github/workflows/test-plugin.yaml +++ b/.github/workflows/test-plugin.yaml @@ -101,6 +101,10 @@ jobs: sudo systemctl is-active --quiet apparmor.service && sudo systemctl stop apparmor.service sudo systemctl disable apparmor.service +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cat << EOF >> user.bazelrc build --strategy=TestRunner=local EOF diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 6c3c003670ef..89b5b80133f0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -2,6 +2,10 @@ name: Test on: push: branches: +<<<<<<< HEAD +======= + - v4.0.x +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - v3.13.x - v3.12.x - v3.11.x @@ -408,6 +412,27 @@ jobs: repo_cache_key: ${{ needs.check-workflow.outputs.repo_cache_key }} plugin: rabbitmq_auth_backend_http secrets: inherit +<<<<<<< HEAD +======= + test-rabbitmq_auth_backend_ldap: + needs: + - check-workflow + - test-rabbit-0 + - test-rabbit-1 + - test-rabbit-2 + - test-rabbit-3 + - test-rabbit-4 + - test-rabbit-5 + - test-rabbit-6 + - test-rabbit-7 + - test-rabbit-8 + - test-rabbit-9 + uses: ./.github/workflows/test-plugin.yaml + with: + repo_cache_key: ${{ needs.check-workflow.outputs.repo_cache_key }} + plugin: rabbitmq_auth_backend_ldap + secrets: inherit +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test-rabbitmq_auth_backend_oauth2: needs: - check-workflow @@ -1071,6 +1096,10 @@ jobs: - test-rabbitmq_amqp1_0 - test-rabbitmq_auth_backend_cache - test-rabbitmq_auth_backend_http +<<<<<<< HEAD +======= + - test-rabbitmq_auth_backend_ldap +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - test-rabbitmq_auth_backend_oauth2 - test-rabbitmq_auth_mechanism_ssl - test-rabbitmq_aws diff --git a/COMMUNITY_SUPPORT.md b/COMMUNITY_SUPPORT.md index 492a057bfcd5..84c11e0f4a76 100644 --- a/COMMUNITY_SUPPORT.md +++ b/COMMUNITY_SUPPORT.md @@ -6,7 +6,17 @@ This document explains who is eligible for community support for open source Rab ### What is Community Support? Community support is defined as all questions, root cause analysis requests, issue reports, and other interactions the RabbitMQ core team has with open source RabbitMQ users on GitHub +<<<<<<< HEAD and our community forums. +======= +and our community forums. + +### How is Community Support Related to Patch Releases? + +Being covered by community support for a release series also means that patch releases, general and security-related ones, +are produced regularly and are available publicly. Patch releases, even if produced, **will not be made available to non-paying users** for series out of community support, with potential +exception for very high severity CVEs. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ### What is Broadcom's Obligation to Reply to Messages or Issues Reported? diff --git a/MODULE.bazel b/MODULE.bazel index 6a0a57e07196..e5c222e0c443 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -284,8 +284,13 @@ erlang_package.hex_package( erlang_package.hex_package( name = "redbug", build_file = "@rabbitmq-server//bazel:BUILD.redbug", +<<<<<<< HEAD sha256 = "55d6d59697481ca4cc5ad54749aa6d78299aa8a8096027e7ae1f59db9dc94c78", version = "2.1.0", +======= + sha256 = "3624feb7a4b78fd9ae0e66cc3158fe7422770ad6987a1ebf8df4d3303b1c4b0c", + version = "2.0.7", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) erlang_package.hex_package( @@ -428,7 +433,11 @@ secondary_umbrella = use_extension( use_repo( secondary_umbrella, +<<<<<<< HEAD "rabbitmq-server-generic-unix-3.13", +======= + "rabbitmq-server-generic-unix-4.0", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) hex = use_extension( diff --git a/Makefile b/Makefile index 2e4fe88c9f7a..f346eebd594f 100644 --- a/Makefile +++ b/Makefile @@ -599,6 +599,10 @@ TIER1_PLUGINS := \ rabbitmq_amqp1_0 \ rabbitmq_auth_backend_cache \ rabbitmq_auth_backend_http \ +<<<<<<< HEAD +======= + rabbitmq_auth_backend_ldap \ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbitmq_auth_backend_oauth2 \ rabbitmq_auth_mechanism_ssl \ rabbitmq_aws \ diff --git a/bazel/BUILD.horus b/bazel/BUILD.horus index 0f7e2369acbb..32c77c7b0d8c 100644 --- a/bazel/BUILD.horus +++ b/bazel/BUILD.horus @@ -24,7 +24,11 @@ erlang_bytecode( srcs = [ "src/horus.erl", "src/horus_cover.erl", +<<<<<<< HEAD "src/horus_utils.erl" +======= + "src/horus_utils.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ], hdrs = [":public_and_private_hdrs"], app_name = "horus", @@ -50,9 +54,14 @@ filegroup( filegroup( name = "private_hdrs", srcs = [ +<<<<<<< HEAD "src/horus_cover.hrl", "src/horus_error.hrl", "src/horus_fun.hrl" +======= + "src/horus_error.hrl", + "src/horus_fun.hrl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ], ) diff --git a/bazel/BUILD.redbug b/bazel/BUILD.redbug index 06c43294b29e..09e48f4ceaff 100644 --- a/bazel/BUILD.redbug +++ b/bazel/BUILD.redbug @@ -49,12 +49,16 @@ filegroup( ], ) +<<<<<<< HEAD filegroup( name = "private_hdrs", srcs = [ "src/redbug_dbg.hrl", ], ) +======= +filegroup(name = "private_hdrs") +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) filegroup(name = "public_hdrs") diff --git a/bazel/bzlmod/secondary_umbrella.bzl b/bazel/bzlmod/secondary_umbrella.bzl index 613961d5a616..964fc8be2e9e 100644 --- a/bazel/bzlmod/secondary_umbrella.bzl +++ b/bazel/bzlmod/secondary_umbrella.bzl @@ -25,6 +25,7 @@ EOF def secondary_umbrella(): http_archive( +<<<<<<< HEAD name = "rabbitmq-server-generic-unix-3.13", build_file = "@//:BUILD.package_generic_unix", patch_cmds = [ADD_PLUGINS_DIR_BUILD_FILE], @@ -32,5 +33,14 @@ def secondary_umbrella(): # This file is produced just in time by the test-mixed-versions.yaml GitHub Actions workflow. urls = [ "https://rabbitmq-github-actions.s3.eu-west-1.amazonaws.com/secondary-umbrellas/26.1/package-generic-unix-for-mixed-version-testing-v3.13.7.tar.xz", +======= + name = "rabbitmq-server-generic-unix-4.0", + build_file = "@//:BUILD.package_generic_unix", + patch_cmds = [ADD_PLUGINS_DIR_BUILD_FILE], + strip_prefix = "rabbitmq_server-4.0.0", + # This file is produced just in time by the test-mixed-versions.yaml GitHub Actions workflow. + urls = [ + "https://rabbitmq-github-actions.s3.eu-west-1.amazonaws.com/secondary-umbrellas/26.1/package-generic-unix-for-mixed-version-testing-v4.0.2.tar.xz", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ], ) diff --git a/deps/amqp10_client/BUILD.bazel b/deps/amqp10_client/BUILD.bazel index df8b879adae1..53eebc1bf5c6 100644 --- a/deps/amqp10_client/BUILD.bazel +++ b/deps/amqp10_client/BUILD.bazel @@ -76,6 +76,10 @@ rabbitmq_app( priv = [":priv"], deps = [ "//deps/amqp10_common:erlang_app", +<<<<<<< HEAD +======= + "//deps/rabbit_common:erlang_app", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "@credentials_obfuscation//:erlang_app", ], ) diff --git a/deps/amqp10_client/app.bzl b/deps/amqp10_client/app.bzl index 8fcdad73cf9d..ce8935140d25 100644 --- a/deps/amqp10_client/app.bzl +++ b/deps/amqp10_client/app.bzl @@ -113,7 +113,10 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/system_SUITE.erl"], outs = ["test/system_SUITE.beam"], +<<<<<<< HEAD hdrs = ["src/amqp10_client.hrl"], +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) app_name = "amqp10_client", erlc_opts = "//:test_erlc_opts", deps = ["//deps/amqp10_common:erlang_app"], diff --git a/deps/amqp10_client/src/amqp10_client.erl b/deps/amqp10_client/src/amqp10_client.erl index c5ebc7ba123f..702ffef05615 100644 --- a/deps/amqp10_client/src/amqp10_client.erl +++ b/deps/amqp10_client/src/amqp10_client.erl @@ -144,6 +144,11 @@ begin_session_sync(Connection, Timeout) when is_pid(Connection) -> receive {amqp10_event, {session, Session, begun}} -> {ok, Session}; +<<<<<<< HEAD +======= + {amqp10_event, {session, Session, {begun, #'v1_0.begin'{}}}} -> + {ok, Session}; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {amqp10_event, {session, Session, {ended, Err}}} -> {error, Err} after Timeout -> session_timeout @@ -186,6 +191,11 @@ attach_sender_link_sync(Session, Name, Target, SettleMode, Durability) -> receive {amqp10_event, {link, Ref, attached}} -> {ok, Ref}; +<<<<<<< HEAD +======= + {amqp10_event, {link, Ref, {attached, #'v1_0.attach'{}}}} -> + {ok, Ref}; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {amqp10_event, {link, Ref, {detached, Err}}} -> {error, Err} after ?TIMEOUT -> link_timeout diff --git a/deps/amqp10_client/src/amqp10_client_connection.erl b/deps/amqp10_client/src/amqp10_client_connection.erl index df0548aa9ef1..6741043ab72a 100644 --- a/deps/amqp10_client/src/amqp10_client_connection.erl +++ b/deps/amqp10_client/src/amqp10_client_connection.erl @@ -63,6 +63,10 @@ notify => pid() | none, % the pid to send connection events to notify_when_opened => pid() | none, notify_when_closed => pid() | none, +<<<<<<< HEAD +======= + notify_with_performative => boolean(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% incoming maximum frame size set by our client application max_frame_size => pos_integer(), % TODO: constrain to large than 512 %% outgoing maximum frame size set by AMQP peer in OPEN performative @@ -253,7 +257,11 @@ hdr_sent({call, From}, begin_session, {keep_state, State1}. open_sent(_EvtType, #'v1_0.open'{max_frame_size = MaybeMaxFrameSize, +<<<<<<< HEAD idle_time_out = Timeout}, +======= + idle_time_out = Timeout} = Open, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #state{pending_session_reqs = PendingSessionReqs, config = Config} = State0) -> State = case Timeout of @@ -278,7 +286,11 @@ open_sent(_EvtType, #'v1_0.open'{max_frame_size = MaybeMaxFrameSize, _ = gen_statem:reply(From, Ret), S2 end, State1, PendingSessionReqs), +<<<<<<< HEAD ok = notify_opened(Config), +======= + ok = notify_opened(Config, Open), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {next_state, opened, State2#state{pending_session_reqs = []}}; open_sent({call, From}, begin_session, #state{pending_session_reqs = PendingSessionReqs} = State) -> @@ -292,19 +304,32 @@ opened(_EvtType, heartbeat, State = #state{idle_time_out = T}) -> ok = send_heartbeat(State), {ok, Tmr} = start_heartbeat_timer(T), {keep_state, State#state{heartbeat_timer = Tmr}}; +<<<<<<< HEAD opened(_EvtType, {close, Reason}, State = #state{config = Config}) -> %% We send the first close frame and wait for the reply. %% TODO: stop all sessions writing %% We could still accept incoming frames (See: 2.4.6) ok = notify_closed(Config, Reason), +======= +opened(_EvtType, {close, Reason}, State) -> + %% We send the first close frame and wait for the reply. + %% TODO: stop all sessions writing + %% We could still accept incoming frames (See: 2.4.6) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case send_close(State, Reason) of ok -> {next_state, close_sent, State}; {error, closed} -> {stop, normal, State}; Error -> {stop, Error, State} end; +<<<<<<< HEAD opened(_EvtType, #'v1_0.close'{error = Error}, State = #state{config = Config}) -> %% We receive the first close frame, reply and terminate. ok = notify_closed(Config, translate_err(Error)), +======= +opened(_EvtType, #'v1_0.close'{} = Close, State = #state{config = Config}) -> + %% We receive the first close frame, reply and terminate. + ok = notify_closed(Config, Close), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ = send_close(State, none), {stop, normal, State}; opened({call, From}, begin_session, State) -> @@ -329,7 +354,12 @@ close_sent(_EvtType, {'DOWN', _Ref, process, ReaderPid, _}, #state{reader = ReaderPid} = State) -> %% if the reader exits we probably wont receive a close frame {stop, normal, State}; +<<<<<<< HEAD close_sent(_EvtType, #'v1_0.close'{}, State) -> +======= +close_sent(_EvtType, #'v1_0.close'{} = Close, State = #state{config = Config}) -> + ok = notify_closed(Config, Close), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% TODO: we should probably set up a timer before this to ensure %% we close down event if no reply is received {stop, normal, State}. @@ -489,6 +519,7 @@ socket_shutdown({tcp, Socket}, How) -> socket_shutdown({ssl, Socket}, How) -> ssl:shutdown(Socket, How). +<<<<<<< HEAD notify_opened(#{notify_when_opened := none}) -> ok; notify_opened(#{notify_when_opened := Pid}) when is_pid(Pid) -> @@ -498,16 +529,54 @@ notify_opened(#{notify := Pid}) when is_pid(Pid) -> Pid ! amqp10_event(opened), ok; notify_opened(_) -> +======= +notify_opened(#{notify_when_opened := none}, _) -> + ok; +notify_opened(#{notify_when_opened := Pid} = Config, Perf) + when is_pid(Pid) -> + notify_opened0(Config, Pid, Perf); +notify_opened(#{notify := Pid} = Config, Perf) + when is_pid(Pid) -> + notify_opened0(Config, Pid, Perf); +notify_opened(_, _) -> + ok. + +notify_opened0(Config, Pid, Perf) -> + Evt = case Config of + #{notify_with_performative := true} -> + {opened, Perf}; + _ -> + opened + end, + Pid ! amqp10_event(Evt), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok. notify_closed(#{notify_when_closed := none}, _Reason) -> ok; notify_closed(#{notify := none}, _Reason) -> ok; +<<<<<<< HEAD notify_closed(#{notify_when_closed := Pid}, Reason) when is_pid(Pid) -> Pid ! amqp10_event({closed, Reason}), ok; notify_closed(#{notify := Pid}, Reason) when is_pid(Pid) -> +======= +notify_closed(#{notify_when_closed := Pid} = Config, Reason) + when is_pid(Pid) -> + notify_closed0(Config, Pid, Reason); +notify_closed(#{notify := Pid} = Config, Reason) + when is_pid(Pid) -> + notify_closed0(Config, Pid, Reason). + +notify_closed0(#{notify_with_performative := true}, Pid, Perf = #'v1_0.close'{}) -> + Pid ! amqp10_event({closed, Perf}), + ok; +notify_closed0(_, Pid, #'v1_0.close'{error = Error}) -> + Pid ! amqp10_event({closed, translate_err(Error)}), + ok; +notify_closed0(_, Pid, Reason) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Pid ! amqp10_event({closed, Reason}), ok. diff --git a/deps/amqp10_client/src/amqp10_client_frame_reader.erl b/deps/amqp10_client/src/amqp10_client_frame_reader.erl index 05d8823999b1..8d4bc6545aef 100644 --- a/deps/amqp10_client/src/amqp10_client_frame_reader.erl +++ b/deps/amqp10_client/src/amqp10_client_frame_reader.erl @@ -105,7 +105,12 @@ init([Sup, ConnConfig]) when is_map(ConnConfig) -> {ok, expecting_connection_pid, State} end. +<<<<<<< HEAD connect(Address, Port, #{tls_opts := {secure_port, Opts}}) -> +======= +connect(Address, Port, #{tls_opts := {secure_port, Opts0}}) -> + Opts = rabbit_ssl_options:fix_client(Opts0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case ssl:connect(Address, Port, ?RABBIT_TCP_OPTS ++ Opts) of {ok, S} -> {ssl, S}; diff --git a/deps/amqp10_client/src/amqp10_client_session.erl b/deps/amqp10_client/src/amqp10_client_session.erl index e55775539206..5a8495297977 100644 --- a/deps/amqp10_client/src/amqp10_client_session.erl +++ b/deps/amqp10_client/src/amqp10_client_session.erl @@ -254,7 +254,11 @@ unmapped({call, From}, {attach, Attach}, begin_sent(cast, #'v1_0.begin'{remote_channel = {ushort, RemoteChannel}, next_outgoing_id = {uint, NOI}, incoming_window = {uint, InWindow}, +<<<<<<< HEAD outgoing_window = {uint, OutWindow}}, +======= + outgoing_window = {uint, OutWindow}} = Begin, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #state{early_attach_requests = EARs} = State) -> State1 = State#state{remote_channel = RemoteChannel}, @@ -264,7 +268,11 @@ begin_sent(cast, #'v1_0.begin'{remote_channel = {ushort, RemoteChannel}, S2 end, State1, EARs), +<<<<<<< HEAD ok = notify_session_begun(State2), +======= + ok = notify_session_begun(Begin, State2), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {next_state, mapped, State2#state{early_attach_requests = [], next_incoming_id = NOI, @@ -291,18 +299,30 @@ mapped(cast, {flow_session, Flow0 = #'v1_0.flow'{incoming_window = {uint, Incomi outgoing_window = ?UINT_OUTGOING_WINDOW}, ok = send(Flow, State), {keep_state, State#state{incoming_window = IncomingWindow}}; +<<<<<<< HEAD mapped(cast, #'v1_0.end'{error = Err}, State) -> %% We receive the first end frame, reply and terminate. _ = send_end(State), % TODO: send notifications for links? Reason = reason(Err), ok = notify_session_ended(State, Reason), +======= +mapped(cast, #'v1_0.end'{} = End, State) -> + %% We receive the first end frame, reply and terminate. + _ = send_end(State), + % TODO: send notifications for links? + ok = notify_session_ended(End, State), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {stop, normal, State}; mapped(cast, #'v1_0.attach'{name = {utf8, Name}, initial_delivery_count = IDC, handle = {uint, InHandle}, role = PeerRoleBool, +<<<<<<< HEAD max_message_size = MaybeMaxMessageSize}, +======= + max_message_size = MaybeMaxMessageSize} = Attach, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #state{links = Links, link_index = LinkIndex, link_handle_index = LHI} = State0) -> @@ -311,7 +331,11 @@ mapped(cast, #'v1_0.attach'{name = {utf8, Name}, LinkIndexKey = {OurRole, Name}, #{LinkIndexKey := OutHandle} = LinkIndex, #{OutHandle := Link0} = Links, +<<<<<<< HEAD ok = notify_link_attached(Link0), +======= + ok = notify_link_attached(Link0, Attach, State0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {DeliveryCount, MaxMessageSize} = case Link0 of @@ -334,6 +358,7 @@ mapped(cast, #'v1_0.attach'{name = {utf8, Name}, link_index = maps:remove(LinkIndexKey, LinkIndex), link_handle_index = LHI#{InHandle => OutHandle}}, {keep_state, State}; +<<<<<<< HEAD mapped(cast, #'v1_0.detach'{handle = {uint, InHandle}, error = Err}, #state{links = Links, link_handle_index = LHI} = State0) -> @@ -341,6 +366,13 @@ mapped(cast, #'v1_0.detach'{handle = {uint, InHandle}, fun (#link{output_handle = OutHandle} = Link, State) -> Reason = reason(Err), ok = notify_link_detached(Link, Reason), +======= +mapped(cast, #'v1_0.detach'{handle = {uint, InHandle}} = Detach, + #state{links = Links, link_handle_index = LHI} = State0) -> + with_link(InHandle, State0, + fun (#link{output_handle = OutHandle} = Link, State) -> + ok = notify_link_detached(Link, Detach, State), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {keep_state, State#state{links = maps:remove(OutHandle, Links), link_handle_index = maps:remove(InHandle, LHI)}} @@ -470,6 +502,14 @@ mapped({call, From}, #state{remote_incoming_window = Window}) when Window =< 0 -> {keep_state_and_data, {reply, From, {error, remote_incoming_window_exceeded}}}; +<<<<<<< HEAD +======= +mapped({call, From}, + {transfer, _Transfer, _Sections}, + #state{remote_incoming_window = Window}) + when Window =< 0 -> + {keep_state_and_data, {reply, From, {error, remote_incoming_window_exceeded}}}; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) mapped({call, From = {Pid, _}}, {transfer, #'v1_0.transfer'{handle = {uint, OutHandle}, delivery_tag = {binary, DeliveryTag}, @@ -547,9 +587,14 @@ mapped(_EvtType, Msg, _State) -> [Msg, 10]), keep_state_and_data. +<<<<<<< HEAD end_sent(_EvtType, #'v1_0.end'{error = Err}, State) -> Reason = reason(Err), ok = notify_session_ended(State, Reason), +======= +end_sent(_EvtType, #'v1_0.end'{} = End, State) -> + ok = notify_session_ended(End, State), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {stop, normal, State}; end_sent(_EvtType, _Frame, _State) -> % just drop frames here @@ -732,6 +777,7 @@ translate_terminus_durability(configuration) -> 1; translate_terminus_durability(unsettled_state) -> 2. translate_filters(Filters) +<<<<<<< HEAD when is_map(Filters) andalso map_size(Filters) == 0 -> undefined; @@ -741,6 +787,15 @@ translate_filters(Filters) maps:fold( fun (<<"apache.org:legacy-amqp-headers-binding:map">> = K, V, Acc) when is_map(V) -> +======= + when map_size(Filters) =:= 0 -> + undefined; +translate_filters(Filters) -> + {map, + maps:fold( + fun + (<<"apache.org:legacy-amqp-headers-binding:map">> = K, V, Acc) when is_map(V) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% special case conversion Key = sym(K), [{Key, {described, Key, translate_legacy_amqp_headers_binding(V)}} | Acc]; @@ -986,10 +1041,31 @@ maybe_notify_link_credit(#link{role = sender, maybe_notify_link_credit(_Old, _New) -> ok. +<<<<<<< HEAD notify_link_attached(Link) -> notify_link(Link, attached). notify_link_detached(Link, Reason) -> +======= +notify_link_attached(Link, Perf, #state{connection_config = Cfg}) -> + What = case Cfg of + #{notify_with_performative := true} -> + {attached, Perf}; + _ -> + attached + end, + notify_link(Link, What). + +notify_link_detached(Link, + Perf = #'v1_0.detach'{error = Err}, + #state{connection_config = Cfg}) -> + Reason = case Cfg of + #{notify_with_performative := true} -> + Perf; + _ -> + reason(Err) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) notify_link(Link, {detached, Reason}). notify_link(#link{notify = Pid, ref = Ref}, What) -> @@ -997,11 +1073,34 @@ notify_link(#link{notify = Pid, ref = Ref}, What) -> Pid ! Evt, ok. +<<<<<<< HEAD notify_session_begun(#state{notify = Pid}) -> Pid ! amqp10_session_event(begun), ok. notify_session_ended(#state{notify = Pid}, Reason) -> +======= +notify_session_begun(Perf, #state{notify = Pid, + connection_config = Cfg}) -> + Evt = case Cfg of + #{notify_with_performative := true} -> + {begun, Perf}; + _ -> + begun + end, + Pid ! amqp10_session_event(Evt), + ok. + +notify_session_ended(Perf = #'v1_0.end'{error = Err}, + #state{notify = Pid, + connection_config = Cfg}) -> + Reason = case Cfg of + #{notify_with_performative := true} -> + Perf; + _ -> + reason(Err) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Pid ! amqp10_session_event({ended, Reason}), ok. @@ -1166,11 +1265,16 @@ make_link_ref(Role, Session, Handle) -> translate_message_annotations(MA) when map_size(MA) > 0 -> {map, maps:fold(fun(K, V, Acc) -> +<<<<<<< HEAD [{sym(K), wrap_map_value(V)} | Acc] +======= + [{sym(K), amqp10_client_types:infer(V)} | Acc] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, [], MA)}; translate_message_annotations(_MA) -> undefined. +<<<<<<< HEAD wrap_map_value(true) -> {boolean, true}; wrap_map_value(false) -> @@ -1193,6 +1297,8 @@ wrap_map_value(TaggedValue) when is_atom(element(1, TaggedValue)) -> utf8(V) -> amqp10_client_types:utf8(V). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sym(B) when is_binary(B) -> {symbol, B}; sym(B) when is_list(B) -> {symbol, list_to_binary(B)}; sym(B) when is_atom(B) -> {symbol, atom_to_binary(B, utf8)}. diff --git a/deps/amqp10_client/src/amqp10_client_types.erl b/deps/amqp10_client/src/amqp10_client_types.erl index 5758012e9335..722e97e100e3 100644 --- a/deps/amqp10_client/src/amqp10_client_types.erl +++ b/deps/amqp10_client/src/amqp10_client_types.erl @@ -9,6 +9,10 @@ -include_lib("amqp10_common/include/amqp10_framing.hrl"). -export([unpack/1, +<<<<<<< HEAD +======= + infer/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) utf8/1, uint/1, make_properties/1]). @@ -73,6 +77,7 @@ properties/0]). +<<<<<<< HEAD unpack({_, Value}) -> Value; unpack(Value) -> Value. @@ -80,6 +85,34 @@ utf8(S) when is_list(S) -> {utf8, list_to_binary(S)}; utf8(B) when is_binary(B) -> {utf8, B}. uint(N) -> {uint, N}. +======= +unpack({_, Value}) -> + Value; +unpack(Value) -> + Value. + +infer(V) when is_integer(V) -> + {long, V}; +infer(V) when is_number(V) -> + %% AMQP double and Erlang float are both 64-bit. + {double, V}; +infer(V) when is_boolean(V) -> + {boolean, V}; +infer(V) when is_atom(V) -> + {utf8, atom_to_binary(V, utf8)}; +infer(TaggedValue) when is_atom(element(1, TaggedValue)) -> + TaggedValue; +infer(V) -> + utf8(V). + +utf8(V) when is_binary(V) -> + {utf8, V}; +utf8(V) when is_list(V) -> + {utf8, unicode:characters_to_binary(V)}. + +uint(N) -> + {uint, N}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) make_properties(#{properties := Props}) when map_size(Props) > 0 -> diff --git a/deps/amqp10_client/src/amqp10_msg.erl b/deps/amqp10_client/src/amqp10_msg.erl index 673617acc6a0..ffc0b78be091 100644 --- a/deps/amqp10_client/src/amqp10_msg.erl +++ b/deps/amqp10_client/src/amqp10_msg.erl @@ -38,6 +38,11 @@ set_message_annotations/2 ]). +<<<<<<< HEAD +======= +-import(amqp10_client_types, [utf8/1]). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("amqp10_common/include/amqp10_framing.hrl"). -type opt(T) :: T | undefined. @@ -380,13 +385,21 @@ set_application_properties( Props0, #amqp10_msg{application_properties = #'v1_0.application_properties'{content = APs0}} = Msg) -> Props = maps:fold(fun (K, V, S) -> +<<<<<<< HEAD S#{utf8(K) => wrap_ap_value(V)} +======= + S#{utf8(K) => amqp10_client_types:infer(V)} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, maps:from_list(APs0), Props0), APs = #'v1_0.application_properties'{content = maps:to_list(Props)}, Msg#amqp10_msg{application_properties = APs}. -spec set_delivery_annotations(#{binary() => binary() | integer() | string()}, +<<<<<<< HEAD amqp10_msg()) -> amqp10_msg(). +======= + amqp10_msg()) -> amqp10_msg(). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) set_delivery_annotations(Props, #amqp10_msg{delivery_annotations = undefined} = Msg) -> @@ -394,6 +407,7 @@ set_delivery_annotations(Props, set_delivery_annotations(Props, Msg#amqp10_msg{delivery_annotations = Anns}); set_delivery_annotations( +<<<<<<< HEAD Props0, #amqp10_msg{delivery_annotations = #'v1_0.delivery_annotations'{content = Anns0}} = Msg) -> Anns = maps:fold(fun (K, V, S) -> @@ -401,10 +415,20 @@ set_delivery_annotations( end, maps:from_list(Anns0), Props0), Anns1 = #'v1_0.delivery_annotations'{content = maps:to_list(Anns)}, Msg#amqp10_msg{delivery_annotations = Anns1}. +======= + Props, #amqp10_msg{delivery_annotations = + #'v1_0.delivery_annotations'{content = Anns0}} = Msg) -> + Anns1 = maps:fold(fun (K, V, S) -> + S#{sym(K) => amqp10_client_types:infer(V)} + end, maps:from_list(Anns0), Props), + Anns = #'v1_0.delivery_annotations'{content = maps:to_list(Anns1)}, + Msg#amqp10_msg{delivery_annotations = Anns}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec set_message_annotations(#{binary() => binary() | number() | string() | tuple()}, amqp10_msg()) -> amqp10_msg(). set_message_annotations(Props, +<<<<<<< HEAD #amqp10_msg{message_annotations = undefined} = Msg) -> Anns = #'v1_0.message_annotations'{content = []}, @@ -439,6 +463,21 @@ wrap_ap_value(V) when is_number(V) -> {double, V}; wrap_ap_value(TaggedValue) when is_tuple(TaggedValue) -> TaggedValue. +======= + #amqp10_msg{message_annotations = undefined} = + Msg) -> + Anns = #'v1_0.message_annotations'{content = []}, + set_message_annotations(Props, + Msg#amqp10_msg{message_annotations = Anns}); +set_message_annotations( + Props, #amqp10_msg{message_annotations = + #'v1_0.message_annotations'{content = Anns0}} = Msg) -> + Anns1 = maps:fold(fun (K, V, S) -> + S#{sym(K) => amqp10_client_types:infer(V)} + end, maps:from_list(Anns0), Props), + Anns = #'v1_0.message_annotations'{content = maps:to_list(Anns1)}, + Msg#amqp10_msg{message_annotations = Anns}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% LOCAL header_value(durable, undefined) -> false; @@ -474,7 +513,10 @@ parse_from_amqp(#'v1_0.footer'{} = Header, AmqpMsg) -> AmqpMsg#amqp10_msg{footer = Header}. unpack(V) -> amqp10_client_types:unpack(V). +<<<<<<< HEAD utf8(V) -> amqp10_client_types:utf8(V). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sym(B) when is_list(B) -> {symbol, list_to_binary(B)}; sym(B) when is_binary(B) -> {symbol, B}. uint(B) -> {uint, B}. diff --git a/deps/amqp10_client/test/system_SUITE.erl b/deps/amqp10_client/test/system_SUITE.erl index 7a64425c7583..5c4162238dde 100644 --- a/deps/amqp10_client/test/system_SUITE.erl +++ b/deps/amqp10_client/test/system_SUITE.erl @@ -12,10 +12,17 @@ -include_lib("amqp10_common/include/amqp10_framing.hrl"). +<<<<<<< HEAD -include("src/amqp10_client.hrl"). -compile([export_all, nowarn_export_all]). +======= +-compile([export_all, nowarn_export_all]). + +-define(TIMEOUT, 30000). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) suite() -> [{timetrap, {minutes, 4}}]. @@ -30,7 +37,11 @@ all() -> groups() -> [ +<<<<<<< HEAD {rabbitmq, [], shared()}, +======= + {rabbitmq, [], shared() ++ [notify_with_performative]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {activemq, [], shared()}, {rabbitmq_strict, [], [ basic_roundtrip_tls, @@ -184,7 +195,11 @@ open_close_connection(Config) -> {ok, Connection2} = amqp10_client:open_connection(OpnConf), receive {amqp10_event, {connection, Connection2, opened}} -> ok +<<<<<<< HEAD after 5000 -> exit(connection_timeout) +======= + after ?TIMEOUT -> exit(connection_timeout) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, ok = amqp10_client:close_connection(Connection2), ok = amqp10_client:close_connection(Connection). @@ -201,7 +216,11 @@ open_connection_plain_sasl(Config) -> {ok, Connection} = amqp10_client:open_connection(OpnConf), receive {amqp10_event, {connection, Connection, opened}} -> ok +<<<<<<< HEAD after 5000 -> exit(connection_timeout) +======= + after ?TIMEOUT -> exit(connection_timeout) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, ok = amqp10_client:close_connection(Connection). @@ -225,7 +244,11 @@ open_connection_plain_sasl_parse_uri(Config) -> {ok, Connection} = amqp10_client:open_connection(OpnConf), receive {amqp10_event, {connection, Connection, opened}} -> ok +<<<<<<< HEAD after 5000 -> exit(connection_timeout) +======= + after ?TIMEOUT -> exit(connection_timeout) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, ok = amqp10_client:close_connection(Connection). @@ -245,7 +268,11 @@ open_connection_plain_sasl_failure(Config) -> % some implementation may simply close the tcp_connection {amqp10_event, {connection, Connection, {closed, shutdown}}} -> ok +<<<<<<< HEAD after 5000 -> +======= + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ct:pal("Connection process is alive? = ~tp~n", [erlang:is_process_alive(Connection)]), exit(connection_timeout) @@ -458,6 +485,55 @@ transfer_id_vs_delivery_id(Config) -> ?assertEqual(serial_number:add(amqp10_msg:delivery_id(RcvMsg1), 1), amqp10_msg:delivery_id(RcvMsg2)). +<<<<<<< HEAD +======= +notify_with_performative(Config) -> + Hostname = ?config(rmq_hostname, Config), + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + + OpenConf = #{?FUNCTION_NAME => true, + address => Hostname, + port => Port, + sasl => anon}, + + {ok, Connection} = amqp10_client:open_connection(OpenConf), + receive {amqp10_event, {connection, Connection, {opened, #'v1_0.open'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end, + + {ok, Session1} = amqp10_client:begin_session(Connection), + receive {amqp10_event, {session, Session1, {begun, #'v1_0.begin'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end, + + {ok, Sender1} = amqp10_client:attach_sender_link(Session1, <<"sender 1">>, <<"/exchanges/amq.fanout">>), + receive {amqp10_event, {link, Sender1, {attached, #'v1_0.attach'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:detach_link(Sender1), + receive {amqp10_event, {link, Sender1, {detached, #'v1_0.detach'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:end_session(Session1), + receive {amqp10_event, {session, Session1, {ended, #'v1_0.end'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end, + + %% Test that the amqp10_client:*_sync functions work. + {ok, Session2} = amqp10_client:begin_session_sync(Connection), + {ok, Sender2} = amqp10_client:attach_sender_link_sync(Session2, <<"sender 2">>, <<"/exchanges/amq.fanout">>), + ok = amqp10_client:detach_link(Sender2), + ok = amqp10_client:end_session(Session2), + flush(), + + ok = amqp10_client:close_connection(Connection), + receive {amqp10_event, {connection, Connection, {closed, #'v1_0.close'{}}}} -> ok + after ?TIMEOUT -> ct:fail({missing_event, ?LINE}) + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) % a message is sent before the link attach is guaranteed to % have completed and link credit granted % also queue a link detached immediately after transfer @@ -554,13 +630,21 @@ subscribe(Config) -> [begin receive {amqp10_msg, Receiver, Msg} -> ok = amqp10_client:accept_msg(Receiver, Msg) +<<<<<<< HEAD after 2000 -> ct:fail(timeout) +======= + after ?TIMEOUT -> ct:fail(timeout) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end || _ <- lists:seq(1, 10)], ok = assert_no_message(Receiver), receive {amqp10_event, {link, Receiver, credit_exhausted}} -> ok +<<<<<<< HEAD after 5000 -> flush(), +======= + after ?TIMEOUT -> flush(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) exit(credit_exhausted_assert) end, @@ -808,7 +892,11 @@ multi_transfer_without_delivery_id(Config) -> receive {amqp10_msg, Receiver, _InMsg} -> ok +<<<<<<< HEAD after 2000 -> +======= + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) exit(delivery_timeout) end, @@ -832,8 +920,15 @@ incoming_heartbeat(Config) -> Hostname = ?config(mock_host, Config), Port = ?config(mock_port, Config), OpenStep = fun({0 = Ch, #'v1_0.open'{}, _Pay}) -> +<<<<<<< HEAD {Ch, [#'v1_0.open'{container_id = {utf8, <<"mock">>}, idle_time_out = {uint, 0}}]} +======= + {Ch, [#'v1_0.open'{ + container_id = {utf8, <<"mock">>}, + %% The server doesn't expect any heartbeats from us (client). + idle_time_out = {uint, 0}}]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, CloseStep = fun({0 = Ch, #'v1_0.close'{error = _TODO}, _Pay}) -> @@ -847,6 +942,7 @@ incoming_heartbeat(Config) -> MockRef = monitor(process, MockPid), ok = mock_server:set_steps(Mock, Steps), CConf = #{address => Hostname, port => Port, sasl => ?config(sasl, Config), +<<<<<<< HEAD idle_time_out => 1000, notify => self()}, {ok, Connection} = amqp10_client:open_connection(CConf), receive @@ -856,11 +952,31 @@ incoming_heartbeat(Config) -> when Connection0 =:= Connection -> ok after 5000 -> +======= + %% If the server does not send any traffic to us (client), we will expect + %% our client to close the connection after 1 second because + %% "the value in idle-time-out SHOULD be half the peer's actual timeout threshold." + idle_time_out => 500, + notify => self()}, + {ok, Connection} = amqp10_client:open_connection(CConf), + %% We expect our client to initiate closing the connection + %% and the server to reply with a close frame. + receive + {amqp10_event, + {connection, Connection0, + {closed, _}}} + when Connection0 =:= Connection -> + ok + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) exit(incoming_heartbeat_assert) end, demonitor(MockRef). +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %%% HELPERS %%% @@ -873,7 +989,11 @@ await_link(Who, What, Err) -> {amqp10_event, {link, Who0, {detached, Why}}} when Who0 =:= Who -> ct:fail(Why) +<<<<<<< HEAD after 5000 -> +======= + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) flush(), ct:fail(Err) end. @@ -890,7 +1010,11 @@ await_disposition(DeliveryTag) -> receive {amqp10_disposition, {accepted, DeliveryTag0}} when DeliveryTag0 =:= DeliveryTag -> ok +<<<<<<< HEAD after 3000 -> +======= + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) flush(), ct:fail(dispostion_timeout) end. @@ -902,7 +1026,11 @@ count_received_messages0(Receiver, Count) -> receive {amqp10_msg, Receiver, _Msg} -> count_received_messages0(Receiver, Count + 1) +<<<<<<< HEAD after 500 -> +======= + after 5000 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Count end. @@ -915,7 +1043,11 @@ receive_messages0(Receiver, N, Acc) -> receive {amqp10_msg, Receiver, Msg} -> receive_messages0(Receiver, N - 1, [Msg | Acc]) +<<<<<<< HEAD after 5000 -> +======= + after ?TIMEOUT -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) LastReceivedMsg = case Acc of [] -> none; [M | _] -> M diff --git a/deps/amqp10_common/app.bzl b/deps/amqp10_common/app.bzl index a233c945cebe..27108830883e 100644 --- a/deps/amqp10_common/app.bzl +++ b/deps/amqp10_common/app.bzl @@ -72,7 +72,11 @@ def all_srcs(name = "all_srcs"): ) filegroup( name = "public_hdrs", +<<<<<<< HEAD srcs = ["include/amqp10_framing.hrl", "include/amqp10_types.hrl"], +======= + srcs = ["include/amqp10_filtex.hrl", "include/amqp10_framing.hrl", "include/amqp10_types.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) filegroup( name = "private_hdrs", diff --git a/deps/amqp10_common/include/amqp10_filtex.hrl b/deps/amqp10_common/include/amqp10_filtex.hrl new file mode 100644 index 000000000000..a1743ea9669c --- /dev/null +++ b/deps/amqp10_common/include/amqp10_filtex.hrl @@ -0,0 +1,15 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. + + +%% AMQP Filter Expressions Version 1.0 Working Draft 09 +%% https://groups.oasis-open.org/higherlogic/ws/public/document?document_id=66227 + +-define(DESCRIPTOR_NAME_PROPERTIES_FILTER, <<"amqp:properties-filter">>). +-define(DESCRIPTOR_CODE_PROPERTIES_FILTER, 16#173). + +-define(DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER, <<"amqp:application-properties-filter">>). +-define(DESCRIPTOR_CODE_APPLICATION_PROPERTIES_FILTER, 16#174). diff --git a/deps/amqp_client/src/amqp_network_connection.erl b/deps/amqp_client/src/amqp_network_connection.erl index a5ef739ea0f3..9cd8e8995238 100644 --- a/deps/amqp_client/src/amqp_network_connection.erl +++ b/deps/amqp_client/src/amqp_network_connection.erl @@ -137,7 +137,11 @@ do_connect({Addr, Family}, [Family | ?RABBIT_TCP_OPTS] ++ ExtraOpts, Timeout) of {ok, Sock} -> +<<<<<<< HEAD SslOpts = rabbit_ssl_options:fix( +======= + SslOpts = rabbit_ssl_options:fix_client( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) orddict:to_list( orddict:merge(fun (_, _A, B) -> B end, orddict:from_list(GlobalSslOpts), diff --git a/deps/oauth2_client/app.bzl b/deps/oauth2_client/app.bzl index 6b4b31789a16..b19273f7531f 100644 --- a/deps/oauth2_client/app.bzl +++ b/deps/oauth2_client/app.bzl @@ -64,7 +64,11 @@ def all_srcs(name = "all_srcs"): ) filegroup( name = "public_hdrs", +<<<<<<< HEAD srcs = ["include/oauth2_client.hrl"], +======= + srcs = ["include/oauth2_client.hrl", "include/types.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) filegroup( name = "license_files", @@ -88,7 +92,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/system_SUITE.erl"], outs = ["test/system_SUITE.beam"], +<<<<<<< HEAD hdrs = ["include/oauth2_client.hrl"], +======= + hdrs = ["include/oauth2_client.hrl", "include/types.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) app_name = "oauth2_client", erlc_opts = "//:test_erlc_opts", ) @@ -97,7 +105,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/unit_SUITE.erl"], outs = ["test/unit_SUITE.beam"], +<<<<<<< HEAD hdrs = ["include/oauth2_client.hrl"], +======= + hdrs = ["include/oauth2_client.hrl", "include/types.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) app_name = "oauth2_client", erlc_opts = "//:test_erlc_opts", ) diff --git a/deps/oauth2_client/include/oauth2_client.hrl b/deps/oauth2_client/include/oauth2_client.hrl index b7f93104f167..a6df4d4f4996 100644 --- a/deps/oauth2_client/include/oauth2_client.hrl +++ b/deps/oauth2_client/include/oauth2_client.hrl @@ -5,6 +5,10 @@ %% Copyright (c) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. %% +<<<<<<< HEAD +======= +-include("types.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) % define access token request common constants @@ -44,6 +48,7 @@ -define(RESPONSE_END_SESSION_ENDPOINT, <<"end_session_endpoint">>). -define(RESPONSE_JWKS_URI, <<"jwks_uri">>). -define(RESPONSE_TLS_OPTIONS, <<"ssl_options">>). +<<<<<<< HEAD %% The closest we have to a type import in Erlang -type option(T) :: rabbit_types:option(T). @@ -107,3 +112,5 @@ }). -type refresh_token_request() :: #refresh_token_request{}. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/oauth2_client/include/types.hrl b/deps/oauth2_client/include/types.hrl new file mode 100644 index 000000000000..622cae22202c --- /dev/null +++ b/deps/oauth2_client/include/types.hrl @@ -0,0 +1,75 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. +%% + +%% Matches the option type in rabbit_types without introducing a dependency +%% on that module and RabbitMQ core (rabbit_common) +-type(option(T) :: T | 'none' | 'undefined'). + +-type oauth_provider_id() :: root | binary(). + +-record(openid_configuration, { + issuer :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()) +}). +-type openid_configuration() :: #openid_configuration{}. + +-record(oauth_provider, { + id :: oauth_provider_id(), + issuer :: option(uri_string:uri_string()), + discovery_endpoint :: option(uri_string:uri_string()), + token_endpoint :: option(uri_string:uri_string()), + authorization_endpoint :: option(uri_string:uri_string()), + end_session_endpoint :: option(uri_string:uri_string()), + jwks_uri :: option(uri_string:uri_string()), + ssl_options :: option(list()) +}). + +-type query_list() :: [{unicode:chardata(), unicode:chardata() | true}]. + +-type oauth_provider() :: #oauth_provider{}. + +-record(access_token_request, { + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: option(string() | binary()), + extra_parameters :: option(query_list()), + timeout :: option(integer()) +}). + +-type access_token_request() :: #access_token_request{}. + +-record(successful_access_token_response, { + access_token :: binary(), + token_type :: binary(), + %% Note: a refresh token SHOULD NOT be included + %% ... for client-credentials flow. + %% See https://www.rfc-editor.org/rfc/rfc6749#section-4.4.3 + refresh_token :: option(binary()), + expires_in :: option(integer()) +}). + +-type successful_access_token_response() :: #successful_access_token_response{}. + +-record(unsuccessful_access_token_response, { + error :: integer(), + error_description :: binary() | string() | undefined +}). + +-type unsuccessful_access_token_response() :: #unsuccessful_access_token_response{}. + +-record(refresh_token_request, { + client_id :: string() | binary(), + client_secret :: string() | binary(), + scope :: string() | binary() | undefined, + refresh_token :: binary(), + timeout :: option(integer()) +}). + +-type refresh_token_request() :: #refresh_token_request{}. diff --git a/deps/oauth2_client/src/oauth2_client.erl b/deps/oauth2_client/src/oauth2_client.erl index 335bcfdfba1b..6724e9b2f361 100644 --- a/deps/oauth2_client/src/oauth2_client.erl +++ b/deps/oauth2_client/src/oauth2_client.erl @@ -8,6 +8,7 @@ -export([get_access_token/2, get_expiration_time/1, refresh_access_token/2, get_oauth_provider/1, get_oauth_provider/2, +<<<<<<< HEAD get_openid_configuration/2, get_openid_configuration/3, merge_openid_configuration/2, merge_oauth_provider/2, @@ -17,6 +18,21 @@ -include("oauth2_client.hrl"). -spec get_access_token(oauth_provider(), access_token_request()) -> {ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}. +======= + get_openid_configuration/2, + build_openid_discovery_endpoint/3, + merge_openid_configuration/2, + merge_oauth_provider/2, + extract_ssl_options_as_list/1, + format_ssl_options/1, format_oauth_provider/1, format_oauth_provider_id/1 + ]). + +-include("oauth2_client.hrl"). + +-spec get_access_token(oauth_provider(), access_token_request()) -> + {ok, successful_access_token_response()} | + {error, unsuccessful_access_token_response() | any()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) get_access_token(OAuthProvider, Request) -> rabbit_log:debug("get_access_token using OAuthProvider:~p and client_id:~p", [OAuthProvider, Request#access_token_request.client_id]), @@ -31,7 +47,12 @@ get_access_token(OAuthProvider, Request) -> parse_access_token_response(Response). -spec refresh_access_token(oauth_provider(), refresh_token_request()) -> +<<<<<<< HEAD {ok, successful_access_token_response()} | {error, unsuccessful_access_token_response() | any()}. +======= + {ok, successful_access_token_response()} | + {error, unsuccessful_access_token_response() | any()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) refresh_access_token(OAuthProvider, Request) -> URL = OAuthProvider#oauth_provider.token_endpoint, Header = [], @@ -46,6 +67,7 @@ refresh_access_token(OAuthProvider, Request) -> append_paths(Path1, Path2) -> erlang:iolist_to_binary([Path1, Path2]). +<<<<<<< HEAD -spec get_openid_configuration(uri_string:uri_string(), erlang:iodata() | <<>>, ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}. get_openid_configuration(IssuerURI, OpenIdConfigurationPath, TLSOptions) -> @@ -77,21 +99,90 @@ merge_openid_configuration(OpendIdConfiguration, OAuthProvider) -> OAuthProvider#oauth_provider{issuer = Issuer} end, OAuthProvider1 = case OpendIdConfiguration#openid_configuration.token_endpoint of +======= +-spec build_openid_discovery_endpoint(Issuer :: uri_string:uri_string(), + OpenIdConfigurationPath :: uri_string:uri_string() | undefined, + Params :: query_list()) -> uri_string:uri_string() | undefined. + +build_openid_discovery_endpoint(undefined, _, _) -> undefined; +build_openid_discovery_endpoint(Issuer, undefined, Params) -> + build_openid_discovery_endpoint(Issuer, ?DEFAULT_OPENID_CONFIGURATION_PATH, + Params); +build_openid_discovery_endpoint(Issuer, OpenIdConfigurationPath, Params) -> + URLMap0 = uri_string:parse(Issuer), + OpenIdPath = ensure_leading_path_separator(OpenIdConfigurationPath), + URLMap1 = URLMap0#{ + path := case maps:get(path, URLMap0) of + [] -> OpenIdPath; + P -> append_paths(drop_trailing_path_separator(P), OpenIdPath) + end + }, + uri_string:recompose( + case {Params, maps:get(query, URLMap1, undefined)} of + {undefined, undefined} -> + URLMap1; + {_, undefined} -> + URLMap1#{query => uri_string:compose_query(Params)}; + {_, Q} -> + URLMap1#{query => uri_string:compose_query(Q ++ Params)} + end). +ensure_leading_path_separator(Path) when is_binary(Path) -> + ensure_leading_path_separator(binary:bin_to_list(Path)); +ensure_leading_path_separator(Path) when is_list(Path) -> + case string:slice(Path, 0, 1) of + "/" -> Path; + _ -> "/" ++ Path + end. +drop_trailing_path_separator(Path) when is_binary(Path) -> + drop_trailing_path_separator(binary:bin_to_list(Path)); +drop_trailing_path_separator("") -> ""; +drop_trailing_path_separator(Path) when is_list(Path) -> + case string:slice(Path, string:len(Path)-1, 1) of + "/" -> lists:droplast(Path); + _ -> Path + end. + +-spec get_openid_configuration(DiscoveryEndpoint :: uri_string:uri_string(), + ssl:tls_option() | []) -> {ok, openid_configuration()} | {error, term()}. +get_openid_configuration(DiscoverEndpoint, TLSOptions) -> + rabbit_log:debug("get_openid_configuration from ~p (~p)", [DiscoverEndpoint, + format_ssl_options(TLSOptions)]), + Options = [], + Response = httpc:request(get, {DiscoverEndpoint, []}, TLSOptions, Options), + parse_openid_configuration_response(Response). + +-spec merge_openid_configuration(openid_configuration(), oauth_provider()) -> + oauth_provider(). +merge_openid_configuration(OpenId, OAuthProvider0) -> + OAuthProvider1 = case OpenId#openid_configuration.token_endpoint of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) undefined -> OAuthProvider0; TokenEndpoint -> OAuthProvider0#oauth_provider{token_endpoint = TokenEndpoint} end, +<<<<<<< HEAD OAuthProvider2 = case OpendIdConfiguration#openid_configuration.authorization_endpoint of +======= + OAuthProvider2 = case OpenId#openid_configuration.authorization_endpoint of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) undefined -> OAuthProvider1; AuthorizationEndpoint -> OAuthProvider1#oauth_provider{authorization_endpoint = AuthorizationEndpoint} end, +<<<<<<< HEAD OAuthProvider3 = case OpendIdConfiguration#openid_configuration.end_session_endpoint of +======= + OAuthProvider3 = case OpenId#openid_configuration.end_session_endpoint of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) undefined -> OAuthProvider2; EndSessionEndpoint -> OAuthProvider2#oauth_provider{end_session_endpoint = EndSessionEndpoint} end, +<<<<<<< HEAD case OpendIdConfiguration#openid_configuration.jwks_uri of +======= + case OpenId#openid_configuration.jwks_uri of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) undefined -> OAuthProvider3; JwksUri -> OAuthProvider3#oauth_provider{jwks_uri = JwksUri} @@ -126,7 +217,12 @@ parse_openid_configuration_response({error, Reason}) -> parse_openid_configuration_response({ok,{{_,Code,Reason}, Headers, Body}}) -> map_response_to_openid_configuration(Code, Reason, Headers, Body). map_response_to_openid_configuration(Code, Reason, Headers, Body) -> +<<<<<<< HEAD case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of +======= + case decode_body(proplists:get_value("content-type", Headers, + ?CONTENT_JSON), Body) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {error, {error, InternalError}} -> {error, InternalError}; {error, _} = Error -> @@ -142,13 +238,25 @@ map_to_openid_configuration(Map) -> #openid_configuration{ issuer = maps:get(?RESPONSE_ISSUER, Map), token_endpoint = maps:get(?RESPONSE_TOKEN_ENDPOINT, Map, undefined), +<<<<<<< HEAD authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, Map, undefined), end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, Map, undefined), +======= + authorization_endpoint = maps:get(?RESPONSE_AUTHORIZATION_ENDPOINT, + Map, undefined), + end_session_endpoint = maps:get(?RESPONSE_END_SESSION_ENDPOINT, + Map, undefined), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) jwks_uri = maps:get(?RESPONSE_JWKS_URI, Map, undefined) }. -spec get_expiration_time(successful_access_token_response()) -> +<<<<<<< HEAD {ok, [{expires_in, integer() }| {exp, integer() }]} | {error, missing_exp_field}. +======= + {ok, [{expires_in, integer() }| {exp, integer() }]} | + {error, missing_exp_field}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) get_expiration_time(#successful_access_token_response{expires_in = ExpiresInSec, access_token = AccessToken}) -> case ExpiresInSec of @@ -168,6 +276,7 @@ update_oauth_provider_endpoints_configuration(OAuthProvider) -> unlock(LockId) end. +<<<<<<< HEAD update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) -> LockId = lock(), try do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) of @@ -210,11 +319,46 @@ do_update_oauth_provider_endpoints_configuration(OAuthProviderId, OAuthProvider) ModifiedOAuthProviders = maps:put(OAuthProviderId, merge_oauth_provider(OAuthProvider, Proplist), OAuthProviders), application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, ModifiedOAuthProviders), +======= +do_update_oauth_provider_endpoints_configuration(OAuthProvider) when + OAuthProvider#oauth_provider.id == root -> + case OAuthProvider#oauth_provider.token_endpoint of + undefined -> do_nothing; + TokenEndpoint -> set_env(token_endpoint, TokenEndpoint) + end, + case OAuthProvider#oauth_provider.authorization_endpoint of + undefined -> do_nothing; + AuthzEndpoint -> set_env(authorization_endpoint, AuthzEndpoint) + end, + case OAuthProvider#oauth_provider.end_session_endpoint of + undefined -> do_nothing; + EndSessionEndpoint -> set_env(end_session_endpoint, EndSessionEndpoint) + end, + case OAuthProvider#oauth_provider.jwks_uri of + undefined -> do_nothing; + JwksUri -> set_env(jwks_uri, JwksUri) + end, + rabbit_log:debug("Updated oauth_provider details: ~p ", + [format_oauth_provider(OAuthProvider)]), + OAuthProvider; + +do_update_oauth_provider_endpoints_configuration(OAuthProvider) -> + OAuthProviderId = OAuthProvider#oauth_provider.id, + OAuthProviders = get_env(oauth_providers, #{}), + Proplist = maps:get(OAuthProviderId, OAuthProviders), + ModifiedOAuthProviders = maps:put(OAuthProviderId, + merge_oauth_provider(OAuthProvider, Proplist), OAuthProviders), + set_env(oauth_providers, ModifiedOAuthProviders), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_log:debug("Replaced oauth_providers "), OAuthProvider. use_global_locks_on_all_nodes() -> +<<<<<<< HEAD case application:get_env(rabbitmq_auth_backend_oauth2, use_global_locks, true) of +======= + case get_env(use_global_locks, true) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true -> {rabbit_nodes:list_running(), rabbit_nodes:lock_retries()}; _ -> {} end. @@ -227,7 +371,12 @@ lock() -> false -> undefined end; {Nodes, Retries} -> +<<<<<<< HEAD case global:set_lock({oauth2_config_lock, rabbitmq_auth_backend_oauth2}, Nodes, Retries) of +======= + case global:set_lock({oauth2_config_lock, rabbitmq_auth_backend_oauth2}, + Nodes, Retries) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true -> rabbitmq_auth_backend_oauth2; false -> undefined end @@ -238,13 +387,21 @@ unlock(LockId) -> undefined -> ok; Value -> case use_global_locks_on_all_nodes() of +<<<<<<< HEAD {} -> global:del_lock({oauth2_config_lock, Value}); {Nodes, _Retries} -> global:del_lock({oauth2_config_lock, Value}, Nodes) +======= + {} -> + global:del_lock({oauth2_config_lock, Value}); + {Nodes, _Retries} -> + global:del_lock({oauth2_config_lock, Value}, Nodes) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end. -spec get_oauth_provider(list()) -> {ok, oauth_provider()} | {error, any()}. get_oauth_provider(ListOfRequiredAttributes) -> +<<<<<<< HEAD case application:get_env(rabbitmq_auth_backend_oauth2, default_oauth_provider) of undefined -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes); {ok, DefaultOauthProviderId} -> @@ -255,10 +412,50 @@ get_oauth_provider(ListOfRequiredAttributes) -> get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> OAuthProvider = lookup_oauth_provider_from_keyconfig(), rabbit_log:debug("Using oauth_provider ~s from keyconfig", [format_oauth_provider(OAuthProvider)]), +======= + case get_env(default_oauth_provider) of + undefined -> get_root_oauth_provider(ListOfRequiredAttributes); + DefaultOauthProviderId -> + rabbit_log:debug("Using default_oauth_provider ~p", + [DefaultOauthProviderId]), + get_oauth_provider(DefaultOauthProviderId, ListOfRequiredAttributes) + end. + +-spec download_oauth_provider(oauth_provider()) -> {ok, oauth_provider()} | + {error, any()}. +download_oauth_provider(OAuthProvider) -> + case OAuthProvider#oauth_provider.discovery_endpoint of + undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; + URL -> + rabbit_log:debug("Downloading oauth_provider using ~p ", [URL]), + case get_openid_configuration(URL, get_ssl_options_if_any(OAuthProvider)) of + {ok, OpenIdConfiguration} -> + {ok, update_oauth_provider_endpoints_configuration( + merge_openid_configuration(OpenIdConfiguration, OAuthProvider))}; + {error, _} = Error2 -> Error2 + end + end. + +ensure_oauth_provider_has_attributes(OAuthProvider, ListOfRequiredAttributes) -> + case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of + [] -> + rabbit_log:debug("Resolved oauth_provider ~p", + [format_oauth_provider(OAuthProvider)]), + {ok, OAuthProvider}; + _ = Attrs -> + {error, {missing_oauth_provider_attributes, Attrs}} + end. + +get_root_oauth_provider(ListOfRequiredAttributes) -> + OAuthProvider = lookup_root_oauth_provider(), + rabbit_log:debug("Using root oauth_provider ~p", + [format_oauth_provider(OAuthProvider)]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case find_missing_attributes(OAuthProvider, ListOfRequiredAttributes) of [] -> {ok, OAuthProvider}; _ = MissingAttributes -> +<<<<<<< HEAD rabbit_log:debug("OauthProvider has following missing attributes ~p", [MissingAttributes]), Result2 = case OAuthProvider#oauth_provider.issuer of undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; @@ -281,10 +478,21 @@ get_oauth_provider_from_keyconfig(ListOfRequiredAttributes) -> {error, {missing_oauth_provider_attributes, Attrs}} end; {error, _} = Error3 -> Error3 +======= + rabbit_log:debug("Looking up missing attributes ~p ...", + [MissingAttributes]), + case download_oauth_provider(OAuthProvider) of + {ok, OAuthProvider2} -> + ensure_oauth_provider_has_attributes(OAuthProvider2, + ListOfRequiredAttributes); + {error, _} = Error3 -> + Error3 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end. +<<<<<<< HEAD -spec get_oauth_provider(oauth_provider_id(), list()) -> {ok, oauth_provider()} | {error, any()}. get_oauth_provider(root, ListOfRequiredAttributes) -> get_oauth_provider(ListOfRequiredAttributes); @@ -294,6 +502,22 @@ get_oauth_provider(OAuth2ProviderId, ListOfRequiredAttributes) when is_list(OAut get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAuthProviderId) -> rabbit_log:debug("get_oauth_provider ~p with at least these attributes: ~p", [OAuthProviderId, ListOfRequiredAttributes]), +======= +-spec get_oauth_provider(oauth_provider_id(), list()) -> {ok, oauth_provider()} | + {error, any()}. +get_oauth_provider(root, ListOfRequiredAttributes) -> + get_oauth_provider(ListOfRequiredAttributes); + +get_oauth_provider(OAuth2ProviderId, ListOfRequiredAttributes) + when is_list(OAuth2ProviderId) -> + get_oauth_provider(list_to_binary(OAuth2ProviderId), + ListOfRequiredAttributes); + +get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) + when is_binary(OAuthProviderId) -> + rabbit_log:debug("get_oauth_provider ~p with at least these attributes: ~p", + [OAuthProviderId, ListOfRequiredAttributes]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case lookup_oauth_provider_config(OAuthProviderId) of {error, _} = Error0 -> rabbit_log:debug("Failed to find oauth_provider ~p configuration due to ~p", @@ -308,6 +532,7 @@ get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAu {ok, OAuthProvider}; _ = MissingAttributes -> rabbit_log:debug("OauthProvider has following missing attributes ~p", [MissingAttributes]), +<<<<<<< HEAD Result2 = case OAuthProvider#oauth_provider.issuer of undefined -> {error, {missing_oauth_provider_attributes, [issuer]}}; Issuer -> @@ -330,6 +555,14 @@ get_oauth_provider(OAuthProviderId, ListOfRequiredAttributes) when is_binary(OAu {error, {missing_oauth_provider_attributes, Attrs}} end; {error, _} = Error3 -> Error3 +======= + case download_oauth_provider(OAuthProvider) of + {ok, OAuthProvider2} -> + ensure_oauth_provider_has_attributes(OAuthProvider2, + ListOfRequiredAttributes); + {error, _} = Error3 -> + Error3 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end end. @@ -357,6 +590,7 @@ find_missing_attributes(#oauth_provider{} = OAuthProvider, RequiredAttributes) - Filtered = filter_undefined_props(PropList), intersection(Filtered, RequiredAttributes). +<<<<<<< HEAD lookup_oauth_provider_from_keyconfig() -> Issuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined), TokenEndpoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined), @@ -370,6 +604,21 @@ lookup_oauth_provider_from_keyconfig() -> token_endpoint = TokenEndpoint, authorization_endpoint = AuthorizationEndpoint, end_session_endpoint = EndSessionEndpoint, +======= +lookup_root_oauth_provider() -> + Map = maps:from_list(get_env(key_config, [])), + Issuer = get_env(issuer), + DiscoverEndpoint = build_openid_discovery_endpoint(Issuer, + get_env(discovery_endpoint_path), get_env(discovery_endpoint_params)), + #oauth_provider{ + id = root, + issuer = Issuer, + discovery_endpoint = DiscoverEndpoint, + jwks_uri = get_env(jwks_uri, maps:get(jwks_url, Map, undefined)), + token_endpoint = get_env(token_endpoint), + authorization_endpoint = get_env(authorization_endpoint), + end_session_endpoint = get_env(end_session_endpoint), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ssl_options = extract_ssl_options_as_list(Map) }. @@ -410,7 +659,12 @@ extract_ssl_options_as_list(Map) -> ++ case maps:get(hostname_verification, Map, none) of wildcard -> +<<<<<<< HEAD [{customize_hostname_check, [{match_fun, public_key:pkix_verify_hostname_match_fun(https)}]}]; +======= + [{customize_hostname_check, [{match_fun, + public_key:pkix_verify_hostname_match_fun(https)}]}]; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) none -> [] end. @@ -418,7 +672,12 @@ extract_ssl_options_as_list(Map) -> % Replace peer_verification with verify to make it more consistent with other % ssl_options in RabbitMQ and Erlang's ssl options % Eventually, peer_verification will be removed. For now, both are allowed +<<<<<<< HEAD -spec get_verify_or_peer_verification(#{atom() => any()}, verify_none | verify_peer ) -> verify_none | verify_peer. +======= +-spec get_verify_or_peer_verification(#{atom() => + any()}, verify_none | verify_peer ) -> verify_none | verify_peer. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) get_verify_or_peer_verification(Ssl_options, Default) -> case maps:get(verify, Ssl_options, undefined) of undefined -> @@ -430,14 +689,25 @@ get_verify_or_peer_verification(Ssl_options, Default) -> end. lookup_oauth_provider_config(OAuth2ProviderId) -> +<<<<<<< HEAD case application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers) of undefined -> {error, oauth_providers_not_found}; {ok, MapOfProviders} when is_map(MapOfProviders) -> +======= + case get_env(oauth_providers) of + undefined -> {error, oauth_providers_not_found}; + MapOfProviders when is_map(MapOfProviders) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case maps:get(OAuth2ProviderId, MapOfProviders, undefined) of undefined -> {error, {oauth_provider_not_found, OAuth2ProviderId}}; OAuthProvider -> +<<<<<<< HEAD ensure_oauth_provider_has_id_property(OAuth2ProviderId, OAuthProvider) +======= + ensure_oauth_provider_has_id_property(OAuth2ProviderId, + OAuthProvider) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end; _ -> {error, invalid_oauth_provider_configuration} end. @@ -448,6 +718,7 @@ ensure_oauth_provider_has_id_property(OAuth2ProviderId, OAuth2Provider) -> end. build_access_token_request_body(Request) -> +<<<<<<< HEAD uri_string:compose_query([ grant_type_request_parameter(?CLIENT_CREDENTIALS_GRANT_TYPE), client_id_request_parameter(Request#access_token_request.client_id), @@ -475,6 +746,52 @@ scope_request_parameter_or_default(Scope, Default) -> undefined -> Default; <<>> -> Default; Scope -> [{?REQUEST_SCOPE, Scope}] +======= + uri_string:compose_query( + append_extra_parameters(Request, + append_scope_request_parameter(Request#access_token_request.scope, [ + grant_type_request_parameter(?CLIENT_CREDENTIALS_GRANT_TYPE), + client_id_request_parameter( + Request#access_token_request.client_id), + client_secret_request_parameter( + Request#access_token_request.client_secret)]))). + +build_refresh_token_request_body(Request) -> + uri_string:compose_query( + append_scope_request_parameter(Request#refresh_token_request.scope, [ + grant_type_request_parameter(?REFRESH_TOKEN_GRANT_TYPE), + refresh_token_request_parameter(Request), + client_id_request_parameter(Request#refresh_token_request.client_id), + client_secret_request_parameter( + Request#refresh_token_request.client_secret)])). + +grant_type_request_parameter(Type) -> + {?REQUEST_GRANT_TYPE, Type}. + +client_id_request_parameter(ClientId) -> + {?REQUEST_CLIENT_ID, + binary_to_list(ClientId)}. + +client_secret_request_parameter(ClientSecret) -> + {?REQUEST_CLIENT_SECRET, + binary_to_list(ClientSecret)}. + +refresh_token_request_parameter(Request) -> + {?REQUEST_REFRESH_TOKEN, Request#refresh_token_request.refresh_token}. + +append_scope_request_parameter(Scope, QueryList) -> + case Scope of + undefined -> QueryList; + <<>> -> QueryList; + Scope -> [{?REQUEST_SCOPE, Scope} | QueryList] + end. + +append_extra_parameters(Request, QueryList) -> + case Request#access_token_request.extra_parameters of + undefined -> QueryList; + [] -> QueryList; + Params -> Params ++ QueryList +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. get_ssl_options_if_any(OAuthProvider) -> @@ -491,8 +808,14 @@ get_timeout_of_default(Timeout) -> is_json(?CONTENT_JSON) -> true; is_json(_) -> false. +<<<<<<< HEAD -spec decode_body(string(), string() | binary() | term()) -> 'false' | 'null' | 'true' | binary() | [any()] | number() | map() | {error, term()}. +======= +-spec decode_body(string(), string() | binary() | term()) -> + 'false' | 'null' | 'true' | binary() | [any()] | number() | map() | + {error, term()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) decode_body(_, []) -> []; decode_body(?CONTENT_JSON, Body) -> @@ -521,6 +844,7 @@ map_to_unsuccessful_access_token_response(Map) -> error_description = maps:get(?RESPONSE_ERROR_DESCRIPTION, Map, undefined) }. map_to_oauth_provider(PropList) when is_list(PropList) -> +<<<<<<< HEAD #oauth_provider{ id = proplists:get_value(id, PropList), issuer = proplists:get_value(issuer, PropList), @@ -529,6 +853,30 @@ map_to_oauth_provider(PropList) when is_list(PropList) -> end_session_endpoint = proplists:get_value(end_session_endpoint, PropList, undefined), jwks_uri = proplists:get_value(jwks_uri, PropList, undefined), ssl_options = extract_ssl_options_as_list(maps:from_list(proplists:get_value(https, PropList, []))) +======= + Issuer = proplists:get_value(issuer, PropList), + DiscoveryEndpoint = build_openid_discovery_endpoint(Issuer, + proplists:get_value(discovery_endpoint_path, PropList), + proplists:get_value(discovery_endpoint_params, PropList)), + #oauth_provider{ + id = + proplists:get_value(id, PropList), + issuer = + Issuer, + discovery_endpoint = + DiscoveryEndpoint, + token_endpoint = + proplists:get_value(token_endpoint, PropList), + authorization_endpoint = + proplists:get_value(authorization_endpoint, PropList, undefined), + end_session_endpoint = + proplists:get_value(end_session_endpoint, PropList, undefined), + jwks_uri = + proplists:get_value(jwks_uri, PropList, undefined), + ssl_options = + extract_ssl_options_as_list(maps:from_list( + proplists:get_value(https, PropList, []))) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }. map_to_access_token_response(Code, Reason, Headers, Body) -> case decode_body(proplists:get_value("content-type", Headers, ?CONTENT_JSON), Body) of @@ -557,27 +905,57 @@ format_ssl_options(TlsOptions) -> [] -> 0; Certs -> length(Certs) end, +<<<<<<< HEAD io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, crl_check: ~p, " ++ "depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [ +======= + lists:flatten(io_lib:format("{verify: ~p, fail_if_no_peer_cert: ~p, " ++ + "crl_check: ~p, depth: ~p, cacertfile: ~p, cacerts(count): ~p }", [ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) proplists:get_value(verify, TlsOptions), proplists:get_value(fail_if_no_peer_cert, TlsOptions), proplists:get_value(crl_check, TlsOptions), proplists:get_value(depth, TlsOptions), proplists:get_value(cacertfile, TlsOptions), +<<<<<<< HEAD CaCertsCount]). +======= + CaCertsCount])). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) format_oauth_provider_id(root) -> ""; format_oauth_provider_id(Id) -> binary_to_list(Id). -spec format_oauth_provider(oauth_provider()) -> string(). format_oauth_provider(OAuthProvider) -> +<<<<<<< HEAD io_lib:format("{id: ~p, issuer: ~p, token_endpoint: ~p, " ++ "authorization_endpoint: ~p, end_session_endpoint: ~p, " ++ "jwks_uri: ~p, ssl_options: ~s }", [ format_oauth_provider_id(OAuthProvider#oauth_provider.id), OAuthProvider#oauth_provider.issuer, +======= + lists:flatten(io_lib:format("{id: ~p, issuer: ~p, discovery_endpoint: ~p, " ++ + " token_endpoint: ~p, " ++ + "authorization_endpoint: ~p, end_session_endpoint: ~p, " ++ + "jwks_uri: ~p, ssl_options: ~p }", [ + format_oauth_provider_id(OAuthProvider#oauth_provider.id), + OAuthProvider#oauth_provider.issuer, + OAuthProvider#oauth_provider.discovery_endpoint, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider#oauth_provider.token_endpoint, OAuthProvider#oauth_provider.authorization_endpoint, OAuthProvider#oauth_provider.end_session_endpoint, OAuthProvider#oauth_provider.jwks_uri, +<<<<<<< HEAD format_ssl_options(OAuthProvider#oauth_provider.ssl_options)]). +======= + format_ssl_options(OAuthProvider#oauth_provider.ssl_options)])). + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl index ecc5aa733bef..6b417bd46667 100644 --- a/deps/oauth2_client/test/system_SUITE.erl +++ b/deps/oauth2_client/test/system_SUITE.erl @@ -11,6 +11,12 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("oauth2_client.hrl"). +<<<<<<< HEAD +======= +-import(oauth2_client, [ + build_openid_discovery_endpoint/3 +]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -compile(export_all). @@ -31,11 +37,22 @@ all() -> groups() -> [ +<<<<<<< HEAD {with_all_oauth_provider_settings, [], [ {group, verify_get_oauth_provider} ]}, {without_all_oauth_providers_settings, [], [ {group, verify_get_oauth_provider} +======= + + {with_all_oauth_provider_settings, [], [ + {group, verify_get_oauth_provider}, + jwks_uri_takes_precedence_over_jwks_url, + jwks_url_is_used_in_absense_of_jwks_uri + ]}, + {without_all_oauth_providers_settings, [], [ + {group, verify_get_oauth_provider} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {verify_openid_configuration, [], [ get_openid_configuration, @@ -54,7 +71,11 @@ groups() -> expiration_time_in_token ]}, {verify_get_oauth_provider, [], [ +<<<<<<< HEAD get_oauth_provider, +======= + get_oauth_provider, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {with_default_oauth_provider, [], [ get_oauth_provider ]}, @@ -75,10 +96,23 @@ groups() -> init_per_suite(Config) -> [ +<<<<<<< HEAD {denies_access_token, [ {token_endpoint, denies_access_token_expectation()} ]}, {auth_server_error, [ {token_endpoint, auth_server_error_when_access_token_request_expectation()} ]}, {non_json_payload, [ {token_endpoint, non_json_payload_when_access_token_request_expectation()} ]}, {grants_refresh_token, [ {token_endpoint, grants_refresh_token_expectation()} ]} +======= + {jwks_url, build_jwks_uri("https", "/certs4url")}, + {jwks_uri, build_jwks_uri("https")}, + {denies_access_token, [ + {token_endpoint, denies_access_token_expectation()} ]}, + {auth_server_error, [ + {token_endpoint, auth_server_error_when_access_token_request_expectation()} ]}, + {non_json_payload, [ + {token_endpoint, non_json_payload_when_access_token_request_expectation()} ]}, + {grants_refresh_token, [ + {token_endpoint, grants_refresh_token_expectation()} ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) | Config]. end_per_suite(Config) -> @@ -90,11 +124,17 @@ init_per_group(https, Config) -> application:ensure_all_started(cowboy), Config0 = rabbit_ct_helpers:run_setup_steps(Config), CertsDir = ?config(rmq_certsdir, Config0), +<<<<<<< HEAD ct:log("certsdir: ~p", [CertsDir]), CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), WrongCaCertFile = filename:join([CertsDir, "server", "server.pem"]), [{group, https}, {certsDir, CertsDir}, +======= + CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), + WrongCaCertFile = filename:join([CertsDir, "server", "server.pem"]), + [{group, https}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {oauth_provider_id, <<"uaa">>}, {oauth_provider, build_https_oauth_provider(<<"uaa">>, CaCertFile)}, {oauth_provider_with_issuer, keep_only_issuer_and_ssl_options( @@ -119,18 +159,30 @@ init_per_group(openid_configuration_with_path, Config) -> init_per_group(with_all_oauth_provider_settings, Config) -> Config0 = rabbit_ct_helpers:run_setup_steps(Config), +<<<<<<< HEAD CertsDir = ?config(certsDir, Config0), CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), ct:log("certsdir: ~p", [CertsDir]), +======= + CertsDir = ?config(rmq_certsdir, Config0), + CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [{with_all_oauth_provider_settings, true}, {oauth_provider_id, <<"uaa">>}, {oauth_provider, build_https_oauth_provider(<<"uaa">>, CaCertFile)} | Config0]; init_per_group(without_all_oauth_providers_settings, Config) -> Config0 = rabbit_ct_helpers:run_setup_steps(Config), +<<<<<<< HEAD CertsDir = ?config(certsDir, Config0), CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), ct:log("certsdir: ~p", [CertsDir]), +======= + CertsDir = ?config(rmq_certsdir, Config0), + CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [{with_all_oauth_provider_settings, false}, {oauth_provider_id, <<"uaa">>}, {oauth_provider, keep_only_issuer_and_ssl_options( @@ -149,7 +201,10 @@ init_per_group(_, Config) -> get_http_oauth_server_expectations(TestCase, Config) -> case ?config(TestCase, Config) of undefined -> +<<<<<<< HEAD ct:log("get_openid_configuration_http_expectation : ~p", [get_openid_configuration_http_expectation(TestCase)]), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [ {token_endpoint, build_http_mock_behaviour(build_http_access_token_request(), build_http_200_access_token_response())}, {get_openid_configuration, get_openid_configuration_http_expectation(TestCase)} @@ -198,13 +253,18 @@ configure_all_oauth_provider_settings(Config) -> OAuthProvider#oauth_provider.end_session_endpoint), application:set_env(rabbitmq_auth_backend_oauth2, authorization_endpoint, OAuthProvider#oauth_provider.authorization_endpoint), +<<<<<<< HEAD KeyConfig = [ { jwks_url, OAuthProvider#oauth_provider.jwks_uri } ] ++ +======= + KeyConfig0 = +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case OAuthProvider#oauth_provider.ssl_options of undefined -> []; _ -> [ {peer_verification, proplists:get_value(verify, OAuthProvider#oauth_provider.ssl_options) }, +<<<<<<< HEAD {cacertfile, proplists:get_value(cacertfile, OAuthProvider#oauth_provider.ssl_options) } ] @@ -213,6 +273,33 @@ configure_all_oauth_provider_settings(Config) -> configure_minimum_oauth_provider_settings(Config) -> OAuthProvider = ?config(oauth_provider_with_issuer, Config), +======= + {cacertfile, proplists:get_value(cacertfile, + OAuthProvider#oauth_provider.ssl_options) } + ] + end, + KeyConfig = + case ?config(jwks_uri_type_of_config, Config) of + undefined -> + application:set_env(rabbitmq_auth_backend_oauth2, jwks_uri, + OAuthProvider#oauth_provider.jwks_uri), + KeyConfig0; + only_jwks_uri -> + application:set_env(rabbitmq_auth_backend_oauth2, jwks_uri, + OAuthProvider#oauth_provider.jwks_uri), + KeyConfig0; + only_jwks_url -> + [ { jwks_url, ?config(jwks_url, Config) } | KeyConfig0 ]; + both -> + application:set_env(rabbitmq_auth_backend_oauth2, jwks_uri, + OAuthProvider#oauth_provider.jwks_uri), + [ { jwks_url, ?config(jwks_url, Config) } | KeyConfig0 ] + end, + application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig). + +configure_minimum_oauth_provider_settings(Config) -> + OAuthProvider = ?config(oauth_provider, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProviders = #{ ?config(oauth_provider_id, Config) => oauth_provider_to_proplist(OAuthProvider) }, application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, @@ -232,9 +319,24 @@ configure_minimum_oauth_provider_settings(Config) -> end, application:set_env(rabbitmq_auth_backend_oauth2, key_config, KeyConfig). +<<<<<<< HEAD init_per_testcase(TestCase, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, use_global_locks, false), +======= +init_per_testcase(TestCase, Config0) -> + application:set_env(rabbitmq_auth_backend_oauth2, use_global_locks, false), + + Config = [case TestCase of + jwks_url_is_used_in_absense_of_jwks_uri -> + {jwks_uri_type_of_config, only_jwks_url}; + jwks_uri_takes_precedence_over_jwks_url -> + {jwks_uri_type_of_config, both}; + _ -> + {jwks_uri_type_of_config, only_jwks_uri} + end | Config0], + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case ?config(with_all_oauth_provider_settings, Config) of false -> configure_minimum_oauth_provider_settings(Config); true -> configure_all_oauth_provider_settings(Config); @@ -246,8 +348,13 @@ init_per_testcase(TestCase, Config) -> case ?config(group, Config) of https -> +<<<<<<< HEAD start_https_oauth_server(?AUTH_PORT, ?config(certsDir, Config), ListOfExpectations); +======= + start_https_oauth_server(?AUTH_PORT, ?config(rmq_certsdir, Config), + ListOfExpectations); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> do_nothing end, @@ -256,6 +363,10 @@ init_per_testcase(TestCase, Config) -> end_per_testcase(_, Config) -> application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), application:unset_env(rabbitmq_auth_backend_oauth2, issuer), +<<<<<<< HEAD +======= + application:unset_env(rabbitmq_auth_backend_oauth2, jwks_uri), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) application:unset_env(rabbitmq_auth_backend_oauth2, token_endpoint), application:unset_env(rabbitmq_auth_backend_oauth2, authorization_endpoint), application:unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint), @@ -263,8 +374,11 @@ end_per_testcase(_, Config) -> case ?config(group, Config) of https -> stop_https_auth_server(); +<<<<<<< HEAD without_all_oauth_providers_settings -> stop_https_auth_server(); +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> do_nothing end, @@ -280,11 +394,24 @@ end_per_group(with_default_oauth_provider, Config) -> end_per_group(_, Config) -> Config. +<<<<<<< HEAD +======= +build_openid_discovery_endpoint(Issuer) -> + build_openid_discovery_endpoint(Issuer, undefined, undefined). + +build_openid_discovery_endpoint(Issuer, Path) -> + build_openid_discovery_endpoint(Issuer, Path, undefined). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) get_openid_configuration(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, ActualOpenId} = oauth2_client:get_openid_configuration( +<<<<<<< HEAD build_issuer("https"), +======= + build_openid_discovery_endpoint(build_issuer("https")), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, ActualOpenId). @@ -306,7 +433,11 @@ get_openid_configuration_returns_partial_payload(Config) -> SslOptions = [{ssl, ExpectedOAuthProvider0#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( +<<<<<<< HEAD build_issuer("https"), +======= + build_openid_discovery_endpoint(build_issuer("https")), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). @@ -315,7 +446,11 @@ get_openid_configuration_using_path(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( +<<<<<<< HEAD build_issuer("https", ?ISSUER_PATH), +======= + build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) SslOptions), ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId,Actual). @@ -323,18 +458,28 @@ get_openid_configuration_using_path_and_custom_endpoint(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( +<<<<<<< HEAD build_issuer("https", ?ISSUER_PATH), ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT, SslOptions), +======= + build_openid_discovery_endpoint(build_issuer("https", ?ISSUER_PATH), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). get_openid_configuration_using_custom_endpoint(Config) -> ExpectedOAuthProvider = ?config(oauth_provider, Config), SslOptions = [{ssl, ExpectedOAuthProvider#oauth_provider.ssl_options}], {ok, Actual} = oauth2_client:get_openid_configuration( +<<<<<<< HEAD build_issuer("https"), ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT, SslOptions), +======= + build_openid_discovery_endpoint(build_issuer("https"), + ?CUSTOM_OPENID_CONFIGURATION_ENDPOINT), SslOptions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ExpectedOpenId = map_oauth_provider_to_openid_configuration(ExpectedOAuthProvider), assertOpenIdConfiguration(ExpectedOpenId, Actual). @@ -399,6 +544,26 @@ grants_access_token(Config) -> ?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType), ?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken). +<<<<<<< HEAD +======= +grants_access_token_optional_parameters(Config) -> + #{request := #{parameters := Parameters}, + response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } + = lookup_expectation(token_endpoint, Config), + + AccessTokenRequest0 = build_access_token_request(Parameters), + AccessTokenRequest = AccessTokenRequest0#access_token_request{ + scope = "some-scope", + extra_parameters = [{"param1", "value1"}] + }, + {ok, #successful_access_token_response{access_token = AccessToken, + token_type = TokenType} } = + oauth2_client:get_access_token(?config(oauth_provider, Config), + AccessTokenRequest), + ?assertEqual(proplists:get_value(token_type, JsonPayload), TokenType), + ?assertEqual(proplists:get_value(access_token, JsonPayload), AccessToken). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) grants_refresh_token(Config) -> #{request := #{parameters := Parameters}, response := [ {code, 200}, {content_type, _CT}, {payload, JsonPayload}] } @@ -447,16 +612,26 @@ ssl_connection_error(Config) -> {error, {failed_connect, _} } = oauth2_client:get_access_token( ?config(oauth_provider_with_wrong_ca, Config), build_access_token_request(Parameters)). +<<<<<<< HEAD verify_get_oauth_provider_returns_oauth_provider_from_key_config() -> +======= +verify_get_oauth_provider_returns_root_oauth_provider() -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, #oauth_provider{id = Id, issuer = Issuer, token_endpoint = TokenEndPoint, jwks_uri = Jwks_uri}} = oauth2_client:get_oauth_provider([issuer, token_endpoint, jwks_uri]), +<<<<<<< HEAD ExpectedIssuer = application:get_env(rabbitmq_auth_backend_oauth2, issuer, undefined), ExpectedTokenEndPoint = application:get_env(rabbitmq_auth_backend_oauth2, token_endpoint, undefined), ExpectedJwks_uri = proplists:get_value(jwks_url, application:get_env(rabbitmq_auth_backend_oauth2, key_config, [])), +======= + ExpectedIssuer = get_env(issuer), + ExpectedTokenEndPoint = get_env(token_endpoint), + ExpectedJwks_uri = get_env(jwks_uri), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(root, Id), ?assertEqual(ExpectedIssuer, Issuer), ?assertEqual(ExpectedTokenEndPoint, TokenEndPoint), @@ -468,15 +643,24 @@ verify_get_oauth_provider_returns_default_oauth_provider(DefaultOAuthProviderId) {ok, OAuthProvider2} = oauth2_client:get_oauth_provider(DefaultOAuthProviderId, [issuer, token_endpoint, jwks_uri]), +<<<<<<< HEAD ct:log("verify_get_oauth_provider_returns_default_oauth_provider ~p vs ~p", [OAuthProvider1, OAuthProvider2]), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(OAuthProvider1, OAuthProvider2). get_oauth_provider(Config) -> case ?config(with_all_oauth_provider_settings, Config) of true -> +<<<<<<< HEAD case application:get_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, undefined) of undefined -> verify_get_oauth_provider_returns_oauth_provider_from_key_config(); +======= + case get_env(default_oauth_provider) of + undefined -> + verify_get_oauth_provider_returns_root_oauth_provider(); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) DefaultOAuthProviderId -> verify_get_oauth_provider_returns_default_oauth_provider(DefaultOAuthProviderId) end; @@ -507,8 +691,12 @@ get_oauth_provider_given_oauth_provider_id(Config) -> [issuer, token_endpoint, jwks_uri, authorization_endpoint, end_session_endpoint]), +<<<<<<< HEAD OAuthProviders = application:get_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{}), +======= + OAuthProviders = get_env(oauth_providers, #{}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ExpectedProvider = maps:get(Id, OAuthProviders, []), ?assertEqual(proplists:get_value(issuer, ExpectedProvider), Issuer), @@ -546,9 +734,27 @@ get_oauth_provider_given_oauth_provider_id(Config) -> Jwks_uri) end. +<<<<<<< HEAD + + +%%% HELPERS +======= +jwks_url_is_used_in_absense_of_jwks_uri(Config) -> + {ok, #oauth_provider{ + jwks_uri = Jwks_uri}} = oauth2_client:get_oauth_provider([jwks_uri]), + ?assertEqual( + proplists:get_value(jwks_url, get_env(key_config, []), undefined), + Jwks_uri). + +jwks_uri_takes_precedence_over_jwks_url(Config) -> + {ok, #oauth_provider{ + jwks_uri = Jwks_uri}} = oauth2_client:get_oauth_provider([jwks_uri]), + ?assertEqual(get_env(jwks_uri), Jwks_uri). %%% HELPERS + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) build_issuer(Scheme) -> build_issuer(Scheme, ""). build_issuer(Scheme, Path) -> @@ -565,10 +771,20 @@ build_token_endpoint_uri(Scheme) -> path => "/token"}). build_jwks_uri(Scheme) -> +<<<<<<< HEAD uri_string:recompose(#{scheme => Scheme, host => "localhost", port => rabbit_data_coercion:to_integer(?AUTH_PORT), path => "/certs"}). +======= + build_jwks_uri(Scheme, "/certs"). + +build_jwks_uri(Scheme, Path) -> + uri_string:recompose(#{scheme => Scheme, + host => "localhost", + port => rabbit_data_coercion:to_integer(?AUTH_PORT), + path => Path}). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) build_access_token_request(Request) -> #access_token_request { @@ -604,11 +820,19 @@ oauth_provider_to_proplist(#oauth_provider{ authorization_endpoint = AuthorizationEndpoint, ssl_options = SslOptions, jwks_uri = Jwks_uri}) -> +<<<<<<< HEAD [ { issuer, Issuer}, {token_endpoint, TokenEndpoint}, {end_session_endpoint, EndSessionEndpoint}, {authorization_endpoint, AuthorizationEndpoint}, { https, +======= + [ {issuer, Issuer}, + {token_endpoint, TokenEndpoint}, + {end_session_endpoint, EndSessionEndpoint}, + {authorization_endpoint, AuthorizationEndpoint}, + {https, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case SslOptions of undefined -> []; Value -> Value @@ -621,8 +845,11 @@ start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations {'_', [{Path, oauth_http_mock, Expected} || #{request := #{path := Path}} = Expected <- Expectations ]} ]), +<<<<<<< HEAD ct:log("start_https_oauth_server with expectation : ~p -> dispatch: ~p . certsDir: ~p", [Expectations, Dispatch, CertsDir]), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, _} = cowboy:start_tls( mock_http_auth_listener, [{port, Port}, @@ -633,8 +860,11 @@ start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations start_https_oauth_server(Port, CertsDir, #{request := #{path := Path}} = Expected) -> Dispatch = cowboy_router:compile([{'_', [{Path, oauth_http_mock, Expected}]}]), +<<<<<<< HEAD ct:log("start_https_oauth_server with expectation : ~p -> dispatch: ~p . certsDir: ~p", [Expected, Dispatch, CertsDir]), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, _} = cowboy:start_tls( mock_http_auth_listener, [{port, Port}, @@ -662,6 +892,14 @@ token(ExpiresIn) -> EncodedToken. +<<<<<<< HEAD +======= +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Default) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Default). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) build_http_mock_behaviour(Request, Response) -> #{request => Request, response => Response}. diff --git a/deps/oauth2_client/test/unit_SUITE.erl b/deps/oauth2_client/test/unit_SUITE.erl index ab632ceedc68..1d45122589b0 100644 --- a/deps/oauth2_client/test/unit_SUITE.erl +++ b/deps/oauth2_client/test/unit_SUITE.erl @@ -15,13 +15,25 @@ -compile(export_all). +<<<<<<< HEAD +======= +-import(oauth2_client, [build_openid_discovery_endpoint/3]). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -define(UTIL_MOD, oauth2_client_test_util). all() -> [ +<<<<<<< HEAD {group, ssl_options}, {group, merge}, {group, get_expiration_time} +======= + build_openid_discovery_endpoint, + {group, ssl_options}, + {group, merge}, + {group, get_expiration_time} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. groups() -> @@ -45,8 +57,43 @@ groups() -> ]} ]. +<<<<<<< HEAD merge_oauth_provider(_) -> OAuthProvider = #oauth_provider{id = "some_id", ssl_options = [ {verify, verify_none} ]}, +======= +build_openid_discovery_endpoint(_) -> + Issuer = "https://issuer", + ?assertEqual(Issuer ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(Issuer, undefined, undefined)), + + IssuerWithPath = "https://issuer/v2", + ?assertEqual(IssuerWithPath ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(IssuerWithPath, undefined, undefined)), + + IssuerWithPathAndExtraPathSeparator = "https://issuer/v2/", + ?assertEqual("https://issuer/v2" ++ ?DEFAULT_OPENID_CONFIGURATION_PATH, + build_openid_discovery_endpoint(IssuerWithPathAndExtraPathSeparator, + undefined, undefined)), + + IssuerWithPath = "https://issuer/v2", + CustomPath = "/.well-known/other", + ?assertEqual(IssuerWithPath ++ CustomPath, + build_openid_discovery_endpoint(IssuerWithPath, CustomPath, undefined)), + + IssuerWithPath = "https://issuer/v2", + CustomPath = "/.well-known/other", + WithParams = [{"param1", "v1"}, {"param2", "v2"}], + ?assertEqual("https://issuer/v2/.well-known/other?param1=v1¶m2=v2", + build_openid_discovery_endpoint(IssuerWithPath, CustomPath, WithParams)). + + +merge_oauth_provider(_) -> + OAuthProvider = #oauth_provider{ + id = "some_id", + issuer = "https://issuer", + discovery_endpoint = "https://issuer/.well-known/openid_configuration", + ssl_options = [ {verify, verify_none} ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Proplist = [], Proplist1 = oauth2_client:merge_oauth_provider(OAuthProvider, Proplist), ?assertEqual([], Proplist), @@ -74,11 +121,33 @@ merge_oauth_provider(_) -> {end_session_endpoint, OAuthProvider4#oauth_provider.end_session_endpoint}, {authorization_endpoint, OAuthProvider4#oauth_provider.authorization_endpoint}, {token_endpoint, OAuthProvider4#oauth_provider.token_endpoint}], +<<<<<<< HEAD Proplist5). merge_openid_configuration(_) -> OpenIdConfiguration = #openid_configuration{}, OAuthProvider = #oauth_provider{id = "some_id", ssl_options = [ {verify, verify_none} ]}, +======= + Proplist5), + + % ensure id, issuer, ssl_options and discovery_endpoint are not affected + ?assertEqual(OAuthProvider#oauth_provider.id, + OAuthProvider4#oauth_provider.id), + ?assertEqual(OAuthProvider#oauth_provider.issuer, + OAuthProvider4#oauth_provider.issuer), + ?assertEqual(OAuthProvider#oauth_provider.discovery_endpoint, + OAuthProvider4#oauth_provider.discovery_endpoint), + ?assertEqual(OAuthProvider#oauth_provider.ssl_options, + OAuthProvider4#oauth_provider.ssl_options). + +merge_openid_configuration(_) -> + OpenIdConfiguration = #openid_configuration{}, + OAuthProvider = #oauth_provider{ + id = "some_id", + issuer = "https://issuer", + discovery_endpoint = "https://issuer/.well-known/openid_configuration", + ssl_options = [ {verify, verify_none} ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider1 = oauth2_client:merge_openid_configuration( OpenIdConfiguration, OAuthProvider), ?assertEqual(OAuthProvider#oauth_provider.id, OAuthProvider1#oauth_provider.id), @@ -125,7 +194,21 @@ merge_openid_configuration(_) -> ?assertEqual(OpenIdConfiguration2#openid_configuration.end_session_endpoint, OAuthProvider5#oauth_provider.end_session_endpoint), ?assertEqual(OpenIdConfiguration1#openid_configuration.jwks_uri, +<<<<<<< HEAD OAuthProvider5#oauth_provider.jwks_uri). +======= + OAuthProvider5#oauth_provider.jwks_uri), + + % ensure id, issuer, ssl_options and discovery_endpoint are not affected + ?assertEqual(OAuthProvider#oauth_provider.id, + OAuthProvider5#oauth_provider.id), + ?assertEqual(OAuthProvider#oauth_provider.issuer, + OAuthProvider5#oauth_provider.issuer), + ?assertEqual(OAuthProvider#oauth_provider.discovery_endpoint, + OAuthProvider5#oauth_provider.discovery_endpoint), + ?assertEqual(OAuthProvider#oauth_provider.ssl_options, + OAuthProvider5#oauth_provider.ssl_options). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) no_ssl_options_triggers_verify_peer(_) -> diff --git a/deps/rabbit/BUILD.bazel b/deps/rabbit/BUILD.bazel index 1b5d65cd4fed..0c0db1e98baf 100644 --- a/deps/rabbit/BUILD.bazel +++ b/deps/rabbit/BUILD.bazel @@ -462,6 +462,16 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( +<<<<<<< HEAD +======= + name = "msg_size_metrics_SUITE", + runtime_deps = [ + "//deps/rabbitmq_amqp_client:erlang_app", + ], +) + +rabbitmq_integration_suite( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "list_consumers_sanity_check_SUITE", size = "medium", ) @@ -857,6 +867,15 @@ rabbitmq_integration_suite( rabbitmq_integration_suite( name = "topic_permission_SUITE", size = "medium", +<<<<<<< HEAD +======= + additional_beam = [ + ":test_amqp_utils_beam", + ], + runtime_deps = [ + "//deps/rabbitmq_amqp_client:erlang_app", + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) rabbitmq_integration_suite( @@ -977,6 +996,14 @@ rabbitmq_integration_suite( ) rabbitmq_suite( +<<<<<<< HEAD +======= + name = "unit_msg_size_metrics_SUITE", + size = "small", +) + +rabbitmq_suite( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "unit_operator_policy_SUITE", size = "small", deps = [ @@ -1197,6 +1224,10 @@ rabbitmq_integration_suite( name = "amqp_client_SUITE", size = "large", additional_beam = [ +<<<<<<< HEAD +======= + ":test_amqp_utils_beam", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ":test_event_recorder_beam", ], shard_count = 3, @@ -1206,6 +1237,19 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( +<<<<<<< HEAD +======= + name = "amqp_filtex_SUITE", + additional_beam = [ + ":test_amqp_utils_beam", + ], + runtime_deps = [ + "//deps/rabbitmq_amqp_client:erlang_app", + ], +) + +rabbitmq_integration_suite( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "amqp_proxy_protocol_SUITE", size = "medium", ) @@ -1225,6 +1269,10 @@ rabbitmq_integration_suite( rabbitmq_integration_suite( name = "amqp_auth_SUITE", additional_beam = [ +<<<<<<< HEAD +======= + ":test_amqp_utils_beam", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ":test_event_recorder_beam", ], shard_count = 2, @@ -1235,6 +1283,12 @@ rabbitmq_integration_suite( rabbitmq_integration_suite( name = "amqp_address_SUITE", +<<<<<<< HEAD +======= + additional_beam = [ + ":test_amqp_utils_beam", + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) shard_count = 2, runtime_deps = [ "//deps/rabbitmq_amqp_client:erlang_app", @@ -1348,6 +1402,10 @@ eunit( ":test_clustering_utils_beam", ":test_event_recorder_beam", ":test_rabbit_ct_hook_beam", +<<<<<<< HEAD +======= + ":test_amqp_utils_beam", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ], target = ":test_erlang_app", test_env = { diff --git a/deps/rabbit/Makefile b/deps/rabbit/Makefile index 79ad84990739..31b57306632f 100644 --- a/deps/rabbit/Makefile +++ b/deps/rabbit/Makefile @@ -260,7 +260,11 @@ define ct_master.erl endef PARALLEL_CT_SET_1_A = amqp_client unit_cluster_formation_locking_mocks unit_cluster_formation_sort_nodes unit_collections unit_config_value_encryption unit_connection_tracking +<<<<<<< HEAD PARALLEL_CT_SET_1_B = amqp_address amqp_auth amqp_credit_api_v2 amqp_system signal_handling single_active_consumer unit_access_control_authn_authz_context_propagation unit_access_control_credential_validation unit_amqp091_content_framing unit_amqp091_server_properties unit_app_management +======= +PARALLEL_CT_SET_1_B = amqp_address amqp_auth amqp_credit_api_v2 amqp_filtex amqp_system signal_handling single_active_consumer unit_access_control_authn_authz_context_propagation unit_access_control_credential_validation unit_amqp091_content_framing unit_amqp091_server_properties unit_app_management +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) PARALLEL_CT_SET_1_C = amqp_proxy_protocol amqpl_consumer_ack amqpl_direct_reply_to backing_queue bindings rabbit_db_maintenance rabbit_db_msup rabbit_db_policy rabbit_db_queue rabbit_db_topic_exchange rabbit_direct_reply_to_prop cluster_limit cluster_minority term_to_binary_compat_prop topic_permission transactions unicode unit_access_control PARALLEL_CT_SET_1_D = amqqueue_backward_compatibility channel_interceptor channel_operation_timeout classic_queue classic_queue_prop config_schema peer_discovery_dns peer_discovery_tmp_hidden_node per_node_limit per_user_connection_channel_limit @@ -276,7 +280,11 @@ PARALLEL_CT_SET_3_D = metadata_store_phase1 metrics mirrored_supervisor msg_stor PARALLEL_CT_SET_4_A = clustering_events rabbit_local_random_exchange rabbit_message_interceptor rabbitmq_4_0_deprecations unit_pg_local unit_plugin_directories unit_plugin_versioning unit_policy_validators unit_priority_queue PARALLEL_CT_SET_4_B = per_user_connection_tracking per_vhost_connection_limit rabbit_fifo_dlx_integration rabbit_fifo_int +<<<<<<< HEAD PARALLEL_CT_SET_4_C = per_vhost_msg_store per_vhost_queue_limit priority_queue upgrade_preparation vhost +======= +PARALLEL_CT_SET_4_C = msg_size_metrics unit_msg_size_metrics per_vhost_msg_store per_vhost_queue_limit priority_queue upgrade_preparation vhost +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) PARALLEL_CT_SET_4_D = per_user_connection_channel_tracking product_info publisher_confirms_parallel queue_type rabbitmq_queues_cli_integration rabbitmqctl_integration rabbitmqctl_shutdown routing PARALLEL_CT_SET_1 = $(sort $(PARALLEL_CT_SET_1_A) $(PARALLEL_CT_SET_1_B) $(PARALLEL_CT_SET_1_C) $(PARALLEL_CT_SET_1_D)) diff --git a/deps/rabbit/app.bzl b/deps/rabbit/app.bzl index cfd7bc31a469..f1f7e8bab6da 100644 --- a/deps/rabbit/app.bzl +++ b/deps/rabbit/app.bzl @@ -45,6 +45,10 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_access_control.erl", "src/rabbit_alarm.erl", "src/rabbit_amqp1_0.erl", +<<<<<<< HEAD +======= + "src/rabbit_amqp_filtex.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_amqp_management.erl", "src/rabbit_amqp_reader.erl", "src/rabbit_amqp_session.erl", @@ -169,6 +173,10 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_metrics.erl", "src/rabbit_mirror_queue_misc.erl", "src/rabbit_mnesia.erl", +<<<<<<< HEAD +======= + "src/rabbit_msg_size_metrics.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_msg_store.erl", "src/rabbit_msg_store_gc.erl", "src/rabbit_networking.erl", @@ -302,6 +310,10 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/rabbit_access_control.erl", "src/rabbit_alarm.erl", "src/rabbit_amqp1_0.erl", +<<<<<<< HEAD +======= + "src/rabbit_amqp_filtex.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_amqp_management.erl", "src/rabbit_amqp_reader.erl", "src/rabbit_amqp_session.erl", @@ -426,6 +438,10 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/rabbit_metrics.erl", "src/rabbit_mirror_queue_misc.erl", "src/rabbit_mnesia.erl", +<<<<<<< HEAD +======= + "src/rabbit_msg_size_metrics.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_msg_store.erl", "src/rabbit_msg_store_gc.erl", "src/rabbit_networking.erl", @@ -544,6 +560,10 @@ def all_srcs(name = "all_srcs"): name = "private_hdrs", srcs = [ "src/mirrored_supervisor.hrl", +<<<<<<< HEAD +======= + "src/rabbit_amqp_reader.hrl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_feature_flags.hrl", "src/rabbit_ff_registry.hrl", "src/rabbit_fifo.hrl", @@ -578,6 +598,10 @@ def all_srcs(name = "all_srcs"): "src/rabbit_access_control.erl", "src/rabbit_alarm.erl", "src/rabbit_amqp1_0.erl", +<<<<<<< HEAD +======= + "src/rabbit_amqp_filtex.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_amqp_management.erl", "src/rabbit_amqp_reader.erl", "src/rabbit_amqp_session.erl", @@ -705,6 +729,10 @@ def all_srcs(name = "all_srcs"): "src/rabbit_metrics.erl", "src/rabbit_mirror_queue_misc.erl", "src/rabbit_mnesia.erl", +<<<<<<< HEAD +======= + "src/rabbit_msg_size_metrics.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_msg_store.erl", "src/rabbit_msg_store_gc.erl", "src/rabbit_networking.erl", @@ -1553,7 +1581,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"): outs = ["test/topic_permission_SUITE.beam"], app_name = "rabbit", erlc_opts = "//:test_erlc_opts", +<<<<<<< HEAD deps = ["//deps/amqp_client:erlang_app"], +======= + deps = ["//deps/amqp10_common:erlang_app", "//deps/amqp_client:erlang_app"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) erlang_bytecode( name = "transactions_SUITE_beam_files", @@ -1710,6 +1742,17 @@ def test_suite_beam_files(name = "test_suite_beam_files"): deps = ["//deps/amqp_client:erlang_app", "//deps/rabbitmq_ct_helpers:erlang_app"], ) erlang_bytecode( +<<<<<<< HEAD +======= + name = "unit_msg_size_metrics_SUITE_beam_files", + testonly = True, + srcs = ["test/unit_msg_size_metrics_SUITE.erl"], + outs = ["test/unit_msg_size_metrics_SUITE.beam"], + app_name = "rabbit", + erlc_opts = "//:test_erlc_opts", + ) + erlang_bytecode( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "unit_operator_policy_SUITE_beam_files", testonly = True, srcs = ["test/unit_operator_policy_SUITE.erl"], @@ -2178,3 +2221,33 @@ def test_suite_beam_files(name = "test_suite_beam_files"): app_name = "rabbit", erlc_opts = "//:test_erlc_opts", ) +<<<<<<< HEAD +======= + erlang_bytecode( + name = "msg_size_metrics_SUITE_beam_files", + testonly = True, + srcs = ["test/msg_size_metrics_SUITE.erl"], + outs = ["test/msg_size_metrics_SUITE.beam"], + app_name = "rabbit", + erlc_opts = "//:test_erlc_opts", + deps = ["//deps/amqp_client:erlang_app"], + ) + erlang_bytecode( + name = "amqp_filtex_SUITE_beam_files", + testonly = True, + srcs = ["test/amqp_filtex_SUITE.erl"], + outs = ["test/amqp_filtex_SUITE.beam"], + app_name = "rabbit", + erlc_opts = "//:test_erlc_opts", + deps = ["//deps/amqp10_common:erlang_app"], + ) + erlang_bytecode( + name = "test_amqp_utils_beam", + testonly = True, + srcs = ["test/amqp_utils.erl"], + outs = ["test/amqp_utils.beam"], + app_name = "rabbit", + erlc_opts = "//:test_erlc_opts", + deps = ["//deps/amqp10_common:erlang_app"], + ) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit/ct.test.spec b/deps/rabbit/ct.test.spec index 6740594c1500..785d97648bf4 100644 --- a/deps/rabbit/ct.test.spec +++ b/deps/rabbit/ct.test.spec @@ -16,6 +16,10 @@ , amqp_auth_SUITE , amqp_client_SUITE , amqp_credit_api_v2_SUITE +<<<<<<< HEAD +======= +, amqp_filtex_SUITE +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) , amqp_proxy_protocol_SUITE , amqp_system_SUITE , amqpl_consumer_ack_SUITE @@ -65,7 +69,12 @@ ]}. {define, 'Set4', [ +<<<<<<< HEAD peer_discovery_dns_SUITE +======= + msg_size_metrics_SUITE +, peer_discovery_dns_SUITE +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) , peer_discovery_tmp_hidden_node_SUITE , per_node_limit_SUITE , per_user_connection_channel_limit_SUITE @@ -80,6 +89,10 @@ , product_info_SUITE , proxy_protocol_SUITE , publisher_confirms_parallel_SUITE +<<<<<<< HEAD +======= +, unit_msg_size_metrics_SUITE +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}. {define, 'Set5', [ diff --git a/deps/rabbit/include/rabbit_amqp.hrl b/deps/rabbit/include/rabbit_amqp.hrl index 84e98d5d565d..7d240346e58b 100644 --- a/deps/rabbit/include/rabbit_amqp.hrl +++ b/deps/rabbit/include/rabbit_amqp.hrl @@ -37,6 +37,10 @@ [pid, frame_max, timeout, +<<<<<<< HEAD +======= + container_id, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) vhost, user, node diff --git a/deps/rabbit/src/mc.erl b/deps/rabbit/src/mc.erl index b122c4780110..e8b762af3673 100644 --- a/deps/rabbit/src/mc.erl +++ b/deps/rabbit/src/mc.erl @@ -26,6 +26,10 @@ priority/1, set_ttl/2, x_header/2, +<<<<<<< HEAD +======= + x_headers/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) routing_headers/2, exchange/1, routing_keys/1, @@ -88,6 +92,10 @@ {timestamp, non_neg_integer()} | {list, [tagged_value()]} | {map, [{tagged_value(), tagged_value()}]} | +<<<<<<< HEAD +======= + {array, atom(), [tagged_value()]} | +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) null | undefined. @@ -104,11 +112,23 @@ {MetadataSize :: non_neg_integer(), PayloadSize :: non_neg_integer()}. +<<<<<<< HEAD %% retrieve and x- header from the protocol data +======= +%% retrieve an x- header from the protocol data +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% the return value should be tagged with an AMQP 1.0 type -callback x_header(binary(), proto_state()) -> tagged_value(). +<<<<<<< HEAD +======= +%% retrieve x- headers from the protocol data +%% the return values should be tagged with an AMQP 1.0 type +-callback x_headers(proto_state()) -> + #{binary() => tagged_value()}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% retrieve a property field from the protocol data %% e.g. message_id, correlation_id -callback property(atom(), proto_state()) -> @@ -148,7 +168,11 @@ init(Proto, Data, Anns) -> -spec init(protocol(), term(), annotations(), environment()) -> state(). init(Proto, Data, Anns0, Env) -> {ProtoData, ProtoAnns} = Proto:init(Data), +<<<<<<< HEAD Anns1 = case map_size(Env) == 0 of +======= + Anns1 = case map_size(Env) =:= 0 of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true -> Anns0; false -> Anns0#{env => Env} end, @@ -214,6 +238,28 @@ x_header(Key, #?MODULE{protocol = Proto, x_header(Key, BasicMsg) -> mc_compat:x_header(Key, BasicMsg). +<<<<<<< HEAD +======= +-spec x_headers(state()) -> + #{binary() => tagged_value()}. +x_headers(#?MODULE{protocol = Proto, + annotations = Anns, + data = Data}) -> + %% x-headers may be have been added to the annotations map. + New = maps:filtermap( + fun(Key, Val) -> + case mc_util:is_x_header(Key) of + true -> + {true, mc_util:infer_type(Val)}; + false -> + false + end + end, Anns), + maps:merge(Proto:x_headers(Data), New); +x_headers(BasicMsg) -> + mc_compat:x_headers(BasicMsg). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec routing_headers(state(), [x_headers | complex_types]) -> #{binary() => property_value()}. routing_headers(#?MODULE{protocol = Proto, @@ -301,7 +347,11 @@ message_id(BasicMsg) -> mc_compat:message_id(BasicMsg). -spec property(atom(), state()) -> +<<<<<<< HEAD {utf8, binary()} | undefined. +======= + tagged_value(). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property(Property, #?MODULE{protocol = Proto, data = Data}) -> Proto:property(Property, Data); diff --git a/deps/rabbit/src/mc_amqp.erl b/deps/rabbit/src/mc_amqp.erl index be63597c3f96..371a34bc1154 100644 --- a/deps/rabbit/src/mc_amqp.erl +++ b/deps/rabbit/src/mc_amqp.erl @@ -8,6 +8,10 @@ init/1, size/1, x_header/2, +<<<<<<< HEAD +======= + x_headers/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property/2, routing_headers/2, convert_to/3, @@ -21,7 +25,11 @@ -define(MESSAGE_ANNOTATIONS_GUESS_SIZE, 100). +<<<<<<< HEAD -define(SIMPLE_VALUE(V), +======= +-define(IS_SIMPLE_VALUE(V), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) is_binary(V) orelse is_number(V) orelse is_boolean(V)). @@ -125,6 +133,12 @@ size(#v1{message_annotations = MA, x_header(Key, Msg) -> message_annotation(Key, Msg, undefined). +<<<<<<< HEAD +======= +x_headers(Msg) -> + #{K => V || {{_T, K}, V} <- message_annotations(Msg)}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property(_Prop, #msg_body_encoded{properties = undefined}) -> undefined; property(Prop, #msg_body_encoded{properties = Props}) -> @@ -145,6 +159,7 @@ property(Prop, #v1{bare_and_footer = Bin, Props = amqp10_framing:decode(PropsDescribed), property0(Prop, Props). +<<<<<<< HEAD property0(correlation_id, #'v1_0.properties'{correlation_id = Corr}) -> Corr; property0(message_id, #'v1_0.properties'{message_id = MsgId}) -> @@ -155,6 +170,34 @@ property0(subject, #'v1_0.properties'{subject = Subject}) -> Subject; property0(to, #'v1_0.properties'{to = To}) -> To; +======= +property0(message_id, #'v1_0.properties'{message_id = Val}) -> + Val; +property0(user_id, #'v1_0.properties'{user_id = Val}) -> + Val; +property0(to, #'v1_0.properties'{to = Val}) -> + Val; +property0(subject, #'v1_0.properties'{subject = Val}) -> + Val; +property0(reply_to, #'v1_0.properties'{reply_to = Val}) -> + Val; +property0(correlation_id, #'v1_0.properties'{correlation_id = Val}) -> + Val; +property0(content_type, #'v1_0.properties'{content_type = Val}) -> + Val; +property0(content_encoding, #'v1_0.properties'{content_encoding = Val}) -> + Val; +property0(absolute_expiry_time, #'v1_0.properties'{absolute_expiry_time = Val}) -> + Val; +property0(creation_time, #'v1_0.properties'{creation_time = Val}) -> + Val; +property0(group_id, #'v1_0.properties'{group_id = Val}) -> + Val; +property0(group_sequence, #'v1_0.properties'{group_sequence = Val}) -> + Val; +property0(reply_to_group_id, #'v1_0.properties'{reply_to_group_id = Val}) -> + Val; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property0(_Prop, #'v1_0.properties'{}) -> undefined. @@ -454,7 +497,11 @@ message_annotations_as_simple_map(#v1{message_annotations = Content}) -> message_annotations_as_simple_map0(Content) -> %% the section record format really is terrible lists:filtermap(fun({{symbol, K}, {_T, V}}) +<<<<<<< HEAD when ?SIMPLE_VALUE(V) -> +======= + when ?IS_SIMPLE_VALUE(V) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {true, {K, V}}; (_) -> false @@ -480,7 +527,11 @@ application_properties_as_simple_map( application_properties_as_simple_map0(Content, L) -> %% the section record format really is terrible lists:foldl(fun({{utf8, K}, {_T, V}}, Acc) +<<<<<<< HEAD when ?SIMPLE_VALUE(V) -> +======= + when ?IS_SIMPLE_VALUE(V) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [{K, V} | Acc]; ({{utf8, K}, V}, Acc) when V =:= undefined orelse is_boolean(V) -> @@ -602,11 +653,16 @@ encode_deaths(Deaths) -> {map, Map} end, Deaths). +<<<<<<< HEAD essential_properties(#msg_body_encoded{message_annotations = MA} = Msg) -> +======= +essential_properties(Msg) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Durable = get_property(durable, Msg), Priority = get_property(priority, Msg), Timestamp = get_property(timestamp, Msg), Ttl = get_property(ttl, Msg), +<<<<<<< HEAD Anns0 = #{?ANN_DURABLE => Durable}, Anns = maps_put_truthy( ?ANN_PRIORITY, Priority, @@ -640,3 +696,13 @@ essential_properties(#msg_body_encoded{message_annotations = MA} = Msg) -> Acc end, Anns, MA) end. +======= + Anns = #{?ANN_DURABLE => Durable}, + maps_put_truthy( + ?ANN_PRIORITY, Priority, + maps_put_truthy( + ?ANN_TIMESTAMP, Timestamp, + maps_put_truthy( + ttl, Ttl, + Anns))). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit/src/mc_amqpl.erl b/deps/rabbit/src/mc_amqpl.erl index 723e60cd3f79..02acce4f0dff 100644 --- a/deps/rabbit/src/mc_amqpl.erl +++ b/deps/rabbit/src/mc_amqpl.erl @@ -11,6 +11,10 @@ init/1, size/1, x_header/2, +<<<<<<< HEAD +======= + x_headers/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) routing_headers/2, convert_to/3, convert_from/3, @@ -42,7 +46,10 @@ -define(AMQP10_FOOTER, <<"x-amqp-1.0-footer">>). -define(PROTOMOD, rabbit_framing_amqp_0_9_1). -define(CLASS_ID, 60). +<<<<<<< HEAD -define(LONGSTR_UTF8_LIMIT, 4096). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -opaque state() :: #content{}. @@ -273,6 +280,26 @@ x_header(Key, #content{properties = none} = Content0) -> Content = rabbit_binary_parser:ensure_content_decoded(Content0), x_header(Key, Content). +<<<<<<< HEAD +======= +x_headers(#content{properties = #'P_basic'{headers = undefined}}) -> + #{}; +x_headers(#content{properties = #'P_basic'{headers = Headers}}) -> + L = lists:filtermap( + fun({Name, Type, Val}) -> + case mc_util:is_x_header(Name) of + true -> + {true, {Name, from_091(Type, Val)}}; + false -> + false + end + end, Headers), + maps:from_list(L); +x_headers(#content{properties = none} = Content0) -> + Content = rabbit_binary_parser:ensure_content_decoded(Content0), + x_headers(Content). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property(Prop, Content) -> mc_util:infer_type(mc_compat:get_property(Prop, Content)). @@ -664,6 +691,7 @@ wrap(_Type, undefined) -> wrap(Type, Val) -> {Type, Val}. +<<<<<<< HEAD from_091(longstr, V) when is_binary(V) andalso byte_size(V) =< ?LONGSTR_UTF8_LIMIT -> @@ -671,12 +699,19 @@ from_091(longstr, V) %% it _may_ still be valid utf8 but checking this for every longstr header %% value is going to be excessively slow case mc_util:is_utf8_no_null(V) of +======= +from_091(longstr, V) -> + case mc_util:is_utf8_no_null_limited(V) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true -> {utf8, V}; false -> {binary, V} end; +<<<<<<< HEAD from_091(longstr, V) -> {binary, V}; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) from_091(long, V) -> {long, V}; from_091(unsignedbyte, V) -> {ubyte, V}; from_091(short, V) -> {short, V}; @@ -707,7 +742,10 @@ supported_header_value_type(table) -> supported_header_value_type(_) -> true. +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqp10_map_get(_K, []) -> undefined; amqp10_map_get(K, Tuples) -> diff --git a/deps/rabbit/src/mc_compat.erl b/deps/rabbit/src/mc_compat.erl index 056905239d96..c03552819fad 100644 --- a/deps/rabbit/src/mc_compat.erl +++ b/deps/rabbit/src/mc_compat.erl @@ -20,6 +20,10 @@ priority/1, set_ttl/2, x_header/2, +<<<<<<< HEAD +======= + x_headers/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) routing_headers/2, %%% convert_to/2, @@ -138,6 +142,12 @@ set_ttl(Value, #basic_message{content = Content0} = Msg) -> x_header(Key,#basic_message{content = Content}) -> mc_amqpl:x_header(Key, Content). +<<<<<<< HEAD +======= +x_headers(#basic_message{content = Content}) -> + mc_amqpl:x_headers(Content). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) routing_headers(#basic_message{content = Content}, Opts) -> mc_amqpl:routing_headers(Content, Opts). diff --git a/deps/rabbit/src/mc_util.erl b/deps/rabbit/src/mc_util.erl index 1f20d15699db..004794561672 100644 --- a/deps/rabbit/src/mc_util.erl +++ b/deps/rabbit/src/mc_util.erl @@ -3,6 +3,10 @@ -include("mc.hrl"). -export([is_valid_shortstr/1, +<<<<<<< HEAD +======= + is_utf8_no_null_limited/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) is_utf8_no_null/1, uuid_to_urn_string/1, urn_string_to_uuid/1, @@ -12,12 +16,30 @@ is_x_header/1 ]). +<<<<<<< HEAD +======= +-define(UTF8_SCAN_LIMIT, 4096). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec is_valid_shortstr(term()) -> boolean(). is_valid_shortstr(Bin) when ?IS_SHORTSTR_LEN(Bin) -> is_utf8_no_null(Bin); is_valid_shortstr(_) -> false. +<<<<<<< HEAD +======= +-spec is_utf8_no_null_limited(term()) -> boolean(). +is_utf8_no_null_limited(Bin) + when byte_size(Bin) =< ?UTF8_SCAN_LIMIT -> + is_utf8_no_null(Bin); +is_utf8_no_null_limited(_Term) -> + %% If longer than 4096 bytes, just assume it's not UTF-8. + %% It _may_ still be valid UTF-8 but checking this + %% on the hot path is going to be excessively slow. + false. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec is_utf8_no_null(term()) -> boolean(). is_utf8_no_null(Term) -> utf8_scan(Term, fun (C) -> C > 0 end). @@ -61,7 +83,11 @@ utf8_string_is_ascii(UTF8String) -> amqp_map_get(Key, {map, List}, Default) -> amqp_map_get(Key, List, Default); amqp_map_get(Key, List, Default) when is_list(List) -> +<<<<<<< HEAD case lists:search(fun ({{_, K}, _}) -> K == Key end, List) of +======= + case lists:search(fun ({{_, K}, _}) -> K =:= Key end, List) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {value, {_K, V}} -> V; false -> diff --git a/deps/rabbit/src/rabbit_access_control.erl b/deps/rabbit/src/rabbit_access_control.erl index cfc8b591eb3f..bef8707cae65 100644 --- a/deps/rabbit/src/rabbit_access_control.erl +++ b/deps/rabbit/src/rabbit_access_control.erl @@ -249,7 +249,11 @@ check_user_id0(ClaimedUserName, #user{username = ActualUserName, end. -spec update_state(User :: rabbit_types:user(), NewState :: term()) -> +<<<<<<< HEAD {'ok', rabbit_types:auth_user()} | +======= + {'ok', rabbit_types:user()} | +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {'refused', string()} | {'error', any()}. diff --git a/deps/rabbit/src/rabbit_amqp_filtex.erl b/deps/rabbit/src/rabbit_amqp_filtex.erl new file mode 100644 index 000000000000..d8fcd6fa8caa --- /dev/null +++ b/deps/rabbit/src/rabbit_amqp_filtex.erl @@ -0,0 +1,200 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. + +%% AMQP Filter Expressions Version 1.0 Working Draft 09 +%% https://groups.oasis-open.org/higherlogic/ws/public/document?document_id=66227 +-module(rabbit_amqp_filtex). + +-include_lib("amqp10_common/include/amqp10_filtex.hrl"). + +-export([validate/1, + filter/2]). + +-type simple_type() :: number() | binary() | atom(). +-type affix() :: {suffix, non_neg_integer(), binary()} | + {prefix, non_neg_integer(), binary()}. +-type filter_expression_value() :: simple_type() | affix(). +-type filter_expression() :: {properties, [{FieldName :: atom(), filter_expression_value()}]} | + {application_properties, [{binary(), filter_expression_value()}]}. +-type filter_expressions() :: [filter_expression()]. +-export_type([filter_expressions/0]). + +-spec validate(tuple()) -> + {ok, filter_expression()} | error. +validate({described, Descriptor, {map, KVList}}) -> + try validate0(Descriptor, KVList) + catch throw:{?MODULE, _, _} -> + error + end; +validate(_) -> + error. + +-spec filter(filter_expressions(), mc:state()) -> + boolean(). +filter(Filters, Mc) -> + %% "A message will pass through a filter-set if and only if + %% it passes through each of the named filters." [3.5.8] + lists:all(fun(Filter) -> + filter0(Filter, Mc) + end, Filters). + +%%%%%%%%%%%%%%%% +%%% Internal %%% +%%%%%%%%%%%%%%%% + +filter0({properties, KVList}, Mc) -> + %% "The filter evaluates to true if all properties enclosed in the filter expression + %% match the respective properties in the message." + %% [filtex-v1.0-wd09 4.2.4] + lists:all(fun({FieldName, RefVal}) -> + TaggedVal = mc:property(FieldName, Mc), + Val = unwrap(TaggedVal), + match_simple_type(RefVal, Val) + end, KVList); +filter0({application_properties, KVList}, Mc) -> + AppProps = mc:routing_headers(Mc, []), + %% "The filter evaluates to true if all properties enclosed in the filter expression + %% match the respective entries in the application-properties section in the message." + %% [filtex-v1.0-wd09 4.2.5] + lists:all(fun({Key, RefVal}) -> + case AppProps of + #{Key := Val} -> + match_simple_type(RefVal, Val); + _ -> + false + end + end, KVList). + +%% [filtex-v1.0-wd09 4.1.1] +%% "A reference field value in a property filter expression matches +%% its corresponding message metadata field value if: +%% [...] +match_simple_type(null, _Val) -> + %% * The reference field value is NULL + true; +match_simple_type({suffix, SuffixSize, Suffix}, Val) -> + %% * Suffix. The message metadata field matches the expression if the ordinal values of the + %% characters of the suffix expression equal the ordinal values of the same number of + %% characters trailing the message metadata field value. + case is_binary(Val) of + true -> + case Val of + <<_:(size(Val) - SuffixSize)/binary, Suffix:SuffixSize/binary>> -> + true; + _ -> + false + end; + false -> + false + end; +match_simple_type({prefix, PrefixSize, Prefix}, Val) -> + %% * Prefix. The message metadata field matches the expression if the ordinal values of the + %% characters of the prefix expression equal the ordinal values of the same number of + %% characters leading the message metadata field value. + case Val of + <> -> + true; + _ -> + false + end; +match_simple_type(RefVal, Val) -> + %% * the reference field value is of a floating-point or integer number type + %% and the message metadata field is of a different floating-point or integer number type, + %% the reference value and the metadata field value are within the value range of both types, + %% and the values are equal when treated as a floating-point" + RefVal == Val. + +validate0(Descriptor, KVList) when + (Descriptor =:= {symbol, ?DESCRIPTOR_NAME_PROPERTIES_FILTER} orelse + Descriptor =:= {ulong, ?DESCRIPTOR_CODE_PROPERTIES_FILTER}) andalso + KVList =/= [] -> + validate_props(KVList, []); +validate0(Descriptor, KVList) when + (Descriptor =:= {symbol, ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER} orelse + Descriptor =:= {ulong, ?DESCRIPTOR_CODE_APPLICATION_PROPERTIES_FILTER}) andalso + KVList =/= [] -> + validate_app_props(KVList, []); +validate0(_, _) -> + error. + +validate_props([], Acc) -> + {ok, {properties, lists:reverse(Acc)}}; +validate_props([{{symbol, <<"message-id">>}, TaggedVal} | Rest], Acc) -> + case parse_message_id(TaggedVal) of + {ok, Val} -> + validate_props(Rest, [{message_id, Val} | Acc]); + error -> + error + end; +validate_props([{{symbol, <<"user-id">>}, {binary, Val}} | Rest], Acc) -> + validate_props(Rest, [{user_id, Val} | Acc]); +validate_props([{{symbol, <<"to">>}, {utf8, Val}} | Rest], Acc) -> + validate_props(Rest, [{to, parse_string_modifier_prefix(Val)} | Acc]); +validate_props([{{symbol, <<"subject">>}, {utf8, Val}} | Rest], Acc) -> + validate_props(Rest, [{subject, parse_string_modifier_prefix(Val)} | Acc]); +validate_props([{{symbol, <<"reply-to">>}, {utf8, Val}} | Rest], Acc) -> + validate_props(Rest, [{reply_to, parse_string_modifier_prefix(Val)} | Acc]); +validate_props([{{symbol, <<"correlation-id">>}, TaggedVal} | Rest], Acc) -> + case parse_message_id(TaggedVal) of + {ok, Val} -> + validate_props(Rest, [{correlation_id, Val} | Acc]); + error -> + error + end; +validate_props([{{symbol, <<"content-type">>}, {symbol, Val}} | Rest], Acc) -> + validate_props(Rest, [{content_type, Val} | Acc]); +validate_props([{{symbol, <<"content-encoding">>}, {symbol, Val}} | Rest], Acc) -> + validate_props(Rest, [{content_encoding, Val} | Acc]); +validate_props([{{symbol, <<"absolute-expiry-time">>}, {timestamp, Val}} | Rest], Acc) -> + validate_props(Rest, [{absolute_expiry_time, Val} | Acc]); +validate_props([{{symbol, <<"creation-time">>}, {timestamp, Val}} | Rest], Acc) -> + validate_props(Rest, [{creation_time, Val} | Acc]); +validate_props([{{symbol, <<"group-id">>}, {utf8, Val}} | Rest], Acc) -> + validate_props(Rest, [{group_id, parse_string_modifier_prefix(Val)} | Acc]); +validate_props([{{symbol, <<"group-sequence">>}, {uint, Val}} | Rest], Acc) -> + validate_props(Rest, [{group_sequence, Val} | Acc]); +validate_props([{{symbol, <<"reply-to-group-id">>}, {utf8, Val}} | Rest], Acc) -> + validate_props(Rest, [{reply_to_group_id, parse_string_modifier_prefix(Val)} | Acc]); +validate_props(_, _) -> + error. + +parse_message_id({ulong, Val}) -> + {ok, Val}; +parse_message_id({uuid, Val}) -> + {ok, Val}; +parse_message_id({binary, Val}) -> + {ok, Val}; +parse_message_id({utf8, Val}) -> + {ok, parse_string_modifier_prefix(Val)}; +parse_message_id(_) -> + error. + +validate_app_props([], Acc) -> + {ok, {application_properties, lists:reverse(Acc)}}; +validate_app_props([{{utf8, Key}, {utf8, String}} | Rest], Acc) -> + validate_app_props(Rest, [{Key, parse_string_modifier_prefix(String)} | Acc]); +validate_app_props([{{utf8, Key}, TaggedVal} | Rest], Acc) -> + validate_app_props(Rest, [{Key, unwrap(TaggedVal)} | Acc]); +validate_app_props(_, _) -> + error. + +%% [filtex-v1.0-wd09 4.1.1] +parse_string_modifier_prefix(<<"$s:", Suffix/binary>>) -> + {suffix, size(Suffix), Suffix}; +parse_string_modifier_prefix(<<"$p:", Prefix/binary>>) -> + {prefix, size(Prefix), Prefix}; +parse_string_modifier_prefix(<<"$$", _/binary>> = String) -> + %% "Escape prefix for case-sensitive matching of a string starting with ‘&’" + string:slice(String, 1); +parse_string_modifier_prefix(<<"$", _/binary>> = String) -> + throw({?MODULE, invalid_reference_field_value, String}); +parse_string_modifier_prefix(String) -> + String. + +unwrap({_Tag, V}) -> + V; +unwrap(V) -> + V. diff --git a/deps/rabbit/src/rabbit_amqp_management.erl b/deps/rabbit/src/rabbit_amqp_management.erl index e4555e806033..7b12583aac44 100644 --- a/deps/rabbit/src/rabbit_amqp_management.erl +++ b/deps/rabbit/src/rabbit_amqp_management.erl @@ -381,7 +381,23 @@ handle_http_req(<<"GET">>, Bindings0 = rabbit_binding:list_for_source_and_destination(SrcXName, DstName), Bindings = [B || B = #binding{key = K} <- Bindings0, K =:= Key], RespPayload = encode_bindings(Bindings), +<<<<<<< HEAD {<<"200">>, RespPayload, PermCaches}. +======= + {<<"200">>, RespPayload, PermCaches}; + +handle_http_req(<<"PUT">>, + [<<"auth">>, <<"tokens">>], + _Query, + ReqPayload, + _Vhost, + _User, + ConnPid, + PermCaches) -> + {binary, Token} = ReqPayload, + ok = rabbit_amqp_reader:set_credential(ConnPid, Token), + {<<"204">>, null, PermCaches}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) decode_queue({map, KVList}) -> M = lists:foldl( diff --git a/deps/rabbit/src/rabbit_amqp_reader.erl b/deps/rabbit/src/rabbit_amqp_reader.erl index 52e2ba2e8f9c..9133a8c25a09 100644 --- a/deps/rabbit/src/rabbit_amqp_reader.erl +++ b/deps/rabbit/src/rabbit_amqp_reader.erl @@ -7,13 +7,25 @@ -module(rabbit_amqp_reader). +<<<<<<< HEAD -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("amqp10_common/include/amqp10_types.hrl"). +======= +-include_lib("kernel/include/logger.hrl"). +-include_lib("rabbit_common/include/rabbit.hrl"). +-include_lib("amqp10_common/include/amqp10_types.hrl"). +-include("rabbit_amqp_reader.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include("rabbit_amqp.hrl"). -export([init/1, info/2, +<<<<<<< HEAD mainloop/2]). +======= + mainloop/2, + set_credential/2]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -export([system_continue/3, system_terminate/4, @@ -35,6 +47,10 @@ -record(v1_connection, {name :: binary(), +<<<<<<< HEAD +======= + container_id :: none | binary(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) vhost :: none | rabbit_types:vhost(), %% server host host :: inet:ip_address() | inet:hostname(), @@ -52,6 +68,10 @@ channel_max :: non_neg_integer(), auth_mechanism :: sasl_init_unprocessed | {binary(), module()}, auth_state :: term(), +<<<<<<< HEAD +======= + credential_timer :: undefined | reference(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) properties :: undefined | {map, list(tuple())} }). @@ -75,7 +95,12 @@ pending_recv :: boolean(), buf :: list(), buf_len :: non_neg_integer(), +<<<<<<< HEAD tracked_channels :: #{channel_number() => Session :: pid()} +======= + tracked_channels :: #{channel_number() => Session :: pid()}, + stats_timer :: rabbit_event:state() +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }). -type state() :: #v1{}. @@ -86,7 +111,11 @@ unpack_from_0_9_1( {Sock, PendingRecv, SupPid, Buf, BufLen, ProxySocket, +<<<<<<< HEAD ConnectionName, Host, PeerHost, Port, PeerPort, ConnectedAt}, +======= + ConnectionName, Host, PeerHost, Port, PeerPort, ConnectedAt, StatsTimer}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Parent) -> logger:update_process_metadata(#{connection => ConnectionName}), #v1{parent = Parent, @@ -102,8 +131,15 @@ unpack_from_0_9_1( tracked_channels = maps:new(), writer = none, connection_state = received_amqp3100, +<<<<<<< HEAD + connection = #v1_connection{ + name = ConnectionName, +======= + stats_timer = StatsTimer, connection = #v1_connection{ name = ConnectionName, + container_id = none, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) vhost = none, host = Host, peer_host = PeerHost, @@ -137,6 +173,14 @@ server_properties() -> Props = [{{symbol, <<"node">>}, {utf8, atom_to_binary(node())}} | Props1], {map, Props}. +<<<<<<< HEAD +======= +-spec set_credential(pid(), binary()) -> ok. +set_credential(Pid, Credential) -> + Pid ! {set_credential, Credential}, + ok. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %%-------------------------------------------------------------------------- inet_op(F) -> rabbit_misc:throw_on_error(inet_error, F). @@ -191,6 +235,13 @@ mainloop(Deb, State = #v1{sock = Sock, buf = Buf, buf_len = BufLen}) -> end end. +<<<<<<< HEAD +======= +handle_other(emit_stats, State) -> + emit_stats(State); +handle_other(ensure_stats_timer, State) -> + ensure_stats_timer(State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_other({'EXIT', Parent, Reason}, State = #v1{parent = Parent}) -> ReasonString = rabbit_misc:format("broker forced connection closure with reason '~w'", [Reason]), @@ -237,10 +288,27 @@ handle_other({'$gen_call', From, {info, Items}}, State) -> end, gen_server:reply(From, Reply), State; +<<<<<<< HEAD handle_other({'$gen_cast', {force_event_refresh, _Ref}}, State) -> State; handle_other(terminate_connection, _State) -> stop; +======= +handle_other({'$gen_cast', {force_event_refresh, Ref}}, State) -> + case ?IS_RUNNING(State) of + true -> + Infos = infos(?CONNECTION_EVENT_KEYS, State), + rabbit_event:notify(connection_created, Infos, Ref), + rabbit_event:init_stats_timer(State, #v1.stats_timer); + false -> + %% Ignore, we will emit a connection_created event once we start running. + State + end; +handle_other(terminate_connection, _State) -> + stop; +handle_other({set_credential, Cred}, State) -> + set_credential0(Cred, State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_other(credential_expired, State) -> Error = error_frame(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, "credential expired", []), handle_exception(State, 0, Error); @@ -318,16 +386,26 @@ error_frame(Condition, Fmt, Args) -> handle_exception(State = #v1{connection_state = closed}, Channel, #'v1_0.error'{description = {utf8, Desc}}) -> +<<<<<<< HEAD rabbit_log_connection:error( "Error on AMQP 1.0 connection ~tp (~tp), channel number ~b:~n~tp", [self(), closed, Channel, Desc]), +======= + ?LOG_ERROR("Error on AMQP 1.0 connection ~tp (~tp), channel number ~b:~n~tp", + [self(), closed, Channel, Desc]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State; handle_exception(State = #v1{connection_state = CS}, Channel, Error = #'v1_0.error'{description = {utf8, Desc}}) when ?IS_RUNNING(State) orelse CS =:= closing -> +<<<<<<< HEAD rabbit_log_connection:error( "Error on AMQP 1.0 connection ~tp (~tp), channel number ~b:~n~tp", [self(), CS, Channel, Desc]), +======= + ?LOG_ERROR("Error on AMQP 1.0 connection ~tp (~tp), channel number ~b:~n~tp", + [self(), CS, Channel, Desc]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) close(Error, State); handle_exception(State, _Channel, Error) -> silent_close_delay(), @@ -414,14 +492,22 @@ handle_connection_frame( }, helper_sup = HelperSupPid, sock = Sock} = State0) -> +<<<<<<< HEAD logger:update_process_metadata(#{amqp_container => ContainerId}), Vhost = vhost(Hostname), +======= + Vhost = vhost(Hostname), + logger:update_process_metadata(#{amqp_container => ContainerId, + vhost => Vhost, + user => Username}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = check_user_loopback(State0), ok = check_vhost_exists(Vhost, State0), ok = check_vhost_alive(Vhost), ok = rabbit_access_control:check_vhost_access(User, Vhost, {socket, Sock}, #{}), ok = check_vhost_connection_limit(Vhost, Username), ok = check_user_connection_limit(Username), +<<<<<<< HEAD ok = ensure_credential_expiry_timer(User), rabbit_core_metrics:auth_attempt_succeeded(<<>>, Username, amqp10), notify_auth(user_authentication_success, Username, State0), @@ -429,6 +515,15 @@ handle_connection_frame( "Connection from AMQP 1.0 container '~ts': user '~ts' authenticated " "using SASL mechanism ~s and granted access to vhost '~ts'", [ContainerId, Username, Mechanism, Vhost]), +======= + Timer = maybe_start_credential_expiry_timer(User), + rabbit_core_metrics:auth_attempt_succeeded(<<>>, Username, amqp10), + notify_auth(user_authentication_success, Username, State0), + ?LOG_INFO( + "Connection from AMQP 1.0 container '~ts': user '~ts' authenticated " + "using SASL mechanism ~s and granted access to vhost '~ts'", + [ContainerId, Username, Mechanism, Vhost]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OutgoingMaxFrameSize = case ClientMaxFrame of undefined -> @@ -491,12 +586,21 @@ handle_connection_frame( end, State1 = State0#v1{connection_state = running, connection = Connection#v1_connection{ +<<<<<<< HEAD +======= + container_id = ContainerId, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) vhost = Vhost, incoming_max_frame_size = IncomingMaxFrameSize, outgoing_max_frame_size = OutgoingMaxFrameSize, channel_max = EffectiveChannelMax, properties = Properties, +<<<<<<< HEAD timeout = ReceiveTimeoutMillis}, +======= + timeout = ReceiveTimeoutMillis, + credential_timer = Timer}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) heartbeater = Heartbeater}, State = start_writer(State1), HostnameVal = case Hostname of @@ -504,27 +608,46 @@ handle_connection_frame( null -> undefined; {utf8, Val} -> Val end, +<<<<<<< HEAD rabbit_log:debug( "AMQP 1.0 connection.open frame: hostname = ~ts, extracted vhost = ~ts, idle-time-out = ~p", [HostnameVal, Vhost, IdleTimeout]), +======= + ?LOG_DEBUG( + "AMQP 1.0 connection.open frame: hostname = ~ts, extracted vhost = ~ts, idle-time-out = ~p", + [HostnameVal, Vhost, IdleTimeout]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Infos = infos(?CONNECTION_EVENT_KEYS, State), ok = rabbit_core_metrics:connection_created( proplists:get_value(pid, Infos), Infos), ok = rabbit_event:notify(connection_created, Infos), +<<<<<<< HEAD ok = rabbit_amqp1_0:register_connection(self()), Caps = [%% https://docs.oasis-open.org/amqp/linkpair/v1.0/cs01/linkpair-v1.0-cs01.html#_Toc51331306 {symbol, <<"LINK_PAIR_V1_0">>}, %% https://docs.oasis-open.org/amqp/anonterm/v1.0/cs01/anonterm-v1.0-cs01.html#doc-anonymous-relay {symbol, <<"ANONYMOUS-RELAY">>}], +======= + ok = maybe_emit_stats(State), + ok = rabbit_amqp1_0:register_connection(self()), + Caps = [%% https://docs.oasis-open.org/amqp/linkpair/v1.0/cs01/linkpair-v1.0-cs01.html#_Toc51331306 + <<"LINK_PAIR_V1_0">>, + %% https://docs.oasis-open.org/amqp/anonterm/v1.0/cs01/anonterm-v1.0-cs01.html#doc-anonymous-relay + <<"ANONYMOUS-RELAY">>], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Open = #'v1_0.open'{ channel_max = {ushort, EffectiveChannelMax}, max_frame_size = {uint, IncomingMaxFrameSize}, %% "the value in idle-time-out SHOULD be half the peer's actual timeout threshold" [2.4.5] idle_time_out = {uint, ReceiveTimeoutMillis div 2}, container_id = {utf8, rabbit_nodes:cluster_name()}, +<<<<<<< HEAD offered_capabilities = {array, symbol, Caps}, +======= + offered_capabilities = rabbit_amqp_util:capabilities(Caps), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) properties = server_properties()}, ok = send_on_channel0(Sock, Open), State; @@ -615,6 +738,7 @@ handle_input(handshake, switch_callback(State, {frame_header, amqp}, 8); handle_input({frame_header, Mode}, Header = <>, +<<<<<<< HEAD State) when DOff >= 2 -> case {Mode, Type} of {amqp, 0} -> ok; @@ -634,6 +758,28 @@ handle_input({frame_header, Mode}, true -> switch_callback(State, {frame_body, Mode, DOff, Channel}, Size - 8) end; +======= + State0) when DOff >= 2 -> + case {Mode, Type} of + {amqp, 0} -> ok; + {sasl, 1} -> ok; + _ -> throw({bad_1_0_header_type, Header, Mode}) + end, + MaxFrameSize = State0#v1.connection#v1_connection.incoming_max_frame_size, + State = if Size =:= 8 -> + %% heartbeat + State0; + Size > MaxFrameSize -> + Err = error_frame( + ?V_1_0_CONNECTION_ERROR_FRAMING_ERROR, + "frame size (~b bytes) > maximum frame size (~b bytes)", + [Size, MaxFrameSize]), + handle_exception(State0, Channel, Err); + true -> + switch_callback(State0, {frame_body, Mode, DOff, Channel}, Size - 8) + end, + ensure_stats_timer(State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_input({frame_header, _Mode}, Malformed, _State) -> throw({bad_1_0_header, Malformed}); handle_input({frame_body, Mode, DOff, Channel}, @@ -765,16 +911,26 @@ notify_auth(EventType, Username, State) -> rabbit_event:notify(EventType, EventProps). track_channel(ChannelNum, SessionPid, #v1{tracked_channels = Channels} = State) -> +<<<<<<< HEAD rabbit_log:debug("AMQP 1.0 created session process ~p for channel number ~b", [SessionPid, ChannelNum]), +======= + ?LOG_DEBUG("AMQP 1.0 created session process ~p for channel number ~b", + [SessionPid, ChannelNum]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _Ref = erlang:monitor(process, SessionPid, [{tag, {'DOWN', ChannelNum}}]), State#v1{tracked_channels = maps:put(ChannelNum, SessionPid, Channels)}. untrack_channel(ChannelNum, SessionPid, #v1{tracked_channels = Channels0} = State) -> case maps:take(ChannelNum, Channels0) of {SessionPid, Channels} -> +<<<<<<< HEAD rabbit_log:debug("AMQP 1.0 closed session process ~p with channel number ~b", [SessionPid, ChannelNum]), +======= + ?LOG_DEBUG("AMQP 1.0 closed session process ~p with channel number ~b", + [SessionPid, ChannelNum]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State#v1{tracked_channels = Channels}; _ -> State @@ -868,6 +1024,7 @@ check_user_connection_limit(Username) -> end. +<<<<<<< HEAD %% TODO Provide a means for the client to refresh the credential. %% This could be either via: %% 1. SASL (if multiple authentications are allowed on the same AMQP 1.0 connection), see @@ -901,6 +1058,59 @@ ensure_credential_expiry_timer(User) -> false -> protocol_error(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, "Credential expired ~b ms ago", [abs(Time)]) +======= +set_credential0(Cred, + State = #v1{connection = #v1_connection{ + user = User0, + vhost = Vhost, + credential_timer = OldTimer} = Conn, + tracked_channels = Chans, + sock = Sock}) -> + ?LOG_INFO("updating credential", []), + case rabbit_access_control:update_state(User0, Cred) of + {ok, User} -> + try rabbit_access_control:check_vhost_access(User, Vhost, {socket, Sock}, #{}) of + ok -> + maps:foreach(fun(_ChanNum, Pid) -> + rabbit_amqp_session:reset_authz(Pid, User) + end, Chans), + case OldTimer of + undefined -> ok; + Ref -> ok = erlang:cancel_timer(Ref, [{info, false}]) + end, + NewTimer = maybe_start_credential_expiry_timer(User), + State#v1{connection = Conn#v1_connection{ + user = User, + credential_timer = NewTimer}} + catch _:Reason -> + Error = error_frame(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + "access to vhost ~s failed for new credential: ~p", + [Vhost, Reason]), + handle_exception(State, 0, Error) + end; + Err -> + Error = error_frame(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + "credential update failed: ~p", + [Err]), + handle_exception(State, 0, Error) + end. + +maybe_start_credential_expiry_timer(User) -> + case rabbit_access_control:expiry_timestamp(User) of + never -> + undefined; + Ts when is_integer(Ts) -> + Time = (Ts - os:system_time(second)) * 1000, + ?LOG_DEBUG( + "credential expires in ~b ms frow now (absolute timestamp = ~b seconds since epoch)", + [Time, Ts]), + case Time > 0 of + true -> + erlang:send_after(Time, self(), credential_expired); + false -> + protocol_error(?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + "credential expired ~b ms ago", [abs(Time)]) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end. @@ -918,7 +1128,12 @@ silent_close_delay() -> -spec info(rabbit_types:connection(), rabbit_types:info_keys()) -> rabbit_types:infos(). info(Pid, InfoItems) -> +<<<<<<< HEAD case InfoItems -- ?INFO_ITEMS of +======= + KnownItems = [session_pids | ?INFO_ITEMS], + case InfoItems -- KnownItems of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [] -> case gen_server:call(Pid, {info, InfoItems}, infinity) of {ok, InfoList} -> @@ -969,6 +1184,11 @@ i(connected_at, #v1{connection = #v1_connection{connected_at = Val}}) -> Val; i(name, #v1{connection = #v1_connection{name = Val}}) -> Val; +<<<<<<< HEAD +======= +i(container_id, #v1{connection = #v1_connection{container_id = Val}}) -> + Val; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) i(vhost, #v1{connection = #v1_connection{vhost = Val}}) -> Val; i(host, #v1{connection = #v1_connection{host = Val}}) -> @@ -979,6 +1199,7 @@ i(peer_host, #v1{connection = #v1_connection{peer_host = Val}}) -> Val; i(peer_port, #v1{connection = #v1_connection{peer_port = Val}}) -> Val; +<<<<<<< HEAD i(SockStat, S) when SockStat =:= recv_oct; SockStat =:= recv_cnt; SockStat =:= send_oct; @@ -986,6 +1207,20 @@ i(SockStat, S) when SockStat =:= recv_oct; SockStat =:= send_pend -> socket_info(fun (Sock) -> rabbit_net:getstat(Sock, [SockStat]) end, fun ([{_, I}]) -> I end, S); +======= +i(SockStat, #v1{sock = Sock}) + when SockStat =:= recv_oct; + SockStat =:= recv_cnt; + SockStat =:= send_oct; + SockStat =:= send_cnt; + SockStat =:= send_pend -> + case rabbit_net:getstat(Sock, [SockStat]) of + {ok, [{SockStat, Val}]} -> + Val; + {error, _} -> + '' + end; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) i(ssl, #v1{sock = Sock}) -> rabbit_net:is_ssl(Sock); i(SSL, #v1{sock = Sock, proxy_socket = ProxySock}) when SSL =:= ssl_protocol; @@ -1009,6 +1244,7 @@ i(client_properties, #v1{connection = #v1_connection{properties = Props}}) -> end; i(channels, #v1{tracked_channels = Channels}) -> maps:size(Channels); +<<<<<<< HEAD i(channel_max, #v1{connection = #v1_connection{channel_max = Max}}) -> Max; i(Item, #v1{}) -> @@ -1020,6 +1256,43 @@ socket_info(Get, Select, #v1{sock = Sock}) -> {ok, T} -> Select(T); {error, _} -> '' end. +======= +i(session_pids, #v1{tracked_channels = Map}) -> + maps:values(Map); +i(channel_max, #v1{connection = #v1_connection{channel_max = Max}}) -> + Max; +i(reductions = Item, _State) -> + {Item, Reductions} = erlang:process_info(self(), Item), + Reductions; +i(garbage_collection, _State) -> + rabbit_misc:get_gc_info(self()); +i(Item, #v1{}) -> + throw({bad_argument, Item}). + +maybe_emit_stats(State) -> + ok = rabbit_event:if_enabled( + State, + #v1.stats_timer, + fun() -> emit_stats(State) end). + +emit_stats(State) -> + [{_, Pid}, + {_, RecvOct}, + {_, SendOct}, + {_, Reductions}] = infos(?SIMPLE_METRICS, State), + Infos = infos(?OTHER_METRICS, State), + rabbit_core_metrics:connection_stats(Pid, Infos), + rabbit_core_metrics:connection_stats(Pid, RecvOct, SendOct, Reductions), + %% NB: Don't call ensure_stats_timer because it becomes expensive + %% if all idle non-hibernating connections emit stats. + rabbit_event:reset_stats_timer(State, #v1.stats_timer). + +ensure_stats_timer(State) + when ?IS_RUNNING(State) -> + rabbit_event:ensure_stats_timer(State, #v1.stats_timer, emit_stats); +ensure_stats_timer(State) -> + State. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ignore_maintenance({map, Properties}) -> lists:member( diff --git a/deps/rabbit/src/rabbit_amqp_reader.hrl b/deps/rabbit/src/rabbit_amqp_reader.hrl new file mode 100644 index 000000000000..7c71b21dc90f --- /dev/null +++ b/deps/rabbit/src/rabbit_amqp_reader.hrl @@ -0,0 +1,17 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. + +-define(SIMPLE_METRICS, [pid, + recv_oct, + send_oct, + reductions]). + +-define(OTHER_METRICS, [recv_cnt, + send_cnt, + send_pend, + state, + channels, + garbage_collection]). diff --git a/deps/rabbit/src/rabbit_amqp_session.erl b/deps/rabbit/src/rabbit_amqp_session.erl index fdcc3de2be6b..7906fac2b2c5 100644 --- a/deps/rabbit/src/rabbit_amqp_session.erl +++ b/deps/rabbit/src/rabbit_amqp_session.erl @@ -11,6 +11,10 @@ -behaviour(gen_server). +<<<<<<< HEAD +======= +-include_lib("kernel/include/logger.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("amqp10_common/include/amqp10_types.hrl"). -include("rabbit_amqp.hrl"). @@ -30,6 +34,15 @@ }} }). +<<<<<<< HEAD +======= +-rabbit_deprecated_feature( + {amqp_filter_set_bug, + #{deprecation_phase => permitted_by_default, + doc_url => "https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-messaging-v1.0-os.html#type-filter-set" + }}). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% This is the link credit that we grant to sending clients. %% We are free to choose whatever we want, sending clients must obey. %% Default soft limits / credits in deps/rabbit/Makefile are: @@ -84,7 +97,13 @@ list_local/0, conserve_resources/3, check_resource_access/4, +<<<<<<< HEAD check_read_permitted_on_topic/4 +======= + check_read_permitted_on_topic/4, + reset_authz/2, + info/1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]). -export([init/1, @@ -140,7 +159,13 @@ }). -record(incoming_link, { +<<<<<<< HEAD + snd_settle_mode :: snd_settle_mode(), +======= + name :: binary(), snd_settle_mode :: snd_settle_mode(), + target_address :: null | binary(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% The exchange is either defined in the ATTACH frame and static for %% the life time of the link or dynamically provided in each message's %% "to" field (address v2). @@ -148,6 +173,10 @@ %% The routing key is either defined in the ATTACH frame and static for %% the life time of the link or dynamically provided in each message's %% "to" field (address v2) or "subject" field (address v1). +<<<<<<< HEAD +======= + %% (A publisher can set additional routing keys via the x-cc message annotation.) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) routing_key :: rabbit_types:routing_key() | to | subject, %% queue_name_bin is only set if the link target address refers to a queue. queue_name_bin :: undefined | rabbit_misc:resource_name(), @@ -188,6 +217,11 @@ }). -record(outgoing_link, { +<<<<<<< HEAD +======= + name :: binary(), + source_address :: binary(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Although the source address of a link might be an exchange name and binding key %% or a topic filter, an outgoing link will always consume from a queue. queue_name :: rabbit_amqqueue:name(), @@ -386,6 +420,13 @@ init({ReaderPid, WriterPid, ChannelNum, MaxFrameSize, User, Vhost, ConnName, handle_max = ClientHandleMax}}) -> process_flag(trap_exit, true), rabbit_process_flag:adjust_for_message_handling_proc(), +<<<<<<< HEAD +======= + logger:update_process_metadata(#{channel_number => ChannelNum, + connection => ConnName, + vhost => Vhost, + user => User#user.username}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = pg:join(pg_scope(), self(), self()), Alarms0 = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), @@ -473,6 +514,15 @@ list_local() -> conserve_resources(Pid, Source, {_, Conserve, _}) -> gen_server:cast(Pid, {conserve_resources, Source, Conserve}). +<<<<<<< HEAD +======= +-spec reset_authz(pid(), rabbit_types:user()) -> ok. +reset_authz(Pid, User) -> + gen_server:cast(Pid, {reset_authz, User}). + +handle_call(infos, _From, State) -> + reply(infos(State), State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_call(Msg, _From, State) -> Reply = {error, {not_understood, Msg}}, reply(Reply, State). @@ -567,15 +617,35 @@ handle_cast({conserve_resources, Alarm, Conserve}, noreply(State); handle_cast(refresh_config, #state{cfg = #cfg{vhost = Vhost} = Cfg} = State0) -> State = State0#state{cfg = Cfg#cfg{trace_state = rabbit_trace:init(Vhost)}}, +<<<<<<< HEAD noreply(State). +======= + noreply(State); +handle_cast({reset_authz, User}, #state{cfg = Cfg} = State0) -> + State1 = State0#state{ + permission_cache = [], + topic_permission_cache = [], + cfg = Cfg#cfg{user = User}}, + try recheck_authz(State1) of + State -> + noreply(State) + catch exit:#'v1_0.error'{} = Error -> + log_error_and_close_session(Error, State1) + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_error_and_close_session( Error, State = #state{cfg = #cfg{reader_pid = ReaderPid, writer_pid = WriterPid, channel_num = Ch}}) -> End = #'v1_0.end'{error = Error}, +<<<<<<< HEAD rabbit_log:warning("Closing session for connection ~p: ~tp", [ReaderPid, Error]), +======= + ?LOG_WARNING("Closing session for connection ~p: ~tp", + [ReaderPid, Error]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = rabbit_amqp_writer:send_command_sync(WriterPid, Ch, End), {stop, {shutdown, Error}, State}. @@ -862,8 +932,13 @@ destroy_outgoing_link(_, _, _, Acc) -> Acc. detach(Handle, Link, Error = #'v1_0.error'{}) -> +<<<<<<< HEAD rabbit_log:warning("Detaching link handle ~b due to error: ~tp", [Handle, Error]), +======= + ?LOG_WARNING("Detaching link handle ~b due to error: ~tp", + [Handle, Error]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) publisher_or_consumer_deleted(Link), #'v1_0.detach'{handle = ?UINT(Handle), closed = true, @@ -954,8 +1029,13 @@ handle_frame(#'v1_0.flow'{handle = Handle} = Flow, %% "If set to a handle that is not currently associated with %% an attached link, the recipient MUST respond by ending the %% session with an unattached-handle session error." [2.7.4] +<<<<<<< HEAD rabbit_log:warning( "Received Flow frame for unknown link handle: ~tp", [Flow]), +======= + ?LOG_WARNING("Received Flow frame for unknown link handle: ~tp", + [Flow]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) protocol_error( ?V_1_0_SESSION_ERROR_UNATTACHED_HANDLE, "Unattached link handle: ~b", [HandleInt]) @@ -1234,11 +1314,19 @@ handle_attach(#'v1_0.attach'{ reply_frames([Reply], State); handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER, +<<<<<<< HEAD name = LinkName, handle = Handle = ?UINT(HandleInt), source = Source, snd_settle_mode = MaybeSndSettleMode, target = Target, +======= + name = LinkName = {utf8, LinkName0}, + handle = Handle = ?UINT(HandleInt), + source = Source, + snd_settle_mode = MaybeSndSettleMode, + target = Target = #'v1_0.target'{address = TargetAddress}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) initial_delivery_count = DeliveryCount = ?UINT(DeliveryCountInt) }, State0 = #state{incoming_links = IncomingLinks0, @@ -1251,7 +1339,13 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER, SndSettleMode = snd_settle_mode(MaybeSndSettleMode), MaxMessageSize = persistent_term:get(max_message_size), IncomingLink = #incoming_link{ +<<<<<<< HEAD + snd_settle_mode = SndSettleMode, +======= + name = LinkName0, snd_settle_mode = SndSettleMode, + target_address = address(TargetAddress), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) exchange = Exchange, routing_key = RoutingKey, queue_name_bin = QNameBin, @@ -1288,12 +1382,23 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_SENDER, end; handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER, +<<<<<<< HEAD name = LinkName, handle = Handle = ?UINT(HandleInt), source = Source, snd_settle_mode = SndSettleMode, rcv_settle_mode = RcvSettleMode, max_message_size = MaybeMaxMessageSize} = Attach, +======= + name = LinkName = {utf8, LinkName0}, + handle = Handle = ?UINT(HandleInt), + source = Source = #'v1_0.source'{address = SourceAddress, + filter = DesiredFilter}, + snd_settle_mode = SndSettleMode, + rcv_settle_mode = RcvSettleMode, + max_message_size = MaybeMaxMessageSize, + properties = Properties}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State0 = #state{queue_states = QStates0, outgoing_links = OutgoingLinks0, permission_cache = PermCache0, @@ -1363,6 +1468,13 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER, credit_api_v1, credit_api_v1} end, +<<<<<<< HEAD +======= + ConsumerArgs0 = parse_attach_properties(Properties), + {EffectiveFilter, ConsumerFilter, ConsumerArgs1} = + parse_filter(DesiredFilter), + ConsumerArgs = ConsumerArgs0 ++ ConsumerArgs1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Spec = #{no_ack => SndSettled, channel_pid => self(), limiter_pid => none, @@ -1370,11 +1482,21 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER, mode => Mode, consumer_tag => handle_to_ctag(HandleInt), exclusive_consume => false, +<<<<<<< HEAD args => consumer_arguments(Attach), +======= + args => ConsumerArgs, + filter => ConsumerFilter, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok_msg => undefined, acting_user => Username}, case rabbit_queue_type:consume(Q, Spec, QStates0) of {ok, QStates} -> +<<<<<<< HEAD +======= + OfferedCaps0 = rabbit_queue_type:amqp_capabilities(QType), + OfferedCaps = rabbit_amqp_util:capabilities(OfferedCaps0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) A = #'v1_0.attach'{ name = LinkName, handle = Handle, @@ -1386,12 +1508,26 @@ handle_attach(#'v1_0.attach'{role = ?AMQP_ROLE_RECEIVER, %% will be requeued. That's why the we only support RELEASED as the default outcome. source = Source#'v1_0.source'{ default_outcome = #'v1_0.released'{}, +<<<<<<< HEAD outcomes = outcomes(Source)}, role = ?AMQP_ROLE_SENDER, %% Echo back that we will respect the client's requested max-message-size. max_message_size = MaybeMaxMessageSize}, MaxMessageSize = max_message_size(MaybeMaxMessageSize), Link = #outgoing_link{ +======= + outcomes = outcomes(Source), + %% "the sending endpoint sets the filter actually in place" [3.5.3] + filter = EffectiveFilter}, + role = ?AMQP_ROLE_SENDER, + %% Echo back that we will respect the client's requested max-message-size. + max_message_size = MaybeMaxMessageSize, + offered_capabilities = OfferedCaps}, + MaxMessageSize = max_message_size(MaybeMaxMessageSize), + Link = #outgoing_link{ + name = LinkName0, + source_address = address(SourceAddress), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) queue_name = queue_resource(Vhost, QNameBin), queue_type = QType, send_settled = SndSettled, @@ -1952,10 +2088,14 @@ session_flow_fields(Frames, State) session_flow_fields(Flow = #'v1_0.flow'{}, #state{next_outgoing_id = NextOutgoingId, next_incoming_id = NextIncomingId, +<<<<<<< HEAD incoming_window = IncomingWindow0}) -> %% IncomingWindow0 can be negative when the sending client overshoots our window. %% However, we must set a floor of 0 in the FLOW frame because field incoming-window is an uint. IncomingWindow = max(0, IncomingWindow0), +======= + incoming_window = IncomingWindow}) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Flow#'v1_0.flow'{ next_outgoing_id = ?UINT(NextOutgoingId), outgoing_window = ?UINT_OUTGOING_WINDOW, @@ -2126,9 +2266,15 @@ handle_deliver(ConsumerTag, AckRequired, outgoing_links = OutgoingLinks}; _ -> %% TODO handle missing link -- why does the queue think it's there? +<<<<<<< HEAD rabbit_log:warning( "No link handle ~b exists for delivery with consumer tag ~p from queue ~tp", [Handle, ConsumerTag, QName]), +======= + ?LOG_WARNING( + "No link handle ~b exists for delivery with consumer tag ~p from queue ~tp", + [Handle, ConsumerTag, QName]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State end. @@ -2348,16 +2494,30 @@ incoming_link_transfer( end, validate_transfer_snd_settle_mode(SndSettleMode, Settled), validate_transfer_rcv_settle_mode(RcvSettleMode, Settled), +<<<<<<< HEAD validate_message_size(PayloadBin, MaxMessageSize), +======= + PayloadSize = iolist_size(PayloadBin), + validate_message_size(PayloadSize, MaxMessageSize), + rabbit_msg_size_metrics:observe(?PROTOCOL, PayloadSize), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) messages_received(Settled), Mc0 = mc:init(mc_amqp, PayloadBin, #{}), case lookup_target(LinkExchange, LinkRKey, Mc0, Vhost, User, PermCache0) of +<<<<<<< HEAD {ok, X, RoutingKey, Mc1, PermCache} -> Mc2 = rabbit_message_interceptor:intercept(Mc1), check_user_id(Mc2, User), TopicPermCache = check_write_permitted_on_topic( X, User, RoutingKey, TopicPermCache0), +======= + {ok, X, RoutingKeys, Mc1, PermCache} -> + Mc2 = rabbit_message_interceptor:intercept(Mc1), + check_user_id(Mc2, User), + TopicPermCache = check_write_permitted_on_topics( + X, User, RoutingKeys, TopicPermCache0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) QNames = rabbit_exchange:route(X, Mc2, #{return_binding_keys => true}), rabbit_trace:tap_in(Mc2, QNames, ConnName, ChannelNum, Username, Trace), Opts = #{correlation => {HandleInt, DeliveryId}}, @@ -2392,14 +2552,22 @@ incoming_link_transfer( "delivery_tag=~p, delivery_id=~p, reason=~p", [DeliveryTag, DeliveryId, Reason]) end; +<<<<<<< HEAD {error, #'v1_0.error'{} = Err} -> +======= + {error, {anonymous_terminus, false}, #'v1_0.error'{} = Err} -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Disposition = case Settled of true -> []; false -> [released(DeliveryId)] end, Detach = [detach(HandleInt, Link0, Err)], {error, Disposition ++ Detach}; +<<<<<<< HEAD {error, anonymous_terminus, #'v1_0.error'{} = Err} -> +======= + {error, {anonymous_terminus, true}, #'v1_0.error'{} = Err} -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% https://docs.oasis-open.org/amqp/anonterm/v1.0/cs01/anonterm-v1.0-cs01.html#doc-routingerrors case Settled of true -> @@ -2424,6 +2592,7 @@ incoming_link_transfer( end. lookup_target(#exchange{} = X, LinkRKey, Mc, _, _, PermCache) -> +<<<<<<< HEAD lookup_routing_key(X, LinkRKey, Mc, PermCache); lookup_target(#resource{} = XName, LinkRKey, Mc, _, _, PermCache) -> case rabbit_exchange:lookup(XName) of @@ -2431,6 +2600,15 @@ lookup_target(#resource{} = XName, LinkRKey, Mc, _, _, PermCache) -> lookup_routing_key(X, LinkRKey, Mc, PermCache); {error, not_found} -> {error, error_not_found(XName)} +======= + lookup_routing_key(X, LinkRKey, Mc, false, PermCache); +lookup_target(#resource{} = XName, LinkRKey, Mc, _, _, PermCache) -> + case rabbit_exchange:lookup(XName) of + {ok, X} -> + lookup_routing_key(X, LinkRKey, Mc, false, PermCache); + {error, not_found} -> + {error, {anonymous_terminus, false}, error_not_found(XName)} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end; lookup_target(to, to, Mc, Vhost, User, PermCache0) -> case mc:property(to, Mc) of @@ -2442,25 +2620,43 @@ lookup_target(to, to, Mc, Vhost, User, PermCache0) -> case rabbit_exchange:lookup(XName) of {ok, X} -> check_internal_exchange(X), +<<<<<<< HEAD lookup_routing_key(X, RKey, Mc, PermCache); {error, not_found} -> {error, anonymous_terminus, error_not_found(XName)} end; {error, bad_address} -> {error, anonymous_terminus, +======= + lookup_routing_key(X, RKey, Mc, true, PermCache); + {error, not_found} -> + {error, {anonymous_terminus, true}, error_not_found(XName)} + end; + {error, bad_address} -> + {error, {anonymous_terminus, true}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'v1_0.error'{ condition = ?V_1_0_AMQP_ERROR_PRECONDITION_FAILED, description = {utf8, <<"bad 'to' address string: ", String/binary>>}}} end; undefined -> +<<<<<<< HEAD {error, anonymous_terminus, +======= + {error, {anonymous_terminus, true}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'v1_0.error'{ condition = ?V_1_0_AMQP_ERROR_PRECONDITION_FAILED, description = {utf8, <<"anonymous terminus requires 'to' address to be set">>}}} end. lookup_routing_key(X = #exchange{name = #resource{name = XNameBin}}, +<<<<<<< HEAD RKey0, Mc0, PermCache) -> +======= + RKey0, Mc0, AnonTerm, PermCache) -> + Mc1 = mc:set_annotation(?ANN_EXCHANGE, XNameBin, Mc0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) RKey = case RKey0 of subject -> case mc:property(subject, Mc0) of @@ -2472,9 +2668,37 @@ lookup_routing_key(X = #exchange{name = #resource{name = XNameBin}}, _ when is_binary(RKey0) -> RKey0 end, +<<<<<<< HEAD Mc1 = mc:set_annotation(?ANN_EXCHANGE, XNameBin, Mc0), Mc = mc:set_annotation(?ANN_ROUTING_KEYS, [RKey], Mc1), {ok, X, RKey, Mc, PermCache}. +======= + case mc:x_header(<<"x-cc">>, Mc0) of + undefined -> + RKeys = [RKey], + Mc = mc:set_annotation(?ANN_ROUTING_KEYS, RKeys, Mc1), + {ok, X, RKeys, Mc, PermCache}; + {list, CCs0} = L -> + try lists:map(fun({utf8, CC}) -> CC end, CCs0) of + CCs -> + RKeys = [RKey | CCs], + Mc = mc:set_annotation(?ANN_ROUTING_KEYS, RKeys, Mc1), + {ok, X, RKeys, Mc, PermCache} + catch error:function_clause -> + {error, {anonymous_terminus, AnonTerm}, bad_x_cc(L)} + end; + BadValue -> + {error, {anonymous_terminus, AnonTerm}, bad_x_cc(BadValue)} + end. + +bad_x_cc(Value) -> + Desc = unicode:characters_to_binary( + lists:flatten( + io_lib:format( + "bad value for 'x-cc' message-annotation: ~tp", [Value]))), + #'v1_0.error'{condition = ?V_1_0_AMQP_ERROR_INVALID_FIELD, + description = {utf8, Desc}}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) process_routing_confirm([], _SenderSettles = true, _, U) -> rabbit_global_counters:messages_unroutable_dropped(?PROTOCOL, 1), @@ -2611,6 +2835,14 @@ ensure_source_v1(Address, Err end. +<<<<<<< HEAD +======= +address(undefined) -> + null; +address({utf8, String}) -> + String. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec ensure_target(#'v1_0.target'{}, rabbit_types:vhost(), rabbit_types:user(), @@ -2710,11 +2942,18 @@ parse_target_v2_string(String) -> end. parse_target_v2_string0(<<"/exchanges/", Rest/binary>>) -> +<<<<<<< HEAD Key = cp_slash, Pattern = try persistent_term:get(Key) catch error:badarg -> Cp = binary:compile_pattern(<<"/">>), ok = persistent_term:put(Key, Cp), +======= + Pattern = try persistent_term:get(cp_slash) + catch error:badarg -> + Cp = binary:compile_pattern(<<"/">>), + ok = persistent_term:put(cp_slash, Cp), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Cp end, case binary:split(Rest, Pattern, [global]) of @@ -2949,7 +3188,11 @@ credit_reply_timeout(QType, QName) -> Fmt = "Timed out waiting for credit reply from ~s ~s. " "Hint: Enable feature flag rabbitmq_4.0.0", Args = [QType, rabbit_misc:rs(QName)], +<<<<<<< HEAD rabbit_log:error(Fmt, Args), +======= + ?LOG_ERROR(Fmt, Args), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) protocol_error(?V_1_0_AMQP_ERROR_INTERNAL_ERROR, Fmt, Args). default(undefined, Default) -> Default; @@ -2985,6 +3228,7 @@ encode_frames(T, Msg, MaxPayloadSize, Transfers) -> lists:reverse([[T, Msg] | Transfers]) end. +<<<<<<< HEAD consumer_arguments(#'v1_0.attach'{ source = #'v1_0.source'{filter = Filter}, properties = Properties}) -> @@ -2992,12 +3236,18 @@ consumer_arguments(#'v1_0.attach'{ filter_to_consumer_args(Filter). properties_to_consumer_args({map, KVList}) -> +======= +parse_attach_properties(undefined) -> + []; +parse_attach_properties({map, KVList}) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Key = {symbol, <<"rabbitmq:priority">>}, case proplists:lookup(Key, KVList) of {Key, Val = {int, _Prio}} -> [mc_amqpl:to_091(<<"x-priority">>, Val)]; _ -> [] +<<<<<<< HEAD end; properties_to_consumer_args(_) -> []. @@ -3066,6 +3316,82 @@ keyfind_unpack_described(Key, KvList) -> Kv; false -> false +======= + end. + +parse_filter(undefined) -> + {undefined, [], []}; +parse_filter({map, DesiredKVList}) -> + {EffectiveKVList, ConsusumerFilter, ConsumerArgs} = + lists:foldr(fun parse_filters/2, {[], [], []}, DesiredKVList), + {{map, EffectiveKVList}, ConsusumerFilter, ConsumerArgs}. + +parse_filters(Filter = {{symbol, _Key}, {described, {symbol, <<"rabbitmq:stream-offset-spec">>}, Value}}, + Acc = {EffectiveFilters, ConsumerFilter, ConsumerArgs}) -> + case Value of + {timestamp, Ts} -> + %% 0.9.1 uses second based timestamps + Arg = {<<"x-stream-offset">>, timestamp, Ts div 1000}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; + {utf8, Spec} -> + %% next, last, first and "10m" etc + Arg = {<<"x-stream-offset">>, longstr, Spec}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; + {_Type, Offset} + when is_integer(Offset) andalso Offset >= 0 -> + Arg = {<<"x-stream-offset">>, long, Offset}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; + _ -> + Acc + end; +parse_filters(Filter = {{symbol, _Key}, {described, {symbol, <<"rabbitmq:stream-filter">>}, Value}}, + Acc = {EffectiveFilters, ConsumerFilter, ConsumerArgs}) -> + case Value of + {list, Filters0} -> + Filters = lists:filtermap(fun({utf8, Filter0}) -> + {true, {longstr, Filter0}}; + (_) -> + false + end, Filters0), + Arg = {<<"x-stream-filter">>, array, Filters}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; + + {utf8, Filter0} -> + Arg = {<<"x-stream-filter">>, longstr, Filter0}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; + _ -> + Acc + end; +parse_filters(Filter = {{symbol, _Key}, {described, {symbol, <<"rabbitmq:stream-match-unfiltered">>}, Match}}, + {EffectiveFilters, ConsumerFilter, ConsumerArgs}) + when is_boolean(Match) -> + Arg = {<<"x-stream-match-unfiltered">>, bool, Match}, + {[Filter | EffectiveFilters], ConsumerFilter, [Arg | ConsumerArgs]}; +parse_filters({Symbol = {symbol, <<"rabbitmq:stream-", _/binary>>}, Value}, Acc) + when element(1, Value) =/= described -> + case rabbit_deprecated_features:is_permitted(amqp_filter_set_bug) of + true -> + parse_filters({Symbol, {described, Symbol, Value}}, Acc); + false -> + Acc + end; +parse_filters(Filter = {{symbol, _Key}, Value}, + Acc = {EffectiveFilters, ConsumerFilter, ConsumerArgs}) -> + case rabbit_amqp_filtex:validate(Value) of + {ok, FilterExpression = {FilterType, _}} -> + case proplists:is_defined(FilterType, ConsumerFilter) of + true -> + %% For now, let's prohibit multiple top level filters of the same type + %% (properties or application-properties). There should be no use case. + %% In future, we can allow multiple times the same top level grouping + %% filter expression type (all/any/not). + Acc; + false -> + {[Filter | EffectiveFilters], [FilterExpression | ConsumerFilter], ConsumerArgs} + end; + error -> + Acc +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. validate_attach(#'v1_0.attach'{target = #'v1_0.coordinator'{}}) -> @@ -3134,9 +3460,14 @@ validate_transfer_rcv_settle_mode(_, _) -> validate_message_size(_, unlimited) -> ok; +<<<<<<< HEAD validate_message_size(Message, MaxMsgSize) when is_integer(MaxMsgSize) -> MsgSize = iolist_size(Message), +======= +validate_message_size(MsgSize, MaxMsgSize) + when is_integer(MsgSize) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case MsgSize =< MaxMsgSize of true -> ok; @@ -3150,7 +3481,13 @@ validate_message_size(Message, MaxMsgSize) ?V_1_0_LINK_ERROR_MESSAGE_SIZE_EXCEEDED, "message size (~b bytes) > maximum message size (~b bytes)", [MsgSize, MaxMsgSize]) +<<<<<<< HEAD end. +======= + end; +validate_message_size(Msg, MaxMsgSize) -> + validate_message_size(iolist_size(Msg), MaxMsgSize). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec ensure_terminus(source | target, term(), @@ -3427,6 +3764,7 @@ check_resource_access(Resource, Perm, User, Cache) -> end end. +<<<<<<< HEAD -spec check_write_permitted_on_topic( rabbit_types:exchange(), rabbit_types:user(), @@ -3435,6 +3773,22 @@ check_resource_access(Resource, Perm, User, Cache) -> topic_permission_cache(). check_write_permitted_on_topic(Resource, User, RoutingKey, TopicPermCache) -> check_topic_authorisation(Resource, User, RoutingKey, write, TopicPermCache). +======= +-spec check_write_permitted_on_topics( + rabbit_types:exchange(), + rabbit_types:user(), + [rabbit_types:routing_key(),...], + topic_permission_cache()) -> + topic_permission_cache(). +check_write_permitted_on_topics(#exchange{type = topic} = Resource, + User, RoutingKeys, TopicPermCache) -> + lists:foldl( + fun(RoutingKey, Cache) -> + check_topic_authorisation(Resource, User, RoutingKey, write, Cache) + end, TopicPermCache, RoutingKeys); +check_write_permitted_on_topics(_, _, _, TopicPermCache) -> + TopicPermCache. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec check_read_permitted_on_topic( rabbit_types:exchange(), @@ -3474,6 +3828,32 @@ check_topic_authorisation(#exchange{type = topic, check_topic_authorisation(_, _, _, _, Cache) -> Cache. +<<<<<<< HEAD +======= +recheck_authz(#state{incoming_links = IncomingLinks, + outgoing_links = OutgoingLinks, + permission_cache = Cache0, + cfg = #cfg{user = User} + } = State) -> + ?LOG_DEBUG("rechecking link authorizations", []), + Cache1 = maps:fold( + fun(_Handle, #incoming_link{exchange = X}, Cache) -> + case X of + #exchange{name = XName} -> + check_resource_access(XName, write, User, Cache); + #resource{} = XName -> + check_resource_access(XName, write, User, Cache); + to -> + Cache + end + end, Cache0, IncomingLinks), + Cache2 = maps:fold( + fun(_Handle, #outgoing_link{queue_name = QName}, Cache) -> + check_resource_access(QName, read, User, Cache) + end, Cache1, OutgoingLinks), + State#state{permission_cache = Cache2}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) check_user_id(Mc, User) -> case rabbit_access_control:check_user_id(Mc, User) of ok -> @@ -3610,6 +3990,121 @@ format_status( topic_permission_cache => TopicPermissionCache}, maps:update(state, State, Status). +<<<<<<< HEAD +======= +-spec info(pid()) -> + {ok, rabbit_types:infos()} | {error, term()}. +info(Pid) -> + try gen_server:call(Pid, infos) of + Infos -> + {ok, Infos} + catch _:Reason -> + {error, Reason} + end. + +infos(#state{cfg = #cfg{channel_num = ChannelNum, + max_handle = MaxHandle}, + next_incoming_id = NextIncomingId, + incoming_window = IncomingWindow, + next_outgoing_id = NextOutgoingId, + remote_incoming_window = RemoteIncomingWindow, + remote_outgoing_window = RemoteOutgoingWindow, + outgoing_unsettled_map = OutgoingUnsettledMap, + incoming_links = IncomingLinks, + outgoing_links = OutgoingLinks, + incoming_management_links = IncomingManagementLinks, + outgoing_management_links = OutgoingManagementLinks + }) -> + [ + {channel_number, ChannelNum}, + {handle_max, MaxHandle}, + {next_incoming_id, NextIncomingId}, + {incoming_window, IncomingWindow}, + {next_outgoing_id, NextOutgoingId}, + {remote_incoming_window, RemoteIncomingWindow}, + {remote_outgoing_window, RemoteOutgoingWindow}, + {outgoing_unsettled_deliveries, maps:size(OutgoingUnsettledMap)}, + {incoming_links, + info_incoming_management_links(IncomingManagementLinks) ++ + info_incoming_links(IncomingLinks)}, + {outgoing_links, + info_outgoing_management_links(OutgoingManagementLinks) ++ + info_outgoing_links(OutgoingLinks)} + ]. + +info_incoming_management_links(Links) -> + [info_incoming_link(Handle, Name, settled, ?MANAGEMENT_NODE_ADDRESS, + MaxMessageSize, DeliveryCount, Credit, 0) + || Handle := #management_link{ + name = Name, + max_message_size = MaxMessageSize, + delivery_count = DeliveryCount, + credit = Credit} <- Links]. + +info_incoming_links(Links) -> + [info_incoming_link(Handle, Name, SndSettleMode, TargetAddress, MaxMessageSize, + DeliveryCount, Credit, maps:size(IncomingUnconfirmedMap)) + || Handle := #incoming_link{ + name = Name, + snd_settle_mode = SndSettleMode, + target_address = TargetAddress, + max_message_size = MaxMessageSize, + delivery_count = DeliveryCount, + credit = Credit, + incoming_unconfirmed_map = IncomingUnconfirmedMap} <- Links]. + +info_incoming_link(Handle, LinkName, SndSettleMode, TargetAddress, + MaxMessageSize, DeliveryCount, Credit, UnconfirmedMessages) -> + [{handle, Handle}, + {link_name, LinkName}, + {snd_settle_mode, SndSettleMode}, + {target_address, TargetAddress}, + {max_message_size, MaxMessageSize}, + {delivery_count, DeliveryCount}, + {credit, Credit}, + {unconfirmed_messages, UnconfirmedMessages}]. + +info_outgoing_management_links(Links) -> + [info_outgoing_link(Handle, Name, ?MANAGEMENT_NODE_ADDRESS, <<>>, + true, MaxMessageSize, DeliveryCount, Credit) + || Handle := #management_link{ + name = Name, + max_message_size = MaxMessageSize, + delivery_count = DeliveryCount, + credit = Credit} <- Links]. + +info_outgoing_links(Links) -> + [begin + {DeliveryCount, Credit} = case ClientFlowCtl of + #client_flow_ctl{delivery_count = DC, + credit = C} -> + {DC, C}; + credit_api_v1 -> + {'', ''} + end, + info_outgoing_link(Handle, Name, SourceAddress, QueueName#resource.name, + SendSettled, MaxMessageSize, DeliveryCount, Credit) + + end + || Handle := #outgoing_link{ + name = Name, + source_address = SourceAddress, + queue_name = QueueName, + max_message_size = MaxMessageSize, + send_settled = SendSettled, + client_flow_ctl = ClientFlowCtl} <- Links]. + +info_outgoing_link(Handle, LinkName, SourceAddress, QueueNameBin, SendSettled, + MaxMessageSize, DeliveryCount, Credit) -> + [{handle, Handle}, + {link_name, LinkName}, + {source_address, SourceAddress}, + {queue_name, QueueNameBin}, + {send_settled, SendSettled}, + {max_message_size, MaxMessageSize}, + {delivery_count, DeliveryCount}, + {credit, Credit}]. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) unwrap_simple_type(V = {list, _}) -> V; diff --git a/deps/rabbit/src/rabbit_amqp_util.erl b/deps/rabbit/src/rabbit_amqp_util.erl index 3257cef93704..4fb605730927 100644 --- a/deps/rabbit/src/rabbit_amqp_util.erl +++ b/deps/rabbit/src/rabbit_amqp_util.erl @@ -8,7 +8,12 @@ -module(rabbit_amqp_util). -include("rabbit_amqp.hrl"). +<<<<<<< HEAD -export([protocol_error/3]). +======= +-export([protocol_error/3, + capabilities/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec protocol_error(term(), io:format(), [term()]) -> no_return(). @@ -17,3 +22,14 @@ protocol_error(Condition, Msg, Args) -> Reason = #'v1_0.error'{condition = Condition, description = {utf8, Description}}, exit(Reason). +<<<<<<< HEAD +======= + +-spec capabilities([binary()]) -> + undefined | {array, symbol, [{symbol, binary()}]}. +capabilities([]) -> + undefined; +capabilities(Capabilities) -> + Caps = [{symbol, C} || C <- Capabilities], + {array, symbol, Caps}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit/src/rabbit_amqp_writer.erl b/deps/rabbit/src/rabbit_amqp_writer.erl index 7b239a10a107..ffa1512c345e 100644 --- a/deps/rabbit/src/rabbit_amqp_writer.erl +++ b/deps/rabbit/src/rabbit_amqp_writer.erl @@ -31,7 +31,12 @@ pending :: iolist(), %% This field is just an optimisation to minimize the cost of erlang:iolist_size/1 pending_size :: non_neg_integer(), +<<<<<<< HEAD monitored_sessions :: #{pid() => true} +======= + monitored_sessions :: #{pid() => true}, + stats_timer :: rabbit_event:state() +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }). -define(HIBERNATE_AFTER, 6_000). @@ -100,7 +105,12 @@ init({Sock, ReaderPid}) -> reader = ReaderPid, pending = [], pending_size = 0, +<<<<<<< HEAD monitored_sessions = #{}}, +======= + monitored_sessions = #{}, + stats_timer = rabbit_event:init_stats_timer()}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) process_flag(message_queue_data, off_heap), {ok, State}. @@ -123,6 +133,13 @@ handle_call({send_command, ChannelNum, Performative}, _From, State0) -> State = flush(State1), {reply, ok, State}. +<<<<<<< HEAD +======= +handle_info(emit_stats, State0 = #state{reader = ReaderPid}) -> + ReaderPid ! ensure_stats_timer, + State = rabbit_event:reset_stats_timer(State0, #state.stats_timer), + no_reply(State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_info(timeout, State0) -> State = flush(State0), {noreply, State}; @@ -223,18 +240,32 @@ tcp_send(Sock, Data) -> maybe_flush(State = #state{pending_size = PendingSize}) -> case PendingSize > ?FLUSH_THRESHOLD of +<<<<<<< HEAD true -> flush(State); +======= + true -> flush(State); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) false -> State end. flush(State = #state{pending = []}) -> State; +<<<<<<< HEAD flush(State = #state{sock = Sock, pending = Pending}) -> case rabbit_net:send(Sock, lists:reverse(Pending)) of ok -> State#state{pending = [], pending_size = 0}; +======= +flush(State0 = #state{sock = Sock, + pending = Pending}) -> + case rabbit_net:send(Sock, lists:reverse(Pending)) of + ok -> + State = State0#state{pending = [], + pending_size = 0}, + rabbit_event:ensure_stats_timer(State, #state.stats_timer, emit_stats); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {error, Reason} -> exit({writer, send_failed, Reason}) end. diff --git a/deps/rabbit/src/rabbit_amqqueue.erl b/deps/rabbit/src/rabbit_amqqueue.erl index 721fbc3f9efd..cc5bdf499c1e 100644 --- a/deps/rabbit/src/rabbit_amqqueue.erl +++ b/deps/rabbit/src/rabbit_amqqueue.erl @@ -762,6 +762,13 @@ augment_declare_args(VHost, Durable, Exclusive, AutoDelete, Args0) -> end end. +<<<<<<< HEAD +======= +-spec update_args_table_with_queue_type( + rabbit_queue_type:queue_type() | binary(), + boolean(), boolean(), boolean(), + rabbit_framing:amqp_table()) -> rabbit_framing:amqp_table(). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) update_args_table_with_queue_type(DefaultQueueType, Durable, Exclusive, AutoDelete, Args) -> Type = rabbit_queue_type:discover(DefaultQueueType), IsPermitted = is_queue_args_combination_permitted( @@ -1834,8 +1841,13 @@ internal_delete(Queue, ActingUser, Reason) -> {error, timeout} = Err -> Err; Deletions -> +<<<<<<< HEAD _ = rabbit_binding:process_deletions(Deletions), rabbit_binding:notify_deletions(Deletions, ?INTERNAL_USER), +======= + ok = rabbit_binding:process_deletions(Deletions), + ok = rabbit_binding:notify_deletions(Deletions, ?INTERNAL_USER), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_core_metrics:queue_deleted(QueueName), ok = rabbit_event:notify(queue_deleted, [{name, QueueName}, @@ -1958,6 +1970,7 @@ filter_transient_queues_to_delete(Node) -> end. notify_queue_binding_deletions(QueueDeletions) when is_list(QueueDeletions) -> +<<<<<<< HEAD Deletions = rabbit_binding:process_deletions( lists:foldl(fun rabbit_binding:combine_deletions/2, rabbit_binding:new_deletions(), @@ -1966,6 +1979,16 @@ notify_queue_binding_deletions(QueueDeletions) when is_list(QueueDeletions) -> notify_queue_binding_deletions(QueueDeletions) -> Deletions = rabbit_binding:process_deletions(QueueDeletions), rabbit_binding:notify_deletions(Deletions, ?INTERNAL_USER). +======= + Deletions = lists:foldl( + fun rabbit_binding:combine_deletions/2, + rabbit_binding:new_deletions(), QueueDeletions), + ok = rabbit_binding:process_deletions(Deletions), + rabbit_binding:notify_deletions(Deletions, ?INTERNAL_USER); +notify_queue_binding_deletions(QueueDeletions) -> + ok = rabbit_binding:process_deletions(QueueDeletions), + rabbit_binding:notify_deletions(QueueDeletions, ?INTERNAL_USER). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) notify_transient_queues_deleted(QueueDeletions) -> lists:foreach( diff --git a/deps/rabbit/src/rabbit_binding.erl b/deps/rabbit/src/rabbit_binding.erl index cf7f79b51e6a..7a69689d765a 100644 --- a/deps/rabbit/src/rabbit_binding.erl +++ b/deps/rabbit/src/rabbit_binding.erl @@ -13,7 +13,11 @@ -export([list/1, list_for_source/1, list_for_destination/1, list_for_source_and_destination/2, list_for_source_and_destination/3, list_explicit/0]). +<<<<<<< HEAD -export([new_deletions/0, combine_deletions/2, add_deletion/3, +======= +-export([new_deletions/0, combine_deletions/2, add_deletion/5, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) process_deletions/1, notify_deletions/2, group_bindings_fold/3]). -export([info_keys/0, info/1, info/2, info_all/1, info_all/2, info_all/4]). @@ -22,6 +26,12 @@ -export([reverse_route/1, index_route/1]). -export([binding_type/2]). +<<<<<<< HEAD +======= +%% For testing only +-export([fetch_deletion/2]). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -define(DEFAULT_EXCHANGE(VHostPath), #resource{virtual_host = VHostPath, kind = exchange, name = <<>>}). @@ -50,9 +60,18 @@ rabbit_types:ok_or_error(rabbit_types:amqp_error())). -type bindings() :: [rabbit_types:binding()]. +<<<<<<< HEAD %% TODO this should really be opaque but that seems to confuse 17.1's %% dialyzer into objecting to everything that uses it. -type deletions() :: dict:dict(). +======= +-record(deletion, {exchange :: rabbit_types:exchange(), + %% Whether the exchange was deleted. + deleted :: boolean(), + bindings :: sets:set(rabbit_types:binding())}). + +-opaque deletions() :: #{XName :: rabbit_exchange:name() => #deletion{}}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %%---------------------------------------------------------------------------- @@ -159,6 +178,22 @@ binding_type0(false, true) -> binding_type0(_, _) -> transient. +<<<<<<< HEAD +======= +binding_checks(Binding, InnerFun) -> + fun(Src, Dst) -> + case rabbit_exchange:validate_binding(Src, Binding) of + ok -> + %% this argument is used to check queue exclusivity; + %% in general, we want to fail on that in preference to + %% anything else + InnerFun(Src, Dst); + Err -> + Err + end + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec remove(rabbit_types:binding(), rabbit_types:username()) -> bind_res(). remove(Binding, ActingUser) -> remove(Binding, fun (_Src, _Dst) -> ok end, ActingUser). @@ -360,6 +395,7 @@ index_route(#route{binding = #binding{source = Source, %% ---------------------------------------------------------------------------- %% Binding / exchange deletion abstraction API %% ---------------------------------------------------------------------------- +<<<<<<< HEAD anything_but( NotThis, NotThis, NotThis) -> NotThis; anything_but( NotThis, NotThis, This) -> This; @@ -411,6 +447,98 @@ notify_deletions(Deletions, ActingUser) -> (_XName, {_X, not_deleted, Bs}, ok) -> notify_bindings_deletion(Bs, ActingUser) end, ok, Deletions). +======= +%% +%% `deletions()' describe a set of removals of bindings and/or exchanges from +%% the metadata store. +%% +%% This deletion collection is used for two purposes: +%% +%%
    +%%
  • "Processing" of deletions. Processing here means that the +%% exchanges and bindings are passed into the {@link rabbit_exchange} +%% callbacks. When an exchange is deleted the `rabbit_exchange:delete/1' +%% callback is invoked and when the exchange is not deleted but some bindings +%% are deleted the `rabbit_exchange:remove_bindings/2' is invoked.
  • +%%
  • Notification of metadata deletion. Like other internal +%% notifications, {@link rabbit_binding:notify_deletions()} uses {@link +%% rabbit_event} to notify any interested consumers of a resource deletion. +%% An example consumer of {@link rabbit_event} is the `rabbitmq_event_exchange' +%% plugin which publishes these notifications as messages.
  • +%%
+%% +%% The point of collecting deletions into this opaque type is to be able to +%% collect all bindings deleted for a given exchange into a list. This allows +%% us to invoke the `rabbit_exchange:remove_bindings/2' callback with all +%% deleted bindings at once rather than passing each deleted binding +%% individually. + +-spec new_deletions() -> deletions(). + +new_deletions() -> #{}. + +-spec add_deletion(XName, X, XDeleted, Bindings, Deletions) -> Deletions1 + when + XName :: rabbit_exchange:name(), + X :: rabbit_types:exchange(), + XDeleted :: deleted | not_deleted, + Bindings :: bindings(), + Deletions :: deletions(), + Deletions1 :: deletions(). + +add_deletion(XName, X, WasDeleted, Bindings, Deletions) + when (WasDeleted =:= deleted orelse WasDeleted =:= not_deleted) andalso + is_list(Bindings) andalso is_map(Deletions) -> + WasDeleted1 = case WasDeleted of + deleted -> true; + not_deleted -> false + end, + Bindings1 = sets:from_list(Bindings, [{version, 2}]), + Deletion = #deletion{exchange = X, + deleted = WasDeleted1, + bindings = Bindings1}, + maps:update_with( + XName, + fun(Deletion1) -> + merge_deletion(Deletion1, Deletion) + end, Deletion, Deletions). + +-spec combine_deletions(deletions(), deletions()) -> deletions(). + +combine_deletions(Deletions1, Deletions2) + when is_map(Deletions1) andalso is_map(Deletions2) -> + maps:merge_with( + fun (_XName, Deletion1, Deletion2) -> + merge_deletion(Deletion1, Deletion2) + end, Deletions1, Deletions2). + +merge_deletion( + #deletion{deleted = Deleted1, bindings = Bindings1}, + #deletion{exchange = X2, deleted = Deleted2, bindings = Bindings2}) -> + %% Assume that X2 is more up to date than X1. + X = X2, + Deleted = Deleted1 orelse Deleted2, + Bindings = sets:union(Bindings1, Bindings2), + #deletion{exchange = X, + deleted = Deleted, + bindings = Bindings}. + +-spec notify_deletions(Deletions, ActingUser) -> ok when + Deletions :: rabbit_binding:deletions(), + ActingUser :: rabbit_types:username(). + +notify_deletions(Deletions, ActingUser) when is_map(Deletions) -> + maps:foreach( + fun (XName, #deletion{deleted = XDeleted, bindings = Bindings}) -> + case XDeleted of + true -> + notify_exchange_deletion(XName, ActingUser), + notify_bindings_deletion(Bindings, ActingUser); + false -> + notify_bindings_deletion(Bindings, ActingUser) + end + end, Deletions). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) notify_exchange_deletion(XName, ActingUser) -> ok = rabbit_event:notify( @@ -418,6 +546,7 @@ notify_exchange_deletion(XName, ActingUser) -> [{name, XName}, {user_who_performed_action, ActingUser}]). +<<<<<<< HEAD notify_bindings_deletion(Bs, ActingUser) -> [rabbit_event:notify(binding_deleted, info(B) ++ [{user_who_performed_action, ActingUser}]) @@ -449,4 +578,60 @@ binding_checks(Binding, InnerFun) -> Err -> Err end +======= +notify_bindings_deletion(Bindings, ActingUser) -> + sets:fold( + fun(Binding, ok) -> + rabbit_event:notify( + binding_deleted, + info(Binding) ++ [{user_who_performed_action, ActingUser}]), + ok + end, ok, Bindings). + +-spec process_deletions(deletions()) -> ok. +process_deletions(Deletions) -> + maps:foreach( + fun (_XName, #deletion{exchange = X, + deleted = XDeleted, + bindings = Bindings}) -> + Serial = rabbit_exchange:serial(X), + case XDeleted of + true -> + rabbit_exchange:callback(X, delete, Serial, [X]); + false -> + Bindings1 = sets:to_list(Bindings), + rabbit_exchange:callback( + X, remove_bindings, Serial, [X, Bindings1]) + end + end, Deletions). + +-spec fetch_deletion(XName, Deletions) -> Ret when + XName :: rabbit_exchange:name(), + Deletions :: deletions(), + Ret :: {X, WasDeleted, Bindings}, + X :: rabbit_types:exchange(), + WasDeleted :: deleted | not_deleted, + Bindings :: bindings(). +%% @doc Fetches the deletions for the given exchange name. +%% +%% This function is only intended for use in tests. +%% +%% @private + +fetch_deletion(XName, Deletions) -> + case maps:find(XName, Deletions) of + {ok, #deletion{exchange = X, + deleted = Deleted, + bindings = Bindings}} -> + WasDeleted = case Deleted of + true -> + deleted; + false -> + not_deleted + end, + Bindings1 = sets:to_list(Bindings), + {X, WasDeleted, Bindings1}; + error -> + error +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. diff --git a/deps/rabbit/src/rabbit_channel.erl b/deps/rabbit/src/rabbit_channel.erl index 303776471396..14d12e203d15 100644 --- a/deps/rabbit/src/rabbit_channel.erl +++ b/deps/rabbit/src/rabbit_channel.erl @@ -471,7 +471,11 @@ force_event_refresh(Ref) -> list_queue_states(Pid) -> gen_server2:call(Pid, list_queue_states). +<<<<<<< HEAD -spec update_user_state(pid(), rabbit_types:auth_user()) -> 'ok' | {error, channel_terminated}. +======= +-spec update_user_state(pid(), rabbit_types:user()) -> 'ok' | {error, channel_terminated}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) update_user_state(Pid, UserState) when is_pid(Pid) -> case erlang:is_process_alive(Pid) of @@ -997,7 +1001,11 @@ check_msg_size(Content, GCThreshold) -> Size = rabbit_basic:maybe_gc_large_msg(Content, GCThreshold), case Size =< MaxMessageSize of true -> +<<<<<<< HEAD ok; +======= + rabbit_msg_size_metrics:observe(amqp091, Size); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) false -> Fmt = case MaxMessageSize of ?MAX_MSG_SIZE -> diff --git a/deps/rabbit/src/rabbit_core_ff.erl b/deps/rabbit/src/rabbit_core_ff.erl index 5475909eec54..0de041415300 100644 --- a/deps/rabbit/src/rabbit_core_ff.erl +++ b/deps/rabbit/src/rabbit_core_ff.erl @@ -10,14 +10,24 @@ -rabbit_feature_flag( {classic_mirrored_queue_version, #{desc => "Support setting version for classic mirrored queues", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {quorum_queue, #{desc => "Support queues of type `quorum`", doc_url => "https://www.rabbitmq.com/docs/quorum-queues", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( @@ -25,6 +35,10 @@ #{desc => "Support queues of type `stream`", doc_url => "https://www.rabbitmq.com/docs/stream", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [quorum_queue] }}). @@ -32,18 +46,29 @@ {implicit_default_bindings, #{desc => "Default bindings are now implicit, instead of " "being stored in the database", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {virtual_host_metadata, #{desc => "Virtual host metadata (description, tags, etc)", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {maintenance_mode_status, #{desc => "Maintenance mode status", +<<<<<<< HEAD stability => required }}). @@ -51,6 +76,17 @@ {user_limits, #{desc => "Configure connection and channel limits for a user", stability => required +======= + stability => required, + require_level => hard + }}). + +-rabbit_feature_flag( + {user_limits, + #{desc => "Configure connection and channel limits for a user", + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( @@ -58,33 +94,62 @@ #{desc => "Single active consumer for streams", doc_url => "https://www.rabbitmq.com/docs/stream", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_queue] }}). -rabbit_feature_flag( +<<<<<<< HEAD {feature_flags_v2, #{desc => "Feature flags subsystem V2", stability => required +======= + {feature_flags_v2, + #{desc => "Feature flags subsystem V2", + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {direct_exchange_routing_v2, +<<<<<<< HEAD #{desc => "v2 direct exchange routing implementation", stability => required, depends_on => [feature_flags_v2, implicit_default_bindings] +======= + #{desc => "v2 direct exchange routing implementation", + stability => required, + require_level => hard, + depends_on => [feature_flags_v2, implicit_default_bindings] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {listener_records_in_ets, +<<<<<<< HEAD #{desc => "Store listener records in ETS instead of Mnesia", stability => required, depends_on => [feature_flags_v2] +======= + #{desc => "Store listener records in ETS instead of Mnesia", + stability => required, + require_level => hard, + depends_on => [feature_flags_v2] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {tracking_records_in_ets, #{desc => "Store tracking records in ETS instead of Mnesia", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [feature_flags_v2] }}). @@ -94,6 +159,10 @@ doc_url => "https://github.com/rabbitmq/rabbitmq-server/issues/5931", %%TODO remove compatibility code stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_queue] }}). @@ -102,6 +171,10 @@ #{desc => "Support for restarting streams with optional preferred next leader argument." "Used to implement stream leader rebalancing", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_queue] }}). @@ -110,6 +183,10 @@ #{desc => "Bug fix to unblock a group of consumers in a super stream partition", doc_url => "https://github.com/rabbitmq/rabbitmq-server/issues/7743", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_single_active_consumer] }}). @@ -117,6 +194,10 @@ {stream_filtering, #{desc => "Support for stream filtering.", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_queue] }}). @@ -124,14 +205,25 @@ {message_containers, #{desc => "Message containers.", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [feature_flags_v2] }}). -rabbit_feature_flag( {khepri_db, +<<<<<<< HEAD #{desc => "New Raft-based metadata store. Fully supported as of RabbitMQ 4.0", doc_url => "https://www.rabbitmq.com/docs/next/metadata-store", stability => experimental, +======= + #{desc => "New Raft-based metadata store.", + doc_url => "https://www.rabbitmq.com/docs/next/metadata-store", + stability => experimental, + experiment_level => supported, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [feature_flags_v2, direct_exchange_routing_v2, maintenance_mode_status, @@ -154,6 +246,10 @@ #{desc => "A new internal command that is used to update streams as " "part of a policy.", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [stream_queue] }}). diff --git a/deps/rabbit/src/rabbit_db_binding.erl b/deps/rabbit/src/rabbit_db_binding.erl index 37bc82ba246c..d5f321c653ac 100644 --- a/deps/rabbit/src/rabbit_db_binding.erl +++ b/deps/rabbit/src/rabbit_db_binding.erl @@ -304,7 +304,14 @@ delete_in_mnesia(Src, Dst, B) -> should_index_table(Src), fun delete/3), Deletions0 = maybe_auto_delete_exchange_in_mnesia( B#binding.source, [B], rabbit_binding:new_deletions(), false), +<<<<<<< HEAD fun() -> {ok, rabbit_binding:process_deletions(Deletions0)} end. +======= + fun() -> + ok = rabbit_binding:process_deletions(Deletions0), + {ok, Deletions0} + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) absent_errs_only_in_mnesia(Names) -> Errs = [E || Name <- Names, @@ -354,7 +361,12 @@ delete_in_khepri(#binding{source = SrcName, {error, _} = Err -> Err; Deletions -> +<<<<<<< HEAD {ok, rabbit_binding:process_deletions(Deletions)} +======= + ok = rabbit_binding:process_deletions(Deletions), + {ok, Deletions} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. exists_in_khepri(Path, Binding) -> @@ -381,6 +393,7 @@ delete_in_khepri(Binding) -> end. maybe_auto_delete_exchange_in_khepri(XName, Bindings, Deletions, OnlyDurable) -> +<<<<<<< HEAD {Entry, Deletions1} = case rabbit_db_exchange:maybe_auto_delete_in_khepri(XName, OnlyDurable) of {not_deleted, X} -> @@ -390,6 +403,20 @@ maybe_auto_delete_exchange_in_khepri(XName, Bindings, Deletions, OnlyDurable) -> rabbit_binding:combine_deletions(Deletions, Deletions2)} end, rabbit_binding:add_deletion(XName, Entry, Deletions1). +======= + case rabbit_db_exchange:maybe_auto_delete_in_khepri(XName, OnlyDurable) of + {not_deleted, undefined} -> + Deletions; + {not_deleted, X} -> + rabbit_binding:add_deletion( + XName, X, not_deleted, Bindings, Deletions); + {deleted, X, Deletions1} -> + Deletions2 = rabbit_binding:combine_deletions( + Deletions, Deletions1), + rabbit_binding:add_deletion( + XName, X, deleted, Bindings, Deletions2) + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% get_all(). @@ -1153,6 +1180,7 @@ sync_index_route(_, _, _) -> OnlyDurable :: boolean(), Ret :: rabbit_binding:deletions(). maybe_auto_delete_exchange_in_mnesia(XName, Bindings, Deletions, OnlyDurable) -> +<<<<<<< HEAD {Entry, Deletions1} = case rabbit_db_exchange:maybe_auto_delete_in_mnesia(XName, OnlyDurable) of {not_deleted, X} -> @@ -1162,6 +1190,20 @@ maybe_auto_delete_exchange_in_mnesia(XName, Bindings, Deletions, OnlyDurable) -> rabbit_binding:combine_deletions(Deletions, Deletions2)} end, rabbit_binding:add_deletion(XName, Entry, Deletions1). +======= + case rabbit_db_exchange:maybe_auto_delete_in_mnesia(XName, OnlyDurable) of + {not_deleted, undefined} -> + Deletions; + {not_deleted, X} -> + rabbit_binding:add_deletion( + XName, X, not_deleted, Bindings, Deletions); + {deleted, X, Deletions1} -> + Deletions2 = rabbit_binding:combine_deletions( + Deletions, Deletions1), + rabbit_binding:add_deletion( + XName, X, deleted, Bindings, Deletions2) + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Instead of locking entire table on remove operations we can lock the %% affected resource only. diff --git a/deps/rabbit/src/rabbit_db_cluster.erl b/deps/rabbit/src/rabbit_db_cluster.erl index 1df145ccb117..4571a2dadf29 100644 --- a/deps/rabbit/src/rabbit_db_cluster.erl +++ b/deps/rabbit/src/rabbit_db_cluster.erl @@ -57,7 +57,11 @@ can_join(RemoteNode) -> "DB: checking if `~ts` can join cluster using remote node `~ts`", [node(), RemoteNode], #{domain => ?RMQLOG_DOMAIN_DB}), +<<<<<<< HEAD case rabbit_feature_flags:check_node_compatibility(RemoteNode) of +======= + case rabbit_feature_flags:check_node_compatibility(RemoteNode, true) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok -> case rabbit_khepri:is_enabled(RemoteNode) of true -> can_join_using_khepri(RemoteNode); diff --git a/deps/rabbit/src/rabbit_db_exchange.erl b/deps/rabbit/src/rabbit_db_exchange.erl index 5d434563f7e3..f2fae50e1876 100644 --- a/deps/rabbit/src/rabbit_db_exchange.erl +++ b/deps/rabbit/src/rabbit_db_exchange.erl @@ -573,7 +573,11 @@ next_serial_in_khepri_tx(#exchange{name = XName}) -> IfUnused :: boolean(), Exchange :: rabbit_types:exchange(), Binding :: rabbit_types:binding(), +<<<<<<< HEAD Deletions :: dict:dict(), +======= + Deletions :: rabbit_binding:deletions(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Ret :: {deleted, Exchange, [Binding], Deletions} | {error, not_found} | {error, in_use} | @@ -624,7 +628,11 @@ unconditional_delete_in_mnesia(X, OnlyDurable) -> RemoveBindingsForSource :: boolean(), Exchange :: rabbit_types:exchange(), Binding :: rabbit_types:binding(), +<<<<<<< HEAD Deletions :: dict:dict(), +======= + Deletions :: rabbit_binding:deletions(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Ret :: {error, not_found} | {error, in_use} | {deleted, Exchange, [Binding], Deletions}. delete_in_mnesia(X = #exchange{name = XName}, OnlyDurable, RemoveBindingsForSource) -> ok = mnesia:delete({?MNESIA_TABLE, XName}), @@ -695,7 +703,11 @@ delete_all_in_mnesia_tx(VHostName) -> {deleted, #exchange{name = XName}, Bindings, XDeletions} = unconditional_delete_in_mnesia( X, false), XDeletions1 = rabbit_binding:add_deletion( +<<<<<<< HEAD XName, {X, deleted, Bindings}, XDeletions), +======= + XName, X, deleted, Bindings, XDeletions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_binding:combine_deletions(Acc, XDeletions1) end, rabbit_binding:new_deletions(), Xs), {ok, Deletions}. @@ -716,7 +728,11 @@ delete_all_in_khepri_tx(VHostName) -> rabbit_db_binding:delete_all_for_exchange_in_khepri( X, false, true), Deletions1 = rabbit_binding:add_deletion( +<<<<<<< HEAD XName, {X, deleted, Bindings}, XDeletions), +======= + XName, X, deleted, Bindings, XDeletions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_binding:combine_deletions(Deletions, Deletions1) end, rabbit_binding:new_deletions(), NodeProps), {ok, Deletions}. diff --git a/deps/rabbit/src/rabbit_depr_ff_extra.erl b/deps/rabbit/src/rabbit_depr_ff_extra.erl index 5267c3efbfb6..cba1328d0937 100644 --- a/deps/rabbit/src/rabbit_depr_ff_extra.erl +++ b/deps/rabbit/src/rabbit_depr_ff_extra.erl @@ -2,7 +2,11 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2023 Broadcom. All Rights Reserved. The term “Broadcom” +======= +%% Copyright (c) 2023-2024 Broadcom. All Rights Reserved. The term “Broadcom” +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @doc diff --git a/deps/rabbit/src/rabbit_deprecated_features.erl b/deps/rabbit/src/rabbit_deprecated_features.erl index 93289be033eb..9ad5f0bc351b 100644 --- a/deps/rabbit/src/rabbit_deprecated_features.erl +++ b/deps/rabbit/src/rabbit_deprecated_features.erl @@ -2,11 +2,21 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @author The RabbitMQ team %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2023-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% @author The RabbitMQ team +%% @copyright 2023-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% This module provides an API to manage deprecated features in RabbitMQ. It diff --git a/deps/rabbit/src/rabbit_exchange.erl b/deps/rabbit/src/rabbit_exchange.erl index b4037f9a8078..7eda1879739e 100644 --- a/deps/rabbit/src/rabbit_exchange.erl +++ b/deps/rabbit/src/rabbit_exchange.erl @@ -470,6 +470,7 @@ delete(XName, IfUnused, Username) -> _ = rabbit_runtime_parameters:set(XName#resource.virtual_host, ?EXCHANGE_DELETE_IN_PROGRESS_COMPONENT, XName#resource.name, true, Username), +<<<<<<< HEAD Deletions = process_deletions(rabbit_db_exchange:delete(XName, IfUnused)), case Deletions of {error, _} -> @@ -477,6 +478,17 @@ delete(XName, IfUnused, Username) -> _ -> rabbit_binding:notify_deletions(Deletions, Username), ok +======= + case rabbit_db_exchange:delete(XName, IfUnused) of + {deleted, #exchange{name = XName} = X, Bs, Deletions} -> + Deletions1 = rabbit_binding:add_deletion( + XName, X, deleted, Bs, Deletions), + ok = rabbit_binding:process_deletions(Deletions1), + ok = rabbit_binding:notify_deletions(Deletions1, Username), + ok; + {error, _} = Err -> + Err +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end after rabbit_runtime_parameters:clear(XName#resource.virtual_host, @@ -491,6 +503,7 @@ delete(XName, IfUnused, Username) -> delete_all(VHostName, ActingUser) -> {ok, Deletions} = rabbit_db_exchange:delete_all(VHostName), +<<<<<<< HEAD Deletions1 = rabbit_binding:process_deletions(Deletions), rabbit_binding:notify_deletions(Deletions1, ActingUser), ok. @@ -502,6 +515,12 @@ process_deletions({deleted, #exchange{name = XName} = X, Bs, Deletions}) -> rabbit_binding:add_deletion( XName, {X, deleted, Bs}, Deletions)). +======= + ok = rabbit_binding:process_deletions(Deletions), + ok = rabbit_binding:notify_deletions(Deletions, ActingUser), + ok. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec ensure_deleted(ExchangeName, IfUnused, Username) -> Ret when ExchangeName :: name(), IfUnused :: boolean(), diff --git a/deps/rabbit/src/rabbit_feature_flags.erl b/deps/rabbit/src/rabbit_feature_flags.erl index f635e50d2b5f..c451883a8034 100644 --- a/deps/rabbit/src/rabbit_feature_flags.erl +++ b/deps/rabbit/src/rabbit_feature_flags.erl @@ -2,11 +2,21 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @author The RabbitMQ team %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2019-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% @author The RabbitMQ team +%% @copyright 2019-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% This module offers a framework to declare capabilities a RabbitMQ node @@ -103,7 +113,13 @@ init/0, get_state/1, get_stability/1, +<<<<<<< HEAD check_node_compatibility/1, +======= + get_require_level/1, + get_experiment_level/1, + check_node_compatibility/1, check_node_compatibility/2, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sync_feature_flags_with_cluster/2, refresh_feature_flags_after_app_load/0, enabled_feature_flags_list_file/0 @@ -145,6 +161,11 @@ -type feature_props() :: #{desc => string(), doc_url => string(), stability => stability(), +<<<<<<< HEAD +======= + require_level => require_level(), + experiment_level => experiment_level(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [feature_name()], callbacks => #{callback_name() => callback_fun_name()}}. @@ -181,6 +202,11 @@ desc => string(), doc_url => string(), stability => stability(), +<<<<<<< HEAD +======= + require_level => require_level(), + experiment_level => experiment_level(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [feature_name()], callbacks => #{callback_name() => callback_fun_name()}, @@ -205,6 +231,36 @@ %% Experimental feature flags are not enabled by default on a fresh RabbitMQ %% node. They must be enabled by the user. +<<<<<<< HEAD +======= +-type require_level() :: hard | soft. +%% The level of requirement of a feature flag. +%% +%% A hard required feature flags must be enabled before a RabbitMQ node is +%% upgraded to a version where it is required. +%% +%% A soft required feature flag will be automatically enabled when a RabbitMQ +%% node is upgraded to a version where it is required. + +-type experiment_level() :: unsupported | supported. +%% The level of support of an experimental feature flag. +%% +%% At first, an experimental feature flag is offered to give a chance to users +%% to try it and give feedback as part of the design and development of the +%% feature. At this stage, it is unsupported: it must not be enabled in a +%% production environment and upgrade to a later version of RabbitMQ while +%% this experimental feature flag is enabled is not supported. +%% +%% Then, the experimental feature flag becomes supported. At this point, it is +%% stable enough that upgrading is guarantied and help will be provided. +%% However it is not mature enough to be marked as stable (which would make it +%% enabled by default in a new deployment or when running `rabbitmqctl +%% enable_feature_flag all'. +%% +%% The next step is to change its stability to `stable'. Once done, the +%% `experiment_level()' field is irrelevant. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -type callback_fun_name() :: {Module :: module(), Function :: atom()}. %% The name of the module and function to call when changing the state of %% the feature flag. @@ -313,6 +369,11 @@ feature_state/0, feature_states/0, stability/0, +<<<<<<< HEAD +======= + require_level/0, + experiment_level/0, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) callback_fun_name/0, callbacks/0, callback_name/0, @@ -682,13 +743,24 @@ info() -> info(Options) when is_map(Options) -> rabbit_ff_extra:info(Options). +<<<<<<< HEAD -spec get_state(feature_name()) -> enabled | disabled | unavailable. +======= +-spec get_state(feature_name()) -> enabled | + state_changing | + disabled | + unavailable. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @doc %% Returns the state of a feature flag. %% %% The possible states are: %%
    %%
  • `enabled': the feature flag is enabled.
  • +<<<<<<< HEAD +======= +%%
  • `state_changing': the feature flag is being enabled.
  • +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %%
  • `disabled': the feature flag is supported by all nodes in the %% cluster but currently disabled.
  • %%
  • `unavailable': the feature flag is unsupported by at least one @@ -696,6 +768,7 @@ info(Options) when is_map(Options) -> %%
%% %% @param FeatureName The name of the feature flag to check. +<<<<<<< HEAD %% @returns `enabled', `disabled' or `unavailable'. get_state(FeatureName) when is_atom(FeatureName) -> @@ -706,6 +779,22 @@ get_state(FeatureName) when is_atom(FeatureName) -> true -> disabled; false -> unavailable end +======= +%% @returns `enabled', `state_changing', `disabled' or `unavailable'. + +get_state(FeatureName) when is_atom(FeatureName) -> + IsEnabled = is_enabled(FeatureName, non_blocking), + case IsEnabled of + true -> + enabled; + state_changing -> + state_changing; + false -> + case is_supported(FeatureName) of + true -> disabled; + false -> unavailable + end +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. -spec get_stability @@ -742,7 +831,11 @@ get_stability(FeatureName) when is_atom(FeatureName) -> undefined -> undefined; FeatureProps -> get_stability(FeatureProps) end; +<<<<<<< HEAD get_stability(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) -> +======= +get_stability(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) maps:get(stability, FeatureProps, stable); get_stability(FeatureProps) when ?IS_DEPRECATION(FeatureProps) -> Phase = rabbit_deprecated_features:get_phase(FeatureProps), @@ -753,6 +846,90 @@ get_stability(FeatureProps) when ?IS_DEPRECATION(FeatureProps) -> permitted_by_default -> experimental end. +<<<<<<< HEAD +======= +-spec get_require_level +(FeatureName) -> RequireLevel | undefined when + FeatureName :: feature_name(), + RequireLevel :: require_level() | none; +(FeatureProps) -> RequireLevel when + FeatureProps :: + feature_props_extended() | + rabbit_deprecated_features:feature_props_extended(), + RequireLevel :: require_level() | none. +%% @doc +%% Returns the requirement level of a feature flag. +%% +%% The possible requirement levels are: +%%
    +%%
  • `hard': the feature flag must be enabled before the RabbitMQ node is +%% upgraded to a version where it is hard required.
  • +%%
  • `soft': the feature flag will be automatically enabled wher a RabbitMQ +%% node is upgraded to a version where it is soft required.
  • +%%
  • `none': the feature flag is not required.
  • +%%
+%% +%% @param FeatureName The name of the feature flag to check. +%% @param FeatureProps A feature flag properties map. +%% @returns `hard', `soft' or `none', or `undefined' if the given feature flag +%% name doesn't correspond to a known feature flag. + +get_require_level(FeatureName) when is_atom(FeatureName) -> + case rabbit_ff_registry_wrapper:get(FeatureName) of + undefined -> undefined; + FeatureProps -> get_require_level(FeatureProps) + end; +get_require_level(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) -> + case get_stability(FeatureProps) of + required -> maps:get(require_level, FeatureProps, soft); + _ -> none + end; +get_require_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) -> + case get_stability(FeatureProps) of + required -> hard; + _ -> none + end. + +-spec get_experiment_level +(FeatureName) -> ExperimentLevel | undefined when + FeatureName :: feature_name(), + ExperimentLevel :: experiment_level() | none; +(FeatureProps) -> ExperimentLevel when + FeatureProps :: + feature_props_extended() | + rabbit_deprecated_features:feature_props_extended(), + ExperimentLevel :: experiment_level() | none. +%% @doc +%% Returns the experimental level of an experimental feature flag. +%% +%% The possible experiment levels are: +%%
    +%%
  • `unsupported': the experimental feature flag must not be enabled in +%% production and upgrades with it enabled is unsupported.
  • +%%
  • `supported': the experimental feature flag is not yet stable enough but +%% upgrades are guarantied to be possible. This is returned too if the +%% feature flag is stable or required.
  • +%%
+%% +%% @param FeatureName The name of the feature flag to check. +%% @param FeatureProps A feature flag properties map. +%% @returns `unsupported', `supported', or `undefined' if the given feature +%% flag name doesn't correspond to a known feature flag. + +get_experiment_level(FeatureName) when is_atom(FeatureName) -> + case rabbit_ff_registry_wrapper:get(FeatureName) of + undefined -> undefined; + FeatureProps -> get_experiment_level(FeatureProps) + end; +get_experiment_level(FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) -> + case get_stability(FeatureProps) of + experimental -> maps:get(experiment_level, FeatureProps, unsupported); + _ -> supported + end; +get_experiment_level(FeatureProps) when ?IS_DEPRECATION(FeatureProps) -> + supported. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% Feature flags registry. %% ------------------------------------------------------------------- @@ -911,6 +1088,11 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) -> ValidProps = [desc, doc_url, stability, +<<<<<<< HEAD +======= + require_level, + experiment_level, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on, callbacks], ?assertEqual([], maps:keys(FeatureProps) -- ValidProps), @@ -922,6 +1104,20 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) -> ?assert(Stability =:= stable orelse Stability =:= experimental orelse Stability =:= required), +<<<<<<< HEAD +======= + ?assert(Stability =:= experimental orelse + not maps:is_key(experiment_level, FeatureProps)), + ?assert(Stability =:= required orelse + not maps:is_key(require_level, FeatureProps)), + RequireLevel = maps:get(require_level, FeatureProps, soft), + ?assert(RequireLevel =:= hard orelse RequireLevel =:= soft), + ExperimentLevel = maps:get( + experiment_level, FeatureProps, + unsupported), + ?assert(ExperimentLevel =:= unsupported orelse + ExperimentLevel =:= supported), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertNot(maps:is_key(migration_fun, FeatureProps)), ?assertNot(maps:is_key(warning, FeatureProps)), case FeatureProps of @@ -1302,7 +1498,13 @@ does_node_support(Node, FeatureNames, Timeout) -> false end. +<<<<<<< HEAD -spec check_node_compatibility(node()) -> ok | {error, any()}. +======= +-spec check_node_compatibility(RemoteNode) -> Ret when + RemoteNode :: node(), + Ret :: ok | {error, any()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @doc %% Checks if a node is compatible with the local node. %% @@ -1314,11 +1516,48 @@ does_node_support(Node, FeatureNames, Timeout) -> %% local node %% %% +<<<<<<< HEAD %% @param Node the name of the remote node to test. %% @returns `ok' if they are compatible, `{error, Reason}' if they are not. check_node_compatibility(Node) -> rabbit_ff_controller:check_node_compatibility(Node). +======= +%% @param RemoteNode the name of the remote node to test. +%% @returns `ok' if they are compatible, `{error, Reason}' if they are not. + +check_node_compatibility(RemoteNode) -> + check_node_compatibility(RemoteNode, false). + +-spec check_node_compatibility(RemoteNode, LocalNodeAsVirgin) -> Ret when + RemoteNode :: node(), + LocalNodeAsVirgin :: boolean(), + Ret :: ok | {error, any()}. +%% @doc +%% Checks if a node is compatible with the local node. +%% +%% To be compatible, the following two conditions must be met: +%%
    +%%
  1. feature flags enabled on the local node must be supported by the +%% remote node
  2. +%%
  3. feature flags enabled on the remote node must be supported by the +%% local node
  4. +%%
+%% +%% Unlike {@link check_node_compatibility/1}, the local node's feature flags +%% inventory is evaluated as if the node was virgin if `LocalNodeAsVirgin' is +%% true. This is useful if the local node will be reset as part of joining a +%% remote cluster for instance. +%% +%% @param RemoteNode the name of the remote node to test. +%% @param LocalNodeAsVirgin flag to indicate if the local node should be +%% evaluated as if it was virgin. +%% @returns `ok' if they are compatible, `{error, Reason}' if they are not. + +check_node_compatibility(RemoteNode, LocalNodeAsVirgin) -> + rabbit_ff_controller:check_node_compatibility( + RemoteNode, LocalNodeAsVirgin). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) run_feature_flags_mod_on_remote_node(Node, Function, Args, Timeout) -> rabbit_ff_controller:rpc_call(Node, ?MODULE, Function, Args, Timeout). @@ -1330,7 +1569,11 @@ run_feature_flags_mod_on_remote_node(Node, Function, Args, Timeout) -> sync_feature_flags_with_cluster([] = _Nodes, true = _NodeIsVirgin) -> rabbit_ff_controller:enable_default(); sync_feature_flags_with_cluster([] = _Nodes, false = _NodeIsVirgin) -> +<<<<<<< HEAD ok; +======= + rabbit_ff_controller:enable_required(); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sync_feature_flags_with_cluster(Nodes, _NodeIsVirgin) -> %% We don't use `rabbit_nodes:filter_running()' here because the given %% `Nodes' list may contain nodes which are not members yet (the cluster diff --git a/deps/rabbit/src/rabbit_ff_controller.erl b/deps/rabbit/src/rabbit_ff_controller.erl index f82ed6000e16..cc8bd57bc00c 100644 --- a/deps/rabbit/src/rabbit_ff_controller.erl +++ b/deps/rabbit/src/rabbit_ff_controller.erl @@ -2,11 +2,21 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @author The RabbitMQ team %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2019-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% @author The RabbitMQ team +%% @copyright 2019-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% The feature flag controller is responsible for synchronization and managing @@ -36,7 +46,12 @@ -export([is_supported/1, is_supported/2, enable/1, enable_default/0, +<<<<<<< HEAD check_node_compatibility/1, +======= + enable_required/0, + check_node_compatibility/2, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sync_cluster/1, refresh_after_app_load/0, get_forced_feature_flag_names/0]). @@ -134,12 +149,49 @@ enable_default() -> Ret end. +<<<<<<< HEAD check_node_compatibility(RemoteNode) -> ThisNode = node(), ?LOG_DEBUG( "Feature flags: CHECKING COMPATIBILITY between nodes `~ts` and `~ts`", [ThisNode, RemoteNode], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), +======= +enable_required() -> + ?LOG_DEBUG( + "Feature flags: enable required feature flags", + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), + case erlang:whereis(?LOCAL_NAME) of + Pid when is_pid(Pid) -> + %% The function is called while `rabbit' is running. + gen_statem:call(?LOCAL_NAME, enable_required); + undefined -> + %% The function is called while `rabbit' is stopped. We need to + %% start a one-off controller, again to make sure concurrent + %% changes are blocked. + {ok, Pid} = start_link(), + Ret = gen_statem:call(Pid, enable_required), + gen_statem:stop(Pid), + Ret + end. + +check_node_compatibility(RemoteNode, LocalNodeAsVirgin) -> + ThisNode = node(), + case LocalNodeAsVirgin of + true -> + ?LOG_DEBUG( + "Feature flags: CHECKING COMPATIBILITY between nodes `~ts` " + "and `~ts`; consider node `~ts` as virgin", + [ThisNode, RemoteNode, ThisNode], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}); + false -> + ?LOG_DEBUG( + "Feature flags: CHECKING COMPATIBILITY between nodes `~ts` " + "and `~ts`", + [ThisNode, RemoteNode], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% We don't go through the controller process to check nodes compatibility %% because this function is used while `rabbit' is stopped usually. %% @@ -147,7 +199,11 @@ check_node_compatibility(RemoteNode) -> %% because it would not guaranty that the compatibility remains true after %% this function finishes and before the node starts and synchronizes %% feature flags. +<<<<<<< HEAD check_node_compatibility_task(ThisNode, RemoteNode). +======= + check_node_compatibility_task(ThisNode, RemoteNode, LocalNodeAsVirgin). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) sync_cluster(Nodes) -> ?LOG_DEBUG( @@ -194,7 +250,11 @@ standing_by( when EventContent =/= notify_when_done -> ?LOG_DEBUG( "Feature flags: registering controller globally before " +<<<<<<< HEAD "proceeding with task: ~tp", +======= + "proceeding with task: ~0tp", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) [EventContent], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), @@ -292,6 +352,11 @@ proceed_with_task({enable, FeatureNames}) -> enable_task(FeatureNames); proceed_with_task(enable_default) -> enable_default_task(); +<<<<<<< HEAD +======= +proceed_with_task(enable_required) -> + enable_required_task(); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) proceed_with_task({sync_cluster, Nodes}) -> sync_cluster_task(Nodes); proceed_with_task(refresh_after_app_load) -> @@ -382,12 +447,23 @@ notify_waiting_controller({ControlerPid, _} = From) -> %% Code to check compatibility between nodes. %% -------------------------------------------------------------------- +<<<<<<< HEAD -spec check_node_compatibility_task(Node, Node) -> Ret when Node :: node(), Ret :: ok | {error, Reason}, Reason :: incompatible_feature_flags. check_node_compatibility_task(NodeA, NodeB) -> +======= +-spec check_node_compatibility_task(NodeA, NodeB, NodeAAsVirigin) -> Ret when + NodeA :: node(), + NodeB :: node(), + NodeAAsVirigin :: boolean(), + Ret :: ok | {error, Reason}, + Reason :: incompatible_feature_flags. + +check_node_compatibility_task(NodeA, NodeB, NodeAAsVirigin) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_NOTICE( "Feature flags: checking nodes `~ts` and `~ts` compatibility...", [NodeA, NodeB], @@ -400,7 +476,12 @@ check_node_compatibility_task(NodeA, NodeB) -> _ when is_list(NodesB) -> check_node_compatibility_task1( NodeA, NodesA, +<<<<<<< HEAD NodeB, NodesB); +======= + NodeB, NodesB, + NodeAAsVirigin); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Error -> ?LOG_WARNING( "Feature flags: " @@ -419,6 +500,7 @@ check_node_compatibility_task(NodeA, NodeB) -> {error, {aborted_feature_flags_compat_check, Error}} end. +<<<<<<< HEAD check_node_compatibility_task1(NodeA, NodesA, NodeB, NodesB) when is_list(NodesA) andalso is_list(NodesB) -> case collect_inventory_on_nodes(NodesA) of @@ -434,6 +516,18 @@ check_node_compatibility_task1(NodeA, NodesA, NodeB, NodesB) "`~ts`:~n~tp", [NodeB, InventoryB], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), +======= +check_node_compatibility_task1(NodeA, NodesA, NodeB, NodesB, NodeAAsVirigin) + when is_list(NodesA) andalso is_list(NodesB) -> + case collect_inventory_on_nodes(NodesA) of + {ok, InventoryA0} -> + InventoryA = virtually_reset_inventory( + InventoryA0, NodeAAsVirigin), + log_inventory(NodeA, InventoryA), + case collect_inventory_on_nodes(NodesB) of + {ok, InventoryB} -> + log_inventory(NodeB, InventoryB), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case are_compatible(InventoryA, InventoryB) of true -> ?LOG_NOTICE( @@ -468,6 +562,62 @@ check_node_compatibility_task1(NodeA, NodesA, NodeB, NodesB) {error, {aborted_feature_flags_compat_check, Error}} end. +<<<<<<< HEAD +======= +log_inventory( + FromNode, + #{feature_flags := FeatureFlags, states_per_node := StatesPerNode}) -> + ?LOG_DEBUG( + begin + AllFeatureNames = lists:sort(maps:keys(FeatureFlags)), + Nodes = lists:sort(maps:keys(StatesPerNode)), + LongestFeatureName = lists:foldl( + fun(FeatureName, MaxLength) -> + Length = length( + atom_to_list( + FeatureName)), + if + Length > MaxLength -> Length; + true -> MaxLength + end + end, 0, AllFeatureNames), + NodeInitialPrefix = lists:duplicate(LongestFeatureName + 1, $\s), + {Header, + HeaderTail} = lists:foldl( + fun(Node, {String, Prefix}) -> + String1 = io_lib:format( + "~ts~ts ,-- ~ts~n", + [String, Prefix, Node]), + NextPrefix = Prefix ++ " |", + {String1, NextPrefix} + end, {"", NodeInitialPrefix}, Nodes), + lists:flatten( + io_lib:format( + "Feature flags: inventory queried from node `~ts`:~n", + [FromNode]) ++ + Header ++ + HeaderTail ++ + [io_lib:format("~n~*ts:", [LongestFeatureName, FeatureName]) ++ + [io_lib:format( + " ~s", + [begin + State = maps:get( + FeatureName, + maps:get(Node, StatesPerNode), + false), + case State of + true -> "x"; + state_changing -> "~"; + false -> " " + end + end]) + || Node <- Nodes] + || FeatureName <- AllFeatureNames] ++ + []) + end, + #{domain_ => ?RMQLOG_DOMAIN_FEAT_FLAGS}). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec list_nodes_clustered_with(Node) -> Ret when Node :: node(), Ret :: Members | Error, @@ -488,6 +638,45 @@ list_nodes_clustered_with(Node) -> ListOrError -> ListOrError end. +<<<<<<< HEAD +======= +virtually_reset_inventory( + #{feature_flags := FeatureFlags, + states_per_node := StatesPerNode} = Inventory, + true = _NodeAsVirgin) -> + [Node | _] = maps:keys(StatesPerNode), + FeatureStates0 = maps:get(Node, StatesPerNode), + FeatureStates1 = maps:map( + fun(FeatureName, _FeatureState) -> + FeatureProps = maps:get( + FeatureName, FeatureFlags), + state_after_virtual_state( + FeatureName, FeatureProps) + end, FeatureStates0), + StatesPerNode1 = maps:map( + fun(_Node, _FeatureStates) -> + FeatureStates1 + end, StatesPerNode), + Inventory1 = Inventory#{states_per_node => StatesPerNode1}, + Inventory1; +virtually_reset_inventory( + Inventory, + false = _NodeAsVirgin) -> + Inventory. + +state_after_virtual_state(_FeatureName, FeatureProps) + when ?IS_FEATURE_FLAG(FeatureProps) -> + Stability = rabbit_feature_flags:get_stability(FeatureProps), + case Stability of + required -> true; + _ -> false + end; +state_after_virtual_state(FeatureName, FeatureProps) + when ?IS_DEPRECATION(FeatureProps) -> + not rabbit_deprecated_features:should_be_permitted( + FeatureName, FeatureProps). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec are_compatible(Inventory, Inventory) -> AreCompatible when Inventory :: rabbit_feature_flags:cluster_inventory(), AreCompatible :: boolean(). @@ -576,14 +765,20 @@ enable_task(FeatureNames) -> end. enable_default_task() -> +<<<<<<< HEAD FeatureNames = get_forced_feature_flag_names(), case FeatureNames of undefined -> +======= + case get_forced_feature_flag_names() of + {ok, undefined} -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_DEBUG( "Feature flags: starting an unclustered node for the first " "time: all stable feature flags will be enabled by default", #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), {ok, Inventory} = collect_inventory_on_nodes([node()]), +<<<<<<< HEAD #{feature_flags := FeatureFlags} = Inventory, StableFeatureNames = maps:fold( @@ -597,17 +792,27 @@ enable_default_task() -> end, [], FeatureFlags), enable_many(Inventory, StableFeatureNames); [] -> +======= + StableFeatureNames = get_stable_feature_flags(Inventory), + enable_many(Inventory, StableFeatureNames); + {ok, []} -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_DEBUG( "Feature flags: starting an unclustered node for the first " "time: all feature flags are forcibly left disabled from " "the $RABBITMQ_FEATURE_FLAGS environment variable", #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), ok; +<<<<<<< HEAD _ -> +======= + {ok, FeatureNames} when is_list(FeatureNames) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_DEBUG( "Feature flags: starting an unclustered node for the first " "time: only the following feature flags specified in the " "$RABBITMQ_FEATURE_FLAGS environment variable will be enabled: " +<<<<<<< HEAD "~tp", [FeatureNames], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), @@ -618,6 +823,64 @@ enable_default_task() -> -spec get_forced_feature_flag_names() -> Ret when Ret :: FeatureNames | undefined, FeatureNames :: [rabbit_feature_flags:feature_name()]. +======= + "~0tp", + [FeatureNames], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), + {ok, Inventory} = collect_inventory_on_nodes([node()]), + enable_many(Inventory, FeatureNames); + {ok, {rel, Plus, Minus}} -> + ?LOG_DEBUG( + "Feature flags: starting an unclustered node for the first " + "time: all stable feature flags will be enabled, after " + "applying changes from $RABBITMQ_FEATURE_FLAGS: adding ~0tp, " + "skipping ~0tp", + [Plus, Minus], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), + {ok, Inventory} = collect_inventory_on_nodes([node()]), + StableFeatureNames = get_stable_feature_flags(Inventory), + Unsupported = lists:filter( + fun(FeatureName) -> + not is_known_and_supported( + Inventory, FeatureName) + end, Minus), + case Unsupported of + [] -> + FeatureNames = (StableFeatureNames -- Minus) ++ Plus, + enable_many(Inventory, FeatureNames); + _ -> + ?LOG_ERROR( + "Feature flags: unsupported feature flags to skip in " + "$RABBITMQ_FEATURE_FLAGS: ~0tp", + [Unsupported], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), + {error, unsupported} + end; + {error, syntax_error_in_envvar} = Error -> + ?LOG_DEBUG( + "Feature flags: invalid mix of `feature_flag` and " + "`+/-feature_flag` in $RABBITMQ_FEATURE_FLAGS", + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), + Error + end. + +get_stable_feature_flags(#{feature_flags := FeatureFlags}) -> + maps:fold( + fun(FeatureName, FeatureProps, Acc) -> + Stability = rabbit_feature_flags:get_stability(FeatureProps), + case Stability of + stable -> [FeatureName | Acc]; + _ -> Acc + end + end, [], FeatureFlags). + +-spec get_forced_feature_flag_names() -> Ret when + Ret :: {ok, Abs | Rel | undefined} | {error, syntax_error_in_envvar}, + Abs :: [rabbit_feature_flags:feature_name()], + Rel :: {rel, + [rabbit_feature_flags:feature_name()], + [rabbit_feature_flags:feature_name()]}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @doc Returns the (possibly empty) list of feature flags the user wants to %% enable out-of-the-box when starting a node for the first time. %% @@ -638,6 +901,7 @@ enable_default_task() -> %% @private get_forced_feature_flag_names() -> +<<<<<<< HEAD Ret = case get_forced_feature_flag_names_from_env() of undefined -> get_forced_feature_flag_names_from_config(); List -> List @@ -676,6 +940,70 @@ get_forced_feature_flag_names_from_env() -> -spec get_forced_feature_flag_names_from_config() -> Ret when Ret :: FeatureNames | undefined, FeatureNames :: [rabbit_feature_flags:feature_name()]. +======= + case get_forced_feature_flag_names_from_env() of + {ok, undefined} -> get_forced_feature_flag_names_from_config(); + {ok, _} = Ret -> Ret; + {error, _} = Error -> Error + end. + +-spec get_forced_feature_flag_names_from_env() -> Ret when + Ret :: {ok, Abs | Rel | undefined} | {error, syntax_error_in_envvar}, + Abs :: [rabbit_feature_flags:feature_name()], + Rel :: {rel, + [rabbit_feature_flags:feature_name()], + [rabbit_feature_flags:feature_name()]}. +%% @private + +get_forced_feature_flag_names_from_env() -> + Value = case rabbit_prelaunch:get_context() of + #{forced_feature_flags_on_init := ForcedFFs} -> ForcedFFs; + _ -> undefined + end, + case Value of + undefined -> + {ok, Value}; + [] -> + {ok, Value}; + [[Op | _] | _] when Op =:= $+ orelse Op =:= $- -> + lists:foldr( + fun + ([$+ | NameS], {ok, {rel, Plus, Minus}}) -> + Name = list_to_atom(NameS), + Plus1 = [Name | Plus], + {ok, {rel, Plus1, Minus}}; + ([$- | NameS], {ok, {rel, Plus, Minus}}) -> + Name = list_to_atom(NameS), + Minus1 = [Name | Minus], + {ok, {rel, Plus, Minus1}}; + (_, {error, _} = Error) -> + Error; + (_, _) -> + {error, syntax_error_in_envvar} + end, {ok, {rel, [], []}}, Value); + _ when is_list(Value) -> + lists:foldr( + fun + (Name, {ok, Abs}) when is_atom(Name) -> + {ok, [Name | Abs]}; + ([C | _] = NameS, {ok, Abs}) + when C =/= $+ andalso C =/= $- -> + Name = list_to_atom(NameS), + {ok, [Name | Abs]}; + (_, {error, _} = Error) -> + Error; + (_, _) -> + {error, syntax_error_in_envvar} + end, {ok, []}, Value) + end. + +-spec get_forced_feature_flag_names_from_config() -> Ret when + Ret :: {ok, Abs | Rel | undefined}, + Abs :: [rabbit_feature_flags:feature_name()], + Rel :: {rel, + [rabbit_feature_flags:feature_name()], + [rabbit_feature_flags:feature_name()]}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @private get_forced_feature_flag_names_from_config() -> @@ -683,6 +1011,7 @@ get_forced_feature_flag_names_from_config() -> rabbit, forced_feature_flags_on_init, undefined), case Value of undefined -> +<<<<<<< HEAD Value; _ when is_list(Value) -> case lists:all(fun is_atom/1, Value) of @@ -693,6 +1022,33 @@ get_forced_feature_flag_names_from_config() -> undefined end. +======= + {ok, Value}; + _ when is_list(Value) -> + {ok, Value}; + {rel, Plus, Minus} when is_list(Plus) andalso is_list(Minus) -> + {ok, Value} + end. + +-spec enable_required_task() -> Ret when + Ret :: ok | {error, Reason}, + Reason :: term(). + +enable_required_task() -> + {ok, Inventory} = collect_inventory_on_nodes([node()]), + RequiredFeatureNames = list_soft_required_feature_flags(Inventory), + case RequiredFeatureNames of + [] -> + ok; + _ -> + ?LOG_DEBUG( + "Feature flags: enabling required feature flags: ~0p", + [RequiredFeatureNames], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}) + end, + enable_many(Inventory, RequiredFeatureNames). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec sync_cluster_task() -> Ret when Ret :: ok | {error, Reason}, Reason :: term(). @@ -707,6 +1063,7 @@ sync_cluster_task() -> Reason :: term(). sync_cluster_task(Nodes) -> +<<<<<<< HEAD %% We assume that a feature flag can only be enabled, not disabled. %% Therefore this synchronization searches for feature flags enabled on %% some nodes but not all, and make sure they are enabled everywhere. @@ -724,6 +1081,8 @@ sync_cluster_task(Nodes) -> [Nodes], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case collect_inventory_on_nodes(Nodes) of {ok, Inventory} -> CantEnable = list_deprecated_features_that_cant_be_denied( @@ -732,7 +1091,31 @@ sync_cluster_task(Nodes) -> [] -> FeatureNames = list_feature_flags_enabled_somewhere( Inventory, false), +<<<<<<< HEAD enable_many(Inventory, FeatureNames); +======= + + %% In addition to feature flags enabled somewhere, we also + %% ensure required feature flags are enabled accross the + %% board. + RequiredFeatureNames = list_soft_required_feature_flags( + Inventory), + case RequiredFeatureNames of + [] -> + ok; + _ -> + ?LOG_DEBUG( + "Feature flags: enabling required feature " + "flags as part of cluster sync: ~0p", + [RequiredFeatureNames], + #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}) + end, + + FeatureNamesToEnable = lists:usort( + FeatureNames ++ + RequiredFeatureNames), + enable_many(Inventory, FeatureNamesToEnable); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> ?LOG_ERROR( "Feature flags: the following deprecated features " @@ -772,12 +1155,40 @@ refresh_after_app_load_task() -> Ret :: ok | {error, Reason}, Reason :: term(). +<<<<<<< HEAD enable_many(#{states_per_node := _} = Inventory, [FeatureName | Rest]) -> case enable_if_supported(Inventory, FeatureName) of {ok, Inventory1} -> enable_many(Inventory1, Rest); Error -> Error end; enable_many(_Inventory, []) -> +======= +enable_many(#{states_per_node := _} = Inventory, FeatureNames) -> + %% We acquire a lock before making any change to the registry. This is not + %% used by the controller (because it is already using a globally + %% registered name to prevent concurrent runs). But this is used in + %% `rabbit_feature_flags:is_enabled()' to block while the state is + %% `state_changing'. + rabbit_ff_registry_factory:acquire_state_change_lock(), + Ret = enable_many_locked(Inventory, FeatureNames), + rabbit_ff_registry_factory:release_state_change_lock(), + Ret. + +-spec enable_many_locked(Inventory, FeatureNames) -> Ret when + Inventory :: rabbit_feature_flags:cluster_inventory(), + FeatureNames :: [rabbit_feature_flags:feature_name()], + Ret :: ok | {error, Reason}, + Reason :: term(). + +enable_many_locked( + #{states_per_node := _} = Inventory, [FeatureName | Rest]) -> + case enable_if_supported(Inventory, FeatureName) of + {ok, Inventory1} -> enable_many_locked(Inventory1, Rest); + Error -> Error + end; +enable_many_locked( + _Inventory, []) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok. -spec enable_if_supported(Inventory, FeatureName) -> Ret when @@ -794,15 +1205,22 @@ enable_if_supported(#{states_per_node := _} = Inventory, FeatureName) -> "Feature flags: `~ts`: supported; continuing", [FeatureName], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), +<<<<<<< HEAD lock_registry_and_enable(Inventory, FeatureName); false -> ?LOG_DEBUG( +======= + enable_with_registry_locked(Inventory, FeatureName); + false -> + ?LOG_ERROR( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "Feature flags: `~ts`: unsupported; aborting", [FeatureName], #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS}), {error, unsupported} end. +<<<<<<< HEAD -spec lock_registry_and_enable(Inventory, FeatureName) -> Ret when Inventory :: rabbit_feature_flags:cluster_inventory(), FeatureName :: rabbit_feature_flags:feature_name(), @@ -820,6 +1238,8 @@ lock_registry_and_enable(#{states_per_node := _} = Inventory, FeatureName) -> rabbit_ff_registry_factory:release_state_change_lock(), Ret. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec enable_with_registry_locked(Inventory, FeatureName) -> Ret when Inventory :: rabbit_feature_flags:cluster_inventory(), FeatureName :: rabbit_feature_flags:feature_name(), @@ -876,13 +1296,22 @@ check_required_and_enable( FeatureName) -> %% Required feature flags vs. virgin nodes. FeatureProps = maps:get(FeatureName, FeatureFlags), +<<<<<<< HEAD Stability = rabbit_feature_flags:get_stability(FeatureProps), +======= + RequireLevel = rabbit_feature_flags:get_require_level(FeatureProps), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ProvidedBy = maps:get(provided_by, FeatureProps), NodesWhereDisabled = list_nodes_where_feature_flag_is_disabled( Inventory, FeatureName), +<<<<<<< HEAD MarkDirectly = case Stability of required when ProvidedBy =:= rabbit -> +======= + MarkDirectly = case RequireLevel of + hard when ProvidedBy =:= rabbit -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_DEBUG( "Feature flags: `~s`: the feature flag is " "required on some nodes; list virgin nodes " @@ -901,7 +1330,11 @@ check_required_and_enable( end end, NodesWhereDisabled), VirginNodesWhereDisabled =:= NodesWhereDisabled; +<<<<<<< HEAD required when ProvidedBy =/= rabbit -> +======= + hard when ProvidedBy =/= rabbit -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% A plugin can be enabled/disabled at runtime and %% between restarts. Thus we have no way to %% distinguish a newly enabled plugin from a plugin @@ -926,8 +1359,13 @@ check_required_and_enable( case MarkDirectly of false -> +<<<<<<< HEAD case Stability of required -> +======= + case RequireLevel of + hard -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?LOG_DEBUG( "Feature flags: `~s`: some nodes where the feature " "flag is disabled are not virgin, we need to perform " @@ -1295,6 +1733,29 @@ list_feature_flags_enabled_somewhere( end, #{}, StatesPerNode), lists:sort(maps:keys(MergedStates)). +<<<<<<< HEAD +======= +list_soft_required_feature_flags( + #{feature_flags := FeatureFlags, states_per_node := StatesPerNode}) -> + FeatureStates = maps:get(node(), StatesPerNode), + RequiredFeatureNames = maps:fold( + fun(FeatureName, FeatureProps, Acc) -> + RequireLevel = ( + rabbit_feature_flags:get_require_level( + FeatureProps)), + IsEnabled = maps:get( + FeatureName, FeatureStates, + false), + case RequireLevel of + soft when IsEnabled =:= false -> + [FeatureName | Acc]; + _ -> + Acc + end + end, [], FeatureFlags), + lists:sort(RequiredFeatureNames). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec list_deprecated_features_that_cant_be_denied(Inventory) -> Ret when Inventory :: rabbit_feature_flags:cluster_inventory(), @@ -1367,7 +1828,11 @@ list_nodes_where_feature_flag_is_disabled( %% disabled. not Enabled; _ -> +<<<<<<< HEAD %% The feature flags is unknown on this +======= + %% The feature flag is unknown on this +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% node, don't run the migration function. false end diff --git a/deps/rabbit/src/rabbit_ff_extra.erl b/deps/rabbit/src/rabbit_ff_extra.erl index 9eba72185936..ea00d4cedcad 100644 --- a/deps/rabbit/src/rabbit_ff_extra.erl +++ b/deps/rabbit/src/rabbit_ff_extra.erl @@ -2,7 +2,12 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% @copyright 2019-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% This module provides extra functions unused by the feature flags @@ -23,6 +28,15 @@ -type cli_info_entry() :: [{name, rabbit_feature_flags:feature_name()} | {state, enabled | disabled | unavailable} | {stability, rabbit_feature_flags:stability()} | +<<<<<<< HEAD +======= + {require_level, + rabbit_feature_flags:require_level()} | + {experiment_level, + rabbit_feature_flags:experiment_level()} | + {callbacks, + [rabbit_feature_flags:callback_name()]} | +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {provided_by, atom()} | {desc, string()} | {doc_url, string()}]. @@ -60,6 +74,14 @@ cli_info(FeatureFlags) -> FeatureProps = maps:get(FeatureName, FeatureFlags), State = rabbit_feature_flags:get_state(FeatureName), Stability = rabbit_feature_flags:get_stability(FeatureProps), +<<<<<<< HEAD +======= + RequireLevel = rabbit_feature_flags:get_require_level( + FeatureProps), + ExperimentLevel = rabbit_feature_flags:get_experiment_level( + FeatureProps), + Callbacks = maps:keys(maps:get(callbacks, FeatureProps, #{})), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) App = maps:get(provided_by, FeatureProps), Desc = maps:get(desc, FeatureProps, ""), DocUrl = maps:get(doc_url, FeatureProps, ""), @@ -68,6 +90,12 @@ cli_info(FeatureFlags) -> {doc_url, unicode:characters_to_binary(DocUrl)}, {state, State}, {stability, Stability}, +<<<<<<< HEAD +======= + {require_level, RequireLevel}, + {experiment_level, ExperimentLevel}, + {callbacks, Callbacks}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {provided_by, App}], [FFInfo | Acc] end, [], lists:sort(maps:keys(FeatureFlags))). @@ -159,6 +187,11 @@ info(FeatureFlags, Options) -> {State, Color} = case State0 of enabled -> {"Enabled", Green}; +<<<<<<< HEAD +======= + state_changing -> + {"(Changing)", Yellow}; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) disabled -> {"Disabled", Yellow}; unavailable -> diff --git a/deps/rabbit/src/rabbit_ff_registry.erl b/deps/rabbit/src/rabbit_ff_registry.erl index 864ff564dc64..04353890e1b0 100644 --- a/deps/rabbit/src/rabbit_ff_registry.erl +++ b/deps/rabbit/src/rabbit_ff_registry.erl @@ -2,11 +2,21 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @author The RabbitMQ team %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2019-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% @author The RabbitMQ team +%% @copyright 2019-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% This module exposes the API of the {@link rabbit_feature_flags} diff --git a/deps/rabbit/src/rabbit_ff_registry_factory.erl b/deps/rabbit/src/rabbit_ff_registry_factory.erl index 0d91a7b64955..969093e06fef 100644 --- a/deps/rabbit/src/rabbit_ff_registry_factory.erl +++ b/deps/rabbit/src/rabbit_ff_registry_factory.erl @@ -2,7 +2,12 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2019-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% -module(rabbit_ff_registry_factory). @@ -260,26 +265,47 @@ maybe_initialize_registry(NewSupportedFeatureFlags, maps:map( fun (FeatureName, FeatureProps) when ?IS_FEATURE_FLAG(FeatureProps) -> +<<<<<<< HEAD Stability = rabbit_feature_flags:get_stability(FeatureProps), +======= + RequireLevel = ( + rabbit_feature_flags:get_require_level(FeatureProps)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ProvidedBy = maps:get(provided_by, FeatureProps), State = case FeatureStates0 of #{FeatureName := FeatureState} -> FeatureState; _ -> false end, +<<<<<<< HEAD case Stability of required when State =:= true -> %% The required feature flag is already enabled, we keep %% it this way. State; required when NewNode -> +======= + case RequireLevel of + hard when State =:= true -> + %% The required feature flag is already enabled, we keep + %% it this way. + State; + hard when NewNode -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% This is the very first time the node starts, we %% already mark the required feature flag as enabled. ?assertNotEqual(state_changing, State), true; +<<<<<<< HEAD required when ProvidedBy =/= rabbit -> ?assertNotEqual(state_changing, State), true; required -> +======= + hard when ProvidedBy =/= rabbit -> + ?assertNotEqual(state_changing, State), + true; + hard -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% This is not a new node and the required feature flag %% is disabled. This is an error and RabbitMQ must be %% downgraded to enable the feature flag. @@ -442,6 +468,7 @@ do_initialize_registry(#{feature_flags := AllFeatureFlags, written_to_disk := WrittenToDisk} = Inventory) -> %% We log the state of those feature flags. ?LOG_DEBUG( +<<<<<<< HEAD lists:flatten( "Feature flags: list of feature flags found:\n" ++ [io_lib:format( @@ -473,6 +500,68 @@ do_initialize_registry(#{feature_flags := AllFeatureFlags, true -> "yes"; false -> "no" end])]), +======= + begin + AllFeatureNames = lists:sort(maps:keys(AllFeatureFlags)), + {FeatureNames, + DeprFeatureNames} = lists:partition( + fun(FeatureName) -> + FeatureProps = maps:get( + FeatureName, + AllFeatureFlags), + ?IS_FEATURE_FLAG(FeatureProps) + end, AllFeatureNames), + + IsRequired = fun(FeatureName) -> + FeatureProps = maps:get( + FeatureName, + AllFeatureFlags), + required =:= + rabbit_feature_flags:get_stability( + FeatureProps) + end, + {ReqFeatureNames, + NonReqFeatureNames} = lists:partition(IsRequired, FeatureNames), + {ReqDeprFeatureNames, + NonReqDeprFeatureNames} = lists:partition( + IsRequired, DeprFeatureNames), + + lists:flatten( + "Feature flags: list of feature flags found:\n" ++ + [io_lib:format( + "Feature flags: [~ts] ~ts~n", + [case maps:get(FeatureName, FeatureStates, false) of + true -> "x"; + state_changing -> "~"; + false -> " " + end, + FeatureName]) + || FeatureName <- NonReqFeatureNames] ++ + "Feature flags: list of deprecated features found:\n" ++ + [io_lib:format( + "Feature flags: [~ts] ~ts~n", + [case maps:get(FeatureName, FeatureStates, false) of + true -> "x"; + state_changing -> "~"; + false -> " " + end, + FeatureName]) + || FeatureName <- NonReqDeprFeatureNames] ++ + [io_lib:format( + "Feature flags: required feature flags not listed above: ~b~n" + "Feature flags: removed deprecated features not listed " + "above: ~b~n" + "Feature flags: scanned applications: ~0tp~n" + "Feature flags: feature flag states written to disk: ~ts", + [length(ReqFeatureNames), + length(ReqDeprFeatureNames), + ScannedApps, + case WrittenToDisk of + true -> "yes"; + false -> "no" + end])]) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #{domain => ?RMQLOG_DOMAIN_FEAT_FLAGS} ), diff --git a/deps/rabbit/src/rabbit_ff_registry_wrapper.erl b/deps/rabbit/src/rabbit_ff_registry_wrapper.erl index beef32f657cf..64cb0cad69cb 100644 --- a/deps/rabbit/src/rabbit_ff_registry_wrapper.erl +++ b/deps/rabbit/src/rabbit_ff_registry_wrapper.erl @@ -2,11 +2,21 @@ %% License, v. 2.0. If a copy of the MPL was not distributed with this %% file, You can obtain one at https://mozilla.org/MPL/2.0/. %% +<<<<<<< HEAD %% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. %% %% @author The RabbitMQ team %% @copyright 2007-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +======= +%% Copyright (c) 2019-2024 Broadcom. All Rights Reserved. The term “Broadcom” +%% refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% @author The RabbitMQ team +%% @copyright 2019-2024 Broadcom. The term “Broadcom” refers to Broadcom Inc. +%% and/or its subsidiaries. All rights reserved. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% @doc %% This module sits in front of {@link rabbit_ff_registry}. diff --git a/deps/rabbit/src/rabbit_global_counters.erl b/deps/rabbit/src/rabbit_global_counters.erl index b5cdc5b627e1..ad4547534ace 100644 --- a/deps/rabbit/src/rabbit_global_counters.erl +++ b/deps/rabbit/src/rabbit_global_counters.erl @@ -132,12 +132,23 @@ boot_step() -> [begin %% Protocol counters +<<<<<<< HEAD init([{protocol, Proto}]), %% Protocol & Queue Type counters init([{protocol, Proto}, {queue_type, rabbit_classic_queue}]), init([{protocol, Proto}, {queue_type, rabbit_quorum_queue}]), init([{protocol, Proto}, {queue_type, rabbit_stream_queue}]) +======= + Protocol = {protocol, Proto}, + init([Protocol]), + rabbit_msg_size_metrics:init(Proto), + + %% Protocol & Queue Type counters + init([Protocol, {queue_type, rabbit_classic_queue}]), + init([Protocol, {queue_type, rabbit_quorum_queue}]), + init([Protocol, {queue_type, rabbit_stream_queue}]) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end || Proto <- [amqp091, amqp10]], %% Dead Letter counters @@ -247,13 +258,21 @@ publisher_created(Protocol) -> counters:add(fetch(Protocol), ?PUBLISHERS, 1). publisher_deleted(Protocol) -> +<<<<<<< HEAD counters:add(fetch(Protocol), ?PUBLISHERS, -1). +======= + counters:sub(fetch(Protocol), ?PUBLISHERS, 1). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) consumer_created(Protocol) -> counters:add(fetch(Protocol), ?CONSUMERS, 1). consumer_deleted(Protocol) -> +<<<<<<< HEAD counters:add(fetch(Protocol), ?CONSUMERS, -1). +======= + counters:sub(fetch(Protocol), ?CONSUMERS, 1). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) messages_dead_lettered(Reason, QueueType, DeadLetterStrategy, Num) -> Index = case Reason of diff --git a/deps/rabbit/src/rabbit_khepri.erl b/deps/rabbit/src/rabbit_khepri.erl index 2c853d97495a..241728e0e9e5 100644 --- a/deps/rabbit/src/rabbit_khepri.erl +++ b/deps/rabbit/src/rabbit_khepri.erl @@ -892,10 +892,14 @@ check_cluster_consistency(Node, CheckNodesConsistency) -> Error end; {_OTP, _Rabbit, {ok, Status}} -> +<<<<<<< HEAD case rabbit_db_cluster:check_compatibility(Node) of ok -> {ok, Status}; Error -> Error end +======= + {ok, Status} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. remote_node_info(Node) -> diff --git a/deps/rabbit/src/rabbit_mnesia.erl b/deps/rabbit/src/rabbit_mnesia.erl index 0aa4ae5360b5..a4ef36670620 100644 --- a/deps/rabbit/src/rabbit_mnesia.erl +++ b/deps/rabbit/src/rabbit_mnesia.erl @@ -407,7 +407,28 @@ cluster_nodes(WhichNodes) -> cluster_status(WhichNodes). cluster_status_from_mnesia() -> case is_running() of false -> +<<<<<<< HEAD {error, mnesia_not_running}; +======= + case rabbit_khepri:get_feature_state() of + enabled -> + %% To keep this API compatible with older remote nodes who + %% don't know about Khepri, we take the cluster status + %% from `rabbit_khepri' and reformat the return value to + %% ressemble the node from this module. + %% + %% Both nodes won't be compatible, but let's leave that + %% decision to the Feature flags subsystem. + case rabbit_khepri:cluster_status_from_khepri() of + {ok, {All, Running}} -> + {ok, {All, All, Running}}; + {error, _} = Error -> + Error + end; + _ -> + {error, mnesia_not_running} + end; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true -> %% If the tables are not present, it means that %% `init_db/3' hasn't been run yet. In other words, either @@ -475,8 +496,28 @@ members() -> end. node_info() -> +<<<<<<< HEAD {rabbit_misc:otp_release(), rabbit_misc:version(), mnesia:system_info(protocol_version), +======= + %% Once Khepri is enabled, the Mnesia protocol is irrelevant obviously. + %% + %% That said, older remote nodes who don't known about Khepri will request + %% this information anyway as part of calling `node_info/0'. Here, we + %% simply return `unsupported' as the Mnesia protocol. Older versions of + %% RabbitMQ will skip the protocol negotiation and use other ways. + %% + %% The goal is mostly to let older nodes which check Mnesia before feature + %% flags to reach the feature flags check. This one will correctly + %% indicate that they are incompatible. That's why we return `unsupported' + %% here, even if we could return the actual Mnesia protocol. + MnesiaProtocol = case rabbit_khepri:get_feature_state() of + enabled -> unsupported; + _ -> mnesia:system_info(protocol_version) + end, + {rabbit_misc:otp_release(), rabbit_misc:version(), + MnesiaProtocol, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cluster_status_from_mnesia()}. -spec node_type() -> rabbit_db_cluster:node_type(). @@ -694,10 +735,14 @@ check_cluster_consistency(Node, CheckNodesConsistency) -> Error end; {_OTP, _Rabbit, _Protocol, {ok, Status}} -> +<<<<<<< HEAD case rabbit_db_cluster:check_compatibility(Node) of ok -> {ok, Status}; Error -> Error end +======= + {ok, Status} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. remote_node_info(Node) -> diff --git a/deps/rabbit/src/rabbit_msg_size_metrics.erl b/deps/rabbit/src/rabbit_msg_size_metrics.erl new file mode 100644 index 000000000000..1faaa311a515 --- /dev/null +++ b/deps/rabbit/src/rabbit_msg_size_metrics.erl @@ -0,0 +1,143 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% This module tracks received message size distribution as histogram. +%% (A histogram is represented by a set of counters, one for each bucket.) +-module(rabbit_msg_size_metrics). + +-export([init/1, + observe/2, + prometheus_format/0]). + +%% Integration tests. +-export([raw_buckets/1, + diff_raw_buckets/2]). + +-ifdef(TEST). +-export([cleanup/1]). +-endif. + +-define(BUCKET_1, 100). +-define(BUCKET_2, 1_000). +-define(BUCKET_3, 10_000). +-define(BUCKET_4, 100_000). +-define(BUCKET_5, 1_000_000). +-define(BUCKET_6, 10_000_000). +%% rabbit.max_message_size up to RabbitMQ 3.13 was 128 MiB. +%% rabbit.max_message_size since RabbitMQ 4.0 is 16 MiB. +%% To help finding an appropriate rabbit.max_message_size we also add a bucket for 50 MB. +-define(BUCKET_7, 50_000_000). +-define(BUCKET_8, 100_000_000). +%% 'infinity' means practically 512 MiB as hard limited in +%% https://github.com/rabbitmq/rabbitmq-server/blob/v4.0.2/deps/rabbit_common/include/rabbit.hrl#L254-L257 +-define(BUCKET_9, 'infinity'). + +-define(MSG_SIZE_BUCKETS, + [{1, ?BUCKET_1}, + {2, ?BUCKET_2}, + {3, ?BUCKET_3}, + {4, ?BUCKET_4}, + {5, ?BUCKET_5}, + {6, ?BUCKET_6}, + {7, ?BUCKET_7}, + {8, ?BUCKET_8}, + {9, ?BUCKET_9}]). + +-define(POS_MSG_SIZE_SUM, 10). + +-type raw_buckets() :: [{BucketUpperBound :: non_neg_integer(), + NumObservations :: non_neg_integer()}]. + +-spec init(atom()) -> ok. +init(Protocol) -> + Size = ?POS_MSG_SIZE_SUM, + Counters = counters:new(Size, [write_concurrency]), + put_counters(Protocol, Counters). + +-spec observe(atom(), non_neg_integer()) -> ok. +observe(Protocol, MessageSize) -> + BucketPos = find_bucket_pos(MessageSize), + Counters = get_counters(Protocol), + counters:add(Counters, BucketPos, 1), + counters:add(Counters, ?POS_MSG_SIZE_SUM, MessageSize). + +-spec prometheus_format() -> #{atom() => map()}. +prometheus_format() -> + Values = [prometheus_values(Counters) || Counters <- get_labels_counters()], + #{message_size_bytes => #{type => histogram, + help => "Size of messages received from publishers", + values => Values}}. + +find_bucket_pos(Size) when Size =< ?BUCKET_1 -> 1; +find_bucket_pos(Size) when Size =< ?BUCKET_2 -> 2; +find_bucket_pos(Size) when Size =< ?BUCKET_3 -> 3; +find_bucket_pos(Size) when Size =< ?BUCKET_4 -> 4; +find_bucket_pos(Size) when Size =< ?BUCKET_5 -> 5; +find_bucket_pos(Size) when Size =< ?BUCKET_6 -> 6; +find_bucket_pos(Size) when Size =< ?BUCKET_7 -> 7; +find_bucket_pos(Size) when Size =< ?BUCKET_8 -> 8; +find_bucket_pos(_Size) -> 9. + +raw_buckets(Protocol) + when is_atom(Protocol) -> + Counters = get_counters(Protocol), + raw_buckets(Counters); +raw_buckets(Counters) -> + [{UpperBound, counters:get(Counters, Pos)} + || {Pos, UpperBound} <- ?MSG_SIZE_BUCKETS]. + +-spec diff_raw_buckets(raw_buckets(), raw_buckets()) -> raw_buckets(). +diff_raw_buckets(After, Before) -> + diff_raw_buckets(After, Before, []). + +diff_raw_buckets([], [], Acc) -> + lists:reverse(Acc); +diff_raw_buckets([{UpperBound, CounterAfter} | After], + [{UpperBound, CounterBefore} | Before], + Acc) -> + case CounterAfter - CounterBefore of + 0 -> + diff_raw_buckets(After, Before, Acc); + Diff -> + diff_raw_buckets(After, Before, [{UpperBound, Diff} | Acc]) + end. + +%% "If you have looked at a /metrics for a histogram, you probably noticed that the buckets +%% aren’t just a count of events that fall into them. The buckets also include a count of +%% events in all the smaller buckets, all the way up to the +Inf, bucket which is the total +%% number of events. This is known as a cumulative histogram, and why the bucket label +%% is called le, standing for less than or equal to. +%% This is in addition to buckets being counters, so Prometheus histograms are cumula‐ +%% tive in two different ways." +%% [Prometheus: Up & Running] +prometheus_values({Labels, Counters}) -> + {Buckets, Count} = lists:mapfoldl( + fun({UpperBound, NumObservations}, Acc0) -> + Acc = Acc0 + NumObservations, + {{UpperBound, Acc}, Acc} + end, 0, raw_buckets(Counters)), + Sum = counters:get(Counters, ?POS_MSG_SIZE_SUM), + {Labels, Buckets, Count, Sum}. + +put_counters(Protocol, Counters) -> + persistent_term:put({?MODULE, Protocol}, Counters). + +get_counters(Protocol) -> + persistent_term:get({?MODULE, Protocol}). + +get_labels_counters() -> + [{[{protocol, Protocol}], Counters} + || {{?MODULE, Protocol}, Counters} <- persistent_term:get()]. + +-ifdef(TEST). +%% "Counters are not tied to the current process and are automatically +%% garbage collected when they are no longer referenced." +-spec cleanup(atom()) -> ok. +cleanup(Protocol) -> + persistent_term:erase({?MODULE, Protocol}), + ok. +-endif. diff --git a/deps/rabbit/src/rabbit_networking.erl b/deps/rabbit/src/rabbit_networking.erl index 6788336df0e1..d7f10b187b17 100644 --- a/deps/rabbit/src/rabbit_networking.erl +++ b/deps/rabbit/src/rabbit_networking.erl @@ -25,9 +25,15 @@ node_listeners/1, node_client_listeners/1, register_connection/1, unregister_connection/1, register_non_amqp_connection/1, unregister_non_amqp_connection/1, +<<<<<<< HEAD connections/0, non_amqp_connections/0, connection_info_keys/0, connection_info/1, connection_info/2, connection_info_all/0, connection_info_all/1, +======= + connections/0, non_amqp_connections/0, + connection_info/2, + connection_info_all/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) emit_connection_info_all/4, emit_connection_info_local/3, close_connection/2, close_connections/2, close_all_connections/1, close_all_user_connections/2, @@ -482,6 +488,7 @@ non_amqp_connections() -> local_non_amqp_connections() -> pg_local:get_members(rabbit_non_amqp_connections). +<<<<<<< HEAD -spec connection_info_keys() -> rabbit_types:info_keys(). connection_info_keys() -> rabbit_reader:info_keys(). @@ -490,15 +497,20 @@ connection_info_keys() -> rabbit_reader:info_keys(). connection_info(Pid) -> rabbit_reader:info(Pid). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec connection_info(rabbit_types:connection(), rabbit_types:info_keys()) -> rabbit_types:infos(). connection_info(Pid, Items) -> rabbit_reader:info(Pid, Items). +<<<<<<< HEAD -spec connection_info_all() -> [rabbit_types:infos()]. connection_info_all() -> cmap(fun (Q) -> connection_info(Q) end). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec connection_info_all(rabbit_types:info_keys()) -> [rabbit_types:infos()]. diff --git a/deps/rabbit/src/rabbit_prelaunch_feature_flags.erl b/deps/rabbit/src/rabbit_prelaunch_feature_flags.erl index cc8918a6b085..76d47246262a 100644 --- a/deps/rabbit/src/rabbit_prelaunch_feature_flags.erl +++ b/deps/rabbit/src/rabbit_prelaunch_feature_flags.erl @@ -37,7 +37,13 @@ setup(#{feature_flags_file := FFFile}) -> "Failed to initialize feature flags registry: ~tp", [Reason], #{domain => ?RMQLOG_DOMAIN_PRELAUNCH}), +<<<<<<< HEAD throw({error, failed_to_initialize_feature_flags_registry}) +======= + throw({error, + {failed_to_initialize_feature_flags_registry, + Reason}}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end; {error, Reason} -> ?LOG_ERROR( diff --git a/deps/rabbit/src/rabbit_queue_type.erl b/deps/rabbit/src/rabbit_queue_type.erl index 207b1c6a5634..1b85b468fd7f 100644 --- a/deps/rabbit/src/rabbit_queue_type.erl +++ b/deps/rabbit/src/rabbit_queue_type.erl @@ -58,6 +58,10 @@ fold_state/3, is_policy_applicable/2, is_server_named_allowed/1, +<<<<<<< HEAD +======= + amqp_capabilities/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) arguments/1, arguments/2, notify_decorators/1, @@ -129,6 +133,10 @@ consumer_tag := rabbit_types:ctag(), exclusive_consume => boolean(), args => rabbit_framing:amqp_table(), +<<<<<<< HEAD +======= + filter => rabbit_amqp_filtex:filter_expressions(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok_msg := term(), acting_user := rabbit_types:username()}. -type cancel_reason() :: cancel | remove. @@ -493,6 +501,15 @@ is_server_named_allowed(Type) -> Capabilities = Type:capabilities(), maps:get(server_named, Capabilities, false). +<<<<<<< HEAD +======= +-spec amqp_capabilities(queue_type()) -> + [binary()]. +amqp_capabilities(Type) -> + Capabilities = Type:capabilities(), + maps:get(?FUNCTION_NAME, Capabilities, []). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec arguments(arguments()) -> [binary()]. arguments(ArgumentType) -> Args0 = lists:map(fun(T) -> diff --git a/deps/rabbit/src/rabbit_quorum_queue.erl b/deps/rabbit/src/rabbit_quorum_queue.erl index c59c8d8be09c..7734e04a8a89 100644 --- a/deps/rabbit/src/rabbit_quorum_queue.erl +++ b/deps/rabbit/src/rabbit_quorum_queue.erl @@ -965,7 +965,11 @@ consume(Q, Spec, QState0) when ?amqqueue_is_quorum(Q) -> exclusive_consume := ExclusiveConsume, args := Args, ok_msg := OkMsg, +<<<<<<< HEAD acting_user := ActingUser} = Spec, +======= + acting_user := ActingUser} = Spec, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% TODO: validate consumer arguments %% currently quorum queues do not support any arguments QName = amqqueue:get_name(Q), diff --git a/deps/rabbit/src/rabbit_reader.erl b/deps/rabbit/src/rabbit_reader.erl index da5eda69f057..d68e79cf86b6 100644 --- a/deps/rabbit/src/rabbit_reader.erl +++ b/deps/rabbit/src/rabbit_reader.erl @@ -42,8 +42,14 @@ -include_lib("rabbit_common/include/rabbit_framing.hrl"). -include_lib("rabbit_common/include/rabbit.hrl"). +<<<<<<< HEAD -export([start_link/2, info_keys/0, info/1, info/2, force_event_refresh/2, +======= +-include("rabbit_amqp_reader.hrl"). + +-export([start_link/2, info/2, force_event_refresh/2, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) shutdown/2]). -export([system_continue/3, system_terminate/4, system_code_change/4]). @@ -116,6 +122,7 @@ connection_blocked_message_sent }). +<<<<<<< HEAD -define(STATISTICS_KEYS, [pid, recv_oct, recv_cnt, send_oct, send_cnt, send_pend, state, channels, reductions, garbage_collection]). @@ -124,6 +131,8 @@ -define(OTHER_METRICS, [recv_cnt, send_cnt, send_pend, state, channels, garbage_collection]). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -define(CREATION_EVENT_KEYS, [pid, name, port, peer_port, host, peer_host, ssl, peer_cert_subject, peer_cert_issuer, @@ -132,8 +141,11 @@ timeout, frame_max, channel_max, client_properties, connected_at, node, user_who_performed_action]). +<<<<<<< HEAD -define(INFO_KEYS, ?CREATION_EVENT_KEYS ++ ?STATISTICS_KEYS -- [pid]). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -define(AUTH_NOTIFICATION_INFO_KEYS, [host, name, peer_host, peer_port, protocol, auth_mechanism, ssl, ssl_protocol, ssl_cipher, peer_cert_issuer, peer_cert_subject, @@ -184,6 +196,7 @@ system_terminate(Reason, _Parent, _Deb, _State) -> system_code_change(Misc, _Module, _OldVsn, _Extra) -> {ok, Misc}. +<<<<<<< HEAD -spec info_keys() -> rabbit_types:info_keys(). info_keys() -> ?INFO_KEYS. @@ -193,6 +206,8 @@ info_keys() -> ?INFO_KEYS. info(Pid) -> gen_server:call(Pid, info, infinity). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec info(pid(), rabbit_types:info_keys()) -> rabbit_types:infos(). info(Pid, Items) -> @@ -629,9 +644,12 @@ handle_other({'$gen_call', From, {shutdown, Explanation}}, State) -> force -> stop; normal -> NewState end; +<<<<<<< HEAD handle_other({'$gen_call', From, info}, State) -> gen_server:reply(From, infos(?INFO_KEYS, State)), State; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_other({'$gen_call', From, {info, Items}}, State) -> gen_server:reply(From, try {ok, infos(Items, State)} catch Error -> {error, Error} @@ -1600,8 +1618,13 @@ i(state, #v1{connection_state = ConnectionState, end; i(garbage_collection, _State) -> rabbit_misc:get_gc_info(self()); +<<<<<<< HEAD i(reductions, _State) -> {reductions, Reductions} = erlang:process_info(self(), reductions), +======= +i(reductions = Item, _State) -> + {Item, Reductions} = erlang:process_info(self(), Item), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Reductions; i(Item, #v1{connection = Conn}) -> ic(Item, Conn). @@ -1623,6 +1646,10 @@ ic(client_properties, #connection{client_properties = CP}) -> CP; ic(auth_mechanism, #connection{auth_mechanism = none}) -> none; ic(auth_mechanism, #connection{auth_mechanism = {Name, _Mod}}) -> Name; ic(connected_at, #connection{connected_at = T}) -> T; +<<<<<<< HEAD +======= +ic(container_id, _) -> ''; % AMQP 1.0 specific field +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ic(Item, #connection{}) -> throw({bad_argument, Item}). socket_info(Get, Select, #v1{sock = Sock}) -> @@ -1640,12 +1667,21 @@ maybe_emit_stats(State) -> emit_stats(State) -> [{_, Pid}, +<<<<<<< HEAD {_, Recv_oct}, {_, Send_oct}, {_, Reductions}] = infos(?SIMPLE_METRICS, State), Infos = infos(?OTHER_METRICS, State), rabbit_core_metrics:connection_stats(Pid, Infos), rabbit_core_metrics:connection_stats(Pid, Recv_oct, Send_oct, Reductions), +======= + {_, RecvOct}, + {_, SendOct}, + {_, Reductions}] = infos(?SIMPLE_METRICS, State), + Infos = infos(?OTHER_METRICS, State), + rabbit_core_metrics:connection_stats(Pid, Infos), + rabbit_core_metrics:connection_stats(Pid, RecvOct, SendOct, Reductions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State1 = rabbit_event:reset_stats_timer(State, #v1.stats_timer), ensure_stats_timer(State1). @@ -1660,6 +1696,10 @@ pack_for_1_0(Buf, BufLen, #v1{sock = Sock, pending_recv = PendingRecv, helper_sup = {_HelperSup091, HelperSup10}, proxy_socket = ProxySocket, +<<<<<<< HEAD +======= + stats_timer = StatsTimer, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) connection = #connection{ name = Name, host = Host, @@ -1668,7 +1708,11 @@ pack_for_1_0(Buf, BufLen, #v1{sock = Sock, peer_port = PeerPort, connected_at = ConnectedAt}}) -> {Sock, PendingRecv, HelperSup10, Buf, BufLen, ProxySocket, +<<<<<<< HEAD Name, Host, PeerHost, Port, PeerPort, ConnectedAt}. +======= + Name, Host, PeerHost, Port, PeerPort, ConnectedAt, StatsTimer}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) respond_and_close(State, Channel, Protocol, Reason, LogErr) -> log_hard_error(State, Channel, LogErr), diff --git a/deps/rabbit/src/rabbit_stream_queue.erl b/deps/rabbit/src/rabbit_stream_queue.erl index a7aa3a5a18cc..0cd3fd555c9f 100644 --- a/deps/rabbit/src/rabbit_stream_queue.erl +++ b/deps/rabbit/src/rabbit_stream_queue.erl @@ -78,13 +78,21 @@ ack :: boolean(), start_offset = 0 :: non_neg_integer(), listening_offset = 0 :: non_neg_integer(), +<<<<<<< HEAD last_consumed_offset = 0 :: non_neg_integer(), +======= + last_consumed_offset :: non_neg_integer(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log :: undefined | osiris_log:state(), chunk_iterator :: undefined | osiris_log:chunk_iterator(), %% These messages were already read ahead from the Osiris log, %% were part of an uncompressed sub batch, and are buffered in %% reversed order until the consumer has more credits to consume them. buffer_msgs_rev = [] :: [rabbit_amqqueue:qmsg()], +<<<<<<< HEAD +======= + filter :: rabbit_amqp_filtex:filter_expressions(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) reader_options :: map()}). -record(stream_client, {stream_id :: string(), @@ -333,7 +341,12 @@ consume(Q, Spec, #stream_client{} = QState0) %% begins sending maybe_send_reply(ChPid, OkMsg), _ = rabbit_stream_coordinator:register_local_member_listener(Q), +<<<<<<< HEAD begin_stream(QState, ConsumerTag, OffsetSpec, Mode, AckRequired, filter_spec(Args)) +======= + Filter = maps:get(filter, Spec, []), + begin_stream(QState, ConsumerTag, OffsetSpec, Mode, AckRequired, Filter, filter_spec(Args)) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end; {undefined, _} -> {protocol_error, precondition_failed, @@ -424,7 +437,11 @@ query_local_pid(#stream_client{stream_id = StreamId} = State) -> begin_stream(#stream_client{name = QName, readers = Readers0, local_pid = LocalPid} = State, +<<<<<<< HEAD Tag, Offset, Mode, AckRequired, Options) +======= + Tag, Offset, Mode, AckRequired, Filter, Options) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) when is_pid(LocalPid) -> CounterSpec = {{?MODULE, QName, Tag, self()}, []}, {ok, Seg0} = osiris:init_reader(LocalPid, Offset, CounterSpec, Options), @@ -451,6 +468,10 @@ begin_stream(#stream_client{name = QName, listening_offset = NextOffset, last_consumed_offset = StartOffset, log = Seg0, +<<<<<<< HEAD +======= + filter = Filter, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) reader_options = Options}, {ok, State#stream_client{readers = Readers0#{Tag => Str0}}}. @@ -1158,7 +1179,12 @@ stream_entries(QName, Name, LocalPid, #stream{chunk_iterator = Iter0, delivery_count = DC, credit = Credit, +<<<<<<< HEAD start_offset = StartOffset} = Str0, Acc0) -> +======= + start_offset = StartOffset, + filter = Filter} = Str0, Acc0) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case osiris_log:iterator_next(Iter0) of end_of_chunk -> case chunk_iterator(Str0, LocalPid) of @@ -1172,7 +1198,11 @@ stream_entries(QName, Name, LocalPid, {batch, _NumRecords, 0, _Len, BatchedEntries} -> {MsgsRev, NumMsgs} = parse_uncompressed_subbatch( BatchedEntries, Offset, StartOffset, +<<<<<<< HEAD QName, Name, LocalPid, {[], 0}), +======= + QName, Name, LocalPid, Filter, {[], 0}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case Credit >= NumMsgs of true -> {Str0#stream{chunk_iterator = Iter, @@ -1199,12 +1229,28 @@ stream_entries(QName, Name, LocalPid, _SimpleEntry -> case Offset >= StartOffset of true -> +<<<<<<< HEAD Msg = entry_to_msg(Entry, Offset, QName, Name, LocalPid), {Str0#stream{chunk_iterator = Iter, delivery_count = delivery_count_add(DC, 1), credit = Credit - 1, last_consumed_offset = Offset}, [Msg | Acc0]}; +======= + case entry_to_msg(Entry, Offset, QName, + Name, LocalPid, Filter) of + none -> + {Str0#stream{chunk_iterator = Iter, + last_consumed_offset = Offset}, + Acc0}; + Msg -> + {Str0#stream{chunk_iterator = Iter, + delivery_count = delivery_count_add(DC, 1), + credit = Credit - 1, + last_consumed_offset = Offset}, + [Msg | Acc0]} + end; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) false -> {Str0#stream{chunk_iterator = Iter}, Acc0} end @@ -1236,13 +1282,19 @@ chunk_iterator(#stream{credit = Credit, end. %% Deliver each record of an uncompressed sub batch individually. +<<<<<<< HEAD parse_uncompressed_subbatch(<<>>, _Offset, _StartOffset, _QName, _Name, _LocalPid, Acc) -> +======= +parse_uncompressed_subbatch( + <<>>, _Offset, _StartOffset, _QName, _Name, _LocalPid, _Filter, Acc) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Acc; parse_uncompressed_subbatch( <<0:1, %% simple entry Len:31/unsigned, Entry:Len/binary, Rem/binary>>, +<<<<<<< HEAD Offset, StartOffset, QName, Name, LocalPid, Acc0 = {AccList, AccCount}) -> Acc = case Offset >= StartOffset of true -> @@ -1269,6 +1321,62 @@ entry_to_msg(Entry, Offset, #resource{kind = queue, end, Mc = mc:set_annotation(<<"x-stream-offset">>, Offset, Mc2), {Name, LocalPid, Offset, false, Mc}. +======= + Offset, StartOffset, QName, Name, LocalPid, Filter, Acc0 = {AccList, AccCount}) -> + Acc = case Offset >= StartOffset of + true -> + case entry_to_msg(Entry, Offset, QName, Name, LocalPid, Filter) of + none -> + Acc0; + Msg -> + {[Msg | AccList], AccCount + 1} + end; + false -> + Acc0 + end, + parse_uncompressed_subbatch(Rem, Offset + 1, StartOffset, QName, + Name, LocalPid, Filter, Acc). + +entry_to_msg(Entry, Offset, #resource{kind = queue, name = QName}, Name, LocalPid, Filter) -> + Mc0 = mc:init(mc_amqp, Entry, #{}), + %% If exchange or routing keys annotation isn't present the entry most likely came + %% from the rabbitmq-stream plugin so we'll choose defaults that simulate use + %% of the direct exchange. + XHeaders = mc:x_headers(Mc0), + Exchange = case XHeaders of + #{<<"x-exchange">> := {utf8, X}} -> + X; + _ -> + <<>> + end, + RKeys0 = case XHeaders of + #{<<"x-cc">> := {list, CCs}} -> + [CC || {utf8, CC} <- CCs]; + _ -> + [] + end, + RKeys1 = case XHeaders of + #{<<"x-routing-key">> := {utf8, RK}} -> + [RK | RKeys0]; + _ -> + RKeys0 + end, + RKeys = case RKeys1 of + [] -> + [QName]; + _ -> + RKeys1 + end, + Mc1 = mc:set_annotation(?ANN_EXCHANGE, Exchange, Mc0), + Mc2 = mc:set_annotation(?ANN_ROUTING_KEYS, RKeys, Mc1), + Mc = mc:set_annotation(<<"x-stream-offset">>, Offset, Mc2), + case rabbit_amqp_filtex:filter(Filter, Mc) of + true -> + {Name, LocalPid, Offset, false, Mc}; + false -> + none + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) capabilities() -> #{unsupported_policies => [%% Classic policies @@ -1288,6 +1396,12 @@ capabilities() -> consumer_arguments => [<<"x-stream-offset">>, <<"x-stream-filter">>, <<"x-stream-match-unfiltered">>], +<<<<<<< HEAD +======= + %% AMQP property filter expressions + %% https://groups.oasis-open.org/higherlogic/ws/public/document?document_id=66227 + amqp_capabilities => [<<"AMQP_FILTEX_PROP_V1_0">>], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) server_named => false}. notify_decorators(Q) when ?is_amqqueue(Q) -> diff --git a/deps/rabbit/test/amqp_address_SUITE.erl b/deps/rabbit/test/amqp_address_SUITE.erl index 910e1068eeed..144f2903650e 100644 --- a/deps/rabbit/test/amqp_address_SUITE.erl +++ b/deps/rabbit/test/amqp_address_SUITE.erl @@ -18,6 +18,12 @@ [rpc/4]). -import(rabbit_ct_helpers, [eventually/1]). +<<<<<<< HEAD +======= +-import(amqp_utils, + [flush/1, + wait_for_credit/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [ @@ -301,10 +307,16 @@ target_per_message_exchange_routing_key(Config) -> Tag1 = Body1 = <<1>>, Tag2 = Body2 = <<2>>, +<<<<<<< HEAD %% Although mc_amqp:essential_properties/1 parses these annotations, they should be ignored. Msg1 = amqp10_msg:set_message_annotations( #{<<"x-exchange">> => <<"ignored">>, <<"x-routing-key">> => <<"ignored">>}, +======= + %% Although mc_amqp:essential_properties/1 parses the x-exchange annotation, it should be ignored. + Msg1 = amqp10_msg:set_message_annotations( + #{<<"x-exchange">> => <<"ignored">>}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqp10_msg:set_properties(#{to => To1}, amqp10_msg:new(Tag1, Body1))), Msg2 = amqp10_msg:set_properties(#{to => To2}, amqp10_msg:new(Tag2, Body2)), ok = amqp10_client:send_msg(Sender, Msg1), @@ -651,6 +663,7 @@ connection_config(Config) -> container_id => <<"my container">>, sasl => {plain, <<"guest">>, <<"guest">>}}. +<<<<<<< HEAD % before we can send messages we have to wait for credit from the server wait_for_credit(Sender) -> receive @@ -662,6 +675,8 @@ wait_for_credit(Sender) -> ct:fail(?FUNCTION_NAME) end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) wait_for_settled(State, Tag) -> receive {amqp10_disposition, {State, Tag}} -> @@ -671,6 +686,7 @@ wait_for_settled(State, Tag) -> flush(Reason), ct:fail(Reason) end. +<<<<<<< HEAD flush(Prefix) -> receive Msg -> @@ -679,3 +695,5 @@ flush(Prefix) -> after 1 -> ok end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit/test/amqp_auth_SUITE.erl b/deps/rabbit/test/amqp_auth_SUITE.erl index 920f779172d4..d200601580f8 100644 --- a/deps/rabbit/test/amqp_auth_SUITE.erl +++ b/deps/rabbit/test/amqp_auth_SUITE.erl @@ -21,6 +21,13 @@ -import(event_recorder, [assert_event_type/2, assert_event_prop/2]). +<<<<<<< HEAD +======= +-import(amqp_utils, + [flush/1, + wait_for_credit/1, + close_connection_sync/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [ @@ -1077,6 +1084,7 @@ amqp_error(Condition, Description) condition = Condition, description = {utf8, Description}}. +<<<<<<< HEAD % before we can send messages we have to wait for credit from the server wait_for_credit(Sender) -> receive @@ -1096,10 +1104,13 @@ flush(Prefix) -> ok end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) delete_all_queues(Config) -> Qs = rpc(Config, rabbit_amqqueue, list, []), [{ok, _QLen} = rpc(Config, rabbit_amqqueue, delete, [Q, false, false, <<"fake-user">>]) || Q <- Qs]. +<<<<<<< HEAD close_connection_sync(Connection) when is_pid(Connection) -> @@ -1108,3 +1119,5 @@ close_connection_sync(Connection) after 5000 -> flush(missing_closed), ct:fail("missing CLOSE from server") end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit/test/amqp_client_SUITE.erl b/deps/rabbit/test/amqp_client_SUITE.erl index 09916c55c356..871ab9f3a694 100644 --- a/deps/rabbit/test/amqp_client_SUITE.erl +++ b/deps/rabbit/test/amqp_client_SUITE.erl @@ -27,6 +27,20 @@ -import(event_recorder, [assert_event_type/2, assert_event_prop/2]). +<<<<<<< HEAD +======= +-import(amqp_utils, + [init/1, init/2, + connection_config/1, connection_config/2, + flush/1, + wait_for_credit/1, + wait_for_accepts/1, + send_messages/3, send_messages/4, + detach_link_sync/1, + end_session_sync/1, + wait_for_session_end/1, + close_connection_sync/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [ @@ -97,16 +111,28 @@ groups() -> detach_requeues_drop_head_classic_queue, resource_alarm_before_session_begin, resource_alarm_after_session_begin, +<<<<<<< HEAD resource_alarm_send_many, max_message_size_client_to_server, max_message_size_server_to_client, global_counters, stream_filtering, +======= + max_message_size_client_to_server, + max_message_size_server_to_client, + global_counters, + stream_bloom_filter, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) available_messages_classic_queue, available_messages_quorum_queue, available_messages_stream, incoming_message_interceptors, +<<<<<<< HEAD trace, +======= + trace_classic_queue, + trace_stream, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) user_id, message_ttl, plugin, @@ -146,7 +172,16 @@ groups() -> tcp_back_pressure_rabbitmq_internal_flow_quorum_queue, session_max_per_connection, link_max_per_session, +<<<<<<< HEAD reserved_annotation +======= + reserved_annotation, + x_cc_annotation_exchange, + x_cc_annotation_exchange_routing_key_empty, + x_cc_annotation_queue, + x_cc_annotation_null, + bad_x_cc_annotation_exchange +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {cluster_size_3, [shuffle], @@ -287,12 +322,24 @@ init_per_testcase(T, Config) when T =:= detach_requeues_one_session_quorum_queue orelse T =:= single_active_consumer_quorum_queue orelse T =:= detach_requeues_two_connections_quorum_queue -> +<<<<<<< HEAD case rabbit_ct_broker_helpers:enable_feature_flag(Config, 'rabbitmq_4.0.0') of ok -> rabbit_ct_helpers:testcase_started(Config, T); {skip, _} -> {skip, "Feature flag rabbitmq_4.0.0 enables the consumer removal API"} end; +======= + %% Feature flag rabbitmq_4.0.0 enables the consumer removal API. + ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, 'rabbitmq_4.0.0'), + rabbit_ct_helpers:testcase_started(Config, T); +init_per_testcase(T, Config) + when T =:= leader_transfer_quorum_queue_credit_single orelse + T =:= leader_transfer_quorum_queue_credit_batches -> + %% These test cases flake with feature flag 'rabbitmq_4.0.0' disabled. + ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, 'rabbitmq_4.0.0'), + rabbit_ct_helpers:testcase_started(Config, T); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_testcase(T = immutable_bare_message, Config) -> case rpc(Config, rabbit_feature_flags, is_enabled, ['rabbitmq_4.0.0']) of true -> @@ -317,6 +364,7 @@ init_per_testcase(T = dead_letter_reject, Config) -> {skip, "This test is known to fail with feature flag message_containers_deaths_v2 disabled " "due bug https://github.com/rabbitmq/rabbitmq-server/issues/11159"} end; +<<<<<<< HEAD init_per_testcase(T, Config) when T =:= leader_transfer_quorum_queue_credit_single orelse T =:= leader_transfer_quorum_queue_credit_batches orelse @@ -337,6 +385,8 @@ init_per_testcase(T, Config) %% If node 1 runs 3.x, this is the old real plugin. ok = rabbit_ct_broker_helpers:enable_plugin(Config, 1, rabbitmq_amqp1_0), rabbit_ct_helpers:testcase_started(Config, T); +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_started(Config, Testcase). @@ -1170,7 +1220,11 @@ roundtrip_with_drain(Config, QueueType, QName) % wait for a delivery receive {amqp10_msg, Receiver, InMsg} -> ok = amqp10_client:accept_msg(Receiver, InMsg) +<<<<<<< HEAD after 2000 -> +======= + after 30000 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Reason = delivery_timeout, flush(Reason), ct:fail(Reason) @@ -1257,7 +1311,11 @@ drain_many(Config, QueueType, QName) %% We expect the server to send us the last message and %% to advance the delivery-count promptly. receive {amqp10_msg, _, _} -> ok +<<<<<<< HEAD after 2000 -> ct:fail({missing_delivery, ?LINE}) +======= + after 30000 -> ct:fail({missing_delivery, ?LINE}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, receive {amqp10_event, {link, Receiver, credit_exhausted}} -> ok after 300 -> ct:fail("expected credit_exhausted") @@ -1307,7 +1365,11 @@ amqp_amqpl(QType, Config) -> ok = amqp10_client:send_msg( Sender, amqp10_msg:set_application_properties( +<<<<<<< HEAD #{"my int" => -2}, +======= + #{"my int" => {int, -2}}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqp10_msg:new(<<>>, Body1, true))), %% Send with properties CorrelationID = <<"my correlation ID">>, @@ -1322,7 +1384,11 @@ amqp_amqpl(QType, Config) -> amqp10_msg:set_properties( #{correlation_id => CorrelationID}, amqp10_msg:set_application_properties( +<<<<<<< HEAD #{"my int" => -2}, +======= + #{"my long" => -9_000_000_000}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqp10_msg:new(<<>>, Body1, true)))), %% Send with footer Footer = #'v1_0.footer'{content = [{{symbol, <<"x-my footer">>}, {ubyte, 255}}]}, @@ -1411,7 +1477,11 @@ amqp_amqpl(QType, Config) -> correlation_id = Corr9}}} -> ?assertEqual([Body1], amqp10_framing:decode_bin(Payload9)), ?assertEqual(CorrelationID, Corr9), +<<<<<<< HEAD ?assertEqual({signedint, -2}, rabbit_misc:table_lookup(Headers9, <<"my int">>)) +======= + ?assertEqual({long, -9_000_000_000}, rabbit_misc:table_lookup(Headers9, <<"my long">>)) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) after 30000 -> ct:fail({missing_deliver, ?LINE}) end, receive {_, #amqp_msg{payload = Payload10}} -> @@ -1459,12 +1529,22 @@ amqp10_to_amqp091_header_conversion(Session,Ch, QName, Address) -> OutMsg1 = amqp10_msg:new(<<"my-tag">>, <<"my-body">>, false), OutMsg2 = amqp10_msg:set_application_properties( #{"string" => "string-val", +<<<<<<< HEAD "int" => 2, +======= + "long" => -2, + "uint" => {uint, 2}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "bool" => false}, OutMsg1), OutMsg3 = amqp10_msg:set_message_annotations( #{"x-string" => "string-value", +<<<<<<< HEAD "x-int" => 3, +======= + "x-long" => -3, + "x-uint" => {uint, 3}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "x-bool" => true}, OutMsg2), OutMsg = amqp10_msg:set_headers( @@ -1484,11 +1564,21 @@ amqp10_to_amqp091_header_conversion(Session,Ch, QName, Address) -> %% assert application properties ?assertEqual({longstr, <<"string-val">>}, rabbit_misc:table_lookup(Headers, <<"string">>)), +<<<<<<< HEAD ?assertEqual({unsignedint, 2}, rabbit_misc:table_lookup(Headers, <<"int">>)), ?assertEqual({bool, false}, rabbit_misc:table_lookup(Headers, <<"bool">>)), %% assert message annotations ?assertEqual({longstr, <<"string-value">>}, rabbit_misc:table_lookup(Headers, <<"x-string">>)), ?assertEqual({unsignedint, 3}, rabbit_misc:table_lookup(Headers, <<"x-int">>)), +======= + ?assertEqual({long, -2}, rabbit_misc:table_lookup(Headers, <<"long">>)), + ?assertEqual({unsignedint, 2}, rabbit_misc:table_lookup(Headers, <<"uint">>)), + ?assertEqual({bool, false}, rabbit_misc:table_lookup(Headers, <<"bool">>)), + %% assert message annotations + ?assertEqual({longstr, <<"string-value">>}, rabbit_misc:table_lookup(Headers, <<"x-string">>)), + ?assertEqual({long, -3}, rabbit_misc:table_lookup(Headers, <<"x-long">>)), + ?assertEqual({unsignedint, 3}, rabbit_misc:table_lookup(Headers, <<"x-uint">>)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual({bool, true}, rabbit_misc:table_lookup(Headers, <<"x-bool">>)), %% assert headers ?assertEqual(2, DeliveryMode), @@ -1888,18 +1978,31 @@ events(Config) -> Protocol = {protocol, {1, 0}}, AuthProps = [{name, <<"guest">>}, +<<<<<<< HEAD {auth_mechanism, <<"PLAIN">>}, {ssl, false}, Protocol], ?assertMatch( {value, _}, find_event(user_authentication_success, AuthProps, Events)), +======= + {auth_mechanism, <<"PLAIN">>}, + {ssl, false}, + Protocol], + ?assertMatch( + {value, _}, + find_event(user_authentication_success, AuthProps, Events)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Node = get_node_config(Config, 0, nodename), ConnectionCreatedProps = [Protocol, {node, Node}, {vhost, <<"/">>}, {user, <<"guest">>}, +<<<<<<< HEAD +======= + {container_id, <<"my container">>}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {type, network}], {value, ConnectionCreatedEvent} = find_event( connection_created, @@ -1920,8 +2023,13 @@ events(Config) -> Pid, ClientProperties], ?assertMatch( +<<<<<<< HEAD {value, _}, find_event(connection_closed, ConnectionClosedProps, Events)), +======= + {value, _}, + find_event(connection_closed, ConnectionClosedProps, Events)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok. sync_get_unsettled_classic_queue(Config) -> @@ -3011,7 +3119,11 @@ detach_requeues_two_connections(QType, Config) -> ok = gen_statem:cast(Session0, {flow_session, #'v1_0.flow'{incoming_window = {uint, 1}}}), ok = amqp10_client:flow_link_credit(Receiver0, 50, never), %% Wait for credit being applied to the queue. +<<<<<<< HEAD timer:sleep(10), +======= + timer:sleep(100), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Receiver1} = amqp10_client:attach_receiver_link(Session1, <<"receiver 1">>, Address, unsettled), receive {amqp10_event, {link, Receiver1, attached}} -> ok @@ -3019,7 +3131,11 @@ detach_requeues_two_connections(QType, Config) -> end, ok = amqp10_client:flow_link_credit(Receiver1, 40, never), %% Wait for credit being applied to the queue. +<<<<<<< HEAD timer:sleep(10), +======= + timer:sleep(100), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) NumMsgs = 6, [begin @@ -3203,6 +3319,7 @@ resource_alarm_after_session_begin(Config) -> #'queue.delete_ok'{} = amqp_channel:call(Ch, #'queue.delete'{queue = QName}), ok = rabbit_ct_client_helpers:close_channel(Ch). +<<<<<<< HEAD %% Test case for %% https://github.com/rabbitmq/rabbitmq-server/issues/12816 resource_alarm_send_many(Config) -> @@ -3239,6 +3356,8 @@ resource_alarm_send_many(Config) -> #'queue.delete_ok'{} = amqp_channel:call(Ch, #'queue.delete'{queue = QName}), ok = rabbit_ct_client_helpers:close_channel(Ch). +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_attempt_metrics(Config) -> open_and_close_connection(Config), [Attempt1] = rpc(Config, rabbit_core_metrics, get_auth_attempts, []), @@ -3409,7 +3528,11 @@ last_queue_confirms(Config) -> ok = rabbit_ct_broker_helpers:start_node(Config, 2), %% Since the quorum queue has become available, we should now get a confirmation for m2. receive {amqp10_disposition, {accepted, DTag2}} -> ok +<<<<<<< HEAD after 10_000 -> ct:fail({missing_accepted, DTag2}) +======= + after 30_000 -> ct:fail({missing_accepted, DTag2}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, ok = amqp10_client:detach_link(SenderClassicQ), @@ -3458,7 +3581,11 @@ target_queue_deleted(Config) -> after 30000 -> ct:fail({missing_accepted, DTag1}) end, +<<<<<<< HEAD N0 = rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), +======= + N0 = get_node_config(Config, 0, nodename), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) RaName = ra_name(QuorumQ), ServerId0 = {RaName, N0}, {ok, Members, _Leader} = ra:members(ServerId0), @@ -3484,7 +3611,11 @@ target_queue_deleted(Config) -> ok = rabbit_ct_broker_helpers:start_node(Config, ReplicaNode), %% Since the quorum queue has become available, we should now get a confirmation for m2. receive {amqp10_disposition, {accepted, DTag2}} -> ok +<<<<<<< HEAD after 10_000 -> ct:fail({missing_accepted, DTag2}) +======= + after 30_000 -> ct:fail({missing_accepted, DTag2}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, ok = amqp10_client:detach_link(Sender), @@ -3527,7 +3658,11 @@ target_classic_queue_down(Config) -> %% We expect that the server closes links that receive from classic queues that are down. ExpectedError = #'v1_0.error'{condition = ?V_1_0_AMQP_ERROR_ILLEGAL_STATE}, receive {amqp10_event, {link, Receiver1, {detached, ExpectedError}}} -> ok +<<<<<<< HEAD after 10_000 -> ct:fail({missing_event, ?LINE}) +======= + after 30_000 -> ct:fail({missing_event, ?LINE}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, %% However the server should not close links that send to classic queues that are down. receive Unexpected -> ct:fail({unexpected, Unexpected}) @@ -3567,6 +3702,7 @@ async_notify_settled_stream(Config) -> async_notify(settled, <<"stream">>, Config). async_notify_unsettled_classic_queue(Config) -> +<<<<<<< HEAD case rabbit_ct_broker_helpers:enable_feature_flag(Config, 'rabbitmq_4.0.0') of ok -> async_notify(unsettled, <<"classic">>, Config); @@ -3575,6 +3711,13 @@ async_notify_unsettled_classic_queue(Config) -> "queues with credit API v1 is known to be broken: " "https://github.com/rabbitmq/rabbitmq-server/issues/2597"} end. +======= + %% This test flakes with feature flag 'rabbitmq_4.0.0' disabled. + %% Link flow control in classic queues with credit API v1 is known to be broken: + %% https://github.com/rabbitmq/rabbitmq-server/issues/2597 + ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, 'rabbitmq_4.0.0'), + async_notify(unsettled, <<"classic">>, Config). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) async_notify_unsettled_quorum_queue(Config) -> async_notify(unsettled, <<"quorum">>, Config). @@ -3876,7 +4019,10 @@ leader_transfer_credit(QName, QType, Credit, Config) -> ok = end_session_sync(Session1), ok = close_connection_sync(Connection1), +<<<<<<< HEAD %% Consume from a follower. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OpnConf = connection_config(0, Config), {ok, Connection0} = amqp10_client:open_connection(OpnConf), {ok, Session0} = amqp10_client:begin_session_sync(Connection0), @@ -3890,6 +4036,10 @@ leader_transfer_credit(QName, QType, Credit, Config) -> ok = wait_for_accepts(NumMsgs), ok = detach_link_sync(Sender), +<<<<<<< HEAD +======= + %% Consume from a follower. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = wait_for_local_member(QType, QName, Config), Filter = consume_from_first(QType), {ok, Receiver} = amqp10_client:attach_receiver_link( @@ -3958,8 +4108,17 @@ list_connections(Config) -> [ok = rabbit_ct_client_helpers:close_channels_and_connection(Config, Node) || Node <- [0, 1, 2]], Connection091 = rabbit_ct_client_helpers:open_unmanaged_connection(Config, 0), +<<<<<<< HEAD {ok, C0} = amqp10_client:open_connection(connection_config(0, Config)), {ok, C2} = amqp10_client:open_connection(connection_config(2, Config)), +======= + ContainerId0 = <<"ID 0">>, + ContainerId2 = <<"ID 2">>, + Cfg0 = maps:put(container_id, ContainerId0, connection_config(0, Config)), + Cfg2 = maps:put(container_id, ContainerId2, connection_config(2, Config)), + {ok, C0} = amqp10_client:open_connection(Cfg0), + {ok, C2} = amqp10_client:open_connection(Cfg2), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) receive {amqp10_event, {connection, C0, opened}} -> ok after 30000 -> ct:fail({missing_event, ?LINE}) end, @@ -3967,8 +4126,13 @@ list_connections(Config) -> after 30000 -> ct:fail({missing_event, ?LINE}) end, +<<<<<<< HEAD {ok, StdOut} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["list_connections", "--silent", "protocol"]), Protocols0 = re:split(StdOut, <<"\n">>, [trim]), +======= + {ok, StdOut0} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["list_connections", "--silent", "protocol"]), + Protocols0 = re:split(StdOut0, <<"\n">>, [trim]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Remove any whitespaces. Protocols1 = [binary:replace(Subject, <<" ">>, <<>>, [global]) || Subject <- Protocols0], Protocols = lists:sort(Protocols1), @@ -3977,6 +4141,16 @@ list_connections(Config) -> <<"{1,0}">>], Protocols), +<<<<<<< HEAD +======= + %% CLI should list AMQP 1.0 container-id + {ok, StdOut1} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["list_connections", "--silent", "container_id"]), + ContainerIds0 = re:split(StdOut1, <<"\n">>, [trim]), + ContainerIds = lists:sort(ContainerIds0), + ?assertEqual([<<>>, ContainerId0, ContainerId2], + ContainerIds), + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = rabbit_ct_client_helpers:close_connection(Connection091), ok = close_connection_sync(C0), ok = close_connection_sync(C2). @@ -4129,7 +4303,11 @@ global_counters(Config) -> ok = end_session_sync(Session), ok = amqp10_client:close_connection(Connection). +<<<<<<< HEAD stream_filtering(Config) -> +======= +stream_bloom_filter(Config) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Stream = atom_to_binary(?FUNCTION_NAME), Address = rabbitmq_amqp_address:queue(Stream), Ch = rabbit_ct_client_helpers:open_channel(Config), @@ -4412,16 +4590,37 @@ incoming_message_interceptors(Config) -> ok = amqp10_client:close_connection(Connection), true = rpc(Config, persistent_term, erase, [Key]). +<<<<<<< HEAD trace(Config) -> Node = atom_to_binary(get_node_config(Config, 0, nodename)), TraceQ = <<"my trace queue">>, Q = <<"my queue">>, +======= +trace_classic_queue(Config) -> + trace(atom_to_binary(?FUNCTION_NAME), <<"classic">>, Config). + +trace_stream(Config) -> + trace(atom_to_binary(?FUNCTION_NAME), <<"stream">>, Config). + +trace(Q, QType, Config) -> + Node = atom_to_binary(get_node_config(Config, 0, nodename)), + TraceQ = <<"my trace queue">>, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Qs = [Q, TraceQ], RoutingKey = <<"my routing key">>, Payload = <<"my payload">>, CorrelationId = <<"my correlation 👀"/utf8>>, Ch = rabbit_ct_client_helpers:open_channel(Config), +<<<<<<< HEAD [#'queue.declare_ok'{} = amqp_channel:call(Ch, #'queue.declare'{queue = Q0}) || Q0 <- Qs], +======= + #'queue.declare_ok'{} = amqp_channel:call( + Ch, #'queue.declare'{ + queue = Q, + durable = true, + arguments = [{<<"x-queue-type">>, longstr, QType}]}), + #'queue.declare_ok'{} = amqp_channel:call(Ch, #'queue.declare'{queue = TraceQ}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'queue.bind_ok'{} = amqp_channel:call( Ch, #'queue.bind'{queue = TraceQ, exchange = <<"amq.rabbitmq.trace">>, @@ -4439,16 +4638,32 @@ trace(Config) -> {ok, _} = rabbit_ct_broker_helpers:rabbitmqctl(Config, 0, ["trace_on"]), {ok, SessionReceiver} = amqp10_client:begin_session_sync(Connection), +<<<<<<< HEAD +======= + {ok, Receiver} = amqp10_client:attach_receiver_link(SessionReceiver, + <<"test-receiver">>, + rabbitmq_amqp_address:queue(Q)), + receive {amqp10_event, {link, Receiver, attached}} -> ok + after 30000 -> ct:fail({missing_event, ?LINE}) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Sender} = amqp10_client:attach_sender_link( SessionSender, <<"test-sender">>, rabbitmq_amqp_address:exchange(<<"amq.direct">>, RoutingKey)), ok = wait_for_credit(Sender), +<<<<<<< HEAD {ok, Receiver} = amqp10_client:attach_receiver_link(SessionReceiver, <<"test-receiver">>, rabbitmq_amqp_address:queue(Q)), Msg0 = amqp10_msg:new(<<"tag 1">>, Payload, true), Msg = amqp10_msg:set_properties(#{correlation_id => CorrelationId}, Msg0), +======= + Msg0 = amqp10_msg:new(<<"tag 1">>, Payload, true), + Msg = amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"my CC key">>}]}}, + amqp10_msg:set_properties(#{correlation_id => CorrelationId}, Msg0)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = amqp10_client:send_msg(Sender, Msg), {ok, _} = amqp10_client:get_msg(Receiver), @@ -4458,7 +4673,11 @@ trace(Config) -> payload = Payload}} = amqp_channel:call(Ch, #'basic.get'{queue = TraceQ}), ?assertMatch(#{<<"exchange_name">> := <<"amq.direct">>, +<<<<<<< HEAD <<"routing_keys">> := [RoutingKey], +======= + <<"routing_keys">> := [RoutingKey, <<"my CC key">>], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <<"connection">> := <<"127.0.0.1:", _/binary>>, <<"node">> := Node, <<"vhost">> := <<"/">>, @@ -4473,7 +4692,11 @@ trace(Config) -> payload = Payload}} = amqp_channel:call(Ch, #'basic.get'{queue = TraceQ}), ?assertMatch(#{<<"exchange_name">> := <<"amq.direct">>, +<<<<<<< HEAD <<"routing_keys">> := [RoutingKey], +======= + <<"routing_keys">> := [RoutingKey, <<"my CC key">>], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <<"connection">> := <<"127.0.0.1:", _/binary>>, <<"node">> := Node, <<"vhost">> := <<"/">>, @@ -4644,9 +4867,13 @@ idle_time_out_on_client(Config) -> receive {amqp10_event, {connection, Connection, +<<<<<<< HEAD {closed, {resource_limit_exceeded, <<"remote idle-time-out">>}}}} -> ok +======= + {closed, _}}} -> ok +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) after 30000 -> ct:fail({missing_event, ?LINE}) end, @@ -4668,7 +4895,11 @@ handshake_timeout(Config) -> Par = ?FUNCTION_NAME, {ok, DefaultVal} = rpc(Config, application, get_env, [App, Par]), ok = rpc(Config, application, set_env, [App, Par, 200]), +<<<<<<< HEAD Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), +======= + Port = get_node_config(Config, 0, tcp_port_amqp), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Socket} = gen_tcp:connect("localhost", Port, [{active, false}]), ?assertEqual({error, closed}, gen_tcp:recv(Socket, 0, 400)), ok = rpc(Config, application, set_env, [App, Par, DefaultVal]). @@ -4683,7 +4914,11 @@ credential_expires(Config) -> OpnConf = connection_config(Config), {ok, Connection} = amqp10_client:open_connection(OpnConf), receive {amqp10_event, {connection, Connection, opened}} -> ok +<<<<<<< HEAD after 2000 -> ct:fail({missing_event, ?LINE}) +======= + after 30000 -> ct:fail({missing_event, ?LINE}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, %% Since we don't renew our credential, we expect the server to close our connection. @@ -4692,7 +4927,11 @@ credential_expires(Config) -> {connection, Connection, {closed, {unauthorized_access, <<"credential expired">>}}}} -> ok +<<<<<<< HEAD after 10_000 -> +======= + after 30_000 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) flush(?LINE), ct:fail({missing_event, ?LINE}) end, @@ -5977,6 +6216,7 @@ reserved_annotation(Config) -> end, ok = close_connection_sync(Connection). +<<<<<<< HEAD %% internal %% @@ -5990,6 +6230,244 @@ init(Node, Config) -> {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), {Connection, Session, LinkPair}. +======= +%% Test that x-cc routing keys work together with target address +%% /exchanges/:exchange/:routing-key +x_cc_annotation_exchange(Config) -> + QName1 = <<"queue 1">>, + QName2 = <<"queue 2">>, + {Connection, Session, LinkPair} = init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName1, #{}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName2, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName1, <<"amq.direct">>, <<"key 1">>, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName2, <<"amq.direct">>, <<"key 2">>, #{}), + Address = rabbitmq_amqp_address:exchange(<<"amq.direct">>, <<"key 1">>), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + Payload = <<"my message">>, + ok = amqp10_client:send_msg(Sender, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"key 2">>}]}}, + amqp10_msg:new(<<"tag">>, Payload))), + ok = wait_for_accepted(<<"tag">>), + ok = amqp10_client:detach_link(Sender), + + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, rabbitmq_amqp_address:queue(QName1), settled), + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, rabbitmq_amqp_address:queue(QName2), settled), + {ok, Msg1} = amqp10_client:get_msg(Receiver1), + {ok, Msg2} = amqp10_client:get_msg(Receiver2), + ?assertEqual([Payload], amqp10_msg:body(Msg1)), + ?assertEqual([Payload], amqp10_msg:body(Msg2)), + + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName1), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName2), + ok = end_session_sync(Session), + ok = amqp10_client:close_connection(Connection). + +%% Test that x-cc routing keys work together with target address +%% /exchanges/:exchange +x_cc_annotation_exchange_routing_key_empty(Config) -> + QName1 = <<"queue 1">>, + QName2 = <<"queue 2">>, + {Connection, Session, LinkPair} = init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName1, #{}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName2, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName1, <<"amq.direct">>, <<"key 1">>, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName2, <<"amq.direct">>, <<"key 2">>, #{}), + AddressEmptyRoutingKey = rabbitmq_amqp_address:exchange(<<"amq.direct">>), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, AddressEmptyRoutingKey), + ok = wait_for_credit(Sender), + + Payload = <<"my message">>, + ok = amqp10_client:send_msg(Sender, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"key 1">>}, + {utf8, <<"key 2">>}]}}, + amqp10_msg:new(<<"tag">>, Payload))), + ok = wait_for_accepted(<<"tag">>), + ok = amqp10_client:detach_link(Sender), + + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, rabbitmq_amqp_address:queue(QName1), settled), + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, rabbitmq_amqp_address:queue(QName2), settled), + {ok, Msg1} = amqp10_client:get_msg(Receiver1), + {ok, Msg2} = amqp10_client:get_msg(Receiver2), + ?assertEqual([Payload], amqp10_msg:body(Msg1)), + ?assertEqual([Payload], amqp10_msg:body(Msg2)), + + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName1), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName2), + ok = end_session_sync(Session), + ok = amqp10_client:close_connection(Connection). + +%% Test that x-cc routing keys work together with target address +%% /queues/:queue +x_cc_annotation_queue(Config) -> + QName1 = <<"queue 1">>, + QName2 = <<"queue 2">>, + Address1 = rabbitmq_amqp_address:queue(QName1), + Address2 = rabbitmq_amqp_address:queue(QName2), + {Connection, Session, LinkPair} = init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName1, #{}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName2, #{}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address1), + ok = wait_for_credit(Sender), + + Payload = <<"my message">>, + ok = amqp10_client:send_msg(Sender, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, QName2}]}}, + amqp10_msg:new(<<"tag">>, Payload))), + ok = wait_for_accepted(<<"tag">>), + ok = amqp10_client:detach_link(Sender), + + {ok, Receiver1} = amqp10_client:attach_receiver_link(Session, <<"receiver 1">>, Address1, settled), + {ok, Receiver2} = amqp10_client:attach_receiver_link(Session, <<"receiver 2">>, Address2, settled), + {ok, Msg1} = amqp10_client:get_msg(Receiver1), + {ok, Msg2} = amqp10_client:get_msg(Receiver2), + ?assertEqual([Payload], amqp10_msg:body(Msg1)), + ?assertEqual([Payload], amqp10_msg:body(Msg2)), + + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName1), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName2), + ok = end_session_sync(Session), + ok = amqp10_client:close_connection(Connection). + +%% Test that x-cc routing keys work together with target address 'null' +x_cc_annotation_null(Config) -> + QName1 = <<"queue 1">>, + QName2 = <<"queue 2">>, + QAddress1 = rabbitmq_amqp_address:queue(QName1), + QAddress2 = rabbitmq_amqp_address:queue(QName2), + {Connection, Session, LinkPair} = init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName1, #{}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName2, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName1, <<"amq.direct">>, <<"key-1">>, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName2, <<"amq.direct">>, <<"🗝️-2"/utf8>>, #{}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, null), + ok = wait_for_credit(Sender), + {ok, Receiver1} = amqp10_client:attach_receiver_link(Session, <<"receiver 1">>, QAddress1, settled), + {ok, Receiver2} = amqp10_client:attach_receiver_link(Session, <<"receiver 2">>, QAddress2, settled), + + Msg1 = amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"key-1">>}, + {utf8, <<"key-3">>}]}}, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:exchange(<<"amq.direct">>, <<"🗝️-2"/utf8>>)}, + amqp10_msg:new(<<"t1">>, <<"m1">>))), + ok = amqp10_client:send_msg(Sender, Msg1), + ok = wait_for_accepted(<<"t1">>), + {ok, R1M1} = amqp10_client:get_msg(Receiver1), + {ok, R2M1} = amqp10_client:get_msg(Receiver2), + ?assertEqual([<<"m1">>], amqp10_msg:body(R1M1)), + ?assertEqual([<<"m1">>], amqp10_msg:body(R2M1)), + + Msg2 = amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"🗝️-2"/utf8>>}, + {utf8, <<"key-1">>}]}}, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:exchange(<<"amq.direct">>)}, + amqp10_msg:new(<<"t2">>, <<"m2">>))), + ok = amqp10_client:send_msg(Sender, Msg2), + ok = wait_for_accepted(<<"t2">>), + {ok, R1M2} = amqp10_client:get_msg(Receiver1), + {ok, R2M2} = amqp10_client:get_msg(Receiver2), + ?assertEqual([<<"m2">>], amqp10_msg:body(R1M2)), + ?assertEqual([<<"m2">>], amqp10_msg:body(R2M2)), + + Msg3 = amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, QName1}]}}, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:queue(QName2)}, + amqp10_msg:new(<<"t3">>, <<"m3">>))), + ok = amqp10_client:send_msg(Sender, Msg3), + ok = wait_for_accepted(<<"t3">>), + {ok, R1M3} = amqp10_client:get_msg(Receiver1), + {ok, R2M3} = amqp10_client:get_msg(Receiver2), + ?assertEqual([<<"m3">>], amqp10_msg:body(R1M3)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R2M3)), + + Msg4 = amqp10_msg:set_message_annotations( + %% We send a symbol instead of utf8.. + #{<<"x-cc">> => {list, [{symbol, QName1}]}}, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:queue(QName2)}, + amqp10_msg:new(<<"t4">>, <<"m4">>))), + ok = amqp10_client:send_msg(Sender, Msg4), + %% "If the source of the link supports the rejected outcome, and the message has not + %% already been settled by the sender, then the routing node MUST reject the message. + %% In this case the error field of rejected MUST contain the error which would have been communicated + %% in the detach which would have be sent if a link to the same address had been attempted." + %% https://docs.oasis-open.org/amqp/anonterm/v1.0/cs01/anonterm-v1.0-cs01.html#doc-routingerrors + receive {amqp10_disposition, {{rejected, Error}, <<"t4">>}} -> + ?assertMatch( + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_INVALID_FIELD, + description = {utf8, <<"bad value for 'x-cc' message-annotation:", _/binary>>}}, + Error) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:detach_link(Sender), + ok = amqp10_client:detach_link(Receiver1), + ok = amqp10_client:detach_link(Receiver2), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName1), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName2), + ok = end_session_sync(Session), + ok = amqp10_client:close_connection(Connection). + +bad_x_cc_annotation_exchange(Config) -> + OpnConf = connection_config(Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session(Connection), + + Address = rabbitmq_amqp_address:exchange(<<"amq.direct">>, <<"key-1">>), + {ok, Sender1} = amqp10_client:attach_sender_link(Session, <<"sender 1">>, Address), + ok = wait_for_credit(Sender1), + ok = amqp10_client:send_msg( + Sender1, + amqp10_msg:set_message_annotations( + %% We send an array instead of a list. + #{<<"x-cc">> => {array, utf8, [{utf8, <<"🗝️-2"/utf8>>}]}}, + amqp10_msg:new(<<"t1">>, <<"m1">>))), + ok = wait_for_settlement(<<"t1">>, released), + receive {amqp10_event, {link, Sender1, {detached, Error1}}} -> + ?assertMatch( + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_INVALID_FIELD, + description = {utf8, <<"bad value for 'x-cc' message-annotation: " + "{array,utf8,[{utf8,<<\"🗝️-2"/utf8, _Rest/binary>>}}, + Error1) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + + {ok, Sender2} = amqp10_client:attach_sender_link(Session, <<"sender 2">>, Address), + ok = wait_for_credit(Sender2), + ok = amqp10_client:send_msg( + Sender2, + amqp10_msg:set_message_annotations( + %% We include a non-utf8 type in the list. + #{<<"x-cc">> => {list, [{symbol, <<"key-3">>}]}}, + amqp10_msg:new(<<"t2">>, <<"m2">>))), + ok = wait_for_settlement(<<"t2">>, released), + receive {amqp10_event, {link, Sender2, {detached, Error2}}} -> + ?assertEqual( + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_INVALID_FIELD, + description = {utf8, <<"bad value for 'x-cc' message-annotation: " + "{list,[{symbol,<<\"key-3\">>}]}">>}}, + Error2) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + + ok = end_session_sync(Session), + ok = amqp10_client:close_connection(Connection). + +%% internal +%% + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) receive_all_messages(Receiver, Accept) -> receive_all_messages0(Receiver, Accept, []). @@ -6004,6 +6482,7 @@ receive_all_messages0(Receiver, Accept, Acc) -> lists:reverse(Acc) end. +<<<<<<< HEAD connection_config(Config) -> connection_config(0, Config). @@ -6024,6 +6503,8 @@ flush(Prefix) -> ok end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) open_and_close_connection(Config) -> OpnConf = connection_config(Config), {ok, Connection} = amqp10_client:open_connection(OpnConf), @@ -6032,6 +6513,7 @@ open_and_close_connection(Config) -> end, ok = close_connection_sync(Connection). +<<<<<<< HEAD % before we can send messages we have to wait for credit from the server wait_for_credit(Sender) -> receive @@ -6084,6 +6566,8 @@ wait_for_connection_close(Connection) -> ct:fail({connection_close_timeout, Connection}) end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) wait_for_accepted(Tag) -> wait_for_settlement(Tag, accepted). @@ -6096,6 +6580,7 @@ wait_for_settlement(Tag, State) -> ct:fail({settled_timeout, Tag}) end. +<<<<<<< HEAD wait_for_accepts(0) -> ok; wait_for_accepts(N) -> @@ -6106,6 +6591,8 @@ wait_for_accepts(N) -> ct:fail({missing_accepted, N}) end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) delete_queue(Session, QName) -> {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync( Session, <<"delete queue">>), @@ -6156,6 +6643,7 @@ count_received_messages0(Receiver, Count) -> Count end. +<<<<<<< HEAD send_messages(Sender, Left, Settled) -> send_messages(Sender, Left, Settled, <<>>). @@ -6204,6 +6692,8 @@ send_until_remote_incoming_window_exceeded0(Sender, Left) -> ok end. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_link_credit_runs_out(_Sender, 0) -> ct:fail(sufficient_link_credit); assert_link_credit_runs_out(Sender, Left) -> @@ -6216,7 +6706,11 @@ assert_link_credit_runs_out(Sender, Left) -> receive {amqp10_event, {link, Sender, credited}} -> ct:pal("credited with ~b messages left", [Left]), assert_link_credit_runs_out(Sender, Left - 1) +<<<<<<< HEAD after 500 -> +======= + after 30000 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ct:pal("insufficient link credit with ~b messages left", [Left]), ok end @@ -6331,8 +6825,13 @@ find_event(Type, Props, Events) when is_list(Props), is_list(Events) -> fun(#event{type = EventType, props = EventProps}) -> Type =:= EventType andalso lists:all( +<<<<<<< HEAD fun({Key, _Value}) -> lists:keymember(Key, 1, EventProps) +======= + fun(Prop) -> + lists:member(Prop, EventProps) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, Props) end, Events). diff --git a/deps/rabbit/test/amqp_filtex_SUITE.erl b/deps/rabbit/test/amqp_filtex_SUITE.erl new file mode 100644 index 000000000000..cd088a7d6088 --- /dev/null +++ b/deps/rabbit/test/amqp_filtex_SUITE.erl @@ -0,0 +1,665 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +%% Test suite for +%% AMQP Filter Expressions Version 1.0 Working Draft 09 +-module(amqp_filtex_SUITE). + +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp10_common/include/amqp10_filtex.hrl"). +-include_lib("amqp10_common/include/amqp10_framing.hrl"). + +-compile([nowarn_export_all, + export_all]). + +-import(rabbit_ct_broker_helpers, + [rpc/4]). +-import(rabbit_ct_helpers, + [eventually/1]). +-import(amqp_utils, + [init/1, + connection_config/1, + flush/1, + wait_for_credit/1, + wait_for_accepts/1, + send_messages/3, + detach_link_sync/1, + end_session_sync/1, + wait_for_session_end/1, + close_connection_sync/1]). + +all() -> + [ + {group, cluster_size_1} + ]. + +groups() -> + [ + {cluster_size_1, [shuffle], + [ + properties_section, + application_properties_section, + multiple_sections, + filter_few_messages_from_many, + string_modifier + ]} + ]. + +init_per_suite(Config) -> + {ok, _} = application:ensure_all_started(amqp10_client), + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:merge_app_env( + Config, {rabbit, [{quorum_tick_interval, 1000}, + {stream_tick_interval, 1000} + ]}). + +end_per_suite(Config) -> + Config. + +init_per_group(_Group, Config) -> + Suffix = rabbit_ct_helpers:testcase_absname(Config, "", "-"), + Config1 = rabbit_ct_helpers:set_config( + Config, [{rmq_nodename_suffix, Suffix}]), + rabbit_ct_helpers:run_setup_steps( + Config1, + rabbit_ct_broker_helpers:setup_steps() ++ + rabbit_ct_client_helpers:setup_steps()). + +end_per_group(_, Config) -> + rabbit_ct_helpers:run_teardown_steps(Config, + rabbit_ct_client_helpers:teardown_steps() ++ + rabbit_ct_broker_helpers:teardown_steps()). + +init_per_testcase(Testcase, Config) -> + rabbit_ct_helpers:testcase_started(Config, Testcase). + +end_per_testcase(Testcase, Config) -> + %% Assert that every testcase cleaned up. + eventually(?_assertEqual([], rpc(Config, rabbit_amqqueue, list, []))), + %% Wait for sessions to terminate before starting the next test case. + eventually(?_assertEqual([], rpc(Config, rabbit_amqp_session, list_local, []))), + rabbit_ct_helpers:testcase_finished(Config, Testcase). + +properties_section(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(Stream), + + OpnConf0 = connection_config(Config), + OpnConf = OpnConf0#{notify_with_performative => true}, + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), + {ok, #{}} = rabbitmq_amqp_client:declare_queue( + LinkPair, + Stream, + #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + Now = erlang:system_time(millisecond), + To = rabbitmq_amqp_address:exchange(<<"some exchange">>, <<"routing key">>), + ReplyTo = rabbitmq_amqp_address:queue(<<"some queue">>), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{message_id => {ulong, 999}, + user_id => <<"guest">>, + to => To, + subject => <<"🐇"/utf8>>, + reply_to => ReplyTo, + correlation_id => <<"corr-123">>, + content_type => <<"text/plain">>, + content_encoding => <<"some encoding">>, + absolute_expiry_time => Now + 100_000, + creation_time => Now, + group_id => <<"my group ID">>, + group_sequence => 16#ff_ff_ff_ff, + reply_to_group_id => <<"other group ID">>}, + amqp10_msg:new(<<"t1">>, <<"m1">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:new(<<"t2">>, <<"m2">>)), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{group_id => <<"my group ID">>}, + amqp10_msg:new(<<"t3">>, <<"m3">>))), + + ok = wait_for_accepts(3), + ok = detach_link_sync(Sender), + flush(sent), + + PropsFilter1 = [ + {{symbol, <<"message-id">>}, {ulong, 999}}, + {{symbol, <<"user-id">>}, {binary, <<"guest">>}}, + {{symbol, <<"subject">>}, {utf8, <<"🐇"/utf8>>}}, + {{symbol, <<"to">>}, {utf8, To}}, + {{symbol, <<"reply-to">>}, {utf8, ReplyTo}}, + {{symbol, <<"correlation-id">>}, {utf8, <<"corr-123">>}}, + {{symbol, <<"content-type">>}, {symbol, <<"text/plain">>}}, + {{symbol, <<"content-encoding">>}, {symbol, <<"some encoding">>}}, + {{symbol, <<"absolute-expiry-time">>}, {timestamp, Now + 100_000}}, + {{symbol, <<"creation-time">>}, {timestamp, Now}}, + {{symbol, <<"group-id">>}, {utf8, <<"my group ID">>}}, + {{symbol, <<"group-sequence">>}, {uint, 16#ff_ff_ff_ff}}, + {{symbol, <<"reply-to-group-id">>}, {utf8, <<"other group ID">>}} + ], + Filter1 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter1}}, + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, Address, + settled, configuration, Filter1), + ok = amqp10_client:flow_link_credit(Receiver1, 10, never), + receive {amqp10_msg, Receiver1, R1M1} -> + ?assertEqual([<<"m1">>], amqp10_msg:body(R1M1)) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver1), + + PropsFilter2 = [{{symbol, <<"group-id">>}, {utf8, <<"my group ID">>}}], + Filter2 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter2}}, + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, Address, + unsettled, configuration, Filter2), + {ok, R2M1} = amqp10_client:get_msg(Receiver2), + {ok, R2M2} = amqp10_client:get_msg(Receiver2), + ok = amqp10_client:accept_msg(Receiver2, R2M1), + ok = amqp10_client:accept_msg(Receiver2, R2M2), + ?assertEqual([<<"m1">>], amqp10_msg:body(R2M1)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R2M2)), + ok = detach_link_sync(Receiver2), + + %% Filter is in place, but no message matches. + PropsFilter3 = [{{symbol, <<"group-id">>}, {utf8, <<"no match">>}}], + Filter3 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter3}}, + {ok, Receiver3} = amqp10_client:attach_receiver_link( + Session, <<"receiver 3">>, Address, + unsettled, configuration, Filter3), + receive {amqp10_event, {link, Receiver3, {attached, #'v1_0.attach'{}}}} -> ok + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:flow_link_credit(Receiver3, 10, never), + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver3), + + %% Wrong type should fail validation in the server. + %% RabbitMQ should exclude this filter in its reply attach frame because + %% "the sending endpoint [RabbitMQ] sets the filter actually in place". + %% Hence, no filter expression is actually in place and we should receive all messages. + PropsFilter4 = [{{symbol, <<"group-id">>}, {uint, 3}}], + Filter4 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter4}}, + {ok, Receiver4} = amqp10_client:attach_receiver_link( + Session, <<"receiver 4">>, Address, + unsettled, configuration, Filter4), + receive {amqp10_event, + {link, Receiver4, + {attached, #'v1_0.attach'{ + source = #'v1_0.source'{filter = {map, ActualFilter}}}}}} -> + ?assertMatch([{{symbol,<<"rabbitmq:stream-offset-spec">>}, _}], + ActualFilter) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + {ok, R4M1} = amqp10_client:get_msg(Receiver4), + {ok, R4M2} = amqp10_client:get_msg(Receiver4), + {ok, R4M3} = amqp10_client:get_msg(Receiver4), + ok = amqp10_client:accept_msg(Receiver4, R4M1), + ok = amqp10_client:accept_msg(Receiver4, R4M2), + ok = amqp10_client:accept_msg(Receiver4, R4M3), + ?assertEqual([<<"m1">>], amqp10_msg:body(R4M1)), + ?assertEqual([<<"m2">>], amqp10_msg:body(R4M2)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R4M3)), + ok = detach_link_sync(Receiver4), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, Stream), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = end_session_sync(Session), + ok = close_connection_sync(Connection). + +application_properties_section(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(Stream), + OpnConf0 = connection_config(Config), + OpnConf = OpnConf0#{notify_with_performative => true}, + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), + {ok, #{}} = rabbitmq_amqp_client:declare_queue( + LinkPair, + Stream, + #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_application_properties( + #{<<"k1">> => -2, + <<"k2">> => 10, + <<"k3">> => false, + <<"k4">> => true, + <<"k5">> => <<"hey">>}, + amqp10_msg:new(<<"t1">>, <<"m1">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_application_properties( + #{<<"k2">> => 10.1}, + amqp10_msg:new(<<"t2">>, <<"m2">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:new(<<"t3">>, <<"m3">>)), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_application_properties( + #{<<"k2">> => 10.0}, + amqp10_msg:new(<<"t4">>, <<"m4">>))), + + ok = wait_for_accepts(4), + ok = detach_link_sync(Sender), + flush(sent), + + AppPropsFilter0 = [{{utf8, <<"k5">>}, {symbol, <<"no match">>}}], + Filter0 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter0}}, + {ok, Receiver0} = amqp10_client:attach_receiver_link( + Session, <<"receiver 0">>, Address, + unsettled, configuration, Filter0), + %% Wait for the attach so the detach command won't fail + receive {amqp10_event, + {link, Receiver0, {attached, #'v1_0.attach'{}}}} -> + ok + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:flow_link_credit(Receiver0, 10, never), + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver0), + + AppPropsFilter1 = [ + {{utf8, <<"k1">>}, {int, -2}}, + {{utf8, <<"k5">>}, {symbol, <<"hey">>}}, + {{utf8, <<"k4">>}, {boolean, true}}, + {{utf8, <<"k3">>}, false} + ], + Filter1 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter1}}, + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, Address, + settled, configuration, Filter1), + receive {amqp10_event, + {link, Receiver1, + {attached, #'v1_0.attach'{ + source = #'v1_0.source'{filter = {map, ActualFilter1}}}}}} -> + ?assertMatch( + {described, _Type, {map, [ + {{utf8, <<"k1">>}, {int, -2}}, + {{utf8, <<"k5">>}, {symbol, <<"hey">>}}, + {{utf8, <<"k4">>}, true}, + {{utf8, <<"k3">>}, false} + ]}}, + proplists:get_value({symbol, ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER}, ActualFilter1)) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:flow_link_credit(Receiver1, 10, never), + receive {amqp10_msg, Receiver1, R1M1} -> + ?assertEqual([<<"m1">>], amqp10_msg:body(R1M1)) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver1), + + %% Due to simple type matching [filtex-v1.0-wd09 §4.1.1] + %% we expect integer 10 to also match number 10.0. + AppPropsFilter2 = [{{utf8, <<"k2">>}, {uint, 10}}], + Filter2 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter2}}, + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, Address, + unsettled, configuration, Filter2), + {ok, R2M1} = amqp10_client:get_msg(Receiver2), + {ok, R2M2} = amqp10_client:get_msg(Receiver2), + ok = amqp10_client:accept_msg(Receiver2, R2M1), + ok = amqp10_client:accept_msg(Receiver2, R2M2), + ?assertEqual([<<"m1">>], amqp10_msg:body(R2M1)), + ?assertEqual([<<"m4">>], amqp10_msg:body(R2M2)), + ok = detach_link_sync(Receiver2), + + %% A reference field value of NULL should always match. [filtex-v1.0-wd09 §4.1.1] + AppPropsFilter3 = [{{utf8, <<"k2">>}, null}], + Filter3 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter3}}, + {ok, Receiver3} = amqp10_client:attach_receiver_link( + Session, <<"receiver 3">>, Address, + unsettled, configuration, Filter3), + {ok, R3M1} = amqp10_client:get_msg(Receiver3), + {ok, R3M2} = amqp10_client:get_msg(Receiver3), + {ok, R3M3} = amqp10_client:get_msg(Receiver3), + ok = amqp10_client:accept_msg(Receiver3, R3M1), + ok = amqp10_client:accept_msg(Receiver3, R3M2), + ok = amqp10_client:accept_msg(Receiver3, R3M3), + ?assertEqual([<<"m1">>], amqp10_msg:body(R3M1)), + ?assertEqual([<<"m2">>], amqp10_msg:body(R3M2)), + ?assertEqual([<<"m4">>], amqp10_msg:body(R3M3)), + ok = detach_link_sync(Receiver3), + + %% Wrong type should fail validation in the server. + %% RabbitMQ should exclude this filter in its reply attach frame because + %% "the sending endpoint [RabbitMQ] sets the filter actually in place". + %% Hence, no filter expression is actually in place and we should receive all messages. + AppPropsFilter4 = [{{symbol, <<"k2">>}, {uint, 10}}], + Filter4 = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter4}}, + {ok, Receiver4} = amqp10_client:attach_receiver_link( + Session, <<"receiver 4">>, Address, + unsettled, configuration, Filter4), + receive {amqp10_event, + {link, Receiver4, + {attached, #'v1_0.attach'{ + source = #'v1_0.source'{filter = {map, ActualFilter4}}}}}} -> + ?assertMatch([{{symbol,<<"rabbitmq:stream-offset-spec">>}, _}], + ActualFilter4) + after 30000 -> ct:fail({missing_event, ?LINE}) + end, + {ok, R4M1} = amqp10_client:get_msg(Receiver4), + {ok, R4M2} = amqp10_client:get_msg(Receiver4), + {ok, R4M3} = amqp10_client:get_msg(Receiver4), + {ok, R4M4} = amqp10_client:get_msg(Receiver4), + ok = amqp10_client:accept_msg(Receiver4, R4M1), + ok = amqp10_client:accept_msg(Receiver4, R4M2), + ok = amqp10_client:accept_msg(Receiver4, R4M3), + ok = amqp10_client:accept_msg(Receiver4, R4M4), + ?assertEqual([<<"m1">>], amqp10_msg:body(R4M1)), + ?assertEqual([<<"m2">>], amqp10_msg:body(R4M2)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R4M3)), + ?assertEqual([<<"m4">>], amqp10_msg:body(R4M4)), + ok = detach_link_sync(Receiver4), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, Stream), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = end_session_sync(Session), + ok = close_connection_sync(Connection). + +%% Test filter expressions matching multiple message sections. +multiple_sections(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(Stream), + {Connection, Session, LinkPair} = init(Config), + {ok, #{}} = rabbitmq_amqp_client:declare_queue( + LinkPair, + Stream, + #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{subject => <<"The Subject">>}, + amqp10_msg:new(<<"t1">>, <<"m1">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_application_properties( + #{<<"The Key">> => -123}, + amqp10_msg:new(<<"t2">>, <<"m2">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{subject => <<"The Subject">>}, + amqp10_msg:set_application_properties( + #{<<"The Key">> => -123}, + amqp10_msg:new(<<"t3">>, <<"m3">>)))), + + ok = wait_for_accepts(3), + ok = detach_link_sync(Sender), + flush(sent), + + PropsFilter = [{{symbol, <<"subject">>}, {utf8, <<"The Subject">>}}], + Filter1 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, Address, + unsettled, configuration, Filter1), + {ok, R1M1} = amqp10_client:get_msg(Receiver1), + {ok, R1M3} = amqp10_client:get_msg(Receiver1), + ok = amqp10_client:accept_msg(Receiver1, R1M1), + ok = amqp10_client:accept_msg(Receiver1, R1M3), + ?assertEqual([<<"m1">>], amqp10_msg:body(R1M1)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R1M3)), + ok = detach_link_sync(Receiver1), + + AppPropsFilter = [{{utf8, <<"The Key">>}, {byte, -123}}], + Filter2 = #{?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, Address, + unsettled, configuration, Filter2), + {ok, R2M2} = amqp10_client:get_msg(Receiver2), + {ok, R2M3} = amqp10_client:get_msg(Receiver2), + ok = amqp10_client:accept_msg(Receiver2, R2M2), + ok = amqp10_client:accept_msg(Receiver2, R2M3), + ?assertEqual([<<"m2">>], amqp10_msg:body(R2M2)), + ?assertEqual([<<"m3">>], amqp10_msg:body(R2M3)), + ok = detach_link_sync(Receiver2), + + Filter3 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter}, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver3} = amqp10_client:attach_receiver_link( + Session, <<"receiver 3">>, Address, + unsettled, configuration, Filter3), + {ok, R3M3} = amqp10_client:get_msg(Receiver3), + ok = amqp10_client:accept_msg(Receiver3, R3M3), + ?assertEqual([<<"m3">>], amqp10_msg:body(R3M3)), + ok = detach_link_sync(Receiver3), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, Stream), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = end_session_sync(Session), + ok = close_connection_sync(Connection). + +%% Filter a small subset from many messages. +%% We test here that flow control still works correctly. +filter_few_messages_from_many(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(Stream), + {Connection, Session, LinkPair} = init(Config), + {ok, #{}} = rabbitmq_amqp_client:declare_queue( + LinkPair, + Stream, + #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{group_id => <<"my group ID">>}, + amqp10_msg:new(<<"t1">>, <<"first msg">>))), + ok = send_messages(Sender, 1000, false), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{group_id => <<"my group ID">>}, + amqp10_msg:new(<<"t2">>, <<"last msg">>))), + ok = wait_for_accepts(1002), + ok = detach_link_sync(Sender), + flush(sent), + + %% Our filter should cause us to receive only the first and + %% last message out of the 1002 messages in the stream. + PropsFilter = [{{symbol, <<"group-id">>}, {utf8, <<"my group ID">>}}], + Filter = #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter}}, + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session, <<"receiver">>, Address, + unsettled, configuration, Filter), + + ok = amqp10_client:flow_link_credit(Receiver, 2, never), + receive {amqp10_msg, Receiver, M1} -> + ?assertEqual([<<"first msg">>], amqp10_msg:body(M1)), + ok = amqp10_client:accept_msg(Receiver, M1) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, Receiver, M2} -> + ?assertEqual([<<"last msg">>], amqp10_msg:body(M2)), + ok = amqp10_client:accept_msg(Receiver, M2) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + ok = detach_link_sync(Receiver), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, Stream), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = end_session_sync(Session), + ok = close_connection_sync(Connection). + +string_modifier(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(Stream), + {Connection, Session, LinkPair} = init(Config), + {ok, #{}} = rabbitmq_amqp_client:declare_queue( + LinkPair, + Stream, + #{arguments => #{<<"x-queue-type">> => {utf8, <<"stream">>}}}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address), + ok = wait_for_credit(Sender), + + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{to => <<"abc 1">>, + reply_to => <<"abc 2">>, + subject => <<"abc 3">>, + group_id => <<"abc 4">>, + reply_to_group_id => <<"abc 5">>, + message_id => {utf8, <<"abc 6">>}, + correlation_id => <<"abc 7">>, + group_sequence => 16#ff_ff_ff_ff}, + amqp10_msg:set_application_properties( + #{<<"k1">> => <<"abc 8">>, + <<"k2">> => <<"abc 9">>}, + amqp10_msg:new(<<"t1">>, <<"m1">>)))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_application_properties( + #{<<"k1">> => <<"abc">>}, + amqp10_msg:new(<<"t2">>, <<"m2">>))), + ok = amqp10_client:send_msg( + Sender, + amqp10_msg:set_properties( + #{subject => <<"$Hello">>, + reply_to_group_id => <<"xyz 5">>}, + amqp10_msg:new(<<"t3">>, <<"m3">>))), + + ok = wait_for_accepts(3), + ok = detach_link_sync(Sender), + flush(sent), + + PropsFilter1 = [ + {{symbol, <<"to">>}, {utf8, <<"$p:abc ">>}}, + {{symbol, <<"reply-to">>}, {utf8, <<"$p:abc">>}}, + {{symbol, <<"subject">>}, {utf8, <<"$p:ab">>}}, + {{symbol, <<"group-id">>}, {utf8, <<"$p:a">>}}, + {{symbol, <<"reply-to-group-id">>}, {utf8, <<"$s:5">>}}, + {{symbol, <<"correlation-id">>}, {utf8, <<"$s:abc 7">>}}, + {{symbol, <<"message-id">>}, {utf8, <<"$p:abc 6">>}} + ], + AppPropsFilter1 = [ + {{utf8, <<"k1">>}, {utf8, <<"$s: 8">>}}, + {{utf8, <<"k2">>}, {utf8, <<"$p:abc ">>}} + ], + Filter1 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter1}, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter1}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session, <<"receiver 1">>, Address, + settled, configuration, Filter1), + ok = amqp10_client:flow_link_credit(Receiver1, 10, never), + receive {amqp10_msg, Receiver1, R1M1} -> + ?assertEqual([<<"m1">>], amqp10_msg:body(R1M1)) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver1), + + %% Same filters as before except for subject which shouldn't match anymore. + PropsFilter2 = lists:keyreplace( + {symbol, <<"subject">>}, 1, PropsFilter1, + {{symbol, <<"subject">>}, {utf8, <<"$s:xxxxxxxxxxxxxx">>}}), + Filter2 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter2}, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter1}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session, <<"receiver 2">>, Address, + settled, configuration, Filter2), + ok = amqp10_client:flow_link_credit(Receiver2, 10, never), + ok = assert_no_msg_received(?LINE), + ok = detach_link_sync(Receiver2), + + PropsFilter3 = [{{symbol, <<"reply-to-group-id">>}, {utf8, <<"$s: 5">>}}], + Filter3 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter3}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver3} = amqp10_client:attach_receiver_link( + Session, <<"receiver 3">>, Address, + settled, configuration, Filter3), + ok = amqp10_client:flow_link_credit(Receiver3, 10, never), + receive {amqp10_msg, Receiver3, R3M1} -> + ?assertEqual([<<"m1">>], amqp10_msg:body(R3M1)) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, Receiver3, R3M3} -> + ?assertEqual([<<"m3">>], amqp10_msg:body(R3M3)) + after 30000 -> ct:fail({missing_msg, ?LINE}) + end, + ok = detach_link_sync(Receiver3), + + %% '$$" is the escape prefix for case-sensitive matching of a string starting with ‘&’ + PropsFilter4 = [{{symbol, <<"subject">>}, {utf8, <<"$$Hello">>}}], + Filter4 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter4}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver4} = amqp10_client:attach_receiver_link( + Session, <<"receiver 4">>, Address, + settled, configuration, Filter4), + {ok, R4M3} = amqp10_client:get_msg(Receiver4), + ?assertEqual([<<"m3">>], amqp10_msg:body(R4M3)), + ok = detach_link_sync(Receiver4), + + %% Starting the reference field value with $ is invalid without using a valid modifier + %% prefix is invalid. + %% RabbitMQ should exclude this filter in its reply attach frame because + %% "the sending endpoint [RabbitMQ] sets the filter actually in place". + %% Hence, no filter expression is actually in place and we should receive all messages. + PropsFilter5 = [{{symbol, <<"subject">>}, {utf8, <<"$Hello">>}}], + Filter5 = #{?DESCRIPTOR_NAME_PROPERTIES_FILTER => {map, PropsFilter5}, + <<"rabbitmq:stream-offset-spec">> => <<"first">>}, + {ok, Receiver5} = amqp10_client:attach_receiver_link( + Session, <<"receiver 5">>, Address, + settled, configuration, Filter5), + {ok, R5M1} = amqp10_client:get_msg(Receiver5), + ?assertEqual([<<"m1">>], amqp10_msg:body(R5M1)), + {ok, R5M2} = amqp10_client:get_msg(Receiver5), + ?assertEqual([<<"m2">>], amqp10_msg:body(R5M2)), + {ok, R5M3} = amqp10_client:get_msg(Receiver5), + ?assertEqual([<<"m3">>], amqp10_msg:body(R5M3)), + ok = detach_link_sync(Receiver5), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, Stream), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = end_session_sync(Session), + ok = close_connection_sync(Connection). + +%% ------------------------------------------------------------------- +%% Helpers +%% ------------------------------------------------------------------- + +assert_no_msg_received(Line) -> + receive {amqp10_msg, _, _} = Msg -> + ct:fail({received_unexpected_msg, Line, Msg}) + after 10 -> + ok + end. diff --git a/deps/rabbit/test/amqp_system_SUITE.erl b/deps/rabbit/test/amqp_system_SUITE.erl index 0b3fcba3d186..c10a7852b965 100644 --- a/deps/rabbit/test/amqp_system_SUITE.erl +++ b/deps/rabbit/test/amqp_system_SUITE.erl @@ -51,6 +51,14 @@ groups() -> %% Testsuite setup/teardown. %% ------------------------------------------------------------------- +<<<<<<< HEAD +======= +suite() -> + [ + {timetrap, {minutes, 3}} + ]. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_suite(Config) -> rabbit_ct_helpers:log_environment(), Config. diff --git a/deps/rabbit/test/amqp_utils.erl b/deps/rabbit/test/amqp_utils.erl new file mode 100644 index 000000000000..22865df9192d --- /dev/null +++ b/deps/rabbit/test/amqp_utils.erl @@ -0,0 +1,144 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2023 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(amqp_utils). + +-include_lib("amqp10_common/include/amqp10_framing.hrl"). + +-export([init/1, init/2, + connection_config/1, connection_config/2, + flush/1, + wait_for_credit/1, + wait_for_accepts/1, + send_messages/3, send_messages/4, + detach_link_sync/1, + end_session_sync/1, + wait_for_session_end/1, + close_connection_sync/1]). + +init(Config) -> + init(0, Config). + +init(Node, Config) -> + OpnConf = connection_config(Node, Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), + {Connection, Session, LinkPair}. + +connection_config(Config) -> + connection_config(0, Config). + +connection_config(Node, Config) -> + Host = proplists:get_value(rmq_hostname, Config), + Port = rabbit_ct_broker_helpers:get_node_config(Config, Node, tcp_port_amqp), + #{address => Host, + port => Port, + container_id => <<"my container">>, + sasl => {plain, <<"guest">>, <<"guest">>}}. + +flush(Prefix) -> + receive + Msg -> + ct:pal("~p flushed: ~p~n", [Prefix, Msg]), + flush(Prefix) + after 1 -> + ok + end. + +% Before we can send messages we have to wait for credit from the server. +wait_for_credit(Sender) -> + receive + {amqp10_event, {link, Sender, credited}} -> + ok + after 5000 -> + flush("wait_for_credit timed out"), + ct:fail(credited_timeout) + end. + +wait_for_accepts(0) -> + ok; +wait_for_accepts(N) -> + receive + {amqp10_disposition, {accepted, _}} -> + wait_for_accepts(N - 1) + after 5000 -> + ct:fail({missing_accepted, N}) + end. + +send_messages(Sender, Left, Settled) -> + send_messages(Sender, Left, Settled, <<>>). + +send_messages(_, 0, _, _) -> + ok; +send_messages(Sender, Left, Settled, BodySuffix) -> + Bin = integer_to_binary(Left), + Body = <>, + Msg = amqp10_msg:new(Bin, Body, Settled), + case amqp10_client:send_msg(Sender, Msg) of + ok -> + send_messages(Sender, Left - 1, Settled, BodySuffix); + {error, insufficient_credit} -> + ok = wait_for_credit(Sender), + %% The credited event we just processed could have been received some time ago, + %% i.e. we might have 0 credits right now. This happens in the following scenario: + %% 1. We (test case proc) send a message successfully, the client session proc decrements remaining link credit from 1 to 0. + %% 2. The server grants our client session proc new credits. + %% 3. The client session proc sends us (test case proc) a credited event. + %% 4. We didn't even notice that we ran out of credits temporarily. We send the next message, it succeeds, + %% but do not process the credited event in our mailbox. + %% So, we must be defensive here and assume that the next amqp10_client:send/2 call might return {error, insufficient_credit} + %% again causing us then to really wait to receive a credited event (instead of just processing an old credited event). + send_messages(Sender, Left, Settled, BodySuffix) + end. + +detach_link_sync(Link) -> + ok = amqp10_client:detach_link(Link), + ok = wait_for_link_detach(Link). + +wait_for_link_detach(Link) -> + receive + {amqp10_event, {link, Link, {detached, normal}}} -> + flush(?FUNCTION_NAME), + ok; + {amqp10_event, {link, Link, {detached, #'v1_0.detach'{}}}} -> + flush(?FUNCTION_NAME), + ok + after 5000 -> + flush("wait_for_link_detach timed out"), + ct:fail({link_detach_timeout, Link}) + end. + +end_session_sync(Session) + when is_pid(Session) -> + ok = amqp10_client:end_session(Session), + ok = wait_for_session_end(Session). + +wait_for_session_end(Session) -> + receive + {amqp10_event, {session, Session, {ended, _}}} -> + flush(?FUNCTION_NAME), + ok + after 5000 -> + flush("wait_for_session_end timed out"), + ct:fail({session_end_timeout, Session}) + end. + +close_connection_sync(Connection) + when is_pid(Connection) -> + ok = amqp10_client:close_connection(Connection), + ok = wait_for_connection_close(Connection). + +wait_for_connection_close(Connection) -> + receive + {amqp10_event, {connection, Connection, {closed, normal}}} -> + flush(?FUNCTION_NAME), + ok + after 5000 -> + flush("wait_for_connection_close timed out"), + ct:fail({connection_close_timeout, Connection}) + end. diff --git a/deps/rabbit/test/dead_lettering_SUITE.erl b/deps/rabbit/test/dead_lettering_SUITE.erl index 6d0ad63b13d8..e1f4666ae97a 100644 --- a/deps/rabbit/test/dead_lettering_SUITE.erl +++ b/deps/rabbit/test/dead_lettering_SUITE.erl @@ -177,6 +177,7 @@ end_per_group(Group, Config) -> init_per_testcase(T, Config) when T =:= dead_letter_reject_expire_expire orelse T =:= stream -> +<<<<<<< HEAD case rabbit_ct_broker_helpers:enable_feature_flag(Config, message_containers_deaths_v2) of ok -> init_per_testcase0(T, Config); @@ -186,6 +187,13 @@ init_per_testcase(T, Config) %% * stream is known to fail due to https://github.com/rabbitmq/rabbitmq-server/issues/11173 Skip end; +======= + %% With feature flag message_containers_deaths_v2 disabled, test case: + %% * dead_letter_reject_expire_expire is known to fail due to https://github.com/rabbitmq/rabbitmq-server/issues/11159 + %% * stream is known to fail due to https://github.com/rabbitmq/rabbitmq-server/issues/11173 + ok = rabbit_ct_broker_helpers:enable_feature_flag(Config, message_containers_deaths_v2), + init_per_testcase0(T, Config); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_testcase(Testcase, Config) -> init_per_testcase0(Testcase, Config). @@ -1860,6 +1868,13 @@ stream(Config) -> {timestamp, T2} = rabbit_misc:table_lookup(Death2, <<"time">>), ?assert(T1 < T2), +<<<<<<< HEAD +======= + ?assertEqual({array, [{longstr, <<"cc 1">>}, + {longstr, <<"cc 2">>}]}, + rabbit_misc:table_lookup(Headers, <<"CC">>)), + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = rabbit_ct_client_helpers:close_channel(Ch0), ok = rabbit_ct_client_helpers:close_channel(Ch1). diff --git a/deps/rabbit/test/disconnect_detected_during_alarm_SUITE.erl b/deps/rabbit/test/disconnect_detected_during_alarm_SUITE.erl index b44c6de1440f..c2aeb4400b95 100644 --- a/deps/rabbit/test/disconnect_detected_during_alarm_SUITE.erl +++ b/deps/rabbit/test/disconnect_detected_during_alarm_SUITE.erl @@ -96,7 +96,11 @@ disconnect_detected_during_alarm(Config) -> ListConnections = fun() -> +<<<<<<< HEAD rpc:call(A, rabbit_networking, connection_info_all, []) +======= + rpc:call(A, rabbit_networking, connection_info_all, [[state]]) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, %% We've already disconnected, but blocked connection still should still linger on. diff --git a/deps/rabbit/test/feature_flags_v2_SUITE.erl b/deps/rabbit/test/feature_flags_v2_SUITE.erl index 37e881597153..9bf95896586d 100644 --- a/deps/rabbit/test/feature_flags_v2_SUITE.erl +++ b/deps/rabbit/test/feature_flags_v2_SUITE.erl @@ -47,8 +47,15 @@ enable_feature_flag_in_cluster_and_remove_member_concurrently_mfv2/1, enable_feature_flag_with_post_enable/1, failed_enable_feature_flag_with_post_enable/1, +<<<<<<< HEAD have_required_feature_flag_in_cluster_and_add_member_with_it_disabled/1, have_required_feature_flag_in_cluster_and_add_member_without_it/1, +======= + have_soft_required_feature_flag_in_cluster_and_add_member_with_it_disabled/1, + have_soft_required_feature_flag_in_cluster_and_add_member_without_it/1, + have_hard_required_feature_flag_in_cluster_and_add_member_without_it/1, + have_unknown_feature_flag_in_cluster_and_add_member_with_it_enabled/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) error_during_migration_after_initial_success/1, controller_waits_for_own_task_to_finish_before_exiting/1, controller_waits_for_remote_task_to_finish_before_exiting/1 @@ -96,8 +103,15 @@ groups() -> enable_feature_flag_in_cluster_and_remove_member_concurrently_mfv2, enable_feature_flag_with_post_enable, failed_enable_feature_flag_with_post_enable, +<<<<<<< HEAD have_required_feature_flag_in_cluster_and_add_member_with_it_disabled, have_required_feature_flag_in_cluster_and_add_member_without_it, +======= + have_soft_required_feature_flag_in_cluster_and_add_member_with_it_disabled, + have_soft_required_feature_flag_in_cluster_and_add_member_without_it, + have_hard_required_feature_flag_in_cluster_and_add_member_without_it, + have_unknown_feature_flag_in_cluster_and_add_member_with_it_enabled, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) error_during_migration_after_initial_success, controller_waits_for_own_task_to_finish_before_exiting, controller_waits_for_remote_task_to_finish_before_exiting @@ -199,7 +213,11 @@ stop_slave_node(Node) -> persistent_term:erase({?MODULE, Node}), ct:pal("- Stopping slave node `~ts`...", [Node]), +<<<<<<< HEAD ok = peer:stop(NodePid) +======= + _ = peer:stop(NodePid) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. connect_nodes([FirstNode | OtherNodes] = Nodes) -> @@ -1325,7 +1343,11 @@ failed_enable_feature_flag_with_post_enable(Config) -> ok. +<<<<<<< HEAD have_required_feature_flag_in_cluster_and_add_member_with_it_disabled( +======= +have_soft_required_feature_flag_in_cluster_and_add_member_with_it_disabled( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config) -> AllNodes = [NewNode | [FirstNode | _] = Nodes] = ?config(nodes, Config), connect_nodes(Nodes), @@ -1408,7 +1430,11 @@ have_required_feature_flag_in_cluster_and_add_member_with_it_disabled( || Node <- AllNodes], ok. +<<<<<<< HEAD have_required_feature_flag_in_cluster_and_add_member_without_it( +======= +have_soft_required_feature_flag_in_cluster_and_add_member_without_it( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config) -> AllNodes = [NewNode | [FirstNode | _] = Nodes] = ?config(nodes, Config), connect_nodes(Nodes), @@ -1478,6 +1504,101 @@ have_required_feature_flag_in_cluster_and_add_member_without_it( ok = run_on_node( NewNode, fun() -> +<<<<<<< HEAD +======= + ?assertEqual( + ok, + rabbit_feature_flags:sync_feature_flags_with_cluster( + Nodes, false)), + ok + end, []), + + ct:pal("Checking the feature flag state is unchanged"), + _ = [ok = + run_on_node( + Node, + fun() -> + ?assertEqual( + true, + rabbit_feature_flags:is_enabled(FeatureName)), + ok + end, + []) + || Node <- AllNodes], + ok. + +have_hard_required_feature_flag_in_cluster_and_add_member_without_it( + Config) -> + AllNodes = [NewNode | [FirstNode | _] = Nodes] = ?config(nodes, Config), + connect_nodes(Nodes), + override_running_nodes([NewNode]), + override_running_nodes(Nodes), + + FeatureName = ?FUNCTION_NAME, + FeatureFlags = #{FeatureName => + #{provided_by => rabbit, + stability => stable}}, + RequiredFeatureFlags = #{FeatureName => + #{provided_by => rabbit, + stability => required, + require_level => hard}}, + ?assertEqual(ok, inject_on_nodes([NewNode], FeatureFlags)), + ?assertEqual(ok, inject_on_nodes(Nodes, RequiredFeatureFlags)), + + ct:pal( + "Checking the feature flag is supported and enabled on existing the " + "cluster only"), + ok = run_on_node( + NewNode, + fun() -> + ?assert(rabbit_feature_flags:is_supported(FeatureName)), + ?assertNot(rabbit_feature_flags:is_enabled(FeatureName)), + + DBDir = rabbit_db:dir(), + ok = filelib:ensure_path(DBDir), + SomeFile = filename:join(DBDir, "some-file.db"), + ok = file:write_file(SomeFile, <<>>), + ?assertNot(rabbit_db:is_virgin_node()), + ok + end, + []), + _ = [ok = + run_on_node( + Node, + fun() -> + ?assert(rabbit_feature_flags:is_supported(FeatureName)), + ?assert(rabbit_feature_flags:is_enabled(FeatureName)), + ok + end, + []) + || Node <- Nodes], + + %% Check compatibility between NewNodes and Nodes. + ok = run_on_node( + NewNode, + fun() -> + ?assertEqual( + ok, + rabbit_feature_flags:check_node_compatibility( + FirstNode)), + ok + end, []), + + %% Add node to cluster and synchronize feature flags. + connect_nodes(AllNodes), + override_running_nodes(AllNodes), + ct:pal( + "Synchronizing feature flags in the expanded cluster~n" + "~n" + "NOTE: Error messages about crashed migration functions can be " + "ignored for feature~n" + " flags other than `~ts`~n" + " because they assume they run inside RabbitMQ.", + [FeatureName]), + ok = run_on_node( + NewNode, + fun() -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertMatch( {error, {exception, @@ -1506,6 +1627,56 @@ have_required_feature_flag_in_cluster_and_add_member_without_it( || Node <- AllNodes], ok. +<<<<<<< HEAD +======= +have_unknown_feature_flag_in_cluster_and_add_member_with_it_enabled( + Config) -> + [NewNode | [FirstNode | _] = Nodes] = ?config(nodes, Config), + connect_nodes(Nodes), + override_running_nodes([NewNode]), + override_running_nodes(Nodes), + + FeatureName = ?FUNCTION_NAME, + FeatureFlags = #{FeatureName => + #{provided_by => rabbit, + stability => stable}}, + ?assertEqual(ok, inject_on_nodes([NewNode], FeatureFlags)), + + ct:pal( + "Checking the feature flag is unsupported on the cluster but enabled on " + "the standalone node"), + ok = run_on_node( + NewNode, + fun() -> + ?assertEqual(ok, rabbit_feature_flags:enable(FeatureName)), + ?assert(rabbit_feature_flags:is_enabled(FeatureName)), + ok + end, + []), + _ = [ok = + run_on_node( + Node, + fun() -> + ?assertNot(rabbit_feature_flags:is_supported(FeatureName)), + ?assertNot(rabbit_feature_flags:is_enabled(FeatureName)), + ok + end, + []) + || Node <- Nodes], + + %% Check compatibility between NewNodes and Nodes. + ok = run_on_node( + NewNode, + fun() -> + ?assertEqual( + ok, + rabbit_feature_flags:check_node_compatibility( + FirstNode, true)), + ok + end, []), + ok. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) error_during_migration_after_initial_success(Config) -> AllNodes = [NewNode | [FirstNode | _] = Nodes] = ?config(nodes, Config), connect_nodes(Nodes), diff --git a/deps/rabbit/test/mc_unit_SUITE.erl b/deps/rabbit/test/mc_unit_SUITE.erl index acc9ea69adfe..4a19b006b9a4 100644 --- a/deps/rabbit/test/mc_unit_SUITE.erl +++ b/deps/rabbit/test/mc_unit_SUITE.erl @@ -42,7 +42,13 @@ all_tests() -> amqp_amqpl_message_id_binary, amqp_amqpl_unsupported_values_not_converted, amqp_to_amqpl_data_body, +<<<<<<< HEAD amqp_amqpl_amqp_bodies +======= + amqp_amqpl_amqp_bodies, + amqp_x_headers, + amqpl_x_headers +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. %%%=================================================================== @@ -195,10 +201,14 @@ amqpl_table_x_header_array_of_tbls(_Config) -> [{{symbol, <<"type">>}, {utf8, <<"orange">>}}, {{symbol, <<"count">>}, {long, 45}}]} ]}, +<<<<<<< HEAD mc:x_header(<<"x-fruit">>, Msg)), ok. +======= + mc:x_header(<<"x-fruit">>, Msg)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqpl_death_v1_records(_Config) -> ok = amqpl_death_records(#{?FF_MC_DEATHS_V2 => false}). @@ -314,6 +324,7 @@ amqpl_amqp_bin_amqpl(_Config) -> %% incoming amqpl converted to amqp, serialized / deserialized then converted %% back to amqpl. %% simulates a legacy message published then consumed to a stream +<<<<<<< HEAD Props = #'P_basic'{content_type = <<"text/plain">>, content_encoding = <<"gzip">>, headers = [{<<"a-stream-offset">>, long, 99}, @@ -342,6 +353,39 @@ amqpl_amqp_bin_amqpl(_Config) -> user_id = <<"banana">>, app_id = <<"rmq">> }, +======= + String5k = binary:copy(<<"x">>, 5000), + Props = #'P_basic'{ + content_type = <<"text/plain">>, + content_encoding = <<"gzip">>, + headers = [{<<"a-stream-offset">>, long, 99}, + {<<"a-string">>, longstr, <<"a string">>}, + {<<"a-very-long-string">>, longstr, String5k}, + {<<"a-bool">>, bool, false}, + {<<"a-unsignedbyte">>, unsignedbyte, 1}, + {<<"a-unsignedshort">>, unsignedshort, 1}, + {<<"a-unsignedint">>, unsignedint, 1}, + {<<"a-signedint">>, signedint, 1}, + {<<"a-timestamp">>, timestamp, 1}, + {<<"a-double">>, double, 1.0}, + {<<"a-float">>, float, 1.0}, + {<<"a-void">>, void, undefined}, + {<<"a-binary">>, binary, <<"data">>}, + {<<"a-array">>, array, [{long, 1}, {long, 2}]}, + {<<"x-stream-filter">>, longstr, <<"apple">>} + ], + delivery_mode = 2, + priority = 98, + correlation_id = <<"corr">> , + reply_to = <<"reply-to">>, + expiration = <<"1">>, + message_id = <<"msg-id">>, + timestamp = 99, + type = <<"45">>, + user_id = <<"banana">>, + app_id = <<"rmq">> + }, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Content = #content{properties = Props, payload_fragments_rev = [<<"data">>]}, Msg = mc:init(mc_amqpl, Content, annotations()), @@ -364,8 +408,14 @@ amqpl_amqp_bin_amqpl(_Config) -> Msg10Pre = mc:convert(mc_amqp, Msg), Payload = iolist_to_binary(mc:protocol_state(Msg10Pre)), Msg10 = mc:init(mc_amqp, Payload, #{}), +<<<<<<< HEAD ?assertEqual(<<"exch">>, mc:exchange(Msg10)), ?assertEqual([<<"apple">>], mc:routing_keys(Msg10)), +======= + ?assertMatch(#{<<"x-exchange">> := {utf8, <<"exch">>}, + <<"x-routing-key">> := {utf8, <<"apple">>}}, + mc:x_headers(Msg10)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(98, mc:priority(Msg10)), ?assertEqual(true, mc:is_persistent(Msg10)), ?assertEqual(99000, mc:timestamp(Msg10)), @@ -404,6 +454,12 @@ amqpl_amqp_bin_amqpl(_Config) -> ?assertEqual({long, 99}, Get(<<"a-stream-offset">>, AP10)), ?assertEqual({utf8, <<"a string">>}, Get(<<"a-string">>, AP10)), +<<<<<<< HEAD +======= + %% We expect that a very long string is not scanned for valid UTF-8 + %% and instead directly turned into a binary. + ?assertEqual({binary, String5k}, Get(<<"a-very-long-string">>, AP10)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(false, Get(<<"a-bool">>, AP10)), ?assertEqual({ubyte, 1}, Get(<<"a-unsignedbyte">>, AP10)), ?assertEqual({ushort, 1}, Get(<<"a-unsignedshort">>, AP10)), @@ -422,8 +478,11 @@ amqpl_amqp_bin_amqpl(_Config) -> MsgL2 = mc:convert(mc_amqpl, Msg10), +<<<<<<< HEAD ?assertEqual(<<"exch">>, mc:exchange(MsgL2)), ?assertEqual([<<"apple">>], mc:routing_keys(MsgL2)), +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(98, mc:priority(MsgL2)), ?assertEqual(true, mc:is_persistent(MsgL2)), ?assertEqual(99000, mc:timestamp(MsgL2)), @@ -450,9 +509,23 @@ amqpl_cc_amqp_bin_amqpl(_Config) -> Msg10Pre = mc:convert(mc_amqp, Msg), Sections = iolist_to_binary(mc:protocol_state(Msg10Pre)), Msg10 = mc:init(mc_amqp, Sections, #{}), +<<<<<<< HEAD ?assertEqual(RoutingKeys, mc:routing_keys(Msg10)), MsgL2 = mc:convert(mc_amqpl, Msg10), +======= + ?assertMatch(#{<<"x-exchange">> := {utf8, <<"exch">>}, + <<"x-routing-key">> := {utf8, <<"apple">>}, + <<"x-cc">> := {list, [{utf8, <<"q1">>}, + {utf8, <<"q2">>}]}}, + mc:x_headers(Msg10)), + + %% Here, we simulate what rabbit_stream_queue does: + Msg10b = mc:set_annotation(?ANN_EXCHANGE, <<"exch">>, Msg10), + Msg10c = mc:set_annotation(?ANN_ROUTING_KEYS, [<<"apple">>, <<"q1">>, <<"q2">>], Msg10b), + + MsgL2 = mc:convert(mc_amqpl, Msg10c), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(RoutingKeys, mc:routing_keys(MsgL2)), ?assertMatch(#content{properties = #'P_basic'{headers = Headers}}, mc:protocol_state(MsgL2)). @@ -751,6 +824,55 @@ amqp_amqpl_amqp_bodies(_Config) -> end || Body <- Bodies], ok. +<<<<<<< HEAD +======= +amqp_x_headers(_Config) -> + MAC = [ + {{symbol, <<"x-stream-filter">>}, {utf8, <<"apple">>}}, + thead2('x-list', list, [utf8(<<"l">>)]), + thead2('x-map', map, [{utf8(<<"k">>), utf8(<<"v">>)}]) + ], + M = #'v1_0.message_annotations'{content = MAC}, + AC = [thead(long, 5)], + A = #'v1_0.application_properties'{content = AC}, + D = #'v1_0.data'{content = <<"data">>}, + + Payload = serialize_sections([M, A, D]), + Msg0 = mc:init(mc_amqp, Payload, annotations()), + Msg1 = mc:set_annotation(<<"x-1">>, {byte, -2}, Msg0), + ?assertEqual(#{<<"x-1">> => {byte, -2}, + <<"x-list">> => {list,[{utf8,<<"l">>}]}, + <<"x-map">> => {map,[{{utf8,<<"k">>},{utf8,<<"v">>}}]}, + <<"x-stream-filter">> => {utf8,<<"apple">>}}, + mc:x_headers(Msg1)). + +amqpl_x_headers(_Config) -> + Props = #'P_basic'{headers = [{<<"a-string">>, longstr, <<"a string">>}, + {<<"x-1">>, binary, <<"v1">>}, + {<<"x-stream-filter">>, longstr, <<"apple">>}]}, + Payload = [<<"data">>], + Content = #content{properties = Props, + payload_fragments_rev = Payload}, + + Msg0 = mc:init(mc_amqpl, Content, annotations()), + Msg1 = mc:set_annotation(delivery_count, 1, Msg0), + Msg = mc:set_annotation(<<"x-delivery-count">>, 2, Msg1), + ?assertEqual(#{<<"x-1">> => {binary, <<"v1">>}, + <<"x-stream-filter">> => {utf8,<<"apple">>}, + <<"x-delivery-count">> => {long, 2}}, + mc:x_headers(Msg)), + + XName = <<"exch">>, + RoutingKey = <<"apple">>, + {ok, BasicMsg0} = rabbit_basic:message_no_id(XName, RoutingKey, Content), + BasicMsg1 = mc:set_annotation(delivery_count, 1, BasicMsg0), + BasicMsg = mc:set_annotation(<<"x-delivery-count">>, 2, BasicMsg1), + ?assertEqual(#{<<"x-1">> => {binary, <<"v1">>}, + <<"x-stream-filter">> => {utf8,<<"apple">>}, + <<"x-delivery-count">> => {long, 2}}, + mc:x_headers(BasicMsg)). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Utility amqp10_encode_bin(L) when is_list(L) -> diff --git a/deps/rabbit/test/msg_size_metrics_SUITE.erl b/deps/rabbit/test/msg_size_metrics_SUITE.erl new file mode 100644 index 000000000000..0b33ecf1a36b --- /dev/null +++ b/deps/rabbit/test/msg_size_metrics_SUITE.erl @@ -0,0 +1,154 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(msg_size_metrics_SUITE). + +-compile([export_all, nowarn_export_all]). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("amqp_client/include/amqp_client.hrl"). + +-import(rabbit_ct_broker_helpers, + [rpc/4]). + +all() -> + [ + {group, tests} + ]. + +groups() -> + [ + {tests, [shuffle], + [message_size, + over_max_message_size]} + ]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + {ok, _} = application:ensure_all_started(amqp10_client), + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config). + +end_per_suite(Config) -> + rabbit_ct_helpers:run_teardown_steps(Config). + +init_per_group(_Group, Config) -> + rabbit_ct_helpers:run_steps( + Config, + rabbit_ct_broker_helpers:setup_steps() ++ + rabbit_ct_client_helpers:setup_steps()). + +end_per_group(_Group, Config) -> + rabbit_ct_helpers:run_steps( + Config, + rabbit_ct_client_helpers:teardown_steps() ++ + rabbit_ct_broker_helpers:teardown_steps()). + +init_per_testcase(Testcase, Config) -> + rabbit_ct_helpers:testcase_started(Config, Testcase). + +end_per_testcase(Testcase, Config) -> + rabbit_ct_helpers:testcase_finished(Config, Testcase). + +%% ------------------------------------------------------------------- +%% Test cases +%% ------------------------------------------------------------------- + +message_size(Config) -> + AmqplBefore = get_msg_size_metrics(amqp091, Config), + AmqpBefore = get_msg_size_metrics(amqp10, Config), + + Binary2B = <<"12">>, + Binary200K = binary:copy(<<"x">>, 200_000), + Payloads = [Binary2B, Binary200K, Binary2B], + + {AmqplConn, Ch} = rabbit_ct_client_helpers:open_connection_and_channel(Config), + [amqp_channel:call(Ch, + #'basic.publish'{routing_key = <<"nowhere">>}, + #amqp_msg{payload = Payload}) + || Payload <- Payloads], + + OpnConf = connection_config(Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + Address = rabbitmq_amqp_address:exchange(<<"amq.fanout">>), + {ok, Sender} = amqp10_client:attach_sender_link_sync(Session, <<"sender">>, Address), + receive {amqp10_event, {link, Sender, credited}} -> ok + after 5000 -> ct:fail(credited_timeout) + end, + + ok = amqp10_client:send_msg(Sender, amqp10_msg:new(<<"tag1">>, Binary2B)), + ok = amqp10_client:send_msg(Sender, amqp10_msg:new(<<"tag2">>, Binary200K)), + ok = amqp10_client:send_msg(Sender, amqp10_msg:new(<<"tag3">>, Binary2B)), + + ok = wait_for_settlement(released, <<"tag1">>), + ok = wait_for_settlement(released, <<"tag2">>), + ok = wait_for_settlement(released, <<"tag3">>), + + AmqplAfter = get_msg_size_metrics(amqp091, Config), + AmqpAfter = get_msg_size_metrics(amqp10, Config), + + ExpectedDiff = [{100, 2}, + {1_000_000, 1}], + ?assertEqual(ExpectedDiff, + rabbit_msg_size_metrics:diff_raw_buckets(AmqplAfter, AmqplBefore)), + ?assertEqual(ExpectedDiff, + rabbit_msg_size_metrics:diff_raw_buckets(AmqpAfter, AmqpBefore)), + + ok = amqp10_client:close_connection(Connection), + ok = rabbit_ct_client_helpers:close_connection_and_channel(AmqplConn, Ch). + +over_max_message_size(Config) -> + DefaultMaxMessageSize = rpc(Config, persistent_term, get, [max_message_size]), + %% Limit the server to only accept messages up to 2KB. + MaxMessageSize = 2_000, + ok = rpc(Config, persistent_term, put, [max_message_size, MaxMessageSize]), + + Before = get_msg_size_metrics(amqp091, Config), + {Conn, Ch} = rabbit_ct_client_helpers:open_connection_and_channel(Config, 0), + MonitorRef = erlang:monitor(process, Ch), + MessageTooLarge = binary:copy(<<"x">>, MaxMessageSize + 1), + amqp_channel:call(Ch, + #'basic.publish'{routing_key = <<"none">>}, + #amqp_msg{payload = MessageTooLarge}), + receive {'DOWN', MonitorRef, process, Ch, Info} -> + ?assertEqual({shutdown, + {server_initiated_close, + 406, + <<"PRECONDITION_FAILED - message size 2001 is larger than configured max size 2000">>}}, + Info) + after 2000 -> ct:fail(expected_channel_closed) + end, + + After = get_msg_size_metrics(amqp091, Config), + %% No metrics should be increased if client sent message that is too large. + ?assertEqual(Before, After), + + ok = rabbit_ct_client_helpers:close_connection(Conn), + ok = rpc(Config, persistent_term, put, [max_message_size, DefaultMaxMessageSize]). + +get_msg_size_metrics(Protocol, Config) -> + rpc(Config, rabbit_msg_size_metrics, raw_buckets, [Protocol]). + +connection_config(Config) -> + Host = ?config(rmq_hostname, Config), + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + #{address => Host, + port => Port, + container_id => <<"my container">>, + sasl => anon}. + +wait_for_settlement(State, Tag) -> + receive + {amqp10_disposition, {State, Tag}} -> + ok + after 5000 -> + ct:fail({disposition_timeout, Tag}) + end. diff --git a/deps/rabbit/test/quorum_queue_SUITE.erl b/deps/rabbit/test/quorum_queue_SUITE.erl index dd3f2f50ce0d..271bb79f19c7 100644 --- a/deps/rabbit/test/quorum_queue_SUITE.erl +++ b/deps/rabbit/test/quorum_queue_SUITE.erl @@ -322,8 +322,11 @@ init_per_testcase(Testcase, Config) -> {skip, "reclaim_memory_with_wrong_queue_type isn't mixed versions compatible"}; peek_with_wrong_queue_type when IsMixed -> {skip, "peek_with_wrong_queue_type isn't mixed versions compatible"}; +<<<<<<< HEAD subscribe_redelivery_limit_disable when IsMixed -> {skip, "subscribe_redelivery_limit_disable isn't mixed versions compatible"}; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> Config1 = rabbit_ct_helpers:testcase_started(Config, Testcase), rabbit_ct_broker_helpers:rpc(Config, 0, ?MODULE, delete_queues, []), @@ -1471,14 +1474,22 @@ gh_12635(Config) -> rabbit_ct_broker_helpers:get_node_configs(Config, nodename), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, +<<<<<<< HEAD [rabbit, quorum_min_checkpoint_interval, 1]), +======= + [rabbit, quorum_min_checkpoint_interval, 1]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Ch0 = rabbit_ct_client_helpers:open_channel(Config, Server0), #'confirm.select_ok'{} = amqp_channel:call(Ch0, #'confirm.select'{}), QQ = ?config(queue_name, Config), RaName = ra_name(QQ), ?assertEqual({'queue.declare_ok', QQ, 0, 0}, +<<<<<<< HEAD declare(Ch0, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])), +======= + declare(Ch0, QQ, [{<<"x-queue-type">>, longstr, <<"quorum">>}])), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% stop member to simulate slow or down member ok = rpc:call(Server2, ra, stop_server, [quorum_queues, {RaName, Server2}]), @@ -1489,10 +1500,17 @@ gh_12635(Config) -> %% force a checkpoint on leader ok = rpc:call(Server0, ra, cast_aux_command, [{RaName, Server0}, force_checkpoint]), rabbit_ct_helpers:await_condition( +<<<<<<< HEAD fun () -> {ok, #{log := Log}, _} = rpc:call(Server0, ra, member_overview, [{RaName, Server0}]), undefined =/= maps:get(latest_checkpoint_index, Log) end), +======= + fun () -> + {ok, #{log := Log}, _} = rpc:call(Server0, ra, member_overview, [{RaName, Server0}]), + undefined =/= maps:get(latest_checkpoint_index, Log) + end), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% publish 1 more message publish_confirm(Ch0, QQ), @@ -1508,10 +1526,17 @@ gh_12635(Config) -> #'queue.purge_ok'{} = amqp_channel:call(Ch0, #'queue.purge'{queue = QQ}), rabbit_ct_helpers:await_condition( +<<<<<<< HEAD fun () -> {ok, #{log := Log}, _} = rpc:call(Server0, ra, member_overview, [{RaName, Server0}]), undefined =/= maps:get(snapshot_index, Log) end), +======= + fun () -> + {ok, #{log := Log}, _} = rpc:call(Server0, ra, member_overview, [{RaName, Server0}]), + undefined =/= maps:get(snapshot_index, Log) + end), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% restart the down member ok = rpc:call(Server2, ra, restart_server, [quorum_queues, {RaName, Server2}]), Pid2 = rpc:call(Server2, erlang, whereis, [RaName]), @@ -1521,12 +1546,19 @@ gh_12635(Config) -> {'DOWN',Ref, process,_, _} -> ct:fail("unexpected DOWN") after 500 -> +<<<<<<< HEAD ok +======= + ok +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, flush(1), ok. +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) priority_queue_fifo(Config) -> %% testing: if hi priority messages are published before lo priority %% messages they are always consumed first (fifo) diff --git a/deps/rabbit/test/rabbit_db_binding_SUITE.erl b/deps/rabbit/test/rabbit_db_binding_SUITE.erl index 9055e4ff1ddb..d74761c70514 100644 --- a/deps/rabbit/test/rabbit_db_binding_SUITE.erl +++ b/deps/rabbit/test/rabbit_db_binding_SUITE.erl @@ -131,8 +131,13 @@ delete1(_Config) -> Ret = rabbit_db_binding:delete(Binding, fun(_, _) -> ok end), ?assertMatch({ok, _}, Ret), {ok, Deletions} = Ret, +<<<<<<< HEAD ?assertMatch({#exchange{}, not_deleted, [#binding{}], none}, dict:fetch(XName1, Deletions)), +======= + ?assertMatch({#exchange{}, not_deleted, [#binding{}]}, + rabbit_binding:fetch_deletion(XName1, Deletions)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(false, rabbit_db_binding:exists(Binding)), passed. @@ -152,8 +157,13 @@ auto_delete1(_Config) -> Ret = rabbit_db_binding:delete(Binding, fun(_, _) -> ok end), ?assertMatch({ok, _}, Ret), {ok, Deletions} = Ret, +<<<<<<< HEAD ?assertMatch({#exchange{}, deleted, [#binding{}], none}, dict:fetch(XName1, Deletions)), +======= + ?assertMatch({#exchange{}, not_deleted, [#binding{}]}, + rabbit_binding:fetch_deletion(XName1, Deletions)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(false, rabbit_db_binding:exists(Binding)), passed. diff --git a/deps/rabbit/test/rabbit_db_queue_SUITE.erl b/deps/rabbit/test/rabbit_db_queue_SUITE.erl index f66e8fd236c9..02ab7181d856 100644 --- a/deps/rabbit/test/rabbit_db_queue_SUITE.erl +++ b/deps/rabbit/test/rabbit_db_queue_SUITE.erl @@ -292,8 +292,13 @@ delete1(_Config) -> ?assertEqual({ok, Q}, rabbit_db_queue:get(QName)), %% TODO Can we handle the deletions outside of rabbit_db_queue? Probably not because %% they should be done in a single transaction, but what a horrid API to have! +<<<<<<< HEAD Dict = rabbit_db_queue:delete(QName, normal), ?assertEqual(0, dict:size(Dict)), +======= + Deletions = rabbit_db_queue:delete(QName, normal), + ?assertEqual(rabbit_binding:new_deletions(), Deletions), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(ok, rabbit_db_queue:delete(QName, normal)), ?assertEqual({error, not_found}, rabbit_db_queue:get(QName)), passed. diff --git a/deps/rabbit/test/topic_permission_SUITE.erl b/deps/rabbit/test/topic_permission_SUITE.erl index 2849b76fd3b9..bc0b30eaabcf 100644 --- a/deps/rabbit/test/topic_permission_SUITE.erl +++ b/deps/rabbit/test/topic_permission_SUITE.erl @@ -8,6 +8,10 @@ -module(topic_permission_SUITE). -include_lib("eunit/include/eunit.hrl"). +<<<<<<< HEAD +======= +-include_lib("amqp10_common/include/amqp10_framing.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("amqp_client/include/amqp_client.hrl"). -compile([export_all, nowarn_export_all]). @@ -21,6 +25,10 @@ groups() -> [ {sequential_tests, [], [ +<<<<<<< HEAD +======= + amqp_x_cc_annotation, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqpl_cc_headers, amqpl_bcc_headers, topic_permission_database_access, @@ -29,6 +37,10 @@ groups() -> ]. init_per_suite(Config) -> +<<<<<<< HEAD +======= + {ok, _} = application:ensure_all_started(amqp10_client), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_helpers:log_environment(), Config1 = rabbit_ct_helpers:set_config( Config, @@ -56,6 +68,94 @@ init_per_testcase(Testcase, Config) -> end_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_finished(Config, Testcase). +<<<<<<< HEAD +======= +amqp_x_cc_annotation(Config) -> + ok = set_topic_permissions(Config, "^a", ".*"), + + QName1 = <<"queue 1">>, + QName2 = <<"queue 2">>, + {Connection, Session1, LinkPair} = amqp_utils:init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName1, #{}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName2, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName1, <<"amq.topic">>, <<"a.1">>, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName2, <<"amq.topic">>, <<"a.2">>, #{}), + + {ok, Sender1} = amqp10_client:attach_sender_link( + Session1, + <<"sender 1">>, + rabbitmq_amqp_address:exchange(<<"amq.topic">>, <<"a.1">>)), + ok = amqp_utils:wait_for_credit(Sender1), + {ok, Receiver1} = amqp10_client:attach_receiver_link( + Session1, <<"receiver 1">>, rabbitmq_amqp_address:queue(QName1), settled), + {ok, Receiver2} = amqp10_client:attach_receiver_link( + Session1, <<"receiver 2">>, rabbitmq_amqp_address:queue(QName2), settled), + %% We have permissions to send to both topics. + %% Therefore, m1 should be sent to both queues. + ok = amqp10_client:send_msg(Sender1, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"a.2">>}]}}, + amqp10_msg:new(<<"t1">>, <<"m1">>, true))), + {ok, Msg1} = amqp10_client:get_msg(Receiver1), + {ok, Msg2} = amqp10_client:get_msg(Receiver2), + ?assertEqual([<<"m1">>], amqp10_msg:body(Msg1)), + ?assertEqual([<<"m1">>], amqp10_msg:body(Msg2)), + ok = amqp_utils:detach_link_sync(Sender1), + ok = amqp_utils:detach_link_sync(Receiver1), + ok = amqp_utils:detach_link_sync(Receiver2), + + {ok, Session2} = amqp10_client:begin_session_sync(Connection), + {ok, Sender2} = amqp10_client:attach_sender_link( + Session2, + <<"sender 2">>, + rabbitmq_amqp_address:exchange(<<"amq.topic">>, <<"x.1">>)), + ok = amqp_utils:wait_for_credit(Sender2), + ok = amqp10_client:send_msg(Sender2, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"a.2">>}]}}, + amqp10_msg:new(<<"t2">>, <<"m2">>, true))), + receive + {amqp10_event, + {session, Session2, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, Description1}}}}} -> + ?assertEqual( + <<"write access to topic 'x.1' in exchange 'amq.topic' in vhost '/' refused for user 'guest'">>, + Description1) + after 5000 -> amqp_utils:flush(missing_ended), + ct:fail({missing_event, ?LINE}) + end, + + {ok, Session3} = amqp10_client:begin_session_sync(Connection), + {ok, Sender3} = amqp10_client:attach_sender_link( + Session3, + <<"sender 3">>, + rabbitmq_amqp_address:exchange(<<"amq.topic">>, <<"a.1">>)), + ok = amqp_utils:wait_for_credit(Sender3), + ok = amqp10_client:send_msg(Sender3, amqp10_msg:set_message_annotations( + #{<<"x-cc">> => {list, [{utf8, <<"x.2">>}]}}, + amqp10_msg:new(<<"t3">>, <<"m3">>, true))), + receive + {amqp10_event, + {session, Session3, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, Description2}}}}} -> + ?assertEqual( + <<"write access to topic 'x.2' in exchange 'amq.topic' in vhost '/' refused for user 'guest'">>, + Description2) + after 5000 -> amqp_utils:flush(missing_ended), + ct:fail({missing_event, ?LINE}) + end, + + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName1), + {ok, #{message_count := 0}} = rabbitmq_amqp_client:delete_queue(LinkPair, QName2), + ok = amqp_utils:end_session_sync(Session1), + ok = amqp10_client:close_connection(Connection), + ok = clear_topic_permissions(Config). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqpl_cc_headers(Config) -> amqpl_headers(<<"CC">>, Config). diff --git a/deps/rabbit/test/unit_msg_size_metrics_SUITE.erl b/deps/rabbit/test/unit_msg_size_metrics_SUITE.erl new file mode 100644 index 000000000000..cd496932cd92 --- /dev/null +++ b/deps/rabbit/test/unit_msg_size_metrics_SUITE.erl @@ -0,0 +1,64 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(unit_msg_size_metrics_SUITE). + +-include_lib("stdlib/include/assert.hrl"). + +-compile([nowarn_export_all, export_all]). + +all() -> + [ + {group, tests} + ]. + +groups() -> + [ + {tests, [], + [ + prometheus_format + ]} + ]. + +%% ------------------------------------------------------------------- +%% Testsuite setup/teardown. +%% ------------------------------------------------------------------- + +init_per_suite(Config) -> + ok = rabbit_msg_size_metrics:init(fake_protocol), + Config. + +end_per_suite(Config) -> + ok = rabbit_msg_size_metrics:cleanup(fake_protocol), + Config. + +%% ------------------------------------------------------------------- +%% Testcases. +%% ------------------------------------------------------------------- + +prometheus_format(_Config) -> + MsgSizes = [1, 100, 1_000_000_000, 99_000_000, 15_000, 15_000], + [ok = rabbit_msg_size_metrics:observe(fake_protocol, MsgSize) || MsgSize <- MsgSizes], + + ?assertEqual( + #{message_size_bytes => + #{type => histogram, + help => "Size of messages received from publishers", + values => [{ + [{protocol, fake_protocol}], + [{100, 2}, + {1_000, 2}, + {10_000, 2}, + {100_000, 4}, + {1_000_000, 4}, + {10_000_000, 4}, + {50_000_000, 4}, + {100_000_000, 5}, + {infinity, 6}], + length(MsgSizes), + lists:sum(MsgSizes)}]}}, + rabbit_msg_size_metrics:prometheus_format()). diff --git a/deps/rabbit_common/mk/rabbitmq-early-plugin.mk b/deps/rabbit_common/mk/rabbitmq-early-plugin.mk index 1b8aaa3f422a..b61eb387a106 100644 --- a/deps/rabbit_common/mk/rabbitmq-early-plugin.mk +++ b/deps/rabbit_common/mk/rabbitmq-early-plugin.mk @@ -4,7 +4,11 @@ DIALYZER_OPTS ?= -Werror_handling -Wunmatched_returns -Wunknown +<<<<<<< HEAD dialyze: ERL_LIBS = $(APPS_DIR):$(DEPS_DIR):$(DEPS_DIR)/rabbitmq_cli/_build/dev/lib:$(dir $(shell elixir --eval ":io.format '~s~n', [:code.lib_dir :elixir ]")) +======= +dialyze: ERL_LIBS = $(APPS_DIR):$(DEPS_DIR):$(DEPS_DIR)/rabbitmq_cli/_build/dev/lib:$(dir $(shell elixir --eval ':io.format "~s~n", [:code.lib_dir :elixir ]')) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) # -------------------------------------------------------------------- # Common Test flags. diff --git a/deps/rabbit_common/src/rabbit_core_metrics.erl b/deps/rabbit_common/src/rabbit_core_metrics.erl index 8b5430076f53..b16de27e0ee8 100644 --- a/deps/rabbit_common/src/rabbit_core_metrics.erl +++ b/deps/rabbit_common/src/rabbit_core_metrics.erl @@ -141,9 +141,15 @@ connection_stats(Pid, Infos) -> ets:insert(connection_metrics, {Pid, Infos}), ok. +<<<<<<< HEAD connection_stats(Pid, Recv_oct, Send_oct, Reductions) -> %% Includes delete marker ets:insert(connection_coarse_metrics, {Pid, Recv_oct, Send_oct, Reductions, 0}), +======= +connection_stats(Pid, RecvOct, SendOct, Reductions) -> + %% Includes delete marker + ets:insert(connection_coarse_metrics, {Pid, RecvOct, SendOct, Reductions, 0}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok. channel_created(Pid, Infos) -> diff --git a/deps/rabbit_common/src/rabbit_env.erl b/deps/rabbit_common/src/rabbit_env.erl index 4f222ab707f4..bd6f9da28440 100644 --- a/deps/rabbit_common/src/rabbit_env.erl +++ b/deps/rabbit_common/src/rabbit_env.erl @@ -65,7 +65,10 @@ "RABBITMQ_KEEP_PID_FILE_ON_EXIT", "RABBITMQ_LOG", "RABBITMQ_LOG_BASE", +<<<<<<< HEAD "RABBITMQ_LOG_FF_REGISTRY", +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "RABBITMQ_LOGS", "RABBITMQ_MNESIA_BASE", "RABBITMQ_MNESIA_DIR", @@ -150,7 +153,10 @@ get_context_after_reloading_env(Context) -> fun keep_pid_file_on_exit/1, fun feature_flags_file/1, fun forced_feature_flags_on_init/1, +<<<<<<< HEAD fun log_feature_flags_registry/1, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) fun plugins_path/1, fun plugins_expand_dir/1, fun enabled_plugins_file/1, @@ -999,6 +1005,7 @@ forced_feature_flags_on_init(Context) -> case Value of false -> %% get_prefixed_env_var() considers an empty string +<<<<<<< HEAD %% is the same as an undefined environment variable. update_context(Context, forced_feature_flags_on_init, undefined, default); @@ -1017,6 +1024,17 @@ log_feature_flags_registry(Context) -> Log = value_is_yes(Value), update_context(Context, log_feature_flags_registry, Log, environment) +======= + %% as an undefined environment variable. + update_context( + Context, + forced_feature_flags_on_init, undefined, default); + _ -> + FeatureNames = string:lexemes(Value, ","), + update_context( + Context, + forced_feature_flags_on_init, FeatureNames, environment) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. %% ------------------------------------------------------------------- diff --git a/deps/rabbit_common/src/rabbit_event.erl b/deps/rabbit_common/src/rabbit_event.erl index ac584ed0819f..ed2715b1eb3a 100644 --- a/deps/rabbit_common/src/rabbit_event.erl +++ b/deps/rabbit_common/src/rabbit_event.erl @@ -10,7 +10,11 @@ -include("rabbit.hrl"). -export([start_link/0]). +<<<<<<< HEAD -export([init_stats_timer/2, init_disabled_stats_timer/2, +======= +-export([init_stats_timer/0, init_stats_timer/2, init_disabled_stats_timer/2, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ensure_stats_timer/3, stop_stats_timer/2, reset_stats_timer/2]). -export([stats_level/2, if_enabled/3]). -export([notify/2, notify/3, notify_if/3]). @@ -89,6 +93,7 @@ start_link() -> %% Nowadays, instead of sending a message to rabbit_event via notify(stats), %% some stat-emitting objects update ETS tables directly via module rabbit_core_metrics. +<<<<<<< HEAD init_stats_timer(C, P) -> %% If the rabbit app is not loaded - use default none:5000 StatsLevel = application:get_env(rabbit, collect_statistics, none), @@ -106,6 +111,36 @@ ensure_stats_timer(C, P, Msg) -> TRef = erlang:send_after(Interval, self(), Msg), setelement(P, C, State#state{timer = TRef}); #state{} -> +======= +-spec init_stats_timer() -> state(). +init_stats_timer() -> + %% If the rabbit app is not loaded - use default none:5000 + StatsLevel = application:get_env(rabbit, collect_statistics, none), + Interval = application:get_env(rabbit, collect_statistics_interval, 5000), + #state{level = StatsLevel, + interval = Interval, + timer = undefined}. + +init_stats_timer(C, P) -> + State = init_stats_timer(), + setelement(P, C, State). + +init_disabled_stats_timer(C, P) -> + State = #state{level = none, + interval = 0, + timer = undefined}, + setelement(P, C, State). + +ensure_stats_timer(C, P, Msg) -> + case element(P, C) of + #state{level = Level, + interval = Interval, + timer = undefined} = State + when Level =/= none -> + TRef = erlang:send_after(Interval, self(), Msg), + setelement(P, C, State#state{timer = TRef}); + _State -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) C end. @@ -156,5 +191,9 @@ event_cons(Type, Props, Ref) -> #event{type = Type, props = Props, reference = Ref, +<<<<<<< HEAD timestamp = os:system_time(milli_seconds)}. +======= + timestamp = os:system_time(millisecond)}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbit_common/src/rabbit_ssl_options.erl b/deps/rabbit_common/src/rabbit_ssl_options.erl index ee0d1b4a3260..a51dde72e734 100644 --- a/deps/rabbit_common/src/rabbit_ssl_options.erl +++ b/deps/rabbit_common/src/rabbit_ssl_options.erl @@ -8,6 +8,10 @@ -module(rabbit_ssl_options). -export([fix/1]). +<<<<<<< HEAD +======= +-export([fix_client/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -define(BAD_SSL_PROTOCOL_VERSIONS, [ @@ -22,6 +26,30 @@ fix(Config) -> fix_ssl_protocol_versions( hibernate_after(Config))). +<<<<<<< HEAD +======= +-spec fix_client(rabbit_types:infos()) -> rabbit_types:infos(). +fix_client(Config) -> + fix_cacerts( + fix(Config)). + +fix_cacerts(SslOptsConfig) -> + CACerts = proplists:get_value(cacerts, SslOptsConfig, undefined), + CACertfile = proplists:get_value(cacertfile, SslOptsConfig, undefined), + case {CACerts, CACertfile} of + {undefined, undefined} -> + try public_key:cacerts_get() of + CaCerts -> + [{cacerts, CaCerts} | SslOptsConfig] + catch + _ -> + SslOptsConfig + end; + _CaCerts -> + SslOptsConfig + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) fix_verify_fun(SslOptsConfig) -> %% Starting with ssl 4.0.1 in Erlang R14B, the verify_fun function %% takes 3 arguments and returns a tuple. diff --git a/deps/rabbit_common/test/rabbit_env_SUITE.erl b/deps/rabbit_common/test/rabbit_env_SUITE.erl index 0961a37a1855..8681ee208742 100644 --- a/deps/rabbit_common/test/rabbit_env_SUITE.erl +++ b/deps/rabbit_common/test/rabbit_env_SUITE.erl @@ -187,7 +187,10 @@ check_default_values(_) -> interactive_shell => default, keep_pid_file_on_exit => default, log_base_dir => default, +<<<<<<< HEAD log_feature_flags_registry => default, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => default, main_config_file => default, main_log_file => default, @@ -231,7 +234,10 @@ check_default_values(_) -> interactive_shell => false, keep_pid_file_on_exit => false, log_base_dir => "/var/log/rabbitmq", +<<<<<<< HEAD log_feature_flags_registry => false, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => undefined, main_config_file => "/etc/rabbitmq/rabbitmq", main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log", @@ -282,7 +288,10 @@ check_default_values(_) -> interactive_shell => false, keep_pid_file_on_exit => false, log_base_dir => "%APPDATA%/RabbitMQ/log", +<<<<<<< HEAD log_feature_flags_registry => false, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => undefined, main_config_file => "%APPDATA%/RabbitMQ/rabbitmq", main_log_file => "%APPDATA%/RabbitMQ/log/" ++ NodeS ++ ".log", @@ -408,7 +417,10 @@ check_values_from_reachable_remote_node(Config) -> interactive_shell => default, keep_pid_file_on_exit => default, log_base_dir => default, +<<<<<<< HEAD log_feature_flags_registry => default, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => default, main_config_file => default, main_log_file => default, @@ -452,7 +464,10 @@ check_values_from_reachable_remote_node(Config) -> interactive_shell => false, keep_pid_file_on_exit => false, log_base_dir => "/var/log/rabbitmq", +<<<<<<< HEAD log_feature_flags_registry => false, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => undefined, main_config_file => "/etc/rabbitmq/rabbitmq", main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log", @@ -540,7 +555,10 @@ check_values_from_offline_remote_node(_) -> interactive_shell => default, keep_pid_file_on_exit => default, log_base_dir => default, +<<<<<<< HEAD log_feature_flags_registry => default, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => default, main_config_file => default, main_log_file => default, @@ -584,7 +602,10 @@ check_values_from_offline_remote_node(_) -> interactive_shell => false, keep_pid_file_on_exit => false, log_base_dir => "/var/log/rabbitmq", +<<<<<<< HEAD log_feature_flags_registry => false, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log_levels => undefined, main_config_file => "/etc/rabbitmq/rabbitmq", main_log_file => "/var/log/rabbitmq/" ++ NodeS ++ ".log", diff --git a/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl b/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl index ce38b0241d10..776126750610 100644 --- a/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl +++ b/deps/rabbitmq_amqp_client/src/rabbitmq_amqp_client.erl @@ -28,7 +28,13 @@ declare_exchange/3, bind_exchange/5, unbind_exchange/5, +<<<<<<< HEAD delete_exchange/2 +======= + delete_exchange/2, + + set_token/2 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. -define(TIMEOUT, 20_000). @@ -97,6 +103,11 @@ await_attached(Ref) -> receive {amqp10_event, {link, Ref, attached}} -> ok; +<<<<<<< HEAD +======= + {amqp10_event, {link, Ref, {attached, #'v1_0.attach'{}}}} -> + ok; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {amqp10_event, {link, Ref, {detached, Err}}} -> {error, Err} after ?TIMEOUT -> @@ -129,6 +140,11 @@ await_detached(Ref) -> receive {amqp10_event, {link, Ref, {detached, normal}}} -> ok; +<<<<<<< HEAD +======= + {amqp10_event, {link, Ref, {detached, #'v1_0.detach'{}}}} -> + ok; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {amqp10_event, {link, Ref, {detached, Err}}} -> {error, Err} after ?TIMEOUT -> @@ -377,6 +393,26 @@ delete_exchange(LinkPair, ExchangeName) -> Err end. +<<<<<<< HEAD +======= +%% Renew OAuth 2.0 token. +-spec set_token(link_pair(), binary()) -> + ok | {error, term()}. +set_token(LinkPair, Token) -> + Props = #{subject => <<"PUT">>, + to => <<"/auth/tokens">>}, + Body = {binary, Token}, + case request(LinkPair, Props, Body) of + {ok, Resp} -> + case is_success(Resp) of + true -> ok; + false -> {error, Resp} + end; + Err -> + Err + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec request(link_pair(), amqp10_msg:amqp10_properties(), amqp10_prim()) -> {ok, Response :: amqp10_msg:amqp10_msg()} | {error, term()}. request(#link_pair{session = Session, diff --git a/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot/pom.xml b/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot/pom.xml index b17460d8adef..a4239d2c6c3c 100644 --- a/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot/pom.xml +++ b/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot/pom.xml @@ -29,13 +29,21 @@ org.springframework.boot spring-boot-starter-parent +<<<<<<< HEAD 3.3.2 +======= + 3.4.0 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) 17 17 +<<<<<<< HEAD 5.10.3 +======= + 5.11.3 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) com.rabbitmq.examples diff --git a/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot_kotlin/pom.xml b/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot_kotlin/pom.xml index f002a7f09f4b..efdf977c8c38 100644 --- a/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot_kotlin/pom.xml +++ b/deps/rabbitmq_auth_backend_http/examples/rabbitmq_auth_backend_spring_boot_kotlin/pom.xml @@ -14,7 +14,11 @@ org.springframework.boot spring-boot-starter-parent +<<<<<<< HEAD 3.3.2 +======= + 3.4.0 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) @@ -23,7 +27,11 @@ UTF-8 17 17 +<<<<<<< HEAD 2.0.0 +======= + 2.1.0 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) 5.10.0 diff --git a/deps/rabbitmq_auth_backend_http/src/rabbit_auth_backend_http.erl b/deps/rabbitmq_auth_backend_http/src/rabbit_auth_backend_http.erl index c61aceeb8983..ab89aca807e9 100644 --- a/deps/rabbitmq_auth_backend_http/src/rabbit_auth_backend_http.erl +++ b/deps/rabbitmq_auth_backend_http/src/rabbit_auth_backend_http.erl @@ -205,7 +205,11 @@ do_http_req(Path0, Query) -> ssl_options() -> case application:get_env(rabbitmq_auth_backend_http, ssl_options) of {ok, Opts0} when is_list(Opts0) -> +<<<<<<< HEAD Opts1 = [{ssl, rabbit_networking:fix_ssl_options(Opts0)}], +======= + Opts1 = [{ssl, rabbit_ssl_options:fix_client(Opts0)}], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case application:get_env(rabbitmq_auth_backend_http, ssl_hostname_verification) of {ok, wildcard} -> rabbit_log:debug("Enabling wildcard-aware hostname verification for HTTP client connections"), diff --git a/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap.erl b/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap.erl index bba6767a3ce4..ad961397c4cc 100644 --- a/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap.erl +++ b/deps/rabbitmq_auth_backend_ldap/src/rabbit_auth_backend_ldap.erl @@ -761,7 +761,11 @@ ssl_conf() -> end. ssl_options() -> +<<<<<<< HEAD Opts0 = rabbit_networking:fix_ssl_options(env(ssl_options)), +======= + Opts0 = rabbit_ssl_options:fix_client(env(ssl_options)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case env(ssl_hostname_verification, undefined) of wildcard -> rabbit_log_ldap:debug("Enabling wildcard-aware hostname verification for LDAP client connections"), diff --git a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel index 71c3d2e46289..ba891c97d1d9 100644 --- a/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel +++ b/deps/rabbitmq_auth_backend_oauth2/BUILD.bazel @@ -113,7 +113,11 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( +<<<<<<< HEAD name = "rabbit_oauth2_config_SUITE", +======= + name = "rabbit_oauth2_provider_SUITE", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) additional_beam = [ "test/oauth2_http_mock.beam", ], @@ -123,6 +127,13 @@ rabbitmq_integration_suite( ) rabbitmq_integration_suite( +<<<<<<< HEAD +======= + name = "rabbit_oauth2_resource_server_SUITE", +) + +rabbitmq_integration_suite( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "jwks_SUITE", additional_beam = [ "test/rabbit_auth_backend_oauth2_test_util.beam", diff --git a/deps/rabbitmq_auth_backend_oauth2/Makefile b/deps/rabbitmq_auth_backend_oauth2/Makefile index 1066e7be8271..1be72a9bce0f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/Makefile +++ b/deps/rabbitmq_auth_backend_oauth2/Makefile @@ -6,7 +6,11 @@ BUILD_WITHOUT_QUIC=1 export BUILD_WITHOUT_QUIC LOCAL_DEPS = inets public_key +<<<<<<< HEAD BUILD_DEPS = rabbit_common +======= +BUILD_DEPS = rabbit_common rabbitmq_cli +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) DEPS = rabbit cowlib jose base64url oauth2_client TEST_DEPS = cowboy rabbitmq_web_dispatch rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client rabbitmq_web_mqtt emqtt rabbitmq_amqp_client diff --git a/deps/rabbitmq_auth_backend_oauth2/README.md b/deps/rabbitmq_auth_backend_oauth2/README.md index 1d72c5af3e0b..115eae955c6f 100644 --- a/deps/rabbitmq_auth_backend_oauth2/README.md +++ b/deps/rabbitmq_auth_backend_oauth2/README.md @@ -149,13 +149,21 @@ In that case, the configuration would look like this: {rabbitmq_auth_backend_oauth2, [ {resource_server_id, <<"my_rabbit_server">>}, {key_config, [ +<<<<<<< HEAD {jwks_url, <<"https://jwt-issuer.my-domain.local/jwks.json">>} +======= + {jwks_uri, <<"https://jwt-issuer.my-domain.local/jwks.json">>} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]}, ]. ``` +<<<<<<< HEAD Note: if both are configured, `jwks_url` takes precedence over `signing_keys`. +======= +Note: if both are configured, `jwks_uri` takes precedence over `signing_keys`. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ### Variables Configurable in rabbitmq.conf @@ -166,7 +174,11 @@ Note: if both are configured, `jwks_url` takes precedence over `signing_keys`. | `auth_oauth2.additional_scopes_key` | Key to fetch additional scopes from (maps to `additional_rabbitmq_scopes` in the `advanced.config` format) | `auth_oauth2.default_key` | ID (name) of the default signing key | `auth_oauth2.signing_keys` | Paths to signing key files +<<<<<<< HEAD | `auth_oauth2.jwks_url` | The URL of key server. According to the [JWT Specification](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.2) key server URL must be https +======= +| `auth_oauth2.jwks_uri` | The URL of key server. According to the [JWT Specification](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.2) key server URL must be https +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) | `auth_oauth2.https.cacertfile` | Path to a file containing PEM-encoded CA certificates. The CA certificates are used during key server [peer verification](https://rabbitmq.com/ssl.html#peer-verification) | `auth_oauth2.https.depth` | The maximum number of non-self-issued intermediate certificates that may follow the peer certificate in a valid [certification path](https://rabbitmq.com/ssl.html#peer-verification-depth). Default is 10. | `auth_oauth2.https.peer_verification` | Should [peer verification](https://rabbitmq.com/ssl.html#peer-verification) be enabled Available values: `verify_none`, `verify_peer`. Default is `verify_none`. It is recommended to configure `verify_peer`. Peer verification requires a certain amount of setup and is more secure. @@ -194,7 +206,11 @@ auth_oauth2.algorithms.2 = RS256 ``` auth_oauth2.resource_server_id = new_resource_server_id +<<<<<<< HEAD auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json +======= +auth_oauth2.jwks_uri = https://my-jwt-issuer/jwks.json +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem auth_oauth2.https.peer_verification = verify_peer auth_oauth2.https.depth = 5 @@ -234,7 +250,11 @@ resolve the user's identity: `username`, `user_name`, `email`, `sub`, `client_id {resource_server_id, <<"my_rabbit_server">>}, {preferred_username_claims, [ <<"username">>, <<"user_name">>, <<"email">> ]} {key_config, [ +<<<<<<< HEAD {jwks_url, <<"https://jwt-issuer.my-domain.local/jwks.json">>} +======= + {jwks_uri, <<"https://jwt-issuer.my-domain.local/jwks.json">>} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]}, ]. diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl index 003818ac74be..3017305bc727 100644 --- a/deps/rabbitmq_auth_backend_oauth2/app.bzl +++ b/deps/rabbitmq_auth_backend_oauth2/app.bzl @@ -13,7 +13,14 @@ def all_beam_files(name = "all_beam_files"): "src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl", "src/rabbit_auth_backend_oauth2.erl", "src/rabbit_auth_backend_oauth2_app.erl", +<<<<<<< HEAD "src/rabbit_oauth2_config.erl", +======= + "src/rabbit_oauth2_keycloak.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_resource_server.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -48,7 +55,14 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl", "src/rabbit_auth_backend_oauth2.erl", "src/rabbit_auth_backend_oauth2_app.erl", +<<<<<<< HEAD "src/rabbit_oauth2_config.erl", +======= + "src/rabbit_oauth2_keycloak.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_resource_server.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -85,6 +99,10 @@ def all_srcs(name = "all_srcs"): ) filegroup( name = "public_hdrs", +<<<<<<< HEAD +======= + srcs = ["include/oauth2.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) filegroup( @@ -94,7 +112,14 @@ def all_srcs(name = "all_srcs"): "src/Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand.erl", "src/rabbit_auth_backend_oauth2.erl", "src/rabbit_auth_backend_oauth2_app.erl", +<<<<<<< HEAD "src/rabbit_oauth2_config.erl", +======= + "src/rabbit_oauth2_keycloak.erl", + "src/rabbit_oauth2_provider.erl", + "src/rabbit_oauth2_rar.erl", + "src/rabbit_oauth2_resource_server.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_oauth2_schema.erl", "src/rabbit_oauth2_scope.erl", "src/uaa_jwks.erl", @@ -174,7 +199,11 @@ def test_suite_beam_files(name = "test_suite_beam_files"): outs = ["test/system_SUITE.beam"], app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", +<<<<<<< HEAD deps = ["//deps/amqp_client:erlang_app"], +======= + deps = ["//deps/amqp10_common:erlang_app", "//deps/amqp_client:erlang_app"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) erlang_bytecode( name = "test_jwks_http_app_beam", @@ -223,9 +252,19 @@ def test_suite_beam_files(name = "test_suite_beam_files"): testonly = True, srcs = ["test/unit_SUITE.erl"], outs = ["test/unit_SUITE.beam"], +<<<<<<< HEAD app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/rabbit_common:erlang_app"], +======= + hdrs = ["include/oauth2.hrl"], + app_name = "rabbitmq_auth_backend_oauth2", + erlc_opts = "//:test_erlc_opts", + deps = [ + "//deps/oauth2_client:erlang_app", + "//deps/rabbit_common:erlang_app", + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) erlang_bytecode( name = "wildcard_match_SUITE_beam_files", @@ -236,10 +275,28 @@ def test_suite_beam_files(name = "test_suite_beam_files"): erlc_opts = "//:test_erlc_opts", ) erlang_bytecode( +<<<<<<< HEAD name = "rabbit_oauth2_config_SUITE_beam_files", testonly = True, srcs = ["test/rabbit_oauth2_config_SUITE.erl"], outs = ["test/rabbit_oauth2_config_SUITE.beam"], +======= + name = "rabbit_oauth2_provider_SUITE_beam_files", + testonly = True, + srcs = ["test/rabbit_oauth2_provider_SUITE.erl"], + outs = ["test/rabbit_oauth2_provider_SUITE.beam"], + hdrs = ["include/oauth2.hrl"], + app_name = "rabbitmq_auth_backend_oauth2", + erlc_opts = "//:test_erlc_opts", + deps = ["//deps/oauth2_client:erlang_app"], + ) + erlang_bytecode( + name = "rabbit_oauth2_resource_server_SUITE_beam_files", + testonly = True, + srcs = ["test/rabbit_oauth2_resource_server_SUITE.erl"], + outs = ["test/rabbit_oauth2_resource_server_SUITE.beam"], + hdrs = ["include/oauth2.hrl"], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) app_name = "rabbitmq_auth_backend_oauth2", erlc_opts = "//:test_erlc_opts", deps = ["//deps/oauth2_client:erlang_app"], diff --git a/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl new file mode 100644 index 000000000000..4652c16ddcd1 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/include/oauth2.hrl @@ -0,0 +1,47 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2020-2023 VMware, Inc. or its affiliates. All rights reserved. +%% + + +-include_lib("oauth2_client/include/oauth2_client.hrl"). + +-define(APP, rabbitmq_auth_backend_oauth2). +-define(DEFAULT_PREFERRED_USERNAME_CLAIMS, [<<"sub">>, <<"client_id">>]). +%% scope aliases map "role names" to a set of scopes + +%% +%% Key JWT fields +%% + +-define(AUD_JWT_FIELD, <<"aud">>). +-define(SCOPE_JWT_FIELD, <<"scope">>). +-define(TAG_SCOPE_PREFIX, <<"tag:">>). + +%% End of Key JWT fields + +-type raw_jwt_token() :: binary() | #{binary() => any()}. +-type decoded_jwt_token() :: #{binary() => any()}. + +-record(internal_oauth_provider, { + id :: oauth_provider_id(), + default_key :: binary() | undefined, + algorithms :: list() | undefined +}). +-type internal_oauth_provider() :: #internal_oauth_provider{}. + +-record(resource_server, { + id :: resource_server_id(), + resource_server_type :: binary() | undefined, + verify_aud :: boolean(), + scope_prefix :: binary(), + additional_scopes_key :: binary() | undefined, + preferred_username_claims :: list(), + scope_aliases :: map() | undefined, + oauth_provider_id :: oauth_provider_id() + }). + +-type resource_server() :: #resource_server{}. +-type resource_server_id() :: binary() | list(). diff --git a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema index 399708ae2562..70c9995ea7c8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema +++ b/deps/rabbitmq_auth_backend_oauth2/priv/schema/rabbitmq_auth_backend_oauth2.schema @@ -73,6 +73,29 @@ list_to_binary(cuttlefish:conf_get("auth_oauth2.additional_scopes_key", Conf)) end}. +<<<<<<< HEAD +======= +{mapping, + "auth_oauth2.scope_aliases.$alias", + "rabbitmq_auth_backend_oauth2.scope_aliases", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.scope_aliases.$index.alias", + "rabbitmq_auth_backend_oauth2.scope_aliases", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.scope_aliases.$index.scope", + "rabbitmq_auth_backend_oauth2.scope_aliases", + [{datatype, string}]}. + +{translation, + "rabbitmq_auth_backend_oauth2.scope_aliases", + fun(Conf) -> + rabbit_oauth2_schema:translate_scope_aliases(Conf) + end}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Configure the plugin to skip validation of the aud field %% @@ -143,6 +166,16 @@ "rabbitmq_auth_backend_oauth2.token_endpoint", [{datatype, string}, {validators, ["uri", "https_uri"]}]}. +<<<<<<< HEAD +======= +%% DEPRECATES auth_oauth2.jwks_url +{mapping, + "auth_oauth2.jwks_uri", + "rabbitmq_auth_backend_oauth2.jwks_uri", + [{datatype, string}, {validators, ["uri", "https_uri"]}]}. + +%% DEPRECATED +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {mapping, "auth_oauth2.jwks_url", "rabbitmq_auth_backend_oauth2.key_config.jwks_url", @@ -159,6 +192,44 @@ [{datatype, string}, {validators, ["uri", "https_uri"]}]}. {mapping, +<<<<<<< HEAD +======= + "auth_oauth2.discovery_endpoint_path", + "rabbitmq_auth_backend_oauth2.discovery_endpoint_path", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.discovery_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.discovery_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.discovery_endpoint_params", + fun(Conf) -> + rabbit_oauth2_schema:translate_endpoint_params("discovery_endpoint_params", Conf) + end}. + +{mapping, + "auth_oauth2.oauth_providers.$name.discovery_endpoint_params.$param", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.oauth_providers.$name.discovery_endpoint_path", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.oauth_providers.$name.algorithms.$algorithm", + "rabbitmq_auth_backend_oauth2.oauth_providers", + [{datatype, string}]}. + +{translation, "rabbitmq_auth_backend_oauth2.oauth_providers", + fun(Conf) -> + rabbit_oauth2_schema:translate_oauth_providers(Conf) + end}. + +{mapping, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "auth_oauth2.https.peer_verification", "rabbitmq_auth_backend_oauth2.key_config.peer_verification", [{datatype, {enum, [verify_peer, verify_none]}}]}. @@ -301,6 +372,10 @@ [{datatype, string}] }. +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {mapping, "auth_oauth2.resource_servers.$name.scope_prefix", "rabbitmq_auth_backend_oauth2.resource_servers", @@ -320,6 +395,24 @@ }. {mapping, +<<<<<<< HEAD +======= + "auth_oauth2.resource_servers.$name.scope_aliases.$alias", + "rabbitmq_auth_backend_oauth2.resource_servers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.resource_servers.$name.scope_aliases.$index.alias", + "rabbitmq_auth_backend_oauth2.resource_servers", + [{datatype, string}]}. + +{mapping, + "auth_oauth2.resource_servers.$name.scope_aliases.$index.scope", + "rabbitmq_auth_backend_oauth2.resource_servers", + [{datatype, string}]}. + +{mapping, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "auth_oauth2.resource_servers.$name.oauth_provider_id", "rabbitmq_auth_backend_oauth2.resource_servers", [{datatype, string}] @@ -333,5 +426,9 @@ {translation, "rabbitmq_auth_backend_oauth2.resource_servers", fun(Conf) -> +<<<<<<< HEAD rabbit_oauth2_schema:translate_resource_servers(Conf) +======= + rabbit_oauth2_schema:translate_resource_servers(Conf) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end}. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl index cdfe6e15056e..8d4a9cd7b17e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_auth_backend_oauth2.erl @@ -8,6 +8,10 @@ -module(rabbit_auth_backend_oauth2). -include_lib("rabbit_common/include/rabbit.hrl"). +<<<<<<< HEAD +======= +-include("oauth2.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -behaviour(rabbit_authn_backend). -behaviour(rabbit_authz_backend). @@ -15,6 +19,7 @@ -export([description/0]). -export([user_login_authentication/2, user_login_authorization/2, check_vhost_access/3, check_resource_access/4, +<<<<<<< HEAD check_topic_access/4, check_token/1, update_state/2, expiry_timestamp/1]). @@ -23,12 +28,31 @@ -import(uaa_jwt, [resolve_resource_server_id/1]). -import(rabbit_data_coercion, [to_map/1]). -import(rabbit_oauth2_config, [get_preferred_username_claims/1]). +======= + check_topic_access/4, update_state/2, + expiry_timestamp/1]). + +%% for testing +-export([normalize_token_scope/2, get_expanded_scopes/2]). + +-import(rabbit_data_coercion, [to_map/1]). +-import(uaa_jwt, [ + decode_and_verify/3, + get_scope/1, set_scope/2, + resolve_resource_server/1]). + +-import(rabbit_oauth2_keycloak, [has_keycloak_scopes/1, extract_scopes_from_keycloak_format/1]). +-import(rabbit_oauth2_rar, [extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]). + +-import(rabbit_oauth2_scope, [filter_matching_scope_prefix_and_drop_it/2]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -ifdef(TEST). -compile(export_all). -endif. %% +<<<<<<< HEAD %% App environment %% @@ -46,6 +70,14 @@ -define(AUD_JWT_FIELD, <<"aud">>). -define(SCOPE_JWT_FIELD, <<"scope">>). +======= +%% Types +%% + +-type ok_extracted_auth_user() :: {ok, rabbit_types:auth_user()}. +-type auth_user_extraction_fun() :: fun((decoded_jwt_token()) -> any()). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% API %% @@ -56,6 +88,14 @@ description() -> %%-------------------------------------------------------------------- +<<<<<<< HEAD +======= +-spec user_login_authentication(rabbit_types:username(), [term()] | map()) -> + {'ok', rabbit_types:auth_user()} | + {'refused', string(), [any()]} | + {'error', any()}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) user_login_authentication(Username, AuthProps) -> case authenticate(Username, AuthProps) of {refused, Msg, Args} = AuthResult -> @@ -65,18 +105,37 @@ user_login_authentication(Username, AuthProps) -> AuthResult end. +<<<<<<< HEAD +======= +-spec user_login_authorization(rabbit_types:username(), [term()] | map()) -> + {'ok', any()} | + {'ok', any(), any()} | + {'refused', string(), [any()]} | + {'error', any()}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) user_login_authorization(Username, AuthProps) -> case authenticate(Username, AuthProps) of {ok, #auth_user{impl = Impl}} -> {ok, Impl}; Else -> Else end. +<<<<<<< HEAD +======= +-spec check_vhost_access(AuthUser :: rabbit_types:auth_user(), + VHost :: rabbit_types:vhost(), + AuthzData :: rabbit_types:authz_data()) -> boolean() | {'error', any()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) check_vhost_access(#auth_user{impl = DecodedTokenFun}, VHost, _AuthzData) -> with_decoded_token(DecodedTokenFun(), fun(_Token) -> DecodedToken = DecodedTokenFun(), +<<<<<<< HEAD Scopes = get_scopes(DecodedToken), +======= + Scopes = get_scope(DecodedToken), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ScopeString = rabbit_oauth2_scope:concat_scopes(Scopes, ","), rabbit_log:debug("Matching virtual host '~ts' against the following scopes: ~ts", [VHost, ScopeString]), rabbit_oauth2_scope:vhost_access(VHost, Scopes) @@ -86,7 +145,11 @@ check_resource_access(#auth_user{impl = DecodedTokenFun}, Resource, Permission, _AuthzContext) -> with_decoded_token(DecodedTokenFun(), fun(Token) -> +<<<<<<< HEAD Scopes = get_scopes(Token), +======= + Scopes = get_scope(Token), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_oauth2_scope:resource_access(Resource, Permission, Scopes) end). @@ -99,6 +162,7 @@ check_topic_access(#auth_user{impl = DecodedTokenFun}, end). update_state(AuthUser, NewToken) -> +<<<<<<< HEAD case check_token(NewToken) of %% avoid logging the token {error, _} = E -> E; @@ -119,6 +183,30 @@ update_state(AuthUser, NewToken) -> {error, mismatch_username_after_token_refresh} -> {refused, "Not allowed to change username on refreshed token"} +======= + case resolve_resource_server(NewToken) of + {error, _} = Err0 -> Err0; + {ResourceServer, _} = Tuple -> + case check_token(NewToken, Tuple) of + %% avoid logging the token + {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> + {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid"}; + {refused, Err} -> + {refused, rabbit_misc:format("Authentication using an OAuth 2/JWT token failed: ~tp", [Err])}; + {ok, DecodedToken} -> + CurToken = AuthUser#auth_user.impl, + case ensure_same_username( + ResourceServer#resource_server.preferred_username_claims, + CurToken(), DecodedToken) of + ok -> + Tags = tags_from(DecodedToken), + {ok, AuthUser#auth_user{tags = Tags, + impl = fun() -> DecodedToken end}}; + {error, mismatch_username_after_token_refresh} -> + {refused, + "Not allowed to change username on refreshed token"} + end +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end. @@ -132,6 +220,7 @@ expiry_timestamp(#auth_user{impl = DecodedTokenFun}) -> %%-------------------------------------------------------------------- +<<<<<<< HEAD authenticate(_, AuthProps0) -> AuthProps = to_map(AuthProps0), Token = token_from_context(AuthProps), @@ -162,6 +251,39 @@ authenticate(_, AuthProps0) -> end end. +======= +-spec authenticate(Username, Props) -> Result + when Username :: rabbit_types:username(), + Props :: list() | map(), + Result :: {ok, any()} | {refused, list(), list()} | {refused, {error, any()}}. + +authenticate(_, AuthProps0) -> + AuthProps = to_map(AuthProps0), + Token = token_from_context(AuthProps), + case resolve_resource_server(Token) of + {error, _} = Err0 -> + {refused, "Authentication using OAuth 2/JWT token failed: ~tp", [Err0]}; + {ResourceServer, _} = Tuple -> + case check_token(Token, Tuple) of + {refused, {error, {invalid_token, error, _Err, _Stacktrace}}} -> + {refused, "Authentication using an OAuth 2/JWT token failed: provided token is invalid", []}; + {refused, Err} -> + {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [Err]}; + {ok, DecodedToken} -> + case with_decoded_token(DecodedToken, fun(In) -> auth_user_from_token(In, ResourceServer) end) of + {error, Err} -> + {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [Err]}; + Else -> + Else + end + end + end. + +-spec with_decoded_token(Token, Fun) -> Result + when Token :: decoded_jwt_token(), + Fun :: auth_user_extraction_fun(), + Result :: {ok, any()} | {'error', any()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) with_decoded_token(DecodedToken, Fun) -> case validate_token_expiry(DecodedToken) of ok -> Fun(DecodedToken); @@ -169,6 +291,24 @@ with_decoded_token(DecodedToken, Fun) -> rabbit_log:error(Msg), Err end. +<<<<<<< HEAD +======= + +%% This is a helper function used with HOFs that may return errors. +-spec auth_user_from_token(Token, ResourceServer) -> Result + when Token :: decoded_jwt_token(), + ResourceServer :: resource_server(), + Result :: ok_extracted_auth_user(). +auth_user_from_token(Token0, ResourceServer) -> + Username = username_from( + ResourceServer#resource_server.preferred_username_claims, + Token0), + Tags = tags_from(Token0), + {ok, #auth_user{username = Username, + tags = Tags, + impl = fun() -> Token0 end}}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ensure_same_username(PreferredUsernameClaims, CurrentDecodedToken, NewDecodedToken) -> CurUsername = username_from(PreferredUsernameClaims, CurrentDecodedToken), case {CurUsername, username_from(PreferredUsernameClaims, NewDecodedToken)} of @@ -184,6 +324,7 @@ validate_token_expiry(#{<<"exp">> := Exp}) when is_integer(Exp) -> end; validate_token_expiry(#{}) -> ok. +<<<<<<< HEAD -spec check_token(binary() | map()) -> {'ok', map()} | {'error', term() }| @@ -278,6 +419,63 @@ post_process_payload_with_scope_alias_in_extra_scopes_source(ResourceServerId, P ScopeAliasMapping :: map()) -> map(). post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAliasMapping) -> Scopes0 = maps:get(FieldName, Payload, []), +======= +-spec check_token(raw_jwt_token(), {resource_server(), internal_oauth_provider()}) -> + {'ok', decoded_jwt_token()} | + {'error', term() } | + {'refused', 'signature_invalid' | {'error', term()} | {'invalid_aud', term()}}. + +check_token(DecodedToken, _) when is_map(DecodedToken) -> + {ok, DecodedToken}; + +check_token(Token, {ResourceServer, InternalOAuthProvider}) -> + case decode_and_verify(Token, ResourceServer, InternalOAuthProvider) of + {error, Reason} -> {refused, {error, Reason}}; + {true, Payload} -> {ok, normalize_token_scope(ResourceServer, Payload)}; + {false, _} -> {refused, signature_invalid} + end. + +-spec normalize_token_scope( + ResourceServer :: resource_server(), DecodedToken :: decoded_jwt_token()) -> map(). +normalize_token_scope(ResourceServer, Payload) -> + Payload0 = maps:map(fun(K, V) -> + case K of + ?SCOPE_JWT_FIELD when is_binary(V) -> + binary:split(V, <<" ">>, [global, trim_all]); + _ -> V + end + end, Payload), + + Payload1 = case has_additional_scopes_key(ResourceServer, Payload0) of + true -> extract_scopes_from_additional_scopes_key(ResourceServer, Payload0); + false -> Payload0 + end, + + Payload2 = case has_keycloak_scopes(Payload1) of + true -> extract_scopes_from_keycloak_format(Payload1); + false -> Payload1 + end, + + Payload3 = case ResourceServer#resource_server.scope_aliases of + undefined -> Payload2; + ScopeAliases -> extract_scopes_using_scope_aliases(ScopeAliases, Payload2) + end, + + Payload4 = case has_rich_auth_request_scopes(Payload3) of + true -> extract_scopes_from_rich_auth_request(ResourceServer, Payload3); + false -> Payload3 + end, + + FilteredScopes = filter_matching_scope_prefix_and_drop_it( + get_scope(Payload4), ResourceServer#resource_server.scope_prefix), + set_scope(FilteredScopes, Payload4). + + +-spec extract_scopes_using_scope_aliases( + ScopeAliasMapping :: map(), Payload :: map()) -> map(). +extract_scopes_using_scope_aliases(ScopeAliasMapping, Payload) -> + Scopes0 = get_scope(Payload), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Scopes = rabbit_data_coercion:to_list_of_binaries(Scopes0), %% for all scopes, look them up in the scope alias map, and if they are %% present, add the alias to the final scope list. Note that we also preserve @@ -295,6 +493,7 @@ post_process_payload_with_scope_alias_field_named(Payload, FieldName, ScopeAlias Acc ++ Binaries end end, Scopes, Scopes), +<<<<<<< HEAD maps:put(?SCOPE_JWT_FIELD, ExpandedScopes, Payload). @@ -593,6 +792,44 @@ resolve_scope_var(Elem, Token, Vhost) -> _ -> ElemAsBinary end) end. +======= + set_scope(ExpandedScopes, Payload). + +-spec has_additional_scopes_key( + ResourceServer :: resource_server(), Payload :: map()) -> boolean(). +has_additional_scopes_key(ResourceServer, Payload) when is_map(Payload) -> + case ResourceServer#resource_server.additional_scopes_key of + undefined -> false; + ScopeKey -> maps:is_key(ScopeKey, Payload) + end. + +-spec extract_scopes_from_additional_scopes_key( + ResourceServer :: resource_server(), Payload :: map()) -> map(). +extract_scopes_from_additional_scopes_key(ResourceServer, Payload) -> + Claim = maps:get(ResourceServer#resource_server.additional_scopes_key, Payload), + AdditionalScopes = extract_additional_scopes(ResourceServer, Claim), + set_scope(AdditionalScopes ++ get_scope(Payload), Payload). + +extract_additional_scopes(ResourceServer, ComplexClaim) -> + ResourceServerId = ResourceServer#resource_server.id, + case ComplexClaim of + L when is_list(L) -> L; + M when is_map(M) -> + case maps:get(ResourceServerId, M, undefined) of + undefined -> []; + Ks when is_list(Ks) -> + [erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- Ks]; + ClaimBin when is_binary(ClaimBin) -> + UnprefixedClaims = binary:split(ClaimBin, <<" ">>, [global, trim_all]), + [erlang:iolist_to_binary([ResourceServerId, <<".">>, K]) || K <- UnprefixedClaims]; + _ -> [] + end; + Bin when is_binary(Bin) -> + binary:split(Bin, <<" ">>, [global, trim_all]); + _ -> [] + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% A token may be present in the password credential or in the rabbit_auth_backend_oauth2 %% credential. The former is the most common scenario for the first time authentication. @@ -648,6 +885,7 @@ find_claim_in_token(Claim, Token) -> _ -> false end. +<<<<<<< HEAD -define(TAG_SCOPE_PREFIX, <<"tag:">>). -spec tags_from(map()) -> list(atom()). @@ -670,3 +908,52 @@ matching_scopes_without_prefix(Scopes, PrefixPattern) -> end end, Scopes). +======= +-spec get_expanded_scopes(map(), #resource{}) -> [binary()]. +get_expanded_scopes(Token, #resource{virtual_host = VHost}) -> + Context = #{ token => Token , vhost => VHost}, + case get_scope(Token) of + [] -> []; + Scopes -> lists:map(fun(Scope) -> list_to_binary(parse_scope(Scope, Context)) end, Scopes) + end. + + +parse_scope(Scope, Context) -> + { Acc0, _} = lists:foldl(fun(Elem, { Acc, Stage }) -> parse_scope_part(Elem, Acc, Stage, Context) end, + { [], undefined }, re:split(Scope,"([\{.*\}])",[{return,list},trim])), + Acc0. + +parse_scope_part(Elem, Acc, Stage, Context) -> + case Stage of + error -> {Acc, error}; + undefined -> + case Elem of + "{" -> { Acc, fun capture_var_name/3}; + Value -> { Acc ++ Value, Stage} + end; + _ -> Stage(Elem, Acc, Context) + end. + +capture_var_name(Elem, Acc, #{ token := Token, vhost := Vhost}) -> + { Acc ++ resolve_scope_var(Elem, Token, Vhost), fun expect_closing_var/3}. + +expect_closing_var("}" , Acc, _Context) -> { Acc , undefined }; +expect_closing_var(_ , _Acc, _Context) -> {"", error}. + +resolve_scope_var(Elem, Token, Vhost) -> + case Elem of + "vhost" -> binary_to_list(Vhost); + _ -> + ElemAsBinary = list_to_binary(Elem), + binary_to_list(case maps:get(ElemAsBinary, Token, ElemAsBinary) of + Value when is_binary(Value) -> Value; + _ -> ElemAsBinary + end) + end. + +-spec tags_from(decoded_jwt_token()) -> list(atom()). +tags_from(DecodedToken) -> + Scopes = maps:get(?SCOPE_JWT_FIELD, DecodedToken, []), + TagScopes = filter_matching_scope_prefix_and_drop_it(Scopes, ?TAG_SCOPE_PREFIX), + lists:usort(lists:map(fun rabbit_data_coercion:to_atom/1, TagScopes)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl new file mode 100644 index 000000000000..79c056a808a8 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_keycloak.erl @@ -0,0 +1,41 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_keycloak). + +-include("oauth2.hrl"). + +-export([extract_scopes_from_keycloak_format/1, has_keycloak_scopes/1]). +-import(uaa_jwt, [get_scope/1, set_scope/2]). + +-define(AUTHORIZATION_CLAIM, <<"authorization">>). +-define(PERMISSIONS_CLAIM, <<"permissions">>). +-define(SCOPES_CLAIM, <<"scopes">>). + +-spec has_keycloak_scopes(Payload::map()) -> boolean(). +has_keycloak_scopes(Payload) -> + maps:is_key(?AUTHORIZATION_CLAIM, Payload). + +-spec extract_scopes_from_keycloak_format(Payload :: map()) -> map(). +%% keycloak token format: https://github.com/rabbitmq/rabbitmq-auth-backend-oauth2/issues/36 +extract_scopes_from_keycloak_format(#{?AUTHORIZATION_CLAIM := Authorization} = Payload) -> + AdditionalScopes = extract_scopes_from_keycloak_permissions([], + maps:get(?PERMISSIONS_CLAIM, Authorization, [])), + set_scope(AdditionalScopes ++ get_scope(Payload), Payload). + +extract_scopes_from_keycloak_permissions(Acc, []) -> + Acc; +extract_scopes_from_keycloak_permissions(Acc, [H | T]) when is_map(H) -> + Scopes = case maps:get(?SCOPES_CLAIM, H, []) of + ScopesAsList when is_list(ScopesAsList) -> + ScopesAsList; + ScopesAsBinary when is_binary(ScopesAsBinary) -> + [ScopesAsBinary] + end, + extract_scopes_from_keycloak_permissions(Acc ++ Scopes, T); +extract_scopes_from_keycloak_permissions(Acc, [_ | T]) -> + extract_scopes_from_keycloak_permissions(Acc, T). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl new file mode 100644 index 000000000000..2891af5a8b8d --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_provider.erl @@ -0,0 +1,197 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_provider). + +-include("oauth2.hrl"). + +-export([ + get_internal_oauth_provider/0, get_internal_oauth_provider/1, + add_signing_key/2, add_signing_key/3, replace_signing_keys/1, + replace_signing_keys/2, + get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2 +]). + +-spec get_internal_oauth_provider() -> internal_oauth_provider(). +get_internal_oauth_provider() -> + get_internal_oauth_provider(root). + +-spec get_internal_oauth_provider(oauth_provider_id()) -> internal_oauth_provider(). +get_internal_oauth_provider(OAuthProviderId) -> + #internal_oauth_provider{ + id = OAuthProviderId, + default_key = get_default_key(OAuthProviderId), + algorithms = get_algorithms(OAuthProviderId) + }. + + +%% +%% Signing Key storage: +%% +%% * Static signing keys configured via config file are stored under signing_keys attribute +%% in their respective location (under key_config for the root oauth provider and +%% directly under each oauth provider) +%% * Dynamic signing keys loaded via rabbitmqctl or via JWKS endpoint are stored under +%% jwks attribute in their respective location. However, this attribute stores the +%% combination of static signing keys and dynamic signing keys. If the same kid is +%% found in both sets, the dynamic kid overrides the static kid. +%% + +-type key_type() :: json | pem | map. +-spec add_signing_key(binary(), {key_type(), binary()} ) -> map() | {error, term()}. +add_signing_key(KeyId, Key) -> + LockId = lock(), + try do_add_signing_key(KeyId, Key, root) of + V -> V + after + unlock(LockId) + end. + +-spec add_signing_key(binary(), {key_type(), binary()}, oauth_provider_id()) -> + map() | {error, term()}. +add_signing_key(KeyId, Key, OAuthProviderId) -> + case lock() of + {error, _} = Error -> + Error; + LockId -> + try do_add_signing_key(KeyId, Key, OAuthProviderId) of + V -> V + after + unlock(LockId) + end + end. + +do_add_signing_key(KeyId, Key, OAuthProviderId) -> + do_replace_signing_keys(maps:put(KeyId, Key, + get_signing_keys_from_jwks(OAuthProviderId)), OAuthProviderId). + +get_signing_keys_from_jwks(root) -> + KeyConfig = get_env(key_config, []), + proplists:get_value(jwks, KeyConfig, #{}); +get_signing_keys_from_jwks(OAuthProviderId) -> + OAuthProviders0 = get_env(oauth_providers, #{}), + OAuthProvider0 = maps:get(OAuthProviderId, OAuthProviders0, []), + proplists:get_value(jwks, OAuthProvider0, #{}). + +-spec replace_signing_keys(map()) -> map() | {error, term()}. +replace_signing_keys(SigningKeys) -> + replace_signing_keys(SigningKeys, root). + +-spec replace_signing_keys(map(), oauth_provider_id()) -> map() | {error, term()}. +replace_signing_keys(SigningKeys, OAuthProviderId) -> + case lock() of + {error,_} = Error -> + Error; + LockId -> + try do_replace_signing_keys(SigningKeys, OAuthProviderId) of + V -> V + after + unlock(LockId) + end + end. + +do_replace_signing_keys(SigningKeys, root) -> + KeyConfig = get_env(key_config, []), + KeyConfig1 = proplists:delete(jwks, KeyConfig), + KeyConfig2 = [{jwks, maps:merge( + proplists:get_value(signing_keys, KeyConfig1, #{}), + SigningKeys)} | KeyConfig1], + set_env(key_config, KeyConfig2), + rabbit_log:debug("Replacing signing keys for key_config with ~p keys", + [maps:size(SigningKeys)]), + SigningKeys; + +do_replace_signing_keys(SigningKeys, OauthProviderId) -> + OauthProviders0 = get_env(oauth_providers, #{}), + OauthProvider0 = maps:get(OauthProviderId, OauthProviders0, []), + OauthProvider1 = proplists:delete(jwks, OauthProvider0), + OauthProvider = [{jwks, maps:merge( + proplists:get_value(signing_keys, OauthProvider1, #{}), + SigningKeys)} | OauthProvider1], + + OauthProviders = maps:put(OauthProviderId, OauthProvider, OauthProviders0), + set_env(oauth_providers, OauthProviders), + rabbit_log:debug("Replacing signing keys for ~p -> ~p with ~p keys", + [OauthProviderId, OauthProvider, maps:size(SigningKeys)]), + SigningKeys. + + +-spec get_signing_keys() -> map(). +get_signing_keys() -> + get_signing_keys(root). + +-spec get_signing_keys(oauth_provider_id()) -> map(). +get_signing_keys(root) -> + case get_env(key_config) of + undefined -> + #{}; + KeyConfig -> + case proplists:get_value(jwks, KeyConfig, undefined) of + undefined -> proplists:get_value(signing_keys, KeyConfig, #{}); + Jwks -> Jwks + end + end; +get_signing_keys(OauthProviderId) -> + OauthProviders = get_env(oauth_providers, #{}), + OauthProvider = maps:get(OauthProviderId, OauthProviders, []), + case proplists:get_value(jwks, OauthProvider, undefined) of + undefined -> + proplists:get_value(signing_keys, OauthProvider, #{}); + Jwks -> + Jwks + end. + +get_signing_key(KeyId) -> + maps:get(KeyId, get_signing_keys(root), undefined). +get_signing_key(KeyId, OAuthProviderId) -> + maps:get(KeyId, get_signing_keys(OAuthProviderId), undefined). + +-spec get_default_key(oauth_provider_id()) -> binary() | undefined. +get_default_key(root) -> + case application:get_env(?APP, key_config, undefined) of + undefined -> undefined; + KeyConfig -> proplists:get_value(default_key, KeyConfig, undefined) + end; +get_default_key(OauthProviderId) -> + OauthProviders = application:get_env(?APP, oauth_providers, #{}), + case maps:get(OauthProviderId, OauthProviders, []) of + [] -> undefined; + OauthProvider -> proplists:get_value(default_key, OauthProvider, undefined) + end. + +-spec get_algorithms(oauth_provider_id()) -> list() | undefined. +get_algorithms(root) -> + proplists:get_value(algorithms, get_env(key_config, []), undefined); +get_algorithms(OAuthProviderId) -> + OAuthProviders = get_env(oauth_providers, #{}), + case maps:get(OAuthProviderId, OAuthProviders, undefined) of + undefined -> undefined; + V -> proplists:get_value(algorithms, V, undefined) + end. + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Value) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Value). + + +lock() -> + Nodes = rabbit_nodes:list_running(), + Retries = rabbit_nodes:lock_retries(), + LockId = case global:set_lock({oauth2_config_lock, + rabbitmq_auth_backend_oauth2}, Nodes, Retries) of + true -> rabbitmq_auth_backend_oauth2; + false -> {error, unable_to_claim_lock} + end, + LockId. + +unlock(LockId) -> + Nodes = rabbit_nodes:list_running(), + global:del_lock({oauth2_config_lock, LockId}, Nodes), + ok. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl new file mode 100644 index 000000000000..d8a2c36f8325 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_rar.erl @@ -0,0 +1,183 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +% Rich Authorization Request +-module(rabbit_oauth2_rar). + +-include("oauth2.hrl"). +-import(uaa_jwt, [get_scope/1, set_scope/2]). + +-export([extract_scopes_from_rich_auth_request/2, has_rich_auth_request_scopes/1]). + +-define(AUTHORIZATION_DETAILS_CLAIM, <<"authorization_details">>). +-define(RAR_ACTIONS_FIELD, <<"actions">>). +-define(RAR_LOCATIONS_FIELD, <<"locations">>). +-define(RAR_TYPE_FIELD, <<"type">>). + +-define(RAR_CLUSTER_LOCATION_ATTRIBUTE, <<"cluster">>). +-define(RAR_VHOST_LOCATION_ATTRIBUTE, <<"vhost">>). +-define(RAR_QUEUE_LOCATION_ATTRIBUTE, <<"queue">>). +-define(RAR_EXCHANGE_LOCATION_ATTRIBUTE, <<"exchange">>). +-define(RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, <<"routing-key">>). +-define(RAR_LOCATION_ATTRIBUTES, [ + ?RAR_CLUSTER_LOCATION_ATTRIBUTE, + ?RAR_VHOST_LOCATION_ATTRIBUTE, + ?RAR_QUEUE_LOCATION_ATTRIBUTE, + ?RAR_EXCHANGE_LOCATION_ATTRIBUTE, + ?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE]). + +-define(RAR_ALLOWED_TAG_VALUES, [ + <<"monitoring">>, + <<"administrator">>, + <<"management">>, + <<"policymaker">> ]). +-define(RAR_ALLOWED_ACTION_VALUES, [ + <<"read">>, + <<"write">>, + <<"configure">>, + <<"monitoring">>, + <<"administrator">>, + <<"management">>, + <<"policymaker">> ]). + +-spec has_rich_auth_request_scopes(Payload::map()) -> boolean(). +has_rich_auth_request_scopes(Payload) -> + maps:is_key(?AUTHORIZATION_DETAILS_CLAIM, Payload). + +-spec extract_scopes_from_rich_auth_request(ResourceServer :: resource_server(), + Payload :: map()) -> map(). +%% https://oauth.net/2/rich-authorization-requests/ +extract_scopes_from_rich_auth_request(ResourceServer, + #{?AUTHORIZATION_DETAILS_CLAIM := Permissions} = Payload) -> + ResourceServerType = ResourceServer#resource_server.resource_server_type, + + FilteredPermissionsByType = lists:filter(fun(P) -> + is_recognized_permission(P, ResourceServerType) end, Permissions), + AdditionalScopes = map_rich_auth_permissions_to_scopes( + ResourceServer#resource_server.id, FilteredPermissionsByType), + + ExistingScopes = get_scope(Payload), + set_scope(AdditionalScopes ++ ExistingScopes, Payload). + +put_location_attribute(Attribute, Map) -> + put_attribute(binary:split(Attribute, <<":">>, [global, trim_all]), Map). + +put_attribute([Key, Value | _], Map) -> + case lists:member(Key, ?RAR_LOCATION_ATTRIBUTES) of + true -> maps:put(Key, Value, Map); + false -> Map + end; +put_attribute([_|_], Map) -> Map. + + +% convert [ <<"cluster:A">>, <<"vhost:B" >>, <<"A">>, <<"unknown:C">> ] to #{ <<"cluster">> : <<"A">>, <<"vhost">> : <<"B">> } +% filtering out non-key-value-pairs and keys which are not part of LOCATION_ATTRIBUTES +convert_attribute_list_to_attribute_map(L) -> + convert_attribute_list_to_attribute_map(L, #{}). +convert_attribute_list_to_attribute_map([H|L],Map) when is_binary(H) -> + convert_attribute_list_to_attribute_map(L, put_location_attribute(H,Map)); +convert_attribute_list_to_attribute_map([], Map) -> Map. + +build_permission_resource_path(Map) -> + Vhost = maps:get(?RAR_VHOST_LOCATION_ATTRIBUTE, Map, <<"*">>), + Resource = maps:get(?RAR_QUEUE_LOCATION_ATTRIBUTE, Map, + maps:get(?RAR_EXCHANGE_LOCATION_ATTRIBUTE, Map, <<"*">>)), + RoutingKey = maps:get(?RAR_ROUTING_KEY_LOCATION_ATTRIBUTE, Map, <<"*">>), + + <>. + +map_locations_to_permission_resource_paths(ResourceServerId, L) -> + Locations = case L of + undefined -> []; + LocationsAsList when is_list(LocationsAsList) -> + lists:map(fun(Location) -> convert_attribute_list_to_attribute_map( + binary:split(Location,<<"/">>,[global,trim_all])) end, LocationsAsList); + LocationsAsBinary when is_binary(LocationsAsBinary) -> + [convert_attribute_list_to_attribute_map( + binary:split(LocationsAsBinary,<<"/">>,[global,trim_all]))] + end, + + FilteredLocations = lists:filtermap(fun(L2) -> + case cluster_matches_resource_server_id(L2, ResourceServerId) and + legal_queue_and_exchange_values(L2) of + true -> { true, build_permission_resource_path(L2) }; + false -> false + end end, Locations), + + FilteredLocations. + +cluster_matches_resource_server_id(#{?RAR_CLUSTER_LOCATION_ATTRIBUTE := Cluster}, + ResourceServerId) -> + wildcard:match(ResourceServerId, Cluster); + +cluster_matches_resource_server_id(_,_) -> + false. + +legal_queue_and_exchange_values(#{?RAR_QUEUE_LOCATION_ATTRIBUTE := Queue, + ?RAR_EXCHANGE_LOCATION_ATTRIBUTE := Exchange}) -> + case Queue of + <<>> -> + case Exchange of + <<>> -> true; + _ -> false + end; + _ -> + case Exchange of + Queue -> true; + _ -> false + end + end; +legal_queue_and_exchange_values(_) -> true. + +map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions) -> + map_rich_auth_permissions_to_scopes(ResourceServerId, Permissions, []). +map_rich_auth_permissions_to_scopes(_, [], Acc) -> Acc; +map_rich_auth_permissions_to_scopes(ResourceServerId, + [ #{?RAR_ACTIONS_FIELD := Actions, ?RAR_LOCATIONS_FIELD := Locations } | T ], Acc) -> + ResourcePaths = map_locations_to_permission_resource_paths(ResourceServerId, Locations), + case ResourcePaths of + [] -> map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc); + _ -> + Scopes = case Actions of + undefined -> []; + ActionsAsList when is_list(ActionsAsList) -> + build_scopes(ResourceServerId, + skip_unknown_actions(ActionsAsList), ResourcePaths); + ActionsAsBinary when is_binary(ActionsAsBinary) -> + build_scopes(ResourceServerId, + skip_unknown_actions([ActionsAsBinary]), ResourcePaths) + end, + map_rich_auth_permissions_to_scopes(ResourceServerId, T, Acc ++ Scopes) + end. + +skip_unknown_actions(Actions) -> + lists:filter(fun(A) -> lists:member(A, ?RAR_ALLOWED_ACTION_VALUES) end, Actions). + +produce_list_of_user_tag_or_action_on_resources(ResourceServerId, ActionOrUserTag, Locations) -> + case lists:member(ActionOrUserTag, ?RAR_ALLOWED_TAG_VALUES) of + true -> [<< ResourceServerId/binary, ".tag:", ActionOrUserTag/binary >>]; + _ -> build_scopes_for_action(ResourceServerId, ActionOrUserTag, Locations, []) + end. + +build_scopes_for_action(ResourceServerId, Action, [Location|Locations], Acc) -> + Scope = << ResourceServerId/binary, ".", Action/binary, ":", Location/binary >>, + build_scopes_for_action(ResourceServerId, Action, Locations, [ Scope | Acc ] ); +build_scopes_for_action(_, _, [], Acc) -> Acc. + +build_scopes(ResourceServerId, Actions, Locations) -> + lists:flatmap(fun(Action) -> + produce_list_of_user_tag_or_action_on_resources(ResourceServerId, + Action, Locations) end, Actions). + +is_recognized_permission(#{?RAR_ACTIONS_FIELD := _, ?RAR_LOCATIONS_FIELD:= _ , + ?RAR_TYPE_FIELD := Type }, ResourceServerType) -> + case ResourceServerType of + <<>> -> false; + V when V == Type -> true; + _ -> false + end; +is_recognized_permission(_, _) -> false. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl new file mode 100644 index 000000000000..84675df7c96d --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_resource_server.erl @@ -0,0 +1,240 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_resource_server). + +-include("oauth2.hrl"). + +-export([ + resolve_resource_server_from_audience/1, + new_resource_server/1 +]). + +-spec new_resource_server(resource_server_id()) -> resource_server(). +new_resource_server(ResourceServerId) -> + #resource_server{ + id = ResourceServerId, + resource_server_type = undefined, + verify_aud = true, + scope_prefix = erlang:iolist_to_binary([ResourceServerId, <<".">>]), + additional_scopes_key = undefined, + preferred_username_claims = ?DEFAULT_PREFERRED_USERNAME_CLAIMS, + scope_aliases = undefined, + oauth_provider_id = root + }. + +-spec resolve_resource_server_from_audience(binary() | list() | none) -> + {ok, resource_server()} | + {error, aud_matched_many_resource_servers_only_one_allowed} | + {error, no_matching_aud_found} | + {error, no_aud_found} | + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} | + {error, too_many_resources_with_verify_aud_false}. +resolve_resource_server_from_audience(none) -> + translate_error_if_any( + find_unique_resource_server_without_verify_aud(), false); + +resolve_resource_server_from_audience(Audience) -> + RootResourseServerId = get_root_resource_server_id(), + ResourceServers = get_env(resource_servers, #{}), + ResourceServerIds = maps:fold(fun(K, V, List) -> List ++ + [proplists:get_value(id, V, K)] end, [], ResourceServers), + AllowedResourceServerIds = append(ResourceServerIds, RootResourseServerId), + + case find_audience(Audience, AllowedResourceServerIds) of + {error, aud_matched_many_resource_servers_only_one_allowed} = Error -> + Error; + {error, no_matching_aud_found} -> + translate_error_if_any( + find_unique_resource_server_without_verify_aud(), + true); + {ok, ResourceServerId} -> + {ok, get_resource_server(ResourceServerId)} + end. + +-spec get_root_resource_server_id() -> resource_server_id(). +get_root_resource_server_id() -> + get_env(resource_server_id, <<>>). + +-spec get_root_resource_server() -> resource_server(). +get_root_resource_server() -> + ResourceServerId = + get_root_resource_server_id(), + ScopeAliases = + get_env(scope_aliases), + PreferredUsernameClaims = + case get_env(preferred_username_claims) of + undefined -> ?DEFAULT_PREFERRED_USERNAME_CLAIMS; + Value -> + Value + end, + ResourceServerType = + get_env(resource_server_type), + VerifyAud = + get_boolean_env(verify_aud, true), + AdditionalScopesKey = + get_env(extra_scopes_source), + DefaultScopePrefix = + case ResourceServerId of + <<>> -> undefined; + _ -> erlang:iolist_to_binary([ResourceServerId, <<".">>]) + end, + ScopePrefix = + get_env(scope_prefix, DefaultScopePrefix), + OAuthProviderId = + case get_env(default_oauth_provider) of + undefined -> root; + DefaultOauthProviderId -> DefaultOauthProviderId + end, + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }. + +-spec get_resource_server(resource_server_id()) -> resource_server() | undefined. +get_resource_server(ResourceServerId) -> + RootResourseServer = get_root_resource_server(), + RootResourseServerId = RootResourseServer#resource_server.id, + case ResourceServerId of + <<>> -> undefined; + RootResourseServerId -> RootResourseServer; + _ -> get_resource_server(ResourceServerId, RootResourseServer) + end. + +-spec get_resource_server(ResourceServerId :: resource_server_id(), + DefaultResourceServerSettings :: resource_server()) -> resource_server(). +get_resource_server(ResourceServerId, RootResourseServer) when + ResourceServerId == RootResourseServer#resource_server.id -> + RootResourseServer; +get_resource_server(ResourceServerId, RootResourseServer) when + ResourceServerId =/= RootResourseServer#resource_server.id -> + ResourceServerProps = + maps:get(ResourceServerId, get_env(resource_servers, #{}), []), + ScopeAliases = + proplists:get_value(scope_aliases, ResourceServerProps, + RootResourseServer#resource_server.scope_aliases), + PreferredUsernameClaims = + proplists:get_value(preferred_username_claims, ResourceServerProps, + RootResourseServer#resource_server.preferred_username_claims), + ResourceServerType = + proplists:get_value(resource_server_type, ResourceServerProps, + RootResourseServer#resource_server.resource_server_type), + VerifyAud = + proplists:get_value(verify_aud, ResourceServerProps, + RootResourseServer#resource_server.verify_aud), + AdditionalScopesKey = + proplists:get_value(extra_scopes_source, ResourceServerProps, + RootResourseServer#resource_server.additional_scopes_key), + RootScopePrefix = get_env(scope_prefix, undefined), + ScopePrefix = + proplists:get_value(scope_prefix, ResourceServerProps, + case RootScopePrefix of + undefined -> erlang:iolist_to_binary([ResourceServerId, <<".">>]); + Prefix -> Prefix + end), + OAuthProviderId = + proplists:get_value(oauth_provider_id, ResourceServerProps, + RootResourseServer#resource_server.oauth_provider_id), + + #resource_server{ + id = ResourceServerId, + resource_server_type = ResourceServerType, + verify_aud = VerifyAud, + scope_prefix = ScopePrefix, + additional_scopes_key = AdditionalScopesKey, + preferred_username_claims = PreferredUsernameClaims, + scope_aliases = ScopeAliases, + oauth_provider_id = OAuthProviderId + }. + +-spec find_audience(binary() | list(), list()) -> + {ok, resource_server_id()} | + {error, aud_matched_many_resource_servers_only_one_allowed} | + {error, no_matching_aud_found}. +find_audience(Audience, ResourceIdList) when is_binary(Audience) -> + AudList = binary:split(Audience, <<" ">>, [global, trim_all]), + find_audience(AudList, ResourceIdList); +find_audience(AudList, ResourceIdList) when is_list(AudList) -> + case intersection(AudList, ResourceIdList) of + [One] -> {ok, One}; + [_One|_Tail] -> {error, aud_matched_many_resource_servers_only_one_allowed}; + [] -> {error, no_matching_aud_found} + end. + +-spec translate_error_if_any( + {ok, resource_server()} | + {error, not_found} | + {error, found_many}, boolean()) -> + {ok, resource_server()} | + {error, no_aud_found} | + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers} | + {error, no_matching_aud_found} | + {error, too_many_resources_with_verify_aud_false}. +translate_error_if_any(ResourceServerOrError, HasAudience) -> + case {ResourceServerOrError, HasAudience} of + {{ok, _} = Ok, _} -> + Ok; + {{error, not_found}, false} -> + {error, no_aud_found}; + {{error, not_found}, _} -> + {error, no_matching_aud_found}; + {{error, found_many}, false} -> + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}; + {{error, found_many}, _} -> + {error, too_many_resources_with_verify_aud_false} + end. +-spec find_unique_resource_server_without_verify_aud() -> + {ok, resource_server()} | + {error, not_found} | + {error, found_many}. +find_unique_resource_server_without_verify_aud() -> + Root = get_root_resource_server(), + Map0 = maps:filter(fun(_K,V) -> not get_boolean_value(verify_aud, V, + Root#resource_server.verify_aud) end, get_env(resource_servers, #{})), + Map = case {Root#resource_server.id, Root#resource_server.verify_aud} of + {<<>>, _} -> Map0; + {_, true} -> Map0; + {Id, false} -> maps:put(Id, Root, Map0) + end, + case maps:size(Map) of + 0 -> {error, not_found}; + 1 -> {ok, get_resource_server(lists:last(maps:keys(Map)), Root)}; + _ -> {error, found_many} + end. + +append(List, Value) -> + case Value of + <<>> -> List; + _ -> List ++ [Value] + end. +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, undefined). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +-spec get_boolean_env(atom(), boolean()) -> boolean(). +get_boolean_env(Par, Def) -> + case get_env(Par, Def) of + true -> true; + false -> false; + _ -> true + end. +-spec get_boolean_value(term(), list(), boolean()) -> boolean(). +get_boolean_value(Key, Proplist, Def) -> + case proplists:get_value(Key, Proplist, Def) of + true -> true; + false -> false; + _ -> true + end. +intersection(List1, List2) -> + [I || I <- List1, lists:member(I, List2)]. diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl index 6c1e251dacb6..b28673f48d35 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_schema.erl @@ -7,6 +7,7 @@ -module(rabbit_oauth2_schema). +<<<<<<< HEAD -export([ translate_oauth_providers/1, translate_resource_servers/1, @@ -17,17 +18,147 @@ "additional_scopes_key" => "extra_scopes_source" }). +======= +-define(AUTH_OAUTH2, "auth_oauth2"). +-define(SCOPE_ALIASES, "scope_aliases"). +-define(RESOURCE_SERVERS, "resource_servers"). +-define(OAUTH_PROVIDERS, "oauth_providers"). +-define(SIGNING_KEYS, "signing_keys"). +-define(AUTH_OAUTH2_SCOPE_ALIASES, ?AUTH_OAUTH2 ++ "." ++ ?SCOPE_ALIASES). +-define(AUTH_OAUTH2_RESOURCE_SERVERS, ?AUTH_OAUTH2 ++ "." ++ ?RESOURCE_SERVERS). +-define(AUTH_OAUTH2_OAUTH_PROVIDERS, ?AUTH_OAUTH2 ++ "." ++ ?OAUTH_PROVIDERS). +-define(AUTH_OAUTH2_SIGNING_KEYS, ?AUTH_OAUTH2 ++ "." ++ ?SIGNING_KEYS). +-define(RESOURCE_SERVERS_SYNONYMS, #{ + "additional_scopes_key" => "extra_scopes_source" +}). + +-export([ + translate_oauth_providers/1, + translate_resource_servers/1, + translate_signing_keys/1, + translate_endpoint_params/2, + translate_scope_aliases/1 +]). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) resource_servers_key_synonym(Key) -> maps:get(Key, ?RESOURCE_SERVERS_SYNONYMS, Key). extract_key_as_binary({Name,_}) -> list_to_binary(Name). extract_value({_Name,V}) -> V. +<<<<<<< HEAD -spec translate_resource_servers([{list(), binary()}]) -> map(). translate_resource_servers(Conf) -> Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.resource_servers", Conf), Map = merge_list_of_maps([ extract_resource_server_properties(Settings), extract_resource_server_preferred_username_claims(Settings) +======= +-spec translate_scope_aliases([{list(), binary()}]) -> map(). +translate_scope_aliases(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + ?AUTH_OAUTH2_SCOPE_ALIASES, Conf), + maps:merge(extract_scope_alias_as_map(Settings), + extract_scope_aliases_as_list_of_alias_scope_props(Settings)). + +convert_space_separated_string_to_list_of_binaries(String) -> + [ list_to_binary(V) || V <- string:tokens(String, " ")]. + +extract_scope_alias_as_map(Settings) -> + maps:from_list([{ + list_to_binary(Alias), + convert_space_separated_string_to_list_of_binaries(Scope) + } + || {[?AUTH_OAUTH2, ?SCOPE_ALIASES, Alias], Scope} <- Settings ]). + +extract_scope_aliases_as_list_of_alias_scope_props(Settings) -> + KeyFun = fun extract_key_as_binary/1, + ValueFun = fun extract_value/1, + + List0 = [{Index, {list_to_atom(Attr), V}} + || {[?AUTH_OAUTH2, ?SCOPE_ALIASES, Index, Attr], V} <- Settings ], + List1 = maps:to_list(maps:groups_from_list(KeyFun, ValueFun, List0)), + List2 = [extract_scope_alias_mapping(Proplist) || {_, Proplist} <- List1], + maps:from_list([ V || V <- List2, V =/= {}]). + +extract_scope_alias_mapping(Proplist) -> + Alias = + case proplists:get_value(alias, Proplist) of + undefined -> {error, missing_alias_attribute}; + A -> list_to_binary(A) + end, + Scope = + case proplists:get_value(scope, Proplist) of + undefined -> {error, missing_scope_attribute}; + S -> convert_space_separated_string_to_list_of_binaries(S) + end, + case {Alias, Scope} of + {{error, _}, _} -> + cuttlefish:warn( + "Skipped scope_aliases due to missing alias attribute"), + {}; + {_, {error, _}} -> + cuttlefish:warn( + "Skipped scope_aliases due to missing scope attribute"), + {}; + _ = V -> V + end. + +extract_resource_server_scope_aliases_as_list_of_props(Settings) -> + KeyFun = fun extract_key_as_binary/1, + ValueFun = fun extract_value/1, + + List0 = [ + { + Name, + {Index, {list_to_atom(Attr), V}} + } || + {[ + ?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, ?SCOPE_ALIASES, + Index, Attr + ], V + } <- Settings ], + Map0 = maps:groups_from_list(KeyFun, ValueFun, List0), + + Map4 = maps:map(fun (_, L) -> + Map2 = maps:map(fun (_, L2) -> extract_scope_alias_mapping(L2) end, + maps:groups_from_list(KeyFun, ValueFun, L)), + Map3 = maps:filter(fun (_,V) -> V =/= {} end, Map2), + [{scope_aliases, maps:from_list([ V || {_, V} <- maps:to_list(Map3)])}] + end, Map0), + + Map4. + +extract_resource_server_scope_aliases_as_map(Settings) -> + KeyFun = fun extract_key_as_binary/1, + ValueFun = fun extract_value/1, + + List0 = [ + { + Name, + { + list_to_binary(Alias), + convert_space_separated_string_to_list_of_binaries(Scope) + } + } || + {[ + ?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, ?SCOPE_ALIASES, + Alias + ], Scope + } <- Settings ], + Map0 = maps:groups_from_list(KeyFun, ValueFun, List0), + maps:map(fun (_, L) -> [{scope_aliases, maps:from_list(L)}] end, Map0). + +-spec translate_resource_servers([{list(), binary()}]) -> map(). +translate_resource_servers(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + ?AUTH_OAUTH2_RESOURCE_SERVERS, Conf), + Map = merge_list_of_maps([ + extract_resource_server_properties(Settings), + extract_resource_server_preferred_username_claims(Settings), + extract_resource_server_scope_aliases_as_list_of_props(Settings), + extract_resource_server_scope_aliases_as_map(Settings) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]), Map0 = maps:map(fun(K,V) -> case proplists:get_value(id, V) of @@ -35,6 +166,7 @@ translate_resource_servers(Conf) -> _ -> V end end, Map), ResourceServers = maps:values(Map0), +<<<<<<< HEAD lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, ResourceServers). @@ -52,6 +184,31 @@ translate_oauth_providers(Conf) -> translate_signing_keys(Conf) -> Settings = cuttlefish_variable:filter_by_prefix("auth_oauth2.signing_keys", Conf), ListOfKidPath = lists:map(fun({Id, Path}) -> {list_to_binary(lists:last(Id)), Path} end, Settings), +======= + lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), + Elem, AccMap) end, #{}, ResourceServers). + +-spec translate_oauth_providers([{list(), binary()}]) -> map(). +translate_oauth_providers(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + ?AUTH_OAUTH2_OAUTH_PROVIDERS, Conf), + + merge_list_of_maps([ + extract_oauth_providers_properties(Settings), + extract_oauth_providers_endpoint_params(discovery_endpoint_params, + Settings), + extract_oauth_providers_algorithm(Settings), + extract_oauth_providers_https(Settings), + extract_oauth_providers_signing_keys(Settings) + ]). + +-spec translate_signing_keys([{list(), binary()}]) -> map(). +translate_signing_keys(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + ?AUTH_OAUTH2_SIGNING_KEYS, Conf), + ListOfKidPath = lists:map(fun({Id, Path}) -> { + list_to_binary(lists:last(Id)), Path} end, Settings), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) translate_list_of_signing_keys(ListOfKidPath). -spec translate_list_of_signing_keys([{list(), list()}]) -> map(). @@ -62,17 +219,36 @@ translate_list_of_signing_keys(ListOfKidPath) -> {ok, Bin} -> string:trim(Bin, trailing, "\n"); _Error -> +<<<<<<< HEAD %% this throws and makes Cuttlefish treak the key as invalid cuttlefish:invalid("file does not exist or cannot be read by the node") end end, maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, maps:from_list(ListOfKidPath)). +======= + cuttlefish:invalid(io_lib:format( + "File ~p does not exist or cannot be read by the node", + [Path])) + end + end, + maps:map(fun(_K, Path) -> {pem, TryReadingFileFun(Path)} end, + maps:from_list(ListOfKidPath)). + +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> + [{binary(), binary()}]. +translate_endpoint_params(Variable, Conf) -> + Params0 = cuttlefish_variable:filter_by_prefix("auth_oauth2." ++ Variable, + Conf), + [{list_to_binary(Param), list_to_binary(V)} || {["auth_oauth2", _, Param], V} + <- Params0]. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) validator_file_exists(Attr, Filename) -> case file:read_file(Filename) of {ok, _} -> Filename; _Error -> +<<<<<<< HEAD %% this throws and makes Cuttlefish treak the key as invalid cuttlefish:invalid(io_lib:format( "Invalid attribute (~p) value: file ~p does not exist or cannot be read by the node", [Attr, Filename])) @@ -81,31 +257,77 @@ validator_https_uri(Attr, Uri) when is_binary(Uri) -> list_to_binary(validator_https_uri(Attr, binary_to_list(Uri))); validator_https_uri(Attr, Uri) -> +======= + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: file ~p does not exist or " ++ + "cannot be read by the node", [Attr, Filename])) + end. + +validator_uri(Attr, Uri) when is_binary(Uri) -> + validator_uri(Attr, binary_to_list(Uri)); +validator_uri(Attr, Uri) when is_list(Uri) -> + case uri_string:parse(Uri) of + {error, _, _} = Error -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: ~p (~p)", [Attr, Uri, Error])); + _ -> Uri + end. + +validator_https_uri(Attr, Uri) when is_binary(Uri) -> + validator_https_uri(Attr, binary_to_list(Uri)); + +validator_https_uri(Attr, Uri) when is_list(Uri) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case string:nth_lexeme(Uri, 1, "://") == "https" of true -> Uri; false -> cuttlefish:invalid(io_lib:format( +<<<<<<< HEAD "Invalid attribute (~p) value: uri ~p must be a valid https uri", [Attr, Uri])) end. merge_list_of_maps(ListOfMaps) -> lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, Elem, AccIn) end, #{}, ListOfMaps). +======= + "Invalid attribute (~p) value: uri ~p must be a valid " ++ + "https uri", [Attr, Uri])) + end. + +merge_list_of_maps(ListOfMaps) -> + lists:foldl(fun(Elem, AccIn) -> maps:merge_with( + fun(_K,V1,V2) -> V1 ++ V2 end, Elem, AccIn) end, #{}, ListOfMaps). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) extract_oauth_providers_properties(Settings) -> KeyFun = fun extract_key_as_binary/1, ValueFun = fun extract_value/1, +<<<<<<< HEAD OAuthProviders = [{Name, mapOauthProviderProperty({list_to_atom(Key), list_to_binary(V)})} || {["auth_oauth2","oauth_providers", Name, Key], V} <- Settings ], maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). +======= + OAuthProviders = [{Name, mapOauthProviderProperty( + { + list_to_atom(Key), + list_to_binary(V)}) + } || {[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, Key], V} <- Settings ], + maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). + + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) extract_resource_server_properties(Settings) -> KeyFun = fun extract_key_as_binary/1, ValueFun = fun extract_value/1, OAuthProviders = [{Name, {list_to_atom(resource_servers_key_synonym(Key)), list_to_binary(V)}} +<<<<<<< HEAD || {["auth_oauth2","resource_servers", Name, Key], V} <- Settings ], +======= + || {[?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, Key], V} <- Settings ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) maps:groups_from_list(KeyFun, ValueFun, OAuthProviders). mapOauthProviderProperty({Key, Value}) -> @@ -115,6 +337,14 @@ mapOauthProviderProperty({Key, Value}) -> jwks_uri -> validator_https_uri(Key, Value); end_session_endpoint -> validator_https_uri(Key, Value); authorization_endpoint -> validator_https_uri(Key, Value); +<<<<<<< HEAD +======= + discovery_endpoint_path -> validator_uri(Key, Value); + discovery_endpoint_params -> + cuttlefish:invalid(io_lib:format( + "Invalid attribute (~p) value: should be a map of Key,Value pairs", + [Key])); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> Value end}. @@ -122,10 +352,18 @@ extract_oauth_providers_https(Settings) -> ExtractProviderNameFun = fun extract_key_as_binary/1, AttributesPerProvider = [{Name, mapHttpProperty({list_to_atom(Key), V})} || +<<<<<<< HEAD {["auth_oauth2","oauth_providers", Name, "https", Key], V} <- Settings ], maps:map(fun(_K,V)-> [{https, V}] end, maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end, AttributesPerProvider)). +======= + {[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, "https", Key], V} <- Settings ], + + maps:map(fun(_K,V)-> [{https, V}] end, + maps:groups_from_list(ExtractProviderNameFun, fun({_, V}) -> V end, + AttributesPerProvider)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) mapHttpProperty({Key, Value}) -> {Key, case Key of @@ -137,8 +375,15 @@ extract_oauth_providers_algorithm(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedAlgorithms = [{Name, {Index, list_to_binary(V)}} || +<<<<<<< HEAD {["auth_oauth2","oauth_providers", Name, "algorithms", Index], V} <- Settings ], SortedAlgorithms = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, IndexedAlgorithms), +======= + {[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, "algorithms", Index], V} + <- Settings ], + SortedAlgorithms = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, + IndexedAlgorithms), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Algorithms = [{Name, V} || {Name, {_I, V}} <- SortedAlgorithms], maps:map(fun(_K,V)-> [{algorithms, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, Algorithms)). @@ -147,16 +392,40 @@ extract_resource_server_preferred_username_claims(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedClaims = [{Name, {Index, list_to_binary(V)}} || +<<<<<<< HEAD {["auth_oauth2","resource_servers", Name, "preferred_username_claims", Index], V} <- Settings ], SortedClaims = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, IndexedClaims), +======= + {[?AUTH_OAUTH2, ?RESOURCE_SERVERS, Name, "preferred_username_claims", + Index], V} <- Settings ], + SortedClaims = lists:sort(fun({_,{AI,_}},{_,{BI,_}}) -> AI < BI end, + IndexedClaims), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Claims = [{Name, V} || {Name, {_I, V}} <- SortedClaims], maps:map(fun(_K,V)-> [{preferred_username_claims, V}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, Claims)). +<<<<<<< HEAD +======= +extract_oauth_providers_endpoint_params(Variable, Settings) -> + KeyFun = fun extract_key_as_binary/1, + + IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || + {["auth_oauth2","oauth_providers", Name, EndpointVar, ParamName], V} + <- Settings, EndpointVar == atom_to_list(Variable) ], + maps:map(fun(_K,V)-> [{Variable, V}] end, + maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) extract_oauth_providers_signing_keys(Settings) -> KeyFun = fun extract_key_as_binary/1, IndexedSigningKeys = [{Name, {list_to_binary(Kid), list_to_binary(V)}} || +<<<<<<< HEAD {["auth_oauth2","oauth_providers", Name, "signing_keys", Kid], V} <- Settings ], +======= + {[?AUTH_OAUTH2, ?OAUTH_PROVIDERS, Name, ?SIGNING_KEYS, Kid], V} + <- Settings ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) maps:map(fun(_K,V)-> [{signing_keys, translate_list_of_signing_keys(V)}] end, maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedSigningKeys)). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl index d81c7ded0c8f..25e2aca32f7c 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/rabbit_oauth2_scope.erl @@ -7,7 +7,15 @@ -module(rabbit_oauth2_scope). +<<<<<<< HEAD -export([vhost_access/2, resource_access/3, topic_access/4, concat_scopes/2]). +======= +-export([vhost_access/2, + resource_access/3, + topic_access/4, + concat_scopes/2, + filter_matching_scope_prefix_and_drop_it/2]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("rabbit_common/include/rabbit.hrl"). @@ -88,3 +96,23 @@ parse_resource_pattern(Pattern, Permission) -> {VhostPattern, NamePattern, RoutingKeyPattern, Permission}; _Other -> ignore end. +<<<<<<< HEAD +======= + +-spec filter_matching_scope_prefix_and_drop_it(list(), binary()|list()) -> list(). +filter_matching_scope_prefix_and_drop_it(Scopes, <<"">>) -> Scopes; +filter_matching_scope_prefix_and_drop_it(Scopes, PrefixPattern) -> + PatternLength = byte_size(PrefixPattern), + lists:filtermap( + fun(ScopeEl) -> + case binary:match(ScopeEl, PrefixPattern) of + {0, PatternLength} -> + ElLength = byte_size(ScopeEl), + {true, + binary:part(ScopeEl, + {PatternLength, ElLength - PatternLength})}; + _ -> false + end + end, + Scopes). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl index edd81902da15..4cc3959dd2ea 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl @@ -1,7 +1,11 @@ -module(uaa_jwks). -export([get/2]). +<<<<<<< HEAD -spec get(string() | binary(), term()) -> {ok, term()} | {error, term()}. +======= +-spec get(uri_string:uri_string(), list()) -> {ok, term()} | {error, term()}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) get(JwksUrl, SslOptions) -> Options = [{timeout, 60000}] ++ [{ssl, SslOptions}], httpc:request(get, {JwksUrl, []}, Options, []). diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl index cf14486c6ead..a1be023f3cbd 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt.erl @@ -7,6 +7,7 @@ -module(uaa_jwt). -export([add_signing_key/3, +<<<<<<< HEAD decode_and_verify/1, get_jwk/2, resolve_resource_server_id/1, @@ -18,6 +19,30 @@ -include_lib("oauth2_client/include/oauth2_client.hrl"). -define(APP, rabbitmq_auth_backend_oauth2). +======= + decode_and_verify/3, + get_jwk/2, + verify_signing_key/2, + resolve_resource_server/1]). + +-export([client_id/1, sub/1, client_id/2, sub/2, get_scope/1, set_scope/2]). + +-include("oauth2.hrl"). +-include_lib("jose/include/jose_jwk.hrl"). + +-import(rabbit_data_coercion, [ + to_map/1]). +-import(oauth2_client, [ + format_ssl_options/1, + format_oauth_provider_id/1, + get_oauth_provider/2]). +-import(rabbit_oauth2_resource_server, [ + resolve_resource_server_from_audience/1]). +-import(rabbit_oauth2_provider, [ + add_signing_key/2, get_signing_key/2, + get_internal_oauth_provider/1, + replace_signing_keys/2]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -type key_type() :: json | pem | map. @@ -25,7 +50,11 @@ add_signing_key(KeyId, Type, Value) -> case verify_signing_key(Type, Value) of ok -> +<<<<<<< HEAD {ok, rabbit_oauth2_config:add_signing_key(KeyId, {Type, Value})}; +======= + {ok, add_signing_key(KeyId, {Type, Value})}; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {error, _} = Err -> Err end. @@ -33,20 +62,31 @@ add_signing_key(KeyId, Type, Value) -> -spec update_jwks_signing_keys(oauth_provider()) -> ok | {error, term()}. update_jwks_signing_keys(#oauth_provider{id = Id, jwks_uri = JwksUrl, ssl_options = SslOptions}) -> +<<<<<<< HEAD rabbit_log:debug("OAuth 2 JWT: downloading keys from ~tp (TLS options: ~p)", [JwksUrl, SslOptions]), +======= + rabbit_log:debug("Downloading signing keys from ~tp (TLS options: ~p)", + [JwksUrl, format_ssl_options(SslOptions)]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case uaa_jwks:get(JwksUrl, SslOptions) of {ok, {_, _, JwksBody}} -> KeyList = maps:get(<<"keys">>, jose:decode(erlang:iolist_to_binary(JwksBody)), []), Keys = maps:from_list(lists:map(fun(Key) -> {maps:get(<<"kid">>, Key, undefined), {json, Key}} end, KeyList)), +<<<<<<< HEAD rabbit_log:debug("OAuth 2 JWT: downloaded keys ~tp", [Keys]), case rabbit_oauth2_config:replace_signing_keys(Keys, Id) of +======= + rabbit_log:debug("Downloaded ~p signing keys", [maps:size(Keys)]), + case replace_signing_keys(Keys, Id) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {error, _} = Err -> Err; _ -> ok end; {error, _} = Err -> +<<<<<<< HEAD rabbit_log:error("OAuth 2 JWT: failed to download keys: ~tp", [Err]), Err end. @@ -113,11 +153,81 @@ get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> ok -> get_jwk(KeyId, OAuthProviderId, false); {error, no_jwks_url} -> +======= + rabbit_log:error("Failed to download signing keys: ~tp", [Err]), + Err + end. + +-spec decode_and_verify(binary(), resource_server(), internal_oauth_provider()) + -> {boolean(), map()} | {error, term()}. +decode_and_verify(Token, ResourceServer, InternalOAuthProvider) -> + OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, + rabbit_log:debug("Decoding token for resource_server: ~p using oauth_provider_id: ~p", + [ResourceServer#resource_server.id, + format_oauth_provider_id(OAuthProviderId)]), + Result = case uaa_jwt_jwt:get_key_id(Token) of + undefined -> InternalOAuthProvider#internal_oauth_provider.default_key; + {ok, KeyId0} -> KeyId0; + {error, _} = Err -> Err + end, + case Result of + {error, _} = Err2 -> + Err2; + KeyId -> + case get_jwk(KeyId, InternalOAuthProvider) of + {ok, JWK} -> + Algorithms = InternalOAuthProvider#internal_oauth_provider.algorithms, + rabbit_log:debug("Verifying signature using signing_key_id : '~tp' and algorithms: ~p", + [KeyId, Algorithms]), + uaa_jwt_jwt:decode_and_verify(Algorithms, JWK, Token); + {error, _} = Err3 -> + Err3 + end + end. + +-spec resolve_resource_server(binary()|map()) -> {error, term()} | + {resource_server(), internal_oauth_provider()}. +resolve_resource_server(DecodedToken) when is_map(DecodedToken) -> + Aud = maps:get(?AUD_JWT_FIELD, DecodedToken, none), + resolve_resource_server_given_audience(Aud); +resolve_resource_server(Token) -> + case uaa_jwt_jwt:get_aud(Token) of + {error, _} = Error -> Error; + {ok, Audience} -> resolve_resource_server_given_audience(Audience) + end. +resolve_resource_server_given_audience(Audience) -> + case resolve_resource_server_from_audience(Audience) of + {error, _} = Error -> + Error; + {ok, ResourceServer} -> + {ResourceServer, get_internal_oauth_provider( + ResourceServer#resource_server.oauth_provider_id)} + end. + +-spec get_jwk(binary(), internal_oauth_provider()) -> {ok, map()} | {error, term()}. +get_jwk(KeyId, InternalOAuthProvider) -> + get_jwk(KeyId, InternalOAuthProvider, true). + +get_jwk(KeyId, InternalOAuthProvider, AllowUpdateJwks) -> + OAuthProviderId = InternalOAuthProvider#internal_oauth_provider.id, + case get_signing_key(KeyId, OAuthProviderId) of + undefined -> + case AllowUpdateJwks of + true -> + rabbit_log:debug("Signing key '~tp' not found. Downloading it... ", [KeyId]), + case get_oauth_provider(OAuthProviderId, [jwks_uri]) of + {ok, OAuthProvider} -> + case update_jwks_signing_keys(OAuthProvider) of + ok -> + get_jwk(KeyId, InternalOAuthProvider, false); + {error, no_jwks_uri} -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {error, key_not_found}; {error, _} = Err -> Err end; {error, _} = Error -> +<<<<<<< HEAD rabbit_log:debug("OAuth 2 JWT: unable to download keys due to ~p", [Error]), Error end; @@ -127,6 +237,17 @@ get_jwk(KeyId, OAuthProviderId, AllowUpdateJwks) -> end; {Type, Value} -> rabbit_log:debug("OAuth 2 JWT: signing key found: '~tp', '~tp'", [Type, Value]), +======= + rabbit_log:debug("Unable to download signing keys due to ~p", [Error]), + Error + end; + false -> + rabbit_log:debug("Signing key '~tp' not found. Downloading is not allowed", [KeyId]), + {error, key_not_found} + end; + {Type, Value} -> + rabbit_log:debug("Signing key ~p found", [KeyId]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case Type of json -> uaa_jwt_jwk:make_jwk(Value); pem -> uaa_jwt_jwk:from_pem(Value); @@ -153,6 +274,16 @@ verify_signing_key(Type, Value) -> Err -> Err end. +<<<<<<< HEAD +======= +-spec get_scope(map()) -> binary() | list(). +get_scope(#{?SCOPE_JWT_FIELD := Scope}) -> Scope; +get_scope(#{}) -> []. + +-spec set_scope(list(), map()) -> map(). +set_scope(Scopes, DecodedToken) -> + DecodedToken#{?SCOPE_JWT_FIELD => Scopes}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -spec client_id(map()) -> binary() | undefined. client_id(DecodedToken) -> diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl index 7d8c37457028..7aeda6ebd77a 100644 --- a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl +++ b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwt_jwt.erl @@ -6,29 +6,50 @@ %% -module(uaa_jwt_jwt). +<<<<<<< HEAD -export([decode_and_verify/3, get_key_id/2, get_aud/1]). +======= +-export([decode_and_verify/3, get_key_id/1, get_aud/1]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("jose/include/jose_jwt.hrl"). -include_lib("jose/include/jose_jws.hrl"). +<<<<<<< HEAD decode_and_verify(OauthProviderId, Jwk, Token) -> Verify = case rabbit_oauth2_config:get_algorithms(OauthProviderId) of undefined -> jose_jwt:verify(Jwk, Token); Algs -> jose_jwt:verify_strict(Jwk, Algs, Token) end, +======= +-spec decode_and_verify(list() | undefined, map(), binary()) -> {boolean(), map()}. +decode_and_verify(Algs, Jwk, Token) -> + Verify = case Algs of + undefined -> jose_jwt:verify(Jwk, Token); + _ -> jose_jwt:verify_strict(Jwk, Algs, Token) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) case Verify of {true, #jose_jwt{fields = Fields}, _} -> {true, Fields}; {false, #jose_jwt{fields = Fields}, _} -> {false, Fields} end. +<<<<<<< HEAD get_key_id(DefaultKey, Token) -> try case jose_jwt:peek_protected(Token) of #jose_jws{fields = #{<<"kid">> := Kid}} -> {ok, Kid}; #jose_jws{} -> DefaultKey +======= +get_key_id(Token) -> + try + case jose_jwt:peek_protected(Token) of + #jose_jws{fields = #{<<"kid">> := Kid}} -> {ok, Kid}; + #jose_jws{} -> undefined +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end catch Type:Err:Stacktrace -> {error, {invalid_token, Type, Err, Stacktrace}} diff --git a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets index 08ecdb9dec77..261c0620bcc8 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets +++ b/deps/rabbitmq_auth_backend_oauth2/test/config_schema_SUITE_data/rabbitmq_auth_backend_oauth2.snippets @@ -11,6 +11,10 @@ auth_oauth2.default_key = id1 auth_oauth2.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem +<<<<<<< HEAD +======= + auth_oauth2.jwks_uri = https://my-jwt-issuer/jwks.json +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json auth_oauth2.issuer = https://my-jwt-issuer auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem @@ -18,6 +22,11 @@ auth_oauth2.https.depth = 5 auth_oauth2.https.fail_if_no_peer_cert = false auth_oauth2.https.hostname_verification = wildcard +<<<<<<< HEAD +======= + auth_oauth2.discovery_endpoint_path = /.well-known/openid-configuration + auth_oauth2.discovery_endpoint_params.param1 = value1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_oauth2.https.crl_check = true auth_oauth2.algorithms.1 = HS256 auth_oauth2.algorithms.2 = RS256", @@ -30,6 +39,14 @@ {preferred_username_claims, [<<"user_name">>, <<"username">>, <<"email">>]}, {verify_aud, true}, {issuer, "https://my-jwt-issuer"}, +<<<<<<< HEAD +======= + {discovery_endpoint_path, "/.well-known/openid-configuration"}, + {discovery_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, + {jwks_uri, "https://my-jwt-issuer/jwks.json"}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {key_config, [ {default_key, <<"id1">>}, {signing_keys, @@ -63,6 +80,10 @@ auth_oauth2.default_key = id1 auth_oauth2.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem auth_oauth2.signing_keys.id2 = test/config_schema_SUITE_data/certs/cert.pem +<<<<<<< HEAD +======= + auth_oauth2.jwks_uri = https://my-jwt-issuer/jwks.json +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_oauth2.jwks_url = https://my-jwt-issuer/jwks.json auth_oauth2.https.cacertfile = test/config_schema_SUITE_data/certs/cacert.pem auth_oauth2.https.peer_verification = verify_none @@ -84,6 +105,10 @@ {extra_scopes_source, <<"my_custom_scope_key">>}, {preferred_username_claims, [<<"user_name">>, <<"username">>, <<"email">>]}, {verify_aud, true}, +<<<<<<< HEAD +======= + {jwks_uri, "https://my-jwt-issuer/jwks.json"}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {resource_servers, #{ <<"rabbitmq-operations">> => [ @@ -136,6 +161,11 @@ auth_oauth2.oauth_providers.keycloak.https.depth = 2 auth_oauth2.oauth_providers.keycloak.default_key = token-key auth_oauth2.oauth_providers.keycloak.signing_keys.id1 = test/config_schema_SUITE_data/certs/key.pem +<<<<<<< HEAD +======= + auth_oauth2.oauth_providers.keycloak.discovery_endpoint_path = /.well-known/openid-configuration + auth_oauth2.oauth_providers.keycloak.discovery_endpoint_params.param1 = value1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) auth_oauth2.oauth_providers.keycloak.algorithms.1 = HS256 auth_oauth2.oauth_providers.keycloak.algorithms.2 = RS256", [ @@ -160,6 +190,7 @@ {cacertfile, "test/config_schema_SUITE_data/certs/cacert.pem"} ]}, {algorithms, [<<"HS256">>, <<"RS256">>]}, +<<<<<<< HEAD {default_key, <<"token-key">>}, {end_session_endpoint, <<"https://keycloak/logout">>}, {authorization_endpoint, <<"https://keycloak/authorize">>}, @@ -168,6 +199,20 @@ ], <<"uaa">> => [ {issuer, <<"https://uaa">>} +======= + {discovery_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, + {discovery_endpoint_path, "/.well-known/openid-configuration"}, + {default_key, <<"token-key">>}, + {end_session_endpoint, "https://keycloak/logout"}, + {authorization_endpoint, "https://keycloak/authorize"}, + {jwks_uri, "https://keycloak/keys"}, + {token_endpoint, "https://keycloak/token"} + ], + <<"uaa">> => [ + {issuer, "https://uaa"} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ] } @@ -184,5 +229,124 @@ {scope_prefix,<<>>} ]} ],[] +<<<<<<< HEAD +======= + }, + {scope_aliases_1, + "auth_oauth2.resource_server_id = new_resource_server_id + auth_oauth2.scope_aliases.admin = rabbitmq.tag:administrator + auth_oauth2.scope_aliases.developer = rabbitmq.tag:management rabbitmq.read:*/*", + [ + {rabbitmq_auth_backend_oauth2, [ + {resource_server_id,<<"new_resource_server_id">>}, + {scope_aliases, #{ + <<"admin">> => [ + <<"rabbitmq.tag:administrator">> + ], + <<"developer">> => [ + <<"rabbitmq.tag:management">>, + <<"rabbitmq.read:*/*">> + ] + }} + ]} + ], [] + }, + {scope_aliases_2, + "auth_oauth2.resource_server_id = new_resource_server_id + auth_oauth2.scope_aliases.1.alias = admin + auth_oauth2.scope_aliases.1.scope = rabbitmq.tag:administrator + auth_oauth2.scope_aliases.2.alias = developer + auth_oauth2.scope_aliases.2.scope = rabbitmq.tag:management rabbitmq.read:*/*", + [ + {rabbitmq_auth_backend_oauth2, [ + {resource_server_id,<<"new_resource_server_id">>}, + {scope_aliases, #{ + <<"admin">> => [ + <<"rabbitmq.tag:administrator">> + ], + <<"developer">> => [ + <<"rabbitmq.tag:management">>, + <<"rabbitmq.read:*/*">> + ] + }} + ]} + ], [] + }, + {scope_aliases_3, + "auth_oauth2.resource_server_id = new_resource_server_id + auth_oauth2.resource_servers.a.scope_aliases.admin = rabbitmq.tag:administrator + auth_oauth2.resource_servers.a.scope_aliases.developer = rabbitmq.tag:management rabbitmq.read:*/* + auth_oauth2.resource_servers.b.scope_aliases.admin_b = rabbitmq.tag:administrator + auth_oauth2.resource_servers.b.scope_aliases.developer_b = rabbitmq.tag:management rabbitmq.read:*/*", + [ + {rabbitmq_auth_backend_oauth2, [ + {resource_server_id,<<"new_resource_server_id">>}, + {resource_servers, #{ + <<"a">> => [ + {scope_aliases, #{ + <<"admin">> => [ + <<"rabbitmq.tag:administrator">> + ], + <<"developer">> => [ + <<"rabbitmq.tag:management">>, + <<"rabbitmq.read:*/*">> + ] + }}, + {id, <<"a">>} + ], + <<"b">> => [ + {scope_aliases, #{ + <<"admin_b">> => [ + <<"rabbitmq.tag:administrator">> + ], + <<"developer_b">> => [ + <<"rabbitmq.tag:management">>, + <<"rabbitmq.read:*/*">> + ] + }}, + {id, <<"b">>} + ] + } + } + ]} + ], [] + }, + {scope_aliases_4, + "auth_oauth2.resource_server_id = new_resource_server_id + auth_oauth2.resource_servers.b.scope_aliases.1.alias = admin_b + auth_oauth2.resource_servers.b.scope_aliases.1.scope = rabbitmq.tag:administrator + auth_oauth2.resource_servers.a.scope_aliases.1.alias = admin + auth_oauth2.resource_servers.a.scope_aliases.1.scope = rabbitmq.tag:administrator + auth_oauth2.resource_servers.a.scope_aliases.2.alias = developer + auth_oauth2.resource_servers.a.scope_aliases.2.scope = rabbitmq.tag:management rabbitmq.read:*/*", + [ + {rabbitmq_auth_backend_oauth2, [ + {resource_server_id,<<"new_resource_server_id">>}, + {resource_servers, #{ + <<"a">> => [ + {scope_aliases, #{ + <<"admin">> => [ + <<"rabbitmq.tag:administrator">> + ], + <<"developer">> => [ + <<"rabbitmq.tag:management">>, + <<"rabbitmq.read:*/*">> + ] + }}, + {id, <<"a">>} + ], + <<"b">> => [ + {scope_aliases, #{ + <<"admin_b">> => [ + <<"rabbitmq.tag:administrator">> + ] + }}, + {id, <<"b">>} + ] + } + } + ]} + ], [] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } ]. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl index bc1256da8b9d..d5537dacff0c 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl @@ -13,14 +13,33 @@ -include_lib("amqp_client/include/amqp_client.hrl"). -include_lib("eunit/include/eunit.hrl"). +<<<<<<< HEAD -import(rabbit_ct_client_helpers, [close_connection/1, close_channel/1, open_unmanaged_connection/4, open_unmanaged_connection/5, close_connection_and_channel/2]). -import(rabbit_mgmt_test_util, [amqp_port/1]). +======= +-import(rabbit_ct_client_helpers, [ + close_connection/1, + close_channel/1, + open_unmanaged_connection/4, + open_unmanaged_connection/5, + close_connection_and_channel/2 +]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -import(rabbit_ct_helpers, [ set_config/2, get_config/2, get_config/3 ]). +<<<<<<< HEAD +======= +-import(rabbit_ct_broker_helpers, [ + rpc/5 +]). +-import(rabbit_mgmt_test_util, [ + amqp_port/1 +]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [ @@ -164,6 +183,7 @@ end_per_suite(Config) -> ] ++ rabbit_ct_broker_helpers:teardown_steps()). init_per_group(no_peer_verification, Config) -> +<<<<<<< HEAD KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(non_strict_jwks_url, Config)}, {peer_verification, verify_none}]), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); @@ -180,6 +200,23 @@ init_per_group(with_resource_servers_rabbitmq1_with_oauth_provider_A, Config) -> ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, resource_servers, ResourceServersConfig1]); +======= + KeyConfig = set_config(?config(key_config, Config), [ + {jwks_url, ?config(non_strict_jwks_uri, Config)}, + {peer_verification, verify_none} + ]), + ok = rpc_set_env(Config, key_config, KeyConfig), + set_config(Config, {key_config, KeyConfig}); +init_per_group(without_kid, Config) -> + set_config(Config, [{include_kid, false}]); +init_per_group(with_resource_servers_rabbitmq1_with_oauth_provider_A, Config) -> + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), + Resource0 = maps:get(<<"rabbitmq1">>, ResourceServersConfig0, + [{id, <<"rabbitmq1">>}]), + ResourceServersConfig1 = maps:put(<<"rabbitmq1">>, + [{oauth_provider_id, <<"A">>} | Resource0], ResourceServersConfig0), + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_group(with_oauth_providers_A_B_and_C, Config) -> OAuthProviders = #{ <<"A">> => [ @@ -195,6 +232,7 @@ init_per_group(with_oauth_providers_A_B_and_C, Config) -> {https, [{verify, verify_none}]} ] }, +<<<<<<< HEAD ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders]), Config; @@ -235,11 +273,44 @@ init_per_group(with_resource_servers_rabbitmq2, Config) -> init_per_group(with_oauth_providers_B_with_default_key_static_key, Config) -> {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, [rabbitmq_auth_backend_oauth2, oauth_providers]), +======= + ok = rpc_set_env(Config, oauth_providers, OAuthProviders), + Config; +init_per_group(with_default_oauth_provider_B, Config) -> + ok = rpc_set_env(Config, default_oauth_provider, <<"B">>); +init_per_group(with_oauth_providers_A_with_default_key, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), + OAuthProvider = maps:get(<<"A">>, OAuthProviders0, []), + OAuthProviders1 = maps:put(<<"A">>, [ + {default_key, ?UTIL_MOD:token_key(?config(fixture_jwksA, Config))} + | OAuthProvider], OAuthProviders0), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), + Config; +init_per_group(with_oauth_provider_A_with_jwks_with_one_signing_key, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), + OAuthProvider = maps:get(<<"A">>, OAuthProviders0, []), + OAuthProviders1 = maps:put(<<"A">>, [ + {jwks_uri, strict_jwks_uri(Config, "/jwksA")} | OAuthProvider], + OAuthProviders0), + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), + Config; +init_per_group(with_resource_servers_rabbitmq2, Config) -> + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), + Resource0 = maps:get(<<"rabbitmq2">>, ResourceServersConfig0, + [{id, <<"rabbitmq2">>}]), + ResourceServersConfig1 = maps:put(<<"rabbitmq2">>, Resource0, + ResourceServersConfig0), + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1), + Config; +init_per_group(with_oauth_providers_B_with_default_key_static_key, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider = maps:get(<<"B">>, OAuthProviders0, []), OAuthProviders1 = maps:put(<<"B">>, [ {default_key, ?UTIL_MOD:token_key(?config(fixture_staticB, Config))} | proplists:delete(default_key, OAuthProvider)], OAuthProviders0), +<<<<<<< HEAD ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), @@ -247,6 +318,12 @@ init_per_group(with_oauth_providers_B_with_default_key_static_key, Config) -> init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, [rabbitmq_auth_backend_oauth2, oauth_providers]), +======= + ok = rpc_set_env(Config,oauth_providers, OAuthProviders1), + Config; +init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider = maps:get(<<"C">>, OAuthProviders0, []), Jwks1 = ?config(fixture_staticC_1, Config), Jwks2 = ?config(fixture_staticC_2, Config), @@ -254,6 +331,7 @@ init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> ?UTIL_MOD:token_key(Jwks1) => {json, Jwks1}, ?UTIL_MOD:token_key(Jwks2) => {json, Jwks2} }, +<<<<<<< HEAD OAuthProviders1 = maps:put(<<"C">>, [{signing_keys, SigningKeys} | OAuthProvider], OAuthProviders0), @@ -264,6 +342,15 @@ init_per_group(with_oauth_provider_C_with_two_static_keys, Config) -> init_per_group(with_root_oauth_provider_with_two_static_keys_and_one_jwks_key, Config) -> KeyConfig = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, [rabbitmq_auth_backend_oauth2, key_config, []]), +======= + OAuthProviders1 = maps:put(<<"C">>, [ + {signing_keys, SigningKeys} | OAuthProvider], OAuthProviders0), + + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), + Config; +init_per_group(with_root_oauth_provider_with_two_static_keys_and_one_jwks_key, Config) -> + KeyConfig = rpc_get_env(Config, key_config, []), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Jwks1 = ?config(fixture_static_1, Config), Jwks2 = ?config(fixture_static_2, Config), SigningKeys = #{ @@ -271,6 +358,7 @@ init_per_group(with_root_oauth_provider_with_two_static_keys_and_one_jwks_key, C ?UTIL_MOD:token_key(Jwks2) => {json, Jwks2} }, KeyConfig1 = [{signing_keys, SigningKeys}, +<<<<<<< HEAD {jwks_url, strict_jwks_url(Config, "/jwks")}| KeyConfig], ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), @@ -294,6 +382,27 @@ init_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> init_per_group(with_oauth_provider_B_with_one_static_key_and_jwks_with_two_signing_keys, Config) -> {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, [rabbitmq_auth_backend_oauth2, oauth_providers]), +======= + {jwks_url, strict_jwks_uri(Config, "/jwks")}| KeyConfig], + ok = rpc_set_env(Config, key_config, KeyConfig1), + Config; +init_per_group(with_root_oauth_provider_with_default_key_1, Config) -> + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = [ + {default_key, ?UTIL_MOD:token_key(?config(fixture_static_1, Config))} + | KeyConfig], + ok = rpc_set_env(Config, key_config, KeyConfig1), + Config; +init_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = [ + {default_key, ?UTIL_MOD:token_key(?config(fixture_jwk, Config))} + | KeyConfig], + ok = rpc_set_env(Config, key_config, KeyConfig1), + Config; +init_per_group(with_oauth_provider_B_with_one_static_key_and_jwks_with_two_signing_keys, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider = maps:get(<<"B">>, OAuthProviders0, []), Jwks = ?config(fixture_staticB, Config), SigningKeys = #{ @@ -301,6 +410,7 @@ init_per_group(with_oauth_provider_B_with_one_static_key_and_jwks_with_two_signi }, OAuthProviders1 = maps:put(<<"B">>, [ {signing_keys, SigningKeys}, +<<<<<<< HEAD {jwks_uri, strict_jwks_url(Config, "/jwksB")} | OAuthProvider], OAuthProviders0), @@ -321,11 +431,28 @@ init_per_group(with_resource_servers_rabbitmq3_with_oauth_provider_C, Config) -> init_per_group(with_oauth_providers_C_with_default_key_static_key_1, Config) -> {ok, OAuthProviders0} = rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, [rabbitmq_auth_backend_oauth2, oauth_providers]), +======= + {jwks_uri, strict_jwks_uri(Config, "/jwksB")} | OAuthProvider], + OAuthProviders0), + + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), + Config; +init_per_group(with_resource_servers_rabbitmq3_with_oauth_provider_C, Config) -> + ResourceServersConfig0 = rpc_get_env(Config, resource_servers, #{}), + Resource0 = maps:get(<<"rabbitmq3">>, ResourceServersConfig0, [ + {id, <<"rabbitmq3">>},{oauth_provider_id, <<"C">>}]), + ResourceServersConfig1 = maps:put(<<"rabbitmq3">>, Resource0, + ResourceServersConfig0), + ok = rpc_set_env(Config, resource_servers, ResourceServersConfig1); +init_per_group(with_oauth_providers_C_with_default_key_static_key_1, Config) -> + {ok, OAuthProviders0} = rpc_get_env(Config, oauth_providers), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) OAuthProvider = maps:get(<<"C">>, OAuthProviders0, []), Jwks = ?config(fixture_staticC_1, Config), OAuthProviders1 = maps:put(<<"C">>, [ {default_key, ?UTIL_MOD:token_key(Jwks)} | OAuthProvider], OAuthProviders0), +<<<<<<< HEAD ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, oauth_providers, OAuthProviders1]), @@ -334,12 +461,19 @@ init_per_group(with_oauth_providers_C_with_default_key_static_key_1, Config) -> init_per_group(_Group, Config) -> ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, resource_server_id, ?RESOURCE_SERVER_ID]), +======= + ok = rpc_set_env(Config, oauth_providers, OAuthProviders1), + Config; +init_per_group(_Group, Config) -> + ok = rpc_set_env(Config, resource_server_id, ?RESOURCE_SERVER_ID), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config. end_per_group(without_kid, Config) -> rabbit_ct_helpers:delete_config(Config, include_kid); end_per_group(no_peer_verification, Config) -> +<<<<<<< HEAD KeyConfig = rabbit_ct_helpers:set_config(?config(key_config, Config), [{jwks_url, ?config(strict_jwks_url, Config)}, {peer_verification, verify_peer}]), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig]), rabbit_ct_helpers:set_config(Config, {key_config, KeyConfig}); @@ -361,6 +495,26 @@ end_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> KeyConfig1 = proplists:delete(default_key, KeyConfig), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, KeyConfig1]), +======= + KeyConfig = set_config(?config(key_config, Config), [ + {jwks_uri, ?config(strict_jwks_uri, Config)}, + {peer_verification, verify_peer}]), + ok = rpc_set_env(Config, key_config, KeyConfig), + set_config(Config, {key_config, KeyConfig}); + +end_per_group(with_default_oauth_provider_B, Config) -> + ok = rpc_unset_env(Config, default_oauth_provider); + +end_per_group(with_root_oauth_provider_with_default_key_1, Config) -> + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = proplists:delete(default_key, KeyConfig), + ok = rpc_set_env(Config, key_config, KeyConfig1), + Config; +end_per_group(with_root_oauth_provider_with_default_jwks_key, Config) -> + KeyConfig = rpc_get_env(Config, key_config, []), + KeyConfig1 = proplists:delete(default_key, KeyConfig), + ok = rpc_set_env(Config, key_config, KeyConfig1), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; end_per_group(_Group, Config) -> @@ -368,27 +522,50 @@ end_per_group(_Group, Config) -> add_vhosts(Config) -> %% The broker is managed by {init,end}_per_testcase(). +<<<<<<< HEAD lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:add_vhost(Config, Value) end, [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). +======= + lists:foreach(fun(Value) -> + rabbit_ct_broker_helpers:add_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %rabbit_ct_helpers:set_config(Config, []). delete_vhosts(Config) -> %% The broker is managed by {init,end}_per_testcase(). +<<<<<<< HEAD lists:foreach(fun(Value) -> rabbit_ct_broker_helpers:delete_vhost(Config, Value) end, [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost orelse Testcase =:= test_successful_token_refresh -> +======= + lists:foreach(fun(Value) -> + rabbit_ct_broker_helpers:delete_vhost(Config, Value) end, + [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]). + +init_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost orelse + Testcase =:= test_successful_token_refresh -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost1">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +<<<<<<< HEAD init_per_testcase(Testcase, Config) when Testcase =:= test_failed_token_refresh_case1 orelse Testcase =:= test_failed_token_refresh_case2 -> +======= +init_per_testcase(Testcase, Config) when + Testcase =:= test_failed_token_refresh_case1 orelse + Testcase =:= test_failed_token_refresh_case2 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:add_vhost(Config, <<"vhost4">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +<<<<<<< HEAD init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> @@ -406,6 +583,27 @@ init_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection init_per_testcase(Testcase, Config) when Testcase =:= test_failed_connection_with_algorithm_restriction -> KeyConfig = ?config(key_config, Config), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, [{algorithms, [<<"RS256">>]} | KeyConfig]]), +======= +init_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> + ok = rpc_set_env(Config, extra_scopes_source, ?EXTRA_SCOPES_SOURCE), + rabbit_ct_helpers:testcase_started(Config, Testcase), + Config; + +init_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_algorithm_restriction -> + KeyConfig = ?config(key_config, Config), + ok = rpc_set_env(Config, key_config, [{algorithms, [<<"HS256">>]} | KeyConfig]), + rabbit_ct_helpers:testcase_started(Config, Testcase), + Config; + +init_per_testcase(Testcase, Config) when + Testcase =:= test_failed_connection_with_algorithm_restriction -> + KeyConfig = ?config(key_config, Config), + ok = rpc_set_env(Config, key_config, [{algorithms, [<<"RS256">>]} | KeyConfig]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_helpers:testcase_started(Config, Testcase), Config; @@ -413,25 +611,46 @@ init_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_started(Config, Testcase), Config. +<<<<<<< HEAD end_per_testcase(Testcase, Config) when Testcase =:= test_failed_token_refresh_case1 orelse Testcase =:= test_failed_token_refresh_case2 -> +======= +end_per_testcase(Testcase, Config) when + Testcase =:= test_failed_token_refresh_case1 orelse + Testcase =:= test_failed_token_refresh_case2 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost4">>), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +<<<<<<< HEAD end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> +======= +end_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_complex_claim_as_a_map orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_list orelse + Testcase =:= test_successful_connection_with_complex_claim_as_a_binary -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, unset_env, [rabbitmq_auth_backend_oauth2, extra_scopes_source]), rabbit_ct_helpers:testcase_started(Config, Testcase), Config; +<<<<<<< HEAD end_per_testcase(Testcase, Config) when Testcase =:= test_successful_connection_with_algorithm_restriction orelse Testcase =:= test_failed_connection_with_algorithm_restriction -> rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, key_config, ?config(key_config, Config)]), +======= +end_per_testcase(Testcase, Config) when + Testcase =:= test_successful_connection_with_algorithm_restriction orelse + Testcase =:= test_failed_connection_with_algorithm_restriction -> + rabbit_ct_broker_helpers:delete_vhost(Config, <<"vhost1">>), + ok = rpc_set_env(Config, key_config, ?config(key_config, Config)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_helpers:testcase_finished(Config, Testcase), Config; @@ -441,10 +660,16 @@ end_per_testcase(Testcase, Config) -> Config. preconfigure_node(Config) -> +<<<<<<< HEAD ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbit, auth_backends, [rabbit_auth_backend_oauth2]]), ok = rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, [rabbitmq_auth_backend_oauth2, resource_server_id, ?RESOURCE_SERVER_ID]), +======= + ok = rpc(Config, 0, application, set_env, + [rabbit, auth_backends, [rabbit_auth_backend_oauth2]]), + ok = rpc_set_env(Config, resource_server_id, ?RESOURCE_SERVER_ID), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) add_vhosts(Config), Config. @@ -461,12 +686,21 @@ start_jwks_server(Config0) -> %% Assume we don't have more than 100 ports allocated for tests PortBase = rabbit_ct_broker_helpers:get_node_config(Config0, 0, tcp_ports_base), JwksServerPort = PortBase + 100, +<<<<<<< HEAD Config = rabbit_ct_helpers:set_config(Config0, [{jwksServerPort, JwksServerPort}]), %% Both URLs direct to the same JWKS server %% The NonStrictJwksUrl identity cannot be validated while StrictJwksUrl identity can be validated NonStrictJwksUrl = non_strict_jwks_url(Config), StrictJwksUrl = strict_jwks_url(Config), +======= + Config = set_config(Config0, [{jwksServerPort, JwksServerPort}]), + + %% Both URLs direct to the same JWKS server + %% The NonStrictJwksUrl identity cannot be validated while StrictJwksUrl identity can be validated + NonStrictJwksUri = non_strict_jwks_uri(Config), + StrictJwksUri = strict_jwks_uri(Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, _} = application:ensure_all_started(ssl), {ok, _} = application:ensure_all_started(cowboy), @@ -479,6 +713,7 @@ start_jwks_server(Config0) -> {"/jwks1", [Jwk1, Jwk3]}, {"/jwks2", [Jwk2]} ]), +<<<<<<< HEAD KeyConfig = [{jwks_url, StrictJwksUrl}, {peer_verification, verify_peer}, {cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])}], @@ -508,6 +743,35 @@ strict_jwks_url(Config, Path) -> non_strict_jwks_url(Config) -> non_strict_jwks_url(Config, "/jwks"). non_strict_jwks_url(Config, Path) -> +======= + KeyConfig = [{jwks_url, StrictJwksUri}, + {peer_verification, verify_peer}, + {cacertfile, filename:join([CertsDir, "testca", "cacert.pem"])}], + ok = rpc_set_env(Config, key_config, KeyConfig), + set_config(Config, [ + {non_strict_jwks_uri, NonStrictJwksUri}, + {strict_jwks_uri, StrictJwksUri}, + {key_config, KeyConfig}, + {fixture_static_1, Jwk7}, + {fixture_static_2, Jwk8}, + {fixture_staticB, Jwk4}, + {fixture_staticC_1, Jwk5}, + {fixture_staticC_2, Jwk6}, + {fixture_jwksB_1, Jwk1}, + {fixture_jwksB_2, Jwk3}, + {fixture_jwksA, Jwk}, + {fixture_jwk, Jwk}, + {fixture_jwks_1, [Jwk1, Jwk3]}, + {fixture_jwks_2, [Jwk2]} + ]). +strict_jwks_uri(Config) -> + strict_jwks_uri(Config, "/jwks"). +strict_jwks_uri(Config, Path) -> + "https://localhost:" ++ integer_to_list(?config(jwksServerPort, Config)) ++ Path. +non_strict_jwks_uri(Config) -> + non_strict_jwks_uri(Config, "/jwks"). +non_strict_jwks_uri(Config, Path) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "https://127.0.0.1:" ++ integer_to_list(?config(jwksServerPort, Config)) ++ Path. @@ -522,16 +786,31 @@ generate_valid_token(Config, Scopes) -> generate_valid_token(Config, Scopes, undefined). generate_valid_token(Config, Scopes, Audience) -> +<<<<<<< HEAD Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of +======= + Jwk = + case get_config(Config, fixture_jwk) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) undefined -> ?UTIL_MOD:fixture_jwk(); Value -> Value end, generate_valid_token(Config, Jwk, Scopes, Audience). generate_valid_token(Config, Jwk, Scopes, Audience) -> +<<<<<<< HEAD Token = case Audience of undefined -> ?UTIL_MOD:fixture_token_with_scopes(Scopes); DefinedAudience -> maps:put(<<"aud">>, DefinedAudience, ?UTIL_MOD:fixture_token_with_scopes(Scopes)) +======= + Token = + case Audience of + undefined -> + ?UTIL_MOD:fixture_token_with_scopes(Scopes); + DefinedAudience -> + maps:put(<<"aud">>, DefinedAudience, + ?UTIL_MOD:fixture_token_with_scopes(Scopes)) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end, IncludeKid = rabbit_ct_helpers:get_config(Config, include_kid, true), ?UTIL_MOD:sign_token_hs(Token, Jwk, IncludeKid). @@ -542,28 +821,50 @@ generate_valid_token_with_sub(Config, Jwk, Scopes, Sub) -> ?UTIL_MOD:sign_token_hs(Token, Jwk, IncludeKid). generate_valid_token_with_extra_fields(Config, ExtraFields) -> +<<<<<<< HEAD Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of undefined -> ?UTIL_MOD:fixture_jwk(); Value -> Value end, Token = maps:merge(?UTIL_MOD:fixture_token_with_scopes([]), ExtraFields), ?UTIL_MOD:sign_token_hs(Token, Jwk, rabbit_ct_helpers:get_config(Config, include_kid, true)). +======= + Jwk = + case rabbit_ct_helpers:get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, + Token = maps:merge(?UTIL_MOD:fixture_token_with_scopes([]), ExtraFields), + ?UTIL_MOD:sign_token_hs(Token, Jwk, + rabbit_ct_helpers:get_config(Config, include_kid, true)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) generate_expired_token(Config) -> generate_expired_token(Config, ?UTIL_MOD:full_permission_scopes()). generate_expired_token(Config, Scopes) -> +<<<<<<< HEAD Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of undefined -> ?UTIL_MOD:fixture_jwk(); Value -> Value end, ?UTIL_MOD:sign_token_hs(?UTIL_MOD:expired_token_with_scopes(Scopes), Jwk, rabbit_ct_helpers:get_config(Config, include_kid, true)). +======= + Jwk = + case get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, + ?UTIL_MOD:sign_token_hs(?UTIL_MOD:expired_token_with_scopes(Scopes), Jwk, + get_config(Config, include_kid, true)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) generate_expirable_token(Config, Seconds) -> generate_expirable_token(Config, ?UTIL_MOD:full_permission_scopes(), Seconds). generate_expirable_token(Config, Scopes, Seconds) -> +<<<<<<< HEAD Jwk = case rabbit_ct_helpers:get_config(Config, fixture_jwk) of undefined -> ?UTIL_MOD:fixture_jwk(); Value -> Value @@ -575,6 +876,20 @@ generate_expirable_token(Config, Scopes, Seconds) -> preconfigure_token(Config) -> Token = generate_valid_token(Config), rabbit_ct_helpers:set_config(Config, {fixture_jwt, Token}). +======= + Jwk = + case get_config(Config, fixture_jwk) of + undefined -> ?UTIL_MOD:fixture_jwk(); + Value -> Value + end, + Expiration = os:system_time(seconds) + Seconds, + ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_scopes_and_expiration( + Scopes, Expiration), Jwk, get_config(Config, include_kid, true)). + +preconfigure_token(Config) -> + Token = generate_valid_token(Config), + set_config(Config, {fixture_jwt, Token}). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @@ -692,7 +1007,11 @@ test_unsuccessful_connection_for_rabbitmq_audience_signed_by_root_oauth_provider ?assertMatch({error, {auth_failure, _}}, open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token)). test_successful_connection_with_a_full_permission_token_and_all_defaults(Config) -> +<<<<<<< HEAD {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), +======= + {_Algo, Token} = get_config(Config, fixture_jwt), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) verify_queue_declare_with_token(Config, Token). verify_queue_declare_with_token(Config, Token) -> @@ -744,10 +1063,19 @@ test_successful_queue_declaration_using_multiple_keys_and_audiences(Config) -> test_successful_connection_with_a_full_permission_token_and_explicitly_configured_vhost(Config) -> +<<<<<<< HEAD {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost1/*">>, <<"rabbitmq.write:vhost1/*">>, <<"rabbitmq.read:vhost1/*">>]), Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, Token), +======= + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, <<"username">>, + Token), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), @@ -768,7 +1096,17 @@ test_successful_connection_with_simple_strings_for_aud_and_scope(Config) -> test_successful_connection_with_complex_claim_as_a_map(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, +<<<<<<< HEAD #{<<"additional_rabbitmq_scopes">> => #{<<"rabbitmq">> => [<<"configure:*/*">>, <<"read:*/*">>, <<"write:*/*">>]}} +======= + #{<<"additional_rabbitmq_scopes">> => #{ + <<"rabbitmq">> => [ + <<"configure:*/*">>, + <<"read:*/*">>, + <<"write:*/*">> + ]} + } +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -779,7 +1117,15 @@ test_successful_connection_with_complex_claim_as_a_map(Config) -> test_successful_connection_with_complex_claim_as_a_list(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, +<<<<<<< HEAD #{<<"additional_rabbitmq_scopes">> => [<<"rabbitmq.configure:*/*">>, <<"rabbitmq.read:*/*">>, <<"rabbitmq.write:*/*">>]} +======= + #{<<"additional_rabbitmq_scopes">> => [ + <<"rabbitmq.configure:*/*">>, + <<"rabbitmq.read:*/*">>, + <<"rabbitmq.write:*/*">> + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -790,7 +1136,12 @@ test_successful_connection_with_complex_claim_as_a_list(Config) -> test_successful_connection_with_complex_claim_as_a_binary(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, +<<<<<<< HEAD #{<<"additional_rabbitmq_scopes">> => <<"rabbitmq.configure:*/* rabbitmq.read:*/* rabbitmq.write:*/*">>} +======= + #{<<"additional_rabbitmq_scopes">> => + <<"rabbitmq.configure:*/* rabbitmq.read:*/* rabbitmq.write:*/*">>} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -825,6 +1176,7 @@ test_successful_connection_with_keycloak_token(Config) -> test_successful_token_refresh(Config) -> Duration = 5, +<<<<<<< HEAD {_Algo, Token} = generate_expirable_token(Config, [<<"rabbitmq.configure:vhost1/*">>, <<"rabbitmq.write:vhost1/*">>, <<"rabbitmq.read:vhost1/*">>], @@ -844,11 +1196,36 @@ test_successful_token_refresh(Config) -> amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch2, #'queue.declare'{exclusive = true}), +======= + {_Algo, Token} = generate_expirable_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">> + ], Duration), + Conn = open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + + {_Algo2, Token2} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), + ?UTIL_MOD:wait_for_token_to_expire(timer:seconds(Duration)), + ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, + <<"token refresh">>)), + {ok, Ch2} = amqp_connection:open_channel(Conn), + + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, + #'queue.declare'{exclusive = true}), + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch2, + #'queue.declare'{exclusive = true}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) amqp_channel:close(Ch2), close_connection_and_channel(Conn, Ch). test_successful_connection_with_algorithm_restriction(Config) -> +<<<<<<< HEAD {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), {ok, Ch} = amqp_connection:open_channel(Conn), @@ -889,15 +1266,77 @@ test_failed_token_refresh_case1(Config) -> <<"rabbitmq.write:vhost4/*">>, <<"rabbitmq.read:vhost4/*">>]), Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, Token), +======= + {_Algo, Token} = get_config(Config, fixture_jwt), + Conn = open_unmanaged_connection(Config, 0, <<"username">>, Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, + #'queue.declare'{exclusive = true}), + close_connection_and_channel(Conn, Ch). + +test_failed_connection_with_expired_token(Config) -> + {_Algo, Token} = generate_expired_token(Config, [ + <<"rabbitmq.configure:vhost1/*">>, + <<"rabbitmq.write:vhost1/*">>, + <<"rabbitmq.read:vhost1/*">>]), + ?assertMatch({error, {auth_failure, _}}, + open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, Token)). + +test_failed_connection_with_a_non_token(Config) -> + ?assertMatch({error, {auth_failure, _}}, + open_unmanaged_connection(Config, 0, <<"vhost1">>, + <<"username">>, <<"a-non-token-value">>)). + +test_failed_connection_with_a_token_with_insufficient_vhost_permission(Config) -> + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:alt-vhost/*">>, + <<"rabbitmq.write:alt-vhost/*">>, + <<"rabbitmq.read:alt-vhost/*">>]), + ?assertEqual({error, not_allowed}, + open_unmanaged_connection(Config, 0, <<"off-limits-vhost">>, + <<"username">>, Token)). + +test_failed_connection_with_a_token_with_insufficient_resource_permission(Config) -> + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost2/jwt*">>, + <<"rabbitmq.write:vhost2/jwt*">>, + <<"rabbitmq.read:vhost2/jwt*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost2">>, <<"username">>, + Token), + {ok, Ch} = amqp_connection:open_channel(Conn), + ?assertExit({{shutdown, {server_initiated_close, 403, _}}, _}, + amqp_channel:call(Ch, #'queue.declare'{queue = <<"alt-prefix.eq.1">>, + exclusive = true})), + close_connection(Conn). + +test_failed_token_refresh_case1(Config) -> + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, + Token), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), +<<<<<<< HEAD {_Algo2, Token2} = generate_expired_token(Config, [<<"rabbitmq.configure:vhost4/*">>, <<"rabbitmq.write:vhost4/*">>, <<"rabbitmq.read:vhost4/*">>]), %% the error is communicated asynchronously via a connection-level error ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, <<"token refresh">>)), +======= + {_Algo2, Token2} = generate_expired_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), + %% the error is communicated asynchronously via a connection-level error + ?assertEqual(ok, amqp_connection:update_secret(Conn, Token2, + <<"token refresh">>)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Ch2} = amqp_connection:open_channel(Conn), ?assertExit({{shutdown, {server_initiated_close, 403, _}}, _}, @@ -906,16 +1345,30 @@ test_failed_token_refresh_case1(Config) -> close_connection(Conn). test_failed_token_refresh_case2(Config) -> +<<<<<<< HEAD {_Algo, Token} = generate_valid_token(Config, [<<"rabbitmq.configure:vhost4/*">>, <<"rabbitmq.write:vhost4/*">>, <<"rabbitmq.read:vhost4/*">>]), Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, <<"username">>, Token), +======= + {_Algo, Token} = generate_valid_token(Config, [ + <<"rabbitmq.configure:vhost4/*">>, + <<"rabbitmq.write:vhost4/*">>, + <<"rabbitmq.read:vhost4/*">>]), + Conn = open_unmanaged_connection(Config, 0, <<"vhost4">>, + <<"username">>, Token), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, Ch} = amqp_connection:open_channel(Conn), #'queue.declare_ok'{queue = _} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), %% the error is communicated asynchronously via a connection-level error +<<<<<<< HEAD ?assertEqual(ok, amqp_connection:update_secret(Conn, <<"not-a-token-^^^^5%">>, <<"token refresh">>)), +======= + ?assertEqual(ok, amqp_connection:update_secret(Conn, <<"not-a-token-^^^^5%">>, + <<"token refresh">>)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertExit({{shutdown, {connection_closing, {server_initiated_close, 530, _}}}, _}, amqp_connection:open_channel(Conn)), @@ -946,6 +1399,26 @@ cannot_change_username_on_refreshed_token(Config) -> test_failed_connection_with_algorithm_restriction(Config) -> +<<<<<<< HEAD {_Algo, Token} = rabbit_ct_helpers:get_config(Config, fixture_jwt), ?assertMatch({error, {auth_failure, _}}, open_unmanaged_connection(Config, 0, <<"username">>, Token)). +======= + {_Algo, Token} = get_config(Config, fixture_jwt), + ?assertMatch({error, {auth_failure, _}}, + open_unmanaged_connection(Config, 0, <<"username">>, Token)). + +%%% HELPERS +rpc_unset_env(Config, Par) -> + rpc(Config, 0, application, unset_env, + [rabbitmq_auth_backend_oauth2, Par]). +rpc_set_env(Config, Par, Val) -> + rpc(Config, 0, application, set_env, + [rabbitmq_auth_backend_oauth2, Par, Val]). +rpc_get_env(Config, Par) -> + rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par]). +rpc_get_env(Config, Par, Default) -> + rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par, Default]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl new file mode 100644 index 000000000000..ac3ca2b67e89 --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_provider_SUITE.erl @@ -0,0 +1,523 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_provider_SUITE). + +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include("oauth2.hrl"). + +-define(RABBITMQ,<<"rabbitmq">>). +-define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). +-define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). +-define(AUTH_PORT, 8000). + +-import(rabbit_oauth2_provider, [ + get_internal_oauth_provider/0,get_internal_oauth_provider/1, + add_signing_key/2, add_signing_key/3, replace_signing_keys/1, + replace_signing_keys/2, + get_signing_keys/0, get_signing_keys/1, get_signing_key/1, get_signing_key/2 +]). +-import(oauth2_client, [get_oauth_provider/2]). + +all() -> [ + {group, with_rabbitmq_node}, + {group, verify_oauth_provider_A}, + {group, verify_oauth_provider_root} +]. +groups() -> [ + {with_rabbitmq_node, [], [ + add_signing_keys_for_specific_oauth_provider, + add_signing_keys_for_root_oauth_provider, + + replace_signing_keys_for_root_oauth_provider, + replace_signing_keys_for_specific_oauth_provider, + {with_root_static_signing_keys, [], [ + replace_merge_root_static_keys_with_newly_added_keys, + replace_override_root_static_keys_with_newly_added_keys + ]}, + {with_static_signing_keys_for_specific_oauth_provider, [], [ + replace_merge_static_keys_with_newly_added_keys, + replace_override_static_keys_with_newly_added_keys + ]} + ]}, + {verify_oauth_provider_A, [], verify_provider()}, + {verify_oauth_provider_root, [], verify_provider()} +]. + +verify_provider() -> [ + internal_oauth_provider_has_no_default_key, + {oauth_provider_with_default_key, [], [ + internal_oauth_provider_has_default_key + ]}, + internal_oauth_provider_has_no_algorithms, + {oauth_provider_with_algorithms, [], [ + internal_oauth_provider_has_algorithms + ]}, + get_oauth_provider_with_jwks_uri_returns_error, + {oauth_provider_with_jwks_uri, [], [ + get_oauth_provider_has_jwks_uri + ]}, + {oauth_provider_with_issuer, [], [ + get_oauth_provider_has_jwks_uri + ]} +]. + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config). + +end_per_suite(Config) -> + rabbit_ct_helpers:run_teardown_steps(Config). + +init_per_group(with_rabbitmq_node, Config) -> + Config1 = rabbit_ct_helpers:set_config(Config, [ + {rmq_nodename_suffix, with_rabbitmq_node}, + {rmq_nodes_count, 1} + ]), + rabbit_ct_helpers:run_steps(Config1, rabbit_ct_broker_helpers:setup_steps()); + +init_per_group(with_root_static_signing_keys, Config) -> + KeyConfig = call_get_env(Config, key_config, []), + SigningKeys = #{ + <<"mykey-root-1">> => <<"some key root-1">>, + <<"mykey-root-2">> => <<"some key root-2">> + }, + call_set_env(Config, key_config, + proplists:delete(default_key, KeyConfig) ++ [{signing_keys,SigningKeys}]), + Config; + +init_per_group(with_static_signing_keys_for_specific_oauth_provider, Config) -> + OAuthProviders = call_get_env(Config, oauth_providers, #{}), + OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), + SigningKeys = #{ + <<"mykey-root-1">> => <<"some key root-1">>, + <<"mykey-root-2">> => <<"some key root-2">> + }, + OAuthProvider1 = proplists:delete(signing_keys, OAuthProvider) ++ + [{signing_keys, SigningKeys}], + + call_set_env(Config, oauth_providers, maps:put(<<"A">>, OAuthProvider1, + OAuthProviders)), + Config; + +init_per_group(oauth_provider_with_jwks_uri, Config) -> + URL = case ?config(oauth_provider_id, Config) of + root -> + RootUrl = build_url_to_oauth_provider(<<"/keys">>), + set_env(jwks_uri, RootUrl), + RootUrl; + <<"A">> -> + AUrl = build_url_to_oauth_provider(<<"/A/keys">>), + set_oauth_provider_properties(<<"A">>, [{jwks_uri, AUrl}]), + AUrl + end, + [{jwks_uri, URL} | Config]; + +init_per_group(oauth_provider_with_issuer, Config) -> + {ok, _} = application:ensure_all_started(inets), + {ok, _} = application:ensure_all_started(ssl), + application:ensure_all_started(cowboy), + CertsDir = ?config(rmq_certsdir, Config), + CaCertFile = filename:join([CertsDir, "testca", "cacert.pem"]), + SslOptions = ssl_options(verify_peer, false, CaCertFile), + + HttpOauthServerExpectations = get_openid_configuration_expectations(), + ListOfExpectations = maps:values(proplists:to_map(HttpOauthServerExpectations)), + + start_https_oauth_server(?AUTH_PORT, CertsDir, ListOfExpectations), + set_env(use_global_locks, false), + {Issuer, JwksUri} = case ?config(oauth_provider_id, Config) of + root -> + Url = build_url_to_oauth_provider(<<"/">>), + set_env(issuer, Url), + set_env(key_config, SslOptions), + {Url, build_url_to_oauth_provider(<<"/keys">>)}; + <<"A">> -> + Url = build_url_to_oauth_provider(<<"/A">>), + set_oauth_provider_properties(<<"A">>, [{issuer, Url}, {https, SslOptions}]), + {Url, build_url_to_oauth_provider(<<"/A/keys">>)} + end, + [{issuer, Issuer}, {jwks_uri, JwksUri}] ++ Config; + +init_per_group(with_resource_server_id, Config) -> + set_env(resource_server_id, ?RABBITMQ), + Config; + +init_per_group(with_algorithms, Config) -> + KeyConfig = get_env(key_config, []), + set_env(key_config, KeyConfig ++ [{algorithms, [<<"HS256">>, <<"RS256">>]}]), + [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; + +init_per_group(with_algorithms_for_provider_A, Config) -> + OAuthProviders = get_env(oauth_providers, #{}), + OAuthProvider = maps:get(<<"A">>, OAuthProviders, []), + set_env(oauth_providers, maps:put(<<"A">>, + [{algorithms, [<<"HS256">>, <<"RS256">>]} | OAuthProvider], OAuthProviders)), + [{algorithms, [<<"HS256">>, <<"RS256">>]} | Config]; + +init_per_group(with_different_oauth_provider_for_each_resource, Config) -> + {ok, ResourceServers} = get_env(resource_servers), + Rabbit1 = maps:get(?RABBITMQ_RESOURCE_ONE, ResourceServers) ++ + [ {oauth_provider_id, <<"A">>} ], + Rabbit2 = maps:get(?RABBITMQ_RESOURCE_TWO, ResourceServers) ++ + [ {oauth_provider_id, <<"B">>} ], + ResourceServers1 = maps:update(?RABBITMQ_RESOURCE_ONE, Rabbit1, ResourceServers), + set_env(resource_servers, maps:update(?RABBITMQ_RESOURCE_TWO, Rabbit2, + ResourceServers1)), + Config; + + +init_per_group(verify_oauth_provider_A, Config) -> + set_env(oauth_providers, + #{ <<"A">> => [ + {id, <<"A">>} + ] + }), + [{oauth_provider_id, <<"A">>} |Config]; + +init_per_group(verify_oauth_provider_root, Config) -> + [{oauth_provider_id, root} |Config]; + +init_per_group(_any, Config) -> + Config. + +end_per_group(with_rabbitmq_node, Config) -> + rabbit_ct_helpers:run_steps(Config, rabbit_ct_broker_helpers:teardown_steps()); + +end_per_group(with_root_static_signing_keys, Config) -> + KeyConfig = call_get_env(Config, key_config, []), + call_set_env(Config, key_config, KeyConfig), + Config; + +end_per_group(with_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(oauth_provider_with_issuer, Config) -> + case ?config(oauth_provider_id, Config) of + root -> + unset_env(issuer), + unset_env(https); + Id -> + unset_oauth_provider_properties(Id, [issuer, https]) + end, + stop_http_auth_server(), + Config; +end_per_group(oauth_provider_with_jwks_uri, Config) -> + case ?config(oauth_provider_id, Config) of + root -> unset_env(jwks_uri); + Id -> unset_oauth_provider_properties(Id, [jwks_uri]) + end, + Config; + +end_per_group(oauth_provider_with_default_key, Config) -> +case ?config(oauth_provider_id, Config) of + root -> unset_env(default_key); + Id -> unset_oauth_provider_properties(Id, [default_key]) + end, + Config; + +end_per_group(_any, Config) -> + Config. + +%% ----- Utility functions + +call_set_env(Config, Par, Value) -> + rabbit_ct_broker_helpers:rpc(Config, 0, application, set_env, + [rabbitmq_auth_backend_oauth2, Par, Value]). + +call_get_env(Config, Par, Def) -> + rabbit_ct_broker_helpers:rpc(Config, 0, application, get_env, + [rabbitmq_auth_backend_oauth2, Par, Def]). + +call_add_signing_key(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + add_signing_key, Args). + +call_get_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + get_signing_keys, Args). + +call_get_signing_keys(Config) -> + call_get_signing_keys(Config, []). + +call_get_signing_key(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + get_signing_key, Args). + +call_add_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + add_signing_keys, Args). + +call_replace_signing_keys(Config, Args) -> + rabbit_ct_broker_helpers:rpc(Config, 0, rabbit_oauth2_provider, + replace_signing_keys, Args). + +%% ----- Test cases + +add_signing_keys_for_root_oauth_provider(Config) -> + #{<<"mykey-1">> := <<"some key 1">>} = + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + #{<<"mykey-1">> := <<"some key 1">>} = + call_get_signing_keys(Config), + + #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = + call_add_signing_key(Config, [<<"mykey-2">>, <<"some key 2">>]), + #{<<"mykey-1">> := <<"some key 1">>, <<"mykey-2">> := <<"some key 2">>} = + call_get_signing_keys(Config), + + ?assertEqual(<<"some key 1">>, + call_get_signing_key(Config, [<<"mykey-1">>])). + +add_signing_keys_for_specific_oauth_provider(Config) -> + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_add_signing_key(Config, + [<<"mykey-3-1">>, <<"some key 3-1">>, <<"my-oauth-provider-3">>]), + #{<<"mykey-4-1">> := <<"some key 4-1">>} = + call_add_signing_key(Config, + [<<"mykey-4-1">>, <<"some key 4-1">>, <<"my-oauth-provider-4">>]), + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_get_signing_keys(Config, [<<"my-oauth-provider-3">>]), + #{<<"mykey-4-1">> := <<"some key 4-1">>} = + call_get_signing_keys(Config, [<<"my-oauth-provider-4">>]), + + #{<<"mykey-3-1">> := <<"some key 3-1">>, + <<"mykey-3-2">> := <<"some key 3-2">>} = + call_add_signing_key(Config, [ + <<"mykey-3-2">>, <<"some key 3-2">>, <<"my-oauth-provider-3">>]), + + #{<<"mykey-1">> := <<"some key 1">>} = + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + #{<<"mykey-1">> := <<"some key 1">>} = + call_get_signing_keys(Config, []), + + ?assertEqual(<<"some key 3-1">>, + call_get_signing_key(Config, [<<"mykey-3-1">> , <<"my-oauth-provider-3">>])). + +replace_merge_root_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{ <<"mykey-root-1">> := <<"some key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-2">> := <<"some key 2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config). + +replace_merge_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, <<"A">>]), + #{ <<"mykey-root-1">> := <<"some key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-2">> := <<"some key 2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config, [<<"A">>]). + +replace_override_root_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{ <<"mykey-root-1">> := <<"new key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config). +replace_override_static_keys_with_newly_added_keys(Config) -> + NewKeys = #{<<"mykey-root-1">> => <<"new key root-1">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, <<"A">>]), + #{ <<"mykey-root-1">> := <<"new key root-1">>, + <<"mykey-root-2">> := <<"some key root-2">>, + <<"key-3">> := <<"some key 3">> + } = call_get_signing_keys(Config, [<<"A">>]). + +replace_signing_keys_for_root_oauth_provider(Config) -> + call_add_signing_key(Config, [<<"mykey-1">>, <<"some key 1">>]), + NewKeys = #{<<"key-2">> => <<"some key 2">>, <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys]), + #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = + call_get_signing_keys(Config). + +replace_signing_keys_for_specific_oauth_provider(Config) -> + OAuthProviderId = <<"my-oauth-provider-3">>, + #{<<"mykey-3-1">> := <<"some key 3-1">>} = + call_add_signing_key(Config, + [<<"mykey-3-1">>, <<"some key 3-1">>, OAuthProviderId]), + NewKeys = #{<<"key-2">> => <<"some key 2">>, + <<"key-3">> => <<"some key 3">>}, + call_replace_signing_keys(Config, [NewKeys, OAuthProviderId]), + #{<<"key-2">> := <<"some key 2">>, <<"key-3">> := <<"some key 3">>} = + call_get_signing_keys(Config, [OAuthProviderId]). + + +get_algorithms_should_return_undefined(_Config) -> + OAuthProvider = get_internal_oauth_provider(), + undefined = OAuthProvider#internal_oauth_provider.algorithms. + +get_algorithms(Config) -> + OAuthProvider = get_internal_oauth_provider(), + Algorithms = OAuthProvider#internal_oauth_provider.algorithms, + ?assertEqual(?config(algorithms, Config), Algorithms). + +get_algorithms_for_provider_A_should_return_undefined(_Config) -> + OAuthProvider = get_internal_oauth_provider(<<"A">>), + undefined = OAuthProvider#internal_oauth_provider.algorithms. + +get_algorithms_for_provider_A(Config) -> + OAuthProvider = get_internal_oauth_provider(<<"A">>), + Algorithms = OAuthProvider#internal_oauth_provider.algorithms, + ?assertEqual(?config(algorithms, Config), Algorithms). + +append_paths(Path1, Path2) -> + erlang:iolist_to_binary([Path1, Path2]). + + + +internal_oauth_provider_has_no_default_key(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(undefined, + InternalOAuthProvider#internal_oauth_provider.default_key). + +internal_oauth_provider_has_default_key(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(?config(default_key, Config), + InternalOAuthProvider#internal_oauth_provider.default_key). + +internal_oauth_provider_has_no_algorithms(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(undefined, + InternalOAuthProvider#internal_oauth_provider.algorithms). + +internal_oauth_provider_has_algorithms(Config) -> + InternalOAuthProvider = get_internal_oauth_provider( + ?config(oauth_provider_id, Config)), + ?assertEqual(?config(algorithms, Config), + InternalOAuthProvider#internal_oauth_provider.algorithms). + +get_oauth_provider_with_jwks_uri_returns_error(Config) -> + {error, _} = get_oauth_provider( + ?config(oauth_provider_id, Config), [jwks_uri]). + +get_oauth_provider_has_jwks_uri(Config) -> + {ok, OAuthProvider} = get_oauth_provider( + ?config(oauth_provider_id, Config), [jwks_uri]), + ct:log("OAuthProvider: ~p", [OAuthProvider]), + ?assertEqual(?config(jwks_uri, Config), OAuthProvider#oauth_provider.jwks_uri). + + +%% ---- Utility functions + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). +unset_env(Par) -> + application:unset_env(rabbitmq_auth_backend_oauth2, Par). + +get_openid_configuration_expectations() -> + [ {get_root_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/keys">>)} + ]} + ] + } + }, + {get_A_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/A/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/A">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/A/keys">>)} + ]} + ] + } + }, + {get_B_openid_configuration, + + #{request => #{ + method => <<"GET">>, + path => <<"/B/.well-known/openid-configuration">> + }, + response => [ + {code, 200}, + {content_type, ?CONTENT_JSON}, + {payload, [ + {issuer, build_url_to_oauth_provider(<<"/B">>) }, + {jwks_uri, build_url_to_oauth_provider(<<"/B/keys">>)} + ]} + ] + } + } + ]. + +start_https_oauth_server(Port, CertsDir, Expectations) when is_list(Expectations) -> + Dispatch = cowboy_router:compile([ + {'_', [{Path, oauth2_http_mock, Expected} || + #{request := #{path := Path}} = Expected <- Expectations ]} + ]), + {ok, Pid} = cowboy:start_tls( + mock_http_auth_listener, + [{port, Port}, + {certfile, filename:join([CertsDir, "server", "cert.pem"])}, + {keyfile, filename:join([CertsDir, "server", "key.pem"])} + ], + #{env => #{dispatch => Dispatch}}). + +build_url_to_oauth_provider(Path) -> + uri_string:recompose(#{scheme => "https", + host => "localhost", + port => rabbit_data_coercion:to_integer(?AUTH_PORT), + path => Path}). + +stop_http_auth_server() -> + cowboy:stop_listener(mock_http_auth_listener). + +set_oauth_provider_properties(OAuthProviderId, Proplist) -> + OAuthProviders = get_env(oauth_providers, #{}), + CurProplist = maps:get(OAuthProviderId, OAuthProviders), + CurMap = proplists:to_map(CurProplist), + Map = proplists:to_map(Proplist), + set_env(oauth_providers, maps:put(OAuthProviderId, + maps:to_list(maps:merge(CurMap, Map)), OAuthProviders)). + +unset_oauth_provider_properties(OAuthProviderId, PropertyNameList) -> + OAuthProviders = get_env(oauth_providers, #{}), + CurProplist = maps:get(OAuthProviderId, OAuthProviders), + CurMap = proplists:to_map(CurProplist), + set_env(oauth_providers, maps:put(OAuthProviderId, + maps:to_list(maps:filter(fun(K,_V) -> + not proplists:is_defined(K, PropertyNameList) end, CurMap)), + OAuthProviders)). + +-spec ssl_options(ssl:verify_type(), boolean(), file:filename()) -> list(). +ssl_options(PeerVerification, FailIfNoPeerCert, CaCertFile) -> + [{verify, PeerVerification}, + {depth, 10}, + {fail_if_no_peer_cert, FailIfNoPeerCert}, + {crl_check, false}, + {crl_cache, {ssl_crl_cache, {internal, [{http, 10000}]}}}, + {cacertfile, CaCertFile}]. diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl new file mode 100644 index 000000000000..3e1fb745b6ec --- /dev/null +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_resource_server_SUITE.erl @@ -0,0 +1,452 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_oauth2_resource_server_SUITE). + +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include("oauth2.hrl"). + +-define(RABBITMQ,<<"rabbitmq">>). +-define(RABBITMQ_RESOURCE_ONE,<<"rabbitmq1">>). +-define(RABBITMQ_RESOURCE_TWO,<<"rabbitmq2">>). +-define(OAUTH_PROVIDER_A,<<"A">>). +-define(OAUTH_PROVIDER_B,<<"B">>). + +-import(oauth2_client, [get_oauth_provider/2]). +-import(rabbit_oauth2_resource_server, [resolve_resource_server_from_audience/1]). + + +all() -> [ + {group, without_resource_server_id}, + {group, with_rabbitmq_as_resource_server_id}, + {group, with_two_resource_servers} + %{group, with_two_resource_servers_and_rabbitmq_as_resource_server_id} +]. +groups() -> [ + {with_rabbitmq_as_resource_server_id, [], [ + resolve_resource_server_for_rabbitmq_audience, + resolve_resource_server_for_rabbitmq_plus_unknown_audience, + resolve_resource_server_for_none_audience_returns_no_aud_found, + resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, + {with_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_rabbitmq, + resolve_resource_server_for_unknown_audience_returns_rabbitmq + ]}, + {verify_get_rabbitmq_server_configuration, [], + verify_get_rabbitmq_server_configuration()} + ]}, + {without_resource_server_id, [], [ + resolve_resource_server_id_for_any_audience_returns_no_matching_aud_found + ]}, + + {with_two_resource_servers, [], [ + resolve_resource_server_id_for_rabbitmq1, + resolve_resource_server_id_for_rabbitmq2, + resolve_resource_server_id_for_both_resources_returns_error, + resolve_resource_server_for_none_audience_returns_no_aud_found, + resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found, + {with_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_rabbitmq2, + resolve_resource_server_for_unknown_audience_returns_rabbitmq2, + {with_rabbitmq1_verify_aud_false, [], [ + resolve_resource_server_for_none_audience_returns_error + ]} + ]}, + verify_rabbitmq1_server_configuration, + {verify_configuration_inheritance_with_rabbitmq2, [], + verify_configuration_inheritance_with_rabbitmq2()}, + {with_rabbitmq_as_resource_server_id, [], [ + resolve_resource_server_for_rabbitmq_audience, + resolve_resource_server_id_for_rabbitmq1, + resolve_resource_server_id_for_rabbitmq2 + ]} + ]} +]. + +verify_get_rabbitmq_server_configuration() -> [ + rabbitmq_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq_verify_aud_is_false + ]}, + rabbitmq_has_default_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq_has_scope_prefix + ]}, + {with_empty_scope_prefix, [], [ + rabbitmq_has_empty_scope_prefix + ]}, + rabbitmq_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq_oauth_provider_id_is_A + ]}, + rabbitmq_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq_has_additional_scopes_key + ]}, + rabbitmq_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq_has_preferred_username_claims + ]}, + rabbitmq_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq_has_scope_aliases + ]} +]. + +verify_configuration_inheritance_with_rabbitmq2() -> [ + rabbitmq2_verify_aud_is_true, + {with_verify_aud_false, [], [ + rabbitmq2_verify_aud_is_false + ]}, + rabbitmq2_has_default_scope_prefix, + {with_scope_prefix, [], [ + rabbitmq2_has_scope_prefix + ]}, + rabbitmq2_oauth_provider_id_is_root, + {with_default_oauth_provider_A, [], [ + rabbitmq2_oauth_provider_id_is_A + ]}, + rabbitmq2_has_no_additional_scopes_key, + {with_additional_scopes_key, [], [ + rabbitmq2_has_additional_scopes_key + ]}, + rabbitmq2_has_no_preferred_username_claims_but_gets_default, + {with_preferred_username_claims, [], [ + rabbitmq2_has_preferred_username_claims_plus_default + ]}, + rabbitmq2_has_no_scope_aliases, + {with_scope_aliases, [], [ + rabbitmq2_has_scope_aliases + ]} +]. + +init_per_suite(Config) -> + rabbit_ct_helpers:log_environment(), + rabbit_ct_helpers:run_setup_steps(Config). + +end_per_suite(Config) -> + rabbit_ct_helpers:run_teardown_steps(Config). + +init_per_group(with_default_oauth_provider_A, Config) -> + set_env(default_oauth_provider, ?OAUTH_PROVIDER_A), + Config; + +init_per_group(with_default_oauth_provider_B, Config) -> + set_env(default_oauth_provider, ?OAUTH_PROVIDER_B), + Config; + +init_per_group(with_rabbitmq_as_resource_server_id, Config) -> + set_env(resource_server_id, ?RABBITMQ), + Config; + +init_per_group(with_scope_prefix, Config) -> + Prefix = <<"some-prefix:">>, + set_env(scope_prefix, Prefix), + [{scope_prefix, Prefix} | Config]; + +init_per_group(with_empty_scope_prefix, Config) -> + Prefix = <<"">>, + set_env(scope_prefix, Prefix), + Config; + +init_per_group(with_additional_scopes_key, Config) -> + Key = <<"roles">>, + set_env(extra_scopes_source, Key), + [{additional_scopes_key, Key} | Config]; + +init_per_group(with_preferred_username_claims, Config) -> + Claims = [<<"new-user">>, <<"new-email">>], + set_env(preferred_username_claims, Claims), + [{preferred_username_claims, Claims} | Config]; + +init_per_group(with_scope_aliases, Config) -> + Aliases = #{ + <<"admin">> => [<<"rabbitmq.tag:administrator">>] + }, + set_env(scope_aliases, Aliases), + [{scope_aliases, Aliases} | Config]; + +init_per_group(with_verify_aud_false, Config) -> + set_env(verify_aud, false), + Config; + +init_per_group(with_rabbitmq1_verify_aud_false, Config) -> + RabbitMQServers = get_env(resource_servers, #{}), + Resource0 = maps:get(?RABBITMQ_RESOURCE_ONE, RabbitMQServers, []), + Resource = [{verify_aud, false} | Resource0], + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, Resource, + RabbitMQServers)), + Config; + +init_per_group(with_two_resource_servers, Config) -> + RabbitMQ1 = [ + {id, ?RABBITMQ_RESOURCE_ONE}, + {resource_server_type, <<"some-type">>}, + {verify_aud, true}, + {scope_prefix, <<"some-prefix">>}, + {additional_scopes_key, <<"roles">>}, + {preferred_username_claims, [<<"x-username">>, <<"x-email">>]}, + {scope_aliases, #{ <<"admin">> => [<<"rabbitmq.tag:administrator">>]}}, + {oauth_provider_id, ?OAUTH_PROVIDER_A} + ], + RabbitMQ2 = [ + {id, ?RABBITMQ_RESOURCE_TWO} + ], + set_env(resource_servers, #{ + ?RABBITMQ_RESOURCE_ONE => RabbitMQ1, + ?RABBITMQ_RESOURCE_TWO => RabbitMQ2 + }), + [{?RABBITMQ_RESOURCE_ONE, RabbitMQ1}, {?RABBITMQ_RESOURCE_TWO, RabbitMQ2}] + ++ Config; + +init_per_group(_any, Config) -> + Config. + +end_per_group(with_default_oauth_provider_A, Config) -> + unset_env(default_oauth_provider), + Config; + +end_per_group(with_default_oauth_provider_B, Config) -> + unset_env(default_oauth_provider), + Config; + +end_per_group(with_rabbitmq_as_resource_server_id, Config) -> + unset_env(resource_server_id), + Config; + +end_per_group(with_empty_scope_prefix, Config) -> + unset_env(scope_prefix), + Config; + +end_per_group(with_verify_aud_false, Config) -> + unset_env(verify_aud), + Config; + +end_per_group(with_two_resource_servers, Config) -> + unset_env(resource_servers), + Config; + +end_per_group(with_scope_prefix, Config) -> + unset_env(scope_prefix), + Config; + +end_per_group(with_rabbitmq1_verify_aud_false, Config) -> + RabbitMQServers = get_env(resource_servers, #{}), + Resource = maps:get(?RABBITMQ_RESOURCE_ONE, RabbitMQServers, []), + set_env(resource_servers, maps:put(?RABBITMQ_RESOURCE_ONE, + proplists:delete(verify_aud, Resource), + RabbitMQServers)), + Config; + +end_per_group(with_additional_scopes_key, Config) -> + unset_env(extra_scopes_source), + Config; + +end_per_group(with_preferred_username_claims, Config) -> + unset_env(preferred_username_claims), + Config; + +end_per_group(with_scope_aliases, Config) -> + unset_env(scope_aliases), + Config; + +end_per_group(_any, Config) -> + Config. + + +%% --- Test cases + +resolve_resource_server_for_rabbitmq_audience(_) -> + assert_resource_server_id(?RABBITMQ, ?RABBITMQ). + +resolve_resource_server_for_rabbitmq_plus_unknown_audience(_) -> + assert_resource_server_id(?RABBITMQ, [?RABBITMQ, <<"unknown">>]). + +resolve_resource_server_for_none_audience_returns_no_aud_found(_) -> + assert_resource_server_id({error, no_aud_found}, none). + +resolve_resource_server_for_none_audience_returns_rabbitmq2(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, none). + +resolve_resource_server_for_unknown_audience_returns_no_matching_aud_found(_) -> + assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). + +resolve_resource_server_for_none_audience_returns_rabbitmq(_) -> + assert_resource_server_id(?RABBITMQ, none). + +resolve_resource_server_for_unknown_audience_returns_rabbitmq(_) -> + assert_resource_server_id(?RABBITMQ, <<"unknown">>). + +resolve_resource_server_for_unknown_audience_returns_rabbitmq2(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, <<"unknown">>). + +resolve_resource_server_for_none_audience_returns_error(_) -> + assert_resource_server_id( + {error, no_aud_found_cannot_pick_one_from_too_many_resource_servers}, + none). +resolve_resource_server_id_for_any_audience_returns_no_matching_aud_found(_) -> + assert_resource_server_id({error, no_matching_aud_found}, ?RABBITMQ), + assert_resource_server_id({error, no_matching_aud_found}, <<"unknown">>). + +resolve_resource_server_id_for_rabbitmq1(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_ONE, ?RABBITMQ_RESOURCE_ONE). + +resolve_resource_server_id_for_rabbitmq2(_) -> + assert_resource_server_id(?RABBITMQ_RESOURCE_TWO, ?RABBITMQ_RESOURCE_TWO). + +resolve_resource_server_id_for_both_resources_returns_error(_) -> + assert_resource_server_id({error, aud_matched_many_resource_servers_only_one_allowed}, + [?RABBITMQ_RESOURCE_TWO, ?RABBITMQ_RESOURCE_ONE]). + +rabbitmq_verify_aud_is_true(_) -> + assert_verify_aud(true, ?RABBITMQ). + +rabbitmq_verify_aud_is_false(_) -> + assert_verify_aud(false, ?RABBITMQ). + +rabbitmq2_verify_aud_is_true(_) -> + assert_verify_aud(true, ?RABBITMQ_RESOURCE_TWO). + +both_resources_oauth_provider_id_is_root(_) -> + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_ONE), + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_verify_aud_is_false(_) -> + assert_verify_aud(false, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_default_scope_prefix(_) -> + assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ_RESOURCE_TWO, <<".">>]), + ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_scope_prefix(Config) -> + assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_oauth_provider_id_is_root(_) -> + assert_oauth_provider_id(root, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_oauth_provider_id_is_A(_) -> + assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_no_additional_scopes_key(_) -> + assert_additional_scopes_key(undefined, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_additional_scopes_key(Config) -> + assert_additional_scopes_key(?config(additional_scopes_key, Config), + ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_no_preferred_username_claims_but_gets_default(_) -> + assert_preferred_username_claims(?DEFAULT_PREFERRED_USERNAME_CLAIMS, + ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_preferred_username_claims_plus_default(Config) -> + assert_preferred_username_claims(?config(preferred_username_claims, Config) + , ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_no_scope_aliases(_) -> + assert_scope_aliases(undefined, ?RABBITMQ_RESOURCE_TWO). + +rabbitmq2_has_scope_aliases(Config) -> + assert_scope_aliases(?config(scope_aliases, Config), ?RABBITMQ_RESOURCE_TWO). + +rabbitmq_oauth_provider_id_is_root(_) -> + assert_oauth_provider_id(root, ?RABBITMQ). + +rabbitmq_oauth_provider_id_is_A(_) -> + assert_oauth_provider_id(?OAUTH_PROVIDER_A, ?RABBITMQ). + +rabbitmq_has_default_scope_prefix(_) -> + assert_scope_prefix(erlang:iolist_to_binary([?RABBITMQ, <<".">>]), ?RABBITMQ). + +rabbitmq_has_scope_prefix(Config) -> + assert_scope_prefix(?config(scope_prefix, Config), ?RABBITMQ). + +rabbitmq_has_empty_scope_prefix(_) -> + assert_scope_prefix(<<"">>, ?RABBITMQ). + +rabbitmq_has_no_additional_scopes_key(_) -> + assert_additional_scopes_key(undefined, ?RABBITMQ). + +rabbitmq_has_additional_scopes_key(Config) -> + assert_additional_scopes_key(?config(additional_scopes_key, Config), + ?RABBITMQ). + +rabbitmq_has_no_preferred_username_claims_but_gets_default(_) -> + assert_preferred_username_claims(?DEFAULT_PREFERRED_USERNAME_CLAIMS, ?RABBITMQ). + +rabbitmq_has_preferred_username_claims(Config) -> + assert_preferred_username_claims(?config(preferred_username_claims, Config), + ?RABBITMQ). + +rabbitmq_has_no_scope_aliases(_) -> + assert_scope_aliases(undefined, ?RABBITMQ). + +rabbitmq_has_scope_aliases(Config) -> + assert_scope_aliases(?config(scope_aliases, Config), ?RABBITMQ). + +verify_rabbitmq1_server_configuration(Config) -> + ConfigRabbitMQ = ?config(?RABBITMQ_RESOURCE_ONE, Config), + {ok, ActualRabbitMQ} = resolve_resource_server_from_audience(?RABBITMQ_RESOURCE_ONE), + ?assertEqual(proplists:get_value(id, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.id), + ?assertEqual(proplists:get_value(resource_server_type, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.resource_server_type), + ?assertEqual(proplists:get_value(verify_aud, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.verify_aud), + ?assertEqual(proplists:get_value(scope_prefix, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.scope_prefix), + ?assertEqual(proplists:get_value(extract_scopes_source, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.additional_scopes_key), + ?assertEqual(proplists:get_value(preferred_username_claims, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.preferred_username_claims), + ?assertEqual(proplists:get_value(scope_aliases, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.scope_aliases), + ?assertEqual(proplists:get_value(oauth_provider_id, ConfigRabbitMQ), + ActualRabbitMQ#resource_server.oauth_provider_id). + +%% ----- + +assert_resource_server_id({error, ExpectedError}, Audience) -> + {error, ExpectedError} = resolve_resource_server_from_audience(Audience); +assert_resource_server_id(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.id). + +assert_verify_aud(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.verify_aud). + +assert_oauth_provider_id(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ct:log("Actual:~p", [Actual]), + ?assertEqual(Expected, Actual#resource_server.oauth_provider_id). + +assert_scope_prefix(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.scope_prefix). + +assert_additional_scopes_key(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.additional_scopes_key). + +assert_preferred_username_claims(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.preferred_username_claims). + +assert_scope_aliases(Expected, Audience) -> + {ok, Actual} = resolve_resource_server_from_audience(Audience), + ?assertEqual(Expected, Actual#resource_server.scope_aliases). + +get_env(Par) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par). +get_env(Par, Def) -> + application:get_env(rabbitmq_auth_backend_oauth2, Par, Def). +set_env(Par, Val) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Val). +unset_env(Par) -> + application:unset_env(rabbitmq_auth_backend_oauth2, Par). diff --git a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl index 0e20f0844863..a42dcf23917e 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/rabbit_oauth2_schema_SUITE.erl @@ -12,6 +12,15 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +<<<<<<< HEAD +======= +-import(rabbit_oauth2_schema, [ + translate_endpoint_params/2, + translate_oauth_providers/1, + translate_resource_servers/1, + translate_scope_aliases/1 +]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [ @@ -24,15 +33,31 @@ all() -> test_oauth_providers_https, test_oauth_providers_https_with_missing_cacertfile, test_oauth_providers_signing_keys, +<<<<<<< HEAD test_without_resource_servers, test_with_one_resource_server, test_with_many_resource_servers, test_resource_servers_attributes +======= + test_without_endpoint_params, + test_with_endpoint_params, + test_with_invalid_endpoint_params, + test_without_resource_servers, + test_with_one_resource_server, + test_with_many_resource_servers, + test_resource_servers_attributes, + test_invalid_oauth_providers_endpoint_params, + test_without_oauth_providers_with_endpoint_params, + test_scope_aliases_configured_as_list_of_properties, + test_scope_aliases_configured_as_map, + test_scope_aliases_configured_as_list_of_missing_properties +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. test_without_oauth_providers(_) -> +<<<<<<< HEAD #{} = rabbit_oauth2_schema:translate_oauth_providers([]). test_without_resource_servers(_) -> @@ -65,10 +90,105 @@ test_with_many_resource_servers(_) -> Conf = [{["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"}, {["auth_oauth2","resource_servers","rabbitmq2","id"],"rabbitmq2"} ], +======= + #{} = translate_oauth_providers([]). + +test_without_resource_servers(_) -> + #{} = translate_resource_servers([]). + +test_without_endpoint_params(_) -> + [] = translate_endpoint_params("oauth_discovery_endpoint_params", []). + +test_with_invalid_endpoint_params(_) -> + try translate_endpoint_params("discovery_endpoint_params", [ + {["auth_oauth2","discovery_endpoint_params"], "some-value1"}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. + +test_with_endpoint_params(_) -> + Conf = [ + {["auth_oauth2","discovery_endpoint_params","param1"], "some-value1"}, + {["auth_oauth2","discovery_endpoint_params","param2"], "some-value2"} + ], + [ {<<"param1">>, <<"some-value1">>}, {<<"param2">>, <<"some-value2">>} ] = + translate_endpoint_params("discovery_endpoint_params", Conf). + +test_invalid_oauth_providers_endpoint_params(_) -> + try translate_oauth_providers([ + {["auth_oauth2","oauth_providers", "X", "discovery_endpoint_params"], ""}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. + +test_without_oauth_providers_with_endpoint_params(_) -> + Conf = [ + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param1"], + "some-value1"}, + {["auth_oauth2","oauth_providers", "A", "discovery_endpoint_params","param2"], + "some-value2"}, + {["auth_oauth2","oauth_providers", "B", "discovery_endpoint_params","param3"], + "some-value3"} + ], + #{ + <<"A">> := [{discovery_endpoint_params, [ + {<<"param1">>, <<"some-value1">>}, + {<<"param2">>, <<"some-value2">>} + ]}], + <<"B">> := [{discovery_endpoint_params, [ + {<<"param3">>, <<"some-value3">>} + ]}] + + } = translate_oauth_providers(Conf). + +test_with_one_oauth_provider(_) -> + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://rabbit"} + ], + #{<<"keycloak">> := [ + {issuer, "https://rabbit"}] + } = translate_oauth_providers(Conf). + +test_with_one_resource_server(_) -> + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"],"rabbitmq1"} + ], + #{<<"rabbitmq1">> := [{id, <<"rabbitmq1">>}] + } = translate_resource_servers(Conf). + +test_with_many_oauth_providers(_) -> + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","uaa","issuer"], + "https://uaa"}, + {["auth_oauth2","oauth_providers","uaa","discovery_endpoint_path"], + "/some-path"} + ], + #{<<"keycloak">> := [{issuer, "https://keycloak"} + ], + <<"uaa">> := [{issuer, "https://uaa"}, + {discovery_endpoint_path, "/some-path"} + ] + } = translate_oauth_providers(Conf). + + +test_with_many_resource_servers(_) -> + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"], + "rabbitmq1"}, + {["auth_oauth2","resource_servers","rabbitmq2","id"], + "rabbitmq2"} + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #{<<"rabbitmq1">> := [{id, <<"rabbitmq1">>} ], <<"rabbitmq2">> := [{id, <<"rabbitmq2">>} ] +<<<<<<< HEAD } = rabbit_oauth2_schema:translate_resource_servers(Conf). test_oauth_providers_attributes(_) -> @@ -100,10 +220,57 @@ test_resource_servers_attributes(_) -> {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"],"userid"}, {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"],"groupid"} ], +======= + } = translate_resource_servers(Conf). + +test_oauth_providers_attributes(_) -> + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","default_key"], + "token-key"} + ], + #{<<"keycloak">> := [{default_key, <<"token-key">>}, + {issuer, "https://keycloak"} + ] + } = sort_settings(translate_oauth_providers(Conf)). + +test_resource_servers_attributes(_) -> + Conf = [ + {["auth_oauth2","resource_servers","rabbitmq1","id"], + "rabbitmq1xxx"}, + {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"], + "somescope."}, + {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"], + "roles"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"], + "userid"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"], + "groupid"} + ], + #{<<"rabbitmq1xxx">> := [{extra_scopes_source, <<"roles">>}, + {id, <<"rabbitmq1xxx">>}, + {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, + {scope_prefix, <<"somescope.">>} + ] + } = sort_settings(translate_resource_servers(Conf)), + + Conf2 = [ + {["auth_oauth2","resource_servers","rabbitmq1","scope_prefix"], + "somescope."}, + {["auth_oauth2","resource_servers","rabbitmq1","additional_scopes_key"], + "roles"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","1"], + "userid"}, + {["auth_oauth2","resource_servers","rabbitmq1","preferred_username_claims","2"], + "groupid"} + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #{<<"rabbitmq1">> := [{extra_scopes_source, <<"roles">>}, {id, <<"rabbitmq1">>}, {preferred_username_claims, [<<"userid">>, <<"groupid">>]}, {scope_prefix, <<"somescope.">>} +<<<<<<< HEAD ] } = sort_settings(rabbit_oauth2_schema:translate_resource_servers(Conf2)). @@ -112,12 +279,26 @@ test_oauth_providers_attributes_with_invalid_uri(_) -> {["auth_oauth2","oauth_providers","keycloak","default_key"],"token-key"} ], try sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)) of +======= + ] + } = sort_settings(translate_resource_servers(Conf2)). + +test_oauth_providers_attributes_with_invalid_uri(_) -> + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "http://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","default_key"], + "token-key"} + ], + try sort_settings(translate_oauth_providers(Conf)) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> {throw, should_have_failed} catch _ -> ok end. test_oauth_providers_algorithms(_) -> +<<<<<<< HEAD Conf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, {["auth_oauth2","oauth_providers","keycloak","algorithms","2"],"HS256"}, {["auth_oauth2","oauth_providers","keycloak","algorithms","1"],"RS256"} @@ -138,6 +319,41 @@ test_oauth_providers_https(Conf) -> {["auth_oauth2","oauth_providers","keycloak","https","fail_if_no_peer_cert"],true}, {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"],cert_filename(Conf)} ], +======= + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","algorithms","2"], + "HS256"}, + {["auth_oauth2","oauth_providers","keycloak","algorithms","1"], + "RS256"} + ], + #{<<"keycloak">> := [{algorithms, [<<"RS256">>, <<"HS256">>]}, + {issuer, "https://keycloak"} + ] + } = sort_settings(translate_oauth_providers(Conf)). + +test_oauth_providers_https(Conf) -> + + CuttlefishConf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","https","verify"], + verify_none}, + {["auth_oauth2","oauth_providers","keycloak","https","peer_verification"], + verify_peer}, + {["auth_oauth2","oauth_providers","keycloak","https","depth"], + 2}, + {["auth_oauth2","oauth_providers","keycloak","https","hostname_verification"], + wildcard}, + {["auth_oauth2","oauth_providers","keycloak","https","crl_check"], + false}, + {["auth_oauth2","oauth_providers","keycloak","https","fail_if_no_peer_cert"], + true}, + {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"], + cert_filename(Conf)} + ], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #{<<"keycloak">> := [{https, [{verify, verify_none}, {peer_verification, verify_peer}, {depth, 2}, @@ -146,6 +362,7 @@ test_oauth_providers_https(Conf) -> {fail_if_no_peer_cert, true}, {cacertfile, _CaCertFile} ]}, +<<<<<<< HEAD {issuer, <<"https://keycloak">>} ] } = sort_settings(rabbit_oauth2_schema:translate_oauth_providers(CuttlefishConf)). @@ -156,12 +373,28 @@ test_oauth_providers_https_with_missing_cacertfile(_) -> {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"],"/non-existent.pem"} ], try sort_settings(rabbit_oauth2_schema:translate_oauth_providers(Conf)) of +======= + {issuer, "https://keycloak"} + ] + } = sort_settings(translate_oauth_providers(CuttlefishConf)). + +test_oauth_providers_https_with_missing_cacertfile(_) -> + + Conf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","https","cacertfile"], + "/non-existent.pem"} + ], + try sort_settings(translate_oauth_providers(Conf)) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _ -> {throw, should_have_failed} catch _ -> ok end. test_oauth_providers_signing_keys(Conf) -> +<<<<<<< HEAD CuttlefishConf = [{["auth_oauth2","oauth_providers","keycloak","issuer"],"https://keycloak"}, {["auth_oauth2","oauth_providers","keycloak","signing_keys","2"], cert_filename(Conf)}, {["auth_oauth2","oauth_providers","keycloak","signing_keys","1"], cert_filename(Conf)} @@ -174,10 +407,76 @@ test_oauth_providers_signing_keys(Conf) -> #{<<"1">> := {pem, <<"I'm not a certificate">>}, <<"2">> := {pem, <<"I'm not a certificate">>} } = SigningKeys. +======= + CuttlefishConf = [ + {["auth_oauth2","oauth_providers","keycloak","issuer"], + "https://keycloak"}, + {["auth_oauth2","oauth_providers","keycloak","signing_keys","2"], + cert_filename(Conf)}, + {["auth_oauth2","oauth_providers","keycloak","signing_keys","1"], + cert_filename(Conf)} + ], + #{<<"keycloak">> := [{issuer, "https://keycloak"}, + {signing_keys, SigningKeys} + ] + } = sort_settings(translate_oauth_providers(CuttlefishConf)), + ct:log("SigningKey: ~p", [SigningKeys]), + #{<<"1">> := {pem, <<"I'm not a certificate">>}, + <<"2">> := {pem, <<"I'm not a certificate">>} + } = SigningKeys. + +test_scope_aliases_configured_as_list_of_properties(_) -> + CuttlefishConf = [ + {["auth_oauth2","scope_aliases","1","alias"], + "admin"}, + {["auth_oauth2","scope_aliases","1","scope"], + "rabbitmq.tag:administrator"}, + {["auth_oauth2","scope_aliases","2","alias"], + "developer"}, + {["auth_oauth2","scope_aliases","2","scope"], + "rabbitmq.tag:management rabbitmq.read:*/*"} + ], + #{ + <<"admin">> := [<<"rabbitmq.tag:administrator">>], + <<"developer">> := [<<"rabbitmq.tag:management">>, <<"rabbitmq.read:*/*">>] + } = translate_scope_aliases(CuttlefishConf). + +test_scope_aliases_configured_as_list_of_missing_properties(_) -> + CuttlefishConf = [ + {["auth_oauth2","scope_aliases","1","alias"], + "admin"} + ], + #{} = rabbit_oauth2_schema:translate_scope_aliases(CuttlefishConf), + + CuttlefishConf2 = [ + {["auth_oauth2","scope_aliases","1","scope"], + "rabbitmq.tag:management rabbitmq.read:*/*"} + ], + #{} = rabbit_oauth2_schema:translate_scope_aliases(CuttlefishConf2). + + +test_scope_aliases_configured_as_map(_) -> + CuttlefishConf = [ + {["auth_oauth2","scope_aliases","admin"], + "rabbitmq.tag:administrator"}, + {["auth_oauth2","scope_aliases","developer"], + "rabbitmq.tag:management rabbitmq.read:*/*"} + ], + #{ + <<"admin">> := [<<"rabbitmq.tag:administrator">>], + <<"developer">> := [<<"rabbitmq.tag:management">>, <<"rabbitmq.read:*/*">>] + } = rabbit_oauth2_schema:translate_scope_aliases(CuttlefishConf). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cert_filename(Conf) -> string:concat(?config(data_dir, Conf), "certs/cert.pem"). sort_settings(MapOfListOfSettings) -> maps:map(fun(_K,List) -> +<<<<<<< HEAD lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, MapOfListOfSettings). +======= + lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, + MapOfListOfSettings). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl index 692a9e2ab15d..e0a466b79eab 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl @@ -11,6 +11,10 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). +<<<<<<< HEAD +======= +-include_lib("amqp10_common/include/amqp10_framing.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include_lib("eunit/include/eunit.hrl"). -import(rabbit_ct_client_helpers, [close_connection/1, close_channel/1, @@ -46,8 +50,12 @@ groups() -> more_than_one_resource_server_id_not_allowed_in_one_token, mqtt_expired_token, mqtt_expirable_token, +<<<<<<< HEAD web_mqtt_expirable_token, amqp_expirable_token +======= + web_mqtt_expirable_token +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {token_refresh, [], [ @@ -74,7 +82,18 @@ groups() -> ]}, {rich_authorization_requests, [], [ test_successful_connection_with_rich_authorization_request_token +<<<<<<< HEAD ]} +======= + ]}, + {amqp, [shuffle], + [ + amqp_token_expire, + amqp_token_refresh_expire, + amqp_token_refresh_vhost_permission, + amqp_token_refresh_revoked_permissions + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. %% @@ -101,7 +120,13 @@ init_per_suite(Config) -> end_per_suite(Config) -> rabbit_ct_helpers:run_teardown_steps(Config, rabbit_ct_broker_helpers:teardown_steps()). +<<<<<<< HEAD +======= +init_per_group(amqp, Config) -> + {ok, _} = application:ensure_all_started(rabbitmq_amqp_client), + Config; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_group(_Group, Config) -> %% The broker is managed by {init,end}_per_testcase(). lists:foreach(fun(Value) -> @@ -110,6 +135,11 @@ init_per_group(_Group, Config) -> [<<"vhost1">>, <<"vhost2">>, <<"vhost3">>, <<"vhost4">>]), Config. +<<<<<<< HEAD +======= +end_per_group(amqp, Config) -> + Config; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end_per_group(_Group, Config) -> %% The broker is managed by {init,end}_per_testcase(). lists:foreach(fun(Value) -> @@ -513,6 +543,7 @@ mqtt_expirable_token0(Port, AdditionalOpts, Connect, Config) -> after Millis * 2 -> ct:fail("missing DISCONNECT packet from server") end. +<<<<<<< HEAD amqp_expirable_token(Config) -> {ok, _} = application:ensure_all_started(rabbitmq_amqp_client), @@ -536,6 +567,22 @@ amqp_expirable_token(Config) -> {ok, Connection} = amqp10_client:open_connection(OpnConf), {ok, Session} = amqp10_client:begin_session_sync(Connection), {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), +======= +%% Test that RabbitMQ closes the AMQP 1.0 connection when the token expires. +amqp_token_expire(Config) -> + Seconds = 3, + Millis = Seconds * 1000, + {_Algo, Token} = generate_expirable_token(Config, + [<<"rabbitmq.configure:%2F/*">>, + <<"rabbitmq.write:%2F/*">>, + <<"rabbitmq.read:%2F/*">>], + Seconds), + + %% Send and receive a message. + {Connection, Session, LinkPair} = amqp_init(Token, Config), + QName = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(QName), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"my sender">>, Address), receive {amqp10_event, {link, Sender, credited}} -> ok @@ -548,7 +595,59 @@ amqp_expirable_token(Config) -> {ok, Msg} = amqp10_client:get_msg(Receiver), ?assertEqual([Body], amqp10_msg:body(Msg)), +<<<<<<< HEAD %% In 4 seconds from now, we expect that RabbitMQ disconnects us because our token expired. +======= + %% In 3 seconds from now, we expect that RabbitMQ disconnects us because our token expired. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) + receive {amqp10_event, + {connection, Connection, + {closed, {unauthorized_access, <<"credential expired">>}}}} -> + ok + after Millis * 2 -> + ct:fail("server did not close our connection") + end. + +<<<<<<< HEAD +======= +%% First, test the success case that an OAuth 2.0 token can be renewed via AMQP 1.0. +%% Second, test that the new token expires. +amqp_token_refresh_expire(Config) -> + Seconds = 3, + Millis = Seconds * 1000, + Scopes = [<<"rabbitmq.configure:%2F/*">>, + <<"rabbitmq.write:%2F/*">>, + <<"rabbitmq.read:%2F/*">>], + {_, Token1} = generate_expirable_token(Config, Scopes, Seconds), + + %% Send and receive a message. + {Connection, Session, LinkPair} = amqp_init(Token1, Config), + QName = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(QName), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), + {ok, Sender} = amqp10_client:attach_sender_link(Session, <<"my sender">>, Address), + receive {amqp10_event, {link, Sender, credited}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:send_msg(Sender, amqp10_msg:new(<<"t1">>, <<"m1">>, true)), + {ok, Receiver} = amqp10_client:attach_receiver_link(Session, <<"my receiver">>, Address), + {ok, Msg1} = amqp10_client:get_msg(Receiver), + ?assertEqual([<<"m1">>], amqp10_msg:body(Msg1)), + + %% Renew token before the old one expires. + {_, Token2} = generate_expirable_token(Config, Scopes, Seconds * 2), + ok = rabbitmq_amqp_client:set_token(LinkPair, Token2), + + %% Wait until old token would have expired. + timer:sleep(Millis + 500), + + %% We should still be able to send and receive a message thanks to the new token. + ok = amqp10_client:send_msg(Sender, amqp10_msg:new(<<"t2">>, <<"m2">>, true)), + {ok, Msg2} = amqp10_client:get_msg(Receiver), + ?assertEqual([<<"m2">>], amqp10_msg:body(Msg2)), + + %% In 2.5 seconds from now, we expect that RabbitMQ + %% disconnects us because the new token should expire. receive {amqp10_event, {connection, Connection, {closed, {unauthorized_access, <<"credential expired">>}}}} -> @@ -557,6 +656,179 @@ amqp_expirable_token(Config) -> ct:fail("server did not close our connection") end. +%% Test that RabbitMQ closes the AMQP 1.0 connection if the client +%% submits a new token without any permission to the vhost. +amqp_token_refresh_vhost_permission(Config) -> + {_, Token1} = generate_valid_token(Config), + {Connection, _Session, LinkPair} = amqp_init(Token1, Config), + + {_, Token2} = generate_valid_token(Config, + [<<"rabbitmq.configure:wrongvhost/*">>, + <<"rabbitmq.write:wrongvhost/*">>, + <<"rabbitmq.read:wrongvhost/*">>]), + ok = rabbitmq_amqp_client:set_token(LinkPair, Token2), + receive {amqp10_event, + {connection, Connection, + {closed, {unauthorized_access, Reason}}}} -> + ?assertMatch(<<"access to vhost / failed for new credential:", _/binary>>, + Reason) + after 5000 -> ct:fail({missing_event, ?LINE}) + end. + +%% Test that RabbitMQ closes AMQP 1.0 sessions if the client +%% submits a new token with reduced permissions. +amqp_token_refresh_revoked_permissions(Config) -> + {_, Token1} = generate_expirable_token(Config, + [<<"rabbitmq.configure:%2F/*/*">>, + <<"rabbitmq.write:%2F/*/*">>, + <<"rabbitmq.read:%2F/*/*">>], + 30), + {Connection, Session1, LinkPair} = amqp_init(Token1, Config), + {ok, Session2} = amqp10_client:begin_session_sync(Connection), + {ok, Session3} = amqp10_client:begin_session_sync(Connection), + {ok, Session4} = amqp10_client:begin_session_sync(Connection), + {ok, Session5} = amqp10_client:begin_session_sync(Connection), + {ok, Session6} = amqp10_client:begin_session_sync(Connection), + + {ok, Sender2} = amqp10_client:attach_sender_link_sync( + Session2, <<"sender 2">>, + rabbitmq_amqp_address:exchange(<<"amq.fanout">>)), + receive {amqp10_event, {link, Sender2, credited}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + QName = <<"q1">>, + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), + ok = rabbitmq_amqp_client:bind_queue(LinkPair, QName, <<"amq.topic">>, <<"#">>, #{}), + {ok, Receiver3} = amqp10_client:attach_receiver_link( + Session3, <<"receiver 3">>, rabbitmq_amqp_address:queue(QName)), + receive {amqp10_event, {link, Receiver3, attached}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + {ok, Sender4} = amqp10_client:attach_sender_link_sync(Session4, <<"sender 4">>, null), + receive {amqp10_event, {link, Sender4, credited}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:send_msg( + Sender4, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:queue(QName)}, + amqp10_msg:new(<<"t4">>, <<"m4a">>))), + receive {amqp10_disposition, {accepted, <<"t4">>}} -> ok + after 5000 -> ct:fail({settled_timeout, <<"t4">>}) + end, + + {ok, Sender5} = amqp10_client:attach_sender_link_sync(Session5, <<"sender 5">>, null), + receive {amqp10_event, {link, Sender5, credited}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:send_msg( + Sender5, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:exchange(<<"amq.topic">>, <<"topic-1">>)}, + amqp10_msg:new(<<"t5">>, <<"m5a">>))), + receive {amqp10_disposition, {accepted, <<"t5">>}} -> ok + after 5000 -> ct:fail({settled_timeout, <<"t5">>}) + end, + + XName = <<"e1">>, + ok = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{type => <<"fanout">>}), + {ok, Sender6} = amqp10_client:attach_sender_link_sync( + Session6, <<"sender 6">>, + rabbitmq_amqp_address:exchange(XName)), + receive {amqp10_event, {link, Sender6, credited}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + %% Revoke the previous granted permissions on the default vhost. + {_, Token2} = generate_expirable_token( + Config, + [ + %% Set configure access on q1 and e1 so that we can delete this queue and exchange later. + <<"rabbitmq.configure:%2F/*1/nope">>, + %% Set write access on amq.topic so that we can test the revoked topic permission. + <<"rabbitmq.write:%2F/amq.topic/nope">>, + <<"rabbitmq.read:%2F/nope/nope">>], + 30), + flush(<<"setting token...">>), + ok = rabbitmq_amqp_client:set_token(LinkPair, Token2), + + %% We expect RabbitMQ to close Session2 because we are no longer allowed to write to exchange amq.fanout. + receive + {amqp10_event, + {session, Session2, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, <<"write access to exchange 'amq.fanout' in vhost '/' refused", _/binary>>}}}}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + %% We expect RabbitMQ to close Session3 because we are no longer allowed to read from queue q1. + %% This complies with the user expectation in + %% https://github.com/rabbitmq/rabbitmq-server/discussions/11364 + receive + {amqp10_event, + {session, Session3, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, <<"read access to queue 'q1' in vhost '/' refused", _/binary>>}}}}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:send_msg( + Sender4, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:queue(QName)}, + amqp10_msg:new(<<"t4">>, <<"m4b">>))), + %% We expect RabbitMQ to close Session4 because we are no longer allowed to write to the default exchange. + receive + {amqp10_event, + {session, Session4, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, <<"write access to exchange 'amq.default' in vhost '/' refused", _/binary>>}}}}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:send_msg( + Sender5, + amqp10_msg:set_properties( + #{to => rabbitmq_amqp_address:exchange(<<"amq.topic">>, <<"topic-1">>)}, + amqp10_msg:new(<<"t5">>, <<"m5b">>))), + %% We expect RabbitMQ to close Session5 because we are no longer allowed to write to topic topic-1. + receive + {amqp10_event, + {session, Session5, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, <<"write access to topic 'topic-1' in exchange" + " 'amq.topic' in vhost '/' refused", _/binary>>}}}}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + %% We expect RabbitMQ to close Session6 because we are no longer allowed to write to exchange e1. + receive + {amqp10_event, + {session, Session6, + {ended, + #'v1_0.error'{ + condition = ?V_1_0_AMQP_ERROR_UNAUTHORIZED_ACCESS, + description = {utf8, <<"write access to exchange 'e1' in vhost '/' refused", _/binary>>}}}}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + ?assertMatch({ok, #{message_count := 2}}, + rabbitmq_amqp_client:delete_queue(LinkPair, QName)), + ok = rabbitmq_amqp_client:delete_exchange(LinkPair, XName), + ok = amqp10_client:end_session(Session1), + ok = amqp10_client:close_connection(Connection). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_connection_with_complex_claim_as_a_map(Config) -> {_Algo, Token} = generate_valid_token_with_extra_fields( Config, @@ -787,3 +1059,33 @@ test_failed_connection_with_non_existent_scope_alias_in_scope_field(Config) -> more_than_one_resource_server_id_not_allowed_in_one_token(Config) -> {_Algo, Token} = generate_valid_token(Config, <<"rmq.configure:*/*">>, [<<"prod">>, <<"dev">>]), {error, _} = open_unmanaged_connection(Config, 0, <<"username">>, Token). +<<<<<<< HEAD +======= + +amqp_init(Token, Config) -> + OpnConf = amqp_connection_config(Token, Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + receive {amqp10_event, {connection, Connection, opened}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), + {Connection, Session, LinkPair}. + +amqp_connection_config(Token, Config) -> + Host = proplists:get_value(rmq_hostname, Config), + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + #{address => Host, + port => Port, + container_id => <<"my container">>, + sasl => {plain, <<>>, Token}}. + +flush(Prefix) -> + receive + Msg -> + ct:pal("~p flushed: ~p~n", [Prefix, Msg]), + flush(Prefix) + after 1 -> + ok + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl index c8b3f296e213..369bd3ccc85d 100644 --- a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl +++ b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl @@ -11,6 +11,7 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +<<<<<<< HEAD all() -> @@ -24,6 +25,29 @@ all() -> test_unsuccessful_access_without_scopes, test_successful_access_with_a_token_with_variables_in_scopes, test_successful_access_with_a_parsed_token, +======= +-include("oauth2.hrl"). + +-import(rabbit_auth_backend_oauth2, [ + user_login_authentication/2, + user_login_authorization/2, + normalize_token_scope/2, + check_vhost_access/3]). +-import(rabbit_oauth2_resource_server, [ + new_resource_server/1 +]). + +all() -> + [ + filter_matching_scope_prefix_and_drop_it, + normalize_token_scopes_with_scope_prefix, + normalize_token_scope_from_space_separated_list_in_scope_claim, + normalize_token_scope_without_scope_claim, + + unsuccessful_access_without_scopes, + successful_access_with_a_token_with_variables_in_scopes, + successful_access_with_a_parsed_token, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_access_with_a_token_that_has_tag_scopes, test_unsuccessful_access_with_a_bogus_token, test_restricted_vhost_access_with_a_valid_token, @@ -31,10 +55,16 @@ all() -> test_token_expiration, test_invalid_signature, test_incorrect_kid, +<<<<<<< HEAD test_post_process_token_payload, test_post_process_token_payload_keycloak, test_post_process_payload_rich_auth_request, test_post_process_payload_rich_auth_request_using_regular_expression_with_cluster, +======= + normalize_token_scope_with_keycloak_scopes, + normalize_token_scope_with_rich_auth_request, + normalize_token_scope_with_rich_auth_request_using_regular_expression_with_cluster, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_field, test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field, test_username_from, @@ -57,7 +87,11 @@ groups() -> test_successful_authentication_without_scopes, test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_source_field, test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_scope_source_field, +<<<<<<< HEAD test_post_process_token_payload_complex_claims, +======= + normalize_token_scope_with_additional_scopes_complex_claims, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_and_custom_scope_prefix ]} @@ -66,6 +100,7 @@ groups() -> init_per_suite(Config) -> application:load(rabbitmq_auth_backend_oauth2), Env = application:get_all_env(rabbitmq_auth_backend_oauth2), +<<<<<<< HEAD Config1 = rabbit_ct_helpers:set_config(Config, {env, Env}), rabbit_ct_helpers:run_setup_steps(Config1, []). @@ -76,6 +111,12 @@ end_per_suite(Config) -> application:set_env(rabbitmq_auth_backend_oauth2, K, V) end, Env), +======= + lists:foreach(fun({K, _V}) -> unset_env(K) end, Env), + rabbit_ct_helpers:run_setup_steps(Config, []). + +end_per_suite(Config) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_helpers:run_teardown_steps(Config). init_per_group(with_rabbitmq_node, Config) -> @@ -91,7 +132,11 @@ init_per_group(with_rabbitmq_node, Config) -> rabbit_ct_helpers:run_steps(Config2, rabbit_ct_broker_helpers:setup_steps()); init_per_group(with_resource_server_id, Config) -> +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), +======= + set_env(resource_server_id, <<"rabbitmq">>), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; init_per_group(_, Config) -> @@ -104,6 +149,7 @@ end_per_group(_, Config) -> application:unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), Config. +<<<<<<< HEAD init_per_testcase(test_post_process_token_payload_complex_claims, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"additional_rabbitmq_scopes">>), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq-resource">>), @@ -134,6 +180,8 @@ end_per_testcase(test_post_process_token_payload_complex_claims, Config) -> end_per_testcase(_, Config) -> Config. +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% @@ -145,6 +193,7 @@ end_per_testcase(_, Config) -> -define(RESOURCE_SERVER_TYPE, <<"rabbitmq-type">>). -define(DEFAULT_SCOPE_PREFIX, <<"rabbitmq.">>). +<<<<<<< HEAD test_post_process_token_payload(_) -> ArgumentsExpections = [ {{[<<"rabbitmq">>, <<"hare">>], [<<"read">>, <<"write">>, <<"configure">>]}, @@ -239,6 +288,70 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste Pairs = [ +======= + +normalize_token_scope_with_keycloak_scopes(_) -> + Pairs = [ + %% common case + { + "common case", + #{<<"permissions">> => + [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, + <<"rsname">> => <<"allvhost">>, + <<"scopes">> => [<<"rabbitmq-resource.read:*/*">>]}, + #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, + <<"rsname">> => <<"vhost1">>, + <<"scopes">> => [<<"rabbitmq-resource.write:vhost1/*">>]}, + #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, + <<"rsname">> => <<"Default Resource">>, + <<"scopes">> => [<<"unknown-resource.write:vhost1/*">>]} + ] + }, + [<<"read:*/*">>, <<"write:vhost1/*">>] + }, + { + "one scopes field with a string instead of an array", + #{<<"permissions">> => + [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, + <<"rsname">> => <<"allvhost">>, + <<"scopes">> => <<"rabbitmq-resource.read:*/*">>}, + #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, + <<"rsname">> => <<"vhost1">>, + <<"scopes">> => [<<"unknown-resource-read">>]}, + #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, + <<"rsname">> => <<"Default Resource">>}]}, + [<<"read:*/*">>] + }, + { + "no scopes field in permissions", + #{<<"permissions">> => + [#{<<"rsid">> => <<"2c390fe4-02ad-41c7-98a2-cebb8c60ccf1">>, + <<"rsname">> => <<"allvhost">>}, + #{<<"rsid">> => <<"e7f12e94-4c34-43d8-b2b1-c516af644cee">>, + <<"rsname">> => <<"vhost1">>}, + #{<<"rsid">> => <<"12ac3d1c-28c2-4521-8e33-0952eff10bd9">>, + <<"rsname">> => <<"Default Resource">>}]}, + [] + }, + { + "no permissions", + #{<<"permissions">> => []}, + [] + }, + {"missing permissions key", #{}, []} + ], + + lists:foreach(fun({Case, Authorization, ExpectedScope}) -> + ResourceServer = new_resource_server(<<"rabbitmq-resource">>), + Token0 = #{<<"authorization">> => Authorization}, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(ExpectedScope, uaa_jwt:get_scope(Token), Case) + end, Pairs). + +normalize_token_scope_with_rich_auth_request_using_regular_expression_with_cluster(_) -> + + Pairs = [ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) { "should filter out those permisions whose locations do not refer to cluster : {resource_server_id}", [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, <<"locations">> => [<<"cluster:rabbitmq-test">>], @@ -249,7 +362,11 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste <<"actions">> => [<<"read">>] } ], +<<<<<<< HEAD [<<"rabbitmq-test.read:*/*/*">> ] +======= + [<<"read:*/*/*">> ] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }, { "can use regular expression on any location's attribute ", @@ -258,7 +375,11 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste <<"actions">> => [<<"read">>] } ], +<<<<<<< HEAD [<<"rabbitmq-test.read:^finance-*/*/*">> ] +======= + [<<"read:^finance-*/*/*">> ] +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }, { "should filter out any location which does not match the cluster's pattern ", @@ -274,6 +395,7 @@ test_post_process_payload_rich_auth_request_using_regular_expression_with_cluste lists:foreach( fun({Case, Permissions, ExpectedScope}) -> +<<<<<<< HEAD Payload = post_process_payload_with_rich_auth_request(<<"rabbitmq-test">>, Permissions), ?assertEqual(lists:sort(ExpectedScope), lists:sort(maps:get(<<"scope">>, Payload)), Case) end, Pairs). @@ -674,6 +796,426 @@ test_successful_authorization_without_scopes(_) -> {ok, _ } = rabbit_auth_backend_oauth2:user_login_authorization(Username, [{password, Token}]). +======= + ResourceServer0 = new_resource_server(<<"rabbitmq-test">>), + ResourceServer = ResourceServer0#resource_server{ + resource_server_type = ?RESOURCE_SERVER_TYPE + }, + Token0 = #{<<"authorization_details">> => Permissions}, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(lists:sort(ExpectedScope), + lists:sort(uaa_jwt:get_scope(Token)), Case) + end, Pairs). + +normalize_token_scope_with_rich_auth_request(_) -> + + Pairs = [ + { "should merge all permissions for the current cluster", + [ + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:finance/vhost:primary-*">>], + <<"actions">> => [<<"configure">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"management">> ] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"administrator">> ] + } + ], + [ <<"tag:management">>, <<"tag:administrator">> ] + }, + { "should filter out those permisions whose type does not match ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => <<"unknown">>, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out those permisions whose type is the empty string", + [ + #{<<"type">> => <<>>, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + } + ], + [ ] + }, + { "should filter out those permisions with empty string action", + [ + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => <<>> + } + ], + [ ] + }, + { "should filter out those permisions whose locations do not refer to cluster : {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq-other">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out those permisions whose locations' regexpr do not match the cluster : {resource_server_id} ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbit*">>], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:*">>], + <<"actions">> => [<<"write">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq-other">>], + <<"actions">> => [<<"configure">>] + } + ], + [<<"read:*/*/*">>, <<"write:*/*/*">> ] + }, + { "should ignore permissions without actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbit*">>], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">>] + }, + { "should ignore permissions without locations", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"actions">> => [<<"read">>] + } + ], + [] + }, + { "should ignore unknown actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>], + <<"actions">> => [<<"read2">>, <<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should filter out locations with permissions not meant for {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq">>, <<"cluster:unknown">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should produce a scope for every (action, location) permutation for all locations meant for {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, + <<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:a/*/*">>, <<"read:b/*/*">> ] + }, + { "should support all known user tags ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, + <<"cluster:other">> ], + <<"actions">> => [ + <<"management">>, <<"policymaker">>, <<"management">>, + <<"monitoring">>] + } + ], + [<<"tag:management">>, <<"tag:policymaker">>, + <<"tag:management">>, <<"tag:monitoring">> ] + }, + { "should produce a scope for every user tag action but only for the clusters that match {resource_server_id}", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">>, + <<"cluster:other">> ], + <<"actions">> => [<<"management">>, <<"policymaker">>] + } + ], + [<<"tag:management">>, <<"tag:policymaker">> ] + }, + { "should produce as scope for every location meant for {resource_server_id} multiplied by actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:a">>, <<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"read">>, <<"write">>] + } + ], + [<<"read:a/*/*">>, <<"read:b/*/*">>, <<"write:a/*/*">>, <<"write:b/*/*">> ] + }, + { "should accept single value locations", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq">>, + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/*">> ] + }, + { "should accept single value actions", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq">>, + <<"actions">> => <<"read">> + } + ], + [<<"read:*/*/*">> ] + }, + { "should merge all scopes produced by each permission", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:a">> ], + <<"actions">> => [<<"read">>] + }, + #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:b">> ], + <<"actions">> => [<<"write">>] + } + ], + [<<"read:a/*/*">>, <<"write:b/*/*">> ] + }, + { "can grant permission to a queue in any virtual host", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/b/*">> ] + }, + { "can grant permission to an exchange in any virtual host", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/exchange:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/b/*">> ] + }, + { "cannot specify both exchange and queue unless they have the same value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:b/exchange:c">> ], + <<"actions">> => [<<"read">>] + } + ], + [] + }, + { "can specify exchange and queue when have same value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:*/exchange:*">> ], + <<"actions">> => [<<"read">>] + } + ], + [ <<"read:*/*/*">> ] + }, + { "can specify routing-key only -> on any vhost and on any queue if that makes sense ", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/routing-key:b">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/b">> ] + }, + { "can specify vhost, queue or exchange and routing-key that combine fixed values and wildcards", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance-*/*-invoice/r-*">> ] + }, + { "should ignore any location's attribute other than the supported ones", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/unknown:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*-invoice/r-*">> ] + }, + { "should not matter the location's attributes order", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/queue:invoices/vhost:finance/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance/invoices/r-*">> ] + }, + { "should ignore locations like //", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq//routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/r-*">> ] + }, + { "should default to wildcard those attributes with empty value", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"cluster:rabbitmq/queue:/vhost:/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:*/*/r-*">> ] + }, + { "should ignore any location path element which is not compliant with : format", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [ + <<"some-prefix-value/cluster:rabbitmq/vhost:finance-*/queue:*-invoice/routing-key:r-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:finance-*/*-invoice/r-*">> ] + }, + { "can use regular expression on any location's attribute", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => [<<"cluster:rabbitmq/vhost:^finance-*">> ], + <<"actions">> => [<<"read">>] + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "can use single string value for location", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, + <<"actions">> => [<<"read">>] + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "can use single string value for action", + [ #{<<"type">> => ?RESOURCE_SERVER_TYPE, + <<"locations">> => <<"cluster:rabbitmq/vhost:^finance-*">>, + <<"actions">> => <<"read">> + } + ], + [<<"read:^finance-*/*/*">> ] + }, + { "should ignore empty permission lists", + [], + [] + } + ], + + lists:foreach(fun({Case, Permissions, ExpectedScope0}) -> + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server{ + resource_server_type = ?RESOURCE_SERVER_TYPE + }, + Token0 = #{<<"authorization_details">> => Permissions}, + Token = normalize_token_scope(ResourceServer, Token0), + ExpectedScopes = lists:sort(ExpectedScope0), + ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), + ?assertEqual(ExpectedScopes, ActualScopes, Case) + end, Pairs). + +normalize_token_scope_with_additional_scopes_complex_claims(_) -> + Pairs = [ + { + "claims in form of binary", + <<"rabbitmq.rabbitmq-resource.read:*/* rabbitmq.rabbitmq-resource-read">>, + [<<"read:*/*">>] + }, + {"claims in form of binary - empty result", <<>>, []}, + { + "claims in form of list", + [<<"rabbitmq.rabbitmq-resource.read:*/*">>, + <<"rabbitmq2.rabbitmq-resource-read">>], + [<<"read:*/*">>] + }, + {"claims in form of list - empty result", [], []}, + { + "claims are map with list content", + #{<<"rabbitmq">> => + [<<"rabbitmq-resource.read:*/*">>, + <<"rabbitmq-resource-read">>], + <<"rabbitmq3">> => + [<<"rabbitmq-resource.write:*/*">>, + <<"rabbitmq-resource-write">>]}, + [<<"read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>] + }, + { + "claims are map with list content - empty result", + #{<<"rabbitmq2">> => + [<<"rabbitmq-resource.read:*/*">>, + <<"rabbitmq-resource-read">>]}, + [] + }, + { + "claims are map with binary content", + #{ <<"rabbitmq">> => <<"rabbitmq-resource.read:*/* rabbitmq-resource-read">>, + <<"rabbitmq3">> => <<"rabbitmq-resource.write:*/* rabbitmq-resource-write">>}, + [<<"rabbitmq.rabbitmq-resource.read:*/*">>, <<"rabbitmq.rabbitmq-resource-read">>] + }, + { + "claims are map with binary content - empty result", + #{<<"rabbitmq2">> => <<"rabbitmq-resource.read:*/* rabbitmq-resource-read">>}, [] + }, + { + "claims are map with empty binary content - empty result", + #{<<"rabbitmq">> => <<>>}, [] + }, + { + "claims are map with empty list content - empty result", + #{<<"rabbitmq">> => []}, [] + }, + { + "no extra claims provided", + [], [] + }, + { + "no extra claims provided", #{}, [] + }], + lists:foreach(fun({Case, Authorization, ExpectedScope0}) -> + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server{ + scope_prefix = <<"rabbitmq.rabbitmq-resource.">>, + additional_scopes_key = <<"custom-key">> + }, + Token0 = #{<<"custom-key">> => Authorization}, + Token = normalize_token_scope(ResourceServer, Token0), + ExpectedScopes = lists:sort(ExpectedScope0), + ActualScopes = lists:sort(uaa_jwt:get_scope(Token)), + ?assertEqual(ExpectedScopes, ActualScopes, Case) + end, Pairs). + +test_successful_authentication_without_scopes(_) -> + Jwk = ?UTIL_MOD:fixture_jwk(), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), + + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + + {ok, #auth_user{username = Username} } = + user_login_authentication(Username, [{password, Token}]). + +test_successful_authorization_without_scopes(_) -> + Jwk = ?UTIL_MOD:fixture_jwk(), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + set_env(key_config, UaaEnv), + + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + + {ok, _ } = user_login_authorization(Username, [{password, Token}]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_access_with_a_token(_) -> %% Generate a token with JOSE @@ -681,6 +1223,7 @@ test_successful_access_with_a_token(_) -> %% Check user access granted by token Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), VHost = <<"vhost">>, @@ -693,24 +1236,49 @@ test_successful_access_with_a_token(_) -> % rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token}), ?assertEqual(true, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"vhost">>, none)), +======= + set_env(key_config, UaaEnv), + + VHost = <<"vhost">>, + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + + {ok, #auth_user{username = Username} = User} = + user_login_authentication(Username, [{password, Token}]), + + ?assertEqual(true, check_vhost_access(User, <<"vhost">>, none)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_resource_access_granted(User, VHost, <<"foo">>, configure), assert_resource_access_granted(User, VHost, <<"foo">>, write), assert_resource_access_granted(User, VHost, <<"bar">>, read), assert_resource_access_granted(User, VHost, custom, <<"bar">>, read), +<<<<<<< HEAD assert_topic_access_granted(User, VHost, <<"bar">>, read, #{routing_key => <<"#/foo">>}). test_successful_access_with_a_token_with_variables_in_scopes(_) -> +======= + assert_topic_access_granted(User, VHost, <<"bar">>, read, + #{routing_key => <<"#/foo">>}). + +successful_access_with_a_token_with_variables_in_scopes(_) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Generate a token with JOSE %% Check authorization with the token %% Check user access granted by token Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), +======= + set_env(key_config, UaaEnv), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) VHost = <<"my-vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs( +<<<<<<< HEAD ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token([<<"rabbitmq.read:{vhost}/*/{sub}">>]), Username), Jwk), {ok, #auth_user{username = Username} = User} = @@ -730,11 +1298,36 @@ test_successful_access_with_a_parsed_token(_) -> {ok, _ } = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{rabbit_auth_backend_oauth2, Impl}]). +======= + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( + [<<"rabbitmq.read:{vhost}/*/{sub}">>]), Username), + Jwk), + {ok, #auth_user{username = Username} = User} = + user_login_authentication(Username, #{password => Token}), + + assert_topic_access_granted(User, VHost, <<"bar">>, read, + #{routing_key => Username}). + +successful_access_with_a_parsed_token(_) -> + Jwk = ?UTIL_MOD:fixture_jwk(), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + set_env(key_config, UaaEnv), + + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + {ok, #auth_user{impl = Impl} } = + user_login_authentication(Username, [{password, Token}]), + + {ok, _ } = + user_login_authentication(Username, [{rabbit_auth_backend_oauth2, Impl}]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_access_with_a_token_that_has_tag_scopes(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( @@ -742,13 +1335,30 @@ test_successful_access_with_a_token_that_has_tag_scopes(_) -> {ok, #auth_user{username = Username, tags = [management, policymaker]}} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]). +======= + set_env(key_config, UaaEnv), + Username = <<"username">>, + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token( + [<<"rabbitmq.tag:management">>, <<"rabbitmq.tag:policymaker">>]), + Username), Jwk), + + {ok, #auth_user{username = Username, tags = [management, policymaker]}} = + user_login_authentication(Username, [{password, Token}]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), Alias = <<"client-alias-1">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + Alias = <<"client-alias-1">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Alias => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -766,7 +1376,11 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field( ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = +<<<<<<< HEAD rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -786,10 +1400,17 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field( test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_and_custom_scope_prefix(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, scope_prefix, <<>>), Alias = <<"client-alias-1">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + set_env(scope_prefix, <<>>), + Alias = <<"client-alias-1">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Alias => [ <<"configure:vhost/one">>, <<"write:vhost/two">>, @@ -807,7 +1428,11 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_ ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = +<<<<<<< HEAD rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -827,11 +1452,19 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_scope_field_ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), Role1 = <<"client-aliases-1">>, Role2 = <<"client-aliases-2">>, Role3 = <<"client-aliases-3">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + Role1 = <<"client-aliases-1">>, + Role2 = <<"client-aliases-2">>, + Role3 = <<"client-aliases-3">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Role1 => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.tag:management">> @@ -850,10 +1483,18 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( +<<<<<<< HEAD ?UTIL_MOD:token_with_scope_alias_in_scope_field([Role1, Role2, Role3]), Username), Jwk), {ok, #auth_user{username = Username} = AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + ?UTIL_MOD:token_with_scope_alias_in_scope_field([Role1, Role2, Role3]), + Username), Jwk), + + {ok, #auth_user{username = Username} = AuthUser} = + user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -872,10 +1513,17 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_scope_fi test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Alias = <<"client-alias-33">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), + Alias = <<"client-alias-33">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <<"non-existent-alias-23948sdkfjsdof8">> => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -890,7 +1538,11 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_fie Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( ?UTIL_MOD:token_with_scope_alias_in_scope_field(Alias), Username), Jwk), +<<<<<<< HEAD {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_denied(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -909,10 +1561,17 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_scope_fie test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), Alias = <<"client-alias-1">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), + Alias = <<"client-alias-1">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Alias => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -925,9 +1584,16 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_ VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( +<<<<<<< HEAD ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), + Username), Jwk), + + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -946,12 +1612,21 @@ test_successful_access_with_a_token_that_uses_single_scope_alias_in_extra_scope_ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), Role1 = <<"client-aliases-1">>, Role2 = <<"client-aliases-2">>, Role3 = <<"client-aliases-3">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), + Role1 = <<"client-aliases-1">>, + Role2 = <<"client-aliases-2">>, + Role3 = <<"client-aliases-3">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Role1 => [ <<"rabbitmq.configure:vhost/one">> ], @@ -969,9 +1644,16 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc Username = <<"username">>, Claims = [Role1, Role2, Role3], Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( +<<<<<<< HEAD ?UTIL_MOD:token_with_scope_alias_in_claim_field(Claims, [<<"unrelated">>]), Username), Jwk), {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Claims, [<<"unrelated">>]), + Username), Jwk), + + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -990,11 +1672,19 @@ test_successful_access_with_a_token_that_uses_multiple_scope_aliases_in_extra_sc test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_scope_source_field(_) -> Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, extra_scopes_source, <<"claims">>), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Alias = <<"client-alias-11">>, application:set_env(rabbitmq_auth_backend_oauth2, scope_aliases, #{ +======= + set_env(key_config, UaaEnv), + set_env(extra_scopes_source, <<"claims">>), + set_env(resource_server_id, <<"rabbitmq">>), + Alias = <<"client-alias-11">>, + set_env(scope_aliases, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <<"non-existent-client-alias-9238923789">> => [ <<"rabbitmq.configure:vhost/one">>, <<"rabbitmq.write:vhost/two">>, @@ -1007,9 +1697,16 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_sco VHost = <<"vhost">>, Username = <<"username">>, Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( +<<<<<<< HEAD ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), Username), Jwk), {ok, AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + ?UTIL_MOD:token_with_scope_alias_in_claim_field(Alias, [<<"unrelated">>]), + Username), Jwk), + + {ok, AuthUser} = user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_denied(AuthUser, VHost), assert_vhost_access_denied(AuthUser, <<"some-other-vhost">>), @@ -1027,11 +1724,16 @@ test_unsuccessful_access_with_a_token_that_uses_missing_scope_alias_in_extra_sco test_unsuccessful_access_with_a_bogus_token(_) -> Username = <<"username">>, +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), +======= + set_env(resource_server_id, <<"rabbitmq">>), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Jwk0 = ?UTIL_MOD:fixture_jwk(), Jwk = Jwk0#{<<"k">> => <<"bm90b2tlbmtleQ">>}, UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), ?assertMatch({refused, _, _}, @@ -1048,11 +1750,31 @@ test_unsuccessful_access_without_scopes(_) -> {ok, #auth_user{username = Username, tags = [], impl = _CredentialsFun } = AuthUser} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + set_env(key_config, UaaEnv), + + ?assertMatch({refused, _, _}, user_login_authentication(Username, + [{password, <<"not a token">>}])). + +unsuccessful_access_without_scopes(_) -> + Username = <<"username">>, + set_env(resource_server_id, <<"rabbitmq">>), + + Jwk = ?UTIL_MOD:fixture_jwk(), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:token_without_scopes(), Username), Jwk), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + set_env(key_config, UaaEnv), + + {ok, #auth_user{username = Username, tags = [], impl = _CredentialsFun } + = AuthUser} = user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_denied(AuthUser, <<"vhost">>). test_restricted_vhost_access_with_a_valid_token(_) -> Username = <<"username">>, +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Jwk = ?UTIL_MOD:fixture_jwk(), @@ -1066,10 +1788,27 @@ test_restricted_vhost_access_with_a_valid_token(_) -> %% access to a different vhost ?assertEqual(false, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"different vhost">>, none)). +======= + set_env(resource_server_id, <<"rabbitmq">>), + + Jwk = ?UTIL_MOD:fixture_jwk(), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + set_env(key_config, UaaEnv), + + %% this user can authenticate successfully and access certain vhosts + {ok, #auth_user{username = Username, tags = []} = User} = + user_login_authentication(Username, [{password, Token}]), + + %% access to a different vhost + ?assertEqual(false, check_vhost_access(User, <<"different vhost">>, none)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_insufficient_permissions_in_a_valid_token(_) -> VHost = <<"vhost">>, Username = <<"username">>, +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Jwk = ?UTIL_MOD:fixture_jwk(), @@ -1079,35 +1818,70 @@ test_insufficient_permissions_in_a_valid_token(_) -> {ok, #auth_user{username = Username} = User} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + set_env(resource_server_id, <<"rabbitmq">>), + + Jwk = ?UTIL_MOD:fixture_jwk(), + Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], + set_env(key_config, UaaEnv), + + {ok, #auth_user{username = Username} = User} = + user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% access to these resources is not granted assert_resource_access_denied(User, VHost, <<"foo1">>, configure), assert_resource_access_denied(User, VHost, <<"bar">>, write), +<<<<<<< HEAD assert_topic_access_refused(User, VHost, <<"bar">>, read, #{routing_key => <<"foo/#">>}). +======= + assert_topic_access_refused(User, VHost, <<"bar">>, read, + #{routing_key => <<"foo/#">>}). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_invalid_signature(_) -> Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), WrongJwk = ?UTIL_MOD:fixture_jwk("wrong", <<"GawgguFyGrWKav7AX4VKUg">>), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, WrongJwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), ?assertMatch({refused, _, [signature_invalid]}, rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}])). +======= + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), + TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), + Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), + ?assertMatch({refused, _, [signature_invalid]}, + user_login_authentication(Username, [{password, Token}])). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_token_expiration(_) -> VHost = <<"vhost">>, Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), UaaEnv = [{signing_keys, #{<<"token-key">> => {map, Jwk}}}], +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, key_config, UaaEnv), application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), {ok, #auth_user{username = Username} = User} = rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}]), +======= + set_env(key_config, UaaEnv), + set_env(resource_server_id, <<"rabbitmq">>), + TokenData = ?UTIL_MOD:token_with_sub(?UTIL_MOD:expirable_token(), Username), + Token = ?UTIL_MOD:sign_token_hs(TokenData, Jwk), + {ok, #auth_user{username = Username} = User} = + user_login_authentication(Username, [{password, Token}]), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_resource_access_granted(User, VHost, <<"foo">>, configure), assert_resource_access_granted(User, VHost, <<"foo">>, write), @@ -1118,16 +1892,26 @@ test_token_expiration(_) -> ?UTIL_MOD:wait_for_token_to_expire(), #{<<"exp">> := Exp} = TokenData, +<<<<<<< HEAD ExpectedError = "Provided JWT token has expired at timestamp " ++ integer_to_list(Exp) ++ " (validated at " ++ integer_to_list(Exp) ++ ")", assert_resource_access_errors(ExpectedError, User, VHost, <<"foo">>, configure), ?assertMatch({refused, _, _}, rabbit_auth_backend_oauth2:user_login_authentication(Username, [{password, Token}])). +======= + ExpectedError = "Provided JWT token has expired at timestamp " ++ + integer_to_list(Exp) ++ " (validated at " ++ integer_to_list(Exp) ++ ")", + assert_resource_access_errors(ExpectedError, User, VHost, <<"foo">>, configure), + + ?assertMatch({refused, _, _}, + user_login_authentication(Username, [{password, Token}])). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_incorrect_kid(_) -> AltKid = <<"other-token-key">>, Username = <<"username">>, Jwk = ?UTIL_MOD:fixture_jwk(), +<<<<<<< HEAD application:set_env(rabbitmq_auth_backend_oauth2, resource_server_id, <<"rabbitmq">>), Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, AltKid, true), ?assertMatch({refused, "Authentication using an OAuth 2/JWT token failed: ~tp", [{error,{missing_oauth_provider_attributes, [issuer]}}]}, @@ -1138,6 +1922,23 @@ login_and_check_vhost_access(Username, Token, Vhost) -> rabbit_auth_backend_oauth2:user_login_authentication(Username, #{password => Token}), ?assertEqual(true, rabbit_auth_backend_oauth2:check_vhost_access(User, <<"vhost">>, Vhost)). +======= + unset_env(key_config), + set_env(resource_server_id, <<"rabbitmq">>), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, + AltKid, true), + ?assertMatch( + {refused, "Authentication using an OAuth 2/JWT token failed: ~tp", + [{error,{missing_oauth_provider_attributes, [issuer]}}]}, + user_login_authentication(Username, #{password => Token})). + +login_and_check_vhost_access(Username, Token, Vhost) -> + {ok, #auth_user{username = Username} = User} = + user_login_authentication(Username, #{password => Token}), + + ?assertEqual(true, check_vhost_access(User, <<"vhost">>, Vhost)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_command_json(Config) -> Username = <<"username">>, @@ -1146,9 +1947,18 @@ test_command_json(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], +<<<<<<< HEAD #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), json => Json}), Token = ?UTIL_MOD:sign_token_hs(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). +======= + #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), + json => Json}), + Token = ?UTIL_MOD:sign_token_hs( + ?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_username_from(_) -> Pairs = [ @@ -1185,7 +1995,12 @@ test_username_from(_) -> lists:foreach( fun( {Comment, PreferredUsernameClaims, Token, ExpectedUsername}) -> +<<<<<<< HEAD ActualUsername = rabbit_auth_backend_oauth2:username_from(PreferredUsernameClaims, Token), +======= + ActualUsername = rabbit_auth_backend_oauth2:username_from( + PreferredUsernameClaims, Token), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(ExpectedUsername, ActualUsername, Comment) end, Pairs). @@ -1202,10 +2017,20 @@ test_command_pem_file(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], +<<<<<<< HEAD #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem_file => PublicKeyFile}), Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:fixture_token(), Jwk, <<"token-key">>), rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). +======= + #{node => rabbit_ct_broker_helpers:get_node_config( + Config, 0, nodename), pem_file => PublicKeyFile}), + + Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:fixture_token(), + Jwk, <<"token-key">>), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_command_pem(Config) -> @@ -1218,10 +2043,20 @@ test_command_pem(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], +<<<<<<< HEAD #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem => Pem}), Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk, <<"token-key">>), rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, login_and_check_vhost_access, [Username, Token, none]). +======= + #{node => rabbit_ct_broker_helpers:get_node_config( + Config, 0, nodename), pem => Pem}), + + Token = ?UTIL_MOD:sign_token_rsa(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk, <<"token-key">>), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) test_command_pem_no_kid(Config) -> Username = <<"username">>, @@ -1233,6 +2068,7 @@ test_command_pem_no_kid(Config) -> 'Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand':run( [<<"token-key">>], +<<<<<<< HEAD #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), pem => Pem}), Token = ?UTIL_MOD:sign_token_no_kid(?UTIL_MOD:token_with_sub(?UTIL_MOD:fixture_token(), Username), Jwk), @@ -1240,6 +2076,18 @@ test_command_pem_no_kid(Config) -> test_own_scope(_) -> +======= + #{node => rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename), + pem => Pem}), + + Token = ?UTIL_MOD:sign_token_no_kid(?UTIL_MOD:token_with_sub( + ?UTIL_MOD:fixture_token(), Username), Jwk), + rabbit_ct_broker_helpers:rpc(Config, 0, unit_SUITE, + login_and_check_vhost_access, [Username, Token, none]). + + +filter_matching_scope_prefix_and_drop_it(_) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Examples = [ {<<"foo.">>, [<<"foo">>, <<"foo.bar">>, <<"bar.foo">>, <<"one.two">>, <<"foobar">>, <<"foo.other.third">>], @@ -1250,6 +2098,7 @@ test_own_scope(_) -> ], lists:map( fun({ScopePrefix, Src, Dest}) -> +<<<<<<< HEAD Dest = rabbit_auth_backend_oauth2:filter_scopes(Src, ScopePrefix) end, Examples). @@ -1321,11 +2170,67 @@ test_validate_payload_when_verify_aud_false(_) -> ?assertEqual({ok, #{<<"aud">> => [<<"unknown">>], <<"scope">> => [<<"bar">>, <<"other.third">>]}}, rabbit_auth_backend_oauth2:validate_payload(?RESOURCE_SERVER_ID, WithAudWithUnknownResourceId, ?DEFAULT_SCOPE_PREFIX)). +======= + Dest = rabbit_oauth2_scope:filter_matching_scope_prefix_and_drop_it( + Src, ScopePrefix) + end, + Examples). + +normalize_token_scopes_with_scope_prefix(_) -> + Scenarios = [ + { + <<"">>, + #{ + ?SCOPE_JWT_FIELD => [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ] + }, + [<<"foo">>, <<"foo.bar">>, <<"foo.other.third">> ] + }, + { + <<"some-prefix::">>, + #{ + ?SCOPE_JWT_FIELD => [ + <<"some-prefix::foo">>, <<"foo.bar">>, + <<"some-prefix::other.third">> ] + }, + [<<"foo">>, <<"other.third">>] + } + ], + + lists:map(fun({ ScopePrefix, Token0, ExpectedScopes}) -> + ResourceServer0 = new_resource_server(?RESOURCE_SERVER_ID), + ResourceServer = ResourceServer0#resource_server { + scope_prefix = ScopePrefix + }, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual(ExpectedScopes, uaa_jwt:get_scope(Token)) + end, Scenarios). + +normalize_token_scope_from_space_separated_list_in_scope_claim(_) -> + ResourceServer = new_resource_server(?RESOURCE_SERVER_ID), + Token0 = #{ + ?SCOPE_JWT_FIELD => <<"foo rabbitmq.bar bar.foo one.two foobar rabbitmq.other.third">> + }, + Token = normalize_token_scope(ResourceServer, Token0), + ?assertEqual([<<"bar">>, <<"other.third">>], uaa_jwt:get_scope(Token)). + +normalize_token_scope_without_scope_claim(_) -> + ResourceServer = new_resource_server(?RESOURCE_SERVER_ID), + Token0 = #{ }, + ?assertEqual([], uaa_jwt:get_scope(normalize_token_scope(ResourceServer, Token0))). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% %% Helpers %% +<<<<<<< HEAD +======= +set_env(Par, Var) -> + application:set_env(rabbitmq_auth_backend_oauth2, Par, Var). +unset_env(Par) -> + application:unset_env(rabbitmq_auth_backend_oauth2, Par). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_vhost_access_granted(AuthUser, VHost) -> assert_vhost_access_response(true, AuthUser, VHost). @@ -1334,6 +2239,7 @@ assert_vhost_access_denied(AuthUser, VHost) -> assert_vhost_access_response(ExpectedResult, AuthUser, VHost) -> ?assertEqual(ExpectedResult, +<<<<<<< HEAD rabbit_auth_backend_oauth2:check_vhost_access(AuthUser, VHost, none)). assert_resource_access_granted(AuthUser, VHost, ResourceName, PermissionKind) -> @@ -1346,12 +2252,32 @@ assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceName, Perm assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, ResourceName, PermissionKind). assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceName, PermissionKind) -> +======= + check_vhost_access(AuthUser, VHost, none)). + +assert_resource_access_granted(AuthUser, VHost, ResourceName, PermissionKind) -> + assert_resource_access_response(true, AuthUser, VHost, ResourceName, + PermissionKind). + +assert_resource_access_denied(AuthUser, VHost, ResourceName, PermissionKind) -> + assert_resource_access_response(false, AuthUser, VHost, ResourceName, + PermissionKind). + +assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceName, + PermissionKind) -> + assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, + ResourceName, PermissionKind). + +assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceName, + PermissionKind) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_resource_access( AuthUser, rabbit_misc:r(VHost, queue, ResourceName), PermissionKind, #{})). +<<<<<<< HEAD assert_resource_access_granted(AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> assert_resource_access_response(true, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind). @@ -1362,12 +2288,32 @@ assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceKind, Reso assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind). assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceKind, ResourceName, PermissionKind) -> +======= +assert_resource_access_granted(AuthUser, VHost, ResourceKind, ResourceName, + PermissionKind) -> + assert_resource_access_response(true, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind). + +assert_resource_access_denied(AuthUser, VHost, ResourceKind, ResourceName, + PermissionKind) -> + assert_resource_access_response(false, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind). + +assert_resource_access_errors(ExpectedError, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind) -> + assert_resource_access_response({error, ExpectedError}, AuthUser, VHost, + ResourceKind, ResourceName, PermissionKind). + +assert_resource_access_response(ExpectedResult, AuthUser, VHost, ResourceKind, + ResourceName, PermissionKind) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_resource_access( AuthUser, rabbit_misc:r(VHost, ResourceKind, ResourceName), PermissionKind, #{})). +<<<<<<< HEAD assert_topic_access_granted(AuthUser, VHost, ResourceName, PermissionKind, AuthContext) -> assert_topic_access_response(true, AuthUser, VHost, ResourceName, PermissionKind, AuthContext). @@ -1376,6 +2322,22 @@ assert_topic_access_refused(AuthUser, VHost, ResourceName, PermissionKind, AuthC assert_topic_access_response(ExpectedResult, AuthUser, VHost, ResourceName, PermissionKind, AuthContext) -> ?assertEqual(ExpectedResult, rabbit_auth_backend_oauth2:check_topic_access( +======= +assert_topic_access_granted(AuthUser, VHost, ResourceName, PermissionKind, + AuthContext) -> + assert_topic_access_response(true, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext). + +assert_topic_access_refused(AuthUser, VHost, ResourceName, PermissionKind, + AuthContext) -> + assert_topic_access_response(false, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext). + +assert_topic_access_response(ExpectedResult, AuthUser, VHost, ResourceName, + PermissionKind, AuthContext) -> + ?assertEqual(ExpectedResult, + rabbit_auth_backend_oauth2:check_topic_access( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) AuthUser, #resource{virtual_host = VHost, kind = topic, diff --git a/deps/rabbitmq_cli/Makefile b/deps/rabbitmq_cli/Makefile index d32cc0b8c948..d47a03787ad2 100644 --- a/deps/rabbitmq_cli/Makefile +++ b/deps/rabbitmq_cli/Makefile @@ -117,7 +117,12 @@ rel:: $(ESCRIPTS) tests:: $(ESCRIPTS) $(verbose) $(MAKE) -C ../../ install-cli $(verbose) $(MAKE) -C ../../ start-background-broker \ +<<<<<<< HEAD PLUGINS="rabbit rabbitmq_federation rabbitmq_stomp rabbitmq_stream_management amqp_client" +======= + PLUGINS="rabbit rabbitmq_federation rabbitmq_stomp rabbitmq_stream_management amqp_client" \ + $(if $(filter khepri,$(RABBITMQ_METADATA_STORE)),,RABBITMQ_FEATURE_FLAGS="-khepri_db") +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) $(gen_verbose) $(MIX_TEST) \ $(if $(RABBITMQ_METADATA_STORE),--exclude $(filter-out $(RABBITMQ_METADATA_STORE),khepri mnesia),) \ $(TEST_FILE); \ diff --git a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_connections_command.ex b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_connections_command.ex index c5a362e8859c..30decf65e310 100644 --- a/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_connections_command.ex +++ b/deps/rabbitmq_cli/lib/rabbitmq/cli/ctl/commands/list_connections_command.ex @@ -17,7 +17,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand do @info_keys ~w(pid name port host peer_port peer_host ssl ssl_protocol ssl_key_exchange ssl_cipher ssl_hash peer_cert_subject peer_cert_issuer peer_cert_validity state +<<<<<<< HEAD channels protocol auth_mechanism user vhost timeout frame_max +======= + channels protocol auth_mechanism user vhost container_id timeout frame_max +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) channel_max client_properties recv_oct recv_cnt send_oct send_cnt send_pend connected_at)a @@ -79,7 +83,11 @@ defmodule RabbitMQ.CLI.Ctl.Commands.ListConnectionsCommand do def help_section(), do: :observability_and_health_checks +<<<<<<< HEAD def description(), do: "Lists AMQP 0.9.1 connections for the node" +======= + def description(), do: "Lists AMQP connections for the node" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) def banner(_, _), do: "Listing connections ..." end diff --git a/deps/rabbitmq_event_exchange/BUILD.bazel b/deps/rabbitmq_event_exchange/BUILD.bazel index 6d0f269239ca..64101950cb6c 100644 --- a/deps/rabbitmq_event_exchange/BUILD.bazel +++ b/deps/rabbitmq_event_exchange/BUILD.bazel @@ -42,6 +42,10 @@ rabbitmq_app( license_files = [":license_files"], priv = [":priv"], deps = [ +<<<<<<< HEAD +======= + "//deps/amqp10_common:erlang_app", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "//deps/rabbit:erlang_app", "//deps/rabbit_common:erlang_app", ], diff --git a/deps/rabbitmq_event_exchange/Makefile b/deps/rabbitmq_event_exchange/Makefile index f1f5ff81d952..4663562b9780 100644 --- a/deps/rabbitmq_event_exchange/Makefile +++ b/deps/rabbitmq_event_exchange/Makefile @@ -1,12 +1,25 @@ PROJECT = rabbitmq_event_exchange PROJECT_DESCRIPTION = Event Exchange Type +<<<<<<< HEAD +======= +define PROJECT_ENV + [ + {protocol, amqp_0_9_1} + ] +endef + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) define PROJECT_APP_EXTRA_KEYS {broker_version_requirements, []} endef DEPS = rabbit_common rabbit +<<<<<<< HEAD TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client +======= +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers amqp_client rabbitmq_amqp_client +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) DEP_EARLY_PLUGINS = rabbit_common/mk/rabbitmq-early-plugin.mk DEP_PLUGINS = rabbit_common/mk/rabbitmq-plugin.mk diff --git a/deps/rabbitmq_event_exchange/README.md b/deps/rabbitmq_event_exchange/README.md index 1380a4d30f72..219427c5500e 100644 --- a/deps/rabbitmq_event_exchange/README.md +++ b/deps/rabbitmq_event_exchange/README.md @@ -1,5 +1,6 @@ # RabbitMQ Event Exchange +<<<<<<< HEAD ## Overview This plugin exposes the internal RabbitMQ event mechanism as messages that clients @@ -152,3 +153,10 @@ TL;DR: Released under the Mozilla Public License 2.0, the same as RabbitMQ. +======= +See the [website](https://www.rabbitmq.com/docs/event-exchange) for documentation. + +## License + +Released under the Mozilla Public License 2.0, the same as RabbitMQ. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_event_exchange/app.bzl b/deps/rabbitmq_event_exchange/app.bzl index 3ce9ec463521..9cf6852a1cf6 100644 --- a/deps/rabbitmq_event_exchange/app.bzl +++ b/deps/rabbitmq_event_exchange/app.bzl @@ -17,6 +17,10 @@ def all_beam_files(name = "all_beam_files"): dest = "ebin", erlc_opts = "//:erlc_opts", deps = [ +<<<<<<< HEAD +======= + "//deps/amqp10_common:erlang_app", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "//deps/rabbit:erlang_app", "//deps/rabbit_common:erlang_app", ], @@ -40,6 +44,10 @@ def all_test_beam_files(name = "all_test_beam_files"): dest = "test", erlc_opts = "//:test_erlc_opts", deps = [ +<<<<<<< HEAD +======= + "//deps/amqp10_common:erlang_app", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "//deps/rabbit:erlang_app", "//deps/rabbit_common:erlang_app", ], diff --git a/deps/rabbitmq_event_exchange/priv/schema/rabbitmq_event_exchange.schema b/deps/rabbitmq_event_exchange/priv/schema/rabbitmq_event_exchange.schema index c8b2efe5acdd..aba09791224e 100644 --- a/deps/rabbitmq_event_exchange/priv/schema/rabbitmq_event_exchange.schema +++ b/deps/rabbitmq_event_exchange/priv/schema/rabbitmq_event_exchange.schema @@ -5,3 +5,10 @@ fun(Conf) -> list_to_binary(cuttlefish:conf_get("event_exchange.vhost", Conf)) end}. +<<<<<<< HEAD +======= + +{mapping, "event_exchange.protocol", "rabbitmq_event_exchange.protocol", [ + {datatype, {enum, [amqp_0_9_1, amqp_1_0]}} +]}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_event_exchange/src/rabbit_exchange_type_event.erl b/deps/rabbitmq_event_exchange/src/rabbit_exchange_type_event.erl index 70251406b20c..2a2be6b5a7ba 100644 --- a/deps/rabbitmq_event_exchange/src/rabbit_exchange_type_event.erl +++ b/deps/rabbitmq_event_exchange/src/rabbit_exchange_type_event.erl @@ -11,6 +11,11 @@ -include_lib("rabbit_common/include/rabbit.hrl"). -include_lib("rabbit_common/include/rabbit_framing.hrl"). +<<<<<<< HEAD +======= +-include_lib("amqp10_common/include/amqp10_framing.hrl"). +-include_lib("rabbit/include/mc.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -include("rabbit_event_exchange.hrl"). -export([register/0, unregister/0]). @@ -20,8 +25,16 @@ -export([fmt_proplist/1]). %% testing +<<<<<<< HEAD -record(state, {vhost, has_any_bindings +======= +-define(APP_NAME, rabbitmq_event_exchange). + +-record(state, {protocol :: amqp_0_9_1 | amqp_1_0, + vhost :: rabbit_types:vhost(), + has_any_bindings :: boolean() +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }). -rabbit_boot_step({rabbit_event_exchange, @@ -65,6 +78,7 @@ exchange(VHost) -> %%---------------------------------------------------------------------------- init([]) -> +<<<<<<< HEAD VHost = get_vhost(), X = rabbit_misc:r(VHost, exchange, ?EXCH_NAME), HasBindings = case rabbit_binding:list_for_source(X) of @@ -72,10 +86,22 @@ init([]) -> _ -> true end, {ok, #state{vhost = VHost, +======= + {ok, Protocol} = application:get_env(?APP_NAME, protocol), + VHost = get_vhost(), + X = rabbit_misc:r(VHost, exchange, ?EXCH_NAME), + HasBindings = case rabbit_binding:list_for_source(X) of + [] -> false; + _ -> true + end, + {ok, #state{protocol = Protocol, + vhost = VHost, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) has_any_bindings = HasBindings}}. handle_call(_Request, State) -> {ok, not_understood, State}. +<<<<<<< HEAD handle_event(_, #state{has_any_bindings = false} = State) -> {ok, State}; handle_event(#event{type = Type, @@ -100,6 +126,24 @@ handle_event(#event{type = Type, rabbit_queue_type:publish_at_most_once(XName, Msg) end, {ok, State}; +======= +handle_event(#event{type = Type, + props = Props, + reference = none, + timestamp = Timestamp}, + #state{protocol = Protocol, + vhost = VHost, + has_any_bindings = true} = State) -> + case key(Type) of + ignore -> + {ok, State}; + Key -> + XName = exchange(VHost), + Mc = mc_init(Protocol, XName, Key, Props, Timestamp), + _ = rabbit_queue_type:publish_at_most_once(XName, Mc), + {ok, State} + end; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) handle_event(_Event, State) -> {ok, State}. @@ -207,9 +251,115 @@ key(S) -> Tokens -> list_to_binary(string:join(Tokens, ".")) end. +<<<<<<< HEAD fmt_proplist(Props) -> lists:foldl(fun({K, V}, Acc) -> case fmt(a2b(K), V) of +======= +get_vhost() -> + case application:get_env(?APP_NAME, vhost) of + undefined -> + {ok, V} = application:get_env(rabbit, default_vhost), + V; + {ok, V} -> + V + end. + +mc_init(amqp_1_0, #resource{name = XNameBin}, Key, Props, Timestamp) -> + Sections = [#'v1_0.message_annotations'{content = props_to_message_annotations(Props)}, + #'v1_0.properties'{creation_time = {timestamp, Timestamp}}, + #'v1_0.data'{content = <<>>}], + Payload = iolist_to_binary([amqp10_framing:encode_bin(S) || S <- Sections]), + Anns = #{?ANN_EXCHANGE => XNameBin, + ?ANN_ROUTING_KEYS => [Key]}, + mc:init(mc_amqp, Payload, Anns); +mc_init(amqp_0_9_1, XName, Key, Props0, TimestampMillis) -> + Props = [{<<"timestamp_in_ms">>, TimestampMillis} | Props0], + Headers = fmt_proplist(Props), + TimestampSecs = erlang:convert_time_unit(TimestampMillis, millisecond, second), + PBasic = #'P_basic'{delivery_mode = 2, + headers = Headers, + timestamp = TimestampSecs}, + Content = rabbit_basic:build_content(PBasic, <<>>), + {ok, Mc} = mc_amqpl:message(XName, Key, Content), + Mc. + +props_to_message_annotations(Props) -> + KVList = lists:foldl( + fun({K, #resource{virtual_host = Vhost, name = Name}}, Acc) -> + Ann0 = {to_message_annotation_key(K), {utf8, Name}}, + Ann1 = {{symbol, <<"x-opt-vhost">>}, {utf8, Vhost}}, + [Ann0, Ann1 | Acc]; + ({K, V}, Acc) -> + Ann = {to_message_annotation_key(K), + to_message_annotation_val(V)}, + [Ann | Acc] + end, [], Props), + lists:reverse(KVList). + +to_message_annotation_key(Key) -> + Key1 = to_binary(Key), + Pattern = try persistent_term:get(cp_underscore) + catch error:badarg -> + Cp = binary:compile_pattern(<<"_">>), + ok = persistent_term:put(cp_underscore, Cp), + Cp + end, + Key2 = binary:replace(Key1, Pattern, <<"-">>, [global]), + Key3 = case Key2 of + <<"x-", _/binary>> -> + Key2; + _ -> + <<"x-opt-", Key2/binary>> + end, + {symbol, Key3}. + +to_message_annotation_val(V) + when is_boolean(V) -> + {boolean, V}; +to_message_annotation_val(V) + when is_atom(V) -> + {utf8, atom_to_binary(V, utf8)}; +to_message_annotation_val(V) + when is_binary(V) -> + case mc_util:is_utf8_no_null_limited(V) of + true -> + {utf8, V}; + false -> + {binary, V} + end; +to_message_annotation_val(V) + when is_integer(V) -> + {long, V}; +to_message_annotation_val(V) + when is_number(V) -> + %% AMQP double and Erlang float are both 64-bit. + {double, V}; +to_message_annotation_val(V) + when is_pid(V) -> + {utf8, to_pid(V)}; +to_message_annotation_val([{Key, _} | _] = Proplist) + when is_atom(Key) orelse + is_binary(Key) -> + {map, lists:map(fun({K, V}) -> + {{utf8, to_binary(K)}, + to_message_annotation_val(V)} + end, Proplist)}; +to_message_annotation_val([{Key, Type, _Value} | _] = Table) + when is_binary(Key) andalso + is_atom(Type) -> + %% Looks like an AMQP 0.9.1 table + mc_amqpl:from_091(table, Table); +to_message_annotation_val(V) + when is_list(V) -> + {list, [to_message_annotation_val(Val) || Val <- V]}; +to_message_annotation_val(V) -> + {utf8, fmt_other(V)}. + +fmt_proplist(Props) -> + lists:foldl(fun({K, V}, Acc) -> + case fmt(to_binary(K), V) of +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) L when is_list(L) -> lists:append(L, Acc); T -> [T | Acc] end @@ -226,11 +376,16 @@ fmt(K, V) when is_number(V) -> {K, float, V}; fmt(K, V) when is_binary(V) -> {K, longstr, V}; fmt(K, [{_, _}|_] = Vs) -> {K, table, fmt_proplist(Vs)}; fmt(K, Vs) when is_list(Vs) -> {K, array, [fmt(V) || V <- Vs]}; +<<<<<<< HEAD fmt(K, V) when is_pid(V) -> {K, longstr, list_to_binary(rabbit_misc:pid_to_string(V))}; fmt(K, V) -> {K, longstr, list_to_binary( rabbit_misc:format("~1000000000p", [V]))}. +======= +fmt(K, V) when is_pid(V) -> {K, longstr, to_pid(V)}; +fmt(K, V) -> {K, longstr, fmt_other(V)}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Exactly the same as fmt/2, duplicated only for performance issues fmt(true) -> {bool, true}; @@ -241,6 +396,7 @@ fmt(V) when is_number(V) -> {float, V}; fmt(V) when is_binary(V) -> {longstr, V}; fmt([{_, _}|_] = Vs) -> {table, fmt_proplist(Vs)}; fmt(Vs) when is_list(Vs) -> {array, [fmt(V) || V <- Vs]}; +<<<<<<< HEAD fmt(V) when is_pid(V) -> {longstr, list_to_binary(rabbit_misc:pid_to_string(V))}; fmt(V) -> {longstr, @@ -258,3 +414,18 @@ get_vhost() -> {ok, V} -> V end. +======= +fmt(V) when is_pid(V) -> {longstr, to_pid(V)}; +fmt(V) -> {longstr, fmt_other(V)}. + +fmt_other(V) -> + list_to_binary(rabbit_misc:format("~1000000000p", [V])). + +to_binary(Val) when is_atom(Val) -> + atom_to_binary(Val); +to_binary(Val) when is_binary(Val) -> + Val. + +to_pid(Val) -> + list_to_binary(rabbit_misc:pid_to_string(Val)). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_event_exchange/test/config_schema_SUITE_data/rabbitmq_event_exchange.snippets b/deps/rabbitmq_event_exchange/test/config_schema_SUITE_data/rabbitmq_event_exchange.snippets index 2fceed017a96..4d8d46defc05 100644 --- a/deps/rabbitmq_event_exchange/test/config_schema_SUITE_data/rabbitmq_event_exchange.snippets +++ b/deps/rabbitmq_event_exchange/test/config_schema_SUITE_data/rabbitmq_event_exchange.snippets @@ -1,4 +1,5 @@ [ +<<<<<<< HEAD {virtual_host1, "event_exchange.vhost = /", [ @@ -16,4 +17,38 @@ ]} ], [rabbitmq_event_exchange] } +======= +{virtual_host1, + "event_exchange.vhost = /", + [{rabbitmq_event_exchange, [ + {vhost, <<"/">>} + ]}], + [rabbitmq_event_exchange] +}, + +{virtual_host2, + "event_exchange.vhost = dev", + [{rabbitmq_event_exchange, [ + {vhost, <<"dev">>} + ]} + ], + [rabbitmq_event_exchange] +}, + +{protocol_amqp, + "event_exchange.protocol = amqp_1_0", + [{rabbitmq_event_exchange, [ + {protocol, amqp_1_0} + ]}], + [rabbitmq_event_exchange] +}, + +{protocol_amqpl, + "event_exchange.protocol = amqp_0_9_1", + [{rabbitmq_event_exchange, [ + {protocol, amqp_0_9_1} + ]}], + [rabbitmq_event_exchange] +} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. diff --git a/deps/rabbitmq_event_exchange/test/system_SUITE.erl b/deps/rabbitmq_event_exchange/test/system_SUITE.erl index 76d9199a586c..88f64034627f 100644 --- a/deps/rabbitmq_event_exchange/test/system_SUITE.erl +++ b/deps/rabbitmq_event_exchange/test/system_SUITE.erl @@ -13,12 +13,43 @@ -compile(export_all). +<<<<<<< HEAD -define(TAG, <<"user_who_performed_action">>). all() -> [ queue_created, authentication, +======= +all() -> + [ + {group, amqp_1_0}, + {group, amqp_0_9_1} + ]. + +groups() -> + [ + {amqp_1_0, [shuffle], + shared_tests() ++ + [ + amqp_1_0_amqp_connection, + amqp_1_0_queue_created, + headers_exchange + ]}, + {amqp_0_9_1, [], + shared_tests() ++ + [ + amqp_0_9_1_amqp_connection, + amqp_0_9_1_queue_created, + unregister + ]} + ]. + +shared_tests() -> + [ + authentication_success, + authentication_failure, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) audit_queue, audit_exchange, audit_exchange_internal_parameter, @@ -37,8 +68,12 @@ all() -> audit_user_tags, audit_permission, audit_topic_permission, +<<<<<<< HEAD resource_alarm, unregister +======= + resource_alarm +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. %% ------------------------------------------------------------------- @@ -46,6 +81,7 @@ all() -> %% ------------------------------------------------------------------- init_per_suite(Config) -> +<<<<<<< HEAD rabbit_ct_helpers:log_environment(), Config1 = rabbit_ct_helpers:set_config(Config, [ {rmq_nodename_suffix, ?MODULE} @@ -66,17 +102,47 @@ init_per_group(_, Config) -> end_per_group(_, Config) -> Config. +======= + {ok, _} = application:ensure_all_started(rabbitmq_amqp_client), + rabbit_ct_helpers:log_environment(), + Config. + +end_per_suite(Config) -> + Config. + +init_per_group(Group, Config) -> + Config1 = rabbit_ct_helpers:merge_app_env( + Config, + {rabbitmq_event_exchange, [{protocol, Group}]}), + Config2 = rabbit_ct_helpers:set_config( + Config1, [{rmq_nodename_suffix, ?MODULE}]), + rabbit_ct_helpers:run_setup_steps( + Config2, + rabbit_ct_broker_helpers:setup_steps() ++ + rabbit_ct_client_helpers:setup_steps()). + +end_per_group(_Group, Config) -> + rabbit_ct_helpers:run_teardown_steps( + Config, + rabbit_ct_client_helpers:teardown_steps() ++ + rabbit_ct_broker_helpers:teardown_steps()). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_started(Config, Testcase). end_per_testcase(Testcase, Config) -> rabbit_ct_helpers:testcase_finished(Config, Testcase). +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% Testsuite cases %% ------------------------------------------------------------------- +<<<<<<< HEAD %% Only really tests that we're not completely broken. queue_created(Config) -> Now = os:system_time(seconds), @@ -100,6 +166,50 @@ queue_created(Config) -> authentication(Config) -> +======= +amqp_1_0_queue_created(Config) -> + QName = atom_to_binary(?FUNCTION_NAME), + Headers = queue_created(QName, Config), + ?assertEqual({longstr, QName}, + rabbit_misc:table_lookup(Headers, <<"x-opt-name">>)), + ?assertEqual({table, [{<<"x-queue-type">>, longstr, <<"classic">>}]}, + rabbit_misc:table_lookup(Headers, <<"x-opt-arguments">>)). + +amqp_0_9_1_queue_created(Config) -> + QName = atom_to_binary(?FUNCTION_NAME), + Headers = queue_created(QName,Config), + ?assertEqual({longstr, QName}, + rabbit_misc:table_lookup(Headers, <<"name">>)), + {array, QArgs} = rabbit_misc:table_lookup(Headers, <<"arguments">>), + %% Ideally, instead of a longstr containing the formatted Erlang term, + %% we should expect a table. + ?assertEqual(<<"{<<\"x-queue-type\">>,longstr,<<\"classic\">>}">>, + proplists:get_value(longstr, QArgs)). + +queue_created(QName, Config) -> + Ch = declare_event_queue(Config, <<"queue.created">>), + + Now = os:system_time(second), + #'queue.declare_ok'{} = amqp_channel:call( + Ch, #'queue.declare'{ + queue = QName, + exclusive = true, + arguments = [{<<"x-queue-type">>, longstr, <<"classic">>}] + }), + + receive + {#'basic.deliver'{routing_key = Key}, + #amqp_msg{props = #'P_basic'{headers = Headers, + timestamp = TS}}} -> + %% timestamp is within the last 5 seconds + ?assert(((TS - Now) =< 5)), + ?assertEqual(<<"queue.created">>, Key), + rabbit_ct_client_helpers:close_channel(Ch), + Headers + end. + +authentication_success(Config) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Ch = declare_event_queue(Config, <<"user.#">>), Conn2 = rabbit_ct_client_helpers:open_unmanaged_connection(Config, 0), @@ -107,6 +217,7 @@ authentication(Config) -> {#'basic.deliver'{routing_key = Key}, #amqp_msg{props = #'P_basic'{headers = Headers}}} -> <<"user.authentication.success">> = Key, +<<<<<<< HEAD undefined = rabbit_misc:table_lookup(Headers, <<"vhost">>), {longstr, _PeerHost} = rabbit_misc:table_lookup(Headers, <<"peer_host">>), {bool, false} = rabbit_misc:table_lookup(Headers, <<"ssl">>) @@ -115,6 +226,43 @@ authentication(Config) -> amqp_connection:close(Conn2), rabbit_ct_client_helpers:close_channel(Ch), ok. +======= + {Vhost, PeerHost, Ssl} = + case group_name(Config) of + amqp_0_9_1 -> + {<<"vhost">>, <<"peer_host">>, <<"ssl">>}; + amqp_1_0 -> + {<<"x-opt-vhost">>, <<"x-opt-peer-host">>, <<"x-opt-ssl">>} + end, + undefined = rabbit_misc:table_lookup(Headers, Vhost), + {longstr, _PeerHost} = rabbit_misc:table_lookup(Headers, PeerHost), + {bool, false} = rabbit_misc:table_lookup(Headers, Ssl) + after 5000 -> missing_deliver + end, + + ok = amqp_connection:close(Conn2), + ok = rabbit_ct_client_helpers:close_channel(Ch). + +authentication_failure(Config) -> + Ch = declare_event_queue(Config, <<"user.authentication.*">>), + {error, _} = rabbit_ct_client_helpers:open_unmanaged_connection( + Config, 0, <<"fake user">>, <<"fake password">>), + + receive + {#'basic.deliver'{routing_key = Key}, + #amqp_msg{props = #'P_basic'{headers = Headers}}} -> + ?assertEqual(<<"user.authentication.failure">>, Key), + User = case group_name(Config) of + amqp_0_9_1 -> <<"name">>; + amqp_1_0 -> <<"x-opt-name">> + end, + ?assertEqual({longstr, <<"fake user">>}, + rabbit_misc:table_lookup(Headers, User)) + after 5000 -> missing_deliver + end, + + ok = rabbit_ct_client_helpers:close_channel(Ch). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) audit_queue(Config) -> Ch = declare_event_queue(Config, <<"queue.*">>), @@ -122,13 +270,21 @@ audit_queue(Config) -> #'queue.declare_ok'{queue = Q} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), receive_user_in_event(<<"queue.created">>, User), +======= + receive_user_in_event(<<"queue.created">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'queue.delete_ok'{} = amqp_channel:call(Ch, #'queue.delete'{queue = Q}), +<<<<<<< HEAD receive_user_in_event(<<"queue.deleted">>, User), +======= + receive_user_in_event(<<"queue.deleted">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -141,13 +297,21 @@ audit_exchange(Config) -> amqp_channel:call(Ch, #'exchange.declare'{exchange = X, type = <<"topic">>}), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), receive_user_in_event(<<"exchange.created">>, User), +======= + receive_user_in_event(<<"exchange.created">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'exchange.delete_ok'{} = amqp_channel:call(Ch, #'exchange.delete'{exchange = X}), +<<<<<<< HEAD receive_user_in_event(<<"exchange.deleted">>, User), +======= + receive_user_in_event(<<"exchange.deleted">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -155,8 +319,12 @@ audit_exchange(Config) -> audit_binding(Config) -> Ch = declare_event_queue(Config, <<"binding.*">>), %% The binding to the event exchange itself is the first queued event +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), receive_user_in_event(<<"binding.created">>, User), +======= + receive_user_in_event(<<"binding.created">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'queue.declare_ok'{queue = Q} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), @@ -165,26 +333,52 @@ audit_binding(Config) -> amqp_channel:call(Ch, #'queue.bind'{queue = Q, exchange = <<"amq.direct">>, routing_key = <<"test">>}), +<<<<<<< HEAD receive_user_in_event(<<"binding.created">>, User), +======= + receive_user_in_event(<<"binding.created">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'queue.unbind_ok'{} = amqp_channel:call(Ch, #'queue.unbind'{queue = Q, exchange = <<"amq.direct">>, routing_key = <<"test">>}), +<<<<<<< HEAD receive_user_in_event(<<"binding.deleted">>, User), +======= + receive_user_in_event(<<"binding.deleted">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. audit_vhost(Config) -> +<<<<<<< HEAD +======= + Node = atom_to_binary(rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Ch = declare_event_queue(Config, <<"vhost.*">>), User = <<"Bugs Bunny">>, rabbit_ct_broker_helpers:add_vhost(Config, 0, <<"test-vhost">>, User), +<<<<<<< HEAD receive_user_in_event(<<"vhost.created">>, User), rabbit_ct_broker_helpers:delete_vhost(Config, 0, <<"test-vhost">>, User), receive_user_in_event(<<"vhost.deleted">>, User), +======= + Headers = receive_user_in_event(<<"vhost.created">>, User, Config), + + Key = case group_name(Config) of + amqp_0_9_1 -> <<"cluster_state">>; + amqp_1_0 -> <<"x-opt-cluster-state">> + end, + ?assertEqual({table, [{Node, longstr, <<"running">>}]}, + rabbit_misc:table_lookup(Headers, Key)), + + rabbit_ct_broker_helpers:delete_vhost(Config, 0, <<"test-vhost">>, User), + receive_user_in_event(<<"vhost.deleted">>, User, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -202,19 +396,28 @@ audit_vhost_deletion(Config) -> %% The user that creates the queue is the connection one, not the vhost creator #'queue.declare_ok'{queue = _Q} = amqp_channel:call(Ch2, #'queue.declare'{}), +<<<<<<< HEAD receive_user_in_event(<<"queue.created">>, ConnUser), +======= + receive_user_in_event(<<"queue.created">>, ConnUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = rabbit_ct_client_helpers:close_connection_and_channel(Conn, Ch2), %% Validate that the user deleting the queue is the one used to delete the vhost, %% not the original user that created the queue (the connection one) rabbit_ct_broker_helpers:delete_vhost(Config, 0, Vhost, User), +<<<<<<< HEAD receive_user_in_event(<<"queue.deleted">>, User), +======= + receive_user_in_event(<<"queue.deleted">>, User, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. audit_channel(Config) -> Ch = declare_event_queue(Config, <<"channel.*">>), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), Conn = rabbit_ct_client_helpers:open_unmanaged_connection(Config), @@ -223,12 +426,22 @@ audit_channel(Config) -> rabbit_ct_client_helpers:close_channel(Ch2), receive_user_in_event(<<"channel.closed">>, User), +======= + + Conn = rabbit_ct_client_helpers:open_unmanaged_connection(Config), + {ok, Ch2} = amqp_connection:open_channel(Conn), + receive_user_in_event(<<"channel.created">>, Config), + + rabbit_ct_client_helpers:close_channel(Ch2), + receive_user_in_event(<<"channel.closed">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. audit_connection(Config) -> Ch = declare_event_queue(Config, <<"connection.*">>), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), Conn = rabbit_ct_client_helpers:open_unmanaged_connection(Config), @@ -237,12 +450,35 @@ audit_connection(Config) -> %% Username is not available in connection_close rabbit_ct_client_helpers:close_connection(Conn), receive_event(<<"connection.closed">>, ?TAG, undefined), +======= + + Conn = rabbit_ct_client_helpers:open_unmanaged_connection(Config), + receive_user_in_event(<<"connection.created">>, Config), + + %% Username is not available in connection_close + rabbit_ct_client_helpers:close_connection(Conn), + Headers = receive_event(<<"connection.closed">>, user_key(Config), undefined), + case group_name(Config) of + amqp_0_9_1 -> + ?assert(lists:keymember(<<"client_properties">>, 1, Headers)); + amqp_1_0 -> + {table, ClientProps} = rabbit_misc:table_lookup(Headers, <<"x-opt-client-properties">>), + ?assertEqual({longstr, <<"Erlang">>}, + rabbit_misc:table_lookup(ClientProps, <<"platform">>)), + {table, Caps} = rabbit_misc:table_lookup(ClientProps, <<"capabilities">>), + ?assertEqual({bool, true}, + rabbit_misc:table_lookup(Caps, <<"basic.nack">>)), + ?assertEqual({bool, true}, + rabbit_misc:table_lookup(Caps, <<"connection.blocked">>)) + end, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. audit_direct_connection(Config) -> Ch = declare_event_queue(Config, <<"connection.*">>), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), Conn = rabbit_ct_client_helpers:open_unmanaged_connection_direct(Config), @@ -250,24 +486,43 @@ audit_direct_connection(Config) -> rabbit_ct_client_helpers:close_connection(Conn), receive_event(<<"connection.closed">>, ?TAG, undefined), +======= + + Conn = rabbit_ct_client_helpers:open_unmanaged_connection_direct(Config), + receive_user_in_event(<<"connection.created">>, Config), + + rabbit_ct_client_helpers:close_connection(Conn), + receive_event(<<"connection.closed">>, user_key(Config), undefined), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. audit_consumer(Config) -> Ch = declare_event_queue(Config, <<"consumer.*">>), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), receive_user_in_event(<<"consumer.created">>, User), +======= + receive_user_in_event(<<"consumer.created">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) #'queue.declare_ok'{queue = Q} = amqp_channel:call(Ch, #'queue.declare'{exclusive = true}), amqp_channel:subscribe(Ch, #'basic.consume'{queue = Q, no_ack = true}, self()), CTag = receive #'basic.consume_ok'{consumer_tag = C} -> C end, +<<<<<<< HEAD receive_user_in_event(<<"consumer.created">>, User), amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}), receive_user_in_event(<<"consumer.deleted">>, User), +======= + receive_user_in_event(<<"consumer.created">>, Config), + + amqp_channel:call(Ch, #'basic.cancel'{consumer_tag = CTag}), + receive_user_in_event(<<"consumer.deleted">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -282,11 +537,18 @@ audit_exchange_internal_parameter(Config) -> #'exchange.delete_ok'{} = amqp_channel:call(Ch, #'exchange.delete'{exchange = X}), +<<<<<<< HEAD User = proplists:get_value(rmq_username, Config), %% Exchange deletion sets and clears a runtime parameter which acts as a %% kind of lock: receive_user_in_event(<<"parameter.set">>, User), receive_user_in_event(<<"parameter.cleared">>, User), +======= + %% Exchange deletion sets and clears a runtime parameter which acts as a + %% kind of lock: + receive_user_in_event(<<"parameter.set">>, Config), + receive_user_in_event(<<"parameter.cleared">>, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -299,11 +561,19 @@ audit_parameter(Config) -> ok = rabbit_ct_broker_helpers:set_parameter( Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, [{<<"max-connections">>, 200}], User), +<<<<<<< HEAD receive_user_in_event(<<"parameter.set">>, User), ok = rabbit_ct_broker_helpers:clear_parameter( Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, User), receive_user_in_event(<<"parameter.cleared">>, User), +======= + receive_user_in_event(<<"parameter.set">>, User, Config), + + ok = rabbit_ct_broker_helpers:clear_parameter( + Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, User), + receive_user_in_event(<<"parameter.cleared">>, User, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -314,10 +584,17 @@ audit_policy(Config) -> rabbit_ct_broker_helpers:set_policy(Config, 0, <<".*">>, <<"all">>, <<"queues">>, [{<<"max-length-bytes">>, 10000}], User), +<<<<<<< HEAD receive_user_in_event(<<"policy.set">>, User), ok = rabbit_ct_broker_helpers:clear_policy(Config, 0, <<".*">>, User), receive_user_in_event(<<"policy.cleared">>, User), +======= + receive_user_in_event(<<"policy.set">>, User, Config), + + ok = rabbit_ct_broker_helpers:clear_policy(Config, 0, <<".*">>, User), + receive_user_in_event(<<"policy.cleared">>, User, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -330,11 +607,19 @@ audit_vhost_limit(Config) -> ok = rabbit_ct_broker_helpers:set_parameter( Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, [{<<"max-connections">>, 200}], User), +<<<<<<< HEAD receive_user_in_event(<<"vhost.limits.set">>, User), ok = rabbit_ct_broker_helpers:clear_parameter( Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, User), receive_user_in_event(<<"vhost.limits.cleared">>, User), +======= + receive_user_in_event(<<"vhost.limits.set">>, User, Config), + + ok = rabbit_ct_broker_helpers:clear_parameter( + Config, 0, VHost, <<"vhost-limits">>, <<"limits">>, User), + receive_user_in_event(<<"vhost.limits.cleared">>, User, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -345,10 +630,17 @@ audit_user(Config) -> User = <<"Wabbit">>, rabbit_ct_broker_helpers:add_user(Config, 0, User, User, ActingUser), +<<<<<<< HEAD receive_user_in_event(<<"user.created">>, ActingUser), rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), receive_user_in_event(<<"user.deleted">>, ActingUser), +======= + receive_user_in_event(<<"user.created">>, ActingUser, Config), + + rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), + receive_user_in_event(<<"user.deleted">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_client_helpers:close_channel(Ch), ok. @@ -360,10 +652,17 @@ audit_user_password(Config) -> rabbit_ct_broker_helpers:add_user(Config, 0, User, User, ActingUser), rabbit_ct_broker_helpers:change_password(Config, 0, User, <<"pass">>, ActingUser), +<<<<<<< HEAD receive_user_in_event(<<"user.password.changed">>, ActingUser), rabbit_ct_broker_helpers:clear_password(Config, 0, User, ActingUser), receive_user_in_event(<<"user.password.cleared">>, ActingUser), +======= + receive_user_in_event(<<"user.password.changed">>, ActingUser, Config), + + rabbit_ct_broker_helpers:clear_password(Config, 0, User, ActingUser), + receive_user_in_event(<<"user.password.cleared">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), rabbit_ct_client_helpers:close_channel(Ch), @@ -376,7 +675,11 @@ audit_user_tags(Config) -> rabbit_ct_broker_helpers:add_user(Config, 0, User, User, ActingUser), rabbit_ct_broker_helpers:set_user_tags(Config, 0, User, [management], ActingUser), +<<<<<<< HEAD receive_user_in_event(<<"user.tags.set">>, ActingUser), +======= + receive_user_in_event(<<"user.tags.set">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), @@ -392,10 +695,17 @@ audit_permission(Config) -> rabbit_ct_broker_helpers:add_user(Config, 0, User, User, ActingUser), rabbit_ct_broker_helpers:set_permissions(Config, 0, User, VHost, <<".*">>, <<".*">>, <<".*">>, ActingUser), +<<<<<<< HEAD receive_user_in_event(<<"permission.created">>, ActingUser), rabbit_ct_broker_helpers:clear_permissions(Config, 0, User, VHost, ActingUser), receive_user_in_event(<<"permission.deleted">>, ActingUser), +======= + receive_user_in_event(<<"permission.created">>, ActingUser, Config), + + rabbit_ct_broker_helpers:clear_permissions(Config, 0, User, VHost, ActingUser), + receive_user_in_event(<<"permission.deleted">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), rabbit_ct_client_helpers:close_channel(Ch), @@ -411,12 +721,20 @@ audit_topic_permission(Config) -> rabbit_ct_broker_helpers:rpc( Config, 0, rabbit_auth_backend_internal, set_topic_permissions, [User, VHost, <<"amq.topic">>, "^a", "^a", ActingUser]), +<<<<<<< HEAD receive_user_in_event(<<"topic.permission.created">>, ActingUser), +======= + receive_user_in_event(<<"topic.permission.created">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:rpc( Config, 0, rabbit_auth_backend_internal, clear_topic_permissions, [User, VHost, ActingUser]), +<<<<<<< HEAD receive_user_in_event(<<"topic.permission.deleted">>, ActingUser), +======= + receive_user_in_event(<<"topic.permission.deleted">>, ActingUser, Config), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_broker_helpers:delete_user(Config, 0, User, ActingUser), rabbit_ct_client_helpers:close_channel(Ch), @@ -453,6 +771,143 @@ unregister(Config) -> lookup, [X])), ok. +<<<<<<< HEAD +======= +%% Test the plugin publishing internally with AMQP 0.9.1 while the client uses AMQP 1.0. +amqp_0_9_1_amqp_connection(Config) -> + QName = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(QName), + {Connection1, Session, LinkPair} = amqp_init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName,#{}), + ok = rabbitmq_amqp_client:bind_queue( + LinkPair, QName, <<"amq.rabbitmq.event">>, <<"connection.*">>, #{}), + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session, <<"receiver">>, Address, settled), + + OpnConf0 = amqp_connection_config(Config), + OpnConf = maps:update(container_id, <<"2nd container">>, OpnConf0), + {ok, Connection2} = amqp10_client:open_connection(OpnConf), + receive {amqp10_event, {connection, Connection2, opened}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + {ok, Msg} = amqp10_client:get_msg(Receiver), + ?assertMatch(#{<<"x-routing-key">> := <<"connection.created">>}, + amqp10_msg:message_annotations(Msg)), + ?assertMatch(#{<<"container_id">> := <<"2nd container">>}, + amqp10_msg:application_properties(Msg)), + ok = amqp10_client:close_connection(Connection2), + + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, QName), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = amqp10_client:end_session(Session), + ok = amqp10_client:close_connection(Connection1). + +%% Test the plugin publishing internally with AMQP 1.0 and the client using AMQP 1.0. +amqp_1_0_amqp_connection(Config) -> + QName = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(QName), + {Connection1, Session, LinkPair} = amqp_init(Config), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName,#{}), + ok = rabbitmq_amqp_client:bind_queue( + LinkPair, QName, <<"amq.rabbitmq.event">>, <<"connection.*">>, #{}), + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session, <<"receiver">>, Address, settled), + + Now = os:system_time(millisecond), + OpnConf0 = amqp_connection_config(Config), + OpnConf = maps:update(container_id, <<"2nd container">>, OpnConf0), + {ok, Connection2} = amqp10_client:open_connection(OpnConf), + receive {amqp10_event, {connection, Connection2, opened}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + {ok, Msg} = amqp10_client:get_msg(Receiver), + ?assertEqual(<<>>, iolist_to_binary(amqp10_msg:body(Msg))), + MsgAnns = amqp10_msg:message_annotations(Msg), + ?assertMatch(#{<<"x-routing-key">> := <<"connection.created">>, + <<"x-opt-container-id">> := <<"2nd container">>, + <<"x-opt-channel-max">> := ChannelMax} + when is_integer(ChannelMax), + MsgAnns), + %% We expect to receive event properties that have complex types. + ClientProps = maps:get(<<"x-opt-client-properties">>, MsgAnns), + OtpRelease = integer_to_binary(?OTP_RELEASE), + ?assertMatch(#{ + {symbol, <<"version">>} := {utf8, _Version}, + {symbol, <<"product">>} := {utf8, <<"AMQP 1.0 client">>}, + {symbol, <<"platform">>} := {utf8, <<"Erlang/OTP ", OtpRelease/binary>>} + }, + maps:from_list(ClientProps)), + FormattedPid = maps:get(<<"x-opt-pid">>, MsgAnns), + + %% The formatted Pid should include the RabbitMQ node name: + ?assertMatch({match, _}, + re:run(FormattedPid, <<"rmq-ct-system_SUITE">>)), + + #{creation_time := CreationTime} = amqp10_msg:properties(Msg), + ?assert(is_integer(CreationTime)), + ?assert(CreationTime > Now - 5000), + ?assert(CreationTime < Now + 5000), + + ok = amqp10_client:close_connection(Connection2), + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, QName), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = amqp10_client:end_session(Session), + ok = amqp10_client:close_connection(Connection1). + +%% Test that routing on specific event properties works. +headers_exchange(Config) -> + XName = <<"my headers exchange">>, + QName = atom_to_binary(?FUNCTION_NAME), + Address = rabbitmq_amqp_address:queue(QName), + OpnConf = amqp_connection_config(Config), + {Connection, Session, LinkPair} = amqp_init(Config), + + ok = rabbitmq_amqp_client:declare_exchange(LinkPair, XName, #{type => <<"headers">>}), + {ok, _} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), + ok = rabbitmq_amqp_client:bind_queue( + LinkPair, QName, XName, <<>>, + #{<<"x-opt-container-id">> => {utf8, <<"client-2">>}, + <<"x-match">> => {utf8, <<"any-with-x">>}}), + ok = rabbitmq_amqp_client:bind_exchange( + LinkPair, XName, <<"amq.rabbitmq.event">>, <<"connection.created">>, #{}), + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session, <<"receiver">>, Address, settled), + + %% Open two connections. + OpnConf1 = maps:update(container_id, <<"client-1">>, OpnConf), + {ok, Connection1} = amqp10_client:open_connection(OpnConf1), + receive {amqp10_event, {connection, Connection1, opened}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + OpnConf2 = maps:update(container_id, <<"client-2">>, OpnConf), + {ok, Connection2} = amqp10_client:open_connection(OpnConf2), + receive {amqp10_event, {connection, Connection2, opened}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + %% Thanks to routing via headers exchange on event property + %% x-opt-container-id = client-2 + %% we should only receive the second connection.created event. + ok = amqp10_client:flow_link_credit(Receiver, 2, never, true), + receive {amqp10_msg, Receiver, Msg} -> + ?assertMatch(#{<<"x-routing-key">> := <<"connection.created">>, + <<"x-opt-container-id">> := <<"client-2">>}, + amqp10_msg:message_annotations(Msg)) + after 5000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_event, {link, Receiver, credit_exhausted}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + + ok = amqp10_client:close_connection(Connection1), + ok = amqp10_client:close_connection(Connection2), + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, QName), + ok = rabbitmq_amqp_client:delete_exchange(LinkPair, XName), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = amqp10_client:end_session(Session), + ok = amqp10_client:close_connection(Connection). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% Helpers %% ------------------------------------------------------------------- @@ -471,17 +926,48 @@ declare_event_queue(Config, RoutingKey) -> end, Ch. +<<<<<<< HEAD receive_user_in_event(Event, User) -> receive_event(Event, ?TAG, {longstr, User}). +======= +user_key(Config) -> + case group_name(Config) of + amqp_0_9_1 -> + <<"user_who_performed_action">>; + amqp_1_0 -> + <<"x-opt-user-who-performed-action">> + end. + +group_name(Config) -> + GroupProps = proplists:get_value(tc_group_properties, Config), + proplists:get_value(name, GroupProps). + +receive_user_in_event(Event, Config) -> + User = proplists:get_value(rmq_username, Config), + receive_user_in_event(Event, User, Config). + +receive_user_in_event(Event, User, Config) -> + Key = user_key(Config), + Value = {longstr, User}, + receive_event(Event, Key, Value). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) receive_event(Event, Key, Value) -> receive {#'basic.deliver'{routing_key = RoutingKey}, #amqp_msg{props = #'P_basic'{headers = Headers}}} -> +<<<<<<< HEAD Event = RoutingKey, Value = rabbit_misc:table_lookup(Headers, Key) after 60000 -> +======= + ?assertEqual(Event, RoutingKey), + ?assertEqual(Value, rabbit_misc:table_lookup(Headers, Key)), + Headers + after + 10_000 -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) throw({receive_event_timeout, Event, Key, Value}) end. @@ -489,8 +975,31 @@ receive_event(Event) -> receive {#'basic.deliver'{routing_key = RoutingKey}, #amqp_msg{props = #'P_basic'{}}} -> +<<<<<<< HEAD Event = RoutingKey after 60000 -> throw({receive_event_timeout, Event}) end. +======= + ?assertEqual(Event, RoutingKey) + after + 10_000 -> + throw({receive_event_timeout, Event}) + end. + +amqp_init(Config) -> + OpnConf = amqp_connection_config(Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(Session, <<"my link pair">>), + {Connection, Session, LinkPair}. + +amqp_connection_config(Config) -> + Host = proplists:get_value(rmq_hostname, Config), + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + #{address => Host, + port => Port, + container_id => <<"my container">>, + sasl => {plain, <<"guest">>, <<"guest">>}}. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/.gitignore b/deps/rabbitmq_management/.gitignore index 96463fa9b670..4b6eb82a1007 100644 --- a/deps/rabbitmq_management/.gitignore +++ b/deps/rabbitmq_management/.gitignore @@ -2,6 +2,7 @@ test/config_schema_SUITE_data/schema/ +<<<<<<< HEAD selenium/node_modules selenium/package-lock.json selenium/screens/*/* @@ -11,3 +12,7 @@ selenium/suites/screens/* selenium/test/oauth/*/h2/*.trace.db selenium/test/oauth/*/h2/*.lock.db selenium/*/target/* +======= +test/js/node_modules +test/js/package-lock.json +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/BUILD.bazel b/deps/rabbitmq_management/BUILD.bazel index 2f053bb609a7..0ca6e9f75d71 100644 --- a/deps/rabbitmq_management/BUILD.bazel +++ b/deps/rabbitmq_management/BUILD.bazel @@ -89,6 +89,10 @@ rabbitmq_app( "//deps/rabbitmq_web_dispatch:erlang_app", "@cowboy//:erlang_app", "@cowlib//:erlang_app", +<<<<<<< HEAD +======= + "@cuttlefish//:erlang_app", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "@ranch//:erlang_app", ], ) @@ -130,6 +134,14 @@ rabbitmq_suite( ], ) +<<<<<<< HEAD +======= +rabbitmq_suite( + name = "rabbit_mgmt_schema_SUITE", + size = "small", +) + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbitmq_integration_suite( name = "clustering_prop_SUITE", size = "large", @@ -167,7 +179,11 @@ rabbitmq_integration_suite( additional_beam = [ "test/rabbit_mgmt_runtime_parameters_util.beam", ], +<<<<<<< HEAD shard_count = 7, +======= + shard_count = 6, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) runtime_deps = [ "//deps/amqp10_client:erlang_app", ], diff --git a/deps/rabbitmq_management/Makefile b/deps/rabbitmq_management/Makefile index 98998bfcdb48..4f2367e1ec27 100644 --- a/deps/rabbitmq_management/Makefile +++ b/deps/rabbitmq_management/Makefile @@ -22,7 +22,11 @@ define PROJECT_APP_EXTRA_KEYS endef DEPS = rabbit_common rabbit amqp_client cowboy cowlib rabbitmq_web_dispatch rabbitmq_management_agent oauth2_client +<<<<<<< HEAD TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper amqp10_client +======= +TEST_DEPS = rabbitmq_ct_helpers rabbitmq_ct_client_helpers proper rabbitmq_amqp_client +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) LOCAL_DEPS += ranch ssl crypto public_key # FIXME: Add Ranch as a BUILD_DEPS to be sure the correct version is picked. diff --git a/deps/rabbitmq_management/app.bzl b/deps/rabbitmq_management/app.bzl index 5d6adba15b2c..95c5ddeae361 100644 --- a/deps/rabbitmq_management/app.bzl +++ b/deps/rabbitmq_management/app.bzl @@ -32,6 +32,10 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_mgmt_nodes.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_schema.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_stats.erl", "src/rabbit_mgmt_sup.erl", "src/rabbit_mgmt_sup_sup.erl", @@ -47,6 +51,10 @@ def all_beam_files(name = "all_beam_files"): "src/rabbit_mgmt_wm_cluster_name.erl", "src/rabbit_mgmt_wm_connection.erl", "src/rabbit_mgmt_wm_connection_channels.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_wm_connection_sessions.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_wm_connection_user_name.erl", "src/rabbit_mgmt_wm_connections.erl", "src/rabbit_mgmt_wm_connections_vhost.erl", @@ -166,6 +174,10 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/rabbit_mgmt_nodes.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_schema.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_stats.erl", "src/rabbit_mgmt_sup.erl", "src/rabbit_mgmt_sup_sup.erl", @@ -181,6 +193,10 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/rabbit_mgmt_wm_cluster_name.erl", "src/rabbit_mgmt_wm_connection.erl", "src/rabbit_mgmt_wm_connection_channels.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_wm_connection_sessions.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_wm_connection_user_name.erl", "src/rabbit_mgmt_wm_connections.erl", "src/rabbit_mgmt_wm_connections_vhost.erl", @@ -361,6 +377,10 @@ def all_srcs(name = "all_srcs"): "priv/www/js/tmpl/queues.ejs", "priv/www/js/tmpl/rate-options.ejs", "priv/www/js/tmpl/registry.ejs", +<<<<<<< HEAD +======= + "priv/www/js/tmpl/sessions-list.ejs", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "priv/www/js/tmpl/status.ejs", "priv/www/js/tmpl/topic-permissions.ejs", "priv/www/js/tmpl/user.ejs", @@ -391,6 +411,10 @@ def all_srcs(name = "all_srcs"): "src/rabbit_mgmt_nodes.erl", "src/rabbit_mgmt_oauth_bootstrap.erl", "src/rabbit_mgmt_reset_handler.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_schema.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_stats.erl", "src/rabbit_mgmt_sup.erl", "src/rabbit_mgmt_sup_sup.erl", @@ -406,6 +430,10 @@ def all_srcs(name = "all_srcs"): "src/rabbit_mgmt_wm_cluster_name.erl", "src/rabbit_mgmt_wm_connection.erl", "src/rabbit_mgmt_wm_connection_channels.erl", +<<<<<<< HEAD +======= + "src/rabbit_mgmt_wm_connection_sessions.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_mgmt_wm_connection_user_name.erl", "src/rabbit_mgmt_wm_connections.erl", "src/rabbit_mgmt_wm_connections_vhost.erl", @@ -499,6 +527,17 @@ def all_srcs(name = "all_srcs"): def test_suite_beam_files(name = "test_suite_beam_files"): erlang_bytecode( +<<<<<<< HEAD +======= + name = "rabbit_mgmt_schema_SUITE_beam_files", + testonly = True, + srcs = ["test/rabbit_mgmt_schema_SUITE.erl"], + outs = ["test/rabbit_mgmt_schema_SUITE.beam"], + app_name = "rabbitmq_management", + erlc_opts = "//:test_erlc_opts", + ) + erlang_bytecode( +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) name = "cache_SUITE_beam_files", testonly = True, srcs = ["test/cache_SUITE.erl"], diff --git a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema index 83c32b3022ac..f675f254e752 100644 --- a/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema +++ b/deps/rabbitmq_management/priv/schema/rabbitmq_management.schema @@ -472,23 +472,58 @@ end}. {mapping, "management.oauth_response_type", "rabbitmq_management.oauth_response_type", [{datatype, string}]}. +<<<<<<< HEAD %% The scopes RabbitMq should claim during the authorization flow. Defaults to "openid profile" {mapping, "management.oauth_scopes", "rabbitmq_management.oauth_scopes", [{datatype, string}]}. +======= +%% THIS VARIABLE IS DEPRECATED. CHECKOUT auth_oauth2.discovery_endpoint_path VARIABLE. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% The URL of the OIDC discovery url where the provider is listening on %% by default it is /.well-known/openid-configuration which is the %% default OIDC discovery endpoint {mapping, "management.oauth_metadata_url", "rabbitmq_management.oauth_metadata_url", [{datatype, string}]}. +<<<<<<< HEAD +======= +%% Configure OAuth2 authorization_endpoint additional request parameters +{mapping, "management.oauth_authorization_endpoint_params.$name", + "rabbitmq_management.oauth_authorization_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_management.oauth_authorization_endpoint_params", + fun(Conf) -> + rabbit_mgmt_schema:translate_endpoint_params("oauth_authorization_endpoint_params", Conf) + end}. + +%% Configure OAuth2 token_endpoint additional request parameters +{mapping, "management.oauth_token_endpoint_params.$name", + "rabbitmq_management.oauth_token_endpoint_params", + [{datatype, string}]}. + +{translation, "rabbitmq_management.oauth_token_endpoint_params", + fun(Conf) -> + rabbit_mgmt_schema:translate_endpoint_params("oauth_token_endpoint_params", Conf) + end}. + +%% The scopes RabbitMq should claim during the authorization flow. Defaults to "openid profile" +{mapping, "management.oauth_scopes", "rabbitmq_management.oauth_scopes", + [{datatype, string}]}. + + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% Configure the OAuth 2 type allowed for the end users to logon to the management UI %% Default type is sp_initiated meaning the standard OAuth 2.0 mode where users come without any token %% Other type is idp_initiated meaning users must come with a token {mapping, "management.oauth_initiated_logon_type", "rabbitmq_management.oauth_initiated_logon_type", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {mapping, "management.oauth_resource_servers.$name.id", "rabbitmq_management.oauth_resource_servers", @@ -514,8 +549,11 @@ end}. [{datatype, string}] }. +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {mapping, "management.oauth_resource_servers.$name.oauth_client_id", "rabbitmq_management.oauth_resource_servers", @@ -534,7 +572,10 @@ end}. [{datatype, string}] }. +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {mapping, "management.oauth_resource_servers.$name.oauth_scopes", "rabbitmq_management.oauth_resource_servers", @@ -552,6 +593,7 @@ end}. "rabbitmq_management.oauth_resource_servers", [{datatype, {enum, [sp_initiated, idp_initiated]}}]}. +<<<<<<< HEAD {translation, "rabbitmq_management.oauth_resource_servers", fun(Conf) -> Settings = cuttlefish_variable:filter_by_prefix("management.oauth_resource_servers", Conf), @@ -582,6 +624,19 @@ end}. end end, maps:fold(IndexByIdOrElseNameFun,#{}, NewGroupTwo) +======= +{mapping, "management.oauth_resource_servers.$name.oauth_authorization_endpoint_params.$name", + "rabbitmq_management.oauth_resource_servers", + [{datatype, string}]}. + +{mapping, "management.oauth_resource_servers.$name.oauth_token_endpoint_params.$name", + "rabbitmq_management.oauth_resource_servers", + [{datatype, string}]}. + +{translation, "rabbitmq_management.oauth_resource_servers", + fun(Conf) -> + rabbit_mgmt_schema:translate_oauth_resource_servers(Conf) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end}. %% =========================================================================== diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css index a3bcaae5d5f5..e2d5ca6cbcef 100644 --- a/deps/rabbitmq_management/priv/www/css/main.css +++ b/deps/rabbitmq_management/priv/www/css/main.css @@ -232,7 +232,11 @@ div.form-popup-help { width: 500px; z-index: 2; } +<<<<<<< HEAD p.warning, div.form-popup-warn { background: #FF9; } +======= +div.warning, p.warning, div.form-popup-warn { background: #FF9; } +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) div.form-popup-options { z-index: 3; overflow:auto; max-height:95%; } @@ -255,7 +259,18 @@ div.form-popup-options span:hover { cursor: pointer; } +<<<<<<< HEAD p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; } +======= +div.warning, p.warning { padding: 15px; border-radius: 5px; -moz-border-radius: 5px; text-align: center; } +div.warning { + margin: 15px 0; +} + +div.warning button { + margin: auto; +} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) .highlight { min-width: 120px; font-size: 120%; text-align:center; padding:10px; background-color: #ddd; margin: 0 20px 0 0; color: #888; border-radius: 5px; -moz-border-radius: 5px; } .highlight strong { font-size: 2em; display: block; color: #444; font-weight: normal; } @@ -367,3 +382,52 @@ div.bindings-wrapper p.arrow { font-size: 200%; } } table.dynamic-shovels td label {width: 200px; margin-right:10px;padding: 4px 0px 5px 0px} +<<<<<<< HEAD +======= + +input[type=checkbox].toggle { + display: none; +} + +label.toggle { + cursor: pointer; + text-indent: -9999px; + width: 32px; + height: 16px; + background: #ff5630; + display: block; + border-radius: 16px; + position: relative; + margin: auto; +} + +label.toggle:after { + content: ''; + position: absolute; + top: 2px; + left: 2px; + width: 12px; + height: 12px; + background: #fff; + border-radius: 12px; + transition: 0.3s; +} + +input.toggle:indeterminate + label.toggle { + background: #ffab00; +} + +input.toggle:checked + label.toggle { + background: #36b37e; +} + +input.toggle:indeterminate + label.toggle:after { + left: calc(50%); + transform: translateX(-50%); +} + +input.toggle:checked + label.toggle:after { + left: calc(100% - 2px); + transform: translateX(-100%); +} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/priv/www/js/dispatcher.js b/deps/rabbitmq_management/priv/www/js/dispatcher.js index e0e520715fe4..6cdb3bef7448 100644 --- a/deps/rabbitmq_management/priv/www/js/dispatcher.js +++ b/deps/rabbitmq_management/priv/www/js/dispatcher.js @@ -46,10 +46,28 @@ dispatcher_add(function(sammy) { }); sammy.get('#/connections/:name', function() { var name = esc(this.params['name']); +<<<<<<< HEAD render({'connection': {path: '/connections/' + name, options: {ranges: ['data-rates-conn']}}, 'channels': '/connections/' + name + '/channels'}, 'connection', '#/connections'); +======= + var connectionPath = '/connections/' + name; + var reqs = { + 'connection': { + path: connectionPath, + options: { ranges: ['data-rates-conn'] } + } + }; + // First, get the connection details to check the protocol + var connectionDetails = JSON.parse(sync_get(connectionPath)); + if (connectionDetails.protocol === 'AMQP 1-0') { + reqs['sessions'] = connectionPath + '/sessions'; + } else { + reqs['channels'] = connectionPath + '/channels'; + } + render(reqs, 'connection', '#/connections'); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }); sammy.del('#/connections', function() { var options = {headers: { diff --git a/deps/rabbitmq_management/priv/www/js/global.js b/deps/rabbitmq_management/priv/www/js/global.js index a3ad397bc061..498b0647eff9 100644 --- a/deps/rabbitmq_management/priv/www/js/global.js +++ b/deps/rabbitmq_management/priv/www/js/global.js @@ -108,7 +108,12 @@ var ALL_COLUMNS = ['rate-redeliver', 'redelivered', false], ['rate-ack', 'ack', true]]}, 'connections': +<<<<<<< HEAD {'Overview': [['user', 'User name', true], +======= + {'Overview': [['container_id', 'Container ID', true], + ['user', 'User name', true], +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ['state', 'State', true]], 'Details': [['ssl', 'TLS', true], ['ssl_info', 'TLS details', false], @@ -582,8 +587,42 @@ var HELP = {
Rate at which queues are created. Declaring a queue that already exists counts for a "Declared" event, but not for a "Created" event.
\
Deleted
\
Rate at which queues are deleted.
\ +<<<<<<< HEAD ' +======= + ', + + 'container-id': + 'Name of the client application as sent from client to RabbitMQ in the "container-id" field of the AMQP 1.0 open frame.', + + 'incoming-links': + 'Links where the client is the sender/publisher and RabbitMQ is the receiver of messages.', + + 'outgoing-links': + 'Links where the client is the receiver/consumer and RabbitMQ is the sender of messages.', + + 'target-address': + 'The "address" field of the link target.', + + 'source-address': + 'The "address" field of the link source.', + + 'amqp-source-queue': + 'The client receives messages from this queue.', + + 'amqp-unconfirmed-messages': + 'Number of messages that have been sent to queues but have not been confirmed by all queues.', + + 'snd-settle-mode': + 'Sender Settle Mode', + + 'sender-settles': + '"true" if the sender sends all deliveries settled to the receiver. "false" if the sender sends all deliveries initially unsettled to the receiver.', + + 'outgoing-unsettled-deliveries': + 'Number of messages that have been sent to consumers but have not yet been settled/acknowledged.' +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }; /////////////////////////////////////////////////////////////////////////// diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js index 22c9f47bc145..cd0412b53263 100644 --- a/deps/rabbitmq_management/priv/www/js/main.js +++ b/deps/rabbitmq_management/priv/www/js/main.js @@ -301,6 +301,26 @@ function reset_timer() { } } +<<<<<<< HEAD +======= +function pause_auto_refresh() { + if (typeof globalThis.rmq_webui_auto_refresh_paused == 'undefined') + globalThis.rmq_webui_auto_refresh_paused = 0; + + globalThis.rmq_webui_auto_refresh_paused++; + if (timer != null) { + clearInterval(timer); + } +} + +function resume_auto_refresh() { + globalThis.rmq_webui_auto_refresh_paused--; + if (globalThis.rmq_webui_auto_refresh_paused == 0) { + reset_timer(); + } +} + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) function update_manual(div, query) { var path; var template; diff --git a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js index ef5e20f44812..bf31ea0d92db 100644 --- a/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js +++ b/deps/rabbitmq_management/priv/www/js/oidc-oauth/helper.js @@ -46,6 +46,7 @@ function auth_settings_apply_defaults(authSettings) { } if (!resource_server.oauth_response_type) { resource_server.oauth_response_type = authSettings.oauth_response_type +<<<<<<< HEAD if (!resource_server.oauth_response_type) { resource_server.oauth_response_type = "code" } @@ -55,6 +56,11 @@ function auth_settings_apply_defaults(authSettings) { if (!resource_server.oauth_scopes) { resource_server.oauth_scopes = "openid profile" } +======= + } + if (!resource_server.oauth_scopes) { + resource_server.oauth_scopes = authSettings.oauth_scopes +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } if (!resource_server.oauth_client_id) { resource_server.oauth_client_id = authSettings.oauth_client_id @@ -78,6 +84,17 @@ function auth_settings_apply_defaults(authSettings) { if (!resource_server.oauth_metadata_url) { resource_server.oauth_metadata_url = authSettings.metadata_url } +<<<<<<< HEAD +======= + if (!resource_server.oauth_authorization_endpoint_params) { + resource_server.oauth_authorization_endpoint_params = + authSettings.oauth_authorization_endpoint_params + } + if (!resource_server.oauth_token_endpoint_params) { + resource_server.oauth_token_endpoint_params = + authSettings.oauth_token_endpoint_params + } +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) resource_server.id = resource_server_id authSettings.resource_servers.push(resource_server) } @@ -98,6 +115,7 @@ function get_oauth_settings() { export function oauth_initialize_if_required(state = "index") { let oauth = oauth_initialize(get_oauth_settings()) if (!oauth.enabled) return oauth; +<<<<<<< HEAD switch (state) { case 'login-callback': oauth_completeLogin(); break; @@ -107,12 +125,27 @@ export function oauth_initialize_if_required(state = "index") { oauth = oauth_initiate(oauth); } return oauth; +======= + switch (state) { + case 'login-callback': + oauth_completeLogin(); break; + case 'logout-callback': + oauth_completeLogout(); break; + default: + oauth = oauth_initiate(oauth); + } + return oauth; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } export function oauth_initiate(oauth) { if (oauth.enabled) { if (!oauth.sp_initiated) { +<<<<<<< HEAD oauth.logged_in = has_auth_credentials(); +======= + oauth.logged_in = has_auth_credentials(); +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } else { oauth_is_logged_in().then( status => { if (status.loggedIn && !has_auth_credentials()) { @@ -122,7 +155,11 @@ export function oauth_initiate(oauth) { if (!status.loggedIn) { clear_auth(); } else { +<<<<<<< HEAD oauth.logged_in = true; +======= + oauth.logged_in = true; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) oauth.expiryDate = new Date(status.user.expires_at * 1000); // it is epoch in seconds let current = new Date(); _management_logger.debug('token expires in ', (oauth.expiryDate-current)/1000, @@ -139,6 +176,7 @@ export function oauth_initiate(oauth) { } return oauth; } +<<<<<<< HEAD function oauth_initialize_user_manager(resource_server) { let oidcSettings = { userStore: new oidc.WebStorageStateStore({ store: window.localStorage }), @@ -173,6 +211,43 @@ function oauth_initialize_user_manager(resource_server) { mgr = new oidc.UserManager(oidcSettings); // oauth.readiness_url = mgr.settings.metadataUrl; +======= +export function oidc_settings_from(resource_server) { + let oidcSettings = { + userStore: new oidc.WebStorageStateStore({ store: window.localStorage }), + authority: resource_server.oauth_provider_url, + metadataUrl: resource_server.oauth_metadata_url, + client_id: resource_server.oauth_client_id, + response_type: resource_server.oauth_response_type, + scope: resource_server.oauth_scopes, + redirect_uri: rabbit_base_uri() + "/js/oidc-oauth/login-callback.html", + post_logout_redirect_uri: rabbit_base_uri() + "/", + automaticSilentRenew: true, + revokeAccessTokenOnSignout: true + } + if (resource_server.end_session_endpoint != "") { + oidcSettings.metadataSeed = { + end_session_endpoint: resource_server.end_session_endpoint + } + } + if (resource_server.oauth_client_secret != "") { + oidcSettings.client_secret = resource_server.oauth_client_secret + } + if (resource_server.oauth_authorization_endpoint_params) { + oidcSettings.extraQueryParams = resource_server.oauth_authorization_endpoint_params + } + if (resource_server.oauth_token_endpoint_params) { + oidcSettings.extraTokenParams = resource_server.oauth_token_endpoint_params + } + return oidcSettings +} + +function oauth_initialize_user_manager(resource_server) { + oidc.Log.setLevel(oidc.Log.DEBUG); + oidc.Log.setLogger(console); + + mgr = new oidc.UserManager(oidc_settings_from(resource_server)) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) _management_logger = new oidc.Logger("Management"); @@ -218,6 +293,7 @@ export function oauth_initialize(authSettings) { return oauth; } +<<<<<<< HEAD function log() { message = "" Array.prototype.forEach.call(arguments, function(msg) { @@ -232,6 +308,8 @@ function log() { _management_logger.info(message) } +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) function oauth_is_logged_in() { return mgr.getUser().then(user => { if (!user) { diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs index f834b02fb5e0..3524f5884cb0 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/connection.ejs @@ -1,7 +1,11 @@

Connection <%= fmt_string(connection.name) %> <%= fmt_maybe_vhost(connection.vhost) %>

<% if (!disable_stats) { %> +<<<<<<< HEAD
+======= +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)

Overview

<%= data_rates('data-rates-conn', connection, 'Data rates') %> @@ -17,11 +21,27 @@ <% if (connection.client_properties.connection_name) { %> +<<<<<<< HEAD Client-provided name +======= + Client-provided connection name +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <%= fmt_string(connection.client_properties.connection_name) %> <% } %> +<<<<<<< HEAD +======= +<% if (connection.container_id) { %> + + Container ID + + + <%= fmt_string(connection.container_id) %> + +<% } %> + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Username <%= fmt_string(connection.user) %> @@ -75,13 +95,33 @@
+<<<<<<< HEAD
+======= +<% if (connection.protocol === 'AMQP 1-0') { %> + +
+

Sessions (<%=(sessions.length)%>)

+
+ <%= format('sessions-list', {'sessions': sessions}) %> +
+
+ +<% } else { %> + +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)

Channels (<%=(channels.length)%>)

<%= format('channels-list', {'channels': channels, 'mode': 'connection'}) %>
+<<<<<<< HEAD +======= +<% } %> + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% if (connection.ssl) { %>

SSL

@@ -127,7 +167,11 @@ <% } %> <% if (properties_size(connection.client_properties) > 0) { %> +<<<<<<< HEAD
+======= +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)

Client properties

<%= fmt_table_long(connection.client_properties) %> @@ -136,7 +180,11 @@ <% } %> <% if(connection.reductions || connection.garbage_collection) { %> +<<<<<<< HEAD
+======= +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)

Runtime Metrics (Advanced)

<%= data_reductions('reductions-rates-conn', connection) %> @@ -175,7 +223,11 @@ <% } %> <% } %> +<<<<<<< HEAD
+======= +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)

Close this connection

diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/connections.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/connections.ejs index 464894d20876..caf6dd896b28 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/connections.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/connections.ejs @@ -1,8 +1,15 @@

Connections

+<<<<<<< HEAD
<%= paginate_ui(connections, 'connections') %>
+======= +
+ <%= paginate_ui(connections, 'connections') %> +
+
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% if (connections.items.length > 0) { %> @@ -26,6 +33,12 @@ <% if (nodes_interesting) { %> <% } %> +<<<<<<< HEAD +======= +<% if (show_column('connections', 'container_id')) { %> + +<% } %> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% if (show_column('connections', 'user')) { %> <% } %> @@ -84,7 +97,13 @@ <% if(connection.client_properties) { %> <% } else { %> @@ -92,6 +111,16 @@ <% if (nodes_interesting) { %> <% } %> +<<<<<<< HEAD +======= +<% if (show_column('connections', 'container_id')) { %> + +<% } %> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% if (show_column('connections', 'user')) { %> <% } %> diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/deprecated-features.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/deprecated-features.ejs index 384ffefb3c9c..9ec0aedbe5b9 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/deprecated-features.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/deprecated-features.ejs @@ -56,6 +56,12 @@ <% } else { %>

... no deprecated features ...

<% } %> +<<<<<<< HEAD +======= +

+ See the Deprecated features documentation for more information. +

+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs index a2ed48ad4573..fc1a956db3e8 100644 --- a/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs +++ b/deps/rabbitmq_management/priv/www/js/tmpl/feature-flags.ejs @@ -1,3 +1,4 @@ +<<<<<<< HEAD

Feature Flags

<% var needs_enabling = false; @@ -18,16 +19,290 @@ <%= filter_ui(feature_flags) %>
<% if (feature_flags.length > 0) { %> +======= + +

Feature Flags

+ <% + var nonreq_feature_flags = []; + for (var i = 0; i < feature_flags.length; i++) { + if (feature_flags[i].stability == 'required') + continue; + nonreq_feature_flags.push(feature_flags[i]); + } + %> + +
+

Feature Flags

+
+<%= filter_ui(nonreq_feature_flags) %> +
+<% if (nonreq_feature_flags.length > 0) { %> + + + + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906)
<%= fmt_sort('Node', 'node') %>Container ID <%= fmt_sort('User name', 'user') %> <%= link_conn(connection.name) %> +<<<<<<< HEAD <%= fmt_string(short_conn(connection.client_properties.connection_name)) %> +======= + <% if (connection.client_properties.connection_name) { %> + <%= fmt_string(short_conn(connection.client_properties.connection_name)) %> + <% } %> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <%= link_conn(connection.name) %><%= fmt_node(connection.node) %> + <% if (connection.container_id) { %> + <%= fmt_string(connection.container_id) %> + <% } %> + <%= fmt_string(connection.user) %>
+<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% +<<<<<<< HEAD for (var i = 0; i < feature_flags.length; i++) { var feature_flag = feature_flags[i]; if (feature_flag.stability == "experimental") { @@ -135,6 +410,43 @@ These flags can be enabled in production deployments after an appropriate amount <%= fmt_string(feature_flag.state) %> <% } %> +======= + for (var i = 0; i < nonreq_feature_flags.length; i++) { + var feature_flag = nonreq_feature_flags[i]; + %> + > + + + +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) <% } %> diff --git a/deps/rabbitmq_stream_management/test/http_SUITE_data/pom.xml b/deps/rabbitmq_stream_management/test/http_SUITE_data/pom.xml index c8103f607e72..9796d43eb395 100644 --- a/deps/rabbitmq_stream_management/test/http_SUITE_data/pom.xml +++ b/deps/rabbitmq_stream_management/test/http_SUITE_data/pom.xml @@ -27,11 +27,19 @@ [0.12.0-SNAPSHOT,) +<<<<<<< HEAD 5.10.3 3.26.3 1.2.13 3.12.1 3.3.0 +======= + 5.11.3 + 3.26.3 + 1.2.13 + 3.12.1 + 3.5.2 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) 2.43.0 1.18.1 4.12.0 diff --git a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl index 67e99400b500..12044ec824c6 100644 --- a/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl +++ b/deps/rabbitmq_web_mqtt/src/rabbit_web_mqtt_handler.erl @@ -42,7 +42,11 @@ stats_timer :: option(rabbit_event:state()), keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(), conn_name :: option(binary()) +<<<<<<< HEAD }). +======= + }). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -type state() :: #state{}. @@ -79,6 +83,7 @@ init(Req, Opts) -> false -> no_supported_sub_protocol(Protocol, Req); true -> +<<<<<<< HEAD WsOpts0 = proplists:get_value(ws_opts, Opts, #{}), WsOpts = maps:merge(#{compress => true}, WsOpts0), @@ -86,6 +91,14 @@ init(Req, Opts) -> cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, <<"mqtt">>, Req), #state{socket = maps:get(proxy_header, Req, undefined)}, WsOpts} +======= + Req1 = cowboy_req:set_resp_header(<<"sec-websocket-protocol">>, <<"mqtt">>, Req), + State = #state{socket = maps:get(proxy_header, Req, undefined), + stats_timer = rabbit_event:init_stats_timer()}, + WsOpts0 = proplists:get_value(ws_opts, Opts, #{}), + WsOpts = maps:merge(#{compress => true}, WsOpts0), + {?MODULE, Req1, State, WsOpts} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end end. @@ -112,8 +125,12 @@ websocket_init(State0 = #state{socket = Sock}) -> ConnName = rabbit_data_coercion:to_binary(ConnStr), ?LOG_INFO("Accepting Web MQTT connection ~s", [ConnName]), _ = rabbit_alarm:register(self(), {?MODULE, conserve_resources, []}), +<<<<<<< HEAD State1 = State0#state{conn_name = ConnName}, State = rabbit_event:init_stats_timer(State1, #state.stats_timer), +======= + State = State0#state{conn_name = ConnName}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) process_flag(trap_exit, true), {[], State, hibernate}; {error, Reason} -> diff --git a/deps/rabbitmq_web_mqtt/test/web_mqtt_shared_SUITE.erl b/deps/rabbitmq_web_mqtt/test/web_mqtt_shared_SUITE.erl index f3818b34ee06..f3b3da4674f1 100644 --- a/deps/rabbitmq_web_mqtt/test/web_mqtt_shared_SUITE.erl +++ b/deps/rabbitmq_web_mqtt/test/web_mqtt_shared_SUITE.erl @@ -87,6 +87,10 @@ bind_exchange_to_exchange_single_message(Config) -> mqtt_shared_SUITE:?FUNCTION_ pubsub(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). queue_down_qos1(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). consuming_classic_queue_down(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). +<<<<<<< HEAD +======= +flow_classic_queue(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) flow_quorum_queue(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). flow_stream(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). rabbit_mqtt_qos0_queue(Config) -> mqtt_shared_SUITE:?FUNCTION_NAME(Config). diff --git a/moduleindex.yaml b/moduleindex.yaml index c0809c1f9156..0aed7fcc15f8 100755 --- a/moduleindex.yaml +++ b/moduleindex.yaml @@ -543,6 +543,10 @@ rabbit: - rabbit_access_control - rabbit_alarm - rabbit_amqp1_0 +<<<<<<< HEAD +======= +- rabbit_amqp_filtex +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_amqp_management - rabbit_amqp_reader - rabbit_amqp_session @@ -670,6 +674,10 @@ rabbit: - rabbit_metrics - rabbit_mirror_queue_misc - rabbit_mnesia +<<<<<<< HEAD +======= +- rabbit_msg_size_metrics +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_msg_store - rabbit_msg_store_gc - rabbit_networking @@ -835,7 +843,14 @@ rabbitmq_auth_backend_oauth2: - Elixir.RabbitMQ.CLI.Ctl.Commands.AddUaaKeyCommand - rabbit_auth_backend_oauth2 - rabbit_auth_backend_oauth2_app +<<<<<<< HEAD - rabbit_oauth2_config +======= +- rabbit_oauth2_keycloak +- rabbit_oauth2_provider +- rabbit_oauth2_rar +- rabbit_oauth2_resource_server +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_oauth2_schema - rabbit_oauth2_scope - uaa_jwks @@ -928,6 +943,10 @@ rabbitmq_management: - rabbit_mgmt_nodes - rabbit_mgmt_oauth_bootstrap - rabbit_mgmt_reset_handler +<<<<<<< HEAD +======= +- rabbit_mgmt_schema +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_mgmt_stats - rabbit_mgmt_sup - rabbit_mgmt_sup_sup @@ -943,6 +962,10 @@ rabbitmq_management: - rabbit_mgmt_wm_cluster_name - rabbit_mgmt_wm_connection - rabbit_mgmt_wm_connection_channels +<<<<<<< HEAD +======= +- rabbit_mgmt_wm_connection_sessions +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_mgmt_wm_connection_user_name - rabbit_mgmt_wm_connections - rabbit_mgmt_wm_connections_vhost @@ -1103,6 +1126,10 @@ rabbitmq_prometheus: - prometheus_rabbitmq_core_metrics_collector - prometheus_rabbitmq_dynamic_collector - prometheus_rabbitmq_global_metrics_collector +<<<<<<< HEAD +======= +- prometheus_rabbitmq_message_size_metrics_collector +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) - rabbit_prometheus_app - rabbit_prometheus_dispatcher - rabbit_prometheus_handler diff --git a/rabbitmq-components.mk b/rabbitmq-components.mk index 8dfdaed0664a..6a0358ae3fb4 100644 --- a/rabbitmq-components.mk +++ b/rabbitmq-components.mk @@ -53,7 +53,11 @@ dep_prometheus = hex 4.11.0 dep_ra = hex 2.14.0 dep_ranch = hex 2.1.0 dep_recon = hex 2.5.6 +<<<<<<< HEAD dep_redbug = hex 2.1.0 +======= +dep_redbug = hex 2.0.7 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) dep_systemd = hex 0.6.1 dep_thoas = hex 1.2.1 dep_observer_cli = hex 1.7.5 diff --git a/rabbitmq.bzl b/rabbitmq.bzl index d0a5b52405fc..8ffdd8acb398 100644 --- a/rabbitmq.bzl +++ b/rabbitmq.bzl @@ -290,12 +290,20 @@ def rabbitmq_integration_suite( "RABBITMQCTL": "$TEST_SRCDIR/$TEST_WORKSPACE/{}/broker-for-tests-home/sbin/rabbitmqctl".format(package), "RABBITMQ_PLUGINS": "$TEST_SRCDIR/$TEST_WORKSPACE/{}/broker-for-tests-home/sbin/rabbitmq-plugins".format(package), "RABBITMQ_QUEUES": "$TEST_SRCDIR/$TEST_WORKSPACE/{}/broker-for-tests-home/sbin/rabbitmq-queues".format(package), +<<<<<<< HEAD "RABBITMQ_RUN_SECONDARY": "$(location @rabbitmq-server-generic-unix-3.13//:rabbitmq-run)", +======= + "RABBITMQ_RUN_SECONDARY": "$(location @rabbitmq-server-generic-unix-4.0//:rabbitmq-run)", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "LANG": "C.UTF-8", }.items() + test_env.items()), tools = [ ":rabbitmq-for-tests-run", +<<<<<<< HEAD "@rabbitmq-server-generic-unix-3.13//:rabbitmq-run", +======= + "@rabbitmq-server-generic-unix-4.0//:rabbitmq-run", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ] + tools, deps = assumed_deps + deps + runtime_deps, ct_run_extra_args = ["-kernel net_ticktime 5"], diff --git a/release-notes/4.0.1.md b/release-notes/4.0.1.md index 50a0d3c84cce..a2d57cf00541 100644 --- a/release-notes/4.0.1.md +++ b/release-notes/4.0.1.md @@ -356,6 +356,15 @@ This section is incomplete and will be expanded as 4.0 approaches its release ca GitHub issue: [#11743](https://github.com/rabbitmq/rabbitmq-server/issues/11743) +<<<<<<< HEAD +======= + * Several new metrics for streams, for example, `stream_consumer_max_offset_lag`. + + Contributed by @markus812498, @gomoripeti. + + GitHub issue: [#10275](https://github.com/rabbitmq/rabbitmq-server/pull/10275) + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) * New per-exchange and per-queue metrics. Contributed by @LoisSotoLopez. diff --git a/release-notes/4.1.0.md b/release-notes/4.1.0.md new file mode 100644 index 000000000000..56f9f897f7b3 --- /dev/null +++ b/release-notes/4.1.0.md @@ -0,0 +1,196 @@ +## RabbitMQ 4.1.0-beta.2 + +RabbitMQ 4.1.0-beta.2 is a preview release (in development) of a new feature release. + +See Compatibility Notes below to learn about **breaking or potentially breaking changes** in this release. + + +## Highlights + +Some key improvements in this release are listed below. + +### Initial Support for AMQP 1.0 Filter Expressions + +Support for the `properties` and `appliation-properties` filters of [AMQP Filter Expressions Version 1.0 Working Draft 09](https://groups.oasis-open.org/higherlogic/ws/public/document?document_id=66227). + + +### Feature Flags Quality of Life Improvements + +Graduated (mandatory) [feature flags](https://www.rabbitmq.com/docs/feature-flags) several minors ago has proven that they could use some user experience improvements. +For example, certain required feature flags will now be enabled on node boot when all nodes in the cluster support them. + +See core server changes below as well as the [GitHub project dedicated to feature flags improvements](https://github.com/orgs/rabbitmq/projects/4/views/1) +for the complete list of related changes. + + +## Breaking Changes and Compatibility Notes + +### MQTT + + * The default MQTT [Maximum Packet Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901086) changed from 256 MiB to 16 MiB. + + This default can be overridden by [configuring](https://www.rabbitmq.com/docs/configure#config-file) `mqtt.max_packet_size_authenticated`. + Note that this value must not be greater than `max_message_size` (which also defaults to 16 MiB). + + +## Erlang/OTP Compatibility Notes + +This release [requires Erlang 26.2](https://www.rabbitmq.com/docs/which-erlang). + +[Provisioning Latest Erlang Releases](https://www.rabbitmq.com/docs/which-erlang#erlang-repositories) explains +what package repositories and tools can be used to provision latest patch versions of Erlang 26.x. + + +## Release Artifacts + +TBD + + +## Upgrading to 4.1.0 + +### Documentation guides on upgrades + +See the [Upgrading guide](https://www.rabbitmq.com/docs/upgrade) for documentation on upgrades and [GitHub releases](https://github.com/rabbitmq/rabbitmq-server/releases) +for release notes of individual releases. + +This release series only supports upgrades from `4.0.x`. + +[Blue/Green Deployment](https://www.rabbitmq.com/docs/blue-green-upgrade)-style upgrades are avaialble for migrations from 3.12.x and 3.13.x series +to `4.1.x`. + +### Required Feature Flags + +None/TBD. + +### Mixed version cluster compatibility + +RabbitMQ 4.1.0 nodes can run alongside `4.0.x` nodes. `4.1.x`-specific features can only be made available when all nodes in the cluster +upgrade to 4.0.0 or a later patch release in the new series. + +While operating in mixed version mode, some aspects of the system may not behave as expected. The list of known behavior changes will be covered in future updates. +Once all nodes are upgraded to 4.1.0, these irregularities will go away. + +Mixed version clusters are a mechanism that allows rolling upgrade and are not meant to be run for extended +periods of time (no more than a few hours). + +### Recommended Post-upgrade Procedures + +TBD + + + +## Changes Worth Mentioning + +This section is incomplete and will be expanded as 4.1 approaches its release candidate stage. + +### Core Server + +#### Enhancements + + * Feature flag quality of live improvements. + + Certain required feature flags will now be automatically required on node boot + and do not have to be explicitly enabled before an upgrade. + This does not apply to all feature flags, however. + + GitHub project: [#4](https://github.com/orgs/rabbitmq/projects/4/views/1). + + GitHub issues: [#12466](https://github.com/rabbitmq/rabbitmq-server/pull/12466), [#12444](https://github.com/rabbitmq/rabbitmq-server/pull/12444), + [#12447](https://github.com/rabbitmq/rabbitmq-server/pull/12447) + + * `properties` and `appliation-properties` filters of [AMQP Filter Expressions Version 1.0 Working Draft 09](https://groups.oasis-open.org/higherlogic/ws/public/document?document_id=66227) + when consuming from a stream via AMQP 1.0. String prefix and suffix matching is also supported. + + This feature adds the ability to RabbitMQ to have multiple concurrent clients each consuming only a subset of messages while maintaining message order. + It also reduces network traffic between RabbitMQ and clients by only dispatching those messages that the clients are actually interested in. + + GitHub issue: [#12415](https://github.com/rabbitmq/rabbitmq-server/pull/12415) + + * AMQP 1.0 connections that use OAuth 2.0 now can renew their JWT tokens + This allows clients to set a new token proactively before the current one [expires](/docs/oauth2#token-expiration), ensuring uninterrupted connectivity. + If a client does not set a new token before the existing one expires, RabbitMQ will automatically close the AMQP 1.0 connection. + + GitHub issue: [#12599](https://github.com/rabbitmq/rabbitmq-server/pull/12599) + + * Support for Multiple Routing Keys in AMQP 1.0 via `x-cc` Message Annotation. + + AMQP 1.0 publishers now can set multiple routing keys by using the `x-cc` message annotation. + This annotation allows publishers to specify a [list](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#type-list) + of routing keys ([strings](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#type-string)) for more flexible message distribution, + similar to the [CC](https://www.rabbitmq.com/docs/sender-selected) header in AMQP 0.9.1. + + GitHub issue: [#12559](https://github.com/rabbitmq/rabbitmq-server/pull/12559) + + +### MQTT Plugin + +#### Enhancements + + * The default MQTT [Maximum Packet Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901086) changed from 256 MiB to 16 MiB. + + This default can be overridden by [configuring](https://www.rabbitmq.com/docs/configure#config-file) `mqtt.max_packet_size_authenticated`. + Note that this value must not be greater than `max_message_size` (which also defaults to 16 MiB). + + +### Prometheus Plugin + +#### Enhancements + + * RabbitMQ nodes now provide a Prometheus histogram for message sizes published by applications. + + This feature allows operators to gain insights into the message sizes being published to RabbitMQ, + such as average message size, number of messages per pre-defined bucket (which can both be computed accurately), and percentiles (which will be approximated). + Each metric is labelled by protocol (AMQP 1.0, AMQP 0.9.1, MQTT 5.0, MQTT 3.1.1, and MQTT 3.1). + + GitHub issue: [#12342](https://github.com/rabbitmq/rabbitmq-server/pull/12342) + + +### Management UI + +#### Enhancements + +* Connection pages now display detailed AMQP 1.0 session and link information: + + 1. Link names + 2. Link target and source addresses + 3. Link flow control state + 4. Session flow control state + 5. Number of unconfirmed and unacknowledged messages + + GitHub issue: [#12670](https://github.com/rabbitmq/rabbitmq-server/pull/12670) + +* The management UI now shows if a feature flag has a migration function (in other words, it may take time to be enabled), + if it is experimental and whether it is supported or not. To enable an experimental feature flag, + a user must to tick checkboxes to confirm they know what they are doing. + + GitHub issue: [#12643](https://github.com/rabbitmq/rabbitmq-server/pull/12643) + +* Feature flags are now enabled using asynchronous requests in the management UI. + This means that feature flags that perform data migrations (which can take some time) + won't block the browser tab. + + GitHub issue: [#12643](https://github.com/rabbitmq/rabbitmq-server/pull/12643) + + +### Event Exchange Plugin + +#### Enhancements + + * The `rabbitmq_event_exchange` plugin now can be configured to internally publish AMQP 1.0 instead of AMQP 0.9.1 messages to the `amq.rabbitmq.event` topic exchange. + + This allows AMQP 1.0 consumers to receive event properties containing complex types such as [lists](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#type-list) + or [maps](https://docs.oasis-open.org/amqp/core/v1.0/os/amqp-core-types-v1.0-os.html#type-map), for example queue arguments for the `queue.created` + event or client provided properties for the `connection.created` event. + + GitHub issue: [#12714](https://github.com/rabbitmq/rabbitmq-server/pull/12714) + + +### Dependency Changes + +TBD + + +## Source Code Archives + +To obtain source code of the entire distribution, please download the archive named `rabbitmq-server-4.1.0.tar.xz` +instead of the source tarball produced by GitHub. diff --git a/selenium/.gitignore b/selenium/.gitignore index ee78120fba98..4d47196ce4de 100644 --- a/selenium/.gitignore +++ b/selenium/.gitignore @@ -13,4 +13,8 @@ test/*/certs/*.p12 test/*/certs/*.jks test/*/*/*.pem test/*/*/*.p12 -test/*/*/*.jks \ No newline at end of file +<<<<<<< HEAD +test/*/*/*.jks +======= +test/*/*/*.jks +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/bin/components/devkeycloak b/selenium/bin/components/devkeycloak index 87062b67465b..732611c963ab 100644 --- a/selenium/bin/components/devkeycloak +++ b/selenium/bin/components/devkeycloak @@ -48,7 +48,11 @@ start_devkeycloak() { --https-certificate-key-file=/opt/keycloak/data/import/server_devkeycloak_key.pem \ --hostname=devkeycloak --hostname-admin=devkeycloak --https-port=8442 +<<<<<<< HEAD wait_for_oidc_endpoint devkeycloak $DEVKEYCLOAK_URL $MOUNT_DEVKEYCLOAK_CONF_DIR/ca_devkeycloak_certificate.pem +======= + wait_for_oidc_endpoint devkeycloak $DEVKEYCLOAK_URL $MOUNT_DEVKEYCLOAK_CONF_DIR/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end "devkeycloak is ready" print " Note: If you modify devkeycloak configuration, make sure to run the following command to export the configuration." print " docker exec -it devkeycloak /opt/keycloak/bin/kc.sh export --users realm_file --realm test --dir /opt/keycloak/data/import/" diff --git a/selenium/bin/components/fakeportal b/selenium/bin/components/fakeportal index aadbda50327b..d8a4d1ebe947 100644 --- a/selenium/bin/components/fakeportal +++ b/selenium/bin/components/fakeportal @@ -1,3 +1,13 @@ +<<<<<<< HEAD +======= +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ensure_fakeportal() { if docker ps | grep fakeportal &> /dev/null; then @@ -9,7 +19,11 @@ ensure_fakeportal() { init_fakeportal() { FAKEPORTAL_URL=${FAKEPORTAL_URL:-http://fakeportal:3000} +<<<<<<< HEAD FAKEPORTAL_DIR=${SCRIPT}/../fakeportal +======= + FAKEPORTAL_DIR=${SCRIPT}/../../fakeportal +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) CLIENT_ID="${CLIENT_ID:-rabbit_idp_user}" CLIENT_SECRET="${CLIENT_SECRET:-rabbit_idp_user}" RABBITMQ_HOST=${RABBITMQ_HOST:-proxy:9090} @@ -44,6 +58,11 @@ start_fakeportal() { --env UAA_URL="${UAA_URL_FOR_FAKEPORTAL}" \ --env CLIENT_ID="${CLIENT_ID}" \ --env CLIENT_SECRET="${CLIENT_SECRET}" \ +<<<<<<< HEAD +======= + --env NODE_EXTRA_CA_CERTS=/etc/uaa/ca_uaa_certificate.pem \ + -v ${TEST_CONFIG_PATH}/uaa:/etc/uaa \ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -v ${FAKEPORTAL_DIR}:/code/fakeportal \ mocha-test:${mocha_test_tag} run fakeportal diff --git a/selenium/bin/components/fakeproxy b/selenium/bin/components/fakeproxy index 2705ee80427e..1ac224bbab98 100644 --- a/selenium/bin/components/fakeproxy +++ b/selenium/bin/components/fakeproxy @@ -1,4 +1,14 @@ +<<<<<<< HEAD +======= +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ensure_fakeproxy() { if docker ps | grep fakeproxy &> /dev/null; then @@ -10,7 +20,11 @@ ensure_fakeproxy() { init_fakeproxy() { FAKEPROXY_URL=${FAKEPROXY_URL:-http://fakeproxy:9090} +<<<<<<< HEAD FAKEPROXY_DIR=${SCRIPT}/../fakeportal +======= + FAKEPROXY_DIR=${SCRIPT}/../../fakeportal +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) CLIENT_ID="${CLIENT_ID:-rabbit_idp_user}" CLIENT_SECRET="${CLIENT_SECRET:-rabbit_idp_user}" RABBITMQ_HOST_FOR_FAKEPROXY=${RABBITMQ_HOST_FOR_FAKEPROXY:-rabbitmq:15672} @@ -43,6 +57,11 @@ start_fakeproxy() { --env UAA_URL="${UAA_URL_FOR_FAKEPROXY}" \ --env CLIENT_ID="${CLIENT_ID}" \ --env CLIENT_SECRET="${CLIENT_SECRET}" \ +<<<<<<< HEAD +======= + --env NODE_EXTRA_CA_CERTS=/etc/uaa/ca_uaa_certificate.pem \ + -v ${TEST_CONFIG_PATH}/uaa:/etc/uaa \ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -v ${FAKEPROXY_DIR}:/code/fakeportal \ mocha-test:${mocha_test_tag} run fakeproxy diff --git a/selenium/bin/components/prodkeycloak b/selenium/bin/components/prodkeycloak index 9753d66ebf4b..52ce3fd8d0d4 100644 --- a/selenium/bin/components/prodkeycloak +++ b/selenium/bin/components/prodkeycloak @@ -17,7 +17,12 @@ init_prodkeycloak() { print "> PRODKEYCLOAK_URL: ${PRODKEYCLOAK_URL}" print "> KEYCLOAK_DOCKER_IMAGE: ${KEYCLOAK_DOCKER_IMAGE}" +<<<<<<< HEAD generate-ca-server-client-kpi prodkeycloak $PRODKEYCLOAK_CONFIG_DIR +======= + generate-ca-server-client-kpi prodkeycloak $PRODKEYCLOAK_CONFIG_DIR + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } start_prodkeycloak() { begin "Starting prodkeycloak ..." @@ -46,7 +51,11 @@ start_prodkeycloak() { --https-certificate-key-file=/opt/keycloak/data/import/server_prodkeycloak_key.pem \ --hostname=prodkeycloak --hostname-admin=prodkeycloak --https-port=8443 +<<<<<<< HEAD wait_for_oidc_endpoint prodkeycloak $PRODKEYCLOAK_URL $MOUNT_PRODKEYCLOAK_CONF_DIR/ca_prodkeycloak_certificate.pem +======= + wait_for_oidc_endpoint prodkeycloak $PRODKEYCLOAK_URL $MOUNT_PRODKEYCLOAK_CONF_DIR/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end "prodkeycloak is ready" print " Note: If you modify prodkeycloak configuration, make sure to run the following command to export the configuration." print " docker exec -it prodkeycloak /opt/keycloak/bin/kc.sh export --users realm_file --realm test --dir /opt/keycloak/data/import/" diff --git a/selenium/bin/components/rabbitmq b/selenium/bin/components/rabbitmq index 46cbb1ee2738..9f7d8189496e 100644 --- a/selenium/bin/components/rabbitmq +++ b/selenium/bin/components/rabbitmq @@ -1,6 +1,14 @@ +<<<<<<< HEAD #!/usr/bin/env bash +======= +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_rabbitmq() { RABBITMQ_CONFIG_DIR=${TEST_CONFIG_DIR} RABBITMQ_DOCKER_IMAGE=${RABBITMQ_DOCKER_IMAGE:-rabbitmq} @@ -60,6 +68,10 @@ start_local_rabbitmq() { init_rabbitmq RABBITMQ_SERVER_ROOT=$(realpath ../) +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) MOUNT_RABBITMQ_CONF="/etc/rabbitmq/rabbitmq.conf" MOUNT_ADVANCED_CONFIG="/etc/rabbitmq/advanced.config" @@ -164,7 +176,11 @@ start_docker_rabbitmq() { if [ -f ${RABBITMQ_CONFIG_DIR}/enabled_plugins ]; then cp ${RABBITMQ_CONFIG_DIR}/enabled_plugins $CONF_DIR/rabbitmq fi +<<<<<<< HEAD if [ -d ${RABBITMQ_CONFIG_DIR}/certs ]; then +======= + if [ -d "${RABBITMQ_CONFIG_DIR}/certs" ]; then +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cp -r ${RABBITMQ_CONFIG_DIR}/certs $CONF_DIR/rabbitmq fi if [ -d ${RABBITMQ_CONFIG_DIR}/imports ]; then @@ -182,10 +198,17 @@ start_docker_rabbitmq() { -p 15672:15672 \ -p 15671:15671 \ -v $CONF_DIR/rabbitmq/:/etc/rabbitmq \ +<<<<<<< HEAD -v $CONF_DIR/rabbitmq/:/var/rabbitmq \ -v ${TEST_DIR}:/config \ ${RABBITMQ_DOCKER_IMAGE} +======= + -v $CONF_DIR/rabbitmq/imports:/var/rabbitmq/imports \ + -v ${TEST_DIR}:/config \ + ${RABBITMQ_DOCKER_IMAGE} + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) wait_for_message rabbitmq "Server startup complete" end "RabbitMQ ready" } diff --git a/selenium/bin/components/uaa b/selenium/bin/components/uaa index b344ee0211bd..befc8159b2a0 100644 --- a/selenium/bin/components/uaa +++ b/selenium/bin/components/uaa @@ -16,7 +16,11 @@ init_uaa() { print "> UAA_CONFIG_DIR: ${UAA_CONFIG_DIR}" print "> UAA_URL: ${UAA_URL}" print "> UAA_DOCKER_IMAGE: ${UAA_DOCKER_IMAGE}" +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) generate-ca-server-client-kpi uaa $UAA_CONFIG_DIR generate-server-keystore-if-required uaa $UAA_CONFIG_DIR } @@ -37,12 +41,22 @@ start_uaa() { --detach \ --name uaa \ --net ${DOCKER_NETWORK} \ +<<<<<<< HEAD --publish 8080:8080 \ --mount "type=bind,source=$MOUNT_UAA_CONF_DIR,target=/uaa" \ --env UAA_CONFIG_PATH="/uaa" \ --env JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom" \ ${UAA_DOCKER_IMAGE} +======= + --publish 8443:8443 \ + -v ${MOUNT_UAA_CONF_DIR}:/uaa \ + -v ${UAA_CONFIG_DIR}/server.xml:/layers/paketo-buildpacks_apache-tomcat/catalina-base/conf/server.xml \ + --env UAA_CONFIG_PATH="/uaa" \ + --env JAVA_OPTS="-Djava.security.policy=unlimited -Djava.security.egd=file:/dev/./urandom" \ + ${UAA_DOCKER_IMAGE} + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) wait_for_oidc_endpoint uaa $UAA_URL end "UAA is ready" } diff --git a/selenium/bin/gen-env-file b/selenium/bin/gen-env-file index 60c4b4bfc50d..66b9ece5e8ba 100755 --- a/selenium/bin/gen-env-file +++ b/selenium/bin/gen-env-file @@ -1,7 +1,14 @@ #!/usr/bin/env bash SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +<<<<<<< HEAD #set -x +======= +if [[ ! -z "${DEBUG}" ]]; then + set -x +fi + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ENV_FILE="/tmp/rabbitmq/.env" FIND_PATH=$1 @@ -13,6 +20,11 @@ generate_env_file() { mkdir -p $parentdir echo "#!/usr/bin/env bash" > $ENV_FILE echo "set -u" >> $ENV_FILE +<<<<<<< HEAD +======= + echo "export SELENIUM=${SCRIPT}/.." >> $ENV_FILE + echo "export TEST_CONFIG_PATH=${FIND_PATH}" >> $ENV_FILE +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) declare -a FILE_ARRAY for f in $($SCRIPT/find-template-files $FIND_PATH "env") diff --git a/selenium/bin/suite_template b/selenium/bin/suite_template index 4b8427994dfe..4373c2e543cb 100644 --- a/selenium/bin/suite_template +++ b/selenium/bin/suite_template @@ -30,9 +30,15 @@ find_selenium_dir() { SELENIUM_ROOT_FOLDER=$(find_selenium_dir $SCRIPT) TEST_DIR=$SELENIUM_ROOT_FOLDER/test BIN_DIR=$SELENIUM_ROOT_FOLDER/bin +<<<<<<< HEAD LOGS=${SELENIUM_ROOT_FOLDER}/logs/${SUITE} SCREENS=${SELENIUM_ROOT_FOLDER}/screens/${SUITE} CONF_DIR=/tmp/selenium/${SUITE} +======= +SCREENS=${SELENIUM_ROOT_FOLDER}/screens/${SUITE} +CONF_DIR=/tmp/selenium/${SUITE} +LOGS=${CONF_DIR}/logs +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ENV_FILE=$CONF_DIR/.env rm -rf $CONF_DIR @@ -132,7 +138,11 @@ build_mocha_image() { tag=($(md5sum $SELENIUM_ROOT_FOLDER/package.json)) print "> tag : $tag" if [[ $(docker images -q mocha-test:$tag 2> /dev/null) == "" ]]; then +<<<<<<< HEAD docker build -t mocha-test:$tag --target test $SCRIPT/.. +======= + docker build -t mocha-test:$tag --target test $SELENIUM_ROOT_FOLDER +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) print "> Built docker image mocha-test:$tag" fi end "mocha-test image exists" @@ -170,13 +180,21 @@ wait_for_oidc_endpoint() { wait_for_oidc_endpoint_local() { NAME=$1 BASE_URL=$2 +<<<<<<< HEAD CURL_ARGS="-L --fail " +======= + CURL_ARGS="-k --tlsv1.2 -L --fail " +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) DELAY_BETWEEN_ATTEMPTS=5 if [[ $# -eq 3 ]]; then CURL_ARGS="$CURL_ARGS --cacert $3" DELAY_BETWEEN_ATTEMPTS=10 fi +<<<<<<< HEAD max_retry=10 +======= + max_retry=15 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) counter=0 print "Waiting for OIDC discovery endpoint $NAME ... (BASE_URL: $BASE_URL)" until (curl $CURL_ARGS ${BASE_URL}/.well-known/openid-configuration >/dev/null 2>&1) @@ -191,7 +209,11 @@ wait_for_oidc_endpoint_local() { wait_for_oidc_endpoint_docker() { NAME=$1 BASE_URL=$2 +<<<<<<< HEAD CURL_ARGS="-L --fail " +======= + CURL_ARGS="-k --tlsv1.2 -L --fail " +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) DOCKER_ARGS="--rm --net ${DOCKER_NETWORK} " DELAY_BETWEEN_ATTEMPTS=5 if [[ $# -gt 2 ]]; then @@ -199,7 +221,11 @@ wait_for_oidc_endpoint_docker() { CURL_ARGS="$CURL_ARGS --cacert /tmp/ca_certificate.pem" DELAY_BETWEEN_ATTEMPTS=10 fi +<<<<<<< HEAD max_retry=10 +======= + max_retry=15 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) counter=0 print "Waiting for OIDC discovery endpoint $NAME ... (BASE_URL: $BASE_URL)" until (docker run $DOCKER_ARGS curlimages/curl:7.85.0 $CURL_ARGS ${BASE_URL}/.well-known/openid-configuration >/dev/null 2>&1) @@ -333,9 +359,17 @@ _test() { --env SELENIUM_POLLING=${SELENIUM_POLLING} \ --env PROFILES="${PROFILES}" \ --env ENV_FILE="/code/.env" \ +<<<<<<< HEAD + --env NODE_EXTRA_CA_CERTS=/nodejs/ca.pem \ + -v ${MOUNT_NODE_EXTRA_CA_CERTS}:/nodejs/ca.pem \ + -v ${TEST_DIR}:/code/test \ +======= + --env RABBITMQ_CERTS=/etc/rabbitmq/certs \ --env NODE_EXTRA_CA_CERTS=/nodejs/ca.pem \ -v ${MOUNT_NODE_EXTRA_CA_CERTS}:/nodejs/ca.pem \ -v ${TEST_DIR}:/code/test \ + -v ${TEST_CONFIG_DIR}/certs:/etc/rabbitmq/certs \ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -v ${SCREENS}:/screens \ -v ${ENV_FILE}:/code/.env \ mocha-test:${mocha_test_tag} test /code/test${TEST_CASES_PATH} @@ -371,8 +405,13 @@ profiles_with_local_or_docker() { generate_env_file() { begin "Generating env file ..." mkdir -p $CONF_DIR +<<<<<<< HEAD ${BIN_DIR}/gen-env-file $TEST_CONFIG_DIR $ENV_FILE source $ENV_FILE +======= + ${BIN_DIR}/gen-env-file $TEST_CONFIG_DIR $ENV_FILE + source $ENV_FILE +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end "Finished generating env file." } generate-ca-server-client-kpi() { @@ -468,6 +507,10 @@ generate-client-keystore-if-required() { -noprompt fi } +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) run() { runWith rabbitmq } @@ -582,7 +625,11 @@ test_local() { export RABBITMQ_AMQP_PASSWORD=${RABBITMQ_AMQP_PASSWORD} export SELENIUM_TIMEOUT=${SELENIUM_TIMEOUT:-20000} export SELENIUM_POLLING=${SELENIUM_POLLING:-500} +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) print "> SELENIUM_TIMEOUT: ${SELENIUM_TIMEOUT}" print "> SELENIUM_POLLING: ${SELENIUM_POLLING}" print "> RABBITMQ_HOST: ${RABBITMQ_HOST}" diff --git a/selenium/fakeportal/app.js b/selenium/fakeportal/app.js index ea0ff1a37021..51711bfbc1a8 100644 --- a/selenium/fakeportal/app.js +++ b/selenium/fakeportal/app.js @@ -56,8 +56,15 @@ function access_token(id, secret) { if (req.status == 200) { const token = JSON.parse(req.responseText).access_token; console.log("Token => " + token) +<<<<<<< HEAD return token; } else { throw new Error(req.status + " : " + req.responseText); +======= + return token + } else { + throw new Error(req.status + " : " + " : " + + req.response + " : " + req.responseText) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } } diff --git a/selenium/full-suite-authnz-messaging b/selenium/full-suite-authnz-messaging index 5eec8081fa62..2472eade6cf4 100644 --- a/selenium/full-suite-authnz-messaging +++ b/selenium/full-suite-authnz-messaging @@ -4,6 +4,10 @@ authnz-messaging/auth-http-backend.sh authnz-messaging/auth-http-internal-backends-with-internal.sh authnz-messaging/auth-http-internal-backends.sh authnz-messaging/auth-internal-backend.sh +<<<<<<< HEAD +======= +authnz-messaging/auth-internal-mtls-backend.sh +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) authnz-messaging/auth-internal-http-backends.sh authnz-messaging/auth-ldap-backend.sh authnz-messaging/auth-http-backend.sh diff --git a/selenium/full-suite-management-ui b/selenium/full-suite-management-ui index dc58a67e0edf..b4a379e8257c 100644 --- a/selenium/full-suite-management-ui +++ b/selenium/full-suite-management-ui @@ -1,10 +1,17 @@ authnz-mgt/basic-auth-behind-proxy.sh authnz-mgt/basic-auth.sh authnz-mgt/basic-auth-with-mgt-prefix.sh +<<<<<<< HEAD authnz-mgt/multi-oauth-with-basic-auth.sh authnz-mgt/multi-oauth-without-basic-auth-and-resource-label-and-scopes.sh authnz-mgt/multi-oauth-without-basic-auth.sh authnz-mgt/multi-oauth-with-basic-auth-when-idps-down.sh +======= +authnz-mgt/multi-oauth-with-basic-auth-when-idps-down.sh +authnz-mgt/multi-oauth-with-basic-auth.sh +authnz-mgt/multi-oauth-without-basic-auth-and-resource-label-and-scopes.sh +authnz-mgt/multi-oauth-without-basic-auth.sh +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) authnz-mgt/oauth-and-basic-auth.sh authnz-mgt/oauth-idp-initiated-with-uaa-and-prefix-via-proxy.sh authnz-mgt/oauth-idp-initiated-with-uaa-and-prefix.sh diff --git a/selenium/package.json b/selenium/package.json index 7bab8c6add7d..171faeea49e9 100644 --- a/selenium/package.json +++ b/selenium/package.json @@ -12,7 +12,11 @@ "author": "", "license": "ISC", "dependencies": { +<<<<<<< HEAD "chromedriver": "^128.0.0", +======= + "chromedriver": "^130.0.4", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "ejs": "^3.1.8", "express": "^4.18.2", "geckodriver": "^3.0.2", @@ -21,7 +25,11 @@ "path": "^0.12.7", "proxy": "^1.0.2", "rhea": "^3.0.3", +<<<<<<< HEAD "selenium-webdriver": "^4.19.0", +======= + "selenium-webdriver": "^4.26.0", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "xmlhttprequest": "^1.8.0" }, "devDependencies": { diff --git a/selenium/run-suites.sh b/selenium/run-suites.sh index 70f7e5685a45..da11b680c612 100755 --- a/selenium/run-suites.sh +++ b/selenium/run-suites.sh @@ -30,7 +30,11 @@ do fi echo -e "=== $TEST_STATUS $SUITE ===========================================" echo " " +<<<<<<< HEAD done <<< "$(cat $SCRIPT/$SUITE_FILE)" +======= +done <<< "$(cat $SCRIPT/$SUITE_FILE | sort)" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) echo -e "=== Summary (${TOTAL_SUITES}/${GREEN}${#SUCCESSFUL_SUITES[@]}/${RED}${#FAILED_SUITES[@]}${NC}) ============================================" if [ ${#SUCCESSFUL_SUITES[@]} -gt 0 ]; then echo -e " > ${GREEN}Successful suites ${NC}"; fi diff --git a/selenium/short-suite-management-ui b/selenium/short-suite-management-ui index dd0c79f0f889..a2c307b2b0a2 100644 --- a/selenium/short-suite-management-ui +++ b/selenium/short-suite-management-ui @@ -1,5 +1,13 @@ authnz-mgt/basic-auth.sh authnz-mgt/oauth-with-keycloak.sh +<<<<<<< HEAD mgt/vhosts.sh mgt/exchanges.sh mgt/limits.sh +======= +authnz-mgt/oauth-with-uaa.sh +mgt/vhosts.sh +mgt/exchanges.sh +mgt/limits.sh +mgt/amqp10-connections.sh +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/suites/authnz-messaging/auth-internal-backend.sh b/selenium/suites/authnz-messaging/auth-internal-backend.sh index a3f49c7ecf96..98fc5a074cea 100755 --- a/selenium/suites/authnz-messaging/auth-internal-backend.sh +++ b/selenium/suites/authnz-messaging/auth-internal-backend.sh @@ -3,7 +3,11 @@ SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TEST_CASES_PATH=/authnz-msg-protocols +<<<<<<< HEAD PROFILES="internal-user auth_backends-internal " +======= +PROFILES="internal-user auth_backends-internal" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) source $SCRIPT/../../bin/suite_template run diff --git a/selenium/suites/authnz-messaging/auth-internal-mtls-backend.sh b/selenium/suites/authnz-messaging/auth-internal-mtls-backend.sh new file mode 100755 index 000000000000..df92f9d9cd43 --- /dev/null +++ b/selenium/suites/authnz-messaging/auth-internal-mtls-backend.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +TEST_CASES_PATH=/authnz-msg-protocols +PROFILES="internal-user auth_backends-internal tls auth-mtls" + +source $SCRIPT/../../bin/suite_template +run diff --git a/selenium/suites/authnz-mgt/oauth-with-uaa.sh b/selenium/suites/authnz-mgt/oauth-with-uaa.sh index 2e382ab2c5f2..9d6bca4f0bd2 100755 --- a/selenium/suites/authnz-mgt/oauth-with-uaa.sh +++ b/selenium/suites/authnz-mgt/oauth-with-uaa.sh @@ -4,7 +4,11 @@ SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" TEST_CASES_PATH=/oauth/with-sp-initiated TEST_CONFIG_PATH=/oauth +<<<<<<< HEAD PROFILES="uaa uaa-oauth-provider uaa-mgt-oauth-provider" +======= +PROFILES="uaa uaa-oauth-provider uaa-mgt-oauth-provider tls" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) source $SCRIPT/../../bin/suite_template $@ runWith uaa diff --git a/selenium/suites/mgt/amqp10-connections.sh b/selenium/suites/mgt/amqp10-connections.sh new file mode 100755 index 000000000000..91be8686f385 --- /dev/null +++ b/selenium/suites/mgt/amqp10-connections.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +SCRIPT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +TEST_CASES_PATH=/connections/amqp10 +TEST_CONFIG_PATH=/basic-auth + +source $SCRIPT/../../bin/suite_template $@ +run diff --git a/selenium/test/amqp.js b/selenium/test/amqp.js index fe21cfdc8f87..4ce0353e79ae 100644 --- a/selenium/test/amqp.js +++ b/selenium/test/amqp.js @@ -1,6 +1,10 @@ var container = require('rhea') // https://github.com/amqp/rhea var fs = require('fs'); var path = require('path'); +<<<<<<< HEAD +======= +var connectionOptions = getConnectionOptions() +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) function getAmqpConnectionOptions() { return { @@ -19,6 +23,7 @@ function getAmqpsConnectionOptions() { options['enable_sasl_external'] = true } options['transport'] = 'tls' +<<<<<<< HEAD let certsLocation = getEnv("RABBITMQ_CERTS"); options['key'] = fs.readFileSync(path.resolve(certsLocation,'client_rabbitmq_key.pem')) options['cert'] = fs.readFileSync(path.resolve(certsLocation,'client_rabbitmq_certificate.pem')) @@ -29,6 +34,20 @@ function getConnectionOptions() { case 'amqp': return getAmqpConnectionOptions() case 'amqps': +======= + let certsLocation = process.env.RABBITMQ_CERTS + options['key'] = fs.readFileSync(path.resolve(certsLocation,'client_rabbitmq_key.pem')) + options['cert'] = fs.readFileSync(path.resolve(certsLocation,'client_rabbitmq_certificate.pem')) + options['ca'] = fs.readFileSync(path.resolve(certsLocation,'ca_rabbitmq_certificate.pem')) + return options +} +function getConnectionOptions() { + let scheme = process.env.RABBITMQ_AMQP_SCHEME || 'amqp' + switch(scheme){ + case "amqp": + return getAmqpConnectionOptions() + case "amqps": +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) return getAmqpsConnectionOptions() } } @@ -40,7 +59,11 @@ module.exports = { resolve() }) }) +<<<<<<< HEAD let connection = container.connect(getConnectionOptions()) +======= + let connection = container.connect(connectionOptions) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) let receiver = connection.open_receiver({ source: 'my-queue', target: 'receiver-target', diff --git a/selenium/test/authnz-msg-protocols/amqp10.js b/selenium/test/authnz-msg-protocols/amqp10.js index a7cda69e0851..038fbc0ab2df 100644 --- a/selenium/test/authnz-msg-protocols/amqp10.js +++ b/selenium/test/authnz-msg-protocols/amqp10.js @@ -10,7 +10,13 @@ var untilConnectionEstablished = new Promise((resolve, reject) => { }) }) +<<<<<<< HEAD +======= +onAmqp('message', function (context) { + receivedAmqpMessageCount++ +}) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) onceAmqp('sendable', function (context) { context.sender.send({body:'first message'}) }) @@ -50,6 +56,7 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' + }) it('can open an AMQP 1.0 connection', async function () { +<<<<<<< HEAD var untilFirstMessageReceived = new Promise((resolve, reject) => { onAmqp('message', function(context) { resolve() @@ -59,12 +66,22 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' + await untilConnectionEstablished await untilFirstMessageReceived var untilSecondMessageReceived = new Promise((resolve, reject) => { +======= + amqp = openAmqp() + await untilConnectionEstablished + var untilMessageReceived = new Promise((resolve, reject) => { +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) onAmqp('message', function(context) { resolve() }) }) amqp.sender.send({body:'second message'}) +<<<<<<< HEAD await untilSecondMessageReceived +======= + await untilMessageReceived + assert.equal(2, receivedAmqpMessageCount) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }) after(function () { @@ -76,7 +93,12 @@ describe('Having AMQP 1.0 protocol enabled and the following auth_backends: ' + closeAmqp(amqp.connection) } } catch (error) { +<<<<<<< HEAD console.error("Failed to close amqp10 connection due to " + error); } +======= + console.error("Failed to close amqp10 connection due to " + error); + } +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }) }) diff --git a/selenium/test/authnz-msg-protocols/env.auth-mtls b/selenium/test/authnz-msg-protocols/env.auth-mtls new file mode 100644 index 000000000000..d00282f8e180 --- /dev/null +++ b/selenium/test/authnz-msg-protocols/env.auth-mtls @@ -0,0 +1,2 @@ +export MQTT_USE_MTLS=true +export AMQP_USE_MTLS=true diff --git a/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.docker b/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.docker index cd05083899e2..0f46dbf181a5 100644 --- a/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.docker +++ b/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.docker @@ -1,2 +1,6 @@ export OAUTH_PROVIDER_URL=https://devkeycloak:8442/realms/dev +<<<<<<< HEAD export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/devkeycloak/ca_devkeycloak_certificate.pem +======= +export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/devkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.local b/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.local index cd05083899e2..0f46dbf181a5 100644 --- a/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.local +++ b/selenium/test/authnz-msg-protocols/env.auth-oauth-dev.local @@ -1,2 +1,6 @@ export OAUTH_PROVIDER_URL=https://devkeycloak:8442/realms/dev +<<<<<<< HEAD export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/devkeycloak/ca_devkeycloak_certificate.pem +======= +export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/devkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.docker b/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.docker index 935fd0dea6f2..e24c2d306145 100644 --- a/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.docker +++ b/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.docker @@ -1,2 +1,6 @@ export OAUTH_PROVIDER_URL=https://prodkeycloak:8442/realms/prod +<<<<<<< HEAD export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/prodkeycloak/ca_prodkeycloak_certificate.pem +======= +export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/prodkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.local b/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.local index 935fd0dea6f2..e24c2d306145 100644 --- a/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.local +++ b/selenium/test/authnz-msg-protocols/env.auth-oauth-prod.local @@ -1,2 +1,6 @@ export OAUTH_PROVIDER_URL=https://prodkeycloak:8442/realms/prod +<<<<<<< HEAD export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/prodkeycloak/ca_prodkeycloak_certificate.pem +======= +export OAUTH_NODE_EXTRA_CA_CERTS=multi-oauth/prodkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.docker.devkeycloak b/selenium/test/authnz-msg-protocols/env.docker.devkeycloak index 8f4ac30830db..842d4ad50130 100644 --- a/selenium/test/authnz-msg-protocols/env.docker.devkeycloak +++ b/selenium/test/authnz-msg-protocols/env.docker.devkeycloak @@ -1,2 +1,6 @@ export DEVKEYCLOAK_URL=https://devkeycloak:8442/realms/dev +<<<<<<< HEAD export DEVKEYCLOAK_CA_CERT=/config/oauth/keycloak/ca_devkeycloak_certificate.pem +======= +export DEVKEYCLOAK_CA_CERT=/config/oauth/keycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.docker.prodkeycloak b/selenium/test/authnz-msg-protocols/env.docker.prodkeycloak index 82a1215b373c..0252c5421e9d 100644 --- a/selenium/test/authnz-msg-protocols/env.docker.prodkeycloak +++ b/selenium/test/authnz-msg-protocols/env.docker.prodkeycloak @@ -1,2 +1,6 @@ export PRODKEYCLOAK_URL=https://prodkeycloak:8443/realms/prod +<<<<<<< HEAD export PRODKEYCLOAK_CA_CERT=/config/oauth/keycloak/ca_prodkeycloak_certificate.pem +======= +export PRODKEYCLOAK_CA_CERT=/config/oauth/keycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.local.devkeycloak b/selenium/test/authnz-msg-protocols/env.local.devkeycloak index 23978f5c87e5..8b081bd49e31 100644 --- a/selenium/test/authnz-msg-protocols/env.local.devkeycloak +++ b/selenium/test/authnz-msg-protocols/env.local.devkeycloak @@ -1,2 +1,6 @@ export DEVKEYCLOAK_URL=https://localhost:8442/realms/dev +<<<<<<< HEAD export DEVKEYCLOAK_CA_CERT=test/multi-oauth/devkeycloak/ca_devkeycloak_certificate.pem +======= +export DEVKEYCLOAK_CA_CERT=test/multi-oauth/devkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.local.prodkeycloak b/selenium/test/authnz-msg-protocols/env.local.prodkeycloak index 3b98ef1a4c3c..9b892ffabc42 100644 --- a/selenium/test/authnz-msg-protocols/env.local.prodkeycloak +++ b/selenium/test/authnz-msg-protocols/env.local.prodkeycloak @@ -1,2 +1,6 @@ export PRODKEYCLOAK_URL=https://localhost:8443/realms/prod +<<<<<<< HEAD export PRODKEYCLOAK_CA_CERT=test/multi-oauth/prodkeycloak/ca_prodkeycloak_certificate.pem +======= +export PRODKEYCLOAK_CA_CERT=test/multi-oauth/prodkeycloak/ca_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/authnz-msg-protocols/env.tls b/selenium/test/authnz-msg-protocols/env.tls new file mode 100644 index 000000000000..73854e5666ea --- /dev/null +++ b/selenium/test/authnz-msg-protocols/env.tls @@ -0,0 +1,2 @@ +export MQTT_PROTOCOL=mqtts +export RABBITMQ_MQTT_URL=mqtts://rabbitmq:8883 diff --git a/selenium/test/authnz-msg-protocols/mqtt.js b/selenium/test/authnz-msg-protocols/mqtt.js index 8a665c871834..e8346748be7f 100644 --- a/selenium/test/authnz-msg-protocols/mqtt.js +++ b/selenium/test/authnz-msg-protocols/mqtt.js @@ -1,3 +1,7 @@ +<<<<<<< HEAD +======= +const fs = require('fs') +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) const assert = require('assert') const { tokenFor, openIdConfiguration } = require('../utils') const { reset, expectUser, expectVhost, expectResource, allow, verifyAll } = require('../mock_http_backend') @@ -14,11 +18,22 @@ for (const element of profiles.split(" ")) { describe('Having MQTT protocol enbled and the following auth_backends: ' + backends, function () { let mqttOptions let expectations = [] +<<<<<<< HEAD let client_id = 'selenium-client' let rabbit = process.env.RABBITMQ_HOSTNAME || 'localhost' let username = process.env.RABBITMQ_AMQP_USERNAME let password = process.env.RABBITMQ_AMQP_PASSWORD +======= + let mqttProtocol = process.env.MQTT_PROTOCOL || 'mqtt' + let usemtls = process.env.MQTT_USE_MTLS || false + let rabbit = process.env.RABBITMQ_HOSTNAME || 'localhost' + let mqttUrl = process.env.RABBITMQ_MQTT_URL || "mqtt://" + rabbit + ":1883" + let username = process.env.RABBITMQ_AMQP_USERNAME + let password = process.env.RABBITMQ_AMQP_PASSWORD + let client_id = process.env.RABBITMQ_AMQP_USERNAME || 'selenium-client' + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) before(function () { if (backends.includes("http") && username.includes("http")) { reset() @@ -36,17 +51,39 @@ describe('Having MQTT protocol enbled and the following auth_backends: ' + backe mqttOptions = { clientId: client_id, protocolId: 'MQTT', +<<<<<<< HEAD protocolVersion: 4, keepalive: 10000, clean: false, reconnectPeriod: '1000', username: username, password: password, +======= + protocol: mqttProtocol, + protocolVersion: 4, + keepalive: 10000, + clean: false, + reconnectPeriod: '1000' + } + if (mqttProtocol == 'mqtts') { + mqttOptions["ca"] = [fs.readFileSync(process.env.RABBITMQ_CERTS + "/ca_rabbitmq_certificate.pem")] + } + if (usemtls) { + mqttOptions["cert"] = fs.readFileSync(process.env.RABBITMQ_CERTS + "/client_rabbitmq_certificate.pem") + mqttOptions["key"] = fs.readFileSync(process.env.RABBITMQ_CERTS + "/client_rabbitmq_key.pem") + } else { + mqttOptions["username"] = username + mqttOptions["password"] = password +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } }) it('can open an MQTT connection', function () { +<<<<<<< HEAD var client = mqtt.connect("mqtt://" + rabbit + ":1883", mqttOptions) +======= + var client = mqtt.connect(mqttUrl, mqttOptions) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) client.on('error', function(err) { assert.fail("Mqtt connection failed due to " + err) client.end() diff --git a/selenium/test/authnz-msg-protocols/rabbitmq.auth-mtls.conf b/selenium/test/authnz-msg-protocols/rabbitmq.auth-mtls.conf new file mode 100644 index 000000000000..9f40857d94fb --- /dev/null +++ b/selenium/test/authnz-msg-protocols/rabbitmq.auth-mtls.conf @@ -0,0 +1,13 @@ + +auth_mechanisms.1 = EXTERNAL + +ssl_cert_login_from = subject_alternative_name +ssl_cert_login_san_type = dns +ssl_cert_login_san_index = 1 +ssl_options.verify = verify_peer +ssl_options.fail_if_no_peer_cert = true + +mqtt.ssl_cert_login = true +mqtt.ssl_cert_client_id_from = subject_alternative_name +mqtt.ssl_cert_login_san_type = dns +mqtt.ssl_cert_login_san_index = 1 diff --git a/selenium/test/authnz-msg-protocols/rabbitmq.tls.conf b/selenium/test/authnz-msg-protocols/rabbitmq.tls.conf new file mode 100644 index 000000000000..8478c874bf2f --- /dev/null +++ b/selenium/test/authnz-msg-protocols/rabbitmq.tls.conf @@ -0,0 +1,13 @@ + +listeners.ssl.1 = 5671 + +ssl_options.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +ssl_options.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +ssl_options.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem + +management.ssl.port = 15671 +management.ssl.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +management.ssl.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +management.ssl.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem + +mqtt.listeners.ssl.default = 8883 diff --git a/selenium/test/basic-auth/env.local b/selenium/test/basic-auth/env.local index 26cc7522d3b9..64e5cdbd8c2d 100644 --- a/selenium/test/basic-auth/env.local +++ b/selenium/test/basic-auth/env.local @@ -1 +1,5 @@ +<<<<<<< HEAD export IMPORT_DIR=deps/rabbitmq_management/selenium/test/basic-auth/imports +======= +export IMPORT_DIR=selenium/test/basic-auth/imports +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/basic-auth/imports/users.json b/selenium/test/basic-auth/imports/users.json index 83497198e066..a8dfee58f46a 100644 --- a/selenium/test/basic-auth/imports/users.json +++ b/selenium/test/basic-auth/imports/users.json @@ -70,6 +70,7 @@ "read": ".*" }, { +<<<<<<< HEAD "user": "guest", "vhost": "other", "configure": ".*", @@ -77,6 +78,8 @@ "read": ".*" }, { +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "user": "management", "vhost": "/", "configure": ".*", @@ -89,6 +92,23 @@ "configure": ".*", "write": ".*", "read": ".*" +<<<<<<< HEAD +======= + }, + { + "user": "rabbit_no_management", + "vhost": "other", + "configure": ".*", + "write": ".*", + "read": ".*" + }, + { + "user": "monitoring-only", + "vhost": "other", + "configure": ".*", + "write": ".*", + "read": ".*" +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } ] diff --git a/selenium/test/basic-auth/rabbitmq.conf b/selenium/test/basic-auth/rabbitmq.conf index 7bacc14af27a..d7cdcc4a5bf2 100644 --- a/selenium/test/basic-auth/rabbitmq.conf +++ b/selenium/test/basic-auth/rabbitmq.conf @@ -1,6 +1,10 @@ auth_backends.1 = rabbit_auth_backend_internal management.login_session_timeout = 1 +<<<<<<< HEAD load_definitions = ${IMPORT_DIR}/users.json +======= +load_definitions = ${RABBITMQ_TEST_DIR}/imports/users.json +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) loopback_users = none diff --git a/selenium/test/connections/amqp10/sessions-for-monitoring-user.js b/selenium/test/connections/amqp10/sessions-for-monitoring-user.js index 0cd98f6ea08c..7d5a3431198d 100644 --- a/selenium/test/connections/amqp10/sessions-for-monitoring-user.js +++ b/selenium/test/connections/amqp10/sessions-for-monitoring-user.js @@ -58,7 +58,10 @@ describe('Given an amqp10 connection opened, listed and clicked on it', function let sessions = await connectionPage.getSessions() assert.equal(1, sessions.sessions.length) let session = connectionPage.getSessionInfo(sessions.sessions, 0) +<<<<<<< HEAD //console.log("session: " + JSON.stringify(session)) +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert.equal(0, session.channelNumber) assert.equal(1, session.nextIncomingId) assert.equal(0, session.outgoingUnsettledDeliveries) @@ -70,20 +73,33 @@ describe('Given an amqp10 connection opened, listed and clicked on it', function assert.equal(1, sessions.outgoing_links.length) let incomingLink = connectionPage.getIncomingLinkInfo(sessions.incoming_links, 0) +<<<<<<< HEAD //console.log("incomingLink: " + JSON.stringify(incomingLink)) assert.equal(1, incomingLink.handle) assert.equal("sender-link", incomingLink.name) assert.equal("examples", incomingLink.targetAddress) +======= + assert.equal(1, incomingLink.handle) + assert.equal("sender-link", incomingLink.name) + assert.equal("my-queue", incomingLink.targetAddress) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert.equal("mixed", incomingLink.sndSettleMode) assert.equal("0", incomingLink.unconfirmedMessages) assert.equal(1, incomingLink.deliveryCount) let outgoingLink = connectionPage.getOutgoingLinkInfo(sessions.outgoing_links, 0) +<<<<<<< HEAD //console.log("outgoingLink: " + JSON.stringify(outgoingLink)) assert.equal(0, outgoingLink.handle) assert.equal("receiver-link", outgoingLink.name) assert.equal("examples", outgoingLink.sourceAddress) assert.equal("examples", outgoingLink.queueName) +======= + assert.equal(0, outgoingLink.handle) + assert.equal("receiver-link", outgoingLink.name) + assert.equal("my-queue", outgoingLink.sourceAddress) + assert.equal("my-queue", outgoingLink.queueName) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert.equal(false, outgoingLink.sendSettled) assert.equal("unlimited", outgoingLink.maxMessageSize) diff --git a/selenium/test/env.docker b/selenium/test/env.docker index 1d058b9f4e88..41fd84b21b30 100644 --- a/selenium/test/env.docker +++ b/selenium/test/env.docker @@ -2,3 +2,7 @@ export RABBITMQ_SCHEME=http export RABBITMQ_HOSTNAME=rabbitmq export RABBITMQ_HOST=rabbitmq:15672 export IMPORT_DIR=/var/rabbitmq/imports +<<<<<<< HEAD +======= +export RABBITMQ_CERTS=/etc/rabbitmq/certs +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/env.local b/selenium/test/env.local index 8ec9aeac8fac..556800178133 100644 --- a/selenium/test/env.local +++ b/selenium/test/env.local @@ -1,3 +1,8 @@ export RABBITMQ_SCHEME=http export RABBITMQ_HOSTNAME=localhost export RABBITMQ_HOST=localhost:15672 +<<<<<<< HEAD +======= +export RABBITMQ_CERTS=${TEST_CONFIG_PATH}/certs +export IMPORT_DIR=${TEST_CONFIG_PATH}/imports +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/env.tls.docker b/selenium/test/env.tls.docker index e598d14b7439..456e03cbdfec 100644 --- a/selenium/test/env.tls.docker +++ b/selenium/test/env.tls.docker @@ -1,3 +1,8 @@ export RABBITMQ_SCHEME=https export RABBITMQ_HOSTNAME=rabbitmq export RABBITMQ_HOST=rabbitmq:15671 +<<<<<<< HEAD +======= +export RABBITMQ_AMQP_SCHEME=amqps +export RABBITMQ_AMQP_PORT=5671 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/env.tls.local b/selenium/test/env.tls.local index e39b7b520c8a..5f72f307ec7e 100644 --- a/selenium/test/env.tls.local +++ b/selenium/test/env.tls.local @@ -1,3 +1,9 @@ export RABBITMQ_SCHEME=https export RABBITMQ_HOSTNAME=localhost export RABBITMQ_HOST=localhost:15671 +<<<<<<< HEAD +======= +export RABBITMQ_AMQP_SCHEME=amqps +export RABBITMQ_AMQP_PORT=5671 + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/exchanges/management.js b/selenium/test/exchanges/management.js index 3e772ad24d21..6e8e9a922005 100644 --- a/selenium/test/exchanges/management.js +++ b/selenium/test/exchanges/management.js @@ -36,9 +36,15 @@ describe('Exchange management', function () { }) it('list all default exchanges', async function () { +<<<<<<< HEAD actual_table = await exchanges.getExchangesTable(3) console.log("a :" + actual_table) expected_table = [ +======= + let actual_table = await exchanges.getExchangesTable(3) + + let expected_table = [ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ["/", "(AMQP default)", "direct"], ["/", "amq.direct", "direct"], ["/", "amq.fanout", "fanout"], @@ -47,7 +53,11 @@ describe('Exchange management', function () { ["/", "amq.rabbitmq.event", "topic"], ["/", "amq.rabbitmq.trace", "topic"], ["/", "amq.topic", "topic"], +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ["other", "(AMQP default)", "direct"], ["other", "amq.direct", "direct"], ["other", "amq.fanout", "fanout"], @@ -56,6 +66,10 @@ describe('Exchange management', function () { ["other", "amq.rabbitmq.trace", "topic"], ["other", "amq.topic", "topic"] ] +<<<<<<< HEAD +======= + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) console.log("e :" + actual_table) assert.deepEqual(actual_table, expected_table) }) diff --git a/selenium/test/multi-oauth/env.local b/selenium/test/multi-oauth/env.local index d61f528c4e4a..2fe3931a0ac7 100644 --- a/selenium/test/multi-oauth/env.local +++ b/selenium/test/multi-oauth/env.local @@ -1 +1,5 @@ +<<<<<<< HEAD export OAUTH_SERVER_CONFIG_BASEDIR=deps/rabbitmq_management/selenium/test +======= +export OAUTH_SERVER_CONFIG_BASEDIR=${SELENIUM}/test +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/multi-oauth/env.local.devkeycloak b/selenium/test/multi-oauth/env.local.devkeycloak index 92044ba17881..57e9b7bd109b 100644 --- a/selenium/test/multi-oauth/env.local.devkeycloak +++ b/selenium/test/multi-oauth/env.local.devkeycloak @@ -1,2 +1,6 @@ export DEVKEYCLOAK_URL=https://localhost:8442/realms/dev +<<<<<<< HEAD export DEVKEYCLOAK_CA_CERT=deps/rabbitmq_management/selenium/test/multi-oauth/devkeycloak/ca_prodkeycloak_certificate.pem +======= +export DEVKEYCLOAK_CA_CERT=${SELENIUM}/test/multi-oauth/devkeycloak/ca_devkeycloak_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/multi-oauth/env.local.prodkeycloak b/selenium/test/multi-oauth/env.local.prodkeycloak index b8745aa8df98..3fcad9bc31b8 100644 --- a/selenium/test/multi-oauth/env.local.prodkeycloak +++ b/selenium/test/multi-oauth/env.local.prodkeycloak @@ -1,2 +1,6 @@ export PRODKEYCLOAK_URL=https://localhost:8443/realms/prod +<<<<<<< HEAD export PRODKEYCLOAK_CA_CERT=deps/rabbitmq_management/selenium/test/multi-oauth/prodkeycloak/ca_prodkeycloak_certificate.pem +======= +export PRODKEYCLOAK_CA_CERT=${SELENIUM}/test/multi-oauth/prodkeycloak/ca_prodkeycloak_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/multi-oauth/rabbitmq.tls.conf b/selenium/test/multi-oauth/rabbitmq.tls.conf index 7401d4079b1d..0380cfb867d1 100644 --- a/selenium/test/multi-oauth/rabbitmq.tls.conf +++ b/selenium/test/multi-oauth/rabbitmq.tls.conf @@ -2,13 +2,25 @@ auth_backends.1 = rabbit_auth_backend_oauth2 listeners.ssl.1 = 5671 +<<<<<<< HEAD ssl_options.cacertfile = ${RABBITMQ_TEST_DIR}/certs/ca_rabbitmq_certificate.pem ssl_options.certfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_certificate.pem ssl_options.keyfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_key.pem +======= +ssl_options.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +ssl_options.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +ssl_options.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ssl_options.verify = verify_peer ssl_options.fail_if_no_peer_cert = true management.ssl.port = 15671 +<<<<<<< HEAD management.ssl.cacertfile = ${RABBITMQ_TEST_DIR}/certs/ca_rabbitmq_certificate.pem management.ssl.certfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_certificate.pem management.ssl.keyfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_key.pem +======= +management.ssl.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +management.ssl.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +management.ssl.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.docker.fakeportal b/selenium/test/oauth/env.docker.fakeportal index fc6d56f47b3a..d74d3f8f5927 100644 --- a/selenium/test/oauth/env.docker.fakeportal +++ b/selenium/test/oauth/env.docker.fakeportal @@ -1,3 +1,7 @@ export FAKEPORTAL_URL=http://fakeportal:3000 export RABBITMQ_HOST_FOR_FAKEPORTAL=${RABBITMQ_HOST} +<<<<<<< HEAD export UAA_URL_FOR_FAKEPORTAL=http://uaa:8080 +======= +export UAA_URL_FOR_FAKEPORTAL=https://uaa:8443 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.docker.fakeproxy b/selenium/test/oauth/env.docker.fakeproxy index 37d1e5eccd9f..88d842d834dd 100644 --- a/selenium/test/oauth/env.docker.fakeproxy +++ b/selenium/test/oauth/env.docker.fakeproxy @@ -1,4 +1,8 @@ export FAKEPROXY_URL=http://fakeproxy:9090 +<<<<<<< HEAD export UAA_URL_FOR_FAKEPROXY=http://uaa:8080 +======= +export UAA_URL_FOR_FAKEPROXY=https://uaa:8443 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) export RABBITMQ_HOST_FOR_FAKEPROXY=${RABBITMQ_HOST} export PUBLIC_RABBITMQ_HOST=fakeproxy:9090 diff --git a/selenium/test/oauth/env.docker.uaa b/selenium/test/oauth/env.docker.uaa index afc439185290..983b25657e1c 100644 --- a/selenium/test/oauth/env.docker.uaa +++ b/selenium/test/oauth/env.docker.uaa @@ -1 +1,5 @@ +<<<<<<< HEAD export UAA_URL=http://uaa:8080 +======= +export UAA_URL=https://uaa:8443 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.local b/selenium/test/oauth/env.local index 80cfe7430e52..4fb85090a7cb 100644 --- a/selenium/test/oauth/env.local +++ b/selenium/test/oauth/env.local @@ -1 +1,5 @@ +<<<<<<< HEAD export OAUTH_SERVER_CONFIG_BASEDIR=selenium/test +======= +export OAUTH_SERVER_CONFIG_BASEDIR=${SELENIUM}/test +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.local.fakeportal b/selenium/test/oauth/env.local.fakeportal index 520c2ce34c42..f3f84b87bb60 100644 --- a/selenium/test/oauth/env.local.fakeportal +++ b/selenium/test/oauth/env.local.fakeportal @@ -1,3 +1,7 @@ export FAKEPORTAL_URL=http://localhost:3000 export RABBITMQ_HOST_FOR_FAKEPORTAL=localhost:15672 +<<<<<<< HEAD export UAA_URL_FOR_FAKEPORTAL=http://host.docker.internal:8080 +======= +export UAA_URL_FOR_FAKEPORTAL=https://uaa:8443 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.local.keycloak b/selenium/test/oauth/env.local.keycloak index e691790c25d7..6cada004a4bb 100644 --- a/selenium/test/oauth/env.local.keycloak +++ b/selenium/test/oauth/env.local.keycloak @@ -1,3 +1,7 @@ export KEYCLOAK_URL=https://localhost:8443/realms/test export OAUTH_PROVIDER_URL=https://localhost:8443/realms/test +<<<<<<< HEAD export OAUTH_PROVIDER_CA_CERT=deps/rabbitmq_management/selenium/test/oauth/keycloak/ca_keycloak_certificate.pem +======= +export OAUTH_PROVIDER_CA_CERT=selenium/test/oauth/keycloak/ca_keycloak_certificate.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/env.local.uaa b/selenium/test/oauth/env.local.uaa index 40d8bf716099..0014c3e68cc0 100644 --- a/selenium/test/oauth/env.local.uaa +++ b/selenium/test/oauth/env.local.uaa @@ -1 +1,5 @@ +<<<<<<< HEAD export UAA_URL=http://localhost:8080 +======= +export UAA_URL=https://localhost:8443 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/keycloak/openssl.cnf.in b/selenium/test/oauth/keycloak/openssl.cnf.in new file mode 100644 index 000000000000..5ac3282046c5 --- /dev/null +++ b/selenium/test/oauth/keycloak/openssl.cnf.in @@ -0,0 +1,3 @@ +[ client_alt_names ] +email.1 = rabbit_client@localhost +URI.1 = rabbit_client_id_uri diff --git a/selenium/test/oauth/rabbitmq.conf b/selenium/test/oauth/rabbitmq.conf index 02b0227d4bf8..26ffe0058247 100644 --- a/selenium/test/oauth/rabbitmq.conf +++ b/selenium/test/oauth/rabbitmq.conf @@ -10,6 +10,10 @@ auth_oauth2.resource_server_id = rabbitmq auth_oauth2.preferred_username_claims.1 = user_name auth_oauth2.preferred_username_claims.2 = preferred_username auth_oauth2.preferred_username_claims.3 = email +<<<<<<< HEAD +======= +auth_oauth2.preferred_username_claims.4 = sub +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) loopback_users = none diff --git a/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf b/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf index 9e6e55f94073..e722d97407d0 100644 --- a/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf +++ b/selenium/test/oauth/rabbitmq.keycloak-mgt-oauth-provider.conf @@ -1,2 +1,6 @@ # uaa requires a secret in order to renew tokens management.oauth_provider_url = ${KEYCLOAK_URL} +<<<<<<< HEAD +======= +management.oauth_authorization_endpoint_params.resource = rabbitmq +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/rabbitmq.tls.conf b/selenium/test/oauth/rabbitmq.tls.conf index 7401d4079b1d..0380cfb867d1 100644 --- a/selenium/test/oauth/rabbitmq.tls.conf +++ b/selenium/test/oauth/rabbitmq.tls.conf @@ -2,13 +2,25 @@ auth_backends.1 = rabbit_auth_backend_oauth2 listeners.ssl.1 = 5671 +<<<<<<< HEAD ssl_options.cacertfile = ${RABBITMQ_TEST_DIR}/certs/ca_rabbitmq_certificate.pem ssl_options.certfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_certificate.pem ssl_options.keyfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_key.pem +======= +ssl_options.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +ssl_options.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +ssl_options.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ssl_options.verify = verify_peer ssl_options.fail_if_no_peer_cert = true management.ssl.port = 15671 +<<<<<<< HEAD management.ssl.cacertfile = ${RABBITMQ_TEST_DIR}/certs/ca_rabbitmq_certificate.pem management.ssl.certfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_certificate.pem management.ssl.keyfile = ${RABBITMQ_TEST_DIR}/certs/server_rabbitmq_key.pem +======= +management.ssl.cacertfile = ${RABBITMQ_CERTS}/ca_rabbitmq_certificate.pem +management.ssl.certfile = ${RABBITMQ_CERTS}/server_rabbitmq_certificate.pem +management.ssl.keyfile = ${RABBITMQ_CERTS}/server_rabbitmq_key.pem +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/selenium/test/oauth/uaa/server.xml b/selenium/test/oauth/uaa/server.xml new file mode 100644 index 000000000000..f86407ddf87a --- /dev/null +++ b/selenium/test/oauth/uaa/server.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/selenium/test/oauth/uaa/uaa.yml b/selenium/test/oauth/uaa/uaa.yml index 546a78402f2a..3e56532ed1dc 100644 --- a/selenium/test/oauth/uaa/uaa.yml +++ b/selenium/test/oauth/uaa/uaa.yml @@ -1,3 +1,9 @@ +<<<<<<< HEAD +======= +require_https: true +https_port: 8443 + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) logging: config: /uaa/log4j2.properties diff --git a/selenium/test/pageobjects/BasePage.js b/selenium/test/pageobjects/BasePage.js index e4a6da0fb81c..c6b66574a850 100644 --- a/selenium/test/pageobjects/BasePage.js +++ b/selenium/test/pageobjects/BasePage.js @@ -125,9 +125,16 @@ module.exports = class BasePage { } +<<<<<<< HEAD async getTable(locator, firstNColumns) { const table = await this.waitForDisplayed(locator) const rows = await table.findElements(By.css('tbody tr')) +======= + async getTable(tableLocator, firstNColumns, rowClass) { + const table = await this.waitForDisplayed(tableLocator) + const rows = await table.findElements(rowClass == undefined ? + By.css('tbody tr') : By.css('tbody tr.' + rowClass)) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) let table_model = [] for (let row of rows) { let columns = await row.findElements(By.css('td')) diff --git a/selenium/test/pageobjects/ConnectionPage.js b/selenium/test/pageobjects/ConnectionPage.js new file mode 100644 index 000000000000..66e396afbc86 --- /dev/null +++ b/selenium/test/pageobjects/ConnectionPage.js @@ -0,0 +1,65 @@ +const { By, Key, until, Builder } = require('selenium-webdriver') + +const BasePage = require('./BasePage') + + +const OVERVIEW_SECTION = By.css('div#main div.section#connection-overview-section') +const SESSIONS_SECTION = By.css('div#main div.section#connection-sessions-section') +const SESSIONS_TABLE = By.css('div.section#connection-sessions-section table.list#sessions') +const INCOMING_LINKS_TABLE = By.css('div.section#connection-sessions-section table.list#incoming-links') +const OUTCOMING_LINKS_TABLE = By.css('div.section#connection-sessions-section table.list#outgoing-links') +const CONNECTION_NAME = By.css('div#main h2') + + +module.exports = class ConnectionPage extends BasePage { + async isLoaded() { + return this.waitForDisplayed(CONNECTION_NAME) + } + async getName() { + return this.getText(CONNECTION_NAME) + } + async getSessions() { + await this.waitForDisplayed(SESSIONS_SECTION) + return { + sessions : await this.getTable(SESSIONS_TABLE, 100, "session"), + incoming_links : await this.getTable(INCOMING_LINKS_TABLE, 100, "link"), + outgoing_links : await this.getTable(OUTCOMING_LINKS_TABLE, 100, "link") + } + } + getSessionInfo(sessions, index) { + return { + channelNumber: sessions[index][0], + handleMax: sessions[index][1], + nextIncomingId: sessions[index][2], + incomingWindow: sessions[index][3], + nextOutgoingId: sessions[index][4], + remoteIncomingWindow: sessions[index][5], + remoteOutgoingWindow: sessions[index][6], + outgoingUnsettledDeliveries: sessions[index][7] + } + } + getIncomingLinkInfo(links, index) { + return { + handle: links[index][0], + name: links[index][1], + targetAddress: links[index][2], + sndSettleMode: links[index][3], + maxMessageSize: Number(links[index][4]), + deliveryCount: Number(links[index][5]), + linkCredit: Number(links[index][6]), + unconfirmedMessages: Number(links[index][7]) + } + } + getOutgoingLinkInfo(links, index) { + return { + handle: links[index][0], + name: links[index][1], + sourceAddress: links[index][2], + queueName: links[index][3], + sendSettled: links[index][4] == "●" ? true : false, + maxMessageSize: links[index][5], + deliveryCount: Number(links[index][6]), + linkCredit: Number(links[index][7]) + } + } +} diff --git a/selenium/test/pageobjects/ConnectionsPage.js b/selenium/test/pageobjects/ConnectionsPage.js new file mode 100644 index 000000000000..0ef6a0b82c48 --- /dev/null +++ b/selenium/test/pageobjects/ConnectionsPage.js @@ -0,0 +1,25 @@ +const { By, Key, until, Builder } = require('selenium-webdriver') + +const BasePage = require('./BasePage') + + +const PAGING_SECTION = By.css('div#connections-paging-section') +const PAGING_SECTION_HEADER = By.css('div#connections-paging-section h2') + +const TABLE_SECTION = By.css('div#connections-table-section table') + +module.exports = class ConnectionsPage extends BasePage { + async isLoaded () { + return this.waitForDisplayed(PAGING_SECTION) + } + async getPagingSectionHeaderText() { + return this.getText(PAGING_SECTION_HEADER) + } + async getConnectionsTable(firstNColumns) { + return this.getTable(TABLE_SECTION, firstNColumns) + } + async clickOnConnection(index) { + return this.click(By.css( + "div#connections-table-section table tbody tr td:nth-child(" + index + ")")) + } +} diff --git a/selenium/test/pageobjects/OverviewPage.js b/selenium/test/pageobjects/OverviewPage.js index 59eb0758a255..eaa4e96f1ac1 100644 --- a/selenium/test/pageobjects/OverviewPage.js +++ b/selenium/test/pageobjects/OverviewPage.js @@ -26,6 +26,7 @@ module.exports = class OverviewPage extends BasePage { } async downloadBrokerDefinitions(filename) { return this.click(DOWNLOAD_DEFINITIONS_SECTION) +<<<<<<< HEAD /* await this.driver.sleep(1000) @@ -33,5 +34,7 @@ module.exports = class OverviewPage extends BasePage { await this.click(DOWNLOAD_BROKER_FILE) return driver.sleep(5000); */ +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) } }
<%= fmt_sort('Name', 'name') %>Specificities<%= fmt_sort('State', 'state') %> Description
<%= fmt_string(feature_flag.name) %> + <% if (feature_flag.callbacks.includes('enable')) { %> + + This feature flags has a migration function which might take some time and consume resources. + + + <% } %> + <% if (feature_flag.stability == 'experimental') { %> + + This is an experimental feature flag + + + <% } %> + <% if (feature_flag.experiment_level == 'unsupported') { %> + + This experimental feature flag is not yet supported at this stage and an upgrade path is not guaranteed + + + <% } %> + + + checked disabled + <% } %> + <% if (feature_flag.state == 'state_changing') { %> + disabled + <% } %> + onchange='handle_feature_flag(this, "<%= feature_flag.name %>");'/> +

<%= fmt_string(feature_flag.desc) %>

@@ -152,3 +464,62 @@ These flags can be enabled in production deployments after an appropriate amount +<<<<<<< HEAD +======= + + + + +

Enabling an experimental feature flag

+

+ The feature flag is experimental. + This means the functionality behind it is still a work in progress. Here + are a few important things to keep in mind: +

+
    +
  1. +

    + Before enabling it, make sure to try it in a test environment + first before enabling it in production. +

    +

    + The feature flag is supported even though it is still experimental. + Therefore, upgrades to a later version of RabbitMQ with this feature flag + enabled are supported. +

    +

    + + +

    +
  2. +
  3. +

    + This development of this feature is at an early stage. Support is not + provided and enabling it in production is not recommended. +

    +

    + Once it is enabled, upgrades to a future version of RabbitMQ is not + guaranteed! If there is no upgrade path, you will have to use a + blue-green migration + to upgrade RabbitMQ. +

    +

    + + +

    +

    + + +

    +
  4. +
  5. + If you enable it, + please give feedback, + this will help the RabbitMQ team polish it and make it stable as soon as + possible. +
  6. +
+ + +
+>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/priv/www/js/tmpl/sessions-list.ejs b/deps/rabbitmq_management/priv/www/js/tmpl/sessions-list.ejs new file mode 100644 index 000000000000..1bd9558cdac4 --- /dev/null +++ b/deps/rabbitmq_management/priv/www/js/tmpl/sessions-list.ejs @@ -0,0 +1,112 @@ +<% if (sessions.length > 0) { %> + + + + + + + + + + + + + + + +<% + for (var i = 0; i < sessions.length; i++) { + var session = sessions[i]; +%> + + + + + + + + + + +<% if (session.incoming_links.length > 0) { %> + + + +<% } %> +<% if (session.outgoing_links.length > 0) { %> + + + +<% } %> +<% } %> + +
Channel numberhandle-maxnext-incoming-idincoming-windownext-outgoing-idremote-incoming-windowremote-outgoing-windowOutgoing unsettled deliveries
<%= fmt_string(session.channel_number) %><%= fmt_string(session.handle_max) %><%= fmt_string(session.next_incoming_id) %><%= fmt_string(session.incoming_window) %><%= fmt_string(session.next_outgoing_id) %><%= fmt_string(session.remote_incoming_window) %><%= fmt_string(session.remote_outgoing_window) %><%= fmt_string(session.outgoing_unsettled_deliveries) %>
+

Incoming Links (<%=(session.incoming_links.length)%>)

+ + + + + + + + + + + + + + +<% + for (var j = 0; j < session.incoming_links.length; j++) { + var in_link = session.incoming_links[j]; +%> + + + + + + + + + + +<% } %> + + +
+

Outgoing Links (<%=(session.outgoing_links.length)%>)

+ + + + + + + + + + + + + + +<% + for (var k = 0; k < session.outgoing_links.length; k++) { + var out_link = session.outgoing_links[k]; +%> + + + + + + + + + + +<% } %> + + +
+<% } else { %> +

No sessions

+<% } %> diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl index c0bf84ce23b7..61eca6f3d796 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_dispatcher.erl @@ -132,6 +132,10 @@ dispatcher() -> {"/connections/:connection", rabbit_mgmt_wm_connection, []}, {"/connections/username/:username", rabbit_mgmt_wm_connection_user_name, []}, {"/connections/:connection/channels", rabbit_mgmt_wm_connection_channels, []}, +<<<<<<< HEAD +======= + {"/connections/:connection/sessions", rabbit_mgmt_wm_connection_sessions, []}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {"/channels", rabbit_mgmt_wm_channels, []}, {"/channels/:channel", rabbit_mgmt_wm_channel, []}, {"/consumers", rabbit_mgmt_wm_consumers, []}, diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl new file mode 100644 index 000000000000..19e973a47748 --- /dev/null +++ b/deps/rabbitmq_management/src/rabbit_mgmt_schema.erl @@ -0,0 +1,67 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_mgmt_schema). + + +-export([ + translate_oauth_resource_servers/1, + translate_endpoint_params/2 +]). + +extract_key_as_binary({Name,_}) -> list_to_binary(Name). +extract_value({_Name,V}) -> V. + +-spec translate_oauth_resource_servers([{list(), binary()}]) -> map(). +translate_oauth_resource_servers(Conf) -> + Settings = cuttlefish_variable:filter_by_prefix( + "management.oauth_resource_servers", Conf), + Map = merge_list_of_maps([ + extract_resource_server_properties(Settings), + extract_resource_server_endpoint_params(oauth_authorization_endpoint_params, Settings), + extract_resource_server_endpoint_params(oauth_token_endpoint_params, Settings) + ]), + Map0 = maps:map(fun(K,V) -> + case proplists:get_value(id, V) of + undefined -> V ++ [{id, K}]; + _ -> V + end end, Map), + ResourceServers = maps:values(Map0), + lists:foldl(fun(Elem,AccMap)-> maps:put(proplists:get_value(id, Elem), Elem, AccMap) end, #{}, + ResourceServers). + +-spec translate_endpoint_params(list(), [{list(), binary()}]) -> [{binary(), binary()}]. +translate_endpoint_params(Variable, Conf) -> + Params0 = cuttlefish_variable:filter_by_prefix("management." ++ Variable, Conf), + [{list_to_binary(Param), list_to_binary(V)} || {["management", _, Param], V} <- Params0]. + +merge_list_of_maps(ListOfMaps) -> + lists:foldl(fun(Elem, AccIn) -> maps:merge_with(fun(_K,V1,V2) -> V1 ++ V2 end, + Elem, AccIn) end, #{}, ListOfMaps). + +convert_list_to_binary(V) when is_list(V) -> + list_to_binary(V); +convert_list_to_binary(V) -> + V. + +extract_resource_server_properties(Settings) -> + KeyFun = fun extract_key_as_binary/1, + ValueFun = fun extract_value/1, + + OAuthResourceServers = [{Name, {list_to_atom(Key), convert_list_to_binary(V)}} + || {["management","oauth_resource_servers", Name, Key], V} <- Settings ], + maps:groups_from_list(KeyFun, ValueFun, OAuthResourceServers). + + +extract_resource_server_endpoint_params(Variable, Settings) -> + KeyFun = fun extract_key_as_binary/1, + + IndexedParams = [{Name, {list_to_binary(ParamName), list_to_binary(V)}} || + {["management","oauth_resource_servers", Name, EndpointVar, ParamName], V} + <- Settings, EndpointVar == atom_to_list(Variable) ], + maps:map(fun(_K,V)-> [{Variable, V}] end, + maps:groups_from_list(KeyFun, fun({_, V}) -> V end, IndexedParams)). diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl index cc3f0b3f486f..b963a36a5aa3 100644 --- a/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_auth.erl @@ -23,6 +23,7 @@ variances(Req, Context) -> {[<<"accept-encoding">>, <<"origin">>], Req, Context}. content_types_provided(ReqData, Context) -> +<<<<<<< HEAD {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}. merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, ManagementProps) -> @@ -141,6 +142,203 @@ filter_empty_properties(ListOfProperties) -> end end, ListOfProperties). +======= + {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}. + +merge_property(Key, List, MapIn) -> + case proplists:get_value(Key, List) of + undefined -> MapIn; + V0 -> MapIn#{Key => V0} + end. + +extract_oauth_provider_info_props_as_map(ManagementProps) -> + lists:foldl(fun(K, Acc) -> + merge_property(K, ManagementProps, Acc) end, #{}, + [oauth_provider_url, + oauth_metadata_url, + oauth_authorization_endpoint_params, + oauth_token_endpoint_params]). + +merge_oauth_provider_info(OAuthResourceServer, MgtResourceServer, + ManagementProps) -> + OAuthProviderResult = + case proplists:get_value(oauth_provider_id, OAuthResourceServer) of + undefined -> + oauth2_client:get_oauth_provider([issuer]); + OauthProviderId -> + oauth2_client:get_oauth_provider(OauthProviderId, [issuer]) + end, + OAuthProviderInfo0 = + case OAuthProviderResult of + {ok, OAuthProvider} -> oauth_provider_to_map(OAuthProvider); + {error, _} -> #{} + end, + OAuthProviderInfo1 = maps:merge(OAuthProviderInfo0, + extract_oauth_provider_info_props_as_map(ManagementProps)), + maps:merge(OAuthProviderInfo1, proplists:to_map(MgtResourceServer)). + +oauth_provider_to_map(OAuthProvider) -> + % only include issuer and end_session_endpoint for now. + % The other endpoints are resolved by oidc-client library + Map0 = case OAuthProvider#oauth_provider.issuer of + undefined -> + #{}; + Issuer -> + #{ + oauth_provider_url => Issuer, + oauth_metadata_url => + OAuthProvider#oauth_provider.discovery_endpoint + } + end, + case OAuthProvider#oauth_provider.end_session_endpoint of + undefined -> Map0; + V -> maps:put(end_session_endpoint, V, Map0) + end. + +skip_unknown_mgt_resource_servers(ManagementProps, OAuth2Resources) -> + maps:filter(fun(Key, _Value) -> maps:is_key(Key, OAuth2Resources) end, + proplists:get_value(oauth_resource_servers, ManagementProps, #{})). +skip_disabled_mgt_resource_servers(MgtOauthResources) -> + maps:filter(fun(_Key, Value) -> + not proplists:get_value(disabled, Value, false) end, + MgtOauthResources). + +extract_oauth2_and_mgt_resources(OAuth2BackendProps, ManagementProps) -> + OAuth2Resources = getAllDeclaredOauth2Resources(OAuth2BackendProps), + MgtResources0 = skip_unknown_mgt_resource_servers(ManagementProps, + OAuth2Resources), + MgtResources1 = maps:merge(maps:filtermap(fun(K,_V) -> + case maps:is_key(K, MgtResources0) of + true -> false; + false -> {true, [{id, K}]} + end end, OAuth2Resources), MgtResources0), + MgtResources = maps:map( + fun(K,V) -> merge_oauth_provider_info( + maps:get(K, OAuth2Resources, #{}), V, ManagementProps) end, + skip_disabled_mgt_resource_servers(MgtResources1)), + case maps:size(MgtResources) of + 0 -> {}; + _ -> {MgtResources} + end. + +getAllDeclaredOauth2Resources(OAuth2BackendProps) -> + OAuth2Resources = proplists:get_value(resource_servers, OAuth2BackendProps, + #{}), + case proplists:get_value(resource_server_id, OAuth2BackendProps) of + undefined -> + OAuth2Resources; + Id -> + maps:put(Id, buildRootResourceServerIfAny(Id, OAuth2BackendProps), + OAuth2Resources) + end. +buildRootResourceServerIfAny(Id, Props) -> + [ + {id, Id}, + {oauth_provider_id, proplists:get_value(oauth_provider_id, Props)} + ]. + +authSettings() -> + ManagementProps = application:get_all_env(rabbitmq_management), + OAuth2BackendProps = application:get_all_env(rabbitmq_auth_backend_oauth2), + EnableOAUTH = proplists:get_value(oauth_enabled, ManagementProps, false), + case EnableOAUTH of + false -> [{oauth_enabled, false}]; + true -> + case extract_oauth2_and_mgt_resources(OAuth2BackendProps, + ManagementProps) of + {MgtResources} -> + produce_auth_settings(MgtResources, ManagementProps); + {} -> + [{oauth_enabled, false}] + end + end. + +% invalid -> those resources that dont have an oauth_client_id and +% their login_type is sp_initiated +skip_invalid_mgt_resource_servers(MgtResourceServers, ManagementProps) -> + DefaultOauthInitiatedLogonType = proplists:get_value( + oauth_initiated_logon_type, ManagementProps, sp_initiated), + maps:filter(fun(_K,ResourceServer) -> + SpInitiated = + case maps:get(oauth_initiated_logon_type, ResourceServer, + DefaultOauthInitiatedLogonType) of + sp_initiated -> true; + _ -> false + end, + not SpInitiated or not is_invalid([maps:get(oauth_client_id, + ResourceServer, undefined)]) + end, MgtResourceServers). + +% filter -> include only those resources with an oauth_client_id +% or those whose logon type is not sp_initiated +filter_out_invalid_mgt_resource_servers(MgtResourceServers, ManagementProps) -> + case is_invalid([proplists:get_value(oauth_client_id, ManagementProps)]) of + true -> + skip_invalid_mgt_resource_servers(MgtResourceServers, + ManagementProps); + false -> + MgtResourceServers + end. + +filter_mgt_resource_servers_without_oauth_provider_url(MgtResourceServers) -> + maps:filter(fun(_K1,V1) -> maps:is_key(oauth_provider_url, V1) end, + MgtResourceServers). + +ensure_oauth_resource_server_properties_are_binaries(Key, Value) -> + case Key of + oauth_authorization_endpoint_params -> Value; + oauth_token_endpoint_params -> Value; + _ -> to_binary(Value) + end. + +produce_auth_settings(MgtResourceServers, ManagementProps) -> + ConvertValuesToBinary = fun(_K,V) -> + [ + {K1, ensure_oauth_resource_server_properties_are_binaries(K1, V1)} + || {K1,V1} <- maps:to_list(V) + ] end, + FilteredMgtResourceServers = + filter_mgt_resource_servers_without_oauth_provider_url( + filter_out_invalid_mgt_resource_servers(MgtResourceServers, + ManagementProps)), + + case maps:size(FilteredMgtResourceServers) of + 0 -> + [{oauth_enabled, false}]; + _ -> + filter_empty_properties([ + {oauth_enabled, true}, + {oauth_resource_servers, + maps:map(ConvertValuesToBinary, FilteredMgtResourceServers)}, + to_tuple(oauth_disable_basic_auth, ManagementProps, + fun to_binary/1, true), + to_tuple(oauth_client_id, ManagementProps), + to_tuple(oauth_client_secret, ManagementProps), + to_tuple(oauth_scopes, ManagementProps), + case proplists:get_value(oauth_initiated_logon_type, + ManagementProps, sp_initiated) of + sp_initiated -> + {}; + idp_initiated -> + {oauth_initiated_logon_type, <<"idp_initiated">>} + end, + to_tuple(oauth_authorization_endpoint_params, ManagementProps, + undefined, undefined), + to_tuple(oauth_token_endpoint_params, ManagementProps, + undefined, undefined) + ]) + end. + +filter_empty_properties(ListOfProperties) -> + lists:filter(fun(Prop) -> + case Prop of + {} -> false; + _ -> true + end + end, ListOfProperties). + +to_binary(Value) when is_boolean(Value)-> Value; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) to_binary(Value) -> rabbit_data_coercion:to_binary(Value). to_json(ReqData, Context) -> @@ -158,9 +356,28 @@ is_invalid(List) -> end end, List). to_tuple(Key, Proplist) -> +<<<<<<< HEAD case proplists:is_defined(Key, Proplist) of true -> {Key, rabbit_data_coercion:to_binary(proplists:get_value(Key, Proplist))}; false -> {} end. to_tuple(Key, Proplist, DefaultValue) -> {Key, proplists:get_value(Key, Proplist, DefaultValue)}. +======= + to_tuple(Key, Proplist, fun to_binary/1, undefined). + +to_tuple(Key, Proplist, ConvertFun, DefaultValue) -> + case proplists:is_defined(Key, Proplist) of + true -> + {Key, case ConvertFun of + undefined -> proplists:get_value(Key, Proplist); + _ -> ConvertFun(proplists:get_value(Key, Proplist)) + end + }; + false -> + case DefaultValue of + undefined -> {}; + _ -> {Key, proplists:get_value(Key, Proplist, DefaultValue)} + end + end. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_management/src/rabbit_mgmt_wm_connection_sessions.erl b/deps/rabbitmq_management/src/rabbit_mgmt_wm_connection_sessions.erl new file mode 100644 index 000000000000..60768b20e136 --- /dev/null +++ b/deps/rabbitmq_management/src/rabbit_mgmt_wm_connection_sessions.erl @@ -0,0 +1,91 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% + +-module(rabbit_mgmt_wm_connection_sessions). + +-export([init/2, to_json/2, content_types_provided/2, is_authorized/2]). +-export([resource_exists/2]). +-export([variances/2]). + +-include_lib("rabbitmq_management_agent/include/rabbit_mgmt_records.hrl"). +-include_lib("rabbit_common/include/rabbit.hrl"). + +%%-------------------------------------------------------------------- + +init(Req, _State) -> + {cowboy_rest, rabbit_mgmt_headers:set_common_permission_headers(Req, ?MODULE), #context{}}. + +variances(Req, Context) -> + {[<<"accept-encoding">>, <<"origin">>], Req, Context}. + +content_types_provided(ReqData, Context) -> + {rabbit_mgmt_util:responder_map(to_json), ReqData, Context}. + +resource_exists(ReqData, Context) -> + case conn(ReqData) of + not_found -> + {false, ReqData, Context}; + _Conn -> + {true, ReqData, Context} + end. + +to_json(ReqData, Context) -> + Conn = conn(ReqData), + case proplists:get_value(protocol, Conn) of + {1, 0} -> + ConnPid = proplists:get_value(pid, Conn), + try rabbit_amqp_reader:info(ConnPid, [session_pids]) of + [{session_pids, Pids}] -> + rabbit_mgmt_util:reply_list(session_infos(Pids), + ["channel_number"], + ReqData, + Context) + catch Type:Reason0 -> + Reason = unicode:characters_to_binary( + lists:flatten( + io_lib:format( + "failed to get sessions for connection ~p: ~s ~tp", + [ConnPid, Type, Reason0]))), + rabbit_mgmt_util:internal_server_error(Reason, ReqData, Context) + end; + _ -> + rabbit_mgmt_util:bad_request(<<"connection does not use AMQP 1.0">>, + ReqData, + Context) + end. + +is_authorized(ReqData, Context) -> + rabbit_mgmt_util:is_authorized_user(ReqData, Context, conn(ReqData)). + +%%-------------------------------------------------------------------- + +conn(Req) -> + case rabbit_connection_tracking:lookup(rabbit_mgmt_util:id(connection, Req)) of + #tracked_connection{name = Name, + pid = Pid, + protocol = Protocol, + username = Username} -> + [{name, Name}, + {pid, Pid}, + {protocol, Protocol}, + {user, Username}]; + not_found -> + not_found + end. + +session_infos(Pids) -> + lists:filtermap( + fun(Pid) -> + case rabbit_amqp_session:info(Pid) of + {ok, Infos} -> + {true, Infos}; + {error, Reason} -> + rabbit_log:warning("failed to get infos for session ~p: ~tp", + [Pid, Reason]), + false + end + end, Pids). diff --git a/deps/rabbitmq_management/test/clustering_prop_SUITE.erl b/deps/rabbitmq_management/test/clustering_prop_SUITE.erl index df27571f043a..c3c28360e606 100644 --- a/deps/rabbitmq_management/test/clustering_prop_SUITE.erl +++ b/deps/rabbitmq_management/test/clustering_prop_SUITE.erl @@ -109,17 +109,29 @@ prop_connection_channel_counts(Config) -> {1, force_stats}])), begin % ensure we begin with no connections +<<<<<<< HEAD +======= + ct:pal("Init testcase"), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) true = validate_counts(Config, []), Cons = lists:foldl(fun (Op, Agg) -> execute_op(Config, Op, Agg) end, [], Ops), %% TODO retry a few times +<<<<<<< HEAD +======= + ct:pal("Check testcase"), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Res = retry_for( fun() -> force_stats(Config), validate_counts(Config, Cons) end, 60), +<<<<<<< HEAD cleanup(Cons), +======= + ct:pal("Cleanup testcase"), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbit_ct_helpers:await_condition( fun () -> cleanup(Cons), @@ -138,8 +150,21 @@ validate_counts(Config, Conns) -> Ch1 = length(http_get_from_node(Config, 0, "/channels")), Ch2 = length(http_get_from_node(Config, 1, "/channels")), Ch3 = length(http_get_from_node(Config, 2, "/channels")), +<<<<<<< HEAD [Expected, Expected, Expected, ChanCount, ChanCount, ChanCount] =:= [C1, C2, C3, Ch1, Ch2, Ch3]. +======= + Res = ([Expected, Expected, Expected, ChanCount, ChanCount, ChanCount] + =:= [C1, C2, C3, Ch1, Ch2, Ch3]), + case Res of + false -> + ct:pal("Validate counts connections: ~p channels: ~p got ~p", + [Expected, ChanCount, [C1, C2, C3, Ch1, Ch2, Ch3]]); + true -> + ok + end, + Res. +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cleanup(Conns) -> diff --git a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets index d26639620bb8..feaff7decee5 100644 --- a/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets +++ b/deps/rabbitmq_management/test/config_schema_SUITE_data/rabbitmq_management.snippets @@ -621,15 +621,33 @@ management.oauth_client_id = rabbitmq_client_code management.oauth_client_secret = rabbitmq_client_secret management.oauth_scopes = openid profile rabbitmq.* +<<<<<<< HEAD management.oauth_initiated_logon_type = idp_initiated", [ {rabbitmq_management, [ +======= + management.oauth_authorization_endpoint_params.param1 = value1 + management.oauth_token_endpoint_params.param2 = value2 + management.oauth_initiated_logon_type = idp_initiated", + [ + {rabbitmq_management, [ + {oauth_authorization_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {oauth_enabled, true}, {oauth_provider_url, "http://localhost:8080"}, {oauth_client_id, "rabbitmq_client_code"}, {oauth_client_secret, "rabbitmq_client_secret"}, {oauth_scopes, "openid profile rabbitmq.*"}, +<<<<<<< HEAD {oauth_initiated_logon_type, idp_initiated} +======= + {oauth_initiated_logon_type, idp_initiated}, + {oauth_token_endpoint_params, [ + {<<"param2">>, <<"value2">>} + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ], [rabbitmq_management] }, @@ -640,7 +658,13 @@ management.oauth_resource_servers.1.label = One management.oauth_resource_servers.1.oauth_client_id = one management.oauth_resource_servers.1.oauth_scopes = openid profile rabbitmq.* +<<<<<<< HEAD management.oauth_resource_servers.2.oauth_provider_url = http://two +======= + management.oauth_resource_servers.1.oauth_token_endpoint_params.param2 = value2 + management.oauth_resource_servers.2.oauth_provider_url = http://two + management.oauth_resource_servers.2.oauth_authorization_endpoint_params.param1 = value1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) management.oauth_resource_servers.2.id = resource-two management.oauth_resource_servers.2.oauth_client_id = two management.oauth_resource_servers.3.oauth_initiated_logon_type = idp_initiated @@ -650,6 +674,7 @@ {oauth_enabled, true}, {oauth_resource_servers, #{ +<<<<<<< HEAD <<"resource-one">> => [ {oauth_scopes, "openid profile rabbitmq.*"}, {oauth_client_id, "one"}, @@ -665,6 +690,30 @@ <<"3">> => [ {oauth_initiated_logon_type, idp_initiated}, {oauth_provider_url, "http://three"} +======= + <<"3">> => [ + {oauth_provider_url, <<"http://three">>}, + {oauth_initiated_logon_type, idp_initiated}, + {id, <<"3">>} + ], + <<"resource-one">> => [ + {oauth_token_endpoint_params, [ + {<<"param2">>, <<"value2">>} + ]}, + {oauth_scopes, <<"openid profile rabbitmq.*">>}, + {oauth_client_id, <<"one">>}, + {label, <<"One">>}, + {id, <<"resource-one">>}, + {oauth_provider_url, <<"http://one:8080">>} + ], + <<"resource-two">> => [ + {oauth_authorization_endpoint_params, [ + {<<"param1">>, <<"value1">>} + ]}, + {oauth_client_id, <<"two">>}, + {id, <<"resource-two">>}, + {oauth_provider_url, <<"http://two">>} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ] } } diff --git a/deps/rabbitmq_management/test/js/.babelrc b/deps/rabbitmq_management/test/js/.babelrc new file mode 100644 index 000000000000..1320b9a3272a --- /dev/null +++ b/deps/rabbitmq_management/test/js/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/preset-env"] +} diff --git a/deps/rabbitmq_management/test/js/package.json b/deps/rabbitmq_management/test/js/package.json new file mode 100644 index 000000000000..0748d98ba9c0 --- /dev/null +++ b/deps/rabbitmq_management/test/js/package.json @@ -0,0 +1,35 @@ +{ + "type":"module", + "dependencies": { + + + "json": "^11.0.0", + + + "mocha": "^10.7.3" + + }, + + "scripts": { + + + "test": "mocha --recursive --trace-warnings --require @babel/register" + + }, + + "devDependencies": { + + + "@babel/cli": "^7.25.6", + + + "@babel/core": "^7.25.2", + + + "@babel/preset-env": "^7.25.4", + + + "@babel/register": "^7.24.6" + + } +} diff --git a/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js b/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js new file mode 100644 index 000000000000..88431a0c9498 --- /dev/null +++ b/deps/rabbitmq_management/test/js/test/oidc-oauth/helper.test.js @@ -0,0 +1,22 @@ +const assert = require('assert') +import oidc_settings_from from '../../../../priv/www/js/oidc-oauth/helper.js' + +describe('oidc_settings_from', function () { + describe('single root resource', function () { + + describe('with minimum required settings', function () { + var resource = { + oauth_client_id : "some-client", + oauth_provider_url : "https://someurl", + oauth_metadata_url : "https://someurl/extra" + } + var oidc_settings = oidc_settings_from(resource) + + it('oidc_settings should have client_id ', function () { + assert.equal(resource.oauth_provider_url, oidc_settings.authority) + assert.equal(resource.oauth_metadata_url, oidc_settings.metadataUrl) + assert.equal(resource.oauth_client_id, oidc_settings.client_id) + }) + }) + }) +}) \ No newline at end of file diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl index b63828d8b729..8b81fb9b08bf 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl @@ -144,8 +144,11 @@ all_tests() -> [ permissions_validation_test, permissions_list_test, permissions_test, +<<<<<<< HEAD connections_test_amqpl, connections_test_amqp, +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) multiple_invalid_connections_test, quorum_queues_test, stream_queues_have_consumers_field, @@ -212,6 +215,14 @@ all_tests() -> [ qq_status_test, list_deprecated_features_test, list_used_deprecated_features_test, +<<<<<<< HEAD +======= + connections_amqpl, + connections_amqp, + amqp_sessions, + amqpl_sessions, + enable_plugin_amqp, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) cluster_and_node_tags_test, version_test ]. @@ -250,17 +261,26 @@ finish_init(Group, Config) -> merge_app_env(Config1). init_per_suite(Config) -> +<<<<<<< HEAD {ok, _} = application:ensure_all_started(amqp10_client), +======= + {ok, _} = application:ensure_all_started(rabbitmq_amqp_client), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config. end_per_suite(Config) -> Config. +<<<<<<< HEAD init_per_group(Group, Config0) when Group == all_tests_with_prefix -> +======= +init_per_group(all_tests_with_prefix=Group, Config0) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) PathConfig = {rabbitmq_management, [{path_prefix, ?PATH_PREFIX}]}, Config1 = rabbit_ct_helpers:merge_app_env(Config0, PathConfig), Config2 = finish_init(Group, Config1), start_broker(Config2); +<<<<<<< HEAD init_per_group(Group, Config0) when Group == default_queue_type_group_tests -> case rabbit_ct_helpers:is_mixed_versions() of true -> @@ -269,6 +289,8 @@ init_per_group(Group, Config0) when Group == default_queue_type_group_tests -> Config1 = finish_init(Group, Config0), start_broker(Config1) end; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_group(Group, Config0) -> Config1 = finish_init(Group, Config0), start_broker(Config1). @@ -317,6 +339,7 @@ init_per_testcase(queues_detailed_test, Config) -> true -> Config; false -> {skip, "The detailed queues endpoint is not available."} end; +<<<<<<< HEAD init_per_testcase(Testcase, Config) when Testcase == definitions_file_metadata_test -> case rabbit_ct_helpers:is_mixed_versions() of true -> @@ -324,6 +347,8 @@ init_per_testcase(Testcase, Config) when Testcase == definitions_file_metadata_t _ -> rabbit_ct_helpers:testcase_started(Config, Testcase) end; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_testcase(Testcase, Config) -> rabbit_ct_broker_helpers:close_all_connections(Config, 0, <<"rabbit_mgmt_SUITE:init_per_testcase">>), rabbit_ct_helpers:testcase_started(Config, Testcase). @@ -1022,7 +1047,11 @@ topic_permissions_test(Config) -> http_delete(Config, "/vhosts/myvhost2", {group, '2xx'}), passed. +<<<<<<< HEAD connections_test_amqpl(Config) -> +======= +connections_amqpl(Config) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {Conn, _Ch} = open_connection_and_channel(Config), LocalPort = local_port(Conn), Path = binary_to_list( @@ -1055,7 +1084,11 @@ connections_test_amqpl(Config) -> passed. %% Test that AMQP 1.0 connection can be listed and closed via the rabbitmq_management plugin. +<<<<<<< HEAD connections_test_amqp(Config) -> +======= +connections_amqp(Config) -> +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Node = atom_to_binary(rabbit_ct_broker_helpers:get_node_config(Config, 0, nodename)), Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), User = <<"guest">>, @@ -1112,6 +1145,153 @@ connections_test_amqp(Config) -> eventually(?_assertEqual([], http_get(Config, "/connections")), 10, 5), ?assertEqual(0, length(rpc(Config, rabbit_amqp1_0, list_local, []))). +<<<<<<< HEAD +======= +%% Test that AMQP 1.0 sessions and links can be listed via the rabbitmq_management plugin. +amqp_sessions(Config) -> + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + User = <<"guest">>, + OpnConf = #{address => ?config(rmq_hostname, Config), + port => Port, + container_id => <<"my container">>, + sasl => {plain, User, <<"guest">>}}, + {ok, C} = amqp10_client:open_connection(OpnConf), + receive {amqp10_event, {connection, C, opened}} -> ok + after 5000 -> ct:fail(opened_timeout) + end, + + {ok, Session1} = amqp10_client:begin_session_sync(C), + {ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync( + Session1, <<"my link pair">>), + QName = <<"my queue">>, + {ok, #{}} = rabbitmq_amqp_client:declare_queue(LinkPair, QName, #{}), + {ok, Sender} = amqp10_client:attach_sender_link_sync( + Session1, + <<"my sender">>, + rabbitmq_amqp_address:exchange(<<"amq.direct">>, <<"my key">>)), + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session1, + <<"my receiver">>, + rabbitmq_amqp_address:queue(QName)), + receive {amqp10_event, {link, Receiver, attached}} -> ok + after 5000 -> ct:fail({missing_event, ?LINE}) + end, + ok = amqp10_client:flow_link_credit(Receiver, 5000, never), + + eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10), + [Connection] = http_get(Config, "/connections"), + ConnectionName = maps:get(name, Connection), + Path = "/connections/" ++ binary_to_list(uri_string:quote(ConnectionName)) ++ "/sessions", + [Session] = http_get(Config, Path), + ?assertMatch( + #{channel_number := 0, + handle_max := HandleMax, + next_incoming_id := NextIncomingId, + incoming_window := IncomingWindow, + next_outgoing_id := NextOutgoingId, + remote_incoming_window := RemoteIncomingWindow, + remote_outgoing_window := RemoteOutgoingWindow, + outgoing_unsettled_deliveries := 0, + incoming_links := [#{handle := 0, + link_name := <<"my link pair">>, + target_address := <<"/management">>, + delivery_count := DeliveryCount1, + credit := Credit1, + snd_settle_mode := <<"settled">>, + max_message_size := IncomingMaxMsgSize, + unconfirmed_messages := 0}, + #{handle := 2, + link_name := <<"my sender">>, + target_address := <<"/exchanges/amq.direct/my%20key">>, + delivery_count := DeliveryCount2, + credit := Credit2, + snd_settle_mode := <<"mixed">>, + max_message_size := IncomingMaxMsgSize, + unconfirmed_messages := 0}], + outgoing_links := [#{handle := 1, + link_name := <<"my link pair">>, + source_address := <<"/management">>, + queue_name := <<>>, + delivery_count := DeliveryCount3, + credit := 0, + max_message_size := <<"unlimited">>, + send_settled := true}, + #{handle := 3, + link_name := <<"my receiver">>, + source_address := <<"/queues/my%20queue">>, + queue_name := <<"my queue">>, + delivery_count := DeliveryCount4, + credit := 5000, + max_message_size := <<"unlimited">>, + send_settled := true}] + } when is_integer(HandleMax) andalso + is_integer(NextIncomingId) andalso + is_integer(IncomingWindow) andalso + is_integer(NextOutgoingId) andalso + is_integer(RemoteIncomingWindow) andalso + is_integer(RemoteOutgoingWindow) andalso + is_integer(Credit1) andalso + is_integer(Credit2) andalso + is_integer(IncomingMaxMsgSize) andalso + is_integer(DeliveryCount1) andalso + is_integer(DeliveryCount2) andalso + is_integer(DeliveryCount3) andalso + is_integer(DeliveryCount4), + Session), + + {ok, _Session2} = amqp10_client:begin_session_sync(C), + Sessions = http_get(Config, Path), + ?assertEqual(2, length(Sessions)), + + ok = amqp10_client:detach_link(Sender), + ok = amqp10_client:detach_link(Receiver), + {ok, _} = rabbitmq_amqp_client:delete_queue(LinkPair, QName), + ok = rabbitmq_amqp_client:detach_management_link_pair_sync(LinkPair), + ok = amqp10_client:close_connection(C). + +%% Test that GET /connections/:name/sessions returns +%% 400 Bad Request for non-AMQP 1.0 connections. +amqpl_sessions(Config) -> + {Conn, _Ch} = open_connection_and_channel(Config), + LocalPort = local_port(Conn), + Path = binary_to_list( + rabbit_mgmt_format:print( + "/connections/127.0.0.1%3A~w%20-%3E%20127.0.0.1%3A~w/sessions", + [LocalPort, amqp_port(Config)])), + ok = await_condition( + fun() -> + http_get(Config, Path, 400), + true + end). + +%% Test that AMQP 1.0 connection can be listed if the rabbitmq_management plugin gets enabled +%% after the connection was established. +enable_plugin_amqp(Config) -> + ?assertEqual(0, length(http_get(Config, "/connections"))), + + ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, rabbitmq_management), + ok = rabbit_ct_broker_helpers:disable_plugin(Config, 0, rabbitmq_management_agent), + + Port = rabbit_ct_broker_helpers:get_node_config(Config, 0, tcp_port_amqp), + OpnConf = #{address => ?config(rmq_hostname, Config), + port => Port, + container_id => <<"my container">>, + sasl => anon}, + {ok, Conn} = amqp10_client:open_connection(OpnConf), + receive {amqp10_event, {connection, Conn, opened}} -> ok + after 5000 -> ct:fail(opened_timeout) + end, + + ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, rabbitmq_management_agent), + ok = rabbit_ct_broker_helpers:enable_plugin(Config, 0, rabbitmq_management), + eventually(?_assertEqual(1, length(http_get(Config, "/connections"))), 1000, 10), + + ok = amqp10_client:close_connection(Conn), + receive {amqp10_event, {connection, Conn, {closed, normal}}} -> ok + after 5000 -> ct:fail({connection_close_timeout, Conn}) + end. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) flush(Prefix) -> receive Msg -> diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl new file mode 100644 index 000000000000..47c369978cb9 --- /dev/null +++ b/deps/rabbitmq_management/test/rabbit_mgmt_schema_SUITE.erl @@ -0,0 +1,76 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% +-module(rabbit_mgmt_schema_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). + +-import(rabbit_mgmt_schema, [translate_endpoint_params/2, translate_oauth_resource_servers/1]). + +all() -> + [ + test_empty_endpoint_params, + test_invalid_endpoint_params, + test_translate_endpoint_params, + test_with_one_resource_server, + test_with_many_resource_servers + ]. + + +test_empty_endpoint_params(_) -> + [] = translate_endpoint_params("oauth_authorization_endpoint_params", []), + [] = translate_endpoint_params("oauth_token_endpoint_params", []). + +test_invalid_endpoint_params(_) -> + try translate_endpoint_params("oauth_authorization_endpoint_params", [ + {["param1","param2"], "some-value1"}]) of + _ -> {throw, should_have_failed} + catch + _ -> ok + end. + +test_translate_endpoint_params(_) -> + [ {<<"param1">>, <<"some-value1">>} ] = + translate_endpoint_params("oauth_authorization_endpoint_params", [ + {["management","oauth_authorization_endpoint_params","param1"], "some-value1"} + ]). + +test_with_one_resource_server(_) -> + Conf = [ + {["management","oauth_resource_servers","rabbitmq1","id"],"rabbitmq1"} + ], + #{ + <<"rabbitmq1">> := [ + {id, <<"rabbitmq1">>} + ] + } = translate_oauth_resource_servers(Conf). + +test_with_many_resource_servers(_) -> + Conf = [ + {["management","oauth_resource_servers","keycloak","label"],"Keycloak"}, + {["management","oauth_resource_servers","uaa","label"],"Uaa"} + ], + #{ + <<"keycloak">> := [ + {label, <<"Keycloak">>}, + {id, <<"keycloak">>} + ], + <<"uaa">> := [ + {label, <<"Uaa">>}, + {id, <<"uaa">>} + ] + } = translate_oauth_resource_servers(Conf). + + +cert_filename(Conf) -> + string:concat(?config(data_dir, Conf), "certs/cert.pem"). + +sort_settings(MapOfListOfSettings) -> + maps:map(fun(_K,List) -> + lists:sort(fun({K1,_}, {K2,_}) -> K1 < K2 end, List) end, MapOfListOfSettings). diff --git a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl index d47350d2b926..8fc75bf2b1b4 100644 --- a/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl +++ b/deps/rabbitmq_management/test/rabbit_mgmt_wm_auth_SUITE.erl @@ -9,7 +9,12 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). +<<<<<<< HEAD +======= +-import(application, [set_env/3, unset_env/2]). +-import(rabbit_mgmt_wm_auth, [authSettings/0]). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) -compile(export_all). all() -> @@ -21,14 +26,42 @@ all() -> {group, verify_mgt_oauth_provider_url_with_single_resource_and_another_resource}, {group, verify_end_session_endpoint_with_single_resource}, {group, verify_end_session_endpoint_with_single_resource_and_another_resource}, +<<<<<<< HEAD {group, verify_oauth_initiated_logon_type_for_sp_initiated}, {group, verify_oauth_initiated_logon_type_for_idp_initiated}, {group, verify_oauth_disable_basic_auth}, {group, verify_oauth_scopes} +======= + {group, verify_multi_resource_and_provider}, + {group, verify_oauth_initiated_logon_type_for_sp_initiated}, + {group, verify_oauth_initiated_logon_type_for_idp_initiated}, + {group, verify_oauth_disable_basic_auth}, + {group, verify_oauth_scopes}, + {group, verify_extra_endpoint_params} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]. groups() -> [ +<<<<<<< HEAD +======= + + {verify_multi_resource_and_provider, [], [ + {with_oauth_enabled, [], [ + {with_oauth_providers_idp1_idp2, [], [ + {with_default_oauth_provider_idp1, [], [ + {with_resource_server_a, [], [ + should_return_disabled_auth_settings, + {with_mgt_resource_server_a_with_client_id_x, [], [ + should_return_oauth_enabled, + should_return_oauth_resource_server_a_with_client_id_x + ]} + ]} + ]} + ]} + ]} + ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {without_any_settings, [], [ should_return_disabled_auth_settings ]}, @@ -74,8 +107,18 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_mgt_oauth_provider_url_url1, +<<<<<<< HEAD {with_mgt_oauth_provider_url_url0, [], [ should_return_mgt_oauth_provider_url_url0 +======= + should_return_mgt_oauth_metadata_url_url1, + {with_mgt_oauth_provider_url_url0, [], [ + should_return_mgt_oauth_provider_url_url0, + should_return_mgt_oauth_metadata_url_url1, + {with_mgt_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, [], [ + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1 + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]} ]} @@ -86,6 +129,10 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_mgt_oauth_provider_url_idp1_url, +<<<<<<< HEAD +======= + should_return_mgt_oauth_matadata_url_idp1_url, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {with_root_issuer_url1, [], [ should_return_mgt_oauth_provider_url_idp1_url ]}, @@ -102,7 +149,11 @@ groups() -> {with_resource_server_id_rabbit, [], [ {with_root_issuer_url1, [], [ {with_oauth_enabled, [], [ +<<<<<<< HEAD {with_mgt_oauth_client_id_z, [], [ +======= + {with_mgt_oauth_client_id_z, [], [ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) should_not_return_end_session_endpoint, {with_root_end_session_endpoint_0, [], [ should_return_end_session_endpoint_0 @@ -112,7 +163,11 @@ groups() -> ]}, {with_oauth_providers_idp1_idp2, [], [ {with_default_oauth_provider_idp1, [], [ +<<<<<<< HEAD {with_oauth_enabled, [], [ +======= + {with_oauth_enabled, [], [ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {with_mgt_oauth_client_id_z, [], [ should_not_return_end_session_endpoint, {with_end_session_endpoint_for_idp1_1, [], [ @@ -141,7 +196,11 @@ groups() -> should_return_oauth_resource_server_a_without_end_session_endpoint, {with_root_end_session_endpoint_0, [], [ should_return_end_session_endpoint_0, +<<<<<<< HEAD should_return_oauth_resource_server_a_with_end_session_endpoint_0 +======= + should_return_oauth_resource_server_a_with_end_session_endpoint_0 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {with_oauth_providers_idp1_idp2, [], [ {with_default_oauth_provider_idp1, [], [ @@ -151,11 +210,19 @@ groups() -> {with_oauth_provider_idp2_for_resource_server_a, [], [ {with_end_session_endpoint_for_idp2_2, [], [ should_return_oauth_resource_server_a_with_end_session_endpoint_2 +<<<<<<< HEAD ]} ]} ]} ]} ]} +======= + ]} + ]} + ]} + ]} + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]} ]} @@ -170,6 +237,7 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url1, +<<<<<<< HEAD should_return_oauth_resource_server_a_with_oauth_provider_url_url1, {with_mgt_oauth_provider_url_url0, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, @@ -177,6 +245,19 @@ groups() -> {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, should_return_oauth_resource_server_a_with_oauth_provider_url_url1 +======= + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, + should_return_oauth_resource_server_a_with_oauth_provider_url_url1, + should_return_oauth_resource_server_a_with_oauth_metadata_url_url1, + {with_mgt_oauth_provider_url_url0, [], [ + should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, + should_return_oauth_resource_server_a_with_oauth_provider_url_url0, + should_return_oauth_resource_server_a_with_oauth_metadata_url_url1, + {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ + should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, + should_return_oauth_resource_server_a_with_oauth_provider_url_url1 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]} ]} @@ -188,14 +269,31 @@ groups() -> should_return_disabled_auth_settings, {with_mgt_oauth_client_id_z, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url, +<<<<<<< HEAD +======= + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url, + {with_mgt_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, [], [ + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1 + ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {with_root_issuer_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url ]}, {with_mgt_oauth_provider_url_url0, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, +<<<<<<< HEAD {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, should_return_oauth_resource_server_a_with_oauth_provider_url_url1 +======= + should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url, + {with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, [], [ + should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0, + should_return_oauth_resource_server_a_with_oauth_provider_url_url1, + {with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url1, [], [ + should_return_oauth_resource_server_a_with_oauth_metadata_url_url1 + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]} ]} @@ -204,7 +302,11 @@ groups() -> ]} ]} ]} +<<<<<<< HEAD ]}, +======= + ]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {verify_oauth_initiated_logon_type_for_sp_initiated, [], [ should_return_disabled_auth_settings, {with_resource_server_id_rabbit, [], [ @@ -287,6 +389,35 @@ groups() -> ]} ]} ]} +<<<<<<< HEAD +======= + ]}, + {verify_extra_endpoint_params, [], [ + {with_resource_server_id_rabbit, [], [ + {with_root_issuer_url1, [], [ + {with_oauth_enabled, [], [ + {with_mgt_oauth_client_id_z, [], [ + should_return_mgt_oauth_resource_rabbit_without_authorization_endpoint_params, + should_return_mgt_oauth_resource_rabbit_without_token_endpoint_params, + {with_authorization_endpoint_params_0, [], [ + should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0 + ]}, + {with_token_endpoint_params_0, [], [ + should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0 + ]}, + {with_resource_server_a, [], [ + {with_mgt_resource_server_a_with_authorization_endpoint_params_1, [], [ + should_return_mgt_oauth_resource_a_with_authorization_endpoint_params_1 + ]}, + {with_mgt_resource_server_a_with_token_endpoint_params_1, [], [ + should_return_mgt_oauth_resource_a_with_token_endpoint_params_1 + ]} + ]} + ]} + ]} + ]} + ]} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]} ]. @@ -299,10 +430,22 @@ init_per_suite(Config) -> {idp2, <<"idp2">>}, {idp3, <<"idp3">>}, {idp1_url, <<"https://idp1">>}, +<<<<<<< HEAD {idp2_url, <<"https://idp2">>}, {idp3_url, <<"https://idp3">>}, {url0, <<"https://url0">>}, {url1, <<"https://url1">>}, +======= + {meta_idp1_url, <<"https://idp1/.well-known/openid-configuration">>}, + {idp2_url, <<"https://idp2">>}, + {meta_idp2_url, <<"https://idp2/.well-known/openid-configuration">>}, + {idp3_url, <<"https://idp3">>}, + {meta_idp3_url, <<"https://idp3/.well-known/openid-configuration">>}, + {url0, <<"https://url0">>}, + {meta_url0, <<"https://url0/.well-known/openid-configuration">>}, + {url1, <<"https://url1">>}, + {meta_url1, <<"https://url1/.well-known/openid-configuration">>}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {logout_url_0, <<"https://logout_0">>}, {logout_url_1, <<"https://logout_1">>}, {logout_url_2, <<"https://logout_2">>}, @@ -312,6 +455,13 @@ init_per_suite(Config) -> {w, <<"w">>}, {z, <<"z">>}, {x, <<"x">>}, +<<<<<<< HEAD +======= + {authorization_params_0, [{<<"a-param0">>, <<"value0">>}]}, + {authorization_params_1, [{<<"a-param1">>, <<"value1">>}]}, + {token_params_0, [{<<"t-param0">>, <<"value0">>}]}, + {token_params_1, [{<<"t-param1">>, <<"value1">>}]}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {admin_mgt, <<"admin mgt">>}, {read_write, <<"read write">>} | Config]. @@ -319,6 +469,7 @@ end_per_suite(_Config) -> ok. init_per_group(with_oauth_disabled, Config) -> +<<<<<<< HEAD application:set_env(rabbitmq_management, oauth_enabled, false), Config; init_per_group(with_oauth_enabled, Config) -> @@ -329,12 +480,25 @@ init_per_group(with_resource_server_id_rabbit, Config) -> Config; init_per_group(with_mgt_oauth_client_id_z, Config) -> application:set_env(rabbitmq_management, oauth_client_id, ?config(z, Config)), +======= + set_env(rabbitmq_management, oauth_enabled, false), + Config; +init_per_group(with_oauth_enabled, Config) -> + set_env(rabbitmq_management, oauth_enabled, true), + Config; +init_per_group(with_resource_server_id_rabbit, Config) -> + set_env(rabbitmq_auth_backend_oauth2, resource_server_id, ?config(rabbit, Config)), + Config; +init_per_group(with_mgt_oauth_client_id_z, Config) -> + set_env(rabbitmq_management, oauth_client_id, ?config(z, Config)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; init_per_group(with_mgt_resource_server_a_with_client_secret_w, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_client_secret, ?config(w, Config)), Config; init_per_group(with_mgt_oauth_client_secret_q, Config) -> +<<<<<<< HEAD application:set_env(rabbitmq_management, oauth_client_secret, ?config(q, Config)), Config; init_per_group(with_mgt_oauth_provider_url_url0, Config) -> @@ -354,6 +518,27 @@ init_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> Config; init_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> application:set_env(rabbitmq_management, oauth_initiated_logon_type, sp_initiated), +======= + set_env(rabbitmq_management, oauth_client_secret, ?config(q, Config)), + Config; +init_per_group(with_mgt_oauth_provider_url_url0, Config) -> + set_env(rabbitmq_management, oauth_provider_url, ?config(url0, Config)), + Config; +init_per_group(with_root_issuer_url1, Config) -> + set_env(rabbitmq_auth_backend_oauth2, issuer, ?config(url1, Config)), + Config; +init_per_group(with_oauth_scopes_admin_mgt, Config) -> + set_env(rabbitmq_management, oauth_scopes, ?config(admin_mgt, Config)), + Config; +init_per_group(with_oauth_scopes_write_read, Config) -> + set_env(rabbitmq_management, oauth_scopes, ?config(write_read, Config)), + Config; +init_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> + set_env(rabbitmq_management, oauth_initiated_logon_type, idp_initiated), + Config; +init_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> + set_env(rabbitmq_management, oauth_initiated_logon_type, sp_initiated), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; init_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_sp_initiated, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, @@ -364,10 +549,17 @@ init_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_idp_in ?config(a, Config), oauth_initiated_logon_type, idp_initiated), Config; init_per_group(with_oauth_disable_basic_auth_false, Config) -> +<<<<<<< HEAD application:set_env(rabbitmq_management, oauth_disable_basic_auth, false), Config; init_per_group(with_oauth_providers_idp1_idp2, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{ +======= + set_env(rabbitmq_management, oauth_disable_basic_auth, false), + Config; +init_per_group(with_oauth_providers_idp1_idp2, Config) -> + set_env(rabbitmq_auth_backend_oauth2, oauth_providers, #{ +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ?config(idp1, Config) => [ { issuer, ?config(idp1_url, Config)} ], ?config(idp2, Config) => [ { issuer, ?config(idp2_url, Config)} ] }), @@ -392,6 +584,7 @@ init_per_group(with_mgt_resource_server_a_with_client_id_x, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_client_id, ?config(x, Config)), Config; +<<<<<<< HEAD init_per_group(with_default_oauth_provider_idp1, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp1, Config)), Config; @@ -400,6 +593,20 @@ init_per_group(with_default_oauth_provider_idp3, Config) -> Config; init_per_group(with_root_end_session_endpoint_0, Config) -> application:set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, ?config(logout_url_0, Config)), +======= + +init_per_group(with_default_oauth_provider_idp1, Config) -> + set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp1, Config)), + Config; +init_per_group(with_mgt_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, Config) -> + set_env(rabbitmq_management, oauth_metadata_url, ?config(meta_url1, Config)), + Config; +init_per_group(with_default_oauth_provider_idp3, Config) -> + set_env(rabbitmq_auth_backend_oauth2, default_oauth_provider, ?config(idp3, Config)), + Config; +init_per_group(with_root_end_session_endpoint_0, Config) -> + set_env(rabbitmq_auth_backend_oauth2, end_session_endpoint, ?config(logout_url_0, Config)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; init_per_group(with_end_session_endpoint_for_idp1_1, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, @@ -409,16 +616,44 @@ init_per_group(with_end_session_endpoint_for_idp2_2, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, ?config(idp2, Config), end_session_endpoint, ?config(logout_url_2, Config)), Config; +<<<<<<< HEAD +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_group(with_oauth_provider_idp2_for_resource_server_a, Config) -> set_attribute_in_entry_for_env_variable(rabbitmq_auth_backend_oauth2, resource_servers, ?config(a, Config), oauth_provider_id, ?config(idp2, Config)), Config; +<<<<<<< HEAD +======= +init_per_group(with_authorization_endpoint_params_0, Config) -> + set_env(rabbitmq_management, oauth_authorization_endpoint_params, + ?config(authorization_params_0, Config)), + Config; +init_per_group(with_token_endpoint_params_0, Config) -> + set_env(rabbitmq_management, oauth_token_endpoint_params, + ?config(token_params_0, Config)), + Config; +init_per_group(with_mgt_resource_server_a_with_authorization_endpoint_params_1, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_authorization_endpoint_params, ?config(authorization_params_1, Config)), + Config; +init_per_group(with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url1, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_metadata_url, ?config(meta_url1, Config)), + Config; +init_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config) -> + set_attribute_in_entry_for_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_token_endpoint_params, ?config(token_params_1, Config)), + Config; + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) init_per_group(_, Config) -> Config. end_per_group(with_oauth_providers_idp1_idp2, Config) -> +<<<<<<< HEAD application:unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), Config; end_per_group(with_mgt_oauth_client_secret_q, Config) -> @@ -456,6 +691,52 @@ end_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> Config; end_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> application:unset_env(rabbitmq_management, oauth_initiated_logon_type), +======= + unset_env(rabbitmq_auth_backend_oauth2, oauth_providers), + Config; +end_per_group(with_mgt_oauth_client_secret_q, Config) -> + unset_env(rabbitmq_management, oauth_client_secret), + Config; +end_per_group(with_oauth_scopes_admin_mgt, Config) -> + unset_env(rabbitmq_management, oauth_scopes), + Config; +end_per_group(with_oauth_scopes_write_read, Config) -> + unset_env(rabbitmq_management, oauth_scopes), + Config; +end_per_group(with_oauth_disabled, Config) -> + unset_env(rabbitmq_management, oauth_enabled), + Config; +end_per_group(with_oauth_enabled, Config) -> + unset_env(rabbitmq_management, oauth_enabled), + Config; +end_per_group(with_oauth_disable_basic_auth_false, Config) -> + unset_env(rabbitmq_management, oauth_disable_basic_auth), + Config; +end_per_group(with_resource_server_id_rabbit, Config) -> + unset_env(rabbitmq_auth_backend_oauth2, resource_server_id), + Config; +end_per_group(with_default_oauth_provider_idp1, Config) -> + unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), + Config; +end_per_group(with_mgt_oauth_provider_url_url0, Config) -> + unset_env(rabbitmq_management, oauth_provider_url), + Config; +end_per_group(with_mgt_oauth_resource_server_rabbit_with_oauth_metadata_url_url1, Config) -> + unset_env(rabbitmq_management, oauth_metadata_url), + Config; +end_per_group(with_root_issuer_url1, Config) -> + unset_env(rabbitmq_auth_backend_oauth2, issuer), + unset_env(rabbitmq_auth_backend_oauth2, discovery_endpoint), + Config; +end_per_group(with_mgt_oauth_client_id_z, Config) -> + unset_env(rabbitmq_management, oauth_client_id), + Config; +end_per_group(with_oauth_initiated_logon_type_idp_initiated, Config) -> + unset_env(rabbitmq_management, oauth_initiated_logon_type), + Config; +end_per_group(with_oauth_initiated_logon_type_sp_initiated, Config) -> + unset_env(rabbitmq_management, oauth_initiated_logon_type), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; end_per_group(with_mgt_resource_server_a_with_client_secret_w, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, @@ -477,6 +758,13 @@ end_per_group(with_mgt_oauth_resource_server_a_with_oauth_provider_url_url1, Con remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_provider_url), Config; +<<<<<<< HEAD +======= +end_per_group(with_mgt_oauth_resource_server_a_with_oauth_metadata_url_url1, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_metadata_url), + Config; +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end_per_group(with_mgt_resource_server_a_with_oauth_initiated_logon_type_sp_initiated, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, ?config(a, Config), oauth_initiated_logon_type), @@ -490,6 +778,7 @@ end_per_group(with_mgt_resource_server_a_with_client_id_x, Config) -> ?config(a, Config), oauth_client_id), Config; end_per_group(with_default_oauth_provider_idp1, Config) -> +<<<<<<< HEAD application:unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), Config; end_per_group(with_default_oauth_provider_idp3, Config) -> @@ -497,6 +786,15 @@ end_per_group(with_default_oauth_provider_idp3, Config) -> Config; end_per_group(with_root_end_session_endpoint_0, Config) -> application:unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint), +======= + unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), + Config; +end_per_group(with_default_oauth_provider_idp3, Config) -> + unset_env(rabbitmq_auth_backend_oauth2, default_oauth_provider), + Config; +end_per_group(with_root_end_session_endpoint_0, Config) -> + unset_env(rabbitmq_auth_backend_oauth2, end_session_endpoint), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Config; end_per_group(with_end_session_endpoint_for_idp1_1, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_auth_backend_oauth2, oauth_providers, @@ -510,6 +808,24 @@ end_per_group(with_oauth_provider_idp2_for_resource_server_a, Config) -> remove_attribute_from_entry_from_env_variable(rabbitmq_auth_backend_oauth2, resource_servers, ?config(a, Config), oauth_provider_id), Config; +<<<<<<< HEAD +======= +end_per_group(with_authorization_endpoint_params_0, Config) -> + unset_env(rabbitmq_management, oauth_authorization_endpoint_params), + Config; +end_per_group(with_token_endpoint_params_0, Config) -> + unset_env(rabbitmq_management, oauth_token_endpoint_params), + Config; +end_per_group(with_mgt_resource_server_a_with_authorization_endpoint_params_1, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_authorization_endpoint_params), + Config; +end_per_group(with_mgt_resource_server_a_with_token_endpoint_params_1, Config) -> + remove_attribute_from_entry_from_env_variable(rabbitmq_management, oauth_resource_servers, + ?config(a, Config), oauth_token_endpoint_params), + Config; + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end_per_group(_, Config) -> Config. @@ -519,6 +835,7 @@ end_per_group(_, Config) -> %% Test cases. %% ------------------------------------------------------------------- should_not_return_oauth_client_secret(_Config) -> +<<<<<<< HEAD Actual = rabbit_mgmt_wm_auth:authSettings(), ?assertEqual(false, proplists:is_defined(oauth_client_secret, Actual)). should_return_oauth_client_secret_q(Config) -> @@ -664,6 +981,205 @@ should_return_oauth_resource_server_a_with_end_session_endpoint_2(Config) -> assertEqual_on_attribute_for_oauth_resource_server(rabbit_mgmt_wm_auth:authSettings(), Config, a, end_session_endpoint, ?config(logout_url_2, Config)). +======= + Actual = authSettings(), + ?assertEqual(false, proplists:is_defined(oauth_client_secret, Actual)). +should_return_oauth_client_secret_q(Config) -> + Actual = authSettings(), + ?assertEqual(?config(q, Config), proplists:get_value(oauth_client_secret, Actual)). +should_return_oauth_resource_server_a_with_client_id_x(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_client_id, x). +should_return_oauth_resource_server_a_with_client_secret_w(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_client_secret, w). +should_not_return_oauth_resource_server_a_with_client_secret(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, a, oauth_client_secret). + +should_return_mgt_oauth_provider_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, idp1_url). + +should_return_mgt_oauth_matadata_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_idp1_url). + +should_return_mgt_oauth_provider_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, url1). + +should_return_mgt_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url1). + +should_return_mgt_oauth_metadata_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url0). + +should_return_mgt_oauth_provider_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, url0). + +should_return_oauth_scopes_admin_mgt(Config) -> + Actual = authSettings(), + ?assertEqual(?config(admin_mgt, Config), proplists:get_value(oauth_scopes, Actual)). + +should_return_mgt_oauth_resource_server_a_with_scopes_read_write(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, scopes, read_write). + +should_return_disabled_auth_settings(_Config) -> + [{oauth_enabled, false}] = authSettings(). + +should_return_mgt_resource_server_a_oauth_provider_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_provider_url, url0). + +should_return_mgt_oauth_resource_server_a_with_client_id_x(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_client_id, x). + +should_return_oauth_resource_server_a_with_oauth_provider_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_provider_url, idp1_url). + +should_return_oauth_resource_server_a_with_oauth_provider_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_provider_url, url1). + +should_return_oauth_resource_server_a_with_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_metadata_url, meta_url1). + +should_return_oauth_resource_server_a_with_oauth_provider_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_provider_url, url0). + +should_return_oauth_resource_server_rabbit_with_oauth_provider_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, idp1_url). + +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_idp1_url(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_idp1_url). + +should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, url1). + +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url1 ). + +should_return_oauth_resource_server_rabbit_with_oauth_provider_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_provider_url, url0). + +should_return_oauth_resource_server_rabbit_with_oauth_metadata_url_url0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_metadata_url, meta_url0). + +should_not_return_oauth_initiated_logon_type(_Config) -> + Actual = authSettings(), + ?assertEqual(false, proplists:is_defined(oauth_initiated_logon_type, Actual)). +should_return_oauth_initiated_logon_type_idp_initiated(_Config) -> + Actual = authSettings(), + ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). + +should_not_return_oauth_resource_server_a(Config) -> + Actual = authSettings(), + assert_not_defined_oauth_resource_server(Actual, Config, a). + +should_not_return_oauth_resource_server_a_with_oauth_initiated_logon_type(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, a, oauth_initiated_logon_type). + +should_return_oauth_resource_server_a_with_oauth_initiated_logon_type_idp_initiated(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_initiated_logon_type, <<"idp_initiated">>). +should_return_oauth_resource_server_a_with_oauth_initiated_logon_type_sp_initiated(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_initiated_logon_type, <<"sp_initiated">>). + +should_not_return_oauth_scopes(_Config) -> + Actual = authSettings(), + ?assertEqual(false, proplists:is_defined(scopes, Actual)). + +should_return_oauth_enabled(_Config) -> + Actual = authSettings(), + ?assertEqual(true, proplists:get_value(oauth_enabled, Actual)). + + +should_return_oauth_idp_initiated_logon(_Config) -> + Actual = authSettings(), + ?assertEqual(<<"idp_initiated">>, proplists:get_value(oauth_initiated_logon_type, Actual)). + +should_return_oauth_disable_basic_auth_true(_Config) -> + Actual = authSettings(), + ?assertEqual(true, proplists:get_value(oauth_disable_basic_auth, Actual)). + +should_return_oauth_disable_basic_auth_false(_Config) -> + Actual = authSettings(), + ?assertEqual(false, proplists:get_value(oauth_disable_basic_auth, Actual)). + +should_return_oauth_client_id_z(Config) -> + Actual = authSettings(), + ?assertEqual(?config(z, Config), proplists:get_value(oauth_client_id, Actual)). + +should_not_return_end_session_endpoint(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, rabbit, end_session_endpoint). + +should_return_end_session_endpoint_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, end_session_endpoint, ?config(logout_url_0, Config)). + +should_return_end_session_endpoint_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, end_session_endpoint, ?config(logout_url_1, Config)). + +should_return_oauth_resource_server_a_without_end_session_endpoint(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, a, end_session_endpoint). + +should_return_oauth_resource_server_a_with_end_session_endpoint_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, end_session_endpoint, ?config(logout_url_0, Config)). + +should_return_oauth_resource_server_a_with_end_session_endpoint_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, end_session_endpoint, ?config(logout_url_1, Config)). + +should_return_oauth_resource_server_a_with_end_session_endpoint_2(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, end_session_endpoint, ?config(logout_url_2, Config)). + +should_return_mgt_oauth_resource_rabbit_without_authorization_endpoint_params(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_authorization_endpoint_params). + +should_return_mgt_oauth_resource_rabbit_without_token_endpoint_params(Config) -> + assert_attribute_not_defined_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_token_endpoint_params). + +should_return_mgt_oauth_resource_rabbit_with_authorization_endpoint_params_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_authorization_endpoint_params, authorization_params_0). + +should_return_mgt_oauth_resource_rabbit_with_token_endpoint_params_0(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, rabbit, oauth_token_endpoint_params, token_params_0). + +should_return_mgt_oauth_resource_a_with_authorization_endpoint_params_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_authorization_endpoint_params, authorization_params_1). + +should_return_mgt_oauth_resource_a_with_token_endpoint_params_1(Config) -> + assertEqual_on_attribute_for_oauth_resource_server(authSettings(), + Config, a, oauth_token_endpoint_params, token_params_1). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% Utility/helper functions %% ------------------------------------------------------------------- @@ -677,16 +1193,26 @@ remove_entry_from_env_variable(Application, EnvVar, Key) -> Map = application:get_env(Application, EnvVar, #{}), NewMap = maps:remove(Key, Map), case maps:size(NewMap) of +<<<<<<< HEAD 0 -> application:unset_env(Application, EnvVar); _ -> application:set_env(Application, EnvVar, NewMap) +======= + 0 -> unset_env(Application, EnvVar); + _ -> set_env(Application, EnvVar, NewMap) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. remove_attribute_from_entry_from_env_variable(Application, EnvVar, Key, Attribute) -> Map = application:get_env(Application, EnvVar, #{}), Proplist = proplists:delete(Attribute, maps:get(Key, Map, [])), NewMap = delete_key_with_empty_proplist(Key, maps:put(Key, Proplist, Map)), case maps:size(NewMap) of +<<<<<<< HEAD 0 -> application:unset_env(Application, EnvVar); _ -> application:set_env(Application, EnvVar, NewMap) +======= + 0 -> unset_env(Application, EnvVar); + _ -> set_env(Application, EnvVar, NewMap) +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) end. assertEqual_on_attribute_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute, ConfigValue) -> @@ -699,6 +1225,15 @@ assertEqual_on_attribute_for_oauth_resource_server(Actual, Config, ConfigKey, At end, ?assertEqual(Value, proplists:get_value(Attribute, OauthResource)). +<<<<<<< HEAD +======= +assert_attribute_is_defined_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute) -> + log(Actual), + OAuthResourceServers = proplists:get_value(oauth_resource_servers, Actual), + OauthResource = maps:get(?config(ConfigKey, Config), OAuthResourceServers), + ?assertEqual(true, proplists:is_defined(Attribute, OauthResource)). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) assert_attribute_not_defined_for_oauth_resource_server(Actual, Config, ConfigKey, Attribute) -> log(Actual), OAuthResourceServers = proplists:get_value(oauth_resource_servers, Actual), @@ -715,7 +1250,11 @@ set_attribute_in_entry_for_env_variable(Application, EnvVar, Key, Attribute, Val ct:log("set_attribute_in_entry_for_env_variable before ~p", [Map]), Map1 = maps:put(Key, [ { Attribute, Value} | maps:get(Key, Map, []) ], Map), ct:log("set_attribute_in_entry_for_env_variable after ~p", [Map1]), +<<<<<<< HEAD application:set_env(Application, EnvVar, Map1). +======= + set_env(Application, EnvVar, Map1). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) log(AuthSettings) -> logEnvVars(), diff --git a/deps/rabbitmq_management_agent/src/rabbit_mgmt_ff.erl b/deps/rabbitmq_management_agent/src/rabbit_mgmt_ff.erl index 5022adc020b3..be8a2181b66d 100644 --- a/deps/rabbitmq_management_agent/src/rabbit_mgmt_ff.erl +++ b/deps/rabbitmq_management_agent/src/rabbit_mgmt_ff.erl @@ -10,6 +10,7 @@ -rabbit_feature_flag( {empty_basic_get_metric, #{desc => "Count AMQP `basic.get` on empty queues in stats", +<<<<<<< HEAD stability => required }}). @@ -18,3 +19,15 @@ #{desc => "Count unroutable publishes to be dropped in stats", stability => required }}). +======= + stability => required, + require_level => hard + }}). + +-rabbit_feature_flag( + {drop_unroutable_metric, + #{desc => "Count unroutable publishes to be dropped in stats", + stability => required, + require_level => hard + }}). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_mqtt/BUILD.bazel b/deps/rabbitmq_mqtt/BUILD.bazel index ca0c97809625..e474dc0f6a62 100644 --- a/deps/rabbitmq_mqtt/BUILD.bazel +++ b/deps/rabbitmq_mqtt/BUILD.bazel @@ -49,7 +49,12 @@ APP_ENV = """[ {mailbox_soft_limit, 200}, {max_packet_size_unauthenticated, 65536}, %% 256 MB is upper limit defined by MQTT spec +<<<<<<< HEAD {max_packet_size_authenticated, 268435455}, +======= + %% We set 16 MB as defined in deps/rabbit/Makefile max_message_size + {max_packet_size_authenticated, 16777216}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {topic_alias_maximum, 16} ] """ @@ -231,7 +236,11 @@ rabbitmq_integration_suite( ":test_util_beam", ":test_event_recorder_beam", ], +<<<<<<< HEAD shard_count = 10, +======= + shard_count = 5, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) runtime_deps = [ "//deps/rabbitmq_management_agent:erlang_app", "@emqtt//:erlang_app", @@ -246,7 +255,11 @@ rabbitmq_integration_suite( additional_beam = [ ":test_util_beam", ], +<<<<<<< HEAD shard_count = 4, +======= + shard_count = 2, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) runtime_deps = [ "@emqtt//:erlang_app", "@gun//:erlang_app", diff --git a/deps/rabbitmq_mqtt/Makefile b/deps/rabbitmq_mqtt/Makefile index c8ebda54547b..fcb827fd3cbd 100644 --- a/deps/rabbitmq_mqtt/Makefile +++ b/deps/rabbitmq_mqtt/Makefile @@ -27,7 +27,12 @@ define PROJECT_ENV {mailbox_soft_limit, 200}, {max_packet_size_unauthenticated, 65536}, %% 256 MB is upper limit defined by MQTT spec +<<<<<<< HEAD {max_packet_size_authenticated, 268435455}, +======= + %% We set 16 MB as defined in deps/rabbit/Makefile max_message_size + {max_packet_size_authenticated, 16777216}, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) {topic_alias_maximum, 16} ] endef diff --git a/deps/rabbitmq_mqtt/src/mc_mqtt.erl b/deps/rabbitmq_mqtt/src/mc_mqtt.erl index 27f4e9cf81e1..7d65e78e1a6f 100644 --- a/deps/rabbitmq_mqtt/src/mc_mqtt.erl +++ b/deps/rabbitmq_mqtt/src/mc_mqtt.erl @@ -14,6 +14,10 @@ init/1, size/1, x_header/2, +<<<<<<< HEAD +======= + x_headers/1, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property/2, routing_headers/2, convert_to/3, @@ -390,6 +394,14 @@ x_header(Key, #mqtt_msg{props = #{'User-Property' := UserProp}}) -> x_header(_Key, #mqtt_msg{}) -> undefined. +<<<<<<< HEAD +======= +x_headers(#mqtt_msg{props = #{'User-Property' := UserProp}}) -> + #{Key => {utf8, Val} || {<<"x-", _/binary>> = Key, Val} <- UserProp}; +x_headers(#mqtt_msg{}) -> + #{}. + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) property(correlation_id, #mqtt_msg{props = #{'Correlation-Data' := Corr}}) -> case mc_util:urn_string_to_uuid(Corr) of {ok, UUId} -> diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl index c5ea59abedea..d71cd89c56ab 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt.erl @@ -87,7 +87,12 @@ init_global_counters(ProtoVer) -> rabbit_global_counters:init([Proto]), rabbit_global_counters:init([Proto, {queue_type, rabbit_classic_queue}]), rabbit_global_counters:init([Proto, {queue_type, rabbit_quorum_queue}]), +<<<<<<< HEAD rabbit_global_counters:init([Proto, {queue_type, ?QUEUE_TYPE_QOS_0}]). +======= + rabbit_global_counters:init([Proto, {queue_type, ?QUEUE_TYPE_QOS_0}]), + rabbit_msg_size_metrics:init(ProtoVer). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) persist_static_configuration() -> rabbit_mqtt_util:init_sparkplug(), @@ -112,6 +117,11 @@ persist_static_configuration() -> {ok, MaxSizeAuth} = application:get_env(?APP_NAME, max_packet_size_authenticated), assert_valid_max_packet_size(MaxSizeAuth), +<<<<<<< HEAD +======= + {ok, MaxMsgSize} = application:get_env(rabbit, max_message_size), + ?assert(MaxSizeAuth =< MaxMsgSize), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ok = persistent_term:put(?PERSISTENT_TERM_MAX_PACKET_SIZE_AUTHENTICATED, MaxSizeAuth). assert_valid_max_packet_size(Val) -> diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl index 3b35c794af39..8e090201f47f 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_ff.erl @@ -16,13 +16,23 @@ -rabbit_feature_flag( {?QUEUE_TYPE_QOS_0, #{desc => "Support pseudo queue type for MQTT QoS 0 subscribers omitting a queue process", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). -rabbit_feature_flag( {delete_ra_cluster_mqtt_node, #{desc => "Delete Ra cluster 'mqtt_node' since MQTT client IDs are tracked locally", +<<<<<<< HEAD stability => required +======= + stability => required, + require_level => hard +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) }}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -38,6 +48,10 @@ {mqtt_v5, #{desc => "Support MQTT 5.0", stability => required, +<<<<<<< HEAD +======= + require_level => hard, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) depends_on => [ %% MQTT 5.0 feature Will Delay Interval depends on client ID tracking in pg local. delete_ra_cluster_mqtt_node, diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl index 147a4467207a..da7cffc9e33d 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_processor.erl @@ -397,6 +397,10 @@ process_request(?PUBLISH, {ok, Topic, Props, State1} -> EffectiveQos = maybe_downgrade_qos(Qos), rabbit_global_counters:messages_received(ProtoVer, 1), +<<<<<<< HEAD +======= + rabbit_msg_size_metrics:observe(ProtoVer, iolist_size(Payload)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) State = maybe_increment_publisher(State1), Msg = #mqtt_msg{retain = Retain, qos = EffectiveQos, diff --git a/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl b/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl index 54435849214a..de45cb2ec594 100644 --- a/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl +++ b/deps/rabbitmq_mqtt/src/rabbit_mqtt_reader.erl @@ -37,7 +37,11 @@ rabbit_mqtt_processor:state(), connection_state :: running | blocked, conserve :: boolean(), +<<<<<<< HEAD stats_timer :: option(rabbit_event:state()), +======= + stats_timer :: rabbit_event:state(), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) keepalive = rabbit_mqtt_keepalive:init() :: rabbit_mqtt_keepalive:state(), conn_name :: binary() }). @@ -87,9 +91,15 @@ init(Ref) -> await_recv = false, connection_state = running, conserve = false, +<<<<<<< HEAD parse_state = rabbit_mqtt_packet:init_state()}, State1 = control_throttle(State0), State = rabbit_event:init_stats_timer(State1, #state.stats_timer), +======= + parse_state = rabbit_mqtt_packet:init_state(), + stats_timer = rabbit_event:init_stats_timer()}, + State = control_throttle(State0), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) gen_server:enter_loop(?MODULE, [], State); {error, Reason = enotconn} -> ?LOG_INFO("MQTT could not get connection string: ~s", [Reason]), @@ -128,6 +138,7 @@ handle_cast({close_connection, Reason}, handle_cast(QueueEvent = {queue_event, _, _}, State = #state{proc_state = PState0}) -> +<<<<<<< HEAD try case rabbit_mqtt_processor:handle_queue_event(QueueEvent, PState0) of {ok, PState} -> @@ -135,6 +146,13 @@ handle_cast(QueueEvent = {queue_event, _, _}, {error, Reason0, PState} -> {stop, Reason0, pstate(State, PState)} end +======= + try rabbit_mqtt_processor:handle_queue_event(QueueEvent, PState0) of + {ok, PState} -> + maybe_process_deferred_recv(control_throttle(pstate(State, PState))); + {error, Reason0, PState} -> + {stop, Reason0, pstate(State, PState)} +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) catch throw:{send_failed, Reason1} -> network_error(Reason1, State) end; @@ -442,8 +460,11 @@ maybe_process_deferred_recv(State = #state{ deferred_recv = Data, socket = Sock handle_info({tcp, Sock, Data}, State#state{ deferred_recv = undefined }). +<<<<<<< HEAD maybe_emit_stats(#state{stats_timer = undefined}) -> ok; +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) maybe_emit_stats(State) -> rabbit_event:if_enabled(State, #state.stats_timer, fun() -> emit_stats(State) end). diff --git a/deps/rabbitmq_mqtt/test/java_SUITE_data/pom.xml b/deps/rabbitmq_mqtt/test/java_SUITE_data/pom.xml index 8a84a4b3b1b0..571bd6006091 100644 --- a/deps/rabbitmq_mqtt/test/java_SUITE_data/pom.xml +++ b/deps/rabbitmq_mqtt/test/java_SUITE_data/pom.xml @@ -15,11 +15,19 @@ [1.2.5,) [1.2.5,) +<<<<<<< HEAD 5.21.0 5.10.3 3.26.3 1.2.13 3.3.0 +======= + 5.23.0 + 5.11.3 + 3.26.3 + 1.2.13 + 3.5.2 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) 2.1.1 2.4.21 3.12.1 diff --git a/deps/rabbitmq_mqtt/test/mc_mqtt_SUITE.erl b/deps/rabbitmq_mqtt/test/mc_mqtt_SUITE.erl index 57e306ab857c..9579629a23eb 100644 --- a/deps/rabbitmq_mqtt/test/mc_mqtt_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/mc_mqtt_SUITE.erl @@ -62,6 +62,13 @@ roundtrip_amqp(_Config) -> PayloadSize = 10, ExpectedSize = {MetaDataSize, PayloadSize}, ?assertEqual(ExpectedSize, mc:size(Mc0)), +<<<<<<< HEAD +======= + ?assertEqual(#{<<"x-key-1">> => {utf8, <<"val-1">>}, + <<"x-key-2">> => {utf8, <<"val-2">>}, + <<"x-key-3">> => {utf8, <<"val-3">>}}, + mc:x_headers(Mc0)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) Env = #{}, ?assertEqual(Msg, mc_mqtt:convert_to(mc_mqtt, Msg, Env)), @@ -323,6 +330,10 @@ mqtt_amqpl_alt(_Config) -> }, Anns = #{?ANN_ROUTING_KEYS => [rabbit_mqtt_util:mqtt_to_amqp(Msg#mqtt_msg.topic)]}, Mc = mc:init(mc_mqtt, Msg, Anns), +<<<<<<< HEAD +======= + ?assertEqual(#{}, mc:x_headers(Mc)), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) MsgL = mc:convert(mc_amqpl, Mc), #content{properties = #'P_basic'{headers = HL} = Props} = diff --git a/deps/rabbitmq_mqtt/test/mqtt_shared_SUITE.erl b/deps/rabbitmq_mqtt/test/mqtt_shared_SUITE.erl index fab5013f5413..4abdce5da832 100644 --- a/deps/rabbitmq_mqtt/test/mqtt_shared_SUITE.erl +++ b/deps/rabbitmq_mqtt/test/mqtt_shared_SUITE.erl @@ -80,6 +80,10 @@ cluster_size_1_tests_v3() -> cluster_size_1_tests() -> [ global_counters %% must be the 1st test case +<<<<<<< HEAD +======= + ,message_size_metrics +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ,block_only_publisher ,many_qos1_messages ,session_expiry @@ -697,6 +701,37 @@ global_counters(Config) -> messages_unroutable_returned_total => 1}, get_global_counters(Config, ProtoVer))). +<<<<<<< HEAD +======= +message_size_metrics(Config) -> + Protocol = case ?config(mqtt_version, Config) of + v4 -> mqtt311; + v5 -> mqtt50 + end, + BucketsBefore = rpc(Config, rabbit_msg_size_metrics, raw_buckets, [Protocol]), + + Topic = ClientId = atom_to_binary(?FUNCTION_NAME), + C = connect(ClientId, Config), + {ok, _, [0]} = emqtt:subscribe(C, Topic, qos0), + Payload1B = <<255>>, + Payload500B = binary:copy(Payload1B, 500), + Payload5KB = binary:copy(Payload1B, 5_000), + Payload2MB = binary:copy(Payload1B, 2_000_000), + Payloads = [Payload2MB, Payload5KB, Payload500B, Payload1B, Payload500B], + [ok = emqtt:publish(C, Topic, P, qos0) || P <- Payloads], + ok = expect_publishes(C, Topic, Payloads), + + BucketsAfter = rpc(Config, rabbit_msg_size_metrics, raw_buckets, [Protocol]), + ?assertEqual( + [{100, 1}, + {1000, 2}, + {10_000, 1}, + {10_000_000, 1}], + rabbit_msg_size_metrics:diff_raw_buckets(BucketsAfter, BucketsBefore)), + + ok = emqtt:disconnect(C). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) pubsub(Config) -> Topic0 = <<"t/0">>, Topic1 = <<"t/1">>, diff --git a/deps/rabbitmq_prometheus/BUILD.bazel b/deps/rabbitmq_prometheus/BUILD.bazel index fc7195381644..be0eb354fc49 100644 --- a/deps/rabbitmq_prometheus/BUILD.bazel +++ b/deps/rabbitmq_prometheus/BUILD.bazel @@ -1,12 +1,19 @@ load("@rules_erlang//:eunit2.bzl", "eunit") load("@rules_erlang//:xref2.bzl", "xref") load("@rules_erlang//:dialyze.bzl", "dialyze", "plt") +<<<<<<< HEAD load("//:rabbitmq_home.bzl", "rabbitmq_home") load("//:rabbitmq_run.bzl", "rabbitmq_run") +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) load( "//:rabbitmq.bzl", "RABBITMQ_DIALYZER_OPTS", "assert_suites", +<<<<<<< HEAD +======= + "broker_for_integration_suites", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "rabbitmq_app", "rabbitmq_integration_suite", ) @@ -86,6 +93,7 @@ eunit( target = ":test_erlang_app", ) +<<<<<<< HEAD rabbitmq_home( name = "broker-for-tests-home", plugins = [ @@ -99,6 +107,9 @@ rabbitmq_run( name = "rabbitmq-for-tests-run", home = ":broker-for-tests-home", ) +======= +broker_for_integration_suites() +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) rabbitmq_integration_suite( name = "config_schema_SUITE", @@ -109,10 +120,13 @@ rabbitmq_integration_suite( name = "rabbit_prometheus_http_SUITE", size = "medium", flaky = True, +<<<<<<< HEAD runtime_deps = [ "//deps/rabbitmq_stream:erlang_app", "//deps/rabbitmq_stream_common:erlang_app", ], +======= +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ) assert_suites() diff --git a/deps/rabbitmq_prometheus/app.bzl b/deps/rabbitmq_prometheus/app.bzl index a77dcbb9bb09..511d7fe98f6e 100644 --- a/deps/rabbitmq_prometheus/app.bzl +++ b/deps/rabbitmq_prometheus/app.bzl @@ -14,6 +14,10 @@ def all_beam_files(name = "all_beam_files"): "src/collectors/prometheus_rabbitmq_core_metrics_collector.erl", "src/collectors/prometheus_rabbitmq_dynamic_collector.erl", "src/collectors/prometheus_rabbitmq_global_metrics_collector.erl", +<<<<<<< HEAD +======= + "src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_prometheus_app.erl", "src/rabbit_prometheus_dispatcher.erl", "src/rabbit_prometheus_handler.erl", @@ -44,6 +48,10 @@ def all_test_beam_files(name = "all_test_beam_files"): "src/collectors/prometheus_rabbitmq_core_metrics_collector.erl", "src/collectors/prometheus_rabbitmq_dynamic_collector.erl", "src/collectors/prometheus_rabbitmq_global_metrics_collector.erl", +<<<<<<< HEAD +======= + "src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_prometheus_app.erl", "src/rabbit_prometheus_dispatcher.erl", "src/rabbit_prometheus_handler.erl", @@ -85,6 +93,10 @@ def all_srcs(name = "all_srcs"): "src/collectors/prometheus_rabbitmq_core_metrics_collector.erl", "src/collectors/prometheus_rabbitmq_dynamic_collector.erl", "src/collectors/prometheus_rabbitmq_global_metrics_collector.erl", +<<<<<<< HEAD +======= + "src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl", +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) "src/rabbit_prometheus_app.erl", "src/rabbit_prometheus_dispatcher.erl", "src/rabbit_prometheus_handler.erl", diff --git a/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_global_metrics_collector.erl b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_global_metrics_collector.erl index af2073737724..2a968ea56b8f 100644 --- a/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_global_metrics_collector.erl +++ b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_global_metrics_collector.erl @@ -29,6 +29,7 @@ register() -> ok = prometheus_registry:register_collector(?MODULE). +<<<<<<< HEAD deregister_cleanup(_) -> ok. collect_mf(_Registry, Callback) -> @@ -48,3 +49,18 @@ collect_mf(_Registry, Callback) -> %% =================================================================== %% Private functions %% =================================================================== +======= +deregister_cleanup(_) -> + ok. + +collect_mf(_Registry, Callback) -> + maps:foreach( + fun(Name, #{type := Type, help := Help, values := Values}) -> + Callback( + create_mf(?METRIC_NAME(Name), + Help, + Type, + maps:to_list(Values))) + end, + rabbit_global_counters:prometheus_format()). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) diff --git a/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl new file mode 100644 index 000000000000..54a349547744 --- /dev/null +++ b/deps/rabbitmq_prometheus/src/collectors/prometheus_rabbitmq_message_size_metrics_collector.erl @@ -0,0 +1,33 @@ +%% This Source Code Form is subject to the terms of the Mozilla Public +%% License, v. 2.0. If a copy of the MPL was not distributed with this +%% file, You can obtain one at https://mozilla.org/MPL/2.0/. +%% +%% Copyright (c) 2007-2024 Broadcom. All Rights Reserved. The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. All rights reserved. +%% +-module(prometheus_rabbitmq_message_size_metrics_collector). + +-behaviour(prometheus_collector). +-include_lib("prometheus/include/prometheus.hrl"). + +-export([register/0, + deregister_cleanup/1, + collect_mf/2]). + +-define(METRIC_NAME_PREFIX, "rabbitmq_"). + +register() -> + ok = prometheus_registry:register_collector(?MODULE). + +deregister_cleanup(_) -> + ok. + +collect_mf(_Registry, Callback) -> + maps:foreach( + fun(Name, #{type := Type, + help := Help, + values := Values}) -> + MetricsFamily = prometheus_model_helpers:create_mf( + ?METRIC_NAME(Name), Help, Type, Values), + Callback(MetricsFamily) + end, + rabbit_msg_size_metrics:prometheus_format()). diff --git a/deps/rabbitmq_prometheus/src/rabbit_prometheus_dispatcher.erl b/deps/rabbitmq_prometheus/src/rabbit_prometheus_dispatcher.erl index 850494e00666..111d13d0e350 100644 --- a/deps/rabbitmq_prometheus/src/rabbit_prometheus_dispatcher.erl +++ b/deps/rabbitmq_prometheus/src/rabbit_prometheus_dispatcher.erl @@ -16,6 +16,10 @@ build_dispatcher() -> prometheus_registry:register_collectors([ prometheus_rabbitmq_core_metrics_collector, prometheus_rabbitmq_global_metrics_collector, +<<<<<<< HEAD +======= + prometheus_rabbitmq_message_size_metrics_collector, +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) prometheus_rabbitmq_alarm_metrics_collector, prometheus_rabbitmq_dynamic_collector, prometheus_process_collector]), @@ -27,7 +31,12 @@ build_dispatcher() -> prometheus_vm_statistics_collector, prometheus_vm_msacc_collector, prometheus_rabbitmq_core_metrics_collector, +<<<<<<< HEAD prometheus_rabbitmq_global_metrics_collector +======= + prometheus_rabbitmq_global_metrics_collector, + prometheus_rabbitmq_message_size_metrics_collector +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]), prometheus_registry:register_collectors('detailed', [ prometheus_rabbitmq_core_metrics_collector diff --git a/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl index 4aaf622bdcd4..19257dd717e1 100644 --- a/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl +++ b/deps/rabbitmq_prometheus/test/rabbit_prometheus_http_SUITE.erl @@ -39,13 +39,23 @@ groups() -> aggregated_metrics_test, specific_erlang_metrics_present_test, global_metrics_present_test, +<<<<<<< HEAD global_metrics_single_metric_family_test +======= + global_metrics_single_metric_family_test, + message_size_metrics_present +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {per_object_metrics, [], [ globally_configure_per_object_metrics_test, specific_erlang_metrics_present_test, global_metrics_present_test, +<<<<<<< HEAD global_metrics_single_metric_family_test +======= + global_metrics_single_metric_family_test, + message_size_metrics_present +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ]}, {per_object_endpoint_metrics, [], [ endpoint_per_object_metrics, @@ -492,6 +502,38 @@ global_metrics_present_test(Config) -> ?assertEqual(match, re:run(Body, "^rabbitmq_global_publishers{", [{capture, none}, multiline])), ?assertEqual(match, re:run(Body, "^rabbitmq_global_consumers{", [{capture, none}, multiline])). +<<<<<<< HEAD +======= +message_size_metrics_present(Config) -> + {_Headers, Body} = http_get_with_pal(Config, [], 200), + + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"100\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"1000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"10000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"100000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"1000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"10000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"10000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"50000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"100000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp091\",le=\"\\+Inf\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_count{protocol=\"amqp091\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_sum{protocol=\"amqp091\"}", [{capture, none}, multiline])), + + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"100\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"1000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"10000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"100000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"1000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"10000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"10000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"50000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"100000000\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_bucket{protocol=\"amqp10\",le=\"\\+Inf\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_count{protocol=\"amqp10\"}", [{capture, none}, multiline])), + ?assertEqual(match, re:run(Body, "^rabbitmq_message_size_bytes_sum{protocol=\"amqp10\"}", [{capture, none}, multiline])). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) global_metrics_single_metric_family_test(Config) -> {_Headers, Body} = http_get_with_pal(Config, [], 200), {match, MetricFamilyMatches} = re:run(Body, "TYPE rabbitmq_global_messages_acknowledged_total", [global]), diff --git a/deps/rabbitmq_stream/test/protocol_interop_SUITE.erl b/deps/rabbitmq_stream/test/protocol_interop_SUITE.erl index 17d28e2f93d6..db4ad6d9cacd 100644 --- a/deps/rabbitmq_stream/test/protocol_interop_SUITE.erl +++ b/deps/rabbitmq_stream/test/protocol_interop_SUITE.erl @@ -14,6 +14,10 @@ -include_lib("eunit/include/eunit.hrl"). -include_lib("amqp_client/include/amqp_client.hrl"). -include_lib("amqp10_common/include/amqp10_framing.hrl"). +<<<<<<< HEAD +======= +-include_lib("amqp10_common/include/amqp10_filtex.hrl"). +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) all() -> [{group, tests}]. @@ -24,7 +28,12 @@ groups() -> amqpl, amqp_credit_multiple_grants, amqp_credit_single_grant, +<<<<<<< HEAD amqp_attach_sub_batch +======= + amqp_attach_sub_batch, + amqp_filter_expression +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) ] }]. @@ -270,6 +279,54 @@ amqp_attach_sub_batch(Config) -> ok = amqp10_client:detach_link(Receiver), ok = amqp10_client:close_connection(Connection). +<<<<<<< HEAD +======= +%% Test that AMQP filter expressions work when messages +%% are published via the stream protocol and consumed via AMQP. +amqp_filter_expression(Config) -> + Stream = atom_to_binary(?FUNCTION_NAME), + publish_via_stream_protocol(Stream, Config), + + %% Consume from the stream via AMQP 1.0. + OpnConf = connection_config(Config), + {ok, Connection} = amqp10_client:open_connection(OpnConf), + {ok, Session} = amqp10_client:begin_session_sync(Connection), + Address = <<"/queue/", Stream/binary>>, + + AppPropsFilter = [{{utf8, <<"my key">>}, + {utf8, <<"my value">>}}], + {ok, Receiver} = amqp10_client:attach_receiver_link( + Session, <<"test-receiver">>, Address, settled, configuration, + #{<<"rabbitmq:stream-offset-spec">> => <<"first">>, + ?DESCRIPTOR_NAME_APPLICATION_PROPERTIES_FILTER => {map, AppPropsFilter} + }), + + ok = amqp10_client:flow_link_credit(Receiver, 100, never), + receive {amqp10_msg, Receiver, M2} -> + ?assertEqual([<<"m2">>], amqp10_msg:body(M2)) + after 5000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, Receiver, M4} -> + ?assertEqual([<<"m4">>], amqp10_msg:body(M4)) + after 5000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, Receiver, M5} -> + ?assertEqual([<<"m5">>], amqp10_msg:body(M5)) + after 5000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, Receiver, M6} -> + ?assertEqual([<<"m6">>], amqp10_msg:body(M6)) + after 5000 -> ct:fail({missing_msg, ?LINE}) + end, + receive {amqp10_msg, _, _} = Msg -> + ct:fail({received_unexpected_msg, Msg}) + after 10 -> ok + end, + + ok = amqp10_client:detach_link(Receiver), + ok = amqp10_client:close_connection(Connection). + +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) %% ------------------------------------------------------------------- %% Helpers %% ------------------------------------------------------------------- @@ -283,7 +340,13 @@ publish_via_stream_protocol(Stream, Config) -> {ok, C2} = stream_test_utils:declare_publisher(S, C1, Stream, PublisherId), M1 = stream_test_utils:simple_entry(1, <<"m1">>), +<<<<<<< HEAD M2 = stream_test_utils:simple_entry(2, <<"m2">>), +======= + M2 = stream_test_utils:simple_entry(2, <<"m2">>, #'v1_0.application_properties'{ + content = [{{utf8, <<"my key">>}, + {utf8, <<"my value">>}}]}), +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) M3 = stream_test_utils:simple_entry(3, <<"m3">>), Messages1 = [M1, M2, M3], diff --git a/deps/rabbitmq_stream/test/rabbit_stream_SUITE_data/pom.xml b/deps/rabbitmq_stream/test/rabbit_stream_SUITE_data/pom.xml index 6add40e5c989..439355507bb9 100644 --- a/deps/rabbitmq_stream/test/rabbit_stream_SUITE_data/pom.xml +++ b/deps/rabbitmq_stream/test/rabbit_stream_SUITE_data/pom.xml @@ -27,11 +27,19 @@ [0.12.0-SNAPSHOT,) +<<<<<<< HEAD 5.10.3 3.26.3 1.2.13 3.12.1 3.3.0 +======= + 5.11.3 + 3.26.3 + 1.2.13 + 3.12.1 + 3.5.2 +>>>>>>> f3540ee7d2 (web_mqtt_shared_SUITE: propagate flow_classic_queue to mqtt_shared_SUITE #12907 12906) 2.43.0 1.17.0 UTF-8 diff --git a/deps/rabbitmq_stream_management/priv/www/js/tmpl/streamConnection.ejs b/deps/rabbitmq_stream_management/priv/www/js/tmpl/streamConnection.ejs index 571293bf4837..cde213b815f6 100644 --- a/deps/rabbitmq_stream_management/priv/www/js/tmpl/streamConnection.ejs +++ b/deps/rabbitmq_stream_management/priv/www/js/tmpl/streamConnection.ejs @@ -17,7 +17,11 @@ <% if (connection.client_properties.connection_name) { %>
Client-provided nameClient-provided connection name<%= fmt_string(connection.client_properties.connection_name) %>