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..437b48c471c8 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"
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..443f9b60232d 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..d9ad79d79439 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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..a3bf2f67a604 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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:}"
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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 }}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..268256941324 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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..e6e9d2773461 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/**'
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- '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/**'
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- .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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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..2e8dac608ab9 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"
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- name: SETUP DOTNET (rabbit)
uses: actions/setup-dotnet@v4
if: inputs.plugin == 'rabbit'
with:
+<<<<<<< HEAD
dotnet-version: '3.1.x'
+=======
+ dotnet-version: '8.0'
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- name: RUN TESTS
if: inputs.plugin != 'rabbitmq_cli'
diff --git a/.github/workflows/test-make.yaml b/.github/workflows/test-make.yaml
index 60d61dad9fc4..af4f949e8435 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
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..9340dc81b60f 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- name: Upload Test Artifacts
if: always()
diff --git a/.github/workflows/test-management-ui.yaml b/.github/workflows/test-management-ui.yaml
index 53902913d8c0..d48b03f10604 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
/tmp/short-suite
summary-selenium:
diff --git a/.github/workflows/test-mixed-versions.yaml b/.github/workflows/test-mixed-versions.yaml
index bf2ed9ae2fdb..858c183bb9ac 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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..425a6b28707c 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
include:
- erlang_version: 26
elixir_version: 1.17
diff --git a/.github/workflows/test-plugin.yaml b/.github/workflows/test-plugin.yaml
index 3c163ba059a3..02863aeb8bb3 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
+=======
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
cat << EOF >> user.bazelrc
build --strategy=TestRunner=local
EOF
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 6c3c003670ef..c7d4e3522480 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -2,6 +2,10 @@ name: Test
on:
push:
branches:
+<<<<<<< HEAD
+=======
+ - v4.0.x
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
- 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..caf0adcfc441 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
### What is Broadcom's Obligation to Reply to Messages or Issues Reported?
diff --git a/MODULE.bazel b/MODULE.bazel
index 6a0a57e07196..124a56ab7c7b 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
hex = use_extension(
diff --git a/Makefile b/Makefile
index 2e4fe88c9f7a..8fd06eff4528 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 \
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
rabbitmq_auth_backend_oauth2 \
rabbitmq_auth_mechanism_ssl \
rabbitmq_aws \
diff --git a/bazel/BUILD.horus b/bazel/BUILD.horus
index 0f7e2369acbb..aacba50d84b5 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
],
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
],
)
diff --git a/bazel/BUILD.redbug b/bazel/BUILD.redbug
index 06c43294b29e..4f096add7fcb 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")
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
filegroup(name = "public_hdrs")
diff --git a/bazel/bzlmod/secondary_umbrella.bzl b/bazel/bzlmod/secondary_umbrella.bzl
index 613961d5a616..5cdf1edea626 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
],
)
diff --git a/deps/amqp10_client/BUILD.bazel b/deps/amqp10_client/BUILD.bazel
index df8b879adae1..e9919f481ac0 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"@credentials_obfuscation//:erlang_app",
],
)
diff --git a/deps/amqp10_client/app.bzl b/deps/amqp10_client/app.bzl
index 8fcdad73cf9d..95619a349d26 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"],
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c84418a89969 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};
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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};
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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..7e9eb4d61e46 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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ = 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..e3417362dee3 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c60ddae9efce 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..80f6eeadf835 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..ea1b191596b3 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]).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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)}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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().
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..578318b5742c 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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
suite() ->
[{timetrap, {minutes, 4}}].
@@ -30,7 +37,11 @@ all() ->
groups() ->
[
+<<<<<<< HEAD
{rabbitmq, [], shared()},
+=======
+ {rabbitmq, [], shared() ++ [notify_with_performative]},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
% 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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}}]}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
exit(incoming_heartbeat_assert)
end,
demonitor(MockRef).
+<<<<<<< HEAD
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%% 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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
LastReceivedMsg = case Acc of
[] -> none;
[M | _] -> M
diff --git a/deps/amqp10_common/app.bzl b/deps/amqp10_common/app.bzl
index a233c945cebe..0386291f7779 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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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..c7305782e12b 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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..6c0fe0851c6b 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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..7f052a67ba13 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
% 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{}.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..19191a4ca79d 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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)}]}];
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, [])))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}.
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 }", [
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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])).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/oauth2_client/test/system_SUITE.erl b/deps/oauth2_client/test/system_SUITE.erl
index ecc5aa733bef..17bc5099885d 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
+]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]},
{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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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()} ]}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
| 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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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"]),
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[{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"]),
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[{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)]),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[ {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 =
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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],
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ ->
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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();
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ ->
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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")),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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")),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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() ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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]),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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();
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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]),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..d058a351f207 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]).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
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} ]},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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} ]},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
no_ssl_options_triggers_verify_peer(_) ->
diff --git a/deps/rabbit/BUILD.bazel b/deps/rabbit/BUILD.bazel
index 1b5d65cd4fed..9bbb4aff0f89 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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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",
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
rabbitmq_integration_suite(
@@ -977,6 +996,14 @@ rabbitmq_integration_suite(
)
rabbitmq_suite(
+<<<<<<< HEAD
+=======
+ name = "unit_msg_size_metrics_SUITE",
+ size = "small",
+)
+
+rabbitmq_suite(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
":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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
":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",
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
],
target = ":test_erlang_app",
test_env = {
diff --git a/deps/rabbit/Makefile b/deps/rabbit/Makefile
index 79ad84990739..253241b4157a 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..00a1291e16b6 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"],
+ )
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit/ct.test.spec b/deps/rabbit/ct.test.spec
index 6740594c1500..25b6d476a950 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
, 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
, 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]}.
{define, 'Set5', [
diff --git a/deps/rabbit/include/rabbit_amqp.hrl b/deps/rabbit/include/rabbit_amqp.hrl
index 84e98d5d565d..54b198685980 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
vhost,
user,
node
diff --git a/deps/rabbit/src/mc.erl b/deps/rabbit/src/mc.erl
index b122c4780110..c7b06b5b9818 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()]} |
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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()}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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().
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..27897271f85b 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[{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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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))).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit/src/mc_amqpl.erl b/deps/rabbit/src/mc_amqpl.erl
index 723e60cd3f79..b6507e06f7db 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
true ->
{utf8, V};
false ->
{binary, V}
end;
+<<<<<<< HEAD
from_091(longstr, V) -> {binary, V};
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..fa87d732d4a1 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..0a1af9d2b4db 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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..43d702fbaca1 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()} |
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{'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..a2e54ee9250b 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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..cd60660d6cf3 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-include("rabbit_amqp.hrl").
-export([init/1,
info/2,
+<<<<<<< HEAD
mainloop/2]).
+=======
+ mainloop/2,
+ set_credential/2]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-export([system_continue/3,
system_terminate/4,
@@ -35,6 +47,10 @@
-record(v1_connection,
{name :: binary(),
+<<<<<<< HEAD
+=======
+ container_id :: none | binary(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}).
-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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%--------------------------------------------------------------------------
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)])
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[] ->
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..4803b9989b40 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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"
+ }}).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]).
-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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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.)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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} ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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} ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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)}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}].
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c8f5f881dfd3 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit/src/rabbit_amqp_writer.erl b/deps/rabbit/src/rabbit_amqp_writer.erl
index 7b239a10a107..282edc52653d 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()
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}).
-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()},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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..cd7ecc037c96 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().
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..576415116089 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..2ad985de3713 100644
--- a/deps/rabbit/src/rabbit_backing_queue.erl
+++ b/deps/rabbit/src/rabbit_backing_queue.erl
@@ -220,9 +220,14 @@
%% 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
+<<<<<<< HEAD
%% 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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..cd689bbfb447 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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{}}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%----------------------------------------------------------------------------
@@ -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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
end.
diff --git a/deps/rabbit/src/rabbit_channel.erl b/deps/rabbit/src/rabbit_channel.erl
index 303776471396..8693ef667137 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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..ae00106080dd 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-rabbit_feature_flag(
{virtual_host_metadata,
#{desc => "Virtual host metadata (description, tags, etc)",
+<<<<<<< HEAD
stability => required
+=======
+ stability => required,
+ require_level => hard
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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]
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-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]
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}}).
-rabbit_feature_flag(
{tracking_records_in_ets,
#{desc => "Store tracking records in ETS instead of Mnesia",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
depends_on => [stream_single_active_consumer]
}}).
@@ -117,6 +194,10 @@
{stream_filtering,
#{desc => "Support for stream filtering.",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
depends_on => [stream_queue]
}}).
@@ -124,14 +205,25 @@
{message_containers,
#{desc => "Message containers.",
stability => required,
+<<<<<<< HEAD
+=======
+ require_level => hard,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
depends_on => [stream_queue]
}}).
diff --git a/deps/rabbit/src/rabbit_db_binding.erl b/deps/rabbit/src/rabbit_db_binding.erl
index 37bc82ba246c..fbd6d60cb04f 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% -------------------------------------------------------------------
%% 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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..1aa241829b94 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..d40e5827e6b0 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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..7c9c07e1902e 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”
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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..c7342b143de3 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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..73b4053df373 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..682830ea277e 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% @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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
`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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% -------------------------------------------------------------------
%% Feature flags registry.
%% -------------------------------------------------------------------
@@ -911,6 +1088,11 @@ assert_feature_flag_is_valid(FeatureName, FeatureProps) ->
ValidProps = [desc,
doc_url,
stability,
+<<<<<<< HEAD
+=======
+ require_level,
+ experiment_level,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% @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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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();
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..032915888c34 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
[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();
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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} ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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, []} ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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()]}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% @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()]}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% @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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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}),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ ->
?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, []) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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..4f88716e08d9 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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()]} |
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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, #{})),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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};
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
disabled ->
{"Disabled", Yellow};
unavailable ->
diff --git a/deps/rabbit/src/rabbit_ff_registry.erl b/deps/rabbit/src/rabbit_ff_registry.erl
index 864ff564dc64..291fbdc1ac9b 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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..8abcfcb3ba91 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
-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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#{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..af622db3fbfd 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% @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..fbd21b3e3c8a 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}])
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c9a3c16ba585 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}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
end.
remote_node_info(Node) ->
diff --git a/deps/rabbit/src/rabbit_mnesia.erl b/deps/rabbit/src/rabbit_mnesia.erl
index 0aa4ae5360b5..c49c704c9c8c 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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..481df739bb31 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..2504bd1eb4ff 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}})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..1a8bebc57376 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, []).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..781e74ec01b2 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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..66c8dbb59049 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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]).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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;
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..ddb0e81d6ca7 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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..71d8cc274c24 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit/test/amqp_auth_SUITE.erl b/deps/rabbit/test/amqp_auth_SUITE.erl
index 920f779172d4..461c110b89d2 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit/test/amqp_client_SUITE.erl b/deps/rabbit/test/amqp_client_SUITE.erl
index 09916c55c356..1fb87e805f0d 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
available_messages_classic_queue,
available_messages_quorum_queue,
available_messages_stream,
incoming_message_interceptors,
+<<<<<<< HEAD
trace,
+=======
+ trace_classic_queue,
+ trace_stream,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]},
{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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"bool" => false},
OutMsg1),
OutMsg3 = amqp10_msg:set_message_annotations(
#{"x-string" => "string-value",
+<<<<<<< HEAD
"x-int" => 3,
+=======
+ "x-long" => -3,
+ "x-uint" => {uint, 3},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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">>)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
Node = get_node_config(Config, 0, nodename),
ConnectionCreatedProps = [Protocol,
{node, Node},
{vhost, <<"/">>},
{user, <<"guest">>},
+<<<<<<< HEAD
+=======
+ {container_id, <<"my container">>},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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),
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
<<"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">>],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
<<"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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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})
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+%%
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c07e7643cc59 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}}
+ ].
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..000ad4b6eebb 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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>)),
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..294b7ac2cc52 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]])
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c1437aa3b46a 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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() ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..edb621828c63 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
%%%===================================================================
@@ -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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>
+ },
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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)),
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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)).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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..f5992fd9c304 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"};
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ ->
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>}])),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
end,
flush(1),
ok.
+<<<<<<< HEAD
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..8ccd1a61d0d7 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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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..dd19aeb50d8a 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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..a0c551e2a7c2 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..35308e255729 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 ]'))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
# --------------------------------------------------------------------
# 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..4f3928165845 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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..cd9fb20c4592 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",
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
end.
%% -------------------------------------------------------------------
diff --git a/deps/rabbit_common/src/rabbit_event.erl b/deps/rabbit_common/src/rabbit_event.erl
index ac584ed0819f..5ba5895e44b8 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbit_common/src/rabbit_ssl_options.erl b/deps/rabbit_common/src/rabbit_ssl_options.erl
index ee0d1b4a3260..a650c1790be9 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..cea2bb519a89 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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..b2d8a7f9fef8 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
-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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..5a1f5be1c646 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
1717
+<<<<<<< HEAD
5.10.3
+=======
+ 5.11.3
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..5eb4edce7772 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
@@ -23,7 +27,11 @@
UTF-81717
+<<<<<<< HEAD
2.0.0
+=======
+ 2.1.0
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..d1713c045029 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)}],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..860d874941ad 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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c7830b2199ed 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..b267284fb33c 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..5639e23573bb 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">>}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]}
]},
].
```
+<<<<<<< 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`.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
### 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
| `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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]}
]},
].
diff --git a/deps/rabbitmq_auth_backend_oauth2/app.bzl b/deps/rabbitmq_auth_backend_oauth2/app.bzl
index 003818ac74be..b43f7ded9916 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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",
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
)
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"],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..464ec95b7154 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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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
+=======
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..c98e9625c7cd 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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()).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% 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()}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}}.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..aa003b570a7e 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
+]).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]),
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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].
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]));
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ -> 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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..354b041baf79 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl b/deps/rabbitmq_auth_backend_oauth2/src/uaa_jwks.erl
index edd81902da15..d58eb8fd3943 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()}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..75028d9e17a5 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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})};
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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} ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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..f6317b44ccf5 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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..652aa0e6e22f 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"},
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]
}
@@ -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">>}
+ ]
+ }
+ }
+ ]}
+ ], []
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}
].
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/jwks_SUITE.erl
index bc1256da8b9d..bac2f863d6cd 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
+]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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
+]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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);
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, []),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
@@ -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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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:*/*">>
+ ]}
+ }
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
),
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:*/*">>
+ ]}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
),
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:*/*">>}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
),
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}),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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">>)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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">>)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..f85f6d9b2451 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
+]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
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"}
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#{<<"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"}
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#{<<"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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ -> {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)}
+ ],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#{<<"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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_ -> {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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/system_SUITE.erl
index 692a9e2ab15d..af586bbddd92 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
]},
{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
+ ]}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
%%
@@ -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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
+ 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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl b/deps/rabbitmq_auth_backend_oauth2/test/unit_SUITE.erl
index c8b3f296e213..d46f788c830f 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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.
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
@@ -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 = [
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{ "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:*/*/*">> ]
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
},
{ "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-*/*/*">> ]
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
},
{ "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}]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(_) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
<<"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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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, #{
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
<<"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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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/#">>}).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}])).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}]),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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}])).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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]).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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(_) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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))).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%%
%% 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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
?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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
AuthUser,
#resource{virtual_host = VHost,
kind = topic,
diff --git a/deps/rabbitmq_cli/Makefile b/deps/rabbitmq_cli/Makefile
index d32cc0b8c948..f2eb814c1172 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")
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
$(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..96166fc970ba 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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"
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
def banner(_, _), do: "Listing connections ..."
end
diff --git a/deps/rabbitmq_cli/mix.exs b/deps/rabbitmq_cli/mix.exs
index e810ce44bb3b..a1765430f847 100644
--- a/deps/rabbitmq_cli/mix.exs
+++ b/deps/rabbitmq_cli/mix.exs
@@ -11,7 +11,11 @@ defmodule RabbitMQCtl.MixfileBase do
[
app: :rabbitmqctl,
version: "4.0.0-dev",
+<<<<<<< HEAD
elixir: ">= 1.13.4 and < 1.18.0",
+=======
+ elixir: ">= 1.13.4 and < 1.19.0",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
build_embedded: Mix.env() == :prod,
start_permanent: Mix.env() == :prod,
escript: [
diff --git a/deps/rabbitmq_event_exchange/BUILD.bazel b/deps/rabbitmq_event_exchange/BUILD.bazel
index 6d0f269239ca..db9894c9ba51 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"//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..dc78dddb9c60 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
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..428fb4b0161e 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_event_exchange/app.bzl b/deps/rabbitmq_event_exchange/app.bzl
index 3ce9ec463521..1d5030fccd0a 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"//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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"//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..8be7a6e556de 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]}}
+]}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..2839ee08f432 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").
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
-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()
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}).
-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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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)).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..0e59ab732cb7 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]
+}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
diff --git a/deps/rabbitmq_event_exchange/test/system_SUITE.erl b/deps/rabbitmq_event_exchange/test/system_SUITE.erl
index 76d9199a586c..c0218dfc4cfb 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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
].
%% -------------------------------------------------------------------
@@ -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()).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% -------------------------------------------------------------------
%% 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) ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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)),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
#'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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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),
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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).
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% -------------------------------------------------------------------
%% 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).
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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 ->
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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">>}}.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_management/.gitignore b/deps/rabbitmq_management/.gitignore
index 96463fa9b670..7b2ecf172df2 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_management/BUILD.bazel b/deps/rabbitmq_management/BUILD.bazel
index 2f053bb609a7..e48354ee3c47 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"@ranch//:erlang_app",
],
)
@@ -130,6 +134,14 @@ rabbitmq_suite(
],
)
+<<<<<<< HEAD
+=======
+rabbitmq_suite(
+ name = "rabbit_mgmt_schema_SUITE",
+ size = "small",
+)
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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,
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
runtime_deps = [
"//deps/amqp10_client:erlang_app",
],
diff --git a/deps/rabbitmq_management/Makefile b/deps/rabbitmq_management/Makefile
index 98998bfcdb48..d80f3360a9a0 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..b400196a3da8 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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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",
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
"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(
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..8fe6ee8a9640 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.
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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}]}.
+
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
%% 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
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{mapping,
"management.oauth_resource_servers.$name.id",
"rabbitmq_management.oauth_resource_servers",
@@ -514,8 +549,11 @@ end}.
[{datatype, string}]
}.
+<<<<<<< HEAD
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{mapping,
"management.oauth_resource_servers.$name.oauth_client_id",
"rabbitmq_management.oauth_resource_servers",
@@ -534,7 +572,10 @@ end}.
[{datatype, string}]
}.
+<<<<<<< HEAD
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
{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)
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
end}.
%% ===========================================================================
diff --git a/deps/rabbitmq_management/priv/www/css/main.css b/deps/rabbitmq_management/priv/www/css/main.css
index a3bcaae5d5f5..c75a27abb206 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; }
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
.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%);
+}
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
diff --git a/deps/rabbitmq_management/priv/www/js/dispatcher.js b/deps/rabbitmq_management/priv/www/js/dispatcher.js
index e0e520715fe4..c9ffd67cdb5b 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');
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
});
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..ff63ad7f776c 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],
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
['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.'
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
};
///////////////////////////////////////////////////////////////////////////
diff --git a/deps/rabbitmq_management/priv/www/js/main.js b/deps/rabbitmq_management/priv/www/js/main.js
index 22c9f47bc145..36ed4980b3ac 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();
+ }
+}
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..b7709d22a6ee 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
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}
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
+ }
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
}
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();
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
} 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;
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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))
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
_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)
}
+=======
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)
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..0645054891ea 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
+=======
+
+>>>>>>> 5086e283b (Allow building CLI with elixir 1.18.x)