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..5d7c10256381 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"
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..5b2ab7e16e9d 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..7441ed42a82e 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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..6013f28a6abb 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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:}"
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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 }}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..56cf659a0f7c 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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..bd2d214f6d8c 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/**'
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- '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/**'
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- .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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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..cf2adb9ad56d 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"
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- name: SETUP DOTNET (rabbit)
uses: actions/setup-dotnet@v4
if: inputs.plugin == 'rabbit'
with:
+<<<<<<< HEAD
dotnet-version: '3.1.x'
+=======
+ dotnet-version: '8.0'
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- name: RUN TESTS
if: inputs.plugin != 'rabbitmq_cli'
diff --git a/.github/workflows/test-make.yaml b/.github/workflows/test-make.yaml
index 60d61dad9fc4..6d43940ab7eb 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
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..f116109f6db9 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- name: Upload Test Artifacts
if: always()
diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml
index 53902913d8c0..422bc7b58f27 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
/tmp/short-suite
summary-selenium:
diff --git a/.github/workflows/test-mixed-versions.yaml b/.github/workflows/test-mixed-versions.yaml
index bf2ed9ae2fdb..e33d2e2f6af7 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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..a89aaeecffdb 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
include:
- erlang_version: 26
elixir_version: 1.17
diff --git a/.github/workflows/test-plugin.yaml b/.github/workflows/test-plugin.yaml
index 3c163ba059a3..1541ceb879c9 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
+=======
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
cat << EOF >> user.bazelrc
build --strategy=TestRunner=local
EOF
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 6c3c003670ef..01c1108940fb 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -2,6 +2,10 @@ name: Test
on:
push:
branches:
+<<<<<<< HEAD
+=======
+ - v4.0.x
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
- 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..a04024054104 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
### What is Broadcom's Obligation to Reply to Messages or Issues Reported?
diff --git a/MODULE.bazel b/MODULE.bazel
index 6a0a57e07196..cbc28c9a371a 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
hex = use_extension(
diff --git a/Makefile b/Makefile
index 2e4fe88c9f7a..d000d13c2f41 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 \
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
rabbitmq_auth_backend_oauth2 \
rabbitmq_auth_mechanism_ssl \
rabbitmq_aws \
diff --git a/bazel/BUILD.horus b/bazel/BUILD.horus
index 0f7e2369acbb..9a5891ab2d54 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
],
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
],
)
diff --git a/bazel/BUILD.redbug b/bazel/BUILD.redbug
index 06c43294b29e..89dccc564f59 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")
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
filegroup(name = "public_hdrs")
diff --git a/bazel/bzlmod/secondary_umbrella.bzl b/bazel/bzlmod/secondary_umbrella.bzl
index 613961d5a616..0b085d8e5709 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
],
)
diff --git a/deps/amqp10_client/BUILD.bazel b/deps/amqp10_client/BUILD.bazel
index df8b879adae1..e3b0e0b8ab7c 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"@credentials_obfuscation//:erlang_app",
],
)
diff --git a/deps/amqp10_client/app.bzl b/deps/amqp10_client/app.bzl
index 8fcdad73cf9d..cda6a268bb6a 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"],
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..4fc1bc909045 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};
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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};
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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..2fb0c7357342 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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ = 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..4f06e99ab1b6 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..5e0fc7b58157 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{keep_state,
State#state{links = maps:remove(OutHandle, Links),
link_handle_index = maps:remove(InHandle, LHI)}}
@@ -547,9 +579,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{stop, normal, State};
end_sent(_EvtType, _Frame, _State) ->
% just drop frames here
@@ -732,6 +769,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 +779,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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% special case conversion
Key = sym(K),
[{Key, {described, Key, translate_legacy_amqp_headers_binding(V)}} | Acc];
@@ -986,10 +1033,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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
notify_link(Link, {detached, Reason}).
notify_link(#link{notify = Pid, ref = Ref}, What) ->
@@ -997,11 +1065,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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
Pid ! amqp10_session_event({ended, Reason}),
ok.
@@ -1166,11 +1257,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]
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end, [], MA)};
translate_message_annotations(_MA) ->
undefined.
+<<<<<<< HEAD
wrap_map_value(true) ->
{boolean, true};
wrap_map_value(false) ->
@@ -1193,6 +1289,8 @@ wrap_map_value(TaggedValue) when is_atom(element(1, TaggedValue)) ->
utf8(V) -> amqp10_client_types:utf8(V).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..9524bcc4066e 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..02ee7750ceb7 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]).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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)}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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().
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..f91240a9c52d 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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
suite() ->
[{timetrap, {minutes, 4}}].
@@ -30,7 +37,11 @@ all() ->
groups() ->
[
+<<<<<<< HEAD
{rabbitmq, [], shared()},
+=======
+ {rabbitmq, [], shared() ++ [notify_with_performative]},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
% 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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}}]}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
exit(incoming_heartbeat_assert)
end,
demonitor(MockRef).
+<<<<<<< HEAD
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%% 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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
LastReceivedMsg = case Acc of
[] -> none;
[M | _] -> M
diff --git a/deps/amqp10_common/app.bzl b/deps/amqp10_common/app.bzl
index a233c945cebe..db833f9b83af 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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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..b0717c6c2970 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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..04cde8b2845c 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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..88fdb9971ce0 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
% 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{}.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..78ecc39ef5e8 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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)}]}];
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, [])))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}.
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 }", [
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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])).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl
index ecc5aa733bef..4d680fe3c43f 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
+]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]},
{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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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()} ]}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
| 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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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"]),
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[{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"]),
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[{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)]),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[ {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 =
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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],
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ ->
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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();
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ ->
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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")),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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")),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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() ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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]),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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();
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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]),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..e1e9f290e3f6 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]).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
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} ]},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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} ]},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
no_ssl_options_triggers_verify_peer(_) ->
diff --git a/deps/rabbit/BUILD.bazel b/deps/rabbit/BUILD.bazel
index 1b5d65cd4fed..673db0f4a415 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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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",
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
rabbitmq_integration_suite(
@@ -977,6 +996,14 @@ rabbitmq_integration_suite(
)
rabbitmq_suite(
+<<<<<<< HEAD
+=======
+ name = "unit_msg_size_metrics_SUITE",
+ size = "small",
+)
+
+rabbitmq_suite(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
":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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
":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",
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
],
target = ":test_erlang_app",
test_env = {
diff --git a/deps/rabbit/Makefile b/deps/rabbit/Makefile
index 79ad84990739..70f699be4a7b 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..7447f99b7f9b 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"],
+ )
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit/ct.test.spec b/deps/rabbit/ct.test.spec
index 6740594c1500..4510f87d87f4 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
, 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
, 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]}.
{define, 'Set5', [
diff --git a/deps/rabbit/include/rabbit_amqp.hrl b/deps/rabbit/include/rabbit_amqp.hrl
index 84e98d5d565d..ea46f517146f 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
vhost,
user,
node
diff --git a/deps/rabbit/src/mc.erl b/deps/rabbit/src/mc.erl
index b122c4780110..e4008f8f3652 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()]} |
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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()}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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().
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..add9609ffd30 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[{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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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))).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit/src/mc_amqpl.erl b/deps/rabbit/src/mc_amqpl.erl
index 723e60cd3f79..0215dc240fec 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
true ->
{utf8, V};
false ->
{binary, V}
end;
+<<<<<<< HEAD
from_091(longstr, V) -> {binary, V};
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..3c7af89194c8 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..b1bc5327cf89 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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..616f129e690e 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()} |
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{'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..5687c26c7b76
--- /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..64c536fcb3da 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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..8792f8797a74 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-include("rabbit_amqp.hrl").
-export([init/1,
info/2,
+<<<<<<< HEAD
mainloop/2]).
+=======
+ mainloop/2,
+ set_credential/2]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-export([system_continue/3,
system_terminate/4,
@@ -35,6 +47,10 @@
-record(v1_connection,
{name :: binary(),
+<<<<<<< HEAD
+=======
+ container_id :: none | binary(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}).
-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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%--------------------------------------------------------------------------
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)])
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[] ->
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..90c1b306e04a 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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"
+ }}).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]).
-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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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.)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
queue_name = queue_resource(Vhost, QNameBin),
queue_type = QType,
send_settled = SndSettled,
@@ -2126,9 +2262,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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
State
end.
@@ -2348,16 +2490,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 +2548,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} ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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} ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% https://docs.oasis-open.org/amqp/anonterm/v1.0/cs01/anonterm-v1.0-cs01.html#doc-routingerrors
case Settled of
true ->
@@ -2424,6 +2588,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 +2596,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)}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end;
lookup_target(to, to, Mc, Vhost, User, PermCache0) ->
case mc:property(to, Mc) of
@@ -2442,25 +2616,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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
RKey = case RKey0 of
subject ->
case mc:property(subject, Mc0) of
@@ -2472,9 +2664,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}}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
process_routing_confirm([], _SenderSettles = true, _, U) ->
rabbit_global_counters:messages_unroutable_dropped(?PROTOCOL, 1),
@@ -2611,6 +2831,14 @@ ensure_source_v1(Address,
Err
end.
+<<<<<<< HEAD
+=======
+address(undefined) ->
+ null;
+address({utf8, String}) ->
+ String.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-spec ensure_target(#'v1_0.target'{},
rabbit_types:vhost(),
rabbit_types:user(),
@@ -2710,11 +2938,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
Cp
end,
case binary:split(Rest, Pattern, [global]) of
@@ -2949,7 +3184,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
protocol_error(?V_1_0_AMQP_ERROR_INTERNAL_ERROR, Fmt, Args).
default(undefined, Default) -> Default;
@@ -2985,6 +3224,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 +3232,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}) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 +3312,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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end.
validate_attach(#'v1_0.attach'{target = #'v1_0.coordinator'{}}) ->
@@ -3134,9 +3456,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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
case MsgSize =< MaxMsgSize of
true ->
ok;
@@ -3150,7 +3477,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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-spec ensure_terminus(source | target,
term(),
@@ -3427,6 +3760,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 +3769,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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-spec check_read_permitted_on_topic(
rabbit_types:exchange(),
@@ -3474,6 +3824,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}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
check_user_id(Mc, User) ->
case rabbit_access_control:check_user_id(Mc, User) of
ok ->
@@ -3610,6 +3986,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}].
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..1651c0bb3347 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit/src/rabbit_amqp_writer.erl b/deps/rabbit/src/rabbit_amqp_writer.erl
index 7b239a10a107..57454fa3e11a 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()
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}).
-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()},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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..2ad5098b0b9a 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().
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
notify_transient_queues_deleted(QueueDeletions) ->
lists:foreach(
diff --git a/deps/rabbit/src/rabbit_amqqueue_process.erl b/deps/rabbit/src/rabbit_amqqueue_process.erl
index 63f886bd3763..1e462e65a2ec 100644
--- a/deps/rabbit/src/rabbit_amqqueue_process.erl
+++ b/deps/rabbit/src/rabbit_amqqueue_process.erl
@@ -725,6 +725,7 @@ maybe_deliver_or_enqueue(Delivery = #delivery{message = Message},
{IsDuplicate, BQS1} = BQ:is_duplicate(Message, BQS),
State1 = State#q{backing_queue_state = BQS1},
case IsDuplicate of
+<<<<<<< HEAD
true -> State1;
{true, drop} -> State1;
%% Drop publish and nack to publisher
@@ -732,6 +733,28 @@ maybe_deliver_or_enqueue(Delivery = #delivery{message = Message},
send_reject_publish(Delivery, State1);
%% Enqueue and maybe drop head later
false ->
+=======
+ true ->
+ %% Publish to DLX
+ _ = with_dlx(
+ DLX,
+ fun (X) ->
+ rabbit_global_counters:messages_dead_lettered(maxlen,
+ rabbit_classic_queue,
+ at_most_once, 1),
+ QName = qname(State1),
+ rabbit_dead_letter:publish(Message, maxlen, X, RK, QName)
+ end,
+ fun () ->
+ rabbit_global_counters:messages_dead_lettered(maxlen,
+ rabbit_classic_queue,
+ disabled, 1)
+ end),
+ %% Drop publish and nack to publisher
+ send_reject_publish(Delivery, State1);
+ false ->
+ %% Enqueue and maybe drop head later
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
deliver_or_enqueue(Delivery, Delivered, State1)
end
end.
diff --git a/deps/rabbit/src/rabbit_backing_queue.erl b/deps/rabbit/src/rabbit_backing_queue.erl
index ffa0a791f1b5..3169cbf87bfd 100644
--- a/deps/rabbit/src/rabbit_backing_queue.erl
+++ b/deps/rabbit/src/rabbit_backing_queue.erl
@@ -220,9 +220,8 @@
%% Called prior to a publish or publish_delivered call. Allows the BQ
%% to signal that it's already seen this message, (e.g. it was published
-%% or discarded previously) specifying whether to drop the message or reject it.
--callback is_duplicate(mc:state(), state())
- -> {{true, drop} | {true, reject} | boolean(), state()}.
+%% or discarded previously).
+-callback is_duplicate(mc:state(), state()) -> {boolean(), state()}.
-callback set_queue_mode(queue_mode(), state()) -> state().
diff --git a/deps/rabbit/src/rabbit_binding.erl b/deps/rabbit/src/rabbit_binding.erl
index cf7f79b51e6a..f3aa4cdd195f 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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{}}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%----------------------------------------------------------------------------
@@ -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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end.
diff --git a/deps/rabbit/src/rabbit_channel.erl b/deps/rabbit/src/rabbit_channel.erl
index 303776471396..3e11b0ba1a39 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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..866d4ba581f2 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-rabbit_feature_flag(
{virtual_host_metadata,
#{desc => "Virtual host metadata (description, tags, etc)",
+<<<<<<< HEAD
stability => required
+=======
+ stability => required,
+ require_level => hard
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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]
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-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]
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}}).
-rabbit_feature_flag(
{tracking_records_in_ets,
#{desc => "Store tracking records in ETS instead of Mnesia",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
depends_on => [stream_single_active_consumer]
}}).
@@ -117,6 +194,10 @@
{stream_filtering,
#{desc => "Support for stream filtering.",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
depends_on => [stream_queue]
}}).
@@ -124,14 +205,25 @@
{message_containers,
#{desc => "Message containers.",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
depends_on => [stream_queue]
}}).
diff --git a/deps/rabbit/src/rabbit_db_binding.erl b/deps/rabbit/src/rabbit_db_binding.erl
index 37bc82ba246c..a87c78bbb778 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% -------------------------------------------------------------------
%% 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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..5be0c30dabbe 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..19e5af21bb73 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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..1bf8561355cf 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”
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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..942a2eea3817 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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..613b54de9914 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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..b261737614b4 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% @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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
`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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% -------------------------------------------------------------------
%% Feature flags registry.
%% -------------------------------------------------------------------
@@ -911,6 +1088,11 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
ValidProps = [desc,
doc_url,
stability,
+<<<<<<< HEAD
+=======
+ require_level,
+ experiment_level,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% @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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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();
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..3958b8e9b96c 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
[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();
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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} ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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, []} ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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()]}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% @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()]}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% @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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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}),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ ->
?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, []) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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..398ca0f018a9 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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()]} |
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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, #{})),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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};
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
disabled ->
{"Disabled", Yellow};
unavailable ->
diff --git a/deps/rabbit/src/rabbit_ff_registry.erl b/deps/rabbit/src/rabbit_ff_registry.erl
index 864ff564dc64..14f19f0f2f21 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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..632171f5584b 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
-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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#{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..76fe00bf6c2a 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% @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..f31646f31347 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}])
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..f8d457acbded 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}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end.
remote_node_info(Node) ->
diff --git a/deps/rabbit/src/rabbit_mnesia.erl b/deps/rabbit/src/rabbit_mnesia.erl
index 0aa4ae5360b5..3ae68c9a17e8 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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..1ba844b2db8f 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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..a35a9f945c13 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}})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..3dcbbec5c7fe 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, []).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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..28fc20cb1dac 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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..902538686ffa 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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]).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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;
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..074b87c98d8c 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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..d12bc1f58065 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit/test/amqp_auth_SUITE.erl b/deps/rabbit/test/amqp_auth_SUITE.erl
index 920f779172d4..3805b43581c0 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit/test/amqp_client_SUITE.erl b/deps/rabbit/test/amqp_client_SUITE.erl
index 09916c55c356..60ef6b2b1dae 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
all() ->
[
@@ -101,12 +115,21 @@ groups() ->
max_message_size_client_to_server,
max_message_size_server_to_client,
global_counters,
+<<<<<<< HEAD
stream_filtering,
+=======
+ stream_bloom_filter,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
available_messages_classic_queue,
available_messages_quorum_queue,
available_messages_stream,
incoming_message_interceptors,
+<<<<<<< HEAD
trace,
+=======
+ trace_classic_queue,
+ trace_stream,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
user_id,
message_ttl,
plugin,
@@ -146,7 +169,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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]},
{cluster_size_3, [shuffle],
@@ -287,12 +319,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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
init_per_testcase(T = immutable_bare_message, Config) ->
case rpc(Config, rabbit_feature_flags, is_enabled, ['rabbitmq_4.0.0']) of
true ->
@@ -317,6 +361,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 +382,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);
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
init_per_testcase(Testcase, Config) ->
rabbit_ct_helpers:testcase_started(Config, Testcase).
@@ -1170,7 +1217,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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
Reason = delivery_timeout,
flush(Reason),
ct:fail(Reason)
@@ -1257,7 +1308,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})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
receive {amqp10_event, {link, Receiver, credit_exhausted}} -> ok
after 300 -> ct:fail("expected credit_exhausted")
@@ -1307,7 +1362,11 @@ amqp_amqpl(QType, Config) ->
ok = amqp10_client:send_msg(
Sender,
amqp10_msg:set_application_properties(
+<<<<<<< HEAD
#{"my int" => -2},
+=======
+ #{"my int" => {int, -2}},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
amqp10_msg:new(<<>>, Body1, true))),
%% Send with properties
CorrelationID = <<"my correlation ID">>,
@@ -1322,7 +1381,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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
amqp10_msg:new(<<>>, Body1, true)))),
%% Send with footer
Footer = #'v1_0.footer'{content = [{{symbol, <<"x-my footer">>}, {ubyte, 255}}]},
@@ -1411,7 +1474,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">>))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
after 30000 -> ct:fail({missing_deliver, ?LINE})
end,
receive {_, #amqp_msg{payload = Payload10}} ->
@@ -1459,12 +1526,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},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"bool" => false},
OutMsg1),
OutMsg3 = amqp10_msg:set_message_annotations(
#{"x-string" => "string-value",
+<<<<<<< HEAD
"x-int" => 3,
+=======
+ "x-long" => -3,
+ "x-uint" => {uint, 3},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"x-bool" => true},
OutMsg2),
OutMsg = amqp10_msg:set_headers(
@@ -1484,11 +1561,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">>)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?assertEqual({bool, true}, rabbit_misc:table_lookup(Headers, <<"x-bool">>)),
%% assert headers
?assertEqual(2, DeliveryMode),
@@ -1888,18 +1975,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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
Node = get_node_config(Config, 0, nodename),
ConnectionCreatedProps = [Protocol,
{node, Node},
{vhost, <<"/">>},
{user, <<"guest">>},
+<<<<<<< HEAD
+=======
+ {container_id, <<"my container">>},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{type, network}],
{value, ConnectionCreatedEvent} = find_event(
connection_created,
@@ -1920,8 +2020,13 @@ events(Config) ->
Pid,
ClientProperties],
?assertMatch(
+<<<<<<< HEAD
{value, _},
find_event(connection_closed, ConnectionClosedProps, Events)),
+=======
+ {value, _},
+ find_event(connection_closed, ConnectionClosedProps, Events)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
ok.
sync_get_unsettled_classic_queue(Config) ->
@@ -3011,7 +3116,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{ok, Receiver1} = amqp10_client:attach_receiver_link(Session1, <<"receiver 1">>, Address, unsettled),
receive {amqp10_event, {link, Receiver1, attached}} -> ok
@@ -3019,7 +3128,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
NumMsgs = 6,
[begin
@@ -3409,7 +3522,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})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
ok = amqp10_client:detach_link(SenderClassicQ),
@@ -3458,7 +3575,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
RaName = ra_name(QuorumQ),
ServerId0 = {RaName, N0},
{ok, Members, _Leader} = ra:members(ServerId0),
@@ -3484,7 +3605,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})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
ok = amqp10_client:detach_link(Sender),
@@ -3527,7 +3652,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})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
%% However the server should not close links that send to classic queues that are down.
receive Unexpected -> ct:fail({unexpected, Unexpected})
@@ -3567,6 +3696,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 +3705,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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
async_notify_unsettled_quorum_queue(Config) ->
async_notify(unsettled, <<"quorum">>, Config).
@@ -3876,7 +4013,10 @@ leader_transfer_credit(QName, QType, Credit, Config) ->
ok = end_session_sync(Session1),
ok = close_connection_sync(Connection1),
+<<<<<<< HEAD
%% Consume from a follower.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
OpnConf = connection_config(0, Config),
{ok, Connection0} = amqp10_client:open_connection(OpnConf),
{ok, Session0} = amqp10_client:begin_session_sync(Connection0),
@@ -3890,6 +4030,10 @@ leader_transfer_credit(QName, QType, Credit, Config) ->
ok = wait_for_accepts(NumMsgs),
ok = detach_link_sync(Sender),
+<<<<<<< HEAD
+=======
+ %% Consume from a follower.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
ok = wait_for_local_member(QType, QName, Config),
Filter = consume_from_first(QType),
{ok, Receiver} = amqp10_client:attach_receiver_link(
@@ -3958,8 +4102,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
receive {amqp10_event, {connection, C0, opened}} -> ok
after 30000 -> ct:fail({missing_event, ?LINE})
end,
@@ -3967,8 +4120,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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% Remove any whitespaces.
Protocols1 = [binary:replace(Subject, <<" ">>, <<>>, [global]) || Subject <- Protocols0],
Protocols = lists:sort(Protocols1),
@@ -3977,6 +4135,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),
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
ok = rabbit_ct_client_helpers:close_connection(Connection091),
ok = close_connection_sync(C0),
ok = close_connection_sync(C2).
@@ -4129,7 +4297,11 @@ global_counters(Config) ->
ok = end_session_sync(Session),
ok = amqp10_client:close_connection(Connection).
+<<<<<<< HEAD
stream_filtering(Config) ->
+=======
+stream_bloom_filter(Config) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
Stream = atom_to_binary(?FUNCTION_NAME),
Address = rabbitmq_amqp_address:queue(Stream),
Ch = rabbit_ct_client_helpers:open_channel(Config),
@@ -4412,16 +4584,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">>,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'queue.bind_ok'{} = amqp_channel:call(
Ch, #'queue.bind'{queue = TraceQ,
exchange = <<"amq.rabbitmq.trace">>,
@@ -4439,16 +4632,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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
ok = amqp10_client:send_msg(Sender, Msg),
{ok, _} = amqp10_client:get_msg(Receiver),
@@ -4458,7 +4667,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">>],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
<<"connection">> := <<"127.0.0.1:", _/binary>>,
<<"node">> := Node,
<<"vhost">> := <<"/">>,
@@ -4473,7 +4686,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">>],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
<<"connection">> := <<"127.0.0.1:", _/binary>>,
<<"node">> := Node,
<<"vhost">> := <<"/">>,
@@ -4644,9 +4861,13 @@ idle_time_out_on_client(Config) ->
receive
{amqp10_event,
{connection, Connection,
+<<<<<<< HEAD
{closed,
{resource_limit_exceeded,
<<"remote idle-time-out">>}}}} -> ok
+=======
+ {closed, _}}} -> ok
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
after 30000 ->
ct:fail({missing_event, ?LINE})
end,
@@ -4668,7 +4889,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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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 +4908,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})
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
%% Since we don't renew our credential, we expect the server to close our connection.
@@ -4692,7 +4921,11 @@ credential_expires(Config) ->
{connection, Connection,
{closed,
{unauthorized_access, <<"credential expired">>}}}} -> ok
+<<<<<<< HEAD
after 10_000 ->
+=======
+ after 30_000 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
flush(?LINE),
ct:fail({missing_event, ?LINE})
end,
@@ -5977,6 +6210,7 @@ reserved_annotation(Config) ->
end,
ok = close_connection_sync(Connection).
+<<<<<<< HEAD
%% internal
%%
@@ -5990,6 +6224,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
+%%
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
receive_all_messages(Receiver, Accept) ->
receive_all_messages0(Receiver, Accept, []).
@@ -6004,6 +6476,7 @@ receive_all_messages0(Receiver, Accept, Acc) ->
lists:reverse(Acc)
end.
+<<<<<<< HEAD
connection_config(Config) ->
connection_config(0, Config).
@@ -6024,6 +6497,8 @@ flush(Prefix) ->
ok
end.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
open_and_close_connection(Config) ->
OpnConf = connection_config(Config),
{ok, Connection} = amqp10_client:open_connection(OpnConf),
@@ -6032,6 +6507,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 +6560,8 @@ wait_for_connection_close(Connection) ->
ct:fail({connection_close_timeout, Connection})
end.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
wait_for_accepted(Tag) ->
wait_for_settlement(Tag, accepted).
@@ -6096,6 +6574,7 @@ wait_for_settlement(Tag, State) ->
ct:fail({settled_timeout, Tag})
end.
+<<<<<<< HEAD
wait_for_accepts(0) ->
ok;
wait_for_accepts(N) ->
@@ -6106,6 +6585,8 @@ wait_for_accepts(N) ->
ct:fail({missing_accepted, N})
end.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
delete_queue(Session, QName) ->
{ok, LinkPair} = rabbitmq_amqp_client:attach_management_link_pair_sync(
Session, <<"delete queue">>),
@@ -6156,6 +6637,7 @@ count_received_messages0(Receiver, Count) ->
Count
end.
+<<<<<<< HEAD
send_messages(Sender, Left, Settled) ->
send_messages(Sender, Left, Settled, <<>>).
@@ -6182,6 +6664,8 @@ send_messages(Sender, Left, Settled, BodySuffix) ->
send_messages(Sender, Left, Settled, BodySuffix)
end.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
send_until_remote_incoming_window_exceeded(Session, Address) ->
{ok, Sender} = amqp10_client:attach_sender_link(Session, <<"sender">>, Address, settled),
ok = wait_for_credit(Sender),
@@ -6216,7 +6700,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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
ct:pal("insufficient link credit with ~b messages left", [Left]),
ok
end
@@ -6331,8 +6819,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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..75abe3357bcd
--- /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..98246d8b0b6b 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}}
+ ].
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..0a39eb35a875 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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>)),
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..522b4c206913 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]])
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..b84fb236540e 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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() ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..9b6fad5fe822 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
%%%===================================================================
@@ -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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>
+ },
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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)),
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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)).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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..d8edc66d2027 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"};
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ ->
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>}])),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end,
flush(1),
ok.
+<<<<<<< HEAD
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..b053c3eb4526 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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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..71e2a268b09c 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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..2b9256ba5d62 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..a10d9c17c18c 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 ]'))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
# --------------------------------------------------------------------
# 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..9eb123559d8d 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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..11cc53121cc6 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",
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end.
%% -------------------------------------------------------------------
diff --git a/deps/rabbit_common/src/rabbit_event.erl b/deps/rabbit_common/src/rabbit_event.erl
index ac584ed0819f..c92424611b4c 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbit_common/src/rabbit_ssl_options.erl b/deps/rabbit_common/src/rabbit_ssl_options.erl
index ee0d1b4a3260..b4faf83eee21 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..1997af989f68 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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..dfe3b7daef4d 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
-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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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..d3932b8c25f3 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
1717
+<<<<<<< HEAD
5.10.3
+=======
+ 5.11.3
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..00a8e8db455c 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
@@ -23,7 +27,11 @@
UTF-81717
+<<<<<<< HEAD
2.0.0
+=======
+ 2.1.0
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..2e2f4dbd4e85 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)}],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..bfeb3eac70b1 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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..d8b0ad748ea8 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..6b641b3e3be4 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..d6a67e162898 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">>}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]}
]},
].
```
+<<<<<<< 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`.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
### 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
| `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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]}
]},
].
diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl
index 003818ac74be..e5060ffcf118 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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",
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
)
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"],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..e19e08e01c6e 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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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
+=======
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..c6d0d3719fa1 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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()).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% 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()}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}}.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..d2051f0a5295 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
+]).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]),
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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].
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]));
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ -> 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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..abd51f519e8a 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl
index edd81902da15..248a4416c0c5 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()}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..eb0cf2046f7f 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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})};
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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)]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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} ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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..dfbdfd2c7dc0 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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..c08ae49e5af2 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"},
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]
}
@@ -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">>}
+ ]
+ }
+ }
+ ]}
+ ], []
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}
].
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl
index bc1256da8b9d..8558bec48219 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
+]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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
+]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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);
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, []),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
@@ -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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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:*/*">>
+ ]}
+ }
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
),
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:*/*">>
+ ]}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
),
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:*/*">>}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
),
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}),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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">>)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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">>)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..f972aacdc57a 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
+]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
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"}
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#{<<"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"}
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#{<<"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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ -> {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)}
+ ],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#{<<"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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_ -> {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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl
index 692a9e2ab15d..13b71785f738 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
]},
{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
+ ]}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
%%
@@ -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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
+ 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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
index c8b3f296e213..5b34bf841ffb 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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.
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
@@ -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 = [
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{ "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:*/*/*">> ]
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
},
{ "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-*/*/*">> ]
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
},
{ "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}]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(_) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
<<"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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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, #{
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
<<"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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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/#">>}).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}])).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}]),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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}])).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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]).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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(_) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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))).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%%
%% 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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
?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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
AuthUser,
#resource{virtual_host = VHost,
kind = topic,
diff --git a/deps/rabbitmq_cli/Makefile b/deps/rabbitmq_cli/Makefile
index d32cc0b8c948..82103bb02099 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")
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
$(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..1afd2ce6c373 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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"
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
def banner(_, _), do: "Listing connections ..."
end
diff --git a/deps/rabbitmq_event_exchange/BUILD.bazel b/deps/rabbitmq_event_exchange/BUILD.bazel
index 6d0f269239ca..cecf71af35f3 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"//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..8616e5081c6e 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
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..6c11389625f2 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_event_exchange/app.bzl b/deps/rabbitmq_event_exchange/app.bzl
index 3ce9ec463521..c48ef06bd4df 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"//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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"//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..c7e3aef5b5a6 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]}}
+]}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..dd42b908cd0e 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").
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
-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()
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}).
-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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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)).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..9e26bc33d1d8 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]
+}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
diff --git a/deps/rabbitmq_event_exchange/test/system_SUITE.erl b/deps/rabbitmq_event_exchange/test/system_SUITE.erl
index 76d9199a586c..c6c4f135562c 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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
].
%% -------------------------------------------------------------------
@@ -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()).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% -------------------------------------------------------------------
%% 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) ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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)),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
#'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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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),
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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).
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% -------------------------------------------------------------------
%% 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).
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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 ->
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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">>}}.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_management/.gitignore b/deps/rabbitmq_management/.gitignore
index 96463fa9b670..3b0fcbbb2f77 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_management/BUILD.bazel b/deps/rabbitmq_management/BUILD.bazel
index 2f053bb609a7..0ac1afbb69c9 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"@ranch//:erlang_app",
],
)
@@ -130,6 +134,14 @@ rabbitmq_suite(
],
)
+<<<<<<< HEAD
+=======
+rabbitmq_suite(
+ name = "rabbit_mgmt_schema_SUITE",
+ size = "small",
+)
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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,
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
runtime_deps = [
"//deps/amqp10_client:erlang_app",
],
diff --git a/deps/rabbitmq_management/Makefile b/deps/rabbitmq_management/Makefile
index 98998bfcdb48..6df15a1e5a20 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..4c7b37ef6617 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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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",
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
"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(
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..2673db3445b7 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.
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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}]}.
+
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
%% 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
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{mapping,
"management.oauth_resource_servers.$name.id",
"rabbitmq_management.oauth_resource_servers",
@@ -514,8 +549,11 @@ end}.
[{datatype, string}]
}.
+<<<<<<< HEAD
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{mapping,
"management.oauth_resource_servers.$name.oauth_client_id",
"rabbitmq_management.oauth_resource_servers",
@@ -534,7 +572,10 @@ end}.
[{datatype, string}]
}.
+<<<<<<< HEAD
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
{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)
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
end}.
%% ===========================================================================
diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css
index a3bcaae5d5f5..9e4c6023528a 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; }
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
.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%);
+}
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
diff --git a/deps/rabbitmq_management/priv/www/js/dispatcher.js b/deps/rabbitmq_management/priv/www/js/dispatcher.js
index e0e520715fe4..cb7991ab1081 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');
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
});
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..9877ee770e67 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],
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
['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.'
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
};
///////////////////////////////////////////////////////////////////////////
diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js
index 22c9f47bc145..ef76f2e170cf 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();
+ }
+}
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..03ec3acd5129 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
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}
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
+ }
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
}
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();
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
} 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;
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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))
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
_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)
}
+=======
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)
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..16612a7278cc 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
+=======
+
+>>>>>>> 8d7535e0b (amqqueue_process: adopt new `is_duplicate` backing queue callback)