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:
+%%
+%%
feature flags enabled on the local node must be supported by the
+%% remote node
+%%
feature flags enabled on the remote node must be supported by the
+%% local node
+%%
+%%
+%% 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.bootspring-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)
1717
+<<<<<<< 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.bootspring-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-81717
+<<<<<<< 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)