diff --git a/.dockerignore b/.dockerignore
index 31b16d2aab..ec905b5eb8 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,4 +1,2 @@
-.github/
-.gitpod.yml
bin/
tmp/
diff --git a/.editorconfig b/.editorconfig
index 27917441d8..085f9d0e31 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -19,3 +19,6 @@ indent_style = tab
[{config.yaml.dist,config.dev.yaml}]
indent_size = 2
+
+[.golangci.yaml]
+indent_size = 2
diff --git a/.envrc b/.envrc
index 5817bffc67..fb06536f72 100644
--- a/.envrc
+++ b/.envrc
@@ -1,6 +1,6 @@
-if ! has nix_direnv_version || ! nix_direnv_version 1.5.0; then
- source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/1.5.0/direnvrc" "sha256-carKk9aUFHMuHt+IWh74hFj58nY4K3uywpZbwXX0BTI="
+if ! has nix_direnv_version || ! nix_direnv_version 3.0.6; then
+ source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.6/direnvrc" "sha256-RYcUJaRMf8oF5LznDrlCXbkOQrywm0HDv1VjYGaJGdM="
fi
-use flake
+use flake . --impure
dotenv_if_exists
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 64156bfa7a..7b8f9b7e2e 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -1,5 +1,9 @@
blank_issues_enabled: false
contact_links:
+ - name: đ Documentation enhancement
+ url: https://github.com/dexidp/website/issues
+ about: Suggest an improvement to the documentation
+
- name: â Ask a question
url: https://github.com/dexidp/dex/discussions/new?category=q-a
about: Ask and discuss questions with other Dex community members
@@ -13,5 +17,5 @@ contact_links:
about: Please ask and answer questions here
- name: đĄ Dex Enhancement Proposal
- url: https://github.com/dexidp/dex/tree/master/enhancements/README.md
+ url: https://github.com/dexidp/dex/tree/master/docs/enhancements/README.md
about: Open a proposal for significant architectural change
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index bcaee00ae3..a706b551a1 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -21,15 +21,3 @@ Thank you for sending a pull request! Here are some tips for contributors:
-->
#### Special notes for your reviewer
-
-#### Does this PR introduce a user-facing change?
-
-
-
-```release-note
-
-```
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
index 9decd34e3e..eab38858be 100644
--- a/.github/SECURITY.md
+++ b/.github/SECURITY.md
@@ -11,10 +11,10 @@ to confirm receipt of the issue.
## Review Process
Once a maintainer has confirmed the relevance of the report, a draft security
-advisory will be created on Github. The draft advisory will be used to discuss
+advisory will be created on GitHub. The draft advisory will be used to discuss
the issue with maintainers, the reporter(s).
If the reporter(s) wishes to participate in this discussion, then provide
-reporter Github username(s) to be invited to the discussion. If the reporter(s)
+reporter GitHub username(s) to be invited to the discussion. If the reporter(s)
does not wish to participate directly in the discussion, then the reporter(s)
can request to be updated regularly via email.
diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml
index b3129d93cf..f66cc18740 100644
--- a/.github/dependabot.yaml
+++ b/.github/dependabot.yaml
@@ -7,6 +7,10 @@ updates:
- "area/dependencies"
schedule:
interval: "daily"
+ groups:
+ etcd:
+ patterns:
+ - "go.etcd.io/*"
- package-ecosystem: "gomod"
directory: "/api/v2"
@@ -15,6 +19,13 @@ updates:
schedule:
interval: "daily"
+ - package-ecosystem: "gomod"
+ directory: "/examples"
+ labels:
+ - "area/dependencies"
+ schedule:
+ interval: "daily"
+
- package-ecosystem: "docker"
directory: "/"
labels:
diff --git a/.github/workflows/analysis-scorecard.yaml b/.github/workflows/analysis-scorecard.yaml
new file mode 100644
index 0000000000..2071b6eda0
--- /dev/null
+++ b/.github/workflows/analysis-scorecard.yaml
@@ -0,0 +1,47 @@
+name: OpenSSF Scorecard
+
+on:
+ branch_protection_rule:
+ push:
+ branches: [ main ]
+ schedule:
+ - cron: '30 0 * * 5'
+
+permissions:
+ contents: read
+
+jobs:
+ analyze:
+ name: Analyze
+ runs-on: ubuntu-latest
+
+ permissions:
+ actions: read
+ contents: read
+ id-token: write
+ security-events: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ persist-credentials: false
+
+ - name: Run analysis
+ uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ publish_results: true
+
+ - name: Upload results as artifact
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
+ with:
+ name: OpenSSF Scorecard results
+ path: results.sarif
+ retention-days: 5
+
+ - name: Upload results to GitHub Security tab
+ uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
+ with:
+ sarif_file: results.sarif
diff --git a/.github/workflows/artifacts.yaml b/.github/workflows/artifacts.yaml
index 0237b3ac66..f2bc6b0dfe 100644
--- a/.github/workflows/artifacts.yaml
+++ b/.github/workflows/artifacts.yaml
@@ -1,12 +1,31 @@
name: Artifacts
on:
- push:
- branches:
- - master
- tags:
- - v[0-9]+.[0-9]+.[0-9]+
- pull_request:
+ workflow_call:
+ inputs:
+ publish:
+ description: Publish artifacts to the artifact store
+ default: false
+ required: false
+ type: boolean
+ secrets:
+ DOCKER_USERNAME:
+ required: true
+ DOCKER_PASSWORD:
+ required: true
+ outputs:
+ container-image-name:
+ description: Container image name
+ value: ${{ jobs.container-images.outputs.name }}
+ container-image-digest:
+ description: Container image digest
+ value: ${{ jobs.container-images.outputs.digest }}
+ container-image-ref:
+ description: Container image ref
+ value: ${{ jobs.container-images.outputs.ref }}
+
+permissions:
+ contents: read
jobs:
container-images:
@@ -18,62 +37,90 @@ jobs:
- alpine
- distroless
+ permissions:
+ attestations: write
+ contents: read
+ packages: write
+ id-token: write
+ security-events: write
+
+ outputs:
+ name: ${{ steps.image-name.outputs.value }}
+ digest: ${{ steps.build.outputs.digest }}
+ ref: ${{ steps.image-ref.outputs.value }}
+
steps:
- - name: Checkout
- uses: actions/checkout@v3
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3.7.0
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
+
+ - name: Set up Syft
+ uses: anchore/sbom-action/download-syft@43a17d6e7add2b5535efe4dcae9952337c479a93 # v0.20.11
- - name: Gather metadata
+ - name: Install cosign
+ uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
+
+ - name: Set image name
+ id: image-name
+ run: echo "value=ghcr.io/${{ github.repository }}" >> "$GITHUB_OUTPUT"
+
+ - name: Gather build metadata
id: meta
- uses: docker/metadata-action@v4
+ uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5.10.0
with:
images: |
- ghcr.io/dexidp/dex
- dexidp/dex
+ ${{ steps.image-name.outputs.value }}
+ ${{ github.repository == 'dexidp/dex' && 'dexidp/dex' || '' }}
flavor: |
latest = false
tags: |
type=ref,event=branch,enable=${{ matrix.variant == 'alpine' }}
- type=ref,event=pr,enable=${{ matrix.variant == 'alpine' }}
+ type=ref,event=pr,prefix=pr-,enable=${{ matrix.variant == 'alpine' }}
type=semver,pattern={{raw}},enable=${{ matrix.variant == 'alpine' }}
- type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && matrix.variant == 'alpine' }}
+ type=raw,value=latest,enable=${{ github.ref_name == github.event.repository.default_branch && matrix.variant == 'alpine' }}
type=ref,event=branch,suffix=-${{ matrix.variant }}
- type=ref,event=pr,suffix=-${{ matrix.variant }}
+ type=ref,event=pr,prefix=pr-,suffix=-${{ matrix.variant }}
type=semver,pattern={{raw}},suffix=-${{ matrix.variant }}
- type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }},suffix=-${{ matrix.variant }}
+ type=raw,value=latest,enable={{is_default_branch}},suffix=-${{ matrix.variant }}
labels: |
org.opencontainers.image.documentation=https://dexidp.io/docs/
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
+ # Multiple exporters are not supported yet
+ # See https://github.com/moby/buildkit/pull/2760
+ - name: Determine build output
+ uses: haya14busa/action-cond@94f77f7a80cd666cb3155084e428254fea4281fd # v1.2.1
+ id: build-output
with:
- platforms: all
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
+ cond: ${{ inputs.publish }}
+ if_true: type=image,push=true
+ if_false: type=oci,dest=image.tar
- name: Login to GitHub Container Registry
- uses: docker/login-action@v2
+ uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
registry: ghcr.io
- username: ${{ github.repository_owner }}
+ username: ${{ github.actor }}
password: ${{ github.token }}
- if: github.event_name == 'push'
+ if: inputs.publish
- name: Login to Docker Hub
- uses: docker/login-action@v2
+ uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- if: github.event_name == 'push'
+ if: inputs.publish
- - name: Build and push
- uses: docker/build-push-action@v3
+ - name: Build and push image
+ id: build
+ uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
with:
context: .
- platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le
- # cache-from: type=gha
- # cache-to: type=gha,mode=max
- push: ${{ github.event_name == 'push' }}
+ platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le,linux/s390x
tags: ${{ steps.meta.outputs.tags }}
build-args: |
BASE_IMAGE=${{ matrix.variant }}
@@ -81,17 +128,111 @@ jobs:
COMMIT_HASH=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }}
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
labels: ${{ steps.meta.outputs.labels }}
+ # cache-from: type=gha
+ # cache-to: type=gha,mode=max
+ outputs: ${{ steps.build-output.outputs.value }}
+ # push: ${{ inputs.publish }}
+
+ - name: Sign the images with GitHub OIDC Token
+ run: |
+ cosign sign --yes ${{ steps.image-name.outputs.value }}@${{ steps.build.outputs.digest }}
+ if: inputs.publish
+
+ - name: Set image ref
+ id: image-ref
+ run: echo "value=${{ steps.image-name.outputs.value }}@${{ steps.build.outputs.digest }}" >> "$GITHUB_OUTPUT"
+
+ - name: Fetch image
+ run: skopeo --insecure-policy copy docker://${{ steps.image-ref.outputs.value }} oci-archive:image.tar
+ if: inputs.publish
+
+ # Uncomment the following lines for debugging:
+ # - name: Upload image as artifact
+ # uses: actions/upload-artifact@v3
+ # with:
+ # name: "[${{ github.job }}] OCI tarball"
+ # path: image.tar
+
+ - name: Extract OCI tarball
+ run: |
+ mkdir -p image
+ tar -xf image.tar -C image
+
+ # - name: List tags
+ # run: skopeo --insecure-policy list-tags oci:image
+ #
+ # # See https://github.com/anchore/syft/issues/1545
+ # - name: Extract image from multi-arch image
+ # run: skopeo --override-os linux --override-arch amd64 --insecure-policy copy oci:image:${{ steps.image-name.outputs.value }}:${{ steps.meta.outputs.version }} docker-archive:docker.tar
+ #
+ # - name: Generate SBOM
+ # run: syft -o spdx-json=sbom-spdx.json docker-archive:docker.tar
+ #
+ # - name: Upload SBOM as artifact
+ # uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+ # with:
+ # name: "[${{ github.job }}] SBOM"
+ # path: sbom-spdx.json
+ # retention-days: 5
+
+ # TODO: uncomment when the action is working for non ghcr.io pushes. GH Issue: https://github.com/actions/attest-build-provenance/issues/80
+ # - name: Generate build provenance attestation
+ # uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
+ # with:
+ # subject-name: dexidp/dex
+ # subject-digest: ${{ steps.build.outputs.digest }}
+ # push-to-registry: true
+
+ - name: Generate build provenance attestation
+ uses: actions/attest-build-provenance@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.0
+ with:
+ subject-name: ghcr.io/${{ github.repository }}
+ subject-digest: ${{ steps.build.outputs.digest }}
+ push-to-registry: true
+ if: inputs.publish
+
+ ## Use cache for the trivy-db to avoid the TOOMANYREQUESTS error https://github.com/aquasecurity/trivy-action/pull/397
+ ## To avoid the trivy-db becoming outdated, we save the cache for one day
+ - name: Get data
+ id: date
+ run: echo "date=$(date +%Y-%m-%d)" >> $GITHUB_OUTPUT
+
+ - name: Restore trivy cache
+ uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: cache/db
+ key: trivy-cache-${{ steps.date.outputs.date }}
+ restore-keys: trivy-cache-
- name: Run Trivy vulnerability scanner
- uses: aquasecurity/trivy-action@0.7.1
+ uses: aquasecurity/trivy-action@b6643a29fecd7f34b3597bc6acb0a98b03d33ff8 # 0.33.1
+ with:
+ input: image
+ format: sarif
+ output: trivy-results.sarif
+ scan-type: "fs"
+ scan-ref: "."
+ cache-dir: "./cache"
+ # Disable skipping trivy cache for now
+ # env:
+ # TRIVY_SKIP_DB_UPDATE: true
+ # TRIVY_SKIP_JAVA_DB_UPDATE: true
+
+ ## Trivy-db uses `0600` permissions.
+ ## But `action/cache` use `runner` user by default
+ ## So we need to change the permissions before caching the database.
+ - name: change permissions for trivy.db
+ run: sudo chmod 0644 ./cache/db/trivy.db
+
+ - name: Upload Trivy scan results as artifact
+ uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
with:
- image-ref: "ghcr.io/dexidp/dex:${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}"
- format: "sarif"
- output: "trivy-results.sarif"
- if: github.event_name == 'push'
+ name: "[${{ github.job }}] Trivy scan results"
+ path: trivy-results.sarif
+ retention-days: 5
+ overwrite: true
- name: Upload Trivy scan results to GitHub Security tab
- uses: github/codeql-action/upload-sarif@v2
+ uses: github/codeql-action/upload-sarif@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5
with:
- sarif_file: "trivy-results.sarif"
- if: github.event_name == 'push'
+ sarif_file: trivy-results.sarif
diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml
index c7eb4ea73c..feb7cf38f6 100644
--- a/.github/workflows/checks.yaml
+++ b/.github/workflows/checks.yaml
@@ -4,14 +4,19 @@ on:
pull_request:
types: [opened, labeled, unlabeled, synchronize]
+permissions:
+ contents: read
+
jobs:
release-label:
name: Release note label
runs-on: ubuntu-latest
+ if: github.repository == 'dexidp/dex'
+
steps:
- name: Check minimum labels
- uses: mheap/github-action-required-labels@v2
+ uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5
with:
mode: minimum
count: 1
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 192a44046e..02c5764687 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -2,26 +2,30 @@ name: CI
on:
push:
- branches:
- - master
+ branches: [master]
pull_request:
+permissions:
+ contents: read
+
jobs:
- build:
- name: Build
+ test:
+ name: Test
runs-on: ubuntu-latest
- env:
- GOFLAGS: -mod=readonly
services:
postgres:
image: postgres:10.8
+ env:
+ TZ: UTC
ports:
- 5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
postgres-ent:
image: postgres:10.8
+ env:
+ TZ: UTC
ports:
- 5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
@@ -61,25 +65,31 @@ jobs:
options: --health-cmd "curl --fail http://localhost:5000/v3" --health-interval 10s --health-timeout 5s --health-retries 5
steps:
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+
- name: Set up Go
- uses: actions/setup-go@v3
+ uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
with:
- go-version: 1.18
+ go-version: "1.25"
- - name: Checkout code
- uses: actions/checkout@v3
+ - name: Download tool dependencies
+ run: make deps
+
+ # Ensure that generated files were committed.
+ # It can help us determine, that the code is in the intermediate state, which should not be tested.
+ # Thus, heavy jobs like creating a kind cluster and testing / linting will be skipped.
+ - name: Verify
+ run: make verify
- name: Start services
- run: docker-compose -f docker-compose.test.yaml up -d
+ run: docker compose -f docker-compose.test.yaml up -d
- name: Create kind cluster
- uses: helm/kind-action@v1.3.0
+ uses: helm/kind-action@92086f6be054225fa813e0a4b13787fc9088faab # v1.13.0
with:
- version: v0.11.1
- node_image: kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729
-
- - name: Download tool dependencies
- run: make deps
+ version: "v0.17.0"
+ node_image: "kindest/node:v1.25.3@sha256:cd248d1438192f7814fbca8fede13cfe5b9918746dfa12583976158a834fd5c5"
- name: Test
run: make testall
@@ -111,8 +121,8 @@ jobs:
DEX_ETCD_ENDPOINTS: http://localhost:${{ job.services.etcd.ports[2379] }}
DEX_LDAP_HOST: localhost
- DEX_LDAP_PORT: 389
- DEX_LDAP_TLS_PORT: 636
+ DEX_LDAP_PORT: 3890
+ DEX_LDAP_TLS_PORT: 6360
DEX_KEYSTONE_URL: http://localhost:${{ job.services.keystone.ports[5000] }}
DEX_KEYSTONE_ADMIN_URL: http://localhost:${{ job.services.keystone.ports[35357] }}
@@ -121,9 +131,48 @@ jobs:
DEX_KUBERNETES_CONFIG_PATH: ~/.kube/config
+ lint:
+ name: Lint
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+
+ - name: Set up Go
+ uses: actions/setup-go@4dc6199c7b1a012772edbd06daecab0f50c9053c # v6.1.0
+ with:
+ go-version: "1.25"
+
+ - name: Download golangci-lint
+ run: make bin/golangci-lint
+
- name: Lint
run: make lint
- # Ensure proto generation doesn't depend on external packages.
- - name: Verify proto
- run: make verify-proto
+ artifacts:
+ name: Artifacts
+ uses: ./.github/workflows/artifacts.yaml
+ with:
+ publish: ${{ github.event_name == 'push' }}
+ secrets:
+ DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
+ permissions:
+ attestations: write
+ contents: read
+ packages: write
+ id-token: write
+ security-events: write
+
+ dependency-review:
+ name: Dependency review
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+
+ - name: Dependency Review
+ uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2
diff --git a/.github/workflows/codeql-analysis.yaml b/.github/workflows/codeql-analysis.yaml
deleted file mode 100644
index 926f8be539..0000000000
--- a/.github/workflows/codeql-analysis.yaml
+++ /dev/null
@@ -1,67 +0,0 @@
-# For most projects, this workflow file will not need changing; you simply need
-# to commit it to your repository.
-#
-# You may wish to alter this file to override the set of languages analyzed,
-# or to provide custom queries or build logic.
-#
-# ******** NOTE ********
-# We have attempted to detect the languages in your repository. Please check
-# the `language` matrix defined below to confirm you have the correct set of
-# supported CodeQL languages.
-#
-name: "CodeQL"
-
-on:
- push:
- branches: [ master, v1 ]
- pull_request:
- # The branches below must be a subset of the branches above
- branches: [ master ]
- schedule:
- - cron: '28 10 * * 6'
-
-jobs:
- analyze:
- name: Analyze
- runs-on: ubuntu-latest
-
- strategy:
- fail-fast: false
- matrix:
- language: [ 'go' ]
- # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
- # Learn more:
- # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
-
- # Initializes the CodeQL tools for scanning.
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
- with:
- languages: ${{ matrix.language }}
- # If you wish to specify custom queries, you can do so here or in a config file.
- # By default, queries listed here will override any specified in a config file.
- # Prefix the list here with "+" to use these queries and those in the config file.
- # queries: ./path/to/local/query, your-org/your-repo/queries@main
-
- # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
- # If this step fails, then you should remove it and run the build manually (see below)
- - name: Autobuild
- uses: github/codeql-action/autobuild@v2
-
- # âšī¸ Command-line programs to run using the OS shell.
- # đ https://git.io/JvXDl
-
- # âī¸ If the Autobuild fails above, remove it and uncomment the following three lines
- # and modify them (or add more) to build your code if your project
- # uses a compiled language
-
- #- run: |
- # make bootstrap
- # make release
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
deleted file mode 100644
index f841d55640..0000000000
--- a/.github/workflows/docker.yaml
+++ /dev/null
@@ -1,111 +0,0 @@
-name: Docker
-
-on:
- # push:
- # branches:
- # - master
- # tags:
- # - v[0-9]+.[0-9]+.[0-9]+
- pull_request:
-
-jobs:
- docker:
- name: Docker
- runs-on: ubuntu-latest
-
- steps:
- - name: Checkout
- uses: actions/checkout@v3
-
- - name: Calculate Docker image tags
- id: tags
- env:
- DOCKER_IMAGES: "ghcr.io/dexidp/dex dexidp/dex"
- run: |
- case $GITHUB_REF in
- refs/tags/*) VERSION=${GITHUB_REF#refs/tags/};;
- refs/heads/*) VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g');;
- refs/pull/*) VERSION=pr-${{ github.event.number }};;
- *) VERSION=sha-${GITHUB_SHA::8};;
- esac
-
- TAGS=()
- for image in $DOCKER_IMAGES; do
- TAGS+=("${image}:${VERSION}")
-
- if [[ "${{ github.event.repository.default_branch }}" == "$VERSION" ]]; then
- TAGS+=("${image}:latest")
- fi
- done
-
- echo ::set-output name=version::${VERSION}
- echo ::set-output name=tags::$(IFS=,; echo "${TAGS[*]}")
- echo ::set-output name=commit_hash::${GITHUB_SHA::8}
- echo ::set-output name=build_date::$(git show -s --format=%cI)
-
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- with:
- platforms: all
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- with:
- install: true
- version: latest
- # TODO: Remove driver-opts once fix is released docker/buildx#386
- driver-opts: image=moby/buildkit:master
-
- - name: Login to GitHub Container Registry
- uses: docker/login-action@v2
- with:
- registry: ghcr.io
- username: ${{ github.repository_owner }}
- password: ${{ github.token }}
- if: github.event_name == 'push'
-
- - name: Login to Docker Hub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
- if: github.event_name == 'push'
-
- - name: Build and push
- uses: docker/build-push-action@v3
- with:
- context: .
- platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le
- # cache-from: type=gha
- # cache-to: type=gha,mode=max
- push: ${{ github.event_name == 'push' }}
- tags: ${{ steps.tags.outputs.tags }}
- build-args: |
- VERSION=${{ steps.tags.outputs.version }}
- COMMIT_HASH=${{ steps.tags.outputs.commit_hash }}
- BUILD_DATE=${{ steps.tags.outputs.build_date }}
- labels: |
- org.opencontainers.image.title=${{ github.event.repository.name }}
- org.opencontainers.image.description=${{ github.event.repository.description }}
- org.opencontainers.image.url=${{ github.event.repository.html_url }}
- org.opencontainers.image.source=${{ github.event.repository.clone_url }}
- org.opencontainers.image.version=${{ steps.tags.outputs.version }}
- org.opencontainers.image.created=${{ steps.tags.outputs.build_date }}
- org.opencontainers.image.revision=${{ github.sha }}
- org.opencontainers.image.licenses=${{ github.event.repository.license.spdx_id }}
- org.opencontainers.image.documentation=https://dexidp.io/docs/
-
- - name: Run Trivy vulnerability scanner
- uses: aquasecurity/trivy-action@0.7.1
- with:
- image-ref: "ghcr.io/dexidp/dex:${{ steps.tags.outputs.version }}"
- format: "template"
- template: "@/contrib/sarif.tpl"
- output: "trivy-results.sarif"
- if: github.event_name == 'push'
-
- - name: Upload Trivy scan results to GitHub Security tab
- uses: github/codeql-action/upload-sarif@v2
- with:
- sarif_file: "trivy-results.sarif"
- if: github.event_name == 'push'
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
new file mode 100644
index 0000000000..dbf397cbbe
--- /dev/null
+++ b/.github/workflows/release.yaml
@@ -0,0 +1,24 @@
+name: Release
+
+on:
+ push:
+ tags: [ "v[0-9]+.[0-9]+.[0-9]+" ]
+
+permissions:
+ contents: read
+
+jobs:
+ artifacts:
+ name: Artifacts
+ uses: ./.github/workflows/artifacts.yaml
+ with:
+ publish: true
+ secrets:
+ DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
+ DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
+ permissions:
+ attestations: write
+ contents: read
+ packages: write
+ id-token: write
+ security-events: write
diff --git a/.github/workflows/trivydb-cache.yaml b/.github/workflows/trivydb-cache.yaml
new file mode 100644
index 0000000000..f66f29e858
--- /dev/null
+++ b/.github/workflows/trivydb-cache.yaml
@@ -0,0 +1,39 @@
+# Note: This workflow only updates the cache. You should create a separate workflow for your actual Trivy scans.
+# In your scan workflow, set TRIVY_SKIP_DB_UPDATE=true and TRIVY_SKIP_JAVA_DB_UPDATE=true.
+name: Update Trivy Cache
+
+on:
+ schedule:
+ - cron: '0 0 * * *' # Run daily at midnight UTC
+ workflow_dispatch: # Allow manual triggering
+
+jobs:
+ update-trivy-db:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Setup oras
+ uses: oras-project/setup-oras@22ce207df3b08e061f537244349aac6ae1d214f6 # v1.2.4
+
+ - name: Get current date
+ id: date
+ run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
+
+ - name: Download and extract the vulnerability DB
+ run: |
+ mkdir -p $GITHUB_WORKSPACE/.cache/trivy/db
+ oras pull ghcr.io/aquasecurity/trivy-db:2
+ tar -xzf db.tar.gz -C $GITHUB_WORKSPACE/.cache/trivy/db
+ rm db.tar.gz
+
+ - name: Download and extract the Java DB
+ run: |
+ mkdir -p $GITHUB_WORKSPACE/.cache/trivy/java-db
+ oras pull ghcr.io/aquasecurity/trivy-java-db:1
+ tar -xzf javadb.tar.gz -C $GITHUB_WORKSPACE/.cache/trivy/java-db
+ rm javadb.tar.gz
+
+ - name: Cache DBs
+ uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
+ with:
+ path: ${{ github.workspace }}/.cache/trivy
+ key: cache-trivy-${{ steps.date.outputs.date }}
diff --git a/.gitignore b/.gitignore
index 66dc41ccfe..11cfbe81a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+/.devenv/
/.direnv/
/.idea/
/bin/
diff --git a/.golangci.yaml b/.golangci.yaml
new file mode 100644
index 0000000000..9fa3141874
--- /dev/null
+++ b/.golangci.yaml
@@ -0,0 +1,124 @@
+version: "2"
+
+run:
+ timeout: 5m
+
+linters:
+ disable:
+ - staticcheck
+ - errcheck
+ enable:
+ - depguard
+ - dogsled
+ - exhaustive
+ - gochecknoinits
+ # - gocritic
+ - goprintffuncname
+ - govet
+ - ineffassign
+ - misspell
+ - nakedret
+ - nolintlint
+ - prealloc
+ # - revive
+ # - sqlclosecheck
+ # - staticcheck
+ - unconvert
+ - unused
+ - whitespace
+
+ # Disable temporarily until everything works with Go 1.20
+ # - bodyclose
+ # - rowserrcheck
+ # - tparallel
+ # - unparam
+
+ # Disable temporarily until the following issue is resolved: https://github.com/golangci/golangci-lint/issues/3086
+ # - sqlclosecheck
+
+ # TODO: fix linter errors before enabling
+ # - exhaustivestruct
+ # - gochecknoglobals
+ # - errorlint
+ # - gocognit
+ # - godot
+ # - nlreturn
+ # - noctx
+ # - revive
+ # - wrapcheck
+
+ # TODO: fix linter errors before enabling (from original config)
+ # - dupl
+ # - errcheck
+ # - goconst
+ # - gocyclo
+ # - gosec
+ # - lll
+ # - scopelint
+
+ # unused
+ # - goheader
+ # - gomodguard
+
+ # don't enable:
+ # - asciicheck
+ # - funlen
+ # - godox
+ # - goerr113
+ # - gomnd
+ # - interfacer
+ # - maligned
+ # - nestif
+ # - testpackage
+ # - wsl
+
+ exclusions:
+ rules:
+ - linters:
+ - errcheck
+ - noctx
+ path: _test.go
+ presets:
+ - comments
+ - std-error-handling
+
+ settings:
+ misspell:
+ locale: US
+ nolintlint:
+ allow-unused: false # report any unused nolint directives
+ require-specific: false # don't require nolint directives to be specific about which linter is being skipped
+ gocritic:
+ # Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
+ enabled-tags:
+ - diagnostic
+ - experimental
+ - opinionated
+ - style
+ disabled-checks:
+ - importShadow
+ - unnamedResult
+ depguard:
+ rules:
+ deprecated:
+ deny:
+ - pkg: "io/ioutil"
+ desc: "The 'io/ioutil' package is deprecated. Use corresponding 'os' or 'io' functions instead."
+
+formatters:
+ enable:
+ - gci
+ - gofmt
+ - gofumpt
+ - goimports
+ # - golines
+
+ settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - localmodule
+# issues:
+# exclude-dirs:
+# - storage/ent/db # generated ent code
diff --git a/.golangci.yml b/.golangci.yml
deleted file mode 100644
index cfb64a75bf..0000000000
--- a/.golangci.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-run:
- timeout: 4m
-
-linters-settings:
- depguard:
- list-type: blacklist
- include-go-root: true
- packages:
- - io/ioutil
- packages-with-error-message:
- - io/ioutil: "The 'io/ioutil' package is deprecated. Use corresponding 'os' or 'io' functions instead."
- gci:
- local-prefixes: github.com/dexidp/dex
- goimports:
- local-prefixes: github.com/dexidp/dex
-
-
-linters:
- disable-all: true
- enable:
- - bodyclose
- - deadcode
- - depguard
- - dogsled
- - exhaustive
- - exportloopref
- - gci
- - gochecknoinits
- - gocritic
- - gofmt
- - gofumpt
- - goimports
- - goprintffuncname
- - gosimple
- - govet
- - ineffassign
- - misspell
- - nakedret
- - nolintlint
- - prealloc
- - revive
- - rowserrcheck
- - sqlclosecheck
- - staticcheck
- - structcheck
- - stylecheck
- - tparallel
- - unconvert
- - unparam
- - unused
- - varcheck
- - whitespace
-
- # Disable temporarily until everything works with Go 1.18
- # - typecheck
-
- # TODO: fix linter errors before enabling
- # - exhaustivestruct
- # - gochecknoglobals
- # - errorlint
- # - gocognit
- # - godot
- # - nlreturn
- # - noctx
- # - wrapcheck
-
- # TODO: fix linter errors before enabling (from original config)
- # - dupl
- # - errcheck
- # - goconst
- # - gocyclo
- # - gosec
- # - lll
- # - scopelint
-
- # unused
- # - goheader
- # - gomodguard
-
- # don't enable:
- # - asciicheck
- # - funlen
- # - godox
- # - goerr113
- # - gomnd
- # - interfacer
- # - maligned
- # - nestif
- # - testpackage
- # - wsl
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 50f9ba988d..88a835cbdd 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -1,15 +1,26 @@
# Adopters
-This is a list of production adopters of Dex (in alphabetical order):
+This is a list of production adopters of Dex (in alphabetical order).
+
+# Companies
- [Aspect](https://www.aspect.com/) uses Dex for authenticating users across their Kubernetes infrastructure (using Kubernetes OIDC support).
- [Banzai Cloud](https://banzaicloud.com) is using Dex for authenticating to its Pipeline control plane and also to authenticate users against provisioned Kubernetes clusters (via Kubernetes OIDC support).
-- [Chef](https://chef.io) uses Dex for authenticating users in [Chef Automate](https://automate.chef.io/). The code is Open Source, available at [`github.com/chef/automate`](https://github.com/chef/automate).
-- [Elastisys](https://elastisys.com) uses Dex for authentication in their [Compliant Kubernetes](https://compliantkubernetes.io) distribution, including SSO to the custom dashboard, Grafana, Kibana, and Harbor.
+- [Ericsson](https://www.ericsson.com) is using Dex to authenticate access to Kubernetes API server in [Cloud Container Distribution](https://www.ericsson.com/en/portfolio/cloud-software-and-services/cloud-core/cloud-infrastructure/nfvi/cloud-container-distribution).
- [Flant](https://flant.com) uses Dex for providing access to core components of [Managed Kubernetes as a Service](https://flant.com/services/managed-kubernetes-as-a-service), integration with various authentication providers, plugging custom applications.
- [JuliaBox](https://juliabox.com/) is leveraging federated OIDC provided by Dex for authenticating users to their compute infrastructure based on Kubernetes.
+- [Pusher](https://pusher.com) uses Dex for authenticating users across their Kubernetes infrastructure (using Kubernetes OIDC support) in conjunction with the [OAuth2 Proxy](https://github.com/pusher/oauth2_proxy) for protecting web UIs.
+
+# Projects
+
+- [Argo CD](https://argoproj.github.io/cd) integrates Dex to provide convenient Single Sign On capabilities to its web UI and CLI
+- [Chef](https://chef.io) uses Dex for authenticating users in [Chef Automate](https://automate.chef.io/). The code is Open Source, available at [`github.com/chef/automate`](https://github.com/chef/automate).
+- [Elastisys](https://elastisys.com) uses Dex for authentication in [Welkin, The Application Platform for Software Critical to Society](https://elastisys.io/welkin/), including SSO to Grafana, OpenSearch, and Harbor.
- [Kasten](https://www.kasten.io) is using Dex for authenticating access to the dashboard of [K10](https://www.kasten.io/product/), a Kubernetes-native platform for backup, disaster recovery and mobility of Kubernetes applications. K10 is widely used by a variety of customers including large enterprises, financial services, design firms, and IT companies.
+- [Kubeflow](https://www.kubeflow.org/) [uses](https://github.com/kubeflow/manifests#dex) Dex as one of its components in the Kubeflow Platform for external OIDC authentication.
- [Kyma](https://kyma-project.io) is using Dex to authenticate access to Kubernetes API server (even for managed Kubernetes like Google Kubernetes Engine or Azure Kubernetes Service) and for protecting web UI of [Kyma Console](https://github.com/kyma-project/console) and other UIs integrated in Kyma ([Grafana](https://github.com/grafana/grafana), [Loki](https://github.com/grafana/loki), and [Jaeger](https://github.com/jaegertracing/jaeger)). Kyma is an open-source project ([`github.com/kyma-project`](https://github.com/kyma-project/kyma)) designed natively on Kubernetes, that allows you to extend and customize your applications in a quick and modern way, using serverless computing or microservice architecture.
-- [Pusher](https://pusher.com) uses Dex for authenticating users across their Kubernetes infrastructure (using Kubernetes OIDC support) in conjunction with the [OAuth2 Proxy](https://github.com/pusher/oauth2_proxy) for protecting web UIs.
+- [LitmusChaos](https://litmuschaos.io/) uses Dex to [implement](https://docs.litmuschaos.io/docs/user-guides/chaoscenter-oauth-dex-installation#deploy-dex-oidc-provider) OAuth2 login support in ChaosCenter, its centralized chaos management tool.
+- [LLMariner](https://llmariner.ai/) uses Dex for [user management](https://llmariner.ai/docs/features/user_management/).
- [Pydio](https://pydio.com/) Pydio Cells is an open source sync & share platform written in Go. Cells is using Dex as an OIDC service for authentication and authorizations. Check out [Pydio Cells repository](https://github.com/pydio/cells) for more information and/or to contribute.
- [sigstore](https://sigstore.dev) uses Dex for authentication in their public Fulcio instance, which is a certificate authority for code signing certificates bound to OIDC-based identities.
+- [Terrakube](https://docs.terrakube.io/) relies on Dex for [user authentication](https://docs.terrakube.io/getting-started/deployment/user-authentication-dex). Its Helm chart uses Dex as a dependency.
diff --git a/Dockerfile b/Dockerfile
index 3462ae52ac..7f0559c853 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,48 +1,61 @@
ARG BASE_IMAGE=alpine
-FROM golang:1.19.1-alpine3.16 AS builder
+FROM --platform=$BUILDPLATFORM tonistiigi/xx:1.9.0@sha256:c64defb9ed5a91eacb37f96ccc3d4cd72521c4bd18d5442905b95e2226b0e707 AS xx
-WORKDIR /usr/local/src/dex
+FROM --platform=$BUILDPLATFORM golang:1.25.5-alpine3.22@sha256:3587db7cc96576822c606d119729370dbf581931c5f43ac6d3fa03ab4ed85a10 AS builder
-RUN apk add --no-cache --update alpine-sdk ca-certificates openssl
+COPY --from=xx / /
-ARG TARGETOS
-ARG TARGETARCH
-ARG TARGETVARIANT=""
+RUN apk add --update alpine-sdk ca-certificates openssl clang lld
+
+ARG TARGETPLATFORM
+
+RUN xx-apk --update add musl-dev gcc
-ENV GOOS=${TARGETOS} GOARCH=${TARGETARCH} GOARM=${TARGETVARIANT}
+# lld has issues building static binaries for ppc so prefer ld for it
+RUN [ "$(xx-info arch)" != "ppc64le" ] || XX_CC_PREFER_LINKER=ld xx-clang --setup-target-triple
+
+RUN xx-go --wrap
+
+WORKDIR /usr/local/src/dex
ARG GOPROXY
+ENV CGO_ENABLED=1
+
COPY go.mod go.sum ./
COPY api/v2/go.mod api/v2/go.sum ./api/v2/
RUN go mod download
COPY . .
+# Propagate Dex version from build args to the build environment
+ARG VERSION
RUN make release-binary
-FROM alpine:3.16.2 AS stager
+RUN xx-verify /go/bin/dex && xx-verify /go/bin/docker-entrypoint
+
+FROM alpine:3.23.2@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 AS stager
RUN mkdir -p /var/dex
RUN mkdir -p /etc/dex
COPY config.docker.yaml /etc/dex/
-FROM alpine:3.16.2 AS gomplate
+FROM alpine:3.23.2@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 AS gomplate
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
-ENV GOMPLATE_VERSION=v3.11.2
+ENV GOMPLATE_VERSION=v4.3.3
RUN wget -O /usr/local/bin/gomplate \
"https://github.com/hairyhenderson/gomplate/releases/download/${GOMPLATE_VERSION}/gomplate_${TARGETOS:-linux}-${TARGETARCH:-amd64}${TARGETVARIANT}" \
&& chmod +x /usr/local/bin/gomplate
# For Dependabot to detect base image versions
-FROM alpine:3.16.2 AS alpine
-FROM gcr.io/distroless/static:latest AS distroless
+FROM alpine:3.23.2@sha256:865b95f46d98cf867a156fe4a135ad3fe50d2056aa3f25ed31662dff6da4eb62 AS alpine
+FROM gcr.io/distroless/static-debian13:nonroot@sha256:b5b9fd04c8dcf72a173183c0b7dee47e053e002246b308a59f3441db7b8b9cc4 AS distroless
FROM $BASE_IMAGE
diff --git a/Makefile b/Makefile
index 9c55ad2b11..c9a568cbfa 100644
--- a/Makefile
+++ b/Makefile
@@ -1,42 +1,50 @@
-OS = $(shell uname | tr A-Z a-z)
-
export PATH := $(abspath bin/protoc/bin/):$(abspath bin/):${PATH}
-PROJ=dex
-ORG_PATH=github.com/dexidp
-REPO_PATH=$(ORG_PATH)/$(PROJ)
-
-VERSION ?= $(shell ./scripts/git-version)
+OS = $(shell uname | tr A-Z a-z)
-DOCKER_REPO=quay.io/dexidp/dex
-DOCKER_IMAGE=$(DOCKER_REPO):$(VERSION)
+user=$(shell id -u -n)
+group=$(shell id -g -n)
$( shell mkdir -p bin )
-user=$(shell id -u -n)
-group=$(shell id -g -n)
+PROJ = dex
+ORG_PATH = github.com/dexidp
+REPO_PATH = $(ORG_PATH)/$(PROJ)
+VERSION ?= $(shell ./scripts/git-version)
-export GOBIN=$(PWD)/bin
+export GOBIN=$(PWD)/bin
LD_FLAGS="-w -X main.version=$(VERSION)"
# Dependency versions
+GOLANGCI_VERSION = 2.4.0
+GOTESTSUM_VERSION ?= 1.12.0
-KIND_NODE_IMAGE = "kindest/node:v1.19.11@sha256:07db187ae84b4b7de440a73886f008cf903fcf5764ba8106a9fd5243d6f32729"
-KIND_TMP_DIR = "$(PWD)/bin/test/dex-kind-kubeconfig"
+PROTOC_VERSION = 29.3
+PROTOC_GEN_GO_VERSION = 1.36.5
+PROTOC_GEN_GO_GRPC_VERSION = 1.5.1
-.PHONY: generate
-generate:
- @go generate $(REPO_PATH)/storage/ent/
+KIND_VERSION = 0.22.0
+KIND_NODE_IMAGE = "kindest/node:v1.25.3@sha256:cd248d1438192f7814fbca8fede13cfe5b9918746dfa12583976158a834fd5c5"
+KIND_TMP_DIR = "$(PWD)/bin/test/dex-kind-kubeconfig"
+
+
+##@ Build
+
+build: bin/dex ## Build Dex binaries.
-build: generate bin/dex
+examples: bin/grpc-client bin/example-app ## Build example app.
+
+.PHONY: release-binary
+release-binary: LD_FLAGS = "-w -X main.version=$(VERSION) -extldflags \"-static\""
+release-binary: ## Build release binaries (used to build a final container image).
+ @go build -o /go/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
+ @go build -o /go/bin/docker-entrypoint -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/docker-entrypoint
bin/dex:
@mkdir -p bin/
@go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
-examples: bin/grpc-client bin/example-app
-
bin/grpc-client:
@mkdir -p bin/
@cd examples/ && go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/grpc-client
@@ -45,95 +53,29 @@ bin/example-app:
@mkdir -p bin/
@cd examples/ && go install -v -ldflags $(LD_FLAGS) $(REPO_PATH)/examples/example-app
-.PHONY: release-binary
-release-binary: LD_FLAGS = "-w -X main.version=$(VERSION) -extldflags \"-static\""
-release-binary: generate
- @go build -o /go/bin/dex -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/dex
- @go build -o /go/bin/docker-entrypoint -v -ldflags $(LD_FLAGS) $(REPO_PATH)/cmd/docker-entrypoint
-docker-compose.override.yaml:
- cp docker-compose.override.yaml.dist docker-compose.override.yaml
+##@ Generate
-.PHONY: up
-up: docker-compose.override.yaml ## Launch the development environment
- @ if [ docker-compose.override.yaml -ot docker-compose.override.yaml.dist ]; then diff -u docker-compose.override.yaml docker-compose.override.yaml.dist || (echo "!!! The distributed docker-compose.override.yaml example changed. Please update your file accordingly (or at least touch it). !!!" && false); fi
- docker-compose up -d
-
-.PHONY: down
-down: clear ## Destroy the development environment
- docker-compose down --volumes --remove-orphans --rmi local
-
-test:
- @go test -v ./...
-
-testrace:
- @go test -v --race ./...
-
-.PHONY: kind-up kind-down kind-tests
-kind-up:
- @mkdir -p bin/test
- @kind create cluster --image ${KIND_NODE_IMAGE} --kubeconfig ${KIND_TMP_DIR}
-
-kind-down:
- @kind delete cluster
- rm ${KIND_TMP_DIR}
-
-kind-tests: export DEX_KUBERNETES_CONFIG_PATH=${KIND_TMP_DIR}
-kind-tests: testall
-
-.PHONY: lint lint-fix
-lint: ## Run linter
- golangci-lint run
-
-.PHONY: fix
-fix: ## Fix lint violations
- golangci-lint run --fix
-
-.PHONY: docker-image
-docker-image:
- @sudo docker build -t $(DOCKER_IMAGE) .
-
-.PHONY: verify-proto
-verify-proto: proto
- @./scripts/git-diff
-
-clean:
- @rm -rf bin/
-
-testall: testrace
-
-FORCE:
+.PHONY: generate
+generate: generate-proto generate-proto-internal generate-ent go-mod-tidy ## Run all generators.
-.PHONY: test testrace testall
+.PHONY: generate-ent
+generate-ent: ## Generate code for database ORM.
+ @go generate $(REPO_PATH)/storage/ent/
-.PHONY: proto
-proto:
+.PHONY: generate-proto
+generate-proto: ## Generate the Dex client's protobuf code.
@protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. api/v2/*.proto
@protoc --go_out=paths=source_relative:. --go-grpc_out=paths=source_relative:. api/*.proto
- #@cp api/v2/*.proto api/
-.PHONY: proto-internal
-proto-internal:
+.PHONY: generate-proto-internal
+generate-proto-internal: ## Generate protobuf code for token encoding.
@protoc --go_out=paths=source_relative:. server/internal/*.proto
-# Dependency versions
-GOLANGCI_VERSION = 1.46.0
-GOTESTSUM_VERSION ?= 1.7.0
-PROTOC_VERSION = 3.15.6
-PROTOC_GEN_GO_VERSION = 1.26.0
-PROTOC_GEN_GO_GRPC_VERSION = 1.1.0
-KIND_VERSION = 0.11.1
-
-deps: bin/gotestsum bin/golangci-lint bin/protoc bin/protoc-gen-go bin/protoc-gen-go-grpc bin/kind
-
-bin/gotestsum:
- @mkdir -p bin
- curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_$(shell uname | tr A-Z a-z)_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum
- @chmod +x ./bin/gotestsum
-
-bin/golangci-lint:
- @mkdir -p bin
- curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
+go-mod-tidy: ## Run go mod tidy for all targets.
+ @go mod tidy
+ @cd examples/ && go mod tidy
+ @cd api/v2/ && go mod tidy
bin/protoc:
@mkdir -p bin/protoc
@@ -156,7 +98,112 @@ bin/protoc-gen-go-grpc:
curl -L https://github.com/grpc/grpc-go/releases/download/cmd/protoc-gen-go-grpc/v${PROTOC_GEN_GO_GRPC_VERSION}/protoc-gen-go-grpc.v${PROTOC_GEN_GO_GRPC_VERSION}.$(shell uname | tr A-Z a-z).amd64.tar.gz | tar -zOxf - ./protoc-gen-go-grpc > ./bin/protoc-gen-go-grpc
@chmod +x ./bin/protoc-gen-go-grpc
+##@ Verify
+
+verify: generate ## Verify that all the code was generated and committed to repository.
+ @git diff --exit-code
+
+.PHONY: verify-proto
+verify-proto: generate-proto ## Verify that the Dex client's protobuf code was generated.
+ @git diff --exit-code
+
+.PHONY: verify-proto
+verify-proto-internal: generate-proto-internal ## Verify internal protobuf code for token encoding was generated.
+ @git diff --exit-code
+
+.PHONY: verify-ent
+verify-ent: generate-ent ## Verify code for database ORM was generated.
+ @git diff --exit-code
+
+.PHONY: verify-go-mod
+verify-go-mod: go-mod-tidy ## Check that go.mod and go.sum formatted according to the changes.
+ @git diff --exit-code
+
+##@ Test and Lint
+
+deps: bin/gotestsum bin/golangci-lint bin/protoc bin/protoc-gen-go bin/protoc-gen-go-grpc bin/kind ## Install dev dependencies.
+
+.PHONY: test testrace testall
+test: ## Test go code.
+ @go test -v ./...
+
+testrace: ## Test go code and check for possible race conditions.
+ @go test -v --race ./...
+
+testall: testrace ## Run all tests for go code.
+
+.PHONY: lint
+lint: ## Run linter.
+ @golangci-lint version
+ @golangci-lint run
+
+.PHONY: fix
+fix: ## Fix lint violations.
+ @golangci-lint version
+ @golangci-lint fmt
+
+docker-compose.override.yaml:
+ cp docker-compose.override.yaml.dist docker-compose.override.yaml
+
+.PHONY: up
+up: docker-compose.override.yaml ## Launch the development environment.
+ @ if [ docker-compose.override.yaml -ot docker-compose.override.yaml.dist ]; then diff -u docker-compose.override.yaml docker-compose.override.yaml.dist || (echo "!!! The distributed docker-compose.override.yaml example changed. Please update your file accordingly (or at least touch it). !!!" && false); fi
+ docker-compose up -d
+
+.PHONY: down
+down: clear ## Destroy the development environment.
+ docker-compose down --volumes --remove-orphans --rmi local
+
+.PHONY: kind-up kind-down kind-tests
+kind-up: ## Create a kind cluster.
+ @mkdir -p bin/test
+ @kind create cluster --image ${KIND_NODE_IMAGE} --kubeconfig ${KIND_TMP_DIR} --name dex-tests
+
+kind-tests: export DEX_KUBERNETES_CONFIG_PATH=${KIND_TMP_DIR}
+kind-tests: testall ## Run test on kind cluster (kind cluster must be created).
+
+kind-down: ## Delete the kind cluster.
+ @kind delete cluster --name dex-tests
+ rm ${KIND_TMP_DIR}
+
+bin/golangci-lint:
+ @mkdir -p bin
+ curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
+
+bin/gotestsum:
+ @mkdir -p bin
+ curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_$(shell uname | tr A-Z a-z)_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum
+ @chmod +x ./bin/gotestsum
+
bin/kind:
@mkdir -p bin
curl -L https://github.com/kubernetes-sigs/kind/releases/download/v${KIND_VERSION}/kind-$(shell uname | tr A-Z a-z)-amd64 > ./bin/kind
@chmod +x ./bin/kind
+
+##@ Clean
+clean: ## Delete all builds and downloaded dependencies.
+ @rm -rf bin/
+
+
+FORMATTING_BEGIN_YELLOW = \033[0;33m
+FORMATTING_BEGIN_BLUE = \033[36m
+FORMATTING_END = \033[0m
+
+.PHONY: help
+help:
+ @printf -- "${FORMATTING_BEGIN_BLUE}%s${FORMATTING_END}\n" \
+ "" \
+ " ___ " \
+ " / _ \_____ __ " \
+ " / // / -_) \ / " \
+ " /____/\__/_\_\ " \
+ "" \
+ "-----------------------" \
+ ""
+ @awk 'BEGIN {\
+ FS = ":.*##"; \
+ printf "Usage: ${FORMATTING_BEGIN_BLUE}OPTION${FORMATTING_END}= make ${FORMATTING_BEGIN_YELLOW}${FORMATTING_END}\n"\
+ } \
+ /^[a-zA-Z0-9_-]+:.*?##/ { printf " ${FORMATTING_BEGIN_BLUE}%-46s${FORMATTING_END} %s\n", $$1, $$2 } \
+ /^.?.?##~/ { printf " %-46s${FORMATTING_BEGIN_YELLOW}%-46s${FORMATTING_END}\n", "", substr($$1, 6) } \
+ /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
diff --git a/README.md b/README.md
index 271376d65e..dac886ee4a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
# dex - A federated OpenID Connect provider
-
+
+[](https://api.securityscorecards.dev/projects/github.com/dexidp/dex)
[](https://goreportcard.com/report/github.com/dexidp/dex)
[](https://gitpod.io/#https://github.com/dexidp/dex)
@@ -12,7 +13,7 @@ Dex acts as a portal to other identity providers through ["connectors."](#connec
## ID Tokens
-ID Tokens are an OAuth2 extension introduced by OpenID Connect and dex's primary feature. ID Tokens are [JSON Web Tokens][jwt-io] (JWTs) signed by dex and returned as part of the OAuth2 response that attest to the end user's identity. An example JWT might look like:
+ID Tokens are an OAuth2 extension introduced by OpenID Connect and dex's primary feature. ID Tokens are [JSON Web Tokens][jwt-io] (JWTs) signed by dex and returned as part of the OAuth2 response that attests to the end user's identity. An example JWT might look like:
```
eyJhbGciOiJSUzI1NiIsImtpZCI6IjlkNDQ3NDFmNzczYjkzOGNmNjVkZDMyNjY4NWI4NjE4MGMzMjRkOTkifQ.eyJpc3MiOiJodHRwOi8vMTI3LjAuMC4xOjU1NTYvZGV4Iiwic3ViIjoiQ2djeU16UXlOelE1RWdabmFYUm9kV0kiLCJhdWQiOiJleGFtcGxlLWFwcCIsImV4cCI6MTQ5Mjg4MjA0MiwiaWF0IjoxNDkyNzk1NjQyLCJhdF9oYXNoIjoiYmk5NmdPWFpTaHZsV1l0YWw5RXFpdyIsImVtYWlsIjoiZXJpYy5jaGlhbmdAY29yZW9zLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJncm91cHMiOlsiYWRtaW5zIiwiZGV2ZWxvcGVycyJdLCJuYW1lIjoiRXJpYyBDaGlhbmcifQ.OhROPq_0eP-zsQRjg87KZ4wGkjiQGnTi5QuG877AdJDb3R2ZCOk2Vkf5SdP8cPyb3VMqL32G4hLDayniiv8f1_ZXAde0sKrayfQ10XAXFgZl_P1yilkLdknxn6nbhDRVllpWcB12ki9vmAxklAr0B1C4kr5nI3-BZLrFcUR5sQbxwJj4oW1OuG6jJCNGHXGNTBTNEaM28eD-9nhfBeuBTzzO7BKwPsojjj4C9ogU4JQhGvm_l4yfVi0boSx8c0FX3JsiB0yLa1ZdJVWVl9m90XmbWRSD85pNDQHcWZP9hR6CMgbvGkZsgjG32qeRwUL_eNkNowSBNWLrGNPoON1gMg
@@ -49,8 +50,8 @@ For details on how to request or validate an ID Token, see [_"Writing apps that
Dex runs natively on top of any Kubernetes cluster using Custom Resource Definitions and can drive API server authentication through the OpenID Connect plugin. Clients, such as the [`kubernetes-dashboard`](https://github.com/kubernetes/dashboard) and `kubectl`, can act on behalf of users who can login to the cluster through any identity provider dex supports.
-* More docs for running dex as a Kubernetes authenticator can be found [here](https://dexidp.io/docs/kubernetes/).
-* You can find more about companies and projects, which uses dex, [here](./ADOPTERS.md).
+* More docs for running dex as a Kubernetes authenticator can be found [here](https://dexidp.io/docs/guides/kubernetes/).
+* You can find more about companies and projects which use dex, [here](./ADOPTERS.md).
## Connectors
@@ -77,8 +78,8 @@ Dex implements the following connectors:
| [Microsoft](https://dexidp.io/docs/connectors/microsoft/) | yes | yes | no | beta | |
| [AuthProxy](https://dexidp.io/docs/connectors/authproxy/) | no | yes | no | alpha | Authentication proxies such as Apache2 mod_auth, etc. |
| [Bitbucket Cloud](https://dexidp.io/docs/connectors/bitbucketcloud/) | yes | yes | no | alpha | |
-| [OpenShift](https://dexidp.io/docs/connectors/openshift/) | no | yes | no | alpha | |
-| [Atlassian Crowd](https://dexidp.io/docs/connectors/atlassiancrowd/) | yes | yes | yes * | beta | preferred_username claim must be configured through config |
+| [OpenShift](https://dexidp.io/docs/connectors/openshift/) | yes | yes | no | alpha | |
+| [Atlassian Crowd](https://dexidp.io/docs/connectors/atlassian-crowd/) | yes | yes | yes * | beta | preferred_username claim must be configured through config |
| [Gitea](https://dexidp.io/docs/connectors/gitea/) | yes | no | yes | beta | |
| [OpenStack Keystone](https://dexidp.io/docs/connectors/keystone/) | yes | yes | no | alpha | |
@@ -95,7 +96,7 @@ All changes or deprecations of connector features will be announced in the [rele
* [Getting started](https://dexidp.io/docs/getting-started/)
* [Intro to OpenID Connect](https://dexidp.io/docs/openid-connect/)
* [Writing apps that use dex][using-dex]
-* [What's new in v2](https://dexidp.io/docs/v2/)
+* [What's new in v2](https://dexidp.io/docs/archive/v2/)
* [Custom scopes, claims, and client features](https://dexidp.io/docs/custom-scopes-claims-clients/)
* [Storage options](https://dexidp.io/docs/storage/)
* [gRPC API](https://dexidp.io/docs/api/)
@@ -120,7 +121,7 @@ Please see our [security policy](.github/SECURITY.md) for details about reportin
[scopes]: https://dexidp.io/docs/custom-scopes-claims-clients/#scopes
[using-dex]: https://dexidp.io/docs/using-dex/
[jwt-io]: https://jwt.io/
-[kubernetes]: http://kubernetes.io/docs/admin/authentication/#openid-connect-tokens
+[kubernetes]: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens
[aws-sts]: https://docs.aws.amazon.com/STS/latest/APIReference/Welcome.html
[go-oidc]: https://github.com/coreos/go-oidc
[issue-1065]: https://github.com/dexidp/dex/issues/1065
@@ -138,6 +139,8 @@ For the best developer experience, install [Nix](https://builtwithnix.org/) and
Alternatively, install Go and Docker manually or using a package manager. Install the rest of the dependencies by running `make deps`.
+For release process, please read the [release documentation](https://dexidp.io/docs/development/releases/).
+
## License
The project is licensed under the [Apache License, Version 2.0](LICENSE).
diff --git a/api/api.pb.go b/api/api.pb.go
index 6d1c2ca82e..702d3758fa 100644
--- a/api/api.pb.go
+++ b/api/api.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.26.0
-// protoc v3.15.6
+// protoc-gen-go v1.36.5
+// protoc v5.29.3
// source: api/api.proto
package api
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
+ unsafe "unsafe"
)
const (
@@ -22,26 +23,23 @@ const (
// Client represents an OAuth2 client.
type Client struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
+ RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
+ TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
+ Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"`
+ Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
+ LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
- RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
- TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
- Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"`
- Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
- LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *Client) Reset() {
*x = Client{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *Client) String() string {
@@ -52,7 +50,7 @@ func (*Client) ProtoMessage() {}
func (x *Client) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[0]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -118,20 +116,17 @@ func (x *Client) GetLogoUrl() string {
// CreateClientReq is a request to make a client.
type CreateClientReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreateClientReq) Reset() {
*x = CreateClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[1]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreateClientReq) String() string {
@@ -142,7 +137,7 @@ func (*CreateClientReq) ProtoMessage() {}
func (x *CreateClientReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[1]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -166,21 +161,18 @@ func (x *CreateClientReq) GetClient() *Client {
// CreateClientResp returns the response from creating a client.
type CreateClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
+ Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"`
unknownFields protoimpl.UnknownFields
-
- AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
- Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreateClientResp) Reset() {
*x = CreateClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[2]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreateClientResp) String() string {
@@ -191,7 +183,7 @@ func (*CreateClientResp) ProtoMessage() {}
func (x *CreateClientResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[2]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -222,21 +214,18 @@ func (x *CreateClientResp) GetClient() *Client {
// DeleteClientReq is a request to delete a client.
type DeleteClientReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The ID of the client.
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *DeleteClientReq) Reset() {
*x = DeleteClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[3]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeleteClientReq) String() string {
@@ -247,7 +236,7 @@ func (*DeleteClientReq) ProtoMessage() {}
func (x *DeleteClientReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[3]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -271,20 +260,17 @@ func (x *DeleteClientReq) GetId() string {
// DeleteClientResp determines if the client is deleted successfully.
type DeleteClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeleteClientResp) Reset() {
*x = DeleteClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[4]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeleteClientResp) String() string {
@@ -295,7 +281,7 @@ func (*DeleteClientResp) ProtoMessage() {}
func (x *DeleteClientResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[4]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -319,24 +305,21 @@ func (x *DeleteClientResp) GetNotFound() bool {
// UpdateClientReq is a request to update an existing client.
type UpdateClientReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
+ TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
+ Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
+ LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
- TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
- Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
- LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdateClientReq) Reset() {
*x = UpdateClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[5]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdateClientReq) String() string {
@@ -347,7 +330,7 @@ func (*UpdateClientReq) ProtoMessage() {}
func (x *UpdateClientReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[5]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -399,20 +382,17 @@ func (x *UpdateClientReq) GetLogoUrl() string {
// UpdateClientResp returns the response from updating a client.
type UpdateClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdateClientResp) Reset() {
*x = UpdateClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[6]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdateClientResp) String() string {
@@ -423,7 +403,7 @@ func (*UpdateClientResp) ProtoMessage() {}
func (x *UpdateClientResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[6]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -447,24 +427,21 @@ func (x *UpdateClientResp) GetNotFound() bool {
// Password is an email for password mapping managed by the storage.
type Password struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
// Currently we do not accept plain text passwords. Could be an option in the future.
- Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
- Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
- UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
+ Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
+ UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *Password) Reset() {
*x = Password{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[7]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *Password) String() string {
@@ -475,7 +452,7 @@ func (*Password) ProtoMessage() {}
func (x *Password) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[7]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -520,20 +497,17 @@ func (x *Password) GetUserId() string {
// CreatePasswordReq is a request to make a password.
type CreatePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreatePasswordReq) Reset() {
*x = CreatePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[8]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreatePasswordReq) String() string {
@@ -544,7 +518,7 @@ func (*CreatePasswordReq) ProtoMessage() {}
func (x *CreatePasswordReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[8]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -568,20 +542,17 @@ func (x *CreatePasswordReq) GetPassword() *Password {
// CreatePasswordResp returns the response from creating a password.
type CreatePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
unknownFields protoimpl.UnknownFields
-
- AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreatePasswordResp) Reset() {
*x = CreatePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[9]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreatePasswordResp) String() string {
@@ -592,7 +563,7 @@ func (*CreatePasswordResp) ProtoMessage() {}
func (x *CreatePasswordResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[9]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -616,23 +587,20 @@ func (x *CreatePasswordResp) GetAlreadyExists() bool {
// UpdatePasswordReq is a request to modify an existing password.
type UpdatePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The email used to lookup the password. This field cannot be modified
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
- NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"`
- NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"`
+ NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *UpdatePasswordReq) Reset() {
*x = UpdatePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[10]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdatePasswordReq) String() string {
@@ -643,7 +611,7 @@ func (*UpdatePasswordReq) ProtoMessage() {}
func (x *UpdatePasswordReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[10]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -681,20 +649,17 @@ func (x *UpdatePasswordReq) GetNewUsername() string {
// UpdatePasswordResp returns the response from modifying an existing password.
type UpdatePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdatePasswordResp) Reset() {
*x = UpdatePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[11]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdatePasswordResp) String() string {
@@ -705,7 +670,7 @@ func (*UpdatePasswordResp) ProtoMessage() {}
func (x *UpdatePasswordResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[11]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -729,20 +694,17 @@ func (x *UpdatePasswordResp) GetNotFound() bool {
// DeletePasswordReq is a request to delete a password.
type DeletePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeletePasswordReq) Reset() {
*x = DeletePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[12]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeletePasswordReq) String() string {
@@ -753,7 +715,7 @@ func (*DeletePasswordReq) ProtoMessage() {}
func (x *DeletePasswordReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[12]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -777,20 +739,17 @@ func (x *DeletePasswordReq) GetEmail() string {
// DeletePasswordResp returns the response from deleting a password.
type DeletePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeletePasswordResp) Reset() {
*x = DeletePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[13]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeletePasswordResp) String() string {
@@ -801,7 +760,7 @@ func (*DeletePasswordResp) ProtoMessage() {}
func (x *DeletePasswordResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[13]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -825,18 +784,16 @@ func (x *DeletePasswordResp) GetNotFound() bool {
// ListPasswordReq is a request to enumerate passwords.
type ListPasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *ListPasswordReq) Reset() {
*x = ListPasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[14]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListPasswordReq) String() string {
@@ -847,7 +804,7 @@ func (*ListPasswordReq) ProtoMessage() {}
func (x *ListPasswordReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[14]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -864,20 +821,17 @@ func (*ListPasswordReq) Descriptor() ([]byte, []int) {
// ListPasswordResp returns a list of passwords.
type ListPasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *ListPasswordResp) Reset() {
*x = ListPasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[15]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListPasswordResp) String() string {
@@ -888,7 +842,7 @@ func (*ListPasswordResp) ProtoMessage() {}
func (x *ListPasswordResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[15]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -912,18 +866,16 @@ func (x *ListPasswordResp) GetPasswords() []*Password {
// VersionReq is a request to fetch version info.
type VersionReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *VersionReq) Reset() {
*x = VersionReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[16]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VersionReq) String() string {
@@ -934,7 +886,7 @@ func (*VersionReq) ProtoMessage() {}
func (x *VersionReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[16]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -951,24 +903,21 @@ func (*VersionReq) Descriptor() ([]byte, []int) {
// VersionResp holds the version info of components.
type VersionResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// Semantic version of the server.
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
- // Numeric version of the API. It increases everytime a new call is added to the API.
+ // Numeric version of the API. It increases every time a new call is added to the API.
// Clients should use this info to determine if the server supports specific features.
- Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"`
+ Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *VersionResp) Reset() {
*x = VersionResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[17]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VersionResp) String() string {
@@ -979,7 +928,7 @@ func (*VersionResp) ProtoMessage() {}
func (x *VersionResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[17]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1010,24 +959,21 @@ func (x *VersionResp) GetApi() int32 {
// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage.
type RefreshTokenRef struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID of the refresh token.
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
- CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
- LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
+ LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenRef) Reset() {
*x = RefreshTokenRef{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[18]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[18]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RefreshTokenRef) String() string {
@@ -1038,7 +984,7 @@ func (*RefreshTokenRef) ProtoMessage() {}
func (x *RefreshTokenRef) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[18]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1083,21 +1029,18 @@ func (x *RefreshTokenRef) GetLastUsed() int64 {
// ListRefreshReq is a request to enumerate the refresh tokens of a user.
type ListRefreshReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The "sub" claim returned in the ID Token.
- UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *ListRefreshReq) Reset() {
*x = ListRefreshReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[19]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[19]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListRefreshReq) String() string {
@@ -1108,7 +1051,7 @@ func (*ListRefreshReq) ProtoMessage() {}
func (x *ListRefreshReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[19]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1132,20 +1075,17 @@ func (x *ListRefreshReq) GetUserId() string {
// ListRefreshResp returns a list of refresh tokens for a user.
type ListRefreshResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
unknownFields protoimpl.UnknownFields
-
- RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *ListRefreshResp) Reset() {
*x = ListRefreshResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[20]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[20]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListRefreshResp) String() string {
@@ -1156,7 +1096,7 @@ func (*ListRefreshResp) ProtoMessage() {}
func (x *ListRefreshResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[20]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1180,22 +1120,19 @@ func (x *ListRefreshResp) GetRefreshTokens() []*RefreshTokenRef {
// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair.
type RevokeRefreshReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The "sub" claim returned in the ID Token.
- UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
- ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RevokeRefreshReq) Reset() {
*x = RevokeRefreshReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[21]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[21]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RevokeRefreshReq) String() string {
@@ -1206,7 +1143,7 @@ func (*RevokeRefreshReq) ProtoMessage() {}
func (x *RevokeRefreshReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[21]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1237,21 +1174,18 @@ func (x *RevokeRefreshReq) GetClientId() string {
// RevokeRefreshResp determines if the refresh token is revoked successfully.
type RevokeRefreshResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// Set to true is refresh token was not found and token could not be revoked.
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RevokeRefreshResp) Reset() {
*x = RevokeRefreshResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[22]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[22]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RevokeRefreshResp) String() string {
@@ -1262,7 +1196,7 @@ func (*RevokeRefreshResp) ProtoMessage() {}
func (x *RevokeRefreshResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[22]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1285,21 +1219,18 @@ func (x *RevokeRefreshResp) GetNotFound() bool {
}
type VerifyPasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
- Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordReq) Reset() {
*x = VerifyPasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[23]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[23]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordReq) String() string {
@@ -1310,7 +1241,7 @@ func (*VerifyPasswordReq) ProtoMessage() {}
func (x *VerifyPasswordReq) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[23]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1340,21 +1271,18 @@ func (x *VerifyPasswordReq) GetPassword() string {
}
type VerifyPasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"`
+ NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"`
- NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordResp) Reset() {
*x = VerifyPasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_api_proto_msgTypes[24]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_api_proto_msgTypes[24]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordResp) String() string {
@@ -1365,7 +1293,7 @@ func (*VerifyPasswordResp) ProtoMessage() {}
func (x *VerifyPasswordResp) ProtoReflect() protoreflect.Message {
mi := &file_api_api_proto_msgTypes[24]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1396,7 +1324,7 @@ func (x *VerifyPasswordResp) GetNotFound() bool {
var File_api_api_proto protoreflect.FileDescriptor
-var file_api_api_proto_rawDesc = []byte{
+var file_api_api_proto_rawDesc = string([]byte{
0x0a, 0x0d, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x03, 0x61, 0x70, 0x69, 0x22, 0xc1, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12,
0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12,
@@ -1559,22 +1487,22 @@ var file_api_api_proto_rawDesc = []byte{
0x2e, 0x64, 0x65, 0x78, 0x2e, 0x61, 0x70, 0x69, 0x5a, 0x19, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x78, 0x69, 0x64, 0x70, 0x2f, 0x64, 0x65, 0x78, 0x2f,
0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
+})
var (
file_api_api_proto_rawDescOnce sync.Once
- file_api_api_proto_rawDescData = file_api_api_proto_rawDesc
+ file_api_api_proto_rawDescData []byte
)
func file_api_api_proto_rawDescGZIP() []byte {
file_api_api_proto_rawDescOnce.Do(func() {
- file_api_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_api_proto_rawDescData)
+ file_api_api_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_api_proto_rawDesc), len(file_api_api_proto_rawDesc)))
})
return file_api_api_proto_rawDescData
}
var file_api_api_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
-var file_api_api_proto_goTypes = []interface{}{
+var file_api_api_proto_goTypes = []any{
(*Client)(nil), // 0: api.Client
(*CreateClientReq)(nil), // 1: api.CreateClientReq
(*CreateClientResp)(nil), // 2: api.CreateClientResp
@@ -1641,313 +1569,11 @@ func file_api_api_proto_init() {
if File_api_api_proto != nil {
return
}
- if !protoimpl.UnsafeEnabled {
- file_api_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Client); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Password); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreatePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreatePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdatePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdatePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeletePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeletePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListPasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListPasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VersionReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VersionResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RefreshTokenRef); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListRefreshReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListRefreshResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RevokeRefreshReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RevokeRefreshResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VerifyPasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_api_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VerifyPasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- }
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_api_api_proto_rawDesc,
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_api_proto_rawDesc), len(file_api_api_proto_rawDesc)),
NumEnums: 0,
NumMessages: 25,
NumExtensions: 0,
@@ -1958,7 +1584,6 @@ func file_api_api_proto_init() {
MessageInfos: file_api_api_proto_msgTypes,
}.Build()
File_api_api_proto = out.File
- file_api_api_proto_rawDesc = nil
file_api_api_proto_goTypes = nil
file_api_api_proto_depIdxs = nil
}
diff --git a/api/api.proto b/api/api.proto
index 7d25771a6e..cfb7979c5a 100644
--- a/api/api.proto
+++ b/api/api.proto
@@ -112,7 +112,7 @@ message VersionReq {}
message VersionResp {
// Semantic version of the server.
string server = 1;
- // Numeric version of the API. It increases everytime a new call is added to the API.
+ // Numeric version of the API. It increases every time a new call is added to the API.
// Clients should use this info to determine if the server supports specific features.
int32 api = 2;
}
diff --git a/api/api_grpc.pb.go b/api/api_grpc.pb.go
index e8c9873cb5..aeeaa508c0 100644
--- a/api/api_grpc.pb.go
+++ b/api/api_grpc.pb.go
@@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.5.1
+// - protoc v5.29.3
+// source: api/api.proto
package api
@@ -11,12 +15,28 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ Dex_CreateClient_FullMethodName = "/api.Dex/CreateClient"
+ Dex_UpdateClient_FullMethodName = "/api.Dex/UpdateClient"
+ Dex_DeleteClient_FullMethodName = "/api.Dex/DeleteClient"
+ Dex_CreatePassword_FullMethodName = "/api.Dex/CreatePassword"
+ Dex_UpdatePassword_FullMethodName = "/api.Dex/UpdatePassword"
+ Dex_DeletePassword_FullMethodName = "/api.Dex/DeletePassword"
+ Dex_ListPasswords_FullMethodName = "/api.Dex/ListPasswords"
+ Dex_GetVersion_FullMethodName = "/api.Dex/GetVersion"
+ Dex_ListRefresh_FullMethodName = "/api.Dex/ListRefresh"
+ Dex_RevokeRefresh_FullMethodName = "/api.Dex/RevokeRefresh"
+ Dex_VerifyPassword_FullMethodName = "/api.Dex/VerifyPassword"
+)
// DexClient is the client API for Dex service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// Dex represents the dex gRPC service.
type DexClient interface {
// CreateClient creates a client.
CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error)
@@ -53,8 +73,9 @@ func NewDexClient(cc grpc.ClientConnInterface) DexClient {
}
func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/CreateClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_CreateClient_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -62,8 +83,9 @@ func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts
}
func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/UpdateClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_UpdateClient_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -71,8 +93,9 @@ func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts
}
func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/DeleteClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_DeleteClient_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -80,8 +103,9 @@ func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts
}
func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreatePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/CreatePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_CreatePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -89,8 +113,9 @@ func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, o
}
func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdatePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/UpdatePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_UpdatePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -98,8 +123,9 @@ func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, o
}
func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/DeletePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_DeletePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -107,8 +133,9 @@ func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, o
}
func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListPasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/ListPasswords", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_ListPasswords_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -116,8 +143,9 @@ func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts
}
func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VersionResp)
- err := c.cc.Invoke(ctx, "/api.Dex/GetVersion", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_GetVersion_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -125,8 +153,9 @@ func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc
}
func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRefreshResp)
- err := c.cc.Invoke(ctx, "/api.Dex/ListRefresh", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_ListRefresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -134,8 +163,9 @@ func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ..
}
func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opts ...grpc.CallOption) (*RevokeRefreshResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RevokeRefreshResp)
- err := c.cc.Invoke(ctx, "/api.Dex/RevokeRefresh", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_RevokeRefresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -143,8 +173,9 @@ func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opt
}
func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VerifyPasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/VerifyPassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_VerifyPassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -153,7 +184,9 @@ func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, o
// DexServer is the server API for Dex service.
// All implementations must embed UnimplementedDexServer
-// for forward compatibility
+// for forward compatibility.
+//
+// Dex represents the dex gRPC service.
type DexServer interface {
// CreateClient creates a client.
CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error)
@@ -182,9 +215,12 @@ type DexServer interface {
mustEmbedUnimplementedDexServer()
}
-// UnimplementedDexServer must be embedded to have forward compatible implementations.
-type UnimplementedDexServer struct {
-}
+// UnimplementedDexServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedDexServer struct{}
func (UnimplementedDexServer) CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented")
@@ -220,6 +256,7 @@ func (UnimplementedDexServer) VerifyPassword(context.Context, *VerifyPasswordReq
return nil, status.Errorf(codes.Unimplemented, "method VerifyPassword not implemented")
}
func (UnimplementedDexServer) mustEmbedUnimplementedDexServer() {}
+func (UnimplementedDexServer) testEmbeddedByValue() {}
// UnsafeDexServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DexServer will
@@ -229,6 +266,13 @@ type UnsafeDexServer interface {
}
func RegisterDexServer(s grpc.ServiceRegistrar, srv DexServer) {
+ // If the following call pancis, it indicates UnimplementedDexServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
s.RegisterService(&Dex_ServiceDesc, srv)
}
@@ -242,7 +286,7 @@ func _Dex_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/CreateClient",
+ FullMethod: Dex_CreateClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).CreateClient(ctx, req.(*CreateClientReq))
@@ -260,7 +304,7 @@ func _Dex_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/UpdateClient",
+ FullMethod: Dex_UpdateClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).UpdateClient(ctx, req.(*UpdateClientReq))
@@ -278,7 +322,7 @@ func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/DeleteClient",
+ FullMethod: Dex_DeleteClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).DeleteClient(ctx, req.(*DeleteClientReq))
@@ -296,7 +340,7 @@ func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/CreatePassword",
+ FullMethod: Dex_CreatePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).CreatePassword(ctx, req.(*CreatePasswordReq))
@@ -314,7 +358,7 @@ func _Dex_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/UpdatePassword",
+ FullMethod: Dex_UpdatePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).UpdatePassword(ctx, req.(*UpdatePasswordReq))
@@ -332,7 +376,7 @@ func _Dex_DeletePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/DeletePassword",
+ FullMethod: Dex_DeletePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).DeletePassword(ctx, req.(*DeletePasswordReq))
@@ -350,7 +394,7 @@ func _Dex_ListPasswords_Handler(srv interface{}, ctx context.Context, dec func(i
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/ListPasswords",
+ FullMethod: Dex_ListPasswords_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).ListPasswords(ctx, req.(*ListPasswordReq))
@@ -368,7 +412,7 @@ func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/GetVersion",
+ FullMethod: Dex_GetVersion_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).GetVersion(ctx, req.(*VersionReq))
@@ -386,7 +430,7 @@ func _Dex_ListRefresh_Handler(srv interface{}, ctx context.Context, dec func(int
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/ListRefresh",
+ FullMethod: Dex_ListRefresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).ListRefresh(ctx, req.(*ListRefreshReq))
@@ -404,7 +448,7 @@ func _Dex_RevokeRefresh_Handler(srv interface{}, ctx context.Context, dec func(i
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/RevokeRefresh",
+ FullMethod: Dex_RevokeRefresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).RevokeRefresh(ctx, req.(*RevokeRefreshReq))
@@ -422,7 +466,7 @@ func _Dex_VerifyPassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/VerifyPassword",
+ FullMethod: Dex_VerifyPassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).VerifyPassword(ctx, req.(*VerifyPasswordReq))
diff --git a/api/v2/api.pb.go b/api/v2/api.pb.go
index f49310f311..6e8a134e93 100644
--- a/api/v2/api.pb.go
+++ b/api/v2/api.pb.go
@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.26.0
-// protoc v3.15.6
+// protoc-gen-go v1.36.5
+// protoc v5.29.3
// source: api/v2/api.proto
package api
@@ -11,6 +11,7 @@ import (
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
+ unsafe "unsafe"
)
const (
@@ -22,26 +23,23 @@ const (
// Client represents an OAuth2 client.
type Client struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
+ RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
+ TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
+ Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"`
+ Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
+ LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- Secret string `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
- RedirectUris []string `protobuf:"bytes,3,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
- TrustedPeers []string `protobuf:"bytes,4,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
- Public bool `protobuf:"varint,5,opt,name=public,proto3" json:"public,omitempty"`
- Name string `protobuf:"bytes,6,opt,name=name,proto3" json:"name,omitempty"`
- LogoUrl string `protobuf:"bytes,7,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *Client) Reset() {
*x = Client{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[0]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[0]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *Client) String() string {
@@ -52,7 +50,7 @@ func (*Client) ProtoMessage() {}
func (x *Client) ProtoReflect() protoreflect.Message {
mi := &file_api_v2_api_proto_msgTypes[0]
- if protoimpl.UnsafeEnabled && x != nil {
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -116,22 +114,195 @@ func (x *Client) GetLogoUrl() string {
return ""
}
-// CreateClientReq is a request to make a client.
-type CreateClientReq struct {
- state protoimpl.MessageState
+// ClientInfo represents an OAuth2 client without sensitive information.
+type ClientInfo struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
+ TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
+ Public bool `protobuf:"varint,4,opt,name=public,proto3" json:"public,omitempty"`
+ Name string `protobuf:"bytes,5,opt,name=name,proto3" json:"name,omitempty"`
+ LogoUrl string `protobuf:"bytes,6,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ClientInfo) Reset() {
+ *x = ClientInfo{}
+ mi := &file_api_v2_api_proto_msgTypes[1]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ClientInfo) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ClientInfo) ProtoMessage() {}
+
+func (x *ClientInfo) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[1]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ClientInfo.ProtoReflect.Descriptor instead.
+func (*ClientInfo) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *ClientInfo) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *ClientInfo) GetRedirectUris() []string {
+ if x != nil {
+ return x.RedirectUris
+ }
+ return nil
+}
+
+func (x *ClientInfo) GetTrustedPeers() []string {
+ if x != nil {
+ return x.TrustedPeers
+ }
+ return nil
+}
+
+func (x *ClientInfo) GetPublic() bool {
+ if x != nil {
+ return x.Public
+ }
+ return false
+}
+
+func (x *ClientInfo) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *ClientInfo) GetLogoUrl() string {
+ if x != nil {
+ return x.LogoUrl
+ }
+ return ""
+}
+
+// GetClientReq is a request to retrieve client details.
+type GetClientReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // The ID of the client.
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
+}
+
+func (x *GetClientReq) Reset() {
+ *x = GetClientReq{}
+ mi := &file_api_v2_api_proto_msgTypes[2]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *GetClientReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GetClientReq) ProtoMessage() {}
+
+func (x *GetClientReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[2]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetClientReq.ProtoReflect.Descriptor instead.
+func (*GetClientReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *GetClientReq) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+// GetClientResp returns the client details.
+type GetClientResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *GetClientResp) Reset() {
+ *x = GetClientResp{}
+ mi := &file_api_v2_api_proto_msgTypes[3]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
- Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
+func (x *GetClientResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (x *CreateClientReq) Reset() {
- *x = CreateClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[1]
+func (*GetClientResp) ProtoMessage() {}
+
+func (x *GetClientResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[3]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use GetClientResp.ProtoReflect.Descriptor instead.
+func (*GetClientResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *GetClientResp) GetClient() *Client {
+ if x != nil {
+ return x.Client
}
+ return nil
+}
+
+// CreateClientReq is a request to make a client.
+type CreateClientReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Client *Client `protobuf:"bytes,1,opt,name=client,proto3" json:"client,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *CreateClientReq) Reset() {
+ *x = CreateClientReq{}
+ mi := &file_api_v2_api_proto_msgTypes[4]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreateClientReq) String() string {
@@ -141,8 +312,8 @@ func (x *CreateClientReq) String() string {
func (*CreateClientReq) ProtoMessage() {}
func (x *CreateClientReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[1]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[4]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -154,7 +325,7 @@ func (x *CreateClientReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateClientReq.ProtoReflect.Descriptor instead.
func (*CreateClientReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{1}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{4}
}
func (x *CreateClientReq) GetClient() *Client {
@@ -166,21 +337,18 @@ func (x *CreateClientReq) GetClient() *Client {
// CreateClientResp returns the response from creating a client.
type CreateClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
+ Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"`
unknownFields protoimpl.UnknownFields
-
- AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
- Client *Client `protobuf:"bytes,2,opt,name=client,proto3" json:"client,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreateClientResp) Reset() {
*x = CreateClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[2]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[5]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreateClientResp) String() string {
@@ -190,8 +358,8 @@ func (x *CreateClientResp) String() string {
func (*CreateClientResp) ProtoMessage() {}
func (x *CreateClientResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[2]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[5]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -203,7 +371,7 @@ func (x *CreateClientResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreateClientResp.ProtoReflect.Descriptor instead.
func (*CreateClientResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{2}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{5}
}
func (x *CreateClientResp) GetAlreadyExists() bool {
@@ -222,21 +390,18 @@ func (x *CreateClientResp) GetClient() *Client {
// DeleteClientReq is a request to delete a client.
type DeleteClientReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The ID of the client.
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *DeleteClientReq) Reset() {
*x = DeleteClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[3]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[6]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeleteClientReq) String() string {
@@ -246,8 +411,8 @@ func (x *DeleteClientReq) String() string {
func (*DeleteClientReq) ProtoMessage() {}
func (x *DeleteClientReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[3]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[6]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -259,7 +424,7 @@ func (x *DeleteClientReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteClientReq.ProtoReflect.Descriptor instead.
func (*DeleteClientReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{3}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{6}
}
func (x *DeleteClientReq) GetId() string {
@@ -271,20 +436,17 @@ func (x *DeleteClientReq) GetId() string {
// DeleteClientResp determines if the client is deleted successfully.
type DeleteClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeleteClientResp) Reset() {
*x = DeleteClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[4]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[7]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeleteClientResp) String() string {
@@ -294,8 +456,8 @@ func (x *DeleteClientResp) String() string {
func (*DeleteClientResp) ProtoMessage() {}
func (x *DeleteClientResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[4]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[7]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -307,7 +469,7 @@ func (x *DeleteClientResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeleteClientResp.ProtoReflect.Descriptor instead.
func (*DeleteClientResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{4}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{7}
}
func (x *DeleteClientResp) GetNotFound() bool {
@@ -319,24 +481,21 @@ func (x *DeleteClientResp) GetNotFound() bool {
// UpdateClientReq is a request to update an existing client.
type UpdateClientReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
+ TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
+ Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
+ LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- RedirectUris []string `protobuf:"bytes,2,rep,name=redirect_uris,json=redirectUris,proto3" json:"redirect_uris,omitempty"`
- TrustedPeers []string `protobuf:"bytes,3,rep,name=trusted_peers,json=trustedPeers,proto3" json:"trusted_peers,omitempty"`
- Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"`
- LogoUrl string `protobuf:"bytes,5,opt,name=logo_url,json=logoUrl,proto3" json:"logo_url,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdateClientReq) Reset() {
*x = UpdateClientReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[5]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[8]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdateClientReq) String() string {
@@ -346,8 +505,8 @@ func (x *UpdateClientReq) String() string {
func (*UpdateClientReq) ProtoMessage() {}
func (x *UpdateClientReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[5]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[8]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -359,7 +518,7 @@ func (x *UpdateClientReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateClientReq.ProtoReflect.Descriptor instead.
func (*UpdateClientReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{5}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{8}
}
func (x *UpdateClientReq) GetId() string {
@@ -399,20 +558,17 @@ func (x *UpdateClientReq) GetLogoUrl() string {
// UpdateClientResp returns the response from updating a client.
type UpdateClientResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdateClientResp) Reset() {
*x = UpdateClientResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[6]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[9]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdateClientResp) String() string {
@@ -422,8 +578,8 @@ func (x *UpdateClientResp) String() string {
func (*UpdateClientResp) ProtoMessage() {}
func (x *UpdateClientResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[6]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[9]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -435,7 +591,7 @@ func (x *UpdateClientResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdateClientResp.ProtoReflect.Descriptor instead.
func (*UpdateClientResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{6}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{9}
}
func (x *UpdateClientResp) GetNotFound() bool {
@@ -445,37 +601,29 @@ func (x *UpdateClientResp) GetNotFound() bool {
return false
}
-// Password is an email for password mapping managed by the storage.
-type Password struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+// ListClientReq is a request to enumerate clients.
+type ListClientReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
- // Currently we do not accept plain text passwords. Could be an option in the future.
- Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
- Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
- UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ sizeCache protoimpl.SizeCache
}
-func (x *Password) Reset() {
- *x = Password{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[7]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+func (x *ListClientReq) Reset() {
+ *x = ListClientReq{}
+ mi := &file_api_v2_api_proto_msgTypes[10]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (x *Password) String() string {
+func (x *ListClientReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*Password) ProtoMessage() {}
+func (*ListClientReq) ProtoMessage() {}
-func (x *Password) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[7]
- if protoimpl.UnsafeEnabled && x != nil {
+func (x *ListClientReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[10]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -485,55 +633,139 @@ func (x *Password) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use Password.ProtoReflect.Descriptor instead.
-func (*Password) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{7}
+// Deprecated: Use ListClientReq.ProtoReflect.Descriptor instead.
+func (*ListClientReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{10}
}
-func (x *Password) GetEmail() string {
- if x != nil {
- return x.Email
- }
- return ""
+// ListClientResp returns a list of clients.
+type ListClientResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Clients []*ClientInfo `protobuf:"bytes,1,rep,name=clients,proto3" json:"clients,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (x *Password) GetHash() []byte {
- if x != nil {
- return x.Hash
- }
- return nil
+func (x *ListClientResp) Reset() {
+ *x = ListClientResp{}
+ mi := &file_api_v2_api_proto_msgTypes[11]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (x *Password) GetUsername() string {
- if x != nil {
- return x.Username
- }
- return ""
+func (x *ListClientResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
}
-func (x *Password) GetUserId() string {
+func (*ListClientResp) ProtoMessage() {}
+
+func (x *ListClientResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[11]
if x != nil {
- return x.UserId
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
}
- return ""
+ return mi.MessageOf(x)
}
-// CreatePasswordReq is a request to make a password.
-type CreatePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
- Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+// Deprecated: Use ListClientResp.ProtoReflect.Descriptor instead.
+func (*ListClientResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{11}
}
-func (x *CreatePasswordReq) Reset() {
- *x = CreatePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[8]
+func (x *ListClientResp) GetClients() []*ClientInfo {
+ if x != nil {
+ return x.Clients
+ }
+ return nil
+}
+
+// Password is an email for password mapping managed by the storage.
+type Password struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ // Currently we do not accept plain text passwords. Could be an option in the future.
+ Hash []byte `protobuf:"bytes,2,opt,name=hash,proto3" json:"hash,omitempty"`
+ Username string `protobuf:"bytes,3,opt,name=username,proto3" json:"username,omitempty"`
+ UserId string `protobuf:"bytes,4,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *Password) Reset() {
+ *x = Password{}
+ mi := &file_api_v2_api_proto_msgTypes[12]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Password) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Password) ProtoMessage() {}
+
+func (x *Password) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[12]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Password.ProtoReflect.Descriptor instead.
+func (*Password) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *Password) GetEmail() string {
+ if x != nil {
+ return x.Email
+ }
+ return ""
+}
+
+func (x *Password) GetHash() []byte {
+ if x != nil {
+ return x.Hash
}
+ return nil
+}
+
+func (x *Password) GetUsername() string {
+ if x != nil {
+ return x.Username
+ }
+ return ""
+}
+
+func (x *Password) GetUserId() string {
+ if x != nil {
+ return x.UserId
+ }
+ return ""
+}
+
+// CreatePasswordReq is a request to make a password.
+type CreatePasswordReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Password *Password `protobuf:"bytes,1,opt,name=password,proto3" json:"password,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *CreatePasswordReq) Reset() {
+ *x = CreatePasswordReq{}
+ mi := &file_api_v2_api_proto_msgTypes[13]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreatePasswordReq) String() string {
@@ -543,8 +775,8 @@ func (x *CreatePasswordReq) String() string {
func (*CreatePasswordReq) ProtoMessage() {}
func (x *CreatePasswordReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[8]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[13]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -556,7 +788,7 @@ func (x *CreatePasswordReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreatePasswordReq.ProtoReflect.Descriptor instead.
func (*CreatePasswordReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{8}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{13}
}
func (x *CreatePasswordReq) GetPassword() *Password {
@@ -568,20 +800,17 @@ func (x *CreatePasswordReq) GetPassword() *Password {
// CreatePasswordResp returns the response from creating a password.
type CreatePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
unknownFields protoimpl.UnknownFields
-
- AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *CreatePasswordResp) Reset() {
*x = CreatePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[9]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[14]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *CreatePasswordResp) String() string {
@@ -591,8 +820,8 @@ func (x *CreatePasswordResp) String() string {
func (*CreatePasswordResp) ProtoMessage() {}
func (x *CreatePasswordResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[9]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[14]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -604,7 +833,7 @@ func (x *CreatePasswordResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use CreatePasswordResp.ProtoReflect.Descriptor instead.
func (*CreatePasswordResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{9}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{14}
}
func (x *CreatePasswordResp) GetAlreadyExists() bool {
@@ -616,23 +845,20 @@ func (x *CreatePasswordResp) GetAlreadyExists() bool {
// UpdatePasswordReq is a request to modify an existing password.
type UpdatePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The email used to lookup the password. This field cannot be modified
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
- NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"`
- NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ NewHash []byte `protobuf:"bytes,2,opt,name=new_hash,json=newHash,proto3" json:"new_hash,omitempty"`
+ NewUsername string `protobuf:"bytes,3,opt,name=new_username,json=newUsername,proto3" json:"new_username,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *UpdatePasswordReq) Reset() {
*x = UpdatePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[10]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[15]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdatePasswordReq) String() string {
@@ -642,8 +868,8 @@ func (x *UpdatePasswordReq) String() string {
func (*UpdatePasswordReq) ProtoMessage() {}
func (x *UpdatePasswordReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[10]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[15]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -655,7 +881,7 @@ func (x *UpdatePasswordReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdatePasswordReq.ProtoReflect.Descriptor instead.
func (*UpdatePasswordReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{10}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{15}
}
func (x *UpdatePasswordReq) GetEmail() string {
@@ -681,20 +907,17 @@ func (x *UpdatePasswordReq) GetNewUsername() string {
// UpdatePasswordResp returns the response from modifying an existing password.
type UpdatePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *UpdatePasswordResp) Reset() {
*x = UpdatePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[11]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[16]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *UpdatePasswordResp) String() string {
@@ -704,8 +927,8 @@ func (x *UpdatePasswordResp) String() string {
func (*UpdatePasswordResp) ProtoMessage() {}
func (x *UpdatePasswordResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[11]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[16]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -717,7 +940,7 @@ func (x *UpdatePasswordResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use UpdatePasswordResp.ProtoReflect.Descriptor instead.
func (*UpdatePasswordResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{11}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{16}
}
func (x *UpdatePasswordResp) GetNotFound() bool {
@@ -729,20 +952,17 @@ func (x *UpdatePasswordResp) GetNotFound() bool {
// DeletePasswordReq is a request to delete a password.
type DeletePasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeletePasswordReq) Reset() {
*x = DeletePasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[12]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[17]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeletePasswordReq) String() string {
@@ -752,8 +972,8 @@ func (x *DeletePasswordReq) String() string {
func (*DeletePasswordReq) ProtoMessage() {}
func (x *DeletePasswordReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[12]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[17]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -765,7 +985,7 @@ func (x *DeletePasswordReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeletePasswordReq.ProtoReflect.Descriptor instead.
func (*DeletePasswordReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{12}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{17}
}
func (x *DeletePasswordReq) GetEmail() string {
@@ -777,20 +997,17 @@ func (x *DeletePasswordReq) GetEmail() string {
// DeletePasswordResp returns the response from deleting a password.
type DeletePasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *DeletePasswordResp) Reset() {
*x = DeletePasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[13]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[18]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *DeletePasswordResp) String() string {
@@ -800,8 +1017,8 @@ func (x *DeletePasswordResp) String() string {
func (*DeletePasswordResp) ProtoMessage() {}
func (x *DeletePasswordResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[13]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[18]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -813,7 +1030,7 @@ func (x *DeletePasswordResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use DeletePasswordResp.ProtoReflect.Descriptor instead.
func (*DeletePasswordResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{13}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{18}
}
func (x *DeletePasswordResp) GetNotFound() bool {
@@ -825,29 +1042,473 @@ func (x *DeletePasswordResp) GetNotFound() bool {
// ListPasswordReq is a request to enumerate passwords.
type ListPasswordReq struct {
- state protoimpl.MessageState
+ state protoimpl.MessageState `protogen:"open.v1"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ListPasswordReq) Reset() {
+ *x = ListPasswordReq{}
+ mi := &file_api_v2_api_proto_msgTypes[19]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ListPasswordReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListPasswordReq) ProtoMessage() {}
+
+func (x *ListPasswordReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[19]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListPasswordReq.ProtoReflect.Descriptor instead.
+func (*ListPasswordReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{19}
+}
+
+// ListPasswordResp returns a list of passwords.
+type ListPasswordResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *ListPasswordResp) Reset() {
+ *x = ListPasswordResp{}
+ mi := &file_api_v2_api_proto_msgTypes[20]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *ListPasswordResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ListPasswordResp) ProtoMessage() {}
+
+func (x *ListPasswordResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[20]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use ListPasswordResp.ProtoReflect.Descriptor instead.
+func (*ListPasswordResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{20}
+}
+
+func (x *ListPasswordResp) GetPasswords() []*Password {
+ if x != nil {
+ return x.Passwords
+ }
+ return nil
+}
+
+// Connector is a strategy used by Dex for authenticating a user against another identity provider
+type Connector struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
+ Name string `protobuf:"bytes,3,opt,name=name,proto3" json:"name,omitempty"`
+ Config []byte `protobuf:"bytes,4,opt,name=config,proto3" json:"config,omitempty"`
+ unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
+}
+
+func (x *Connector) Reset() {
+ *x = Connector{}
+ mi := &file_api_v2_api_proto_msgTypes[21]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *Connector) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Connector) ProtoMessage() {}
+
+func (x *Connector) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[21]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use Connector.ProtoReflect.Descriptor instead.
+func (*Connector) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{21}
+}
+
+func (x *Connector) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *Connector) GetType() string {
+ if x != nil {
+ return x.Type
+ }
+ return ""
+}
+
+func (x *Connector) GetName() string {
+ if x != nil {
+ return x.Name
+ }
+ return ""
+}
+
+func (x *Connector) GetConfig() []byte {
+ if x != nil {
+ return x.Config
+ }
+ return nil
+}
+
+// CreateConnectorReq is a request to make a connector.
+type CreateConnectorReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Connector *Connector `protobuf:"bytes,1,opt,name=connector,proto3" json:"connector,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *CreateConnectorReq) Reset() {
+ *x = CreateConnectorReq{}
+ mi := &file_api_v2_api_proto_msgTypes[22]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *CreateConnectorReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateConnectorReq) ProtoMessage() {}
+
+func (x *CreateConnectorReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[22]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CreateConnectorReq.ProtoReflect.Descriptor instead.
+func (*CreateConnectorReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{22}
+}
+
+func (x *CreateConnectorReq) GetConnector() *Connector {
+ if x != nil {
+ return x.Connector
+ }
+ return nil
+}
+
+// CreateConnectorResp returns the response from creating a connector.
+type CreateConnectorResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ AlreadyExists bool `protobuf:"varint,1,opt,name=already_exists,json=alreadyExists,proto3" json:"already_exists,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *CreateConnectorResp) Reset() {
+ *x = CreateConnectorResp{}
+ mi := &file_api_v2_api_proto_msgTypes[23]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *CreateConnectorResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CreateConnectorResp) ProtoMessage() {}
+
+func (x *CreateConnectorResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[23]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use CreateConnectorResp.ProtoReflect.Descriptor instead.
+func (*CreateConnectorResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{23}
+}
+
+func (x *CreateConnectorResp) GetAlreadyExists() bool {
+ if x != nil {
+ return x.AlreadyExists
+ }
+ return false
+}
+
+// UpdateConnectorReq is a request to modify an existing connector.
+type UpdateConnectorReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ // The id used to lookup the connector. This field cannot be modified
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ NewType string `protobuf:"bytes,2,opt,name=new_type,json=newType,proto3" json:"new_type,omitempty"`
+ NewName string `protobuf:"bytes,3,opt,name=new_name,json=newName,proto3" json:"new_name,omitempty"`
+ NewConfig []byte `protobuf:"bytes,4,opt,name=new_config,json=newConfig,proto3" json:"new_config,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *UpdateConnectorReq) Reset() {
+ *x = UpdateConnectorReq{}
+ mi := &file_api_v2_api_proto_msgTypes[24]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *UpdateConnectorReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateConnectorReq) ProtoMessage() {}
+
+func (x *UpdateConnectorReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[24]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UpdateConnectorReq.ProtoReflect.Descriptor instead.
+func (*UpdateConnectorReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{24}
+}
+
+func (x *UpdateConnectorReq) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+func (x *UpdateConnectorReq) GetNewType() string {
+ if x != nil {
+ return x.NewType
+ }
+ return ""
+}
+
+func (x *UpdateConnectorReq) GetNewName() string {
+ if x != nil {
+ return x.NewName
+ }
+ return ""
+}
+
+func (x *UpdateConnectorReq) GetNewConfig() []byte {
+ if x != nil {
+ return x.NewConfig
+ }
+ return nil
+}
+
+// UpdateConnectorResp returns the response from modifying an existing connector.
+type UpdateConnectorResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *UpdateConnectorResp) Reset() {
+ *x = UpdateConnectorResp{}
+ mi := &file_api_v2_api_proto_msgTypes[25]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *UpdateConnectorResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UpdateConnectorResp) ProtoMessage() {}
+
+func (x *UpdateConnectorResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[25]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use UpdateConnectorResp.ProtoReflect.Descriptor instead.
+func (*UpdateConnectorResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{25}
+}
+
+func (x *UpdateConnectorResp) GetNotFound() bool {
+ if x != nil {
+ return x.NotFound
+ }
+ return false
+}
+
+// DeleteConnectorReq is a request to delete a connector.
+type DeleteConnectorReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *DeleteConnectorReq) Reset() {
+ *x = DeleteConnectorReq{}
+ mi := &file_api_v2_api_proto_msgTypes[26]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *DeleteConnectorReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConnectorReq) ProtoMessage() {}
+
+func (x *DeleteConnectorReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[26]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConnectorReq.ProtoReflect.Descriptor instead.
+func (*DeleteConnectorReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{26}
+}
+
+func (x *DeleteConnectorReq) GetId() string {
+ if x != nil {
+ return x.Id
+ }
+ return ""
+}
+
+// DeleteConnectorResp returns the response from deleting a connector.
+type DeleteConnectorResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *DeleteConnectorResp) Reset() {
+ *x = DeleteConnectorResp{}
+ mi := &file_api_v2_api_proto_msgTypes[27]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *DeleteConnectorResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeleteConnectorResp) ProtoMessage() {}
+
+func (x *DeleteConnectorResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[27]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeleteConnectorResp.ProtoReflect.Descriptor instead.
+func (*DeleteConnectorResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{27}
+}
+
+func (x *DeleteConnectorResp) GetNotFound() bool {
+ if x != nil {
+ return x.NotFound
+ }
+ return false
+}
+
+// ListConnectorReq is a request to enumerate connectors.
+type ListConnectorReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
-func (x *ListPasswordReq) Reset() {
- *x = ListPasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[14]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+func (x *ListConnectorReq) Reset() {
+ *x = ListConnectorReq{}
+ mi := &file_api_v2_api_proto_msgTypes[28]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (x *ListPasswordReq) String() string {
+func (x *ListConnectorReq) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*ListPasswordReq) ProtoMessage() {}
+func (*ListConnectorReq) ProtoMessage() {}
-func (x *ListPasswordReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[14]
- if protoimpl.UnsafeEnabled && x != nil {
+func (x *ListConnectorReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[28]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -857,38 +1518,35 @@ func (x *ListPasswordReq) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use ListPasswordReq.ProtoReflect.Descriptor instead.
-func (*ListPasswordReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{14}
+// Deprecated: Use ListConnectorReq.ProtoReflect.Descriptor instead.
+func (*ListConnectorReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{28}
}
-// ListPasswordResp returns a list of passwords.
-type ListPasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+// ListConnectorResp returns a list of connectors.
+type ListConnectorResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Connectors []*Connector `protobuf:"bytes,1,rep,name=connectors,proto3" json:"connectors,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Passwords []*Password `protobuf:"bytes,1,rep,name=passwords,proto3" json:"passwords,omitempty"`
+ sizeCache protoimpl.SizeCache
}
-func (x *ListPasswordResp) Reset() {
- *x = ListPasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[15]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+func (x *ListConnectorResp) Reset() {
+ *x = ListConnectorResp{}
+ mi := &file_api_v2_api_proto_msgTypes[29]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
-func (x *ListPasswordResp) String() string {
+func (x *ListConnectorResp) String() string {
return protoimpl.X.MessageStringOf(x)
}
-func (*ListPasswordResp) ProtoMessage() {}
+func (*ListConnectorResp) ProtoMessage() {}
-func (x *ListPasswordResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[15]
- if protoimpl.UnsafeEnabled && x != nil {
+func (x *ListConnectorResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[29]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -898,32 +1556,30 @@ func (x *ListPasswordResp) ProtoReflect() protoreflect.Message {
return mi.MessageOf(x)
}
-// Deprecated: Use ListPasswordResp.ProtoReflect.Descriptor instead.
-func (*ListPasswordResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{15}
+// Deprecated: Use ListConnectorResp.ProtoReflect.Descriptor instead.
+func (*ListConnectorResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{29}
}
-func (x *ListPasswordResp) GetPasswords() []*Password {
+func (x *ListConnectorResp) GetConnectors() []*Connector {
if x != nil {
- return x.Passwords
+ return x.Connectors
}
return nil
}
// VersionReq is a request to fetch version info.
type VersionReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *VersionReq) Reset() {
*x = VersionReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[16]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[30]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VersionReq) String() string {
@@ -933,8 +1589,8 @@ func (x *VersionReq) String() string {
func (*VersionReq) ProtoMessage() {}
func (x *VersionReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[16]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[30]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -946,29 +1602,26 @@ func (x *VersionReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use VersionReq.ProtoReflect.Descriptor instead.
func (*VersionReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{16}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{30}
}
// VersionResp holds the version info of components.
type VersionResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// Semantic version of the server.
Server string `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"`
- // Numeric version of the API. It increases everytime a new call is added to the API.
+ // Numeric version of the API. It increases every time a new call is added to the API.
// Clients should use this info to determine if the server supports specific features.
- Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"`
+ Api int32 `protobuf:"varint,2,opt,name=api,proto3" json:"api,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *VersionResp) Reset() {
*x = VersionResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[17]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[31]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VersionResp) String() string {
@@ -978,8 +1631,8 @@ func (x *VersionResp) String() string {
func (*VersionResp) ProtoMessage() {}
func (x *VersionResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[17]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[31]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -991,7 +1644,7 @@ func (x *VersionResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use VersionResp.ProtoReflect.Descriptor instead.
func (*VersionResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{17}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{31}
}
func (x *VersionResp) GetServer() string {
@@ -1008,26 +1661,217 @@ func (x *VersionResp) GetApi() int32 {
return 0
}
-// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage.
-type RefreshTokenRef struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+// DiscoveryReq is a request to fetch discover information.
+type DiscoveryReq struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *DiscoveryReq) Reset() {
+ *x = DiscoveryReq{}
+ mi := &file_api_v2_api_proto_msgTypes[32]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *DiscoveryReq) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DiscoveryReq) ProtoMessage() {}
+
+func (x *DiscoveryReq) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[32]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DiscoveryReq.ProtoReflect.Descriptor instead.
+func (*DiscoveryReq) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{32}
+}
+
+// DiscoverResp holds the version oidc disovery info.
+type DiscoveryResp struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"`
+ AuthorizationEndpoint string `protobuf:"bytes,2,opt,name=authorization_endpoint,json=authorizationEndpoint,proto3" json:"authorization_endpoint,omitempty"`
+ TokenEndpoint string `protobuf:"bytes,3,opt,name=token_endpoint,json=tokenEndpoint,proto3" json:"token_endpoint,omitempty"`
+ JwksUri string `protobuf:"bytes,4,opt,name=jwks_uri,json=jwksUri,proto3" json:"jwks_uri,omitempty"`
+ UserinfoEndpoint string `protobuf:"bytes,5,opt,name=userinfo_endpoint,json=userinfoEndpoint,proto3" json:"userinfo_endpoint,omitempty"`
+ DeviceAuthorizationEndpoint string `protobuf:"bytes,6,opt,name=device_authorization_endpoint,json=deviceAuthorizationEndpoint,proto3" json:"device_authorization_endpoint,omitempty"`
+ IntrospectionEndpoint string `protobuf:"bytes,7,opt,name=introspection_endpoint,json=introspectionEndpoint,proto3" json:"introspection_endpoint,omitempty"`
+ GrantTypesSupported []string `protobuf:"bytes,8,rep,name=grant_types_supported,json=grantTypesSupported,proto3" json:"grant_types_supported,omitempty"`
+ ResponseTypesSupported []string `protobuf:"bytes,9,rep,name=response_types_supported,json=responseTypesSupported,proto3" json:"response_types_supported,omitempty"`
+ SubjectTypesSupported []string `protobuf:"bytes,10,rep,name=subject_types_supported,json=subjectTypesSupported,proto3" json:"subject_types_supported,omitempty"`
+ IdTokenSigningAlgValuesSupported []string `protobuf:"bytes,11,rep,name=id_token_signing_alg_values_supported,json=idTokenSigningAlgValuesSupported,proto3" json:"id_token_signing_alg_values_supported,omitempty"`
+ CodeChallengeMethodsSupported []string `protobuf:"bytes,12,rep,name=code_challenge_methods_supported,json=codeChallengeMethodsSupported,proto3" json:"code_challenge_methods_supported,omitempty"`
+ ScopesSupported []string `protobuf:"bytes,13,rep,name=scopes_supported,json=scopesSupported,proto3" json:"scopes_supported,omitempty"`
+ TokenEndpointAuthMethodsSupported []string `protobuf:"bytes,14,rep,name=token_endpoint_auth_methods_supported,json=tokenEndpointAuthMethodsSupported,proto3" json:"token_endpoint_auth_methods_supported,omitempty"`
+ ClaimsSupported []string `protobuf:"bytes,15,rep,name=claims_supported,json=claimsSupported,proto3" json:"claims_supported,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
+}
+
+func (x *DiscoveryResp) Reset() {
+ *x = DiscoveryResp{}
+ mi := &file_api_v2_api_proto_msgTypes[33]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
+}
+
+func (x *DiscoveryResp) String() string {
+ return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DiscoveryResp) ProtoMessage() {}
+
+func (x *DiscoveryResp) ProtoReflect() protoreflect.Message {
+ mi := &file_api_v2_api_proto_msgTypes[33]
+ if x != nil {
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ if ms.LoadMessageInfo() == nil {
+ ms.StoreMessageInfo(mi)
+ }
+ return ms
+ }
+ return mi.MessageOf(x)
+}
+
+// Deprecated: Use DiscoveryResp.ProtoReflect.Descriptor instead.
+func (*DiscoveryResp) Descriptor() ([]byte, []int) {
+ return file_api_v2_api_proto_rawDescGZIP(), []int{33}
+}
+
+func (x *DiscoveryResp) GetIssuer() string {
+ if x != nil {
+ return x.Issuer
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetAuthorizationEndpoint() string {
+ if x != nil {
+ return x.AuthorizationEndpoint
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetTokenEndpoint() string {
+ if x != nil {
+ return x.TokenEndpoint
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetJwksUri() string {
+ if x != nil {
+ return x.JwksUri
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetUserinfoEndpoint() string {
+ if x != nil {
+ return x.UserinfoEndpoint
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetDeviceAuthorizationEndpoint() string {
+ if x != nil {
+ return x.DeviceAuthorizationEndpoint
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetIntrospectionEndpoint() string {
+ if x != nil {
+ return x.IntrospectionEndpoint
+ }
+ return ""
+}
+
+func (x *DiscoveryResp) GetGrantTypesSupported() []string {
+ if x != nil {
+ return x.GrantTypesSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetResponseTypesSupported() []string {
+ if x != nil {
+ return x.ResponseTypesSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetSubjectTypesSupported() []string {
+ if x != nil {
+ return x.SubjectTypesSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetIdTokenSigningAlgValuesSupported() []string {
+ if x != nil {
+ return x.IdTokenSigningAlgValuesSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetCodeChallengeMethodsSupported() []string {
+ if x != nil {
+ return x.CodeChallengeMethodsSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetScopesSupported() []string {
+ if x != nil {
+ return x.ScopesSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetTokenEndpointAuthMethodsSupported() []string {
+ if x != nil {
+ return x.TokenEndpointAuthMethodsSupported
+ }
+ return nil
+}
+
+func (x *DiscoveryResp) GetClaimsSupported() []string {
+ if x != nil {
+ return x.ClaimsSupported
+ }
+ return nil
+}
+// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage.
+type RefreshTokenRef struct {
+ state protoimpl.MessageState `protogen:"open.v1"`
// ID of the refresh token.
- Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
- ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
- CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
- LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"`
+ Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
+ ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ CreatedAt int64 `protobuf:"varint,5,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
+ LastUsed int64 `protobuf:"varint,6,opt,name=last_used,json=lastUsed,proto3" json:"last_used,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenRef) Reset() {
*x = RefreshTokenRef{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[18]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[34]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RefreshTokenRef) String() string {
@@ -1037,8 +1881,8 @@ func (x *RefreshTokenRef) String() string {
func (*RefreshTokenRef) ProtoMessage() {}
func (x *RefreshTokenRef) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[18]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[34]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1050,7 +1894,7 @@ func (x *RefreshTokenRef) ProtoReflect() protoreflect.Message {
// Deprecated: Use RefreshTokenRef.ProtoReflect.Descriptor instead.
func (*RefreshTokenRef) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{18}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{34}
}
func (x *RefreshTokenRef) GetId() string {
@@ -1083,21 +1927,18 @@ func (x *RefreshTokenRef) GetLastUsed() int64 {
// ListRefreshReq is a request to enumerate the refresh tokens of a user.
type ListRefreshReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The "sub" claim returned in the ID Token.
- UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *ListRefreshReq) Reset() {
*x = ListRefreshReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[19]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[35]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListRefreshReq) String() string {
@@ -1107,8 +1948,8 @@ func (x *ListRefreshReq) String() string {
func (*ListRefreshReq) ProtoMessage() {}
func (x *ListRefreshReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[19]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[35]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1120,7 +1961,7 @@ func (x *ListRefreshReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListRefreshReq.ProtoReflect.Descriptor instead.
func (*ListRefreshReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{19}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{35}
}
func (x *ListRefreshReq) GetUserId() string {
@@ -1132,20 +1973,17 @@ func (x *ListRefreshReq) GetUserId() string {
// ListRefreshResp returns a list of refresh tokens for a user.
type ListRefreshResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
unknownFields protoimpl.UnknownFields
-
- RefreshTokens []*RefreshTokenRef `protobuf:"bytes,1,rep,name=refresh_tokens,json=refreshTokens,proto3" json:"refresh_tokens,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *ListRefreshResp) Reset() {
*x = ListRefreshResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[20]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[36]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *ListRefreshResp) String() string {
@@ -1155,8 +1993,8 @@ func (x *ListRefreshResp) String() string {
func (*ListRefreshResp) ProtoMessage() {}
func (x *ListRefreshResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[20]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[36]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1168,7 +2006,7 @@ func (x *ListRefreshResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use ListRefreshResp.ProtoReflect.Descriptor instead.
func (*ListRefreshResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{20}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{36}
}
func (x *ListRefreshResp) GetRefreshTokens() []*RefreshTokenRef {
@@ -1180,22 +2018,19 @@ func (x *ListRefreshResp) GetRefreshTokens() []*RefreshTokenRef {
// RevokeRefreshReq is a request to revoke the refresh token of the user-client pair.
type RevokeRefreshReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// The "sub" claim returned in the ID Token.
- UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
- ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
+ ClientId string `protobuf:"bytes,2,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RevokeRefreshReq) Reset() {
*x = RevokeRefreshReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[21]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[37]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RevokeRefreshReq) String() string {
@@ -1205,8 +2040,8 @@ func (x *RevokeRefreshReq) String() string {
func (*RevokeRefreshReq) ProtoMessage() {}
func (x *RevokeRefreshReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[21]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[37]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1218,7 +2053,7 @@ func (x *RevokeRefreshReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use RevokeRefreshReq.ProtoReflect.Descriptor instead.
func (*RevokeRefreshReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{21}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{37}
}
func (x *RevokeRefreshReq) GetUserId() string {
@@ -1237,21 +2072,18 @@ func (x *RevokeRefreshReq) GetClientId() string {
// RevokeRefreshResp determines if the refresh token is revoked successfully.
type RevokeRefreshResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
- unknownFields protoimpl.UnknownFields
-
+ state protoimpl.MessageState `protogen:"open.v1"`
// Set to true is refresh token was not found and token could not be revoked.
- NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ NotFound bool `protobuf:"varint,1,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ unknownFields protoimpl.UnknownFields
+ sizeCache protoimpl.SizeCache
}
func (x *RevokeRefreshResp) Reset() {
*x = RevokeRefreshResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[22]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[38]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *RevokeRefreshResp) String() string {
@@ -1261,8 +2093,8 @@ func (x *RevokeRefreshResp) String() string {
func (*RevokeRefreshResp) ProtoMessage() {}
func (x *RevokeRefreshResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[22]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[38]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1274,7 +2106,7 @@ func (x *RevokeRefreshResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use RevokeRefreshResp.ProtoReflect.Descriptor instead.
func (*RevokeRefreshResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{22}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{38}
}
func (x *RevokeRefreshResp) GetNotFound() bool {
@@ -1285,21 +2117,18 @@ func (x *RevokeRefreshResp) GetNotFound() bool {
}
type VerifyPasswordReq struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
+ Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
- Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordReq) Reset() {
*x = VerifyPasswordReq{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[23]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[39]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordReq) String() string {
@@ -1309,8 +2138,8 @@ func (x *VerifyPasswordReq) String() string {
func (*VerifyPasswordReq) ProtoMessage() {}
func (x *VerifyPasswordReq) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[23]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[39]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1322,7 +2151,7 @@ func (x *VerifyPasswordReq) ProtoReflect() protoreflect.Message {
// Deprecated: Use VerifyPasswordReq.ProtoReflect.Descriptor instead.
func (*VerifyPasswordReq) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{23}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{39}
}
func (x *VerifyPasswordReq) GetEmail() string {
@@ -1340,21 +2169,18 @@ func (x *VerifyPasswordReq) GetPassword() string {
}
type VerifyPasswordResp struct {
- state protoimpl.MessageState
- sizeCache protoimpl.SizeCache
+ state protoimpl.MessageState `protogen:"open.v1"`
+ Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"`
+ NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
unknownFields protoimpl.UnknownFields
-
- Verified bool `protobuf:"varint,1,opt,name=verified,proto3" json:"verified,omitempty"`
- NotFound bool `protobuf:"varint,2,opt,name=not_found,json=notFound,proto3" json:"not_found,omitempty"`
+ sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordResp) Reset() {
*x = VerifyPasswordResp{}
- if protoimpl.UnsafeEnabled {
- mi := &file_api_v2_api_proto_msgTypes[24]
- ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
- ms.StoreMessageInfo(mi)
- }
+ mi := &file_api_v2_api_proto_msgTypes[40]
+ ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+ ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordResp) String() string {
@@ -1364,8 +2190,8 @@ func (x *VerifyPasswordResp) String() string {
func (*VerifyPasswordResp) ProtoMessage() {}
func (x *VerifyPasswordResp) ProtoReflect() protoreflect.Message {
- mi := &file_api_v2_api_proto_msgTypes[24]
- if protoimpl.UnsafeEnabled && x != nil {
+ mi := &file_api_v2_api_proto_msgTypes[40]
+ if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
@@ -1377,7 +2203,7 @@ func (x *VerifyPasswordResp) ProtoReflect() protoreflect.Message {
// Deprecated: Use VerifyPasswordResp.ProtoReflect.Descriptor instead.
func (*VerifyPasswordResp) Descriptor() ([]byte, []int) {
- return file_api_v2_api_proto_rawDescGZIP(), []int{24}
+ return file_api_v2_api_proto_rawDescGZIP(), []int{40}
}
func (x *VerifyPasswordResp) GetVerified() bool {
@@ -1396,7 +2222,7 @@ func (x *VerifyPasswordResp) GetNotFound() bool {
var File_api_v2_api_proto protoreflect.FileDescriptor
-var file_api_v2_api_proto_rawDesc = []byte{
+var file_api_v2_api_proto_rawDesc = string([]byte{
0x0a, 0x10, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x61, 0x70, 0x69, 0x2e, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x12, 0x03, 0x61, 0x70, 0x69, 0x22, 0xc1, 0x01, 0x0a, 0x06, 0x43, 0x6c, 0x69, 0x65,
0x6e, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02,
@@ -1410,231 +2236,403 @@ var file_api_v2_api_proto_rawDesc = []byte{
0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x07, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x22, 0x36, 0x0a, 0x0f, 0x43,
- 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x23,
- 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
- 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x22, 0x5e, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x72, 0x65, 0x61,
+ 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x22, 0xad, 0x01, 0x0a, 0x0a,
+ 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65,
+ 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
+ 0x09, 0x52, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x73, 0x12,
+ 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73,
+ 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50,
+ 0x65, 0x65, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x12, 0x12, 0x0a, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65,
+ 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x06, 0x20, 0x01,
+ 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x22, 0x1e, 0x0a, 0x0c, 0x47,
+ 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69,
+ 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x34, 0x0a, 0x0d, 0x47,
+ 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x23, 0x0a, 0x06,
+ 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61,
+ 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x22, 0x36, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x52, 0x65, 0x71, 0x12, 0x23, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x5e, 0x0a, 0x10, 0x43, 0x72, 0x65,
+ 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x25, 0x0a,
+ 0x0e, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78,
+ 0x69, 0x73, 0x74, 0x73, 0x12, 0x23, 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02,
+ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0f, 0x44, 0x65, 0x6c,
+ 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02,
+ 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2f, 0x0a, 0x10,
+ 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
+ 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x9a, 0x01,
+ 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65,
+ 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69,
+ 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72,
+ 0x69, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65,
+ 0x63, 0x74, 0x55, 0x72, 0x69, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65,
+ 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74,
+ 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+ 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+ 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67, 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67, 0x6f, 0x55, 0x72, 0x6c, 0x22, 0x2f, 0x0a, 0x10, 0x55, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b,
+ 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x0f, 0x0a, 0x0d, 0x4c,
+ 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x22, 0x3b, 0x0a, 0x0e,
+ 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x29,
+ 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+ 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x6e, 0x66, 0x6f,
+ 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x22, 0x69, 0x0a, 0x08, 0x50, 0x61, 0x73,
+ 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01,
+ 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68,
+ 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12,
+ 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75,
+ 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73,
+ 0x65, 0x72, 0x49, 0x64, 0x22, 0x3e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61,
+ 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x12, 0x29, 0x0a, 0x08, 0x70, 0x61, 0x73,
+ 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70,
+ 0x69, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61,
+ 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c,
+ 0x72, 0x65, 0x61, 0x64, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01,
+ 0x28, 0x08, 0x52, 0x0d, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74,
+ 0x73, 0x22, 0x67, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77,
+ 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08,
+ 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07,
+ 0x6e, 0x65, 0x77, 0x48, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x75,
+ 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e,
+ 0x65, 0x77, 0x55, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x12, 0x55, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70,
+ 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x29, 0x0a,
+ 0x11, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
+ 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x31, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65,
+ 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b,
+ 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+ 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x4c,
+ 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x22, 0x3f,
+ 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65,
+ 0x73, 0x70, 0x12, 0x2b, 0x0a, 0x09, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18,
+ 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x52, 0x09, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x22,
+ 0x5b, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x0e, 0x0a, 0x02,
+ 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04,
+ 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65,
+ 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+ 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04,
+ 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x42, 0x0a, 0x12,
+ 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52,
+ 0x65, 0x71, 0x12, 0x2c, 0x0a, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18,
+ 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x09, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x22, 0x3c, 0x0a, 0x13, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63,
+ 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x72, 0x65, 0x61,
0x64, 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
- 0x0d, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x12, 0x23,
- 0x0a, 0x06, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0b,
- 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x06, 0x63, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x22, 0x21, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x22, 0x2f, 0x0a, 0x10, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
- 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f,
+ 0x0d, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x22, 0x79,
+ 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f,
+ 0x72, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x74, 0x79, 0x70, 0x65,
+ 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x54, 0x79, 0x70, 0x65, 0x12,
+ 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6e, 0x65,
+ 0x77, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09,
+ 0x6e, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x32, 0x0a, 0x13, 0x55, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70,
+ 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x24, 0x0a,
+ 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+ 0x02, 0x69, 0x64, 0x22, 0x32, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e,
+ 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f,
0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e,
- 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x9a, 0x01, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61,
- 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x12, 0x0e, 0x0a, 0x02, 0x69,
- 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x23, 0x0a, 0x0d, 0x72,
- 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x73, 0x18, 0x02, 0x20, 0x03,
- 0x28, 0x09, 0x52, 0x0c, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x73,
- 0x12, 0x23, 0x0a, 0x0d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x70, 0x65, 0x65, 0x72,
- 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64,
- 0x50, 0x65, 0x65, 0x72, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20,
- 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x6c, 0x6f, 0x67,
- 0x6f, 0x5f, 0x75, 0x72, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6c, 0x6f, 0x67,
- 0x6f, 0x55, 0x72, 0x6c, 0x22, 0x2f, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c,
- 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f,
- 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74,
- 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x69, 0x0a, 0x08, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
- 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
- 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18,
- 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x75,
- 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75,
- 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f,
- 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64,
- 0x22, 0x3e, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
- 0x72, 0x64, 0x52, 0x65, 0x71, 0x12, 0x29, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
- 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61,
- 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
- 0x22, 0x3b, 0x0a, 0x12, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
- 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64,
- 0x79, 0x5f, 0x65, 0x78, 0x69, 0x73, 0x74, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d,
- 0x61, 0x6c, 0x72, 0x65, 0x61, 0x64, 0x79, 0x45, 0x78, 0x69, 0x73, 0x74, 0x73, 0x22, 0x67, 0x0a,
- 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
+ 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x12, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x43,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x22, 0x43, 0x0a, 0x11, 0x4c,
+ 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70,
+ 0x12, 0x2e, 0x0a, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73, 0x18, 0x01,
+ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x65,
+ 0x63, 0x74, 0x6f, 0x72, 0x52, 0x0a, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x73,
+ 0x22, 0x0c, 0x0a, 0x0a, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x22, 0x37,
+ 0x0a, 0x0b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a,
+ 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x02, 0x20, 0x01,
+ 0x28, 0x05, 0x52, 0x03, 0x61, 0x70, 0x69, 0x22, 0x0e, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x22, 0xb0, 0x06, 0x0a, 0x0d, 0x44, 0x69, 0x73, 0x63,
+ 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x73,
+ 0x75, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x69, 0x73, 0x73, 0x75, 0x65,
+ 0x72, 0x12, 0x35, 0x0a, 0x16, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
+ 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x15, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+ 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x74, 0x6f, 0x6b, 0x65,
+ 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x0d, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12,
+ 0x19, 0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, 0x72, 0x69, 0x12, 0x2b, 0x0a, 0x11, 0x75, 0x73,
+ 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18,
+ 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x75, 0x73, 0x65, 0x72, 0x69, 0x6e, 0x66, 0x6f, 0x45,
+ 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x42, 0x0a, 0x1d, 0x64, 0x65, 0x76, 0x69, 0x63,
+ 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f,
+ 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1b,
+ 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x35, 0x0a, 0x16, 0x69,
+ 0x6e, 0x74, 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x64,
+ 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x15, 0x69, 0x6e, 0x74,
+ 0x72, 0x6f, 0x73, 0x70, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69,
+ 0x6e, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x08, 0x20, 0x03, 0x28,
+ 0x09, 0x52, 0x13, 0x67, 0x72, 0x61, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x53, 0x75, 0x70,
+ 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x38, 0x0a, 0x18, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
+ 0x65, 0x64, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x16, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+ 0x73, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64,
+ 0x12, 0x36, 0x0a, 0x17, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65,
+ 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03, 0x28,
+ 0x09, 0x52, 0x15, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x53,
+ 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x4f, 0x0a, 0x25, 0x69, 0x64, 0x5f, 0x74,
+ 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x61, 0x6c, 0x67,
+ 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65,
+ 0x64, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x52, 0x20, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
+ 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x41, 0x6c, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73,
+ 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x47, 0x0a, 0x20, 0x63, 0x6f, 0x64,
+ 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68,
+ 0x6f, 0x64, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20,
+ 0x03, 0x28, 0x09, 0x52, 0x1d, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e,
+ 0x67, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74,
+ 0x65, 0x64, 0x12, 0x29, 0x0a, 0x10, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x5f, 0x73, 0x75, 0x70,
+ 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x73, 0x63,
+ 0x6f, 0x70, 0x65, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12, 0x50, 0x0a,
+ 0x25, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f,
+ 0x61, 0x75, 0x74, 0x68, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x5f, 0x73, 0x75, 0x70,
+ 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x09, 0x52, 0x21, 0x74, 0x6f,
+ 0x6b, 0x65, 0x6e, 0x45, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x41, 0x75, 0x74, 0x68, 0x4d,
+ 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x12,
+ 0x29, 0x0a, 0x10, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x73, 0x5f, 0x73, 0x75, 0x70, 0x70, 0x6f, 0x72,
+ 0x74, 0x65, 0x64, 0x18, 0x0f, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x63, 0x6c, 0x61, 0x69, 0x6d,
+ 0x73, 0x53, 0x75, 0x70, 0x70, 0x6f, 0x72, 0x74, 0x65, 0x64, 0x22, 0x7a, 0x0a, 0x0f, 0x52, 0x65,
+ 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x0e, 0x0a,
+ 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a,
+ 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+ 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72,
+ 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09,
+ 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x73,
+ 0x74, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x61,
+ 0x73, 0x74, 0x55, 0x73, 0x65, 0x64, 0x22, 0x29, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65,
+ 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72,
+ 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49,
+ 0x64, 0x22, 0x4e, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68,
+ 0x52, 0x65, 0x73, 0x70, 0x12, 0x3b, 0x0a, 0x0e, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f,
+ 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61,
+ 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52,
+ 0x65, 0x66, 0x52, 0x0d, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
+ 0x73, 0x22, 0x48, 0x0a, 0x10, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65,
+ 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64,
+ 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b,
+ 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
+ 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x22, 0x30, 0x0a, 0x11, 0x52,
+ 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70,
+ 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20,
+ 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x45, 0x0a,
+ 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f,
- 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48,
- 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x6e, 0x65, 0x77, 0x5f, 0x75, 0x73, 0x65, 0x72, 0x6e,
- 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6e, 0x65, 0x77, 0x55, 0x73,
- 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x31, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65,
- 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09,
- 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
- 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x29, 0x0a, 0x11, 0x44, 0x65, 0x6c,
- 0x65, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x12, 0x14,
- 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65,
- 0x6d, 0x61, 0x69, 0x6c, 0x22, 0x31, 0x0a, 0x12, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61,
- 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f,
- 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e,
- 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x11, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x50,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x22, 0x3f, 0x0a, 0x10, 0x4c, 0x69,
- 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x2b,
- 0x0a, 0x09, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
- 0x0b, 0x32, 0x0d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
- 0x52, 0x09, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x22, 0x0c, 0x0a, 0x0a, 0x56,
- 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x22, 0x37, 0x0a, 0x0b, 0x56, 0x65, 0x72,
- 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76,
- 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
- 0x12, 0x10, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x61,
- 0x70, 0x69, 0x22, 0x7a, 0x0a, 0x0f, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b,
- 0x65, 0x6e, 0x52, 0x65, 0x66, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f,
- 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
- 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74,
- 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41,
- 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x75, 0x73, 0x65, 0x64, 0x18, 0x06,
- 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, 0x55, 0x73, 0x65, 0x64, 0x22, 0x29,
- 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71,
- 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
- 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x22, 0x4e, 0x0a, 0x0f, 0x4c, 0x69, 0x73,
- 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x12, 0x3b, 0x0a, 0x0e,
- 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x18, 0x01,
- 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65,
- 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x66, 0x52, 0x0d, 0x72, 0x65, 0x66, 0x72,
- 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x73, 0x22, 0x48, 0x0a, 0x10, 0x52, 0x65, 0x76,
- 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x12, 0x17, 0x0a,
- 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06,
- 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
- 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e,
- 0x74, 0x49, 0x64, 0x22, 0x30, 0x0a, 0x11, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66,
- 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f,
- 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74,
- 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x22, 0x45, 0x0a, 0x11, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d,
- 0x61, 0x69, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c,
- 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01,
- 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4d, 0x0a, 0x12,
- 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65,
- 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x01,
- 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1b,
- 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,
- 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x32, 0xc7, 0x05, 0x0a, 0x03,
- 0x44, 0x65, 0x78, 0x12, 0x3d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69,
- 0x65, 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
- 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e,
- 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70,
- 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65,
- 0x6e, 0x74, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43,
- 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55,
- 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22,
- 0x00, 0x12, 0x3d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e,
- 0x74, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c,
- 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,
- 0x12, 0x43, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
- 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69,
+ 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x22, 0x4d, 0x0a, 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61,
+ 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x65,
+ 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x76, 0x65,
+ 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x5f, 0x66, 0x6f,
+ 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x6e, 0x6f, 0x74, 0x46, 0x6f,
+ 0x75, 0x6e, 0x64, 0x32, 0x8b, 0x09, 0x0a, 0x03, 0x44, 0x65, 0x78, 0x12, 0x34, 0x0a, 0x09, 0x47,
+ 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47,
+ 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x61, 0x70,
+ 0x69, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22,
+ 0x00, 0x12, 0x3d, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e,
+ 0x74, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c,
+ 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72,
+ 0x65, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,
+ 0x12, 0x3d, 0x0a, 0x0c, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74,
+ 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64,
+ 0x61, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
+ 0x3d, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x12,
+ 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65,
+ 0x6e, 0x74, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65,
+ 0x74, 0x65, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x38,
+ 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x12, 0x2e,
+ 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65,
+ 0x71, 0x1a, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6c, 0x69, 0x65,
+ 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e, 0x43, 0x72, 0x65, 0x61,
+ 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69,
0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
- 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70,
- 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a,
- 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73,
- 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e, 0x44, 0x65,
- 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61,
+ 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x50,
+ 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a,
+ 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12,
+ 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70,
+ 0x64, 0x61, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70,
+ 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73,
+ 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
+ 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
- 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74,
- 0x65, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
- 0x3e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73,
- 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77,
- 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73,
- 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
- 0x31, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0f, 0x2e,
- 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x1a, 0x10,
- 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x73, 0x70,
- 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73,
- 0x68, 0x12, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72,
- 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73,
- 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x40,
- 0x0a, 0x0d, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12,
- 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72,
- 0x65, 0x73, 0x68, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x76,
- 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00,
- 0x12, 0x43, 0x0a, 0x0e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f,
- 0x72, 0x64, 0x12, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50,
- 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69,
- 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
- 0x65, 0x73, 0x70, 0x22, 0x00, 0x42, 0x36, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x72,
- 0x65, 0x6f, 0x73, 0x2e, 0x64, 0x65, 0x78, 0x2e, 0x61, 0x70, 0x69, 0x5a, 0x20, 0x67, 0x69, 0x74,
- 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x78, 0x69, 0x64, 0x70, 0x2f, 0x64,
- 0x65, 0x78, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70,
- 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
+ 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x3e, 0x0a, 0x0d, 0x4c, 0x69, 0x73, 0x74, 0x50,
+ 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
+ 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x15,
+ 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72,
+ 0x64, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74,
+ 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69,
+ 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65,
+ 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
+ 0x46, 0x0a, 0x0f, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74,
+ 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43,
+ 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x61, 0x70,
+ 0x69, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f,
+ 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x46, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74,
+ 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69,
+ 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x52, 0x65, 0x71, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65,
+ 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12,
+ 0x41, 0x0a, 0x0e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72,
+ 0x73, 0x12, 0x15, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e,
+ 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c,
+ 0x69, 0x73, 0x74, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x52, 0x65, 0x73, 0x70,
+ 0x22, 0x00, 0x12, 0x31, 0x0a, 0x0a, 0x47, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x12, 0x0f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x65,
+ 0x71, 0x1a, 0x10, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52,
+ 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x44, 0x69, 0x73, 0x63,
+ 0x6f, 0x76, 0x65, 0x72, 0x79, 0x12, 0x11, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x69, 0x73, 0x63,
+ 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x71, 0x1a, 0x12, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44,
+ 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x3a,
+ 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x13, 0x2e,
+ 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52,
+ 0x65, 0x71, 0x1a, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x66,
+ 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x0d, 0x52, 0x65,
+ 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x12, 0x15, 0x2e, 0x61, 0x70,
+ 0x69, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52,
+ 0x65, 0x71, 0x1a, 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x52,
+ 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x52, 0x65, 0x73, 0x70, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0e,
+ 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x16,
+ 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77,
+ 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x1a, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x56, 0x65, 0x72,
+ 0x69, 0x66, 0x79, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x22,
+ 0x00, 0x42, 0x36, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x6f, 0x73, 0x2e,
+ 0x64, 0x65, 0x78, 0x2e, 0x61, 0x70, 0x69, 0x5a, 0x20, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
+ 0x63, 0x6f, 0x6d, 0x2f, 0x64, 0x65, 0x78, 0x69, 0x64, 0x70, 0x2f, 0x64, 0x65, 0x78, 0x2f, 0x61,
+ 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+ 0x33,
+})
var (
file_api_v2_api_proto_rawDescOnce sync.Once
- file_api_v2_api_proto_rawDescData = file_api_v2_api_proto_rawDesc
+ file_api_v2_api_proto_rawDescData []byte
)
func file_api_v2_api_proto_rawDescGZIP() []byte {
file_api_v2_api_proto_rawDescOnce.Do(func() {
- file_api_v2_api_proto_rawDescData = protoimpl.X.CompressGZIP(file_api_v2_api_proto_rawDescData)
+ file_api_v2_api_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_api_v2_api_proto_rawDesc), len(file_api_v2_api_proto_rawDesc)))
})
return file_api_v2_api_proto_rawDescData
}
-var file_api_v2_api_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
-var file_api_v2_api_proto_goTypes = []interface{}{
- (*Client)(nil), // 0: api.Client
- (*CreateClientReq)(nil), // 1: api.CreateClientReq
- (*CreateClientResp)(nil), // 2: api.CreateClientResp
- (*DeleteClientReq)(nil), // 3: api.DeleteClientReq
- (*DeleteClientResp)(nil), // 4: api.DeleteClientResp
- (*UpdateClientReq)(nil), // 5: api.UpdateClientReq
- (*UpdateClientResp)(nil), // 6: api.UpdateClientResp
- (*Password)(nil), // 7: api.Password
- (*CreatePasswordReq)(nil), // 8: api.CreatePasswordReq
- (*CreatePasswordResp)(nil), // 9: api.CreatePasswordResp
- (*UpdatePasswordReq)(nil), // 10: api.UpdatePasswordReq
- (*UpdatePasswordResp)(nil), // 11: api.UpdatePasswordResp
- (*DeletePasswordReq)(nil), // 12: api.DeletePasswordReq
- (*DeletePasswordResp)(nil), // 13: api.DeletePasswordResp
- (*ListPasswordReq)(nil), // 14: api.ListPasswordReq
- (*ListPasswordResp)(nil), // 15: api.ListPasswordResp
- (*VersionReq)(nil), // 16: api.VersionReq
- (*VersionResp)(nil), // 17: api.VersionResp
- (*RefreshTokenRef)(nil), // 18: api.RefreshTokenRef
- (*ListRefreshReq)(nil), // 19: api.ListRefreshReq
- (*ListRefreshResp)(nil), // 20: api.ListRefreshResp
- (*RevokeRefreshReq)(nil), // 21: api.RevokeRefreshReq
- (*RevokeRefreshResp)(nil), // 22: api.RevokeRefreshResp
- (*VerifyPasswordReq)(nil), // 23: api.VerifyPasswordReq
- (*VerifyPasswordResp)(nil), // 24: api.VerifyPasswordResp
+var file_api_v2_api_proto_msgTypes = make([]protoimpl.MessageInfo, 41)
+var file_api_v2_api_proto_goTypes = []any{
+ (*Client)(nil), // 0: api.Client
+ (*ClientInfo)(nil), // 1: api.ClientInfo
+ (*GetClientReq)(nil), // 2: api.GetClientReq
+ (*GetClientResp)(nil), // 3: api.GetClientResp
+ (*CreateClientReq)(nil), // 4: api.CreateClientReq
+ (*CreateClientResp)(nil), // 5: api.CreateClientResp
+ (*DeleteClientReq)(nil), // 6: api.DeleteClientReq
+ (*DeleteClientResp)(nil), // 7: api.DeleteClientResp
+ (*UpdateClientReq)(nil), // 8: api.UpdateClientReq
+ (*UpdateClientResp)(nil), // 9: api.UpdateClientResp
+ (*ListClientReq)(nil), // 10: api.ListClientReq
+ (*ListClientResp)(nil), // 11: api.ListClientResp
+ (*Password)(nil), // 12: api.Password
+ (*CreatePasswordReq)(nil), // 13: api.CreatePasswordReq
+ (*CreatePasswordResp)(nil), // 14: api.CreatePasswordResp
+ (*UpdatePasswordReq)(nil), // 15: api.UpdatePasswordReq
+ (*UpdatePasswordResp)(nil), // 16: api.UpdatePasswordResp
+ (*DeletePasswordReq)(nil), // 17: api.DeletePasswordReq
+ (*DeletePasswordResp)(nil), // 18: api.DeletePasswordResp
+ (*ListPasswordReq)(nil), // 19: api.ListPasswordReq
+ (*ListPasswordResp)(nil), // 20: api.ListPasswordResp
+ (*Connector)(nil), // 21: api.Connector
+ (*CreateConnectorReq)(nil), // 22: api.CreateConnectorReq
+ (*CreateConnectorResp)(nil), // 23: api.CreateConnectorResp
+ (*UpdateConnectorReq)(nil), // 24: api.UpdateConnectorReq
+ (*UpdateConnectorResp)(nil), // 25: api.UpdateConnectorResp
+ (*DeleteConnectorReq)(nil), // 26: api.DeleteConnectorReq
+ (*DeleteConnectorResp)(nil), // 27: api.DeleteConnectorResp
+ (*ListConnectorReq)(nil), // 28: api.ListConnectorReq
+ (*ListConnectorResp)(nil), // 29: api.ListConnectorResp
+ (*VersionReq)(nil), // 30: api.VersionReq
+ (*VersionResp)(nil), // 31: api.VersionResp
+ (*DiscoveryReq)(nil), // 32: api.DiscoveryReq
+ (*DiscoveryResp)(nil), // 33: api.DiscoveryResp
+ (*RefreshTokenRef)(nil), // 34: api.RefreshTokenRef
+ (*ListRefreshReq)(nil), // 35: api.ListRefreshReq
+ (*ListRefreshResp)(nil), // 36: api.ListRefreshResp
+ (*RevokeRefreshReq)(nil), // 37: api.RevokeRefreshReq
+ (*RevokeRefreshResp)(nil), // 38: api.RevokeRefreshResp
+ (*VerifyPasswordReq)(nil), // 39: api.VerifyPasswordReq
+ (*VerifyPasswordResp)(nil), // 40: api.VerifyPasswordResp
}
var file_api_v2_api_proto_depIdxs = []int32{
- 0, // 0: api.CreateClientReq.client:type_name -> api.Client
- 0, // 1: api.CreateClientResp.client:type_name -> api.Client
- 7, // 2: api.CreatePasswordReq.password:type_name -> api.Password
- 7, // 3: api.ListPasswordResp.passwords:type_name -> api.Password
- 18, // 4: api.ListRefreshResp.refresh_tokens:type_name -> api.RefreshTokenRef
- 1, // 5: api.Dex.CreateClient:input_type -> api.CreateClientReq
- 5, // 6: api.Dex.UpdateClient:input_type -> api.UpdateClientReq
- 3, // 7: api.Dex.DeleteClient:input_type -> api.DeleteClientReq
- 8, // 8: api.Dex.CreatePassword:input_type -> api.CreatePasswordReq
- 10, // 9: api.Dex.UpdatePassword:input_type -> api.UpdatePasswordReq
- 12, // 10: api.Dex.DeletePassword:input_type -> api.DeletePasswordReq
- 14, // 11: api.Dex.ListPasswords:input_type -> api.ListPasswordReq
- 16, // 12: api.Dex.GetVersion:input_type -> api.VersionReq
- 19, // 13: api.Dex.ListRefresh:input_type -> api.ListRefreshReq
- 21, // 14: api.Dex.RevokeRefresh:input_type -> api.RevokeRefreshReq
- 23, // 15: api.Dex.VerifyPassword:input_type -> api.VerifyPasswordReq
- 2, // 16: api.Dex.CreateClient:output_type -> api.CreateClientResp
- 6, // 17: api.Dex.UpdateClient:output_type -> api.UpdateClientResp
- 4, // 18: api.Dex.DeleteClient:output_type -> api.DeleteClientResp
- 9, // 19: api.Dex.CreatePassword:output_type -> api.CreatePasswordResp
- 11, // 20: api.Dex.UpdatePassword:output_type -> api.UpdatePasswordResp
- 13, // 21: api.Dex.DeletePassword:output_type -> api.DeletePasswordResp
- 15, // 22: api.Dex.ListPasswords:output_type -> api.ListPasswordResp
- 17, // 23: api.Dex.GetVersion:output_type -> api.VersionResp
- 20, // 24: api.Dex.ListRefresh:output_type -> api.ListRefreshResp
- 22, // 25: api.Dex.RevokeRefresh:output_type -> api.RevokeRefreshResp
- 24, // 26: api.Dex.VerifyPassword:output_type -> api.VerifyPasswordResp
- 16, // [16:27] is the sub-list for method output_type
- 5, // [5:16] is the sub-list for method input_type
- 5, // [5:5] is the sub-list for extension type_name
- 5, // [5:5] is the sub-list for extension extendee
- 0, // [0:5] is the sub-list for field type_name
+ 0, // 0: api.GetClientResp.client:type_name -> api.Client
+ 0, // 1: api.CreateClientReq.client:type_name -> api.Client
+ 0, // 2: api.CreateClientResp.client:type_name -> api.Client
+ 1, // 3: api.ListClientResp.clients:type_name -> api.ClientInfo
+ 12, // 4: api.CreatePasswordReq.password:type_name -> api.Password
+ 12, // 5: api.ListPasswordResp.passwords:type_name -> api.Password
+ 21, // 6: api.CreateConnectorReq.connector:type_name -> api.Connector
+ 21, // 7: api.ListConnectorResp.connectors:type_name -> api.Connector
+ 34, // 8: api.ListRefreshResp.refresh_tokens:type_name -> api.RefreshTokenRef
+ 2, // 9: api.Dex.GetClient:input_type -> api.GetClientReq
+ 4, // 10: api.Dex.CreateClient:input_type -> api.CreateClientReq
+ 8, // 11: api.Dex.UpdateClient:input_type -> api.UpdateClientReq
+ 6, // 12: api.Dex.DeleteClient:input_type -> api.DeleteClientReq
+ 10, // 13: api.Dex.ListClients:input_type -> api.ListClientReq
+ 13, // 14: api.Dex.CreatePassword:input_type -> api.CreatePasswordReq
+ 15, // 15: api.Dex.UpdatePassword:input_type -> api.UpdatePasswordReq
+ 17, // 16: api.Dex.DeletePassword:input_type -> api.DeletePasswordReq
+ 19, // 17: api.Dex.ListPasswords:input_type -> api.ListPasswordReq
+ 22, // 18: api.Dex.CreateConnector:input_type -> api.CreateConnectorReq
+ 24, // 19: api.Dex.UpdateConnector:input_type -> api.UpdateConnectorReq
+ 26, // 20: api.Dex.DeleteConnector:input_type -> api.DeleteConnectorReq
+ 28, // 21: api.Dex.ListConnectors:input_type -> api.ListConnectorReq
+ 30, // 22: api.Dex.GetVersion:input_type -> api.VersionReq
+ 32, // 23: api.Dex.GetDiscovery:input_type -> api.DiscoveryReq
+ 35, // 24: api.Dex.ListRefresh:input_type -> api.ListRefreshReq
+ 37, // 25: api.Dex.RevokeRefresh:input_type -> api.RevokeRefreshReq
+ 39, // 26: api.Dex.VerifyPassword:input_type -> api.VerifyPasswordReq
+ 3, // 27: api.Dex.GetClient:output_type -> api.GetClientResp
+ 5, // 28: api.Dex.CreateClient:output_type -> api.CreateClientResp
+ 9, // 29: api.Dex.UpdateClient:output_type -> api.UpdateClientResp
+ 7, // 30: api.Dex.DeleteClient:output_type -> api.DeleteClientResp
+ 11, // 31: api.Dex.ListClients:output_type -> api.ListClientResp
+ 14, // 32: api.Dex.CreatePassword:output_type -> api.CreatePasswordResp
+ 16, // 33: api.Dex.UpdatePassword:output_type -> api.UpdatePasswordResp
+ 18, // 34: api.Dex.DeletePassword:output_type -> api.DeletePasswordResp
+ 20, // 35: api.Dex.ListPasswords:output_type -> api.ListPasswordResp
+ 23, // 36: api.Dex.CreateConnector:output_type -> api.CreateConnectorResp
+ 25, // 37: api.Dex.UpdateConnector:output_type -> api.UpdateConnectorResp
+ 27, // 38: api.Dex.DeleteConnector:output_type -> api.DeleteConnectorResp
+ 29, // 39: api.Dex.ListConnectors:output_type -> api.ListConnectorResp
+ 31, // 40: api.Dex.GetVersion:output_type -> api.VersionResp
+ 33, // 41: api.Dex.GetDiscovery:output_type -> api.DiscoveryResp
+ 36, // 42: api.Dex.ListRefresh:output_type -> api.ListRefreshResp
+ 38, // 43: api.Dex.RevokeRefresh:output_type -> api.RevokeRefreshResp
+ 40, // 44: api.Dex.VerifyPassword:output_type -> api.VerifyPasswordResp
+ 27, // [27:45] is the sub-list for method output_type
+ 9, // [9:27] is the sub-list for method input_type
+ 9, // [9:9] is the sub-list for extension type_name
+ 9, // [9:9] is the sub-list for extension extendee
+ 0, // [0:9] is the sub-list for field type_name
}
func init() { file_api_v2_api_proto_init() }
@@ -1642,315 +2640,13 @@ func file_api_v2_api_proto_init() {
if File_api_v2_api_proto != nil {
return
}
- if !protoimpl.UnsafeEnabled {
- file_api_v2_api_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Client); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreateClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeleteClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateClientReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdateClientResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*Password); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreatePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*CreatePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdatePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*UpdatePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeletePasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*DeletePasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListPasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListPasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VersionReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VersionResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RefreshTokenRef); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListRefreshReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*ListRefreshResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RevokeRefreshReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*RevokeRefreshResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VerifyPasswordReq); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- file_api_v2_api_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
- switch v := v.(*VerifyPasswordResp); i {
- case 0:
- return &v.state
- case 1:
- return &v.sizeCache
- case 2:
- return &v.unknownFields
- default:
- return nil
- }
- }
- }
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
- RawDescriptor: file_api_v2_api_proto_rawDesc,
+ RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_v2_api_proto_rawDesc), len(file_api_v2_api_proto_rawDesc)),
NumEnums: 0,
- NumMessages: 25,
+ NumMessages: 41,
NumExtensions: 0,
NumServices: 1,
},
@@ -1959,7 +2655,6 @@ func file_api_v2_api_proto_init() {
MessageInfos: file_api_v2_api_proto_msgTypes,
}.Build()
File_api_v2_api_proto = out.File
- file_api_v2_api_proto_rawDesc = nil
file_api_v2_api_proto_goTypes = nil
file_api_v2_api_proto_depIdxs = nil
}
diff --git a/api/v2/api.proto b/api/v2/api.proto
index 82a2e2afa1..fc14913b81 100644
--- a/api/v2/api.proto
+++ b/api/v2/api.proto
@@ -16,6 +16,27 @@ message Client {
string logo_url = 7;
}
+// ClientInfo represents an OAuth2 client without sensitive information.
+message ClientInfo {
+ string id = 1;
+ repeated string redirect_uris = 2;
+ repeated string trusted_peers = 3;
+ bool public = 4;
+ string name = 5;
+ string logo_url = 6;
+}
+
+// GetClientReq is a request to retrieve client details.
+message GetClientReq {
+ // The ID of the client.
+ string id = 1;
+}
+
+// GetClientResp returns the client details.
+message GetClientResp {
+ Client client = 1;
+}
+
// CreateClientReq is a request to make a client.
message CreateClientReq {
Client client = 1;
@@ -52,6 +73,14 @@ message UpdateClientResp {
bool not_found = 1;
}
+// ListClientReq is a request to enumerate clients.
+message ListClientReq {}
+
+// ListClientResp returns a list of clients.
+message ListClientResp {
+ repeated ClientInfo clients = 1;
+}
+
// TODO(ericchiang): expand this.
// Password is an email for password mapping managed by the storage.
@@ -105,6 +134,56 @@ message ListPasswordResp {
repeated Password passwords = 1;
}
+// Connector is a strategy used by Dex for authenticating a user against another identity provider
+message Connector {
+ string id = 1;
+ string type = 2;
+ string name = 3;
+ bytes config = 4;
+}
+
+// CreateConnectorReq is a request to make a connector.
+message CreateConnectorReq {
+ Connector connector = 1;
+}
+
+// CreateConnectorResp returns the response from creating a connector.
+message CreateConnectorResp {
+ bool already_exists = 1;
+}
+
+// UpdateConnectorReq is a request to modify an existing connector.
+message UpdateConnectorReq {
+ // The id used to lookup the connector. This field cannot be modified
+ string id = 1;
+ string new_type = 2;
+ string new_name = 3;
+ bytes new_config = 4;
+}
+
+// UpdateConnectorResp returns the response from modifying an existing connector.
+message UpdateConnectorResp {
+ bool not_found = 1;
+}
+
+// DeleteConnectorReq is a request to delete a connector.
+message DeleteConnectorReq {
+ string id = 1;
+}
+
+// DeleteConnectorResp returns the response from deleting a connector.
+message DeleteConnectorResp {
+ bool not_found = 1;
+}
+
+// ListConnectorReq is a request to enumerate connectors.
+message ListConnectorReq {}
+
+// ListConnectorResp returns a list of connectors.
+message ListConnectorResp {
+ repeated Connector connectors = 1;
+}
+
// VersionReq is a request to fetch version info.
message VersionReq {}
@@ -112,11 +191,33 @@ message VersionReq {}
message VersionResp {
// Semantic version of the server.
string server = 1;
- // Numeric version of the API. It increases everytime a new call is added to the API.
+ // Numeric version of the API. It increases every time a new call is added to the API.
// Clients should use this info to determine if the server supports specific features.
int32 api = 2;
}
+// DiscoveryReq is a request to fetch discover information.
+message DiscoveryReq {}
+
+//DiscoverResp holds the version oidc disovery info.
+message DiscoveryResp {
+ string issuer = 1;
+ string authorization_endpoint = 2;
+ string token_endpoint = 3;
+ string jwks_uri = 4;
+ string userinfo_endpoint = 5;
+ string device_authorization_endpoint = 6;
+ string introspection_endpoint = 7;
+ repeated string grant_types_supported = 8;
+ repeated string response_types_supported = 9;
+ repeated string subject_types_supported = 10;
+ repeated string id_token_signing_alg_values_supported = 11;
+ repeated string code_challenge_methods_supported = 12;
+ repeated string scopes_supported = 13;
+ repeated string token_endpoint_auth_methods_supported = 14;
+ repeated string claims_supported = 15;
+}
+
// RefreshTokenRef contains the metadata for a refresh token that is managed by the storage.
message RefreshTokenRef {
// ID of the refresh token.
@@ -162,12 +263,16 @@ message VerifyPasswordResp {
// Dex represents the dex gRPC service.
service Dex {
+ // GetClient gets a client.
+ rpc GetClient(GetClientReq) returns (GetClientResp) {};
// CreateClient creates a client.
rpc CreateClient(CreateClientReq) returns (CreateClientResp) {};
// UpdateClient updates an existing client
rpc UpdateClient(UpdateClientReq) returns (UpdateClientResp) {};
// DeleteClient deletes the provided client.
rpc DeleteClient(DeleteClientReq) returns (DeleteClientResp) {};
+ // ListClients lists all client entries.
+ rpc ListClients(ListClientReq) returns (ListClientResp) {};
// CreatePassword creates a password.
rpc CreatePassword(CreatePasswordReq) returns (CreatePasswordResp) {};
// UpdatePassword modifies existing password.
@@ -176,8 +281,18 @@ service Dex {
rpc DeletePassword(DeletePasswordReq) returns (DeletePasswordResp) {};
// ListPassword lists all password entries.
rpc ListPasswords(ListPasswordReq) returns (ListPasswordResp) {};
+ // CreateConnector creates a connector.
+ rpc CreateConnector(CreateConnectorReq) returns (CreateConnectorResp) {};
+ // UpdateConnector modifies existing connector.
+ rpc UpdateConnector(UpdateConnectorReq) returns (UpdateConnectorResp) {};
+ // DeleteConnector deletes the connector.
+ rpc DeleteConnector(DeleteConnectorReq) returns (DeleteConnectorResp) {};
+ // ListConnectors lists all connector entries.
+ rpc ListConnectors(ListConnectorReq) returns (ListConnectorResp) {};
// GetVersion returns version information of the server.
rpc GetVersion(VersionReq) returns (VersionResp) {};
+ // GetDiscovery returns discovery information of the server.
+ rpc GetDiscovery(DiscoveryReq) returns (DiscoveryResp) {};
// ListRefresh lists all the refresh token entries for a particular user.
rpc ListRefresh(ListRefreshReq) returns (ListRefreshResp) {};
// RevokeRefresh revokes the refresh token for the provided user-client pair.
diff --git a/api/v2/api_grpc.pb.go b/api/v2/api_grpc.pb.go
index 8b3b10bc52..3fe210e6ff 100644
--- a/api/v2/api_grpc.pb.go
+++ b/api/v2/api_grpc.pb.go
@@ -1,4 +1,8 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.5.1
+// - protoc v5.29.3
+// source: api/v2/api.proto
package api
@@ -11,19 +15,46 @@ import (
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
-// Requires gRPC-Go v1.32.0 or later.
-const _ = grpc.SupportPackageIsVersion7
+// Requires gRPC-Go v1.64.0 or later.
+const _ = grpc.SupportPackageIsVersion9
+
+const (
+ Dex_GetClient_FullMethodName = "/api.Dex/GetClient"
+ Dex_CreateClient_FullMethodName = "/api.Dex/CreateClient"
+ Dex_UpdateClient_FullMethodName = "/api.Dex/UpdateClient"
+ Dex_DeleteClient_FullMethodName = "/api.Dex/DeleteClient"
+ Dex_ListClients_FullMethodName = "/api.Dex/ListClients"
+ Dex_CreatePassword_FullMethodName = "/api.Dex/CreatePassword"
+ Dex_UpdatePassword_FullMethodName = "/api.Dex/UpdatePassword"
+ Dex_DeletePassword_FullMethodName = "/api.Dex/DeletePassword"
+ Dex_ListPasswords_FullMethodName = "/api.Dex/ListPasswords"
+ Dex_CreateConnector_FullMethodName = "/api.Dex/CreateConnector"
+ Dex_UpdateConnector_FullMethodName = "/api.Dex/UpdateConnector"
+ Dex_DeleteConnector_FullMethodName = "/api.Dex/DeleteConnector"
+ Dex_ListConnectors_FullMethodName = "/api.Dex/ListConnectors"
+ Dex_GetVersion_FullMethodName = "/api.Dex/GetVersion"
+ Dex_GetDiscovery_FullMethodName = "/api.Dex/GetDiscovery"
+ Dex_ListRefresh_FullMethodName = "/api.Dex/ListRefresh"
+ Dex_RevokeRefresh_FullMethodName = "/api.Dex/RevokeRefresh"
+ Dex_VerifyPassword_FullMethodName = "/api.Dex/VerifyPassword"
+)
// DexClient is the client API for Dex service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+//
+// Dex represents the dex gRPC service.
type DexClient interface {
+ // GetClient gets a client.
+ GetClient(ctx context.Context, in *GetClientReq, opts ...grpc.CallOption) (*GetClientResp, error)
// CreateClient creates a client.
CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error)
// UpdateClient updates an existing client
UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error)
// DeleteClient deletes the provided client.
DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error)
+ // ListClients lists all client entries.
+ ListClients(ctx context.Context, in *ListClientReq, opts ...grpc.CallOption) (*ListClientResp, error)
// CreatePassword creates a password.
CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error)
// UpdatePassword modifies existing password.
@@ -32,8 +63,18 @@ type DexClient interface {
DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error)
// ListPassword lists all password entries.
ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error)
+ // CreateConnector creates a connector.
+ CreateConnector(ctx context.Context, in *CreateConnectorReq, opts ...grpc.CallOption) (*CreateConnectorResp, error)
+ // UpdateConnector modifies existing connector.
+ UpdateConnector(ctx context.Context, in *UpdateConnectorReq, opts ...grpc.CallOption) (*UpdateConnectorResp, error)
+ // DeleteConnector deletes the connector.
+ DeleteConnector(ctx context.Context, in *DeleteConnectorReq, opts ...grpc.CallOption) (*DeleteConnectorResp, error)
+ // ListConnectors lists all connector entries.
+ ListConnectors(ctx context.Context, in *ListConnectorReq, opts ...grpc.CallOption) (*ListConnectorResp, error)
// GetVersion returns version information of the server.
GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error)
+ // GetDiscovery returns discovery information of the server.
+ GetDiscovery(ctx context.Context, in *DiscoveryReq, opts ...grpc.CallOption) (*DiscoveryResp, error)
// ListRefresh lists all the refresh token entries for a particular user.
ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error)
// RevokeRefresh revokes the refresh token for the provided user-client pair.
@@ -52,9 +93,20 @@ func NewDexClient(cc grpc.ClientConnInterface) DexClient {
return &dexClient{cc}
}
+func (c *dexClient) GetClient(ctx context.Context, in *GetClientReq, opts ...grpc.CallOption) (*GetClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(GetClientResp)
+ err := c.cc.Invoke(ctx, Dex_GetClient_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts ...grpc.CallOption) (*CreateClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreateClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/CreateClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_CreateClient_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -62,8 +114,9 @@ func (c *dexClient) CreateClient(ctx context.Context, in *CreateClientReq, opts
}
func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts ...grpc.CallOption) (*UpdateClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdateClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/UpdateClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_UpdateClient_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -71,8 +124,19 @@ func (c *dexClient) UpdateClient(ctx context.Context, in *UpdateClientReq, opts
}
func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts ...grpc.CallOption) (*DeleteClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeleteClientResp)
- err := c.cc.Invoke(ctx, "/api.Dex/DeleteClient", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_DeleteClient_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) ListClients(ctx context.Context, in *ListClientReq, opts ...grpc.CallOption) (*ListClientResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(ListClientResp)
+ err := c.cc.Invoke(ctx, Dex_ListClients_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -80,8 +144,9 @@ func (c *dexClient) DeleteClient(ctx context.Context, in *DeleteClientReq, opts
}
func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, opts ...grpc.CallOption) (*CreatePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(CreatePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/CreatePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_CreatePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -89,8 +154,9 @@ func (c *dexClient) CreatePassword(ctx context.Context, in *CreatePasswordReq, o
}
func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, opts ...grpc.CallOption) (*UpdatePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(UpdatePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/UpdatePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_UpdatePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -98,8 +164,9 @@ func (c *dexClient) UpdatePassword(ctx context.Context, in *UpdatePasswordReq, o
}
func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, opts ...grpc.CallOption) (*DeletePasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(DeletePasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/DeletePassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_DeletePassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -107,8 +174,49 @@ func (c *dexClient) DeletePassword(ctx context.Context, in *DeletePasswordReq, o
}
func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts ...grpc.CallOption) (*ListPasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListPasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/ListPasswords", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_ListPasswords_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) CreateConnector(ctx context.Context, in *CreateConnectorReq, opts ...grpc.CallOption) (*CreateConnectorResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(CreateConnectorResp)
+ err := c.cc.Invoke(ctx, Dex_CreateConnector_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) UpdateConnector(ctx context.Context, in *UpdateConnectorReq, opts ...grpc.CallOption) (*UpdateConnectorResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(UpdateConnectorResp)
+ err := c.cc.Invoke(ctx, Dex_UpdateConnector_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) DeleteConnector(ctx context.Context, in *DeleteConnectorReq, opts ...grpc.CallOption) (*DeleteConnectorResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(DeleteConnectorResp)
+ err := c.cc.Invoke(ctx, Dex_DeleteConnector_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) ListConnectors(ctx context.Context, in *ListConnectorReq, opts ...grpc.CallOption) (*ListConnectorResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(ListConnectorResp)
+ err := c.cc.Invoke(ctx, Dex_ListConnectors_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -116,8 +224,19 @@ func (c *dexClient) ListPasswords(ctx context.Context, in *ListPasswordReq, opts
}
func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc.CallOption) (*VersionResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VersionResp)
- err := c.cc.Invoke(ctx, "/api.Dex/GetVersion", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_GetVersion_FullMethodName, in, out, cOpts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+func (c *dexClient) GetDiscovery(ctx context.Context, in *DiscoveryReq, opts ...grpc.CallOption) (*DiscoveryResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
+ out := new(DiscoveryResp)
+ err := c.cc.Invoke(ctx, Dex_GetDiscovery_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -125,8 +244,9 @@ func (c *dexClient) GetVersion(ctx context.Context, in *VersionReq, opts ...grpc
}
func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ...grpc.CallOption) (*ListRefreshResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(ListRefreshResp)
- err := c.cc.Invoke(ctx, "/api.Dex/ListRefresh", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_ListRefresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -134,8 +254,9 @@ func (c *dexClient) ListRefresh(ctx context.Context, in *ListRefreshReq, opts ..
}
func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opts ...grpc.CallOption) (*RevokeRefreshResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(RevokeRefreshResp)
- err := c.cc.Invoke(ctx, "/api.Dex/RevokeRefresh", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_RevokeRefresh_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -143,8 +264,9 @@ func (c *dexClient) RevokeRefresh(ctx context.Context, in *RevokeRefreshReq, opt
}
func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, opts ...grpc.CallOption) (*VerifyPasswordResp, error) {
+ cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VerifyPasswordResp)
- err := c.cc.Invoke(ctx, "/api.Dex/VerifyPassword", in, out, opts...)
+ err := c.cc.Invoke(ctx, Dex_VerifyPassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
@@ -153,14 +275,20 @@ func (c *dexClient) VerifyPassword(ctx context.Context, in *VerifyPasswordReq, o
// DexServer is the server API for Dex service.
// All implementations must embed UnimplementedDexServer
-// for forward compatibility
+// for forward compatibility.
+//
+// Dex represents the dex gRPC service.
type DexServer interface {
+ // GetClient gets a client.
+ GetClient(context.Context, *GetClientReq) (*GetClientResp, error)
// CreateClient creates a client.
CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error)
// UpdateClient updates an existing client
UpdateClient(context.Context, *UpdateClientReq) (*UpdateClientResp, error)
// DeleteClient deletes the provided client.
DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error)
+ // ListClients lists all client entries.
+ ListClients(context.Context, *ListClientReq) (*ListClientResp, error)
// CreatePassword creates a password.
CreatePassword(context.Context, *CreatePasswordReq) (*CreatePasswordResp, error)
// UpdatePassword modifies existing password.
@@ -169,8 +297,18 @@ type DexServer interface {
DeletePassword(context.Context, *DeletePasswordReq) (*DeletePasswordResp, error)
// ListPassword lists all password entries.
ListPasswords(context.Context, *ListPasswordReq) (*ListPasswordResp, error)
+ // CreateConnector creates a connector.
+ CreateConnector(context.Context, *CreateConnectorReq) (*CreateConnectorResp, error)
+ // UpdateConnector modifies existing connector.
+ UpdateConnector(context.Context, *UpdateConnectorReq) (*UpdateConnectorResp, error)
+ // DeleteConnector deletes the connector.
+ DeleteConnector(context.Context, *DeleteConnectorReq) (*DeleteConnectorResp, error)
+ // ListConnectors lists all connector entries.
+ ListConnectors(context.Context, *ListConnectorReq) (*ListConnectorResp, error)
// GetVersion returns version information of the server.
GetVersion(context.Context, *VersionReq) (*VersionResp, error)
+ // GetDiscovery returns discovery information of the server.
+ GetDiscovery(context.Context, *DiscoveryReq) (*DiscoveryResp, error)
// ListRefresh lists all the refresh token entries for a particular user.
ListRefresh(context.Context, *ListRefreshReq) (*ListRefreshResp, error)
// RevokeRefresh revokes the refresh token for the provided user-client pair.
@@ -182,10 +320,16 @@ type DexServer interface {
mustEmbedUnimplementedDexServer()
}
-// UnimplementedDexServer must be embedded to have forward compatible implementations.
-type UnimplementedDexServer struct {
-}
+// UnimplementedDexServer must be embedded to have
+// forward compatible implementations.
+//
+// NOTE: this should be embedded by value instead of pointer to avoid a nil
+// pointer dereference when methods are called.
+type UnimplementedDexServer struct{}
+func (UnimplementedDexServer) GetClient(context.Context, *GetClientReq) (*GetClientResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetClient not implemented")
+}
func (UnimplementedDexServer) CreateClient(context.Context, *CreateClientReq) (*CreateClientResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreateClient not implemented")
}
@@ -195,6 +339,9 @@ func (UnimplementedDexServer) UpdateClient(context.Context, *UpdateClientReq) (*
func (UnimplementedDexServer) DeleteClient(context.Context, *DeleteClientReq) (*DeleteClientResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method DeleteClient not implemented")
}
+func (UnimplementedDexServer) ListClients(context.Context, *ListClientReq) (*ListClientResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method ListClients not implemented")
+}
func (UnimplementedDexServer) CreatePassword(context.Context, *CreatePasswordReq) (*CreatePasswordResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method CreatePassword not implemented")
}
@@ -207,9 +354,24 @@ func (UnimplementedDexServer) DeletePassword(context.Context, *DeletePasswordReq
func (UnimplementedDexServer) ListPasswords(context.Context, *ListPasswordReq) (*ListPasswordResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListPasswords not implemented")
}
+func (UnimplementedDexServer) CreateConnector(context.Context, *CreateConnectorReq) (*CreateConnectorResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method CreateConnector not implemented")
+}
+func (UnimplementedDexServer) UpdateConnector(context.Context, *UpdateConnectorReq) (*UpdateConnectorResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method UpdateConnector not implemented")
+}
+func (UnimplementedDexServer) DeleteConnector(context.Context, *DeleteConnectorReq) (*DeleteConnectorResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method DeleteConnector not implemented")
+}
+func (UnimplementedDexServer) ListConnectors(context.Context, *ListConnectorReq) (*ListConnectorResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method ListConnectors not implemented")
+}
func (UnimplementedDexServer) GetVersion(context.Context, *VersionReq) (*VersionResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetVersion not implemented")
}
+func (UnimplementedDexServer) GetDiscovery(context.Context, *DiscoveryReq) (*DiscoveryResp, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method GetDiscovery not implemented")
+}
func (UnimplementedDexServer) ListRefresh(context.Context, *ListRefreshReq) (*ListRefreshResp, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListRefresh not implemented")
}
@@ -220,6 +382,7 @@ func (UnimplementedDexServer) VerifyPassword(context.Context, *VerifyPasswordReq
return nil, status.Errorf(codes.Unimplemented, "method VerifyPassword not implemented")
}
func (UnimplementedDexServer) mustEmbedUnimplementedDexServer() {}
+func (UnimplementedDexServer) testEmbeddedByValue() {}
// UnsafeDexServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to DexServer will
@@ -229,9 +392,34 @@ type UnsafeDexServer interface {
}
func RegisterDexServer(s grpc.ServiceRegistrar, srv DexServer) {
+ // If the following call pancis, it indicates UnimplementedDexServer was
+ // embedded by pointer and is nil. This will cause panics if an
+ // unimplemented method is ever invoked, so we test this at initialization
+ // time to prevent it from happening at runtime later due to I/O.
+ if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
+ t.testEmbeddedByValue()
+ }
s.RegisterService(&Dex_ServiceDesc, srv)
}
+func _Dex_GetClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(GetClientReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).GetClient(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_GetClient_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).GetClient(ctx, req.(*GetClientReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
func _Dex_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateClientReq)
if err := dec(in); err != nil {
@@ -242,7 +430,7 @@ func _Dex_CreateClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/CreateClient",
+ FullMethod: Dex_CreateClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).CreateClient(ctx, req.(*CreateClientReq))
@@ -260,7 +448,7 @@ func _Dex_UpdateClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/UpdateClient",
+ FullMethod: Dex_UpdateClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).UpdateClient(ctx, req.(*UpdateClientReq))
@@ -278,7 +466,7 @@ func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(in
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/DeleteClient",
+ FullMethod: Dex_DeleteClient_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).DeleteClient(ctx, req.(*DeleteClientReq))
@@ -286,6 +474,24 @@ func _Dex_DeleteClient_Handler(srv interface{}, ctx context.Context, dec func(in
return interceptor(ctx, in, info, handler)
}
+func _Dex_ListClients_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ListClientReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).ListClients(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_ListClients_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).ListClients(ctx, req.(*ListClientReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreatePasswordReq)
if err := dec(in); err != nil {
@@ -296,7 +502,7 @@ func _Dex_CreatePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/CreatePassword",
+ FullMethod: Dex_CreatePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).CreatePassword(ctx, req.(*CreatePasswordReq))
@@ -314,7 +520,7 @@ func _Dex_UpdatePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/UpdatePassword",
+ FullMethod: Dex_UpdatePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).UpdatePassword(ctx, req.(*UpdatePasswordReq))
@@ -332,7 +538,7 @@ func _Dex_DeletePassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/DeletePassword",
+ FullMethod: Dex_DeletePassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).DeletePassword(ctx, req.(*DeletePasswordReq))
@@ -350,7 +556,7 @@ func _Dex_ListPasswords_Handler(srv interface{}, ctx context.Context, dec func(i
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/ListPasswords",
+ FullMethod: Dex_ListPasswords_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).ListPasswords(ctx, req.(*ListPasswordReq))
@@ -358,6 +564,78 @@ func _Dex_ListPasswords_Handler(srv interface{}, ctx context.Context, dec func(i
return interceptor(ctx, in, info, handler)
}
+func _Dex_CreateConnector_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(CreateConnectorReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).CreateConnector(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_CreateConnector_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).CreateConnector(ctx, req.(*CreateConnectorReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Dex_UpdateConnector_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(UpdateConnectorReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).UpdateConnector(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_UpdateConnector_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).UpdateConnector(ctx, req.(*UpdateConnectorReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Dex_DeleteConnector_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DeleteConnectorReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).DeleteConnector(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_DeleteConnector_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).DeleteConnector(ctx, req.(*DeleteConnectorReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+func _Dex_ListConnectors_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(ListConnectorReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).ListConnectors(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_ListConnectors_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).ListConnectors(ctx, req.(*ListConnectorReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VersionReq)
if err := dec(in); err != nil {
@@ -368,7 +646,7 @@ func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(inte
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/GetVersion",
+ FullMethod: Dex_GetVersion_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).GetVersion(ctx, req.(*VersionReq))
@@ -376,6 +654,24 @@ func _Dex_GetVersion_Handler(srv interface{}, ctx context.Context, dec func(inte
return interceptor(ctx, in, info, handler)
}
+func _Dex_GetDiscovery_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DiscoveryReq)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(DexServer).GetDiscovery(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: Dex_GetDiscovery_FullMethodName,
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(DexServer).GetDiscovery(ctx, req.(*DiscoveryReq))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
func _Dex_ListRefresh_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ListRefreshReq)
if err := dec(in); err != nil {
@@ -386,7 +682,7 @@ func _Dex_ListRefresh_Handler(srv interface{}, ctx context.Context, dec func(int
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/ListRefresh",
+ FullMethod: Dex_ListRefresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).ListRefresh(ctx, req.(*ListRefreshReq))
@@ -404,7 +700,7 @@ func _Dex_RevokeRefresh_Handler(srv interface{}, ctx context.Context, dec func(i
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/RevokeRefresh",
+ FullMethod: Dex_RevokeRefresh_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).RevokeRefresh(ctx, req.(*RevokeRefreshReq))
@@ -422,7 +718,7 @@ func _Dex_VerifyPassword_Handler(srv interface{}, ctx context.Context, dec func(
}
info := &grpc.UnaryServerInfo{
Server: srv,
- FullMethod: "/api.Dex/VerifyPassword",
+ FullMethod: Dex_VerifyPassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(DexServer).VerifyPassword(ctx, req.(*VerifyPasswordReq))
@@ -437,6 +733,10 @@ var Dex_ServiceDesc = grpc.ServiceDesc{
ServiceName: "api.Dex",
HandlerType: (*DexServer)(nil),
Methods: []grpc.MethodDesc{
+ {
+ MethodName: "GetClient",
+ Handler: _Dex_GetClient_Handler,
+ },
{
MethodName: "CreateClient",
Handler: _Dex_CreateClient_Handler,
@@ -449,6 +749,10 @@ var Dex_ServiceDesc = grpc.ServiceDesc{
MethodName: "DeleteClient",
Handler: _Dex_DeleteClient_Handler,
},
+ {
+ MethodName: "ListClients",
+ Handler: _Dex_ListClients_Handler,
+ },
{
MethodName: "CreatePassword",
Handler: _Dex_CreatePassword_Handler,
@@ -465,10 +769,30 @@ var Dex_ServiceDesc = grpc.ServiceDesc{
MethodName: "ListPasswords",
Handler: _Dex_ListPasswords_Handler,
},
+ {
+ MethodName: "CreateConnector",
+ Handler: _Dex_CreateConnector_Handler,
+ },
+ {
+ MethodName: "UpdateConnector",
+ Handler: _Dex_UpdateConnector_Handler,
+ },
+ {
+ MethodName: "DeleteConnector",
+ Handler: _Dex_DeleteConnector_Handler,
+ },
+ {
+ MethodName: "ListConnectors",
+ Handler: _Dex_ListConnectors_Handler,
+ },
{
MethodName: "GetVersion",
Handler: _Dex_GetVersion_Handler,
},
+ {
+ MethodName: "GetDiscovery",
+ Handler: _Dex_GetDiscovery_Handler,
+ },
{
MethodName: "ListRefresh",
Handler: _Dex_ListRefresh_Handler,
diff --git a/api/v2/go.mod b/api/v2/go.mod
index dc78ec4d96..88af18801d 100644
--- a/api/v2/go.mod
+++ b/api/v2/go.mod
@@ -1,16 +1,15 @@
module github.com/dexidp/dex/api/v2
-go 1.17
+go 1.24.0
require (
- google.golang.org/grpc v1.47.0
- google.golang.org/protobuf v1.28.1
+ google.golang.org/grpc v1.77.0
+ google.golang.org/protobuf v1.36.11
)
require (
- github.com/golang/protobuf v1.5.2 // indirect
- golang.org/x/net v0.0.0-20220607020251-c690dde0001d // indirect
- golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
- golang.org/x/text v0.3.7 // indirect
- google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 // indirect
+ golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
+ golang.org/x/sys v0.37.0 // indirect
+ golang.org/x/text v0.30.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
)
diff --git a/api/v2/go.sum b/api/v2/go.sum
index 59d53d2d63..a2371605fd 100644
--- a/api/v2/go.sum
+++ b/api/v2/go.sum
@@ -1,142 +1,36 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d h1:4SFsTMi4UahlKoloni7L4eYzhFRifURQLw+yv0QDCx8=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8 h1:qRu95HZ148xXw+XeZ3dvqe85PxH4X8+jIo0iRPKcEnM=
-google.golang.org/genproto v0.0.0-20220602131408-e326c6e8e9c8/go.mod h1:yKyY4AMRwFiC8yMMNaMi+RkCnjZJt9LoWuvhXjMs+To=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
+go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
+go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
+golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
+golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
+golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
+golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
+golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
+google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
+google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
diff --git a/cmd/dex/config.go b/cmd/dex/config.go
index 7bb7fbb780..c76ff03041 100644
--- a/cmd/dex/config.go
+++ b/cmd/dex/config.go
@@ -4,13 +4,15 @@ import (
"encoding/base64"
"encoding/json"
"fmt"
+ "log/slog"
+ "net/http"
+ "net/netip"
"os"
- "strconv"
"strings"
"golang.org/x/crypto/bcrypt"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/featureflags"
"github.com/dexidp/dex/server"
"github.com/dexidp/dex/storage"
"github.com/dexidp/dex/storage/ent"
@@ -64,10 +66,16 @@ func (c Config) Validate() error {
{c.Web.HTTP == "" && c.Web.HTTPS == "", "must supply a HTTP/HTTPS address to listen on"},
{c.Web.HTTPS != "" && c.Web.TLSCert == "", "no cert specified for HTTPS"},
{c.Web.HTTPS != "" && c.Web.TLSKey == "", "no private key specified for HTTPS"},
+ {c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion != "1.2" && c.Web.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+ {c.Web.TLSMaxVersion != "" && c.Web.TLSMaxVersion != "1.2" && c.Web.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+ {c.Web.TLSMaxVersion != "" && c.Web.TLSMinVersion != "" && c.Web.TLSMinVersion > c.Web.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
{c.GRPC.TLSCert != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
{c.GRPC.TLSKey != "" && c.GRPC.Addr == "", "no address specified for gRPC"},
{(c.GRPC.TLSCert == "") != (c.GRPC.TLSKey == ""), "must specific both a gRPC TLS cert and key"},
{c.GRPC.TLSCert == "" && c.GRPC.TLSClientCA != "", "cannot specify gRPC TLS client CA without a gRPC TLS cert"},
+ {c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion != "1.2" && c.GRPC.TLSMinVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+ {c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMaxVersion != "1.2" && c.GRPC.TLSMaxVersion != "1.3", "supported TLS versions are: 1.2, 1.3"},
+ {c.GRPC.TLSMaxVersion != "" && c.GRPC.TLSMinVersion != "" && c.GRPC.TLSMinVersion > c.GRPC.TLSMaxVersion, "TLSMinVersion greater than TLSMaxVersion"},
}
var checkErrors []string
@@ -87,19 +95,23 @@ type password storage.Password
func (p *password) UnmarshalJSON(b []byte) error {
var data struct {
- Email string `json:"email"`
- Username string `json:"username"`
- UserID string `json:"userID"`
- Hash string `json:"hash"`
- HashFromEnv string `json:"hashFromEnv"`
+ Email string `json:"email"`
+ Username string `json:"username"`
+ PreferredUsername string `json:"preferredUsername"`
+ UserID string `json:"userID"`
+ Hash string `json:"hash"`
+ HashFromEnv string `json:"hashFromEnv"`
+ Groups []string `json:"groups"`
}
if err := json.Unmarshal(b, &data); err != nil {
return err
}
*p = password(storage.Password{
- Email: data.Email,
- Username: data.Username,
- UserID: data.UserID,
+ Email: data.Email,
+ Username: data.Username,
+ PreferredUsername: data.PreferredUsername,
+ UserID: data.UserID,
+ Groups: data.Groups,
})
if len(data.Hash) == 0 && len(data.HashFromEnv) > 0 {
data.Hash = os.Getenv(data.HashFromEnv)
@@ -129,6 +141,10 @@ func (p *password) UnmarshalJSON(b []byte) error {
// OAuth2 describes enabled OAuth2 extensions.
type OAuth2 struct {
+ // list of allowed grant types,
+ // defaults to all supported types
+ GrantTypes []string `json:"grantTypes"`
+
ResponseTypes []string `json:"responseTypes"`
// If specified, do not prompt the user to approve client authorization. The
// act of logging in implies authorization.
@@ -141,11 +157,84 @@ type OAuth2 struct {
// Web is the config format for the HTTP server.
type Web struct {
- HTTP string `json:"http"`
- HTTPS string `json:"https"`
- TLSCert string `json:"tlsCert"`
- TLSKey string `json:"tlsKey"`
- AllowedOrigins []string `json:"allowedOrigins"`
+ HTTP string `json:"http"`
+ HTTPS string `json:"https"`
+ Headers Headers `json:"headers"`
+ TLSCert string `json:"tlsCert"`
+ TLSKey string `json:"tlsKey"`
+ TLSMinVersion string `json:"tlsMinVersion"`
+ TLSMaxVersion string `json:"tlsMaxVersion"`
+ AllowedOrigins []string `json:"allowedOrigins"`
+ AllowedHeaders []string `json:"allowedHeaders"`
+ ClientRemoteIP ClientRemoteIP `json:"clientRemoteIP"`
+}
+
+type ClientRemoteIP struct {
+ Header string `json:"header"`
+ TrustedProxies []string `json:"trustedProxies"`
+}
+
+func (cr *ClientRemoteIP) ParseTrustedProxies() ([]netip.Prefix, error) {
+ if cr == nil {
+ return nil, nil
+ }
+
+ trusted := make([]netip.Prefix, 0, len(cr.TrustedProxies))
+ for _, cidr := range cr.TrustedProxies {
+ ipNet, err := netip.ParsePrefix(cidr)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse CIDR %q: %v", cidr, err)
+ }
+ trusted = append(trusted, ipNet)
+ }
+
+ return trusted, nil
+}
+
+type Headers struct {
+ // Set the Content-Security-Policy header to HTTP responses.
+ // Unset if blank.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+ ContentSecurityPolicy string `json:"Content-Security-Policy"`
+ // Set the X-Frame-Options header to HTTP responses.
+ // Unset if blank. Accepted values are deny and sameorigin.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
+ XFrameOptions string `json:"X-Frame-Options"`
+ // Set the X-Content-Type-Options header to HTTP responses.
+ // Unset if blank. Accepted value is nosniff.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
+ XContentTypeOptions string `json:"X-Content-Type-Options"`
+ // Set the X-XSS-Protection header to all responses.
+ // Unset if blank.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection
+ XXSSProtection string `json:"X-XSS-Protection"`
+ // Set the Strict-Transport-Security header to HTTP responses.
+ // Unset if blank.
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
+ StrictTransportSecurity string `json:"Strict-Transport-Security"`
+}
+
+func (h *Headers) ToHTTPHeader() http.Header {
+ if h == nil {
+ return make(map[string][]string)
+ }
+ header := make(map[string][]string)
+ if h.ContentSecurityPolicy != "" {
+ header["Content-Security-Policy"] = []string{h.ContentSecurityPolicy}
+ }
+ if h.XFrameOptions != "" {
+ header["X-Frame-Options"] = []string{h.XFrameOptions}
+ }
+ if h.XContentTypeOptions != "" {
+ header["X-Content-Type-Options"] = []string{h.XContentTypeOptions}
+ }
+ if h.XXSSProtection != "" {
+ header["X-XSS-Protection"] = []string{h.XXSSProtection}
+ }
+ if h.StrictTransportSecurity != "" {
+ header["Strict-Transport-Security"] = []string{h.StrictTransportSecurity}
+ }
+ return header
}
// Telemetry is the config format for telemetry including the HTTP server config.
@@ -158,11 +247,13 @@ type Telemetry struct {
// GRPC is the config for the gRPC API.
type GRPC struct {
// The port to listen on.
- Addr string `json:"addr"`
- TLSCert string `json:"tlsCert"`
- TLSKey string `json:"tlsKey"`
- TLSClientCA string `json:"tlsClientCA"`
- Reflection bool `json:"reflection"`
+ Addr string `json:"addr"`
+ TLSCert string `json:"tlsCert"`
+ TLSKey string `json:"tlsKey"`
+ TLSClientCA string `json:"tlsClientCA"`
+ TLSMinVersion string `json:"tlsMinVersion"`
+ TLSMaxVersion string `json:"tlsMaxVersion"`
+ Reflection bool `json:"reflection"`
}
// Storage holds app's storage configuration.
@@ -173,7 +264,7 @@ type Storage struct {
// StorageConfig is a configuration that can create a storage.
type StorageConfig interface {
- Open(logger log.Logger) (storage.Storage, error)
+ Open(logger *slog.Logger) (storage.Storage, error)
}
var (
@@ -188,13 +279,32 @@ var (
_ StorageConfig = (*ent.MySQL)(nil)
)
-func getORMBasedSQLStorage(normal, entBased StorageConfig) func() StorageConfig {
+func getORMBasedSQLStorage(normal, entBased func() StorageConfig) func() StorageConfig {
return func() StorageConfig {
- switch os.Getenv("DEX_ENT_ENABLED") {
- case "true", "yes":
- return entBased
- default:
- return normal
+ if featureflags.EntEnabled.Enabled() {
+ return entBased()
+ }
+ return normal()
+ }
+}
+
+// Recursively expand environment variables in the map to avoid
+// issues with JSON special characters and escapes
+func expandEnvInMap(m map[string]interface{}) {
+ for k, v := range m {
+ switch vt := v.(type) {
+ case string:
+ m[k] = os.ExpandEnv(vt)
+ case map[string]interface{}:
+ expandEnvInMap(vt)
+ case []interface{}:
+ for i, item := range vt {
+ if itemMap, ok := item.(map[string]interface{}); ok {
+ expandEnvInMap(itemMap)
+ } else if itemString, ok := item.(string); ok {
+ vt[i] = os.ExpandEnv(itemString)
+ }
+ }
}
}
}
@@ -203,22 +313,9 @@ var storages = map[string]func() StorageConfig{
"etcd": func() StorageConfig { return new(etcd.Etcd) },
"kubernetes": func() StorageConfig { return new(kubernetes.Config) },
"memory": func() StorageConfig { return new(memory.Config) },
- "sqlite3": getORMBasedSQLStorage(&sql.SQLite3{}, &ent.SQLite3{}),
- "postgres": getORMBasedSQLStorage(&sql.Postgres{}, &ent.Postgres{}),
- "mysql": getORMBasedSQLStorage(&sql.MySQL{}, &ent.MySQL{}),
-}
-
-// isExpandEnvEnabled returns if os.ExpandEnv should be used for each storage and connector config.
-// Disabling this feature avoids surprises e.g. if the LDAP bind password contains a dollar character.
-// Returns false if the env variable "DEX_EXPAND_ENV" is a falsy string, e.g. "false".
-// Returns true if the env variable is unset or a truthy string, e.g. "true", or can't be parsed as bool.
-func isExpandEnvEnabled() bool {
- enabled, err := strconv.ParseBool(os.Getenv("DEX_EXPAND_ENV"))
- if err != nil {
- // Unset, empty string or can't be parsed as bool: Default = true.
- return true
- }
- return enabled
+ "sqlite3": getORMBasedSQLStorage(func() StorageConfig { return new(sql.SQLite3) }, func() StorageConfig { return new(ent.SQLite3) }),
+ "postgres": getORMBasedSQLStorage(func() StorageConfig { return new(sql.Postgres) }, func() StorageConfig { return new(ent.Postgres) }),
+ "mysql": getORMBasedSQLStorage(func() StorageConfig { return new(sql.MySQL) }, func() StorageConfig { return new(ent.MySQL) }),
}
// UnmarshalJSON allows Storage to implement the unmarshaler interface to
@@ -239,10 +336,25 @@ func (s *Storage) UnmarshalJSON(b []byte) error {
storageConfig := f()
if len(store.Config) != 0 {
data := []byte(store.Config)
- if isExpandEnvEnabled() {
- // Caution, we're expanding in the raw JSON/YAML source. This may not be what the admin expects.
- data = []byte(os.ExpandEnv(string(store.Config)))
+ if featureflags.ExpandEnv.Enabled() {
+ var rawMap map[string]interface{}
+ if err := json.Unmarshal(store.Config, &rawMap); err != nil {
+ return fmt.Errorf("unmarshal config for env expansion: %v", err)
+ }
+
+ // Recursively expand environment variables in the map to avoid
+ // issues with JSON special characters and escapes
+ expandEnvInMap(rawMap)
+
+ // Marshal the expanded map back to JSON
+ expandedData, err := json.Marshal(rawMap)
+ if err != nil {
+ return fmt.Errorf("marshal expanded config: %v", err)
+ }
+
+ data = expandedData
}
+
if err := json.Unmarshal(data, storageConfig); err != nil {
return fmt.Errorf("parse storage config: %v", err)
}
@@ -285,14 +397,30 @@ func (c *Connector) UnmarshalJSON(b []byte) error {
connConfig := f()
if len(conn.Config) != 0 {
data := []byte(conn.Config)
- if isExpandEnvEnabled() {
- // Caution, we're expanding in the raw JSON/YAML source. This may not be what the admin expects.
- data = []byte(os.ExpandEnv(string(conn.Config)))
+ if featureflags.ExpandEnv.Enabled() {
+ var rawMap map[string]interface{}
+ if err := json.Unmarshal(conn.Config, &rawMap); err != nil {
+ return fmt.Errorf("unmarshal config for env expansion: %v", err)
+ }
+
+ // Recursively expand environment variables in the map to avoid
+ // issues with JSON special characters and escapes
+ expandEnvInMap(rawMap)
+
+ // Marshal the expanded map back to JSON
+ expandedData, err := json.Marshal(rawMap)
+ if err != nil {
+ return fmt.Errorf("marshal expanded config: %v", err)
+ }
+
+ data = expandedData
}
+
if err := json.Unmarshal(data, connConfig); err != nil {
return fmt.Errorf("parse connector config: %v", err)
}
}
+
*c = Connector{
Type: conn.Type,
Name: conn.Name,
@@ -338,7 +466,7 @@ type Expiry struct {
// Logger holds configuration required to customize logging for dex.
type Logger struct {
// Level sets logging level severity.
- Level string `json:"level"`
+ Level slog.Level `json:"level"`
// Format specifies the format to be used for logging.
Format string `json:"format"`
diff --git a/cmd/dex/config_test.go b/cmd/dex/config_test.go
index 8ee02d5aa2..30d50c6cb7 100644
--- a/cmd/dex/config_test.go
+++ b/cmd/dex/config_test.go
@@ -1,6 +1,7 @@
package main
import (
+ "log/slog"
"os"
"testing"
@@ -37,6 +38,7 @@ func TestValidConfiguration(t *testing.T) {
},
},
}
+
if err := configuration.Validate(); err != nil {
t.Fatalf("this configuration should have been valid: %v", err)
}
@@ -71,7 +73,11 @@ storage:
connMaxLifetime: 30
connectionTimeout: 3
web:
- http: 127.0.0.1:5556
+ https: 127.0.0.1:5556
+ tlsMinVersion: 1.3
+ tlsMaxVersion: 1.2
+ headers:
+ Strict-Transport-Security: "max-age=31536000; includeSubDomains"
frontend:
dir: ./web
@@ -87,6 +93,9 @@ staticClients:
oauth2:
alwaysShowLoginScreen: true
+ grantTypes:
+ - refresh_token
+ - "urn:ietf:params:oauth:grant-type:token-exchange"
connectors:
- type: mockCallback
@@ -107,6 +116,10 @@ staticPasswords:
# bcrypt hash of the string "password"
hash: "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"
username: "admin"
+ preferredUsername: "admin-public"
+ groups:
+ - "team-a"
+ - "team-a/admins"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
- email: "foo@example.com"
# base64'd value of the same bcrypt hash above. We want to be able to parse both of these
@@ -123,6 +136,10 @@ expiry:
logger:
level: "debug"
format: "json"
+
+additionalFeatures: [
+ "ConnectorsCRUD"
+]
`)
want := Config{
@@ -141,7 +158,12 @@ logger:
},
},
Web: Web{
- HTTP: "127.0.0.1:5556",
+ HTTPS: "127.0.0.1:5556",
+ TLSMinVersion: "1.3",
+ TLSMaxVersion: "1.2",
+ Headers: Headers{
+ StrictTransportSecurity: "max-age=31536000; includeSubDomains",
+ },
},
Frontend: server.WebConfig{
Dir: "./web",
@@ -161,6 +183,10 @@ logger:
},
OAuth2: OAuth2{
AlwaysShowLoginScreen: true,
+ GrantTypes: []string{
+ "refresh_token",
+ "urn:ietf:params:oauth:grant-type:token-exchange",
+ },
},
StaticConnectors: []Connector{
{
@@ -184,10 +210,15 @@ logger:
EnablePasswordDB: true,
StaticPasswords: []password{
{
- Email: "admin@example.com",
- Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
- Username: "admin",
- UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466",
+ Email: "admin@example.com",
+ Hash: []byte("$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy"),
+ Username: "admin",
+ PreferredUsername: "admin-public",
+ UserID: "08a8684b-db88-4b73-90a9-3cd1661f5466",
+ Groups: []string{
+ "team-a",
+ "team-a/admins",
+ },
},
{
Email: "foo@example.com",
@@ -203,7 +234,7 @@ logger:
DeviceRequests: "10m",
},
Logger: Logger{
- Level: "debug",
+ Level: slog.LevelDebug,
Format: "json",
},
}
@@ -212,6 +243,7 @@ logger:
if err := yaml.Unmarshal(rawConfig, &c); err != nil {
t.Fatalf("failed to decode config: %v", err)
}
+
if diff := pretty.Compare(c, want); diff != "" {
t.Errorf("got!=want: %s", diff)
}
@@ -250,7 +282,8 @@ func checkUnmarshalConfigWithEnv(t *testing.T, dexExpandEnv string, wantExpandEn
os.Setenv("DEX_FOO_USER_PASSWORD", "$2a$10$33EMT0cVYVlPy6WAMCLsceLYjWhuHpbz5yuZxu/GAFj03J9Lytjuy")
// For os.ExpandEnv ($VAR -> value_of_VAR):
os.Setenv("DEX_FOO_POSTGRES_HOST", "10.0.0.1")
- os.Setenv("DEX_FOO_OIDC_CLIENT_SECRET", "bar")
+ os.Setenv("DEX_FOO_POSTGRES_PASSWORD", `psql"test\pass`)
+ os.Setenv("DEX_FOO_OIDC_CLIENT_SECRET", `abc"def\ghi`)
if dexExpandEnv != "UNSET" {
os.Setenv("DEX_EXPAND_ENV", dexExpandEnv)
} else {
@@ -265,6 +298,7 @@ storage:
# Env variables are expanded in raw YAML source.
# Single quotes work fine, as long as the env variable doesn't contain any.
host: '$DEX_FOO_POSTGRES_HOST'
+ password: '$DEX_FOO_POSTGRES_PASSWORD'
port: 65432
maxOpenConns: 5
maxIdleConns: 3
@@ -327,10 +361,12 @@ logger:
// This is not a valid hostname. It's only used to check whether os.ExpandEnv was applied or not.
wantPostgresHost := "$DEX_FOO_POSTGRES_HOST"
+ wantPostgresPassword := "$DEX_FOO_POSTGRES_PASSWORD"
wantOidcClientSecret := "$DEX_FOO_OIDC_CLIENT_SECRET"
if wantExpandEnv {
wantPostgresHost = "10.0.0.1"
- wantOidcClientSecret = "bar"
+ wantPostgresPassword = `psql"test\pass`
+ wantOidcClientSecret = `abc"def\ghi`
}
want := Config{
@@ -340,6 +376,7 @@ logger:
Config: &sql.Postgres{
NetworkDB: sql.NetworkDB{
Host: wantPostgresHost,
+ Password: wantPostgresPassword,
Port: 65432,
MaxOpenConns: 5,
MaxIdleConns: 3,
@@ -410,7 +447,7 @@ logger:
AuthRequests: "25h",
},
Logger: Logger{
- Level: "debug",
+ Level: slog.LevelDebug,
Format: "json",
},
}
@@ -419,6 +456,7 @@ logger:
if err := yaml.Unmarshal(rawConfig, &c); err != nil {
t.Fatalf("failed to decode config: %v", err)
}
+
if diff := pretty.Compare(c, want); diff != "" {
t.Errorf("got!=want: %s", diff)
}
diff --git a/cmd/dex/logger.go b/cmd/dex/logger.go
new file mode 100644
index 0000000000..c1fe6b4a88
--- /dev/null
+++ b/cmd/dex/logger.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "log/slog"
+ "os"
+ "strings"
+
+ "github.com/dexidp/dex/server"
+)
+
+var logFormats = []string{"json", "text"}
+
+func newLogger(level slog.Level, format string) (*slog.Logger, error) {
+ var handler slog.Handler
+ switch strings.ToLower(format) {
+ case "", "text":
+ handler = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{
+ Level: level,
+ })
+ case "json":
+ handler = slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{
+ Level: level,
+ })
+ default:
+ return nil, fmt.Errorf("log format is not one of the supported values (%s): %s", strings.Join(logFormats, ", "), format)
+ }
+
+ return slog.New(newRequestContextHandler(handler)), nil
+}
+
+var _ slog.Handler = requestContextHandler{}
+
+type requestContextHandler struct {
+ handler slog.Handler
+}
+
+func newRequestContextHandler(handler slog.Handler) slog.Handler {
+ return requestContextHandler{
+ handler: handler,
+ }
+}
+
+func (h requestContextHandler) Enabled(ctx context.Context, level slog.Level) bool {
+ return h.handler.Enabled(ctx, level)
+}
+
+func (h requestContextHandler) Handle(ctx context.Context, record slog.Record) error {
+ if v, ok := ctx.Value(server.RequestKeyRemoteIP).(string); ok {
+ record.AddAttrs(slog.String(string(server.RequestKeyRemoteIP), v))
+ }
+
+ if v, ok := ctx.Value(server.RequestKeyRequestID).(string); ok {
+ record.AddAttrs(slog.String(string(server.RequestKeyRequestID), v))
+ }
+
+ return h.handler.Handle(ctx, record)
+}
+
+func (h requestContextHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ return requestContextHandler{h.handler.WithAttrs(attrs)}
+}
+
+func (h requestContextHandler) WithGroup(name string) slog.Handler {
+ return requestContextHandler{h.handler.WithGroup(name)}
+}
diff --git a/cmd/dex/serve.go b/cmd/dex/serve.go
index c8fb95eb16..ac715e606f 100644
--- a/cmd/dex/serve.go
+++ b/cmd/dex/serve.go
@@ -6,32 +6,36 @@ import (
"crypto/x509"
"errors"
"fmt"
+ "log/slog"
"net"
"net/http"
"net/http/pprof"
"os"
+ "os/signal"
+ "path/filepath"
"runtime"
"strings"
+ "sync/atomic"
"syscall"
"time"
gosundheit "github.com/AppsFlyer/go-sundheit"
"github.com/AppsFlyer/go-sundheit/checks"
gosundheithttp "github.com/AppsFlyer/go-sundheit/http"
+ "github.com/fsnotify/fsnotify"
"github.com/ghodss/yaml"
grpcprometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/oklog/run"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
- "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/reflection"
"github.com/dexidp/dex/api/v2"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/featureflags"
"github.com/dexidp/dex/server"
"github.com/dexidp/dex/storage"
)
@@ -47,6 +51,15 @@ type serveOptions struct {
grpcAddr string
}
+var buildInfo = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "build_info",
+ Namespace: "dex",
+ Help: "A metric with a constant '1' value labeled by version from which Dex was built.",
+ },
+ []string{"version", "go_version", "platform"},
+)
+
func commandServe() *cobra.Command {
options := serveOptions{}
@@ -94,24 +107,30 @@ func runServe(options serveOptions) error {
return fmt.Errorf("invalid config: %v", err)
}
- logger.Infof(
- "Dex Version: %s, Go Version: %s, Go OS/ARCH: %s %s",
- version,
- runtime.Version(),
- runtime.GOOS,
- runtime.GOARCH,
+ logger.Info(
+ "Version info",
+ "dex_version", version,
+ slog.Group("go",
+ "version", runtime.Version(),
+ "os", runtime.GOOS,
+ "arch", runtime.GOARCH,
+ ),
)
- if c.Logger.Level != "" {
- logger.Infof("config using log level: %s", c.Logger.Level)
+ if c.Logger.Level != slog.LevelInfo {
+ logger.Info("config using log level", "level", c.Logger.Level)
}
if err := c.Validate(); err != nil {
return err
}
- logger.Infof("config issuer: %s", c.Issuer)
+ logger.Info("config issuer", "issuer", c.Issuer)
prometheusRegistry := prometheus.NewRegistry()
+
+ prometheusRegistry.MustRegister(buildInfo)
+ recordBuildInfo()
+
err = prometheusRegistry.Register(collectors.NewGoCollector())
if err != nil {
return fmt.Errorf("failed to register Go runtime metrics: %v", err)
@@ -141,34 +160,33 @@ func runServe(options serveOptions) error {
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
}
+ allowedTLSVersions := map[string]int{
+ "1.2": tls.VersionTLS12,
+ "1.3": tls.VersionTLS13,
+ }
+
if c.GRPC.TLSCert != "" {
- // Parse certificates from certificate file and key file for server.
- cert, err := tls.LoadX509KeyPair(c.GRPC.TLSCert, c.GRPC.TLSKey)
- if err != nil {
- return fmt.Errorf("invalid config: error parsing gRPC certificate file: %v", err)
+ tlsMinVersion := tls.VersionTLS12
+ if c.GRPC.TLSMinVersion != "" {
+ tlsMinVersion = allowedTLSVersions[c.GRPC.TLSMinVersion]
}
-
- tlsConfig := tls.Config{
- Certificates: []tls.Certificate{cert},
- MinVersion: tls.VersionTLS12,
+ tlsMaxVersion := 0 // default for max is whatever Go defaults to
+ if c.GRPC.TLSMaxVersion != "" {
+ tlsMaxVersion = allowedTLSVersions[c.GRPC.TLSMaxVersion]
+ }
+ baseTLSConfig := &tls.Config{
+ MinVersion: uint16(tlsMinVersion),
+ MaxVersion: uint16(tlsMaxVersion),
CipherSuites: allowedTLSCiphers,
PreferServerCipherSuites: true,
}
- if c.GRPC.TLSClientCA != "" {
- // Parse certificates from client CA file to a new CertPool.
- cPool := x509.NewCertPool()
- clientCert, err := os.ReadFile(c.GRPC.TLSClientCA)
- if err != nil {
- return fmt.Errorf("invalid config: reading from client CA file: %v", err)
- }
- if !cPool.AppendCertsFromPEM(clientCert) {
- return errors.New("invalid config: failed to parse client CA")
- }
-
- tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
- tlsConfig.ClientCAs = cPool
+ tlsConfig, err := newTLSReloader(logger, c.GRPC.TLSCert, c.GRPC.TLSKey, c.GRPC.TLSClientCA, baseTLSConfig)
+ if err != nil {
+ return fmt.Errorf("invalid config: get gRPC TLS: %v", err)
+ }
+ if c.GRPC.TLSClientCA != "" {
// Only add metrics if client auth is enabled
grpcOptions = append(grpcOptions,
grpc.StreamInterceptor(grpcMetrics.StreamServerInterceptor()),
@@ -176,7 +194,7 @@ func runServe(options serveOptions) error {
)
}
- grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(&tlsConfig)))
+ grpcOptions = append(grpcOptions, grpc.Creds(credentials.NewTLS(tlsConfig)))
}
s, err := c.Storage.Config.Open(logger)
@@ -185,7 +203,7 @@ func runServe(options serveOptions) error {
}
defer s.Close()
- logger.Infof("config storage: %s", c.Storage.Type)
+ logger.Info("config storage", "storage_type", c.Storage.Type)
if len(c.StaticClients) > 0 {
for i, client := range c.StaticClients {
@@ -210,7 +228,7 @@ func runServe(options serveOptions) error {
}
c.StaticClients[i].Secret = os.Getenv(client.SecretEnv)
}
- logger.Infof("config static client: %s", client.Name)
+ logger.Info("config static client", "client_name", client.Name)
}
s = storage.WithStaticClients(s, c.StaticClients)
}
@@ -230,7 +248,7 @@ func runServe(options serveOptions) error {
if c.Config == nil {
return fmt.Errorf("invalid config: no config field for connector %q", c.ID)
}
- logger.Infof("config connector: %s", c.ID)
+ logger.Info("config connector", "connector_id", c.ID)
// convert to a storage connector object
conn, err := ToStorageConnector(c)
@@ -246,22 +264,25 @@ func runServe(options serveOptions) error {
Name: "Email",
Type: server.LocalConnector,
})
- logger.Infof("config connector: local passwords enabled")
+ logger.Info("config connector: local passwords enabled")
}
s = storage.WithStaticConnectors(s, storageConnectors)
if len(c.OAuth2.ResponseTypes) > 0 {
- logger.Infof("config response types accepted: %s", c.OAuth2.ResponseTypes)
+ logger.Info("config response types accepted", "response_types", c.OAuth2.ResponseTypes)
}
if c.OAuth2.SkipApprovalScreen {
- logger.Infof("config skipping approval screen")
+ logger.Info("config skipping approval screen")
}
if c.OAuth2.PasswordConnector != "" {
- logger.Infof("config using password grant connector: %s", c.OAuth2.PasswordConnector)
+ logger.Info("config using password grant connector", "password_connector", c.OAuth2.PasswordConnector)
}
if len(c.Web.AllowedOrigins) > 0 {
- logger.Infof("config allowed origins: %s", c.Web.AllowedOrigins)
+ logger.Info("config allowed origins", "origins", c.Web.AllowedOrigins)
+ }
+ if featureflags.ContinueOnConnectorFailure.Enabled() {
+ logger.Info("continue on connector failure feature flag enabled")
}
// explicitly convert to UTC.
@@ -270,25 +291,29 @@ func runServe(options serveOptions) error {
healthChecker := gosundheit.New()
serverConfig := server.Config{
- SupportedResponseTypes: c.OAuth2.ResponseTypes,
- SkipApprovalScreen: c.OAuth2.SkipApprovalScreen,
- AlwaysShowLoginScreen: c.OAuth2.AlwaysShowLoginScreen,
- PasswordConnector: c.OAuth2.PasswordConnector,
- AllowedOrigins: c.Web.AllowedOrigins,
- Issuer: c.Issuer,
- Storage: s,
- Web: c.Frontend,
- Logger: logger,
- Now: now,
- PrometheusRegistry: prometheusRegistry,
- HealthChecker: healthChecker,
+ AllowedGrantTypes: c.OAuth2.GrantTypes,
+ SupportedResponseTypes: c.OAuth2.ResponseTypes,
+ SkipApprovalScreen: c.OAuth2.SkipApprovalScreen,
+ AlwaysShowLoginScreen: c.OAuth2.AlwaysShowLoginScreen,
+ PasswordConnector: c.OAuth2.PasswordConnector,
+ Headers: c.Web.Headers.ToHTTPHeader(),
+ AllowedOrigins: c.Web.AllowedOrigins,
+ AllowedHeaders: c.Web.AllowedHeaders,
+ Issuer: c.Issuer,
+ Storage: s,
+ Web: c.Frontend,
+ Logger: logger,
+ Now: now,
+ PrometheusRegistry: prometheusRegistry,
+ HealthChecker: healthChecker,
+ ContinueOnConnectorFailure: featureflags.ContinueOnConnectorFailure.Enabled(),
}
if c.Expiry.SigningKeys != "" {
signingKeys, err := time.ParseDuration(c.Expiry.SigningKeys)
if err != nil {
return fmt.Errorf("invalid config value %q for signing keys expiry: %v", c.Expiry.SigningKeys, err)
}
- logger.Infof("config signing keys expire after: %v", signingKeys)
+ logger.Info("config signing keys", "expire_after", signingKeys)
serverConfig.RotateKeysAfter = signingKeys
}
if c.Expiry.IDTokens != "" {
@@ -296,7 +321,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for id token expiry: %v", c.Expiry.IDTokens, err)
}
- logger.Infof("config id tokens valid for: %v", idTokens)
+ logger.Info("config id tokens", "valid_for", idTokens)
serverConfig.IDTokensValidFor = idTokens
}
if c.Expiry.AuthRequests != "" {
@@ -304,7 +329,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for auth request expiry: %v", c.Expiry.AuthRequests, err)
}
- logger.Infof("config auth requests valid for: %v", authRequests)
+ logger.Info("config auth requests", "valid_for", authRequests)
serverConfig.AuthRequestsValidFor = authRequests
}
if c.Expiry.DeviceRequests != "" {
@@ -312,7 +337,7 @@ func runServe(options serveOptions) error {
if err != nil {
return fmt.Errorf("invalid config value %q for device request expiry: %v", c.Expiry.AuthRequests, err)
}
- logger.Infof("config device requests valid for: %v", deviceRequests)
+ logger.Info("config device requests", "valid_for", deviceRequests)
serverConfig.DeviceRequestsValidFor = deviceRequests
}
refreshTokenPolicy, err := server.NewRefreshTokenPolicy(
@@ -327,6 +352,13 @@ func runServe(options serveOptions) error {
}
serverConfig.RefreshTokenPolicy = refreshTokenPolicy
+
+ serverConfig.RealIPHeader = c.Web.ClientRemoteIP.Header
+ serverConfig.TrustedRealIPCIDRs, err = c.Web.ClientRemoteIP.ParseTrustedProxies()
+ if err != nil {
+ return fmt.Errorf("failed to parse client remote IP settings: %v", err)
+ }
+
serv, err := server.NewServer(context.Background(), serverConfig)
if err != nil {
return fmt.Errorf("failed to initialize server: %v", err)
@@ -362,7 +394,7 @@ func runServe(options serveOptions) error {
if c.Telemetry.HTTP != "" {
const name = "telemetry"
- logger.Infof("listening (%s) on %s", name, c.Telemetry.HTTP)
+ logger.Info("listening on", "server", name, "address", c.Telemetry.HTTP)
l, err := net.Listen("tcp", c.Telemetry.HTTP)
if err != nil {
@@ -384,9 +416,9 @@ func runServe(options serveOptions) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
- logger.Debugf("starting graceful shutdown (%s)", name)
+ logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
- logger.Errorf("graceful shutdown (%s): %v", name, err)
+ logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
@@ -395,7 +427,7 @@ func runServe(options serveOptions) error {
if c.Web.HTTP != "" {
const name = "http"
- logger.Infof("listening (%s) on %s", name, c.Web.HTTP)
+ logger.Info("listening on", "server", name, "address", c.Web.HTTP)
l, err := net.Listen("tcp", c.Web.HTTP)
if err != nil {
@@ -413,9 +445,9 @@ func runServe(options serveOptions) error {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
- logger.Debugf("starting graceful shutdown (%s)", name)
+ logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
- logger.Errorf("graceful shutdown (%s): %v", name, err)
+ logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
@@ -424,39 +456,56 @@ func runServe(options serveOptions) error {
if c.Web.HTTPS != "" {
const name = "https"
- logger.Infof("listening (%s) on %s", name, c.Web.HTTPS)
+ logger.Info("listening on", "server", name, "address", c.Web.HTTPS)
l, err := net.Listen("tcp", c.Web.HTTPS)
if err != nil {
return fmt.Errorf("listening (%s) on %s: %v", name, c.Web.HTTPS, err)
}
+ tlsMinVersion := tls.VersionTLS12
+ if c.Web.TLSMinVersion != "" {
+ tlsMinVersion = allowedTLSVersions[c.Web.TLSMinVersion]
+ }
+ tlsMaxVersion := 0 // default for max is whatever Go defaults to
+ if c.Web.TLSMaxVersion != "" {
+ tlsMaxVersion = allowedTLSVersions[c.Web.TLSMaxVersion]
+ }
+
+ baseTLSConfig := &tls.Config{
+ MinVersion: uint16(tlsMinVersion),
+ MaxVersion: uint16(tlsMaxVersion),
+ CipherSuites: allowedTLSCiphers,
+ PreferServerCipherSuites: true,
+ }
+
+ tlsConfig, err := newTLSReloader(logger, c.Web.TLSCert, c.Web.TLSKey, "", baseTLSConfig)
+ if err != nil {
+ return fmt.Errorf("invalid config: get HTTP TLS: %v", err)
+ }
+
server := &http.Server{
- Handler: serv,
- TLSConfig: &tls.Config{
- CipherSuites: allowedTLSCiphers,
- PreferServerCipherSuites: true,
- MinVersion: tls.VersionTLS12,
- },
+ Handler: serv,
+ TLSConfig: tlsConfig,
}
defer server.Close()
group.Add(func() error {
- return server.ServeTLS(l, c.Web.TLSCert, c.Web.TLSKey)
+ return server.ServeTLS(l, "", "")
}, func(err error) {
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
- logger.Debugf("starting graceful shutdown (%s)", name)
+ logger.Debug("starting graceful shutdown", "server", name)
if err := server.Shutdown(ctx); err != nil {
- logger.Errorf("graceful shutdown (%s): %v", name, err)
+ logger.Error("graceful shutdown", "server", name, "err", err)
}
})
}
// Set up grpc server
if c.GRPC.Addr != "" {
- logger.Infof("listening (grpc) on %s", c.GRPC.Addr)
+ logger.Info("listening on", "server", "grpc", "address", c.GRPC.Addr)
grpcListener, err := net.Listen("tcp", c.GRPC.Addr)
if err != nil {
@@ -464,7 +513,7 @@ func runServe(options serveOptions) error {
}
grpcSrv := grpc.NewServer(grpcOptions...)
- api.RegisterDexServer(grpcSrv, server.NewAPI(serverConfig.Storage, logger, version))
+ api.RegisterDexServer(grpcSrv, server.NewAPI(serverConfig.Storage, logger, version, serv))
grpcMetrics.InitializeMetrics(grpcSrv)
if c.GRPC.Reflection {
@@ -475,7 +524,7 @@ func runServe(options serveOptions) error {
group.Add(func() error {
return grpcSrv.Serve(grpcListener)
}, func(err error) {
- logger.Debugf("starting graceful shutdown (grpc)")
+ logger.Debug("starting graceful shutdown", "server", "grpc")
grpcSrv.GracefulStop()
})
}
@@ -485,55 +534,11 @@ func runServe(options serveOptions) error {
if _, ok := err.(run.SignalError); !ok {
return fmt.Errorf("run groups: %w", err)
}
- logger.Infof("%v, shutdown now", err)
+ logger.Info("shutdown now", "err", err)
}
return nil
}
-var (
- logLevels = []string{"debug", "info", "error"}
- logFormats = []string{"json", "text"}
-)
-
-type utcFormatter struct {
- f logrus.Formatter
-}
-
-func (f *utcFormatter) Format(e *logrus.Entry) ([]byte, error) {
- e.Time = e.Time.UTC()
- return f.f.Format(e)
-}
-
-func newLogger(level string, format string) (log.Logger, error) {
- var logLevel logrus.Level
- switch strings.ToLower(level) {
- case "debug":
- logLevel = logrus.DebugLevel
- case "", "info":
- logLevel = logrus.InfoLevel
- case "error":
- logLevel = logrus.ErrorLevel
- default:
- return nil, fmt.Errorf("log level is not one of the supported values (%s): %s", strings.Join(logLevels, ", "), level)
- }
-
- var formatter utcFormatter
- switch strings.ToLower(format) {
- case "", "text":
- formatter.f = &logrus.TextFormatter{DisableColors: true}
- case "json":
- formatter.f = &logrus.JSONFormatter{}
- default:
- return nil, fmt.Errorf("log format is not one of the supported values (%s): %s", strings.Join(logFormats, ", "), format)
- }
-
- return &logrus.Logger{
- Out: os.Stderr,
- Formatter: &formatter,
- Level: logLevel,
- }, nil
-}
-
func applyConfigOverrides(options serveOptions, config *Config) {
if options.webHTTPAddr != "" {
config.Web.HTTP = options.webHTTPAddr
@@ -554,6 +559,17 @@ func applyConfigOverrides(options serveOptions, config *Config) {
if config.Frontend.Dir == "" {
config.Frontend.Dir = os.Getenv("DEX_FRONTEND_DIR")
}
+
+ if len(config.OAuth2.GrantTypes) == 0 {
+ config.OAuth2.GrantTypes = []string{
+ "authorization_code",
+ "implicit",
+ "password",
+ "refresh_token",
+ "urn:ietf:params:oauth:grant-type:device_code",
+ "urn:ietf:params:oauth:grant-type:token-exchange",
+ }
+ }
}
func pprofHandler(router *http.ServeMux) {
@@ -563,3 +579,118 @@ func pprofHandler(router *http.ServeMux) {
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
}
+
+// newTLSReloader returns a [tls.Config] with GetCertificate or GetConfigForClient set
+// to reload certificates from the given paths on SIGHUP or on file creates (atomic update via rename).
+func newTLSReloader(logger *slog.Logger, certFile, keyFile, caFile string, baseConfig *tls.Config) (*tls.Config, error) {
+ // trigger reload on channel
+ sigc := make(chan os.Signal, 1)
+ signal.Notify(sigc, syscall.SIGHUP)
+
+ // files to watch
+ watchFiles := map[string]struct{}{
+ certFile: {},
+ keyFile: {},
+ }
+ if caFile != "" {
+ watchFiles[caFile] = struct{}{}
+ }
+ watchDirs := make(map[string]struct{}) // dedupe dirs
+ for f := range watchFiles {
+ dir := filepath.Dir(f)
+ if !strings.HasPrefix(f, dir) {
+ // normalize name to have ./ prefix if only a local path was provided
+ // can't pass "" to watcher.Add
+ watchFiles[dir+string(filepath.Separator)+f] = struct{}{}
+ }
+ watchDirs[dir] = struct{}{}
+ }
+ // trigger reload on file change
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ return nil, fmt.Errorf("create watcher for TLS reloader: %v", err)
+ }
+ // recommended by fsnotify: watch the dir to handle renames
+ // https://pkg.go.dev/github.com/fsnotify/fsnotify#hdr-Watching_files
+ for dir := range watchDirs {
+ logger.Debug("watching dir", "dir", dir)
+ err := watcher.Add(dir)
+ if err != nil {
+ return nil, fmt.Errorf("watch dir for TLS reloader: %v", err)
+ }
+ }
+
+ // load once outside the goroutine so we can return an error on misconfig
+ initialConfig, err := loadTLSConfig(certFile, keyFile, caFile, baseConfig)
+ if err != nil {
+ return nil, fmt.Errorf("load TLS config: %v", err)
+ }
+
+ // stored version of current tls config
+ ptr := &atomic.Pointer[tls.Config]{}
+ ptr.Store(initialConfig)
+
+ // start background worker to reload certs
+ go func() {
+ loop:
+ for {
+ select {
+ case sig := <-sigc:
+ logger.Debug("reloading cert from signal", "signal", sig)
+ case evt := <-watcher.Events:
+ if _, ok := watchFiles[evt.Name]; !ok || !evt.Has(fsnotify.Create) {
+ continue loop
+ }
+ logger.Debug("reloading cert from fsnotify", "event", evt.Name, "operation", evt.Op.String())
+ case err := <-watcher.Errors:
+ logger.Error("TLS reloader watch", "err", err)
+ }
+
+ loaded, err := loadTLSConfig(certFile, keyFile, caFile, baseConfig)
+ if err != nil {
+ logger.Error("reload TLS config", "err", err)
+ }
+ ptr.Store(loaded)
+ }
+ }()
+
+ // https://pkg.go.dev/crypto/tls#baseConfig
+ // Server configurations must set one of Certificates, GetCertificate or GetConfigForClient.
+ if caFile != "" {
+ // grpc will use this via tls.Server for mTLS
+ initialConfig.GetConfigForClient = func(chi *tls.ClientHelloInfo) (*tls.Config, error) { return ptr.Load(), nil }
+ } else {
+ // net/http only uses Certificates or GetCertificate
+ initialConfig.GetCertificate = func(chi *tls.ClientHelloInfo) (*tls.Certificate, error) { return &ptr.Load().Certificates[0], nil }
+ }
+ return initialConfig, nil
+}
+
+// loadTLSConfig loads the given file paths into a [tls.Config]
+func loadTLSConfig(certFile, keyFile, caFile string, baseConfig *tls.Config) (*tls.Config, error) {
+ cert, err := tls.LoadX509KeyPair(certFile, keyFile)
+ if err != nil {
+ return nil, fmt.Errorf("loading TLS keypair: %v", err)
+ }
+ loadedConfig := baseConfig.Clone() // copy
+ loadedConfig.Certificates = []tls.Certificate{cert}
+ if caFile != "" {
+ cPool := x509.NewCertPool()
+ clientCert, err := os.ReadFile(caFile)
+ if err != nil {
+ return nil, fmt.Errorf("reading from client CA file: %v", err)
+ }
+ if !cPool.AppendCertsFromPEM(clientCert) {
+ return nil, errors.New("failed to parse client CA")
+ }
+
+ loadedConfig.ClientAuth = tls.RequireAndVerifyClientCert
+ loadedConfig.ClientCAs = cPool
+ }
+ return loadedConfig, nil
+}
+
+// recordBuildInfo publishes information about Dex version and runtime info through an info metric (gauge).
+func recordBuildInfo() {
+ buildInfo.WithLabelValues(version, runtime.Version(), fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH)).Set(1)
+}
diff --git a/cmd/dex/serve_test.go b/cmd/dex/serve_test.go
new file mode 100644
index 0000000000..9e214480d3
--- /dev/null
+++ b/cmd/dex/serve_test.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+ "log/slog"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+func TestNewLogger(t *testing.T) {
+ t.Run("JSON", func(t *testing.T) {
+ logger, err := newLogger(slog.LevelInfo, "json")
+ require.NoError(t, err)
+ require.NotEqual(t, (*slog.Logger)(nil), logger)
+ })
+
+ t.Run("Text", func(t *testing.T) {
+ logger, err := newLogger(slog.LevelError, "text")
+ require.NoError(t, err)
+ require.NotEqual(t, (*slog.Logger)(nil), logger)
+ })
+
+ t.Run("Unknown", func(t *testing.T) {
+ logger, err := newLogger(slog.LevelError, "gofmt")
+ require.Error(t, err)
+ require.Equal(t, "log format is not one of the supported values (json, text): gofmt", err.Error())
+ require.Equal(t, (*slog.Logger)(nil), logger)
+ })
+}
diff --git a/cmd/docker-entrypoint/main.go b/cmd/docker-entrypoint/main.go
index 0c507d1712..14d837e5ee 100644
--- a/cmd/docker-entrypoint/main.go
+++ b/cmd/docker-entrypoint/main.go
@@ -17,21 +17,18 @@ func main() {
// Note that this docker-entrypoint program is args[0], and it is provided with the true process
// args.
args := os.Args[1:]
+ if len(args) == 0 {
+ fmt.Println("error: no args passed to entrypoint")
+ os.Exit(1)
+ }
- if err := run(args, realExec, realWhich); err != nil {
+ if err := run(args, realExec, realWhich, realGomplate); err != nil {
fmt.Println("error:", err.Error())
os.Exit(1)
}
}
-func realExec(fork bool, args ...string) error {
- if fork {
- if output, err := exec.Command(args[0], args[1:]...).CombinedOutput(); err != nil {
- return fmt.Errorf("cannot fork/exec command %s: %w (output: %q)", args, err, string(output))
- }
- return nil
- }
-
+func realExec(args ...string) error {
argv0, err := exec.LookPath(args[0])
if err != nil {
return fmt.Errorf("cannot lookup path for command %s: %w", args[0], err)
@@ -52,34 +49,49 @@ func realWhich(path string) string {
return fullPath
}
-func run(args []string, execFunc func(bool, ...string) error, whichFunc func(string) string) error {
+func realGomplate(path string) (string, error) {
+ tmpFile, err := os.CreateTemp("/tmp", "dex.config.yaml-*")
+ if err != nil {
+ return "", fmt.Errorf("cannot create temp file: %w", err)
+ }
+
+ cmd := exec.Command("gomplate", "-f", path, "-o", tmpFile.Name())
+ // TODO(nabokihms): Workaround to run gomplate from a non-root directory in distroless images
+ // gomplate tries to access CWD on start, see: https://github.com/hairyhenderson/gomplate/pull/2202
+ cmd.Dir = "/etc/dex"
+
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ return "", fmt.Errorf("error executing gomplate: %w, (output: %q)", err, string(output))
+ }
+
+ return tmpFile.Name(), nil
+}
+
+func run(args []string, execFunc func(...string) error, whichFunc func(string) string, gomplateFunc func(string) (string, error)) error {
if args[0] != "dex" && args[0] != whichFunc("dex") {
- return execFunc(false, args...)
+ return execFunc(args...)
}
if args[1] != "serve" {
- return execFunc(false, args...)
+ return execFunc(args...)
}
newArgs := []string{}
for _, tplCandidate := range args {
if hasSuffixes(tplCandidate, ".tpl", ".tmpl", ".yaml") {
- tmpFile, err := os.CreateTemp("/tmp", "dex.config.yaml-*")
+ fileName, err := gomplateFunc(tplCandidate)
if err != nil {
- return fmt.Errorf("cannot create temp file: %w", err)
- }
-
- if err := execFunc(true, "gomplate", "-f", tplCandidate, "-o", tmpFile.Name()); err != nil {
return err
}
- newArgs = append(newArgs, tmpFile.Name())
+ newArgs = append(newArgs, fileName)
} else {
newArgs = append(newArgs, tplCandidate)
}
}
- return execFunc(false, newArgs...)
+ return execFunc(newArgs...)
}
func hasSuffixes(s string, suffixes ...string) bool {
diff --git a/cmd/docker-entrypoint/main_test.go b/cmd/docker-entrypoint/main_test.go
index c8aef16979..49da3b5f02 100644
--- a/cmd/docker-entrypoint/main_test.go
+++ b/cmd/docker-entrypoint/main_test.go
@@ -6,7 +6,7 @@ import (
)
type execArgs struct {
- fork bool
+ gomplate bool
argPrefixes []string
}
@@ -16,98 +16,89 @@ func TestRun(t *testing.T) {
args []string
execReturns error
whichReturns string
- wantExecArgs []execArgs
+ wantExecArgs execArgs
wantErr error
}{
{
name: "executable not dex",
args: []string{"tuna", "fish"},
- wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"tuna", "fish"}}},
+ wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"tuna", "fish"}},
},
{
name: "executable is full path to dex",
args: []string{"/usr/local/bin/dex", "marshmallow", "zelda"},
whichReturns: "/usr/local/bin/dex",
- wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}}},
+ wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"/usr/local/bin/dex", "marshmallow", "zelda"}},
},
{
name: "command is not serve",
args: []string{"dex", "marshmallow", "zelda"},
- wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "marshmallow", "zelda"}}},
+ wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "marshmallow", "zelda"}},
},
{
name: "no templates",
args: []string{"dex", "serve", "config.yaml.not-a-template"},
- wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}},
+ wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}},
},
{
name: "no templates",
args: []string{"dex", "serve", "config.yaml.not-a-template"},
- wantExecArgs: []execArgs{{fork: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}}},
+ wantExecArgs: execArgs{gomplate: false, argPrefixes: []string{"dex", "serve", "config.yaml.not-a-template"}},
},
{
- name: ".tpl template",
- args: []string{"dex", "serve", "config.tpl"},
- wantExecArgs: []execArgs{
- {fork: true, argPrefixes: []string{"gomplate", "-f", "config.tpl", "-o", "/tmp/dex.config.yaml-"}},
- {fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
- },
+ name: ".tpl template",
+ args: []string{"dex", "serve", "config.tpl"},
+ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
{
- name: ".tmpl template",
- args: []string{"dex", "serve", "config.tmpl"},
- wantExecArgs: []execArgs{
- {fork: true, argPrefixes: []string{"gomplate", "-f", "config.tmpl", "-o", "/tmp/dex.config.yaml-"}},
- {fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
- },
+ name: ".tmpl template",
+ args: []string{"dex", "serve", "config.tmpl"},
+ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
{
- name: ".yaml template",
- args: []string{"dex", "serve", "some/path/config.yaml"},
- wantExecArgs: []execArgs{
- {fork: true, argPrefixes: []string{"gomplate", "-f", "some/path/config.yaml", "-o", "/tmp/dex.config.yaml-"}},
- {fork: false, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
- },
+ name: ".yaml template",
+ args: []string{"dex", "serve", "some/path/config.yaml"},
+ wantExecArgs: execArgs{gomplate: true, argPrefixes: []string{"dex", "serve", "/tmp/dex.config.yaml-"}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
- var gotExecForks []bool
- var gotExecArgs [][]string
- fakeExec := func(fork bool, args ...string) error {
- gotExecForks = append(gotExecForks, fork)
- gotExecArgs = append(gotExecArgs, args)
+ var gotExecArgs []string
+ var runsGomplate bool
+
+ fakeExec := func(args ...string) error {
+ gotExecArgs = append(args, gotExecArgs...)
return test.execReturns
}
fakeWhich := func(_ string) string { return test.whichReturns }
- gotErr := run(test.args, fakeExec, fakeWhich)
+ fakeGomplate := func(file string) (string, error) {
+ runsGomplate = true
+ return "/tmp/dex.config.yaml-", nil
+ }
+
+ gotErr := run(test.args, fakeExec, fakeWhich, fakeGomplate)
if (test.wantErr == nil) != (gotErr == nil) {
t.Errorf("wanted error %s, got %s", test.wantErr, gotErr)
}
- if !execArgsMatch(test.wantExecArgs, gotExecForks, gotExecArgs) {
- t.Errorf("wanted exec args %+v, got %+v %+v", test.wantExecArgs, gotExecForks, gotExecArgs)
+
+ if !execArgsMatch(test.wantExecArgs, runsGomplate, gotExecArgs) {
+ t.Errorf("wanted exec args %+v (running gomplate: %+v), got %+v (running gomplate: %+v)",
+ test.wantExecArgs.argPrefixes, test.wantExecArgs.gomplate, gotExecArgs, runsGomplate)
}
})
}
}
-func execArgsMatch(wantExecArgs []execArgs, gotForks []bool, gotExecArgs [][]string) bool {
- if len(wantExecArgs) != len(gotForks) {
+func execArgsMatch(wantExecArgs execArgs, gomplate bool, gotExecArgs []string) bool {
+ if wantExecArgs.gomplate != gomplate {
return false
}
-
- for i := range wantExecArgs {
- if wantExecArgs[i].fork != gotForks[i] {
+ for i := range wantExecArgs.argPrefixes {
+ if !strings.HasPrefix(gotExecArgs[i], wantExecArgs.argPrefixes[i]) {
return false
}
- for j := range wantExecArgs[i].argPrefixes {
- if !strings.HasPrefix(gotExecArgs[i][j], wantExecArgs[i].argPrefixes[j]) {
- return false
- }
- }
}
-
return true
}
diff --git a/config.dev.yaml b/config.dev.yaml
index dda65e08f7..b0dc959c58 100644
--- a/config.dev.yaml
+++ b/config.dev.yaml
@@ -32,4 +32,8 @@ staticPasswords:
- email: "admin@example.com"
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
+ preferredUsername: "admin"
+ groups:
+ - "team-a"
+ - "team-a/admins"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
diff --git a/config.yaml.dist b/config.yaml.dist
index ba7bad68e0..c187ca3ce6 100644
--- a/config.yaml.dist
+++ b/config.yaml.dist
@@ -55,6 +55,8 @@ web:
# https: 127.0.0.1:5554
# tlsCert: /etc/dex/tls.crt
# tlsKey: /etc/dex/tls.key
+ # tlsMinVersion: 1.2
+ # tlsMaxVersion: 1.3
# Dex UI configuration
# frontend:
@@ -118,7 +120,7 @@ web:
# name: 'Example App'
# secret: ZXhhbXBsZS1hcHAtc2VjcmV0
-# Connectors are used to authenticate users agains upstream identity providers.
+# Connectors are used to authenticate users against upstream identity providers.
#
# See the documentation (https://dexidp.io/docs/connectors/) for further information.
# connectors: []
@@ -133,4 +135,15 @@ enablePasswordDB: true
# A static list of passwords for the password connector.
#
# Alternatively, passwords my be added/updated through the gRPC API.
-# staticPasswords: []
+# staticPasswords:
+# - email: "user@example.com"
+# # bcrypt hash of the string "password"
+# hash: "$2a$10$examplehash..."
+# username: "user-login"
+# # Optional. Maps to OIDC "preferred_username" claim.
+# preferredUsername: "user-public"
+# # Optional. Maps to OIDC "groups" claim (when 'groups' scope is requested).
+# groups:
+# - "team-a"
+# - "team-a/admins"
+# userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
diff --git a/connector/atlassiancrowd/atlassiancrowd.go b/connector/atlassiancrowd/atlassiancrowd.go
index e2ca94b0de..d36832846e 100644
--- a/connector/atlassiancrowd/atlassiancrowd.go
+++ b/connector/atlassiancrowd/atlassiancrowd.go
@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "log/slog"
"net"
"net/http"
"strings"
@@ -14,7 +15,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
// Config holds configuration options for Atlassian Crowd connector.
@@ -24,18 +24,17 @@ import (
//
// An example config:
//
-// type: atlassian-crowd
-// config:
-// baseURL: https://crowd.example.com/context
-// clientID: applogin
-// clientSecret: appP4$$w0rd
-// # users can be restricted by a list of groups
-// groups:
-// - admin
-// # Prompt for username field
-// usernamePrompt: Login
-// preferredUsernameField: name
-//
+// type: atlassian-crowd
+// config:
+// baseURL: https://crowd.example.com/context
+// clientID: applogin
+// clientSecret: appP4$$w0rd
+// # users can be restricted by a list of groups
+// groups:
+// - admin
+// # Prompt for username field
+// usernamePrompt: Login
+// preferredUsernameField: name
type Config struct {
BaseURL string `json:"baseURL"`
ClientID string `json:"clientID"`
@@ -81,16 +80,16 @@ type crowdAuthenticationError struct {
}
// Open returns a strategy for logging in through Atlassian Crowd
-func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
return nil, fmt.Errorf("crowd: no baseURL provided for crowd connector")
}
- return &crowdConnector{Config: *c, logger: logger}, nil
+ return &crowdConnector{Config: *c, logger: logger.With(slog.Group("connector", "type", "atlassiancrowd", "id", id))}, nil
}
type crowdConnector struct {
Config
- logger log.Logger
+ logger *slog.Logger
}
var (
@@ -376,7 +375,7 @@ func (c *crowdConnector) identityFromCrowdUser(user crowdUser) connector.Identit
identity.PreferredUsername = user.Email
default:
if c.PreferredUsernameField != "" {
- c.logger.Warnf("preferred_username left empty. Invalid crowd field mapped to preferred_username: %s", c.PreferredUsernameField)
+ c.logger.Warn("preferred_username left empty. Invalid crowd field mapped to preferred_username", "field", c.PreferredUsernameField)
}
}
@@ -437,12 +436,12 @@ func (c *crowdConnector) validateCrowdResponse(resp *http.Response) ([]byte, err
}
if resp.StatusCode == http.StatusForbidden && strings.Contains(string(body), "The server understood the request but refuses to authorize it.") {
- c.logger.Debugf("crowd response validation failed: %s", string(body))
+ c.logger.Debug("crowd response validation failed", "response", string(body))
return nil, fmt.Errorf("dex is forbidden from making requests to the Atlassian Crowd application by URL %q", c.BaseURL)
}
if resp.StatusCode == http.StatusUnauthorized && string(body) == "Application failed to authenticate" {
- c.logger.Debugf("crowd response validation failed: %s", string(body))
+ c.logger.Debug("crowd response validation failed", "response", string(body))
return nil, fmt.Errorf("dex failed to authenticate Crowd Application with ID %q", c.ClientID)
}
return body, nil
diff --git a/connector/atlassiancrowd/atlassiancrowd_test.go b/connector/atlassiancrowd/atlassiancrowd_test.go
index 36789a3919..17d0422ac8 100644
--- a/connector/atlassiancrowd/atlassiancrowd_test.go
+++ b/connector/atlassiancrowd/atlassiancrowd_test.go
@@ -6,13 +6,11 @@ import (
"crypto/tls"
"encoding/json"
"fmt"
- "io"
+ "log/slog"
"net/http"
"net/http/httptest"
"reflect"
"testing"
-
- "github.com/sirupsen/logrus"
)
func TestUserGroups(t *testing.T) {
@@ -115,7 +113,7 @@ func TestIdentityFromCrowdUser(t *testing.T) {
expectEquals(t, user.Name, "testuser")
expectEquals(t, user.Email, "testuser@example.com")
- // Test unconfigured behaviour
+ // Test unconfigured behavior
i := c.identityFromCrowdUser(user)
expectEquals(t, i.UserID, "12345")
expectEquals(t, i.Username, "testuser")
@@ -151,11 +149,7 @@ type TestServerResponse struct {
func newTestCrowdConnector(baseURL string) crowdConnector {
connector := crowdConnector{}
connector.BaseURL = baseURL
- connector.logger = &logrus.Logger{
- Out: io.Discard,
- Level: logrus.DebugLevel,
- Formatter: &logrus.TextFormatter{DisableColors: true},
- }
+ connector.logger = slog.New(slog.DiscardHandler)
return connector
}
diff --git a/connector/authproxy/authproxy.go b/connector/authproxy/authproxy.go
index 8715412146..f3d87fcb56 100644
--- a/connector/authproxy/authproxy.go
+++ b/connector/authproxy/authproxy.go
@@ -5,12 +5,12 @@ package authproxy
import (
"fmt"
+ "log/slog"
"net/http"
"net/url"
"strings"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
// Config holds the configuration parameters for a connector which returns an
@@ -19,33 +19,67 @@ import (
// Headers retrieved to fetch user's email and group can be configured
// with userHeader and groupHeader.
type Config struct {
- UserHeader string `json:"userHeader"`
- GroupHeader string `json:"groupHeader"`
- Groups []string `json:"staticGroups"`
+ UserIDHeader string `json:"userIDHeader"`
+ UserHeader string `json:"userHeader"`
+ UserNameHeader string `json:"userNameHeader"`
+ EmailHeader string `json:"emailHeader"`
+ GroupHeader string `json:"groupHeader"`
+ GroupHeaderSeparator string `json:"groupHeaderSeparator"`
+ Groups []string `json:"staticGroups"`
}
// Open returns an authentication strategy which requires no user interaction.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
+ userIDHeader := c.UserIDHeader
+ if userIDHeader == "" {
+ userIDHeader = "X-Remote-User-Id"
+ }
userHeader := c.UserHeader
if userHeader == "" {
userHeader = "X-Remote-User"
}
+ userNameHeader := c.UserNameHeader
+ if userNameHeader == "" {
+ userNameHeader = "X-Remote-User-Name"
+ }
+ emailHeader := c.EmailHeader
+ if emailHeader == "" {
+ emailHeader = "X-Remote-User-Email"
+ }
groupHeader := c.GroupHeader
if groupHeader == "" {
groupHeader = "X-Remote-Group"
}
+ groupHeaderSeparator := c.GroupHeaderSeparator
+ if groupHeaderSeparator == "" {
+ groupHeaderSeparator = ","
+ }
- return &callback{userHeader: userHeader, groupHeader: groupHeader, logger: logger, pathSuffix: "/" + id, groups: c.Groups}, nil
+ return &callback{
+ userIDHeader: userIDHeader,
+ userHeader: userHeader,
+ userNameHeader: userNameHeader,
+ emailHeader: emailHeader,
+ groupHeader: groupHeader,
+ groupHeaderSeparator: groupHeaderSeparator,
+ groups: c.Groups,
+ logger: logger.With(slog.Group("connector", "type", "authproxy", "id", id)),
+ pathSuffix: "/" + id,
+ }, nil
}
// Callback is a connector which returns an identity with the HTTP header
// X-Remote-User as verified email.
type callback struct {
- userHeader string
- groupHeader string
- groups []string
- logger log.Logger
- pathSuffix string
+ userIDHeader string
+ userNameHeader string
+ userHeader string
+ emailHeader string
+ groupHeader string
+ groupHeaderSeparator string
+ groups []string
+ logger *slog.Logger
+ pathSuffix string
}
// LoginURL returns the URL to redirect the user to login with.
@@ -67,19 +101,33 @@ func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connecto
if remoteUser == "" {
return connector.Identity{}, fmt.Errorf("required HTTP header %s is not set", m.userHeader)
}
+ remoteUserName := r.Header.Get(m.userNameHeader)
+ if remoteUserName == "" {
+ remoteUserName = remoteUser
+ }
+ remoteUserID := r.Header.Get(m.userIDHeader)
+ if remoteUserID == "" {
+ remoteUserID = remoteUser
+ }
+ remoteUserEmail := r.Header.Get(m.emailHeader)
+ if remoteUserEmail == "" {
+ remoteUserEmail = remoteUser
+ }
groups := m.groups
headerGroup := r.Header.Get(m.groupHeader)
if headerGroup != "" {
- splitheaderGroup := strings.Split(headerGroup, ",")
+ splitheaderGroup := strings.Split(headerGroup, m.groupHeaderSeparator)
for i, v := range splitheaderGroup {
splitheaderGroup[i] = strings.TrimSpace(v)
}
groups = append(splitheaderGroup, groups...)
}
return connector.Identity{
- UserID: remoteUser, // TODO: figure out if this is a bad ID value.
- Email: remoteUser,
- EmailVerified: true,
- Groups: groups,
+ UserID: remoteUserID,
+ Username: remoteUser,
+ PreferredUsername: remoteUserName,
+ Email: remoteUserEmail,
+ EmailVerified: true,
+ Groups: groups,
}, nil
}
diff --git a/connector/authproxy/authproxy_test.go b/connector/authproxy/authproxy_test.go
index 5d42530e07..fbdd2a5d91 100644
--- a/connector/authproxy/authproxy_test.go
+++ b/connector/authproxy/authproxy_test.go
@@ -1,55 +1,82 @@
package authproxy
import (
- "io"
+ "log/slog"
"net/http"
"reflect"
"testing"
- "github.com/sirupsen/logrus"
-
"github.com/dexidp/dex/connector"
)
const (
- testEmail = "testuser@example.com"
- testGroup1 = "group1"
- testGroup2 = "group2"
- testGroup3 = "group 3"
- testGroup4 = "group 4"
- testStaticGroup1 = "static1"
- testStaticGroup2 = "static 2"
+ testEmail = "testuser@example.com"
+ testGroup1 = "group1"
+ testGroup2 = "group2"
+ testGroup3 = "group 3"
+ testGroup4 = "group 4"
+ testStaticGroup1 = "static1"
+ testStaticGroup2 = "static 2"
+ testUsername = "Test User"
+ testPreferredUsername = "testuser"
+ testUserID = "1234567890"
)
-var logger = &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
+var logger = slog.New(slog.DiscardHandler)
func TestUser(t *testing.T) {
- config := Config{
- UserHeader: "X-Remote-User",
+ config := Config{}
+
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
+
+ req, err := http.NewRequest("GET", "/", nil)
+ expectNil(t, err)
+ req.Header = map[string][]string{
+ "X-Remote-User": {testUsername},
}
- conn := callback{userHeader: config.UserHeader, logger: logger, pathSuffix: "/test"}
+
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ expectNil(t, err)
+
+ // If not specified, the userID and email should fall back to the remote user
+ expectEquals(t, ident.UserID, testUsername)
+ expectEquals(t, ident.PreferredUsername, testUsername)
+ expectEquals(t, ident.Username, testUsername)
+ expectEquals(t, ident.Email, testUsername)
+ expectEquals(t, len(ident.Groups), 0)
+}
+
+func TestExtraHeaders(t *testing.T) {
+ config := Config{}
+
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
- "X-Remote-User": {testEmail},
+ "X-Remote-User-Id": {testUserID},
+ "X-Remote-User": {testUsername},
+ "X-Remote-User-Name": {testPreferredUsername},
+ "X-Remote-User-Email": {testEmail},
}
- ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
- expectEquals(t, ident.UserID, testEmail)
+ expectEquals(t, ident.UserID, testUserID)
+ expectEquals(t, ident.PreferredUsername, testPreferredUsername)
+ expectEquals(t, ident.Username, testUsername)
expectEquals(t, ident.Email, testEmail)
expectEquals(t, len(ident.Groups), 0)
}
func TestSingleGroup(t *testing.T) {
- config := Config{
- UserHeader: "X-Remote-User",
- GroupHeader: "X-Remote-Group",
- }
+ config := Config{}
- conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, logger: logger, pathSuffix: "/test"}
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
@@ -58,7 +85,7 @@ func TestSingleGroup(t *testing.T) {
"X-Remote-Group": {testGroup1},
}
- ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
@@ -67,21 +94,45 @@ func TestSingleGroup(t *testing.T) {
}
func TestMultipleGroup(t *testing.T) {
+ config := Config{}
+
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
+
+ req, err := http.NewRequest("GET", "/", nil)
+ expectNil(t, err)
+ req.Header = map[string][]string{
+ "X-Remote-User": {testEmail},
+ "X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4},
+ }
+
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ expectNil(t, err)
+
+ expectEquals(t, ident.UserID, testEmail)
+ expectEquals(t, len(ident.Groups), 4)
+ expectEquals(t, ident.Groups[0], testGroup1)
+ expectEquals(t, ident.Groups[1], testGroup2)
+ expectEquals(t, ident.Groups[2], testGroup3)
+ expectEquals(t, ident.Groups[3], testGroup4)
+}
+
+func TestMultipleGroupWithCustomSeparator(t *testing.T) {
config := Config{
- UserHeader: "X-Remote-User",
- GroupHeader: "X-Remote-Group",
+ GroupHeaderSeparator: ";",
}
- conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, logger: logger, pathSuffix: "/test"}
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
"X-Remote-User": {testEmail},
- "X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4},
+ "X-Remote-Group": {testGroup1 + ";" + testGroup2 + ";" + testGroup3 + ";" + testGroup4},
}
- ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
@@ -94,12 +145,11 @@ func TestMultipleGroup(t *testing.T) {
func TestStaticGroup(t *testing.T) {
config := Config{
- UserHeader: "X-Remote-User",
- GroupHeader: "X-Remote-Group",
- Groups: []string{"static1", "static 2"},
+ Groups: []string{"static1", "static 2"},
}
- conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, groups: config.Groups, logger: logger, pathSuffix: "/test"}
+ conn, _ := config.Open("test", logger)
+ callback := conn.(*callback)
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
@@ -108,7 +158,7 @@ func TestStaticGroup(t *testing.T) {
"X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4},
}
- ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
+ ident, err := callback.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
diff --git a/connector/bitbucketcloud/bitbucketcloud.go b/connector/bitbucketcloud/bitbucketcloud.go
index 27eafb5299..5f802e3414 100644
--- a/connector/bitbucketcloud/bitbucketcloud.go
+++ b/connector/bitbucketcloud/bitbucketcloud.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"net/http"
"sync"
"time"
@@ -16,7 +17,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
const (
@@ -42,7 +42,7 @@ type Config struct {
}
// Open returns a strategy for logging in through Bitbucket.
-func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
b := bitbucketConnector{
redirectURI: c.RedirectURI,
teams: c.Teams,
@@ -51,7 +51,7 @@ func (c *Config) Open(_ string, logger log.Logger) (connector.Connector, error)
includeTeamGroups: c.IncludeTeamGroups,
apiURL: apiURL,
legacyAPIURL: legacyAPIURL,
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "bitbucketcloud", "id", id)),
}
return &b, nil
@@ -73,7 +73,7 @@ type bitbucketConnector struct {
teams []string
clientID string
clientSecret string
- logger log.Logger
+ logger *slog.Logger
apiURL string
legacyAPIURL string
diff --git a/connector/connector.go b/connector/connector.go
index aab994b468..d812390f0c 100644
--- a/connector/connector.go
+++ b/connector/connector.go
@@ -70,7 +70,8 @@ type CallbackConnector interface {
}
// SAMLConnector represents SAML connectors which implement the HTTP POST binding.
-// RelayState is handled by the server.
+//
+// RelayState is handled by the server.
//
// See: https://docs.oasis-open.org/security/saml/v2.0/saml-bindings-2.0-os.pdf
// "3.5 HTTP POST Binding"
@@ -98,3 +99,7 @@ type RefreshConnector interface {
// changes since the token was last refreshed.
Refresh(ctx context.Context, s Scopes, identity Identity) (Identity, error)
}
+
+type TokenIdentityConnector interface {
+ TokenIdentity(ctx context.Context, subjectTokenType, subjectToken string) (Identity, error)
+}
diff --git a/connector/gitea/gitea.go b/connector/gitea/gitea.go
index 6b02099414..62523185d5 100644
--- a/connector/gitea/gitea.go
+++ b/connector/gitea/gitea.go
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"net/http"
"strconv"
"sync"
@@ -15,7 +16,6 @@ import (
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
// Config holds configuration options for gitea logins.
@@ -51,7 +51,7 @@ type giteaUser struct {
}
// Open returns a strategy for logging in through Gitea
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
c.BaseURL = "https://gitea.com"
}
@@ -61,7 +61,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
orgs: c.Orgs,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "gitea", "id", id)),
loadAllGroups: c.LoadAllGroups,
useLoginAsID: c.UseLoginAsID,
}, nil
@@ -84,7 +84,7 @@ type giteaConnector struct {
orgs []Org
clientID string
clientSecret string
- logger log.Logger
+ logger *slog.Logger
httpClient *http.Client
// if set to true and no orgs are configured then connector loads all user claims (all orgs and team)
loadAllGroups bool
diff --git a/connector/github/github.go b/connector/github/github.go
index ef8d418fa8..18a56628af 100644
--- a/connector/github/github.go
+++ b/connector/github/github.go
@@ -3,26 +3,22 @@ package github
import (
"context"
- "crypto/tls"
- "crypto/x509"
"encoding/json"
"errors"
"fmt"
"io"
- "net"
+ "log/slog"
"net/http"
- "os"
"regexp"
"strconv"
"strings"
- "time"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
"github.com/dexidp/dex/connector"
groups_pkg "github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/httpclient"
)
const (
@@ -43,16 +39,17 @@ var (
// Config holds configuration options for github logins.
type Config struct {
- ClientID string `json:"clientID"`
- ClientSecret string `json:"clientSecret"`
- RedirectURI string `json:"redirectURI"`
- Org string `json:"org"`
- Orgs []Org `json:"orgs"`
- HostName string `json:"hostName"`
- RootCA string `json:"rootCA"`
- TeamNameField string `json:"teamNameField"`
- LoadAllGroups bool `json:"loadAllGroups"`
- UseLoginAsID bool `json:"useLoginAsID"`
+ ClientID string `json:"clientID"`
+ ClientSecret string `json:"clientSecret"`
+ RedirectURI string `json:"redirectURI"`
+ Org string `json:"org"`
+ Orgs []Org `json:"orgs"`
+ HostName string `json:"hostName"`
+ RootCA string `json:"rootCA"`
+ TeamNameField string `json:"teamNameField"`
+ LoadAllGroups bool `json:"loadAllGroups"`
+ UseLoginAsID bool `json:"useLoginAsID"`
+ PreferredEmailDomain string `json:"preferredEmailDomain"`
}
// Org holds org-team filters, in which teams are optional.
@@ -69,7 +66,7 @@ type Org struct {
}
// Open returns a strategy for logging in through GitHub.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.Org != "" {
// Return error if both 'org' and 'orgs' fields are used.
if len(c.Orgs) > 0 {
@@ -79,14 +76,15 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
}
g := githubConnector{
- redirectURI: c.RedirectURI,
- org: c.Org,
- orgs: c.Orgs,
- clientID: c.ClientID,
- clientSecret: c.ClientSecret,
- apiURL: apiURL,
- logger: logger,
- useLoginAsID: c.UseLoginAsID,
+ redirectURI: c.RedirectURI,
+ org: c.Org,
+ orgs: c.Orgs,
+ clientID: c.ClientID,
+ clientSecret: c.ClientSecret,
+ apiURL: apiURL,
+ logger: logger.With(slog.Group("connector", "type", "github", "id", id)),
+ useLoginAsID: c.UseLoginAsID,
+ preferredEmailDomain: c.PreferredEmailDomain,
}
if c.HostName != "" {
@@ -106,7 +104,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
g.rootCA = c.RootCA
var err error
- if g.httpClient, err = newHTTPClient(g.rootCA); err != nil {
+ if g.httpClient, err = httpclient.NewHTTPClient([]string{g.rootCA}, false); err != nil {
return nil, fmt.Errorf("failed to create HTTP client: %v", err)
}
}
@@ -119,6 +117,12 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
return nil, fmt.Errorf("invalid connector config: unsupported team name field value `%s`", c.TeamNameField)
}
+ if c.PreferredEmailDomain != "" {
+ if strings.HasSuffix(c.PreferredEmailDomain, "*") {
+ return nil, errors.New("invalid PreferredEmailDomain: glob pattern cannot end with \"*\"")
+ }
+ }
+
return &g, nil
}
@@ -138,7 +142,7 @@ type githubConnector struct {
orgs []Org
clientID string
clientSecret string
- logger log.Logger
+ logger *slog.Logger
// apiURL defaults to "https://api.github.com"
apiURL string
// hostName of the GitHub enterprise account.
@@ -153,6 +157,8 @@ type githubConnector struct {
loadAllGroups bool
// if set to true will use the user's handle rather than their numeric id as the ID
useLoginAsID bool
+ // the domain to be preferred among the user's emails. e.g. "github.com"
+ preferredEmailDomain string
}
// groupsRequired returns whether dex requires GitHub's 'read:org' scope. Dex
@@ -208,34 +214,6 @@ func (e *oauth2Error) Error() string {
return e.error + ": " + e.errorDescription
}
-// newHTTPClient returns a new HTTP client that trusts the custom declared rootCA cert.
-func newHTTPClient(rootCA string) (*http.Client, error) {
- tlsConfig := tls.Config{RootCAs: x509.NewCertPool()}
- rootCABytes, err := os.ReadFile(rootCA)
- if err != nil {
- return nil, fmt.Errorf("failed to read root-ca: %v", err)
- }
- if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCABytes) {
- return nil, fmt.Errorf("no certs found in root CA file %q", rootCA)
- }
-
- return &http.Client{
- Transport: &http.Transport{
- TLSClientConfig: &tlsConfig,
- Proxy: http.ProxyFromEnvironment,
- DialContext: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- DualStack: true,
- }).DialContext,
- MaxIdleConns: 100,
- IdleConnTimeout: 90 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 1 * time.Second,
- },
- }, nil
-}
-
func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
q := r.URL.Query()
if errType := q.Get("error"); errType != "" {
@@ -356,9 +334,11 @@ func formatTeamName(org string, team string) string {
// groupsForOrgs enforces org and team constraints on user authorization
// Cases in which user is authorized:
-// N orgs, no teams: user is member of at least 1 org
-// N orgs, M teams per org: user is member of any team from at least 1 org
-// N-1 orgs, M teams per org, 1 org with no teams: user is member of any team
+//
+// N orgs, no teams: user is member of at least 1 org
+// N orgs, M teams per org: user is member of any team from at least 1 org
+// N-1 orgs, M teams per org, 1 org with no teams: user is member of any team
+//
// from at least 1 org, or member of org with no teams
func (c *githubConnector) groupsForOrgs(ctx context.Context, client *http.Client, userName string) ([]string, error) {
groups := make([]string, 0)
@@ -382,7 +362,7 @@ func (c *githubConnector) groupsForOrgs(ctx context.Context, client *http.Client
if len(org.Teams) == 0 {
inOrgNoTeams = true
} else if teams = groups_pkg.Filter(teams, org.Teams); len(teams) == 0 {
- c.logger.Infof("github: user %q in org %q but no teams", userName, org.Name)
+ c.logger.Info("user in org but no teams", "user", userName, "org", org.Name)
}
for _, teamName := range teams {
@@ -551,9 +531,10 @@ func (c *githubConnector) user(ctx context.Context, client *http.Client) (user,
return u, err
}
- // Only public user emails are returned by 'GET /user'. u.Email will be empty
- // if a users' email is private. We must retrieve private emails explicitly.
- if u.Email == "" {
+ // Only public user emails are returned by 'GET /user'.
+ // If a user has no public email, we must retrieve private emails explicitly.
+ // If preferredEmailDomain is set, we always need to retrieve all emails.
+ if u.Email == "" || c.preferredEmailDomain != "" {
var err error
if u.Email, err = c.userEmail(ctx, client); err != nil {
return u, err
@@ -578,7 +559,13 @@ type userEmail struct {
// The HTTP client is expected to be constructed by the golang.org/x/oauth2 package,
// which inserts a bearer token as part of the request.
func (c *githubConnector) userEmail(ctx context.Context, client *http.Client) (string, error) {
+ var (
+ primaryEmail userEmail
+ preferredEmails []userEmail
+ )
+
apiURL := c.apiURL + "/user/emails"
+
for {
// https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user
var (
@@ -605,7 +592,17 @@ func (c *githubConnector) userEmail(ctx context.Context, client *http.Client) (s
}
if email.Verified && email.Primary {
- return email.Email, nil
+ primaryEmail = email
+ }
+
+ if c.preferredEmailDomain != "" {
+ _, domainPart, ok := strings.Cut(email.Email, "@")
+ if !ok {
+ return "", errors.New("github: invalid format email is detected")
+ }
+ if email.Verified && c.isPreferredEmailDomain(domainPart) {
+ preferredEmails = append(preferredEmails, email)
+ }
}
}
@@ -614,7 +611,36 @@ func (c *githubConnector) userEmail(ctx context.Context, client *http.Client) (s
}
}
- return "", errors.New("github: user has no verified, primary email")
+ if len(preferredEmails) > 0 {
+ return preferredEmails[0].Email, nil
+ }
+
+ if primaryEmail.Email != "" {
+ return primaryEmail.Email, nil
+ }
+
+ return "", errors.New("github: user has no verified, primary email or preferred-domain email")
+}
+
+// isPreferredEmailDomain checks the domain is matching with preferredEmailDomain.
+func (c *githubConnector) isPreferredEmailDomain(domain string) bool {
+ if domain == c.preferredEmailDomain {
+ return true
+ }
+
+ preferredDomainParts := strings.Split(c.preferredEmailDomain, ".")
+ domainParts := strings.Split(domain, ".")
+
+ if len(preferredDomainParts) != len(domainParts) {
+ return false
+ }
+
+ for i, v := range preferredDomainParts {
+ if domainParts[i] != v && v != "*" {
+ return false
+ }
+ }
+ return true
}
// userInOrg queries the GitHub API for a users' org membership.
@@ -642,7 +668,7 @@ func (c *githubConnector) userInOrg(ctx context.Context, client *http.Client, us
switch resp.StatusCode {
case http.StatusNoContent:
case http.StatusFound, http.StatusNotFound:
- c.logger.Infof("github: user %q not in org %q or application not authorized to read org data", userName, orgName)
+ c.logger.Info("user not in org or application not authorized to read org data", "user", userName, "org", orgName)
default:
err = fmt.Errorf("github: unexpected return status: %q", resp.Status)
}
diff --git a/connector/github/github_test.go b/connector/github/github_test.go
index 76d7463cf6..a2222cc1bf 100644
--- a/connector/github/github_test.go
+++ b/connector/github/github_test.go
@@ -4,7 +4,9 @@ import (
"context"
"crypto/tls"
"encoding/json"
+ "errors"
"fmt"
+ "log/slog"
"net/http"
"net/http/httptest"
"net/url"
@@ -198,6 +200,291 @@ func TestLoginUsedAsIDWhenConfigured(t *testing.T) {
expectEquals(t, identity.Username, "Joe Bloggs")
}
+func TestPreferredEmailDomainConfigured(t *testing.T) {
+ ctx := context.Background()
+ s := newTestServer(map[string]testResponse{
+ "/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
+ "/user/emails": {
+ data: []userEmail{
+ {
+ Email: "some@email.com",
+ Verified: true,
+ Primary: true,
+ },
+ {
+ Email: "another@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "some@preferred-domain.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "another@preferred-domain.com",
+ Verified: true,
+ Primary: false,
+ },
+ },
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ client := newClient()
+ c := githubConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: client, preferredEmailDomain: "preferred-domain.com"}
+
+ u, err := c.user(ctx, client)
+ expectNil(t, err)
+ expectEquals(t, u.Email, "some@preferred-domain.com")
+}
+
+func TestPreferredEmailDomainConfiguredWithGlob(t *testing.T) {
+ ctx := context.Background()
+ s := newTestServer(map[string]testResponse{
+ "/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
+ "/user/emails": {
+ data: []userEmail{
+ {
+ Email: "some@email.com",
+ Verified: true,
+ Primary: true,
+ },
+ {
+ Email: "another@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "some@another.preferred-domain.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "some@sub-domain.preferred-domain.co",
+ Verified: true,
+ Primary: false,
+ },
+ },
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ client := newClient()
+ c := githubConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: client, preferredEmailDomain: "*.preferred-domain.co"}
+
+ u, err := c.user(ctx, client)
+ expectNil(t, err)
+ expectEquals(t, u.Email, "some@sub-domain.preferred-domain.co")
+}
+
+func TestPreferredEmailDomainConfigured_UserHasNoPreferredDomainEmail(t *testing.T) {
+ ctx := context.Background()
+ s := newTestServer(map[string]testResponse{
+ "/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
+ "/user/emails": {
+ data: []userEmail{
+ {
+ Email: "some@email.com",
+ Verified: true,
+ Primary: true,
+ },
+ {
+ Email: "another@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ },
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ client := newClient()
+ c := githubConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: client, preferredEmailDomain: "preferred-domain.com"}
+
+ u, err := c.user(ctx, client)
+ expectNil(t, err)
+ expectEquals(t, u.Email, "some@email.com")
+}
+
+func TestPreferredEmailDomainNotConfigured(t *testing.T) {
+ ctx := context.Background()
+ s := newTestServer(map[string]testResponse{
+ "/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
+ "/user/emails": {
+ data: []userEmail{
+ {
+ Email: "some@email.com",
+ Verified: true,
+ Primary: true,
+ },
+ {
+ Email: "another@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "some@preferred-domain.com",
+ Verified: true,
+ Primary: false,
+ },
+ },
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ client := newClient()
+ c := githubConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: client}
+
+ u, err := c.user(ctx, client)
+ expectNil(t, err)
+ expectEquals(t, u.Email, "some@email.com")
+}
+
+func TestPreferredEmailDomainConfigured_Error_BothPrimaryAndPreferredDomainEmailNotFound(t *testing.T) {
+ ctx := context.Background()
+ s := newTestServer(map[string]testResponse{
+ "/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
+ "/user/emails": {
+ data: []userEmail{
+ {
+ Email: "some@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "another@email.com",
+ Verified: true,
+ Primary: false,
+ },
+ {
+ Email: "some@preferred-domain.com",
+ Verified: true,
+ Primary: false,
+ },
+ },
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ client := newClient()
+ c := githubConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: client, preferredEmailDomain: "foo.bar"}
+
+ _, err = c.user(ctx, client)
+ expectNotNil(t, err, "Email not found error")
+ expectEquals(t, err.Error(), "github: user has no verified, primary email or preferred-domain email")
+}
+
+func Test_isPreferredEmailDomain(t *testing.T) {
+ client := newClient()
+ tests := []struct {
+ preferredEmailDomain string
+ email string
+ expected bool
+ }{
+ {
+ preferredEmailDomain: "example.com",
+ email: "test@example.com",
+ expected: true,
+ },
+ {
+ preferredEmailDomain: "example.com",
+ email: "test@another.com",
+ expected: false,
+ },
+ {
+ preferredEmailDomain: "*.example.com",
+ email: "test@my.example.com",
+ expected: true,
+ },
+ {
+ preferredEmailDomain: "*.example.com",
+ email: "test@my.another.com",
+ expected: false,
+ },
+ {
+ preferredEmailDomain: "*.example.com",
+ email: "test@my.domain.example.com",
+ expected: false,
+ },
+ {
+ preferredEmailDomain: "*.example.com",
+ email: "test@sub.domain.com",
+ expected: false,
+ },
+ {
+ preferredEmailDomain: "*.*.example.com",
+ email: "test@sub.my.example.com",
+ expected: true,
+ },
+ {
+ preferredEmailDomain: "*.*.example.com",
+ email: "test@a.my.google.com",
+ expected: false,
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.preferredEmailDomain, func(t *testing.T) {
+ c := githubConnector{apiURL: "apiURL", hostName: "github.com", httpClient: client, preferredEmailDomain: test.preferredEmailDomain}
+ _, domainPart, _ := strings.Cut(test.email, "@")
+ res := c.isPreferredEmailDomain(domainPart)
+
+ expectEquals(t, res, test.expected)
+ })
+ }
+}
+
+func Test_Open_PreferredDomainConfig(t *testing.T) {
+ log := slog.New(slog.DiscardHandler)
+ tests := []struct {
+ preferredEmailDomain string
+ email string
+ expected error
+ }{
+ {
+ preferredEmailDomain: "example.com",
+ expected: nil,
+ },
+ {
+ preferredEmailDomain: "*.example.com",
+ expected: nil,
+ },
+ {
+ preferredEmailDomain: "*.*.example.com",
+ expected: nil,
+ },
+ {
+ preferredEmailDomain: "example.*",
+ expected: errors.New("invalid PreferredEmailDomain: glob pattern cannot end with \"*\""),
+ },
+ }
+ for _, test := range tests {
+ t.Run(test.preferredEmailDomain, func(t *testing.T) {
+ c := Config{
+ PreferredEmailDomain: test.preferredEmailDomain,
+ }
+ _, err := c.Open("id", log)
+
+ expectEquals(t, err, test.expected)
+ })
+ }
+}
+
func newTestServer(responses map[string]testResponse) *httptest.Server {
var s *httptest.Server
s = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -231,6 +518,12 @@ func expectNil(t *testing.T, a interface{}) {
}
}
+func expectNotNil(t *testing.T, a interface{}, msg string) {
+ if a == nil {
+ t.Errorf("Expected %+v to not to be nil", msg)
+ }
+}
+
func expectEquals(t *testing.T, a interface{}, b interface{}) {
if !reflect.DeepEqual(a, b) {
t.Errorf("Expected %+v to equal %+v", a, b)
diff --git a/connector/gitlab/gitlab.go b/connector/gitlab/gitlab.go
index f35ac35753..7aa4439842 100644
--- a/connector/gitlab/gitlab.go
+++ b/connector/gitlab/gitlab.go
@@ -1,4 +1,4 @@
-// Package gitlab provides authentication strategies using Gitlab.
+// Package gitlab provides authentication strategies using GitLab.
package gitlab
import (
@@ -7,6 +7,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"net/http"
"strconv"
"time"
@@ -15,7 +16,6 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
const (
@@ -28,12 +28,13 @@ const (
// Config holds configuration options for gitlab logins.
type Config struct {
- BaseURL string `json:"baseURL"`
- ClientID string `json:"clientID"`
- ClientSecret string `json:"clientSecret"`
- RedirectURI string `json:"redirectURI"`
- Groups []string `json:"groups"`
- UseLoginAsID bool `json:"useLoginAsID"`
+ BaseURL string `json:"baseURL"`
+ ClientID string `json:"clientID"`
+ ClientSecret string `json:"clientSecret"`
+ RedirectURI string `json:"redirectURI"`
+ Groups []string `json:"groups"`
+ UseLoginAsID bool `json:"useLoginAsID"`
+ GetGroupsPermission bool `json:"getGroupsPermission"`
}
type gitlabUser struct {
@@ -46,18 +47,19 @@ type gitlabUser struct {
}
// Open returns a strategy for logging in through GitLab.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.BaseURL == "" {
c.BaseURL = "https://gitlab.com"
}
return &gitlabConnector{
- baseURL: c.BaseURL,
- redirectURI: c.RedirectURI,
- clientID: c.ClientID,
- clientSecret: c.ClientSecret,
- logger: logger,
- groups: c.Groups,
- useLoginAsID: c.UseLoginAsID,
+ baseURL: c.BaseURL,
+ redirectURI: c.RedirectURI,
+ clientID: c.ClientID,
+ clientSecret: c.ClientSecret,
+ logger: logger.With(slog.Group("connector", "type", "gitlab", "id", id)),
+ groups: c.Groups,
+ useLoginAsID: c.UseLoginAsID,
+ getGroupsPermission: c.GetGroupsPermission,
}, nil
}
@@ -78,10 +80,13 @@ type gitlabConnector struct {
groups []string
clientID string
clientSecret string
- logger log.Logger
+ logger *slog.Logger
httpClient *http.Client
// if set to true will use the user's handle rather than their numeric id as the ID
useLoginAsID bool
+
+ // if set to true permissions will be added to list of groups
+ getGroupsPermission bool
}
func (c *gitlabConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config {
@@ -256,7 +261,10 @@ func (c *gitlabConnector) user(ctx context.Context, client *http.Client) (gitlab
}
type userInfo struct {
- Groups []string
+ Groups []string `json:"groups"`
+ OwnerPermission []string `json:"https://gitlab.org/claims/groups/owner"`
+ MaintainerPermission []string `json:"https://gitlab.org/claims/groups/maintainer"`
+ DeveloperPermission []string `json:"https://gitlab.org/claims/groups/developer"`
}
// userGroups queries the GitLab API for group membership.
@@ -287,9 +295,62 @@ func (c *gitlabConnector) userGroups(ctx context.Context, client *http.Client) (
return nil, fmt.Errorf("failed to decode response: %v", err)
}
+ if c.getGroupsPermission {
+ groups := c.setGroupsPermission(u)
+ return groups, nil
+ }
+
return u.Groups, nil
}
+func (c *gitlabConnector) setGroupsPermission(u userInfo) []string {
+ groups := u.Groups
+
+L1:
+ for _, g := range groups {
+ for _, op := range u.OwnerPermission {
+ if g == op {
+ groups = append(groups, fmt.Sprintf("%s:owner", g))
+ continue L1
+ }
+ if len(g) > len(op) {
+ if g[0:len(op)] == op && string(g[len(op)]) == "/" {
+ groups = append(groups, fmt.Sprintf("%s:owner", g))
+ continue L1
+ }
+ }
+ }
+
+ for _, mp := range u.MaintainerPermission {
+ if g == mp {
+ groups = append(groups, fmt.Sprintf("%s:maintainer", g))
+ continue L1
+ }
+ if len(g) > len(mp) {
+ if g[0:len(mp)] == mp && string(g[len(mp)]) == "/" {
+ groups = append(groups, fmt.Sprintf("%s:maintainer", g))
+ continue L1
+ }
+ }
+ }
+
+ for _, dp := range u.DeveloperPermission {
+ if g == dp {
+ groups = append(groups, fmt.Sprintf("%s:developer", g))
+ continue L1
+ }
+ if len(g) > len(dp) {
+ if g[0:len(dp)] == dp && string(g[len(dp)]) == "/" {
+ groups = append(groups, fmt.Sprintf("%s:developer", g))
+ continue L1
+ }
+ }
+ }
+ }
+
+ return groups
+}
+
func (c *gitlabConnector) getGroups(ctx context.Context, client *http.Client, groupScope bool, userLogin string) ([]string, error) {
gitlabGroups, err := c.userGroups(ctx, client)
if err != nil {
diff --git a/connector/gitlab/gitlab_test.go b/connector/gitlab/gitlab_test.go
index d828b8bd16..b67b30c045 100644
--- a/connector/gitlab/gitlab_test.go
+++ b/connector/gitlab/gitlab_test.go
@@ -249,6 +249,47 @@ func TestRefreshWithEmptyConnectorData(t *testing.T) {
expectEquals(t, emptyIdentity, identity)
}
+func TestGroupsWithPermission(t *testing.T) {
+ s := newTestServer(map[string]interface{}{
+ "/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs", Username: "joebloggs"},
+ "/oauth/token": map[string]interface{}{
+ "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
+ "expires_in": "30",
+ },
+ "/oauth/userinfo": userInfo{
+ Groups: []string{"ops", "dev", "ops-test", "ops/project", "dev/project1", "dev/project2"},
+ OwnerPermission: []string{"ops"},
+ DeveloperPermission: []string{"dev"},
+ MaintainerPermission: []string{"dev/project1"},
+ },
+ })
+ defer s.Close()
+
+ hostURL, err := url.Parse(s.URL)
+ expectNil(t, err)
+
+ req, err := http.NewRequest("GET", hostURL.String(), nil)
+ expectNil(t, err)
+
+ c := gitlabConnector{baseURL: s.URL, httpClient: newClient(), getGroupsPermission: true}
+ identity, err := c.HandleCallback(connector.Scopes{Groups: true}, req)
+ expectNil(t, err)
+
+ expectEquals(t, identity.Groups, []string{
+ "ops",
+ "dev",
+ "ops-test",
+ "ops/project",
+ "dev/project1",
+ "dev/project2",
+ "ops:owner",
+ "dev:developer",
+ "ops/project:owner",
+ "dev/project1:maintainer",
+ "dev/project2:developer",
+ })
+}
+
func newTestServer(responses map[string]interface{}) *httptest.Server {
return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
response := responses[r.RequestURI]
diff --git a/connector/google/google.go b/connector/google/google.go
index 72cc6a18a5..e17ec5bd7f 100644
--- a/connector/google/google.go
+++ b/connector/google/google.go
@@ -5,23 +5,28 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"net/http"
"os"
+ "strings"
"time"
+ "cloud.google.com/go/compute/metadata"
"github.com/coreos/go-oidc/v3/oidc"
+ "golang.org/x/exp/slices"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
admin "google.golang.org/api/admin/directory/v1"
+ "google.golang.org/api/impersonate"
"google.golang.org/api/option"
"github.com/dexidp/dex/connector"
pkg_groups "github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
const (
- issuerURL = "https://accounts.google.com"
+ issuerURL = "https://accounts.google.com"
+ wildcardDomainToAdminEmail = "*"
)
// Config holds configuration options for Google logins.
@@ -45,17 +50,33 @@ type Config struct {
// check groups with the admin directory api
ServiceAccountFilePath string `json:"serviceAccountFilePath"`
+ // Deprecated: Use DomainToAdminEmail
+ AdminEmail string
+
// Required if ServiceAccountFilePath
- // The email of a GSuite super user which the service account will impersonate
+ // The map workspace domain to email of a GSuite super user which the service account will impersonate
// when listing groups
- AdminEmail string
+ DomainToAdminEmail map[string]string
// If this field is true, fetch direct group membership and transitive group membership
FetchTransitiveGroupMembership bool `json:"fetchTransitiveGroupMembership"`
+
+ // Optional value for the prompt parameter, defaults to consent when offline_access
+ // scope is requested
+ PromptType *string `json:"promptType"`
}
// Open returns a connector which can be used to login users through Google.
-func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
+func (c *Config) Open(id string, logger *slog.Logger) (conn connector.Connector, err error) {
+ logger = logger.With(slog.Group("connector", "type", "google", "id", id))
+ if c.AdminEmail != "" {
+ logger.Warn(`use "domainToAdminEmail.*" option instead of "adminEmail"`, "deprecated", true)
+ if c.DomainToAdminEmail == nil {
+ c.DomainToAdminEmail = make(map[string]string)
+ }
+
+ c.DomainToAdminEmail[wildcardDomainToAdminEmail] = c.AdminEmail
+ }
ctx, cancel := context.WithCancel(context.Background())
provider, err := oidc.NewProvider(ctx, issuerURL)
@@ -71,10 +92,30 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
scopes = append(scopes, "profile", "email")
}
- srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail, logger)
- if err != nil {
+ adminSrv := make(map[string]*admin.Service)
+
+ // We know impersonation is required when using a service account credential
+ // TODO: or is it?
+ if len(c.DomainToAdminEmail) == 0 && c.ServiceAccountFilePath != "" {
cancel()
- return nil, fmt.Errorf("could not create directory service: %v", err)
+ return nil, fmt.Errorf("directory service requires the domainToAdminEmail option to be configured")
+ }
+
+ if (len(c.DomainToAdminEmail) > 0) || slices.Contains(scopes, "groups") {
+ for domain, adminEmail := range c.DomainToAdminEmail {
+ srv, err := createDirectoryService(c.ServiceAccountFilePath, adminEmail, logger)
+ if err != nil {
+ cancel()
+ return nil, fmt.Errorf("could not create directory service: %v", err)
+ }
+
+ adminSrv[domain] = srv
+ }
+ }
+
+ promptType := "consent"
+ if c.PromptType != nil {
+ promptType = *c.PromptType
}
clientID := c.ClientID
@@ -95,9 +136,10 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
hostedDomains: c.HostedDomains,
groups: c.Groups,
serviceAccountFilePath: c.ServiceAccountFilePath,
- adminEmail: c.AdminEmail,
+ domainToAdminEmail: c.DomainToAdminEmail,
fetchTransitiveGroupMembership: c.FetchTransitiveGroupMembership,
- adminSrv: srv,
+ adminSrv: adminSrv,
+ promptType: promptType,
}, nil
}
@@ -111,13 +153,14 @@ type googleConnector struct {
oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier
cancel context.CancelFunc
- logger log.Logger
+ logger *slog.Logger
hostedDomains []string
groups []string
serviceAccountFilePath string
- adminEmail string
+ domainToAdminEmail map[string]string
fetchTransitiveGroupMembership bool
- adminSrv *admin.Service
+ adminSrv map[string]*admin.Service
+ promptType string
}
func (c *googleConnector) Close() error {
@@ -140,8 +183,9 @@ func (c *googleConnector) LoginURL(s connector.Scopes, callbackURL, state string
}
if s.OfflineAccess {
- opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent"))
+ opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", c.promptType))
}
+
return c.oauth2Config.AuthCodeURL(state, opts...), nil
}
@@ -218,8 +262,9 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector
}
var groups []string
- if s.Groups && c.adminSrv != nil {
- groups, err = c.getGroups(claims.Email, c.fetchTransitiveGroupMembership)
+ if s.Groups && len(c.adminSrv) > 0 {
+ checkedGroups := make(map[string]struct{})
+ groups, err = c.getGroups(claims.Email, c.fetchTransitiveGroupMembership, checkedGroups)
if err != nil {
return identity, fmt.Errorf("google: could not retrieve groups: %v", err)
}
@@ -245,30 +290,43 @@ func (c *googleConnector) createIdentity(ctx context.Context, identity connector
// getGroups creates a connection to the admin directory service and lists
// all groups the user is a member of
-func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership bool) ([]string, error) {
+func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership bool, checkedGroups map[string]struct{}) ([]string, error) {
var userGroups []string
var err error
groupsList := &admin.Groups{}
+ domain := c.extractDomainFromEmail(email)
+ adminSrv, err := c.findAdminService(domain)
+ if err != nil {
+ return nil, err
+ }
+
for {
- groupsList, err = c.adminSrv.Groups.List().
+ groupsList, err = adminSrv.Groups.List().
UserKey(email).PageToken(groupsList.NextPageToken).Do()
if err != nil {
return nil, fmt.Errorf("could not list groups: %v", err)
}
for _, group := range groupsList.Groups {
+ if _, exists := checkedGroups[group.Email]; exists {
+ continue
+ }
+
+ checkedGroups[group.Email] = struct{}{}
// TODO (joelspeed): Make desired group key configurable
userGroups = append(userGroups, group.Email)
- // getGroups takes a user's email/alias as well as a group's email/alias
- if fetchTransitiveGroupMembership {
- transitiveGroups, err := c.getGroups(group.Email, fetchTransitiveGroupMembership)
- if err != nil {
- return nil, fmt.Errorf("could not list transitive groups: %v", err)
- }
+ if !fetchTransitiveGroupMembership {
+ continue
+ }
- userGroups = append(userGroups, transitiveGroups...)
+ // getGroups takes a user's email/alias as well as a group's email/alias
+ transitiveGroups, err := c.getGroups(group.Email, fetchTransitiveGroupMembership, checkedGroups)
+ if err != nil {
+ return nil, fmt.Errorf("could not list transitive groups: %v", err)
}
+
+ userGroups = append(userGroups, transitiveGroups...)
}
if groupsList.NextPageToken == "" {
@@ -276,51 +334,124 @@ func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership
}
}
- return uniqueGroups(userGroups), nil
+ return userGroups, nil
+}
+
+func (c *googleConnector) findAdminService(domain string) (*admin.Service, error) {
+ adminSrv, ok := c.adminSrv[domain]
+ if !ok {
+ adminSrv, ok = c.adminSrv[wildcardDomainToAdminEmail]
+ c.logger.Debug("using wildcard admin email to fetch groups", "admin_email", c.domainToAdminEmail[wildcardDomainToAdminEmail])
+ }
+
+ if !ok {
+ return nil, fmt.Errorf("unable to find super admin email, domainToAdminEmail for domain: %s not set, %s is also empty", domain, wildcardDomainToAdminEmail)
+ }
+
+ return adminSrv, nil
+}
+
+// extracts the domain name from an email input. If the email is valid, it returns the domain name after the "@" symbol.
+// However, in the case of a broken or invalid email, it returns a wildcard symbol.
+func (c *googleConnector) extractDomainFromEmail(email string) string {
+ at := strings.LastIndex(email, "@")
+ if at >= 0 {
+ _, domain := email[:at], email[at+1:]
+
+ return domain
+ }
+
+ return wildcardDomainToAdminEmail
+}
+
+// getCredentialsFromFilePath reads and returns the service account credentials from the file at the provided path.
+// If an error occurs during the read, it is returned.
+func getCredentialsFromFilePath(serviceAccountFilePath string) ([]byte, error) {
+ jsonCredentials, err := os.ReadFile(serviceAccountFilePath)
+ if err != nil {
+ return nil, fmt.Errorf("error reading credentials from file: %v", err)
+ }
+ return jsonCredentials, nil
+}
+
+// getCredentialsFromDefault retrieves the application's default credentials.
+// If the default credential is empty, it attempts to create a new service with metadata credentials.
+// If successful, it returns the service and nil error.
+// If unsuccessful, it returns the error and a nil service.
+func getCredentialsFromDefault(ctx context.Context, email string, logger *slog.Logger) ([]byte, *admin.Service, error) {
+ credential, err := google.FindDefaultCredentials(ctx)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to fetch application default credentials: %w", err)
+ }
+
+ if credential.JSON == nil {
+ logger.Info("JSON is empty, using flow for GCE")
+ service, err := createServiceWithMetadataServer(ctx, email, logger)
+ if err != nil {
+ return nil, nil, err
+ }
+ return nil, service, nil
+ }
+
+ return credential.JSON, nil, nil
+}
+
+// createServiceWithMetadataServer creates a new service using metadata server.
+// If an error occurs during the process, it is returned along with a nil service.
+func createServiceWithMetadataServer(ctx context.Context, adminEmail string, logger *slog.Logger) (*admin.Service, error) {
+ serviceAccountEmail, err := metadata.EmailWithContext(ctx, "default")
+ logger.Info("discovered serviceAccountEmail", "email", serviceAccountEmail)
+
+ if err != nil {
+ return nil, fmt.Errorf("unable to get service account email from metadata server: %v", err)
+ }
+
+ config := impersonate.CredentialsConfig{
+ TargetPrincipal: serviceAccountEmail,
+ Scopes: []string{admin.AdminDirectoryGroupReadonlyScope},
+ Lifetime: 0,
+ Subject: adminEmail,
+ }
+
+ tokenSource, err := impersonate.CredentialsTokenSource(ctx, config)
+ if err != nil {
+ return nil, fmt.Errorf("unable to impersonate with %s, error: %v", adminEmail, err)
+ }
+
+ return admin.NewService(ctx, option.WithHTTPClient(oauth2.NewClient(ctx, tokenSource)))
}
// createDirectoryService sets up super user impersonation and creates an admin client for calling
// the google admin api. If no serviceAccountFilePath is defined, the application default credential
// is used.
-func createDirectoryService(serviceAccountFilePath, email string, logger log.Logger) (*admin.Service, error) {
- if email == "" {
- return nil, fmt.Errorf("directory service requires adminEmail")
- }
-
+func createDirectoryService(serviceAccountFilePath, email string, logger *slog.Logger) (service *admin.Service, err error) {
var jsonCredentials []byte
- var err error
ctx := context.Background()
if serviceAccountFilePath == "" {
logger.Warn("the application default credential is used since the service account file path is not used")
- credential, err := google.FindDefaultCredentials(ctx)
+ jsonCredentials, service, err = getCredentialsFromDefault(ctx, email, logger)
if err != nil {
- return nil, fmt.Errorf("failed to fetch application default credentials: %w", err)
+ return
+ }
+ if service != nil {
+ return
}
- jsonCredentials = credential.JSON
} else {
- jsonCredentials, err = os.ReadFile(serviceAccountFilePath)
+ jsonCredentials, err = getCredentialsFromFilePath(serviceAccountFilePath)
if err != nil {
- return nil, fmt.Errorf("error reading credentials from file: %v", err)
+ return
}
}
config, err := google.JWTConfigFromJSON(jsonCredentials, admin.AdminDirectoryGroupReadonlyScope)
if err != nil {
- return nil, fmt.Errorf("unable to parse credentials to config: %v", err)
+ return nil, fmt.Errorf("unable to parse client secret file to config: %v", err)
}
- config.Subject = email
- return admin.NewService(ctx, option.WithHTTPClient(config.Client(ctx)))
-}
-// uniqueGroups returns the unique groups of a slice
-func uniqueGroups(groups []string) []string {
- keys := make(map[string]struct{})
- unique := []string{}
- for _, group := range groups {
- if _, exists := keys[group]; !exists {
- keys[group] = struct{}{}
- unique = append(unique, group)
- }
+ // Only attempt impersonation when there is a user configured
+ if email != "" {
+ config.Subject = email
}
- return unique
+
+ return admin.NewService(ctx, option.WithHTTPClient(config.Client(ctx)))
}
diff --git a/connector/google/google_test.go b/connector/google/google_test.go
index 5cecbec994..8cc7973969 100644
--- a/connector/google/google_test.go
+++ b/connector/google/google_test.go
@@ -1,31 +1,57 @@
package google
import (
+ "context"
"encoding/json"
"fmt"
+ "log/slog"
"net/http"
"net/http/httptest"
+ "net/url"
"os"
+ "strings"
"testing"
- "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
+ admin "google.golang.org/api/admin/directory/v1"
+ "google.golang.org/api/option"
+
+ "github.com/dexidp/dex/connector"
+)
+
+var (
+ // groups_0
+ // âââââââââ¤
+ // groups_2 groups_1
+ // â ââââââââââ
+ // âââ user_1 user_2
+ testGroups = map[string][]*admin.Group{
+ "user_1@dexidp.com": {{Email: "groups_2@dexidp.com"}, {Email: "groups_1@dexidp.com"}},
+ "user_2@dexidp.com": {{Email: "groups_1@dexidp.com"}},
+ "groups_1@dexidp.com": {{Email: "groups_0@dexidp.com"}},
+ "groups_2@dexidp.com": {{Email: "groups_0@dexidp.com"}},
+ "groups_0@dexidp.com": {},
+ }
+ callCounter = make(map[string]int)
)
-func testSetup(t *testing.T) *httptest.Server {
+func testSetup() *httptest.Server {
mux := http.NewServeMux()
- // TODO: mock calls
- // mux.HandleFunc("/admin/directory/v1/groups", func(w http.ResponseWriter, r *http.Request) {
- // w.Header().Add("Content-Type", "application/json")
- // json.NewEncoder(w).Encode(&admin.Groups{
- // Groups: []*admin.Group{},
- // })
- // })
+
+ mux.HandleFunc("/admin/directory/v1/groups/", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", "application/json")
+ userKey := r.URL.Query().Get("userKey")
+ if groups, ok := testGroups[userKey]; ok {
+ json.NewEncoder(w).Encode(admin.Groups{Groups: groups})
+ callCounter[userKey]++
+ }
+ })
+
return httptest.NewServer(mux)
}
-func newConnector(config *Config, serverURL string) (*googleConnector, error) {
- log := logrus.New()
+func newConnector(config *Config) (*googleConnector, error) {
+ log := slog.New(slog.DiscardHandler)
conn, err := config.Open("id", log)
if err != nil {
return nil, err
@@ -56,7 +82,7 @@ func tempServiceAccountKey() (string, error) {
}
func TestOpen(t *testing.T) {
- ts := testSetup(t)
+ ts := testSetup()
defer ts.Close()
type testCase struct {
@@ -64,7 +90,7 @@ func TestOpen(t *testing.T) {
expectedErr string
// string to set in GOOGLE_APPLICATION_CREDENTIALS. As local development environments can
- // already contain ADC, test cases will be built uppon this setting this env variable
+ // already contain ADC, test cases will be built upon this setting this env variable
adc string
}
@@ -74,12 +100,13 @@ func TestOpen(t *testing.T) {
for name, reference := range map[string]testCase{
"missing_admin_email": {
config: &Config{
- ClientID: "testClient",
- ClientSecret: "testSecret",
- RedirectURI: ts.URL + "/callback",
- Scopes: []string{"openid", "groups"},
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ ServiceAccountFilePath: serviceAccountFilePath,
},
- expectedErr: "requires adminEmail",
+ expectedErr: "requires the domainToAdminEmail",
},
"service_account_key_not_found": {
config: &Config{
@@ -87,7 +114,7 @@ func TestOpen(t *testing.T) {
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
- AdminEmail: "foo@bar.com",
+ DomainToAdminEmail: map[string]string{"*": "foo@bar.com"},
ServiceAccountFilePath: "not_found.json",
},
expectedErr: "error reading credentials",
@@ -98,18 +125,18 @@ func TestOpen(t *testing.T) {
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
- AdminEmail: "foo@bar.com",
+ DomainToAdminEmail: map[string]string{"bar.com": "foo@bar.com"},
ServiceAccountFilePath: serviceAccountFilePath,
},
expectedErr: "",
},
"adc": {
config: &Config{
- ClientID: "testClient",
- ClientSecret: "testSecret",
- RedirectURI: ts.URL + "/callback",
- Scopes: []string{"openid", "groups"},
- AdminEmail: "foo@bar.com",
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ DomainToAdminEmail: map[string]string{"*": "foo@bar.com"},
},
adc: serviceAccountFilePath,
expectedErr: "",
@@ -120,7 +147,7 @@ func TestOpen(t *testing.T) {
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
- AdminEmail: "foo@bar.com",
+ DomainToAdminEmail: map[string]string{"*": "foo@bar.com"},
ServiceAccountFilePath: serviceAccountFilePath,
},
adc: "/dev/null",
@@ -132,7 +159,7 @@ func TestOpen(t *testing.T) {
assert := assert.New(t)
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", reference.adc)
- conn, err := newConnector(reference.config, ts.URL)
+ conn, err := newConnector(reference.config)
if reference.expectedErr == "" {
assert.Nil(err)
@@ -143,3 +170,282 @@ func TestOpen(t *testing.T) {
})
}
}
+
+func TestGetGroups(t *testing.T) {
+ ts := testSetup()
+ defer ts.Close()
+
+ serviceAccountFilePath, err := tempServiceAccountKey()
+ assert.Nil(t, err)
+
+ os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFilePath)
+ conn, err := newConnector(&Config{
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ DomainToAdminEmail: map[string]string{"*": "admin@dexidp.com"},
+ })
+ assert.Nil(t, err)
+
+ conn.adminSrv[wildcardDomainToAdminEmail], err = admin.NewService(context.Background(), option.WithoutAuthentication(), option.WithEndpoint(ts.URL))
+ assert.Nil(t, err)
+ type testCase struct {
+ userKey string
+ fetchTransitiveGroupMembership bool
+ shouldErr bool
+ expectedGroups []string
+ }
+
+ for name, testCase := range map[string]testCase{
+ "user1_non_transitive_lookup": {
+ userKey: "user_1@dexidp.com",
+ fetchTransitiveGroupMembership: false,
+ shouldErr: false,
+ expectedGroups: []string{"groups_1@dexidp.com", "groups_2@dexidp.com"},
+ },
+ "user1_transitive_lookup": {
+ userKey: "user_1@dexidp.com",
+ fetchTransitiveGroupMembership: true,
+ shouldErr: false,
+ expectedGroups: []string{"groups_0@dexidp.com", "groups_1@dexidp.com", "groups_2@dexidp.com"},
+ },
+ "user2_non_transitive_lookup": {
+ userKey: "user_2@dexidp.com",
+ fetchTransitiveGroupMembership: false,
+ shouldErr: false,
+ expectedGroups: []string{"groups_1@dexidp.com"},
+ },
+ "user2_transitive_lookup": {
+ userKey: "user_2@dexidp.com",
+ fetchTransitiveGroupMembership: true,
+ shouldErr: false,
+ expectedGroups: []string{"groups_0@dexidp.com", "groups_1@dexidp.com"},
+ },
+ } {
+ testCase := testCase
+ callCounter = map[string]int{}
+ t.Run(name, func(t *testing.T) {
+ assert := assert.New(t)
+ lookup := make(map[string]struct{})
+
+ groups, err := conn.getGroups(testCase.userKey, testCase.fetchTransitiveGroupMembership, lookup)
+ if testCase.shouldErr {
+ assert.NotNil(err)
+ } else {
+ assert.Nil(err)
+ }
+ assert.ElementsMatch(testCase.expectedGroups, groups)
+ t.Logf("[%s] Amount of API calls per userKey: %+v\n", t.Name(), callCounter)
+ })
+ }
+}
+
+func TestDomainToAdminEmailConfig(t *testing.T) {
+ ts := testSetup()
+ defer ts.Close()
+
+ serviceAccountFilePath, err := tempServiceAccountKey()
+ assert.Nil(t, err)
+
+ os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFilePath)
+ conn, err := newConnector(&Config{
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ DomainToAdminEmail: map[string]string{"dexidp.com": "admin@dexidp.com"},
+ })
+ assert.Nil(t, err)
+
+ conn.adminSrv["dexidp.com"], err = admin.NewService(context.Background(), option.WithoutAuthentication(), option.WithEndpoint(ts.URL))
+ assert.Nil(t, err)
+ type testCase struct {
+ userKey string
+ expectedErr string
+ }
+
+ for name, testCase := range map[string]testCase{
+ "correct_user_request": {
+ userKey: "user_1@dexidp.com",
+ expectedErr: "",
+ },
+ "wrong_user_request": {
+ userKey: "user_1@foo.bar",
+ expectedErr: "unable to find super admin email",
+ },
+ "wrong_connector_response": {
+ userKey: "user_1_foo.bar",
+ expectedErr: "unable to find super admin email",
+ },
+ } {
+ testCase := testCase
+ callCounter = map[string]int{}
+ t.Run(name, func(t *testing.T) {
+ assert := assert.New(t)
+ lookup := make(map[string]struct{})
+
+ _, err := conn.getGroups(testCase.userKey, true, lookup)
+ if testCase.expectedErr != "" {
+ assert.ErrorContains(err, testCase.expectedErr)
+ } else {
+ assert.Nil(err)
+ }
+ t.Logf("[%s] Amount of API calls per userKey: %+v\n", t.Name(), callCounter)
+ })
+ }
+}
+
+var gceMetadataFlags = map[string]bool{
+ "failOnEmailRequest": false,
+}
+
+func mockGCEMetadataServer() *httptest.Server {
+ mux := http.NewServeMux()
+
+ mux.HandleFunc("/computeMetadata/v1/instance/service-accounts/default/email", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", "application/json")
+ if gceMetadataFlags["failOnEmailRequest"] {
+ w.WriteHeader(http.StatusBadRequest)
+ }
+ json.NewEncoder(w).Encode("my-service-account@example-project.iam.gserviceaccount.com")
+ })
+ mux.HandleFunc("/computeMetadata/v1/instance/service-accounts/default/token", func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Add("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(struct {
+ AccessToken string `json:"access_token"`
+ ExpiresInSec int `json:"expires_in"`
+ TokenType string `json:"token_type"`
+ }{
+ AccessToken: "my-example.token",
+ ExpiresInSec: 3600,
+ TokenType: "Bearer",
+ })
+ })
+
+ return httptest.NewServer(mux)
+}
+
+func TestGCEWorkloadIdentity(t *testing.T) {
+ ts := testSetup()
+ defer ts.Close()
+
+ metadataServer := mockGCEMetadataServer()
+ defer metadataServer.Close()
+ metadataServerHost := strings.Replace(metadataServer.URL, "http://", "", 1)
+
+ os.Setenv("GCE_METADATA_HOST", metadataServerHost)
+ os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", "")
+ os.Setenv("HOME", "/tmp")
+
+ gceMetadataFlags["failOnEmailRequest"] = true
+ _, err := newConnector(&Config{
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ DomainToAdminEmail: map[string]string{"dexidp.com": "admin@dexidp.com"},
+ })
+ assert.Error(t, err)
+
+ gceMetadataFlags["failOnEmailRequest"] = false
+ conn, err := newConnector(&Config{
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups"},
+ DomainToAdminEmail: map[string]string{"dexidp.com": "admin@dexidp.com"},
+ })
+ assert.Nil(t, err)
+
+ conn.adminSrv["dexidp.com"], err = admin.NewService(context.Background(), option.WithoutAuthentication(), option.WithEndpoint(ts.URL))
+ assert.Nil(t, err)
+ type testCase struct {
+ userKey string
+ expectedErr string
+ }
+
+ for name, testCase := range map[string]testCase{
+ "correct_user_request": {
+ userKey: "user_1@dexidp.com",
+ expectedErr: "",
+ },
+ "wrong_user_request": {
+ userKey: "user_1@foo.bar",
+ expectedErr: "unable to find super admin email",
+ },
+ "wrong_connector_response": {
+ userKey: "user_1_foo.bar",
+ expectedErr: "unable to find super admin email",
+ },
+ } {
+ t.Run(name, func(t *testing.T) {
+ assert := assert.New(t)
+ lookup := make(map[string]struct{})
+
+ _, err := conn.getGroups(testCase.userKey, true, lookup)
+ if testCase.expectedErr != "" {
+ assert.ErrorContains(err, testCase.expectedErr)
+ } else {
+ assert.Nil(err)
+ }
+ })
+ }
+}
+
+func TestPromptTypeConfig(t *testing.T) {
+ promptTypeLogin := "login"
+ cases := []struct {
+ name string
+ promptType *string
+ expectedPromptTypeValue string
+ }{
+ {
+ name: "prompt type is nil",
+ promptType: nil,
+ expectedPromptTypeValue: "consent",
+ },
+ {
+ name: "prompt type is empty",
+ promptType: new(string),
+ expectedPromptTypeValue: "",
+ },
+ {
+ name: "prompt type is set",
+ promptType: &promptTypeLogin,
+ expectedPromptTypeValue: "login",
+ },
+ }
+
+ ts := testSetup()
+ defer ts.Close()
+
+ serviceAccountFilePath, err := tempServiceAccountKey()
+ assert.Nil(t, err)
+
+ os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", serviceAccountFilePath)
+
+ for _, test := range cases {
+ t.Run(test.name, func(t *testing.T) {
+ conn, err := newConnector(&Config{
+ ClientID: "testClient",
+ ClientSecret: "testSecret",
+ RedirectURI: ts.URL + "/callback",
+ Scopes: []string{"openid", "groups", "offline_access"},
+ DomainToAdminEmail: map[string]string{"dexidp.com": "admin@dexidp.com"},
+ PromptType: test.promptType,
+ })
+
+ assert.Nil(t, err)
+ assert.Equal(t, test.expectedPromptTypeValue, conn.promptType)
+
+ loginURL, err := conn.LoginURL(connector.Scopes{OfflineAccess: true}, ts.URL+"/callback", "state")
+ assert.Nil(t, err)
+
+ urlp, err := url.Parse(loginURL)
+ assert.Nil(t, err)
+
+ assert.Equal(t, test.expectedPromptTypeValue, urlp.Query().Get("prompt"))
+ })
+ }
+}
diff --git a/connector/keystone/keystone.go b/connector/keystone/keystone.go
index db97b5a71f..cdfdb55894 100644
--- a/connector/keystone/keystone.go
+++ b/connector/keystone/keystone.go
@@ -7,18 +7,21 @@ import (
"encoding/json"
"fmt"
"io"
+ "log/slog"
"net/http"
+ "github.com/google/uuid"
+
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
type conn struct {
- Domain string
+ Domain domainKeystone
Host string
AdminUsername string
AdminPassword string
- Logger log.Logger
+ client *http.Client
+ Logger *slog.Logger
}
type userKeystone struct {
@@ -28,13 +31,14 @@ type userKeystone struct {
}
type domainKeystone struct {
- ID string `json:"id"`
- Name string `json:"name"`
+ ID string `json:"id,omitempty"`
+ Name string `json:"name,omitempty"`
}
// Config holds the configuration parameters for Keystone connector.
// Keystone should expose API v3
// An example config:
+//
// connectors:
// type: keystone
// id: keystone
@@ -69,13 +73,9 @@ type password struct {
}
type user struct {
- Name string `json:"name"`
- Domain domain `json:"domain"`
- Password string `json:"password"`
-}
-
-type domain struct {
- ID string `json:"id"`
+ Name string `json:"name"`
+ Domain domainKeystone `json:"domain"`
+ Password string `json:"password"`
}
type token struct {
@@ -109,13 +109,28 @@ var (
)
// Open returns an authentication strategy using Keystone.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
+ _, err := uuid.Parse(c.Domain)
+ var domain domainKeystone
+ // check if the supplied domain is a UUID or the special "default" value
+ // which is treated as an ID and not a name
+ if err == nil || c.Domain == "default" {
+ domain = domainKeystone{
+ ID: c.Domain,
+ }
+ } else {
+ domain = domainKeystone{
+ Name: c.Domain,
+ }
+ }
+
return &conn{
- c.Domain,
- c.Host,
- c.AdminUsername,
- c.AdminPassword,
- logger,
+ Domain: domain,
+ Host: c.Host,
+ AdminUsername: c.AdminUsername,
+ AdminPassword: c.AdminPassword,
+ Logger: logger.With(slog.Group("connector", "type", "keystone", "id", id)),
+ client: http.DefaultClient,
}, nil
}
@@ -192,7 +207,6 @@ func (p *conn) Refresh(
}
func (p *conn) getTokenResponse(ctx context.Context, username, pass string) (response *http.Response, err error) {
- client := &http.Client{}
jsonData := loginRequestData{
auth: auth{
Identity: identity{
@@ -200,7 +214,7 @@ func (p *conn) getTokenResponse(ctx context.Context, username, pass string) (res
Password: password{
User: user{
Name: username,
- Domain: domain{ID: p.Domain},
+ Domain: p.Domain,
Password: pass,
},
},
@@ -221,7 +235,7 @@ func (p *conn) getTokenResponse(ctx context.Context, username, pass string) (res
req.Header.Set("Content-Type", "application/json")
req = req.WithContext(ctx)
- return client.Do(req)
+ return p.client.Do(req)
}
func (p *conn) getAdminToken(ctx context.Context) (string, error) {
@@ -243,7 +257,6 @@ func (p *conn) checkIfUserExists(ctx context.Context, userID string, token strin
func (p *conn) getUser(ctx context.Context, userID string, token string) (*userResponse, error) {
// https://developer.openstack.org/api-ref/identity/v3/#show-user-details
userURL := p.Host + "/v3/users/" + userID
- client := &http.Client{}
req, err := http.NewRequest("GET", userURL, nil)
if err != nil {
return nil, err
@@ -251,7 +264,7 @@ func (p *conn) getUser(ctx context.Context, userID string, token string) (*userR
req.Header.Set("X-Auth-Token", token)
req = req.WithContext(ctx)
- resp, err := client.Do(req)
+ resp, err := p.client.Do(req)
if err != nil {
return nil, err
}
@@ -276,7 +289,6 @@ func (p *conn) getUser(ctx context.Context, userID string, token string) (*userR
}
func (p *conn) getUserGroups(ctx context.Context, userID string, token string) ([]string, error) {
- client := &http.Client{}
// https://developer.openstack.org/api-ref/identity/v3/#list-groups-to-which-a-user-belongs
groupsURL := p.Host + "/v3/users/" + userID + "/groups"
req, err := http.NewRequest("GET", groupsURL, nil)
@@ -285,9 +297,9 @@ func (p *conn) getUserGroups(ctx context.Context, userID string, token string) (
}
req.Header.Set("X-Auth-Token", token)
req = req.WithContext(ctx)
- resp, err := client.Do(req)
+ resp, err := p.client.Do(req)
if err != nil {
- p.Logger.Errorf("keystone: error while fetching user %q groups\n", userID)
+ p.Logger.Error("error while fetching user groups", "user_id", userID, "err", err)
return nil, err
}
diff --git a/connector/keystone/keystone_test.go b/connector/keystone/keystone_test.go
index fc6c01e229..9b0590df12 100644
--- a/connector/keystone/keystone_test.go
+++ b/connector/keystone/keystone_test.go
@@ -17,11 +17,13 @@ import (
const (
invalidPass = "WRONG_PASS"
- testUser = "test_user"
- testPass = "test_pass"
- testEmail = "test@example.com"
- testGroup = "test_group"
- testDomain = "default"
+ testUser = "test_user"
+ testPass = "test_pass"
+ testEmail = "test@example.com"
+ testGroup = "test_group"
+ testDomainAltName = "altdomain"
+ testDomainID = "default"
+ testDomainName = "Default"
)
var (
@@ -32,8 +34,26 @@ var (
authTokenURL = ""
usersURL = ""
groupsURL = ""
+ domainsURL = ""
)
+type userReq struct {
+ Name string `json:"name"`
+ Email string `json:"email"`
+ Enabled bool `json:"enabled"`
+ Password string `json:"password"`
+ Roles []string `json:"roles"`
+ DomainID string `json:"domain_id,omitempty"`
+}
+
+type domainResponse struct {
+ Domain domainKeystone `json:"domain"`
+}
+
+type domainsResponse struct {
+ Domains []domainKeystone `json:"domains"`
+}
+
type groupResponse struct {
Group struct {
ID string `json:"id"`
@@ -42,8 +62,6 @@ type groupResponse struct {
func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string) {
t.Helper()
- client := &http.Client{}
-
jsonData := loginRequestData{
auth: auth{
Identity: identity{
@@ -51,7 +69,7 @@ func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string)
Password: password{
User: user{
Name: adminName,
- Domain: domain{ID: testDomain},
+ Domain: domainKeystone{ID: testDomainID},
Password: adminPass,
},
},
@@ -70,7 +88,7 @@ func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string)
}
req.Header.Set("Content-Type", "application/json")
- resp, err := client.Do(req)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -91,17 +109,91 @@ func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string)
return token, tokenResp.Token.User.ID
}
-func createUser(t *testing.T, token, userName, userEmail, userPass string) string {
+func getOrCreateDomain(t *testing.T, token, domainName string) string {
+ t.Helper()
+
+ domainSearchURL := domainsURL + "?name=" + domainName
+ reqGet, err := http.NewRequest("GET", domainSearchURL, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ reqGet.Header.Set("X-Auth-Token", token)
+ reqGet.Header.Add("Content-Type", "application/json")
+ respGet, err := http.DefaultClient.Do(reqGet)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ dataGet, err := io.ReadAll(respGet.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer respGet.Body.Close()
+
+ domainsResp := new(domainsResponse)
+ err = json.Unmarshal(dataGet, &domainsResp)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if len(domainsResp.Domains) >= 1 {
+ return domainsResp.Domains[0].ID
+ }
+
+ createDomainData := map[string]interface{}{
+ "domain": map[string]interface{}{
+ "name": domainName,
+ "enabled": true,
+ },
+ }
+
+ body, err := json.Marshal(createDomainData)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ req, err := http.NewRequest("POST", domainsURL, bytes.NewBuffer(body))
+ if err != nil {
+ t.Fatal(err)
+ }
+ req.Header.Set("X-Auth-Token", token)
+ req.Header.Add("Content-Type", "application/json")
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if resp.StatusCode != 201 {
+ t.Fatalf("failed to create domain %s", domainName)
+ }
+
+ data, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer resp.Body.Close()
+
+ domainResp := new(domainResponse)
+ err = json.Unmarshal(data, &domainResp)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ return domainResp.Domain.ID
+}
+
+func createUser(t *testing.T, token, domainID, userName, userEmail, userPass string) string {
t.Helper()
- client := &http.Client{}
createUserData := map[string]interface{}{
- "user": map[string]interface{}{
- "name": userName,
- "email": userEmail,
- "enabled": true,
- "password": userPass,
- "roles": []string{"admin"},
+ "user": userReq{
+ DomainID: domainID,
+ Name: userName,
+ Email: userEmail,
+ Enabled: true,
+ Password: userPass,
+ Roles: []string{"admin"},
},
}
@@ -116,7 +208,7 @@ func createUser(t *testing.T, token, userName, userEmail, userPass string) strin
}
req.Header.Set("X-Auth-Token", token)
req.Header.Add("Content-Type", "application/json")
- resp, err := client.Do(req)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -139,7 +231,6 @@ func createUser(t *testing.T, token, userName, userEmail, userPass string) strin
// delete group or user
func deleteResource(t *testing.T, token, id, uri string) {
t.Helper()
- client := &http.Client{}
deleteURI := uri + id
req, err := http.NewRequest("DELETE", deleteURI, nil)
@@ -148,7 +239,7 @@ func deleteResource(t *testing.T, token, id, uri string) {
}
req.Header.Set("X-Auth-Token", token)
- resp, err := client.Do(req)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("error: %v", err)
}
@@ -157,7 +248,6 @@ func deleteResource(t *testing.T, token, id, uri string) {
func createGroup(t *testing.T, token, description, name string) string {
t.Helper()
- client := &http.Client{}
createGroupData := map[string]interface{}{
"group": map[string]interface{}{
@@ -177,7 +267,7 @@ func createGroup(t *testing.T, token, description, name string) string {
}
req.Header.Set("X-Auth-Token", token)
req.Header.Add("Content-Type", "application/json")
- resp, err := client.Do(req)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -200,14 +290,13 @@ func createGroup(t *testing.T, token, description, name string) string {
func addUserToGroup(t *testing.T, token, groupID, userID string) error {
t.Helper()
uri := groupsURL + groupID + "/users/" + userID
- client := &http.Client{}
req, err := http.NewRequest("PUT", uri, nil)
if err != nil {
return err
}
req.Header.Set("X-Auth-Token", token)
- resp, err := client.Do(req)
+ resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("error: %v", err)
}
@@ -219,7 +308,8 @@ func addUserToGroup(t *testing.T, token, groupID, userID string) error {
func TestIncorrectCredentialsLogin(t *testing.T) {
setupVariables(t)
c := conn{
- Host: keystoneURL, Domain: testDomain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: domainKeystone{ID: testDomainID},
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -243,10 +333,11 @@ func TestValidUserLogin(t *testing.T) {
token, _ := getAdminToken(t, adminUser, adminPass)
type tUser struct {
- username string
- domain string
- email string
- password string
+ createDomain bool
+ domain domainKeystone
+ username string
+ email string
+ password string
}
type expect struct {
@@ -263,10 +354,11 @@ func TestValidUserLogin(t *testing.T) {
{
name: "test with email address",
input: tUser{
- username: testUser,
- domain: testDomain,
- email: testEmail,
- password: testPass,
+ createDomain: false,
+ domain: domainKeystone{ID: testDomainID},
+ username: testUser,
+ email: testEmail,
+ password: testPass,
},
expected: expect{
username: testUser,
@@ -277,10 +369,11 @@ func TestValidUserLogin(t *testing.T) {
{
name: "test without email address",
input: tUser{
- username: testUser,
- domain: testDomain,
- email: "",
- password: testPass,
+ createDomain: false,
+ domain: domainKeystone{ID: testDomainID},
+ username: testUser,
+ email: "",
+ password: testPass,
},
expected: expect{
username: testUser,
@@ -288,21 +381,77 @@ func TestValidUserLogin(t *testing.T) {
verifiedEmail: false,
},
},
+ {
+ name: "test with default domain Name",
+ input: tUser{
+ createDomain: false,
+ domain: domainKeystone{Name: testDomainName},
+ username: testUser,
+ email: testEmail,
+ password: testPass,
+ },
+ expected: expect{
+ username: testUser,
+ email: testEmail,
+ verifiedEmail: true,
+ },
+ },
+ {
+ name: "test with custom domain Name",
+ input: tUser{
+ createDomain: true,
+ domain: domainKeystone{Name: testDomainAltName},
+ username: testUser,
+ email: testEmail,
+ password: testPass,
+ },
+ expected: expect{
+ username: testUser,
+ email: testEmail,
+ verifiedEmail: true,
+ },
+ },
+ {
+ name: "test with custom domain ID",
+ input: tUser{
+ createDomain: true,
+ domain: domainKeystone{},
+ username: testUser,
+ email: testEmail,
+ password: testPass,
+ },
+ expected: expect{
+ username: testUser,
+ email: testEmail,
+ verifiedEmail: true,
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- userID := createUser(t, token, tt.input.username, tt.input.email, tt.input.password)
+ domainID := ""
+ if tt.input.createDomain == true {
+ domainID = getOrCreateDomain(t, token, testDomainAltName)
+ t.Logf("getOrCreateDomain ID: %s\n", domainID)
+
+ // if there was nothing set then use the dynamically generated domain ID
+ if tt.input.domain.ID == "" && tt.input.domain.Name == "" {
+ tt.input.domain.ID = domainID
+ }
+ }
+ userID := createUser(t, token, domainID, tt.input.username, tt.input.email, tt.input.password)
defer deleteResource(t, token, userID, usersURL)
c := conn{
- Host: keystoneURL, Domain: tt.input.domain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: tt.input.domain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
identity, validPW, err := c.Login(context.Background(), s, tt.input.username, tt.input.password)
if err != nil {
- t.Fatal(err.Error())
+ t.Fatalf("Login failed for user %s: %v", tt.input.username, err.Error())
}
t.Log(identity)
if identity.Username != tt.expected.username {
@@ -333,7 +482,8 @@ func TestUseRefreshToken(t *testing.T) {
defer deleteResource(t, token, groupID, groupsURL)
c := conn{
- Host: keystoneURL, Domain: testDomain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: domainKeystone{ID: testDomainID},
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -355,10 +505,11 @@ func TestUseRefreshToken(t *testing.T) {
func TestUseRefreshTokenUserDeleted(t *testing.T) {
setupVariables(t)
token, _ := getAdminToken(t, adminUser, adminPass)
- userID := createUser(t, token, testUser, testEmail, testPass)
+ userID := createUser(t, token, "", testUser, testEmail, testPass)
c := conn{
- Host: keystoneURL, Domain: testDomain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: domainKeystone{ID: testDomainID},
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -384,11 +535,12 @@ func TestUseRefreshTokenUserDeleted(t *testing.T) {
func TestUseRefreshTokenGroupsChanged(t *testing.T) {
setupVariables(t)
token, _ := getAdminToken(t, adminUser, adminPass)
- userID := createUser(t, token, testUser, testEmail, testPass)
+ userID := createUser(t, token, "", testUser, testEmail, testPass)
defer deleteResource(t, token, userID, usersURL)
c := conn{
- Host: keystoneURL, Domain: testDomain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: domainKeystone{ID: testDomainID},
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -420,11 +572,12 @@ func TestUseRefreshTokenGroupsChanged(t *testing.T) {
func TestNoGroupsInScope(t *testing.T) {
setupVariables(t)
token, _ := getAdminToken(t, adminUser, adminPass)
- userID := createUser(t, token, testUser, testEmail, testPass)
+ userID := createUser(t, token, "", testUser, testEmail, testPass)
defer deleteResource(t, token, userID, usersURL)
c := conn{
- Host: keystoneURL, Domain: testDomain,
+ client: http.DefaultClient,
+ Host: keystoneURL, Domain: domainKeystone{ID: testDomainID},
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: false}
@@ -474,6 +627,7 @@ func setupVariables(t *testing.T) {
authTokenURL = keystoneURL + "/v3/auth/tokens/"
usersURL = keystoneAdminURL + "/v3/users/"
groupsURL = keystoneAdminURL + "/v3/groups/"
+ domainsURL = keystoneAdminURL + "/v3/domains/"
}
func expectEquals(t *testing.T, a interface{}, b interface{}) {
diff --git a/connector/ldap/ldap.go b/connector/ldap/ldap.go
index 543402718c..9fe386c6e5 100644
--- a/connector/ldap/ldap.go
+++ b/connector/ldap/ldap.go
@@ -7,13 +7,15 @@ import (
"crypto/x509"
"encoding/json"
"fmt"
+ "log/slog"
"net"
+ "net/url"
"os"
+ "strings"
"github.com/go-ldap/ldap/v3"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
// Config holds the configuration parameters for the LDAP connector. The LDAP
@@ -60,6 +62,8 @@ import (
type UserMatcher struct {
UserAttr string `json:"userAttr"`
GroupAttr string `json:"groupAttr"`
+ // Look for parent groups
+ RecursionGroupAttr string `json:"recursionGroupAttr"`
}
// Config holds configuration options for LDAP logins.
@@ -142,6 +146,8 @@ type Config struct {
UserAttr string `json:"userAttr"`
GroupAttr string `json:"groupAttr"`
+ RecursionGroupAttr string `json:"recursionGroupAttr"`
+
// Array of the field pairs used to match a user to a group.
// See the "UserMatcher" struct for the exact field names
//
@@ -187,22 +193,24 @@ func parseScope(s string) (int, bool) {
// Function exists here to allow backward compatibility between old and new
// group to user matching implementations.
// See "Config.GroupSearch.UserMatchers" comments for the details
-func userMatchers(c *Config, logger log.Logger) []UserMatcher {
+func userMatchers(c *Config, logger *slog.Logger) []UserMatcher {
if len(c.GroupSearch.UserMatchers) > 0 && c.GroupSearch.UserMatchers[0].UserAttr != "" {
return c.GroupSearch.UserMatchers
}
- log.Deprecated(logger, `LDAP: use groupSearch.userMatchers option instead of "userAttr/groupAttr" fields.`)
+ logger.Warn(`use "groupSearch.userMatchers" option instead of "userAttr/groupAttr" fields`, "deprecated", true)
return []UserMatcher{
{
- UserAttr: c.GroupSearch.UserAttr,
- GroupAttr: c.GroupSearch.GroupAttr,
+ UserAttr: c.GroupSearch.UserAttr,
+ GroupAttr: c.GroupSearch.GroupAttr,
+ RecursionGroupAttr: c.GroupSearch.RecursionGroupAttr,
},
}
}
// Open returns an authentication strategy using LDAP.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
+ logger = logger.With(slog.Group("connector", "type", "ldap", "id", id))
conn, err := c.OpenConnector(logger)
if err != nil {
return nil, err
@@ -216,7 +224,7 @@ type refreshData struct {
}
// OpenConnector is the same as Open but returns a type with all implemented connector interfaces.
-func (c *Config) OpenConnector(logger log.Logger) (interface {
+func (c *Config) OpenConnector(logger *slog.Logger) (interface {
connector.Connector
connector.PasswordConnector
connector.RefreshConnector
@@ -225,7 +233,7 @@ func (c *Config) OpenConnector(logger log.Logger) (interface {
return c.openConnector(logger)
}
-func (c *Config) openConnector(logger log.Logger) (*ldapConnector, error) {
+func (c *Config) openConnector(logger *slog.Logger) (*ldapConnector, error) {
requiredFields := []struct {
name string
val string
@@ -299,7 +307,7 @@ type ldapConnector struct {
tlsConfig *tls.Config
- logger log.Logger
+ logger *slog.Logger
}
var (
@@ -316,11 +324,14 @@ func (c *ldapConnector) do(_ context.Context, f func(c *ldap.Conn) error) error
conn *ldap.Conn
err error
)
+
switch {
case c.InsecureNoSSL:
- conn, err = ldap.Dial("tcp", c.Host)
+ u := url.URL{Scheme: "ldap", Host: c.Host}
+ conn, err = ldap.DialURL(u.String())
case c.StartTLS:
- conn, err = ldap.Dial("tcp", c.Host)
+ u := url.URL{Scheme: "ldap", Host: c.Host}
+ conn, err = ldap.DialURL(u.String())
if err != nil {
return fmt.Errorf("failed to connect: %v", err)
}
@@ -328,7 +339,8 @@ func (c *ldapConnector) do(_ context.Context, f func(c *ldap.Conn) error) error
return fmt.Errorf("start TLS failed: %v", err)
}
default:
- conn, err = ldap.DialTLS("tcp", c.Host, c.tlsConfig)
+ u := url.URL{Scheme: "ldaps", Host: c.Host}
+ conn, err = ldap.DialURL(u.String(), ldap.DialWithTLSConfig(c.tlsConfig))
}
if err != nil {
return fmt.Errorf("failed to connect: %v", err)
@@ -347,21 +359,23 @@ func (c *ldapConnector) do(_ context.Context, f func(c *ldap.Conn) error) error
return f(conn)
}
-func getAttrs(e ldap.Entry, name string) []string {
+func (c *ldapConnector) getAttrs(e ldap.Entry, name string) []string {
for _, a := range e.Attributes {
if a.Name != name {
continue
}
return a.Values
}
- if name == "DN" {
+ if strings.ToLower(name) == "dn" {
return []string{e.DN}
}
+
+ c.logger.Debug("attribute is not fround in entry", "attribute", name)
return nil
}
-func getAttr(e ldap.Entry, name string) string {
- if a := getAttrs(e, name); len(a) > 0 {
+func (c *ldapConnector) getAttr(e ldap.Entry, name string) string {
+ if a := c.getAttrs(e, name); len(a) > 0 {
return a[0]
}
return ""
@@ -373,25 +387,25 @@ func (c *ldapConnector) identityFromEntry(user ldap.Entry) (ident connector.Iden
missing := []string{}
// Fill the identity struct using the attributes from the user entry.
- if ident.UserID = getAttr(user, c.UserSearch.IDAttr); ident.UserID == "" {
+ if ident.UserID = c.getAttr(user, c.UserSearch.IDAttr); ident.UserID == "" {
missing = append(missing, c.UserSearch.IDAttr)
}
if c.UserSearch.NameAttr != "" {
- if ident.Username = getAttr(user, c.UserSearch.NameAttr); ident.Username == "" {
+ if ident.Username = c.getAttr(user, c.UserSearch.NameAttr); ident.Username == "" {
missing = append(missing, c.UserSearch.NameAttr)
}
}
if c.UserSearch.PreferredUsernameAttrAttr != "" {
- if ident.PreferredUsername = getAttr(user, c.UserSearch.PreferredUsernameAttrAttr); ident.PreferredUsername == "" {
+ if ident.PreferredUsername = c.getAttr(user, c.UserSearch.PreferredUsernameAttrAttr); ident.PreferredUsername == "" {
missing = append(missing, c.UserSearch.PreferredUsernameAttrAttr)
}
}
if c.UserSearch.EmailSuffix != "" {
ident.Email = ident.Username + "@" + c.UserSearch.EmailSuffix
- } else if ident.Email = getAttr(user, c.UserSearch.EmailAttr); ident.Email == "" {
+ } else if ident.Email = c.getAttr(user, c.UserSearch.EmailAttr); ident.Email == "" {
missing = append(missing, c.UserSearch.EmailAttr)
}
// TODO(ericchiang): Let this value be set from an attribute.
@@ -435,8 +449,8 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
req.Attributes = append(req.Attributes, c.UserSearch.PreferredUsernameAttrAttr)
}
- c.logger.Infof("performing ldap search %s %s %s",
- req.BaseDN, scopeString(req.Scope), req.Filter)
+ c.logger.Info("performing ldap search",
+ "base_dn", req.BaseDN, "scope", scopeString(req.Scope), "filter", req.Filter)
resp, err := conn.Search(req)
if err != nil {
return ldap.Entry{}, false, fmt.Errorf("ldap: search with filter %q failed: %v", req.Filter, err)
@@ -444,11 +458,11 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
switch n := len(resp.Entries); n {
case 0:
- c.logger.Errorf("ldap: no results returned for filter: %q", filter)
+ c.logger.Error("no results returned for filter", "filter", filter)
return ldap.Entry{}, false, nil
case 1:
user = *resp.Entries[0]
- c.logger.Infof("username %q mapped to entry %s", username, user.DN)
+ c.logger.Info("username mapped to entry", "username", username, "user_dn", user.DN)
return user, true, nil
default:
return ldap.Entry{}, false, fmt.Errorf("ldap: filter returned multiple (%d) results: %q", n, filter)
@@ -457,6 +471,7 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
func (c *ldapConnector) Login(ctx context.Context, s connector.Scopes, username, password string) (ident connector.Identity, validPass bool, err error) {
// make this check to avoid unauthenticated bind to the LDAP server.
+
if password == "" {
return connector.Identity{}, false, nil
}
@@ -468,6 +483,8 @@ func (c *ldapConnector) Login(ctx context.Context, s connector.Scopes, username,
user ldap.Entry
)
+ username = ldap.EscapeFilter(username)
+
err = c.do(ctx, func(conn *ldap.Conn) error {
entry, found, err := c.userEntry(conn, username)
if err != nil {
@@ -485,11 +502,11 @@ func (c *ldapConnector) Login(ctx context.Context, s connector.Scopes, username,
if ldapErr, ok := err.(*ldap.Error); ok {
switch ldapErr.ResultCode {
case ldap.LDAPResultInvalidCredentials:
- c.logger.Errorf("ldap: invalid password for user %q", user.DN)
+ c.logger.Error("invalid password for user", "user_dn", user.DN)
incorrectPass = true
return nil
case ldap.LDAPResultConstraintViolation:
- c.logger.Errorf("ldap: constraint violation for user %q: %s", user.DN, ldapErr.Error())
+ c.logger.Error("constraint violation for user", "user_dn", user.DN, "err", ldapErr.Error())
incorrectPass = true
return nil
}
@@ -575,61 +592,124 @@ func (c *ldapConnector) Refresh(ctx context.Context, s connector.Scopes, ident c
func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, error) {
if c.GroupSearch.BaseDN == "" {
- c.logger.Debugf("No groups returned for %q because no groups baseDN has been configured.", getAttr(user, c.UserSearch.NameAttr))
+ c.logger.Debug("No groups returned because no groups baseDN has been configured.", "base_dn", c.getAttr(user, c.UserSearch.NameAttr))
return nil, nil
}
- var groups []*ldap.Entry
+ var groupNames []string
+
for _, matcher := range c.GroupSearch.UserMatchers {
- for _, attr := range getAttrs(user, matcher.UserAttr) {
- filter := fmt.Sprintf("(%s=%s)", matcher.GroupAttr, ldap.EscapeFilter(attr))
- if c.GroupSearch.Filter != "" {
- filter = fmt.Sprintf("(&%s%s)", c.GroupSearch.Filter, filter)
+ // Initial Search
+ var groups []*ldap.Entry
+ for _, attr := range c.getAttrs(user, matcher.UserAttr) {
+ obtained, filter, err := c.queryGroups(ctx, matcher.GroupAttr, attr)
+ if err != nil {
+ return nil, err
+ }
+ gotGroups := len(obtained) != 0
+ if !gotGroups {
+ // TODO(ericchiang): Is this going to spam the logs?
+ c.logger.Error("ldap: groups search returned no groups", "filter", filter)
}
+ groups = append(groups, obtained...)
+ }
- req := &ldap.SearchRequest{
- BaseDN: c.GroupSearch.BaseDN,
- Filter: filter,
- Scope: c.groupSearchScope,
- Attributes: []string{c.GroupSearch.NameAttr},
+ // If RecursionGroupAttr is not set, convert direct groups into names and return
+ if matcher.RecursionGroupAttr == "" {
+ for _, group := range groups {
+ name := c.getAttr(*group, c.GroupSearch.NameAttr)
+ if name == "" {
+ return nil, fmt.Errorf(
+ "ldap: group entity %q missing required attribute %q",
+ group.DN, c.GroupSearch.NameAttr,
+ )
+ }
+ groupNames = append(groupNames, name)
}
+ continue
+ }
+
+ // Recursive Search
+ c.logger.Info("Recursive group search enabled", "groupAttr", matcher.GroupAttr, "recursionAttr", matcher.RecursionGroupAttr)
+ for {
+ var nextLevel []*ldap.Entry
+ for _, group := range groups {
+ name := c.getAttr(*group, c.GroupSearch.NameAttr)
+ if name == "" {
+ return nil, fmt.Errorf("ldap: group entity %q missing required attribute %q",
+ group.DN, c.GroupSearch.NameAttr)
+ }
+
+ // Prevent duplicates and circular references.
+ duplicate := false
+ for _, existingName := range groupNames {
+ if name == existingName {
+ c.logger.Debug("Found duplicate group", "name", name)
+ duplicate = true
+ break
+ }
+ }
+ if duplicate {
+ continue
+ }
+
+ groupNames = append(groupNames, name)
- gotGroups := false
- if err := c.do(ctx, func(conn *ldap.Conn) error {
- c.logger.Infof("performing ldap search %s %s %s",
- req.BaseDN, scopeString(req.Scope), req.Filter)
- resp, err := conn.Search(req)
+ // Search for parent groups using the group's DN.
+ parents, filter, err := c.queryGroups(ctx, matcher.RecursionGroupAttr, group.DN)
if err != nil {
- return fmt.Errorf("ldap: search failed: %v", err)
+ return nil, err
+ }
+ if len(parents) == 0 {
+ c.logger.Debug("No parent groups found", "filter", filter)
+ } else {
+ nextLevel = append(nextLevel, parents...)
}
- gotGroups = len(resp.Entries) != 0
- groups = append(groups, resp.Entries...)
- return nil
- }); err != nil {
- return nil, err
}
- if !gotGroups {
- // TODO(ericchiang): Is this going to spam the logs?
- c.logger.Errorf("ldap: groups search with filter %q returned no groups", filter)
+ if len(nextLevel) == 0 {
+ break
}
+ groups = nextLevel
}
}
+ return groupNames, nil
+}
+
+func (c *ldapConnector) queryGroups(ctx context.Context, memberAttr, dn string) ([]*ldap.Entry, string, error) {
+ filter := fmt.Sprintf("(%s=%s)", memberAttr, ldap.EscapeFilter(dn))
+ if c.GroupSearch.Filter != "" {
+ filter = fmt.Sprintf("(&%s%s)", c.GroupSearch.Filter, filter)
+ }
- groupNames := make([]string, 0, len(groups))
- for _, group := range groups {
- name := getAttr(*group, c.GroupSearch.NameAttr)
- if name == "" {
- // Be obnoxious about missing missing attributes. If the group entry is
- // missing its name attribute, that indicates a misconfiguration.
- //
- // In the future we can add configuration options to just log these errors.
- return nil, fmt.Errorf("ldap: group entity %q missing required attribute %q",
- group.DN, c.GroupSearch.NameAttr)
+ req := &ldap.SearchRequest{
+ BaseDN: c.GroupSearch.BaseDN,
+ Filter: filter,
+ Scope: c.groupSearchScope,
+ Attributes: []string{c.GroupSearch.NameAttr},
+ }
+
+ var entries []*ldap.Entry
+ if err := c.do(ctx, func(conn *ldap.Conn) error {
+ c.logger.Info(
+ "performing ldap search",
+ "base_dn", req.BaseDN,
+ "scope", scopeString(req.Scope),
+ "filter", req.Filter,
+ )
+ resp, err := conn.Search(req)
+ if err != nil {
+ if ldapErr, ok := err.(*ldap.Error); ok && ldapErr.ResultCode == ldap.LDAPResultNoSuchObject {
+ c.logger.Info("LDAP search returned no groups", "filter", filter)
+ return nil
+ }
+ return fmt.Errorf("ldap: search failed: %v", err)
}
-
- groupNames = append(groupNames, name)
+ entries = append(entries, resp.Entries...)
+ return nil
+ }); err != nil {
+ return nil, filter, err
}
- return groupNames, nil
+ return entries, filter, nil
}
func (c *ldapConnector) Prompt() string {
diff --git a/connector/ldap/ldap_test.go b/connector/ldap/ldap_test.go
index 83f9f4790c..a9665e128d 100644
--- a/connector/ldap/ldap_test.go
+++ b/connector/ldap/ldap_test.go
@@ -3,12 +3,11 @@ package ldap
import (
"context"
"fmt"
- "io"
+ "log/slog"
"os"
"testing"
"github.com/kylelemons/godebug/pretty"
- "github.com/sirupsen/logrus"
"github.com/dexidp/dex/connector"
)
@@ -83,6 +82,18 @@ func TestQuery(t *testing.T) {
password: "foo",
wantBadPW: true, // Want invalid password, not a query error.
},
+ {
+ name: "invalid wildcard username",
+ username: "a*", // wildcard query is not allowed
+ password: "foo",
+ wantBadPW: true, // Want invalid password, not a query error.
+ },
+ {
+ name: "invalid wildcard password",
+ username: "john",
+ password: "*", // wildcard password is not allowed
+ wantBadPW: true, // Want invalid password, not a query error.
+ },
}
runTests(t, connectLDAP, c, tests)
@@ -277,7 +288,7 @@ func TestGroupFilter(t *testing.T) {
c.GroupSearch.BaseDN = "ou=TestGroupFilter,dc=example,dc=org"
c.GroupSearch.UserMatchers = []UserMatcher{
{
- UserAttr: "DN",
+ UserAttr: "dn",
GroupAttr: "member",
},
}
@@ -514,6 +525,56 @@ func TestUsernamePrompt(t *testing.T) {
}
}
+func TestNestedGroups(t *testing.T) {
+ c := &Config{}
+ c.UserSearch.BaseDN = "ou=People,ou=TestNestedGroups,dc=example,dc=org"
+ c.UserSearch.NameAttr = "cn"
+ c.UserSearch.EmailAttr = "mail"
+ c.UserSearch.IDAttr = "DN"
+ c.UserSearch.Username = "cn"
+
+ c.GroupSearch.BaseDN = "ou=TestNestedGroups,dc=example,dc=org"
+ c.GroupSearch.UserMatchers = []UserMatcher{
+ {
+ UserAttr: "DN",
+ GroupAttr: "member",
+ // Enable Recursive Search
+ RecursionGroupAttr: "member",
+ },
+ }
+ c.GroupSearch.NameAttr = "cn"
+
+ tests := []subtest{
+ {
+ name: "nestedgroups_jane",
+ username: "jane",
+ password: "foo",
+ groups: true,
+ want: connector.Identity{
+ UserID: "cn=jane,ou=People,ou=TestNestedGroups,dc=example,dc=org",
+ Username: "jane",
+ Email: "janedoe@example.com",
+ EmailVerified: true,
+ Groups: []string{"childGroup", "circularGroup1", "intermediateGroup", "circularGroup2", "parentGroup"},
+ },
+ },
+ {
+ name: "nestedgroups_john",
+ username: "john",
+ password: "bar",
+ groups: true,
+ want: connector.Identity{
+ UserID: "cn=john,ou=People,ou=TestNestedGroups,dc=example,dc=org",
+ Username: "john",
+ Email: "johndoe@example.com",
+ EmailVerified: true,
+ Groups: []string{"circularGroup2", "intermediateGroup", "circularGroup1", "parentGroup"},
+ },
+ },
+ }
+ runTests(t, connectLDAP, c, tests)
+}
+
func getenv(key, defaultVal string) string {
if val := os.Getenv(key); val != "" {
return val
@@ -523,7 +584,7 @@ func getenv(key, defaultVal string) string {
// runTests runs a set of tests against an LDAP schema.
//
-// The tests require LDAP to be runnning.
+// The tests require LDAP to be running.
// You can use the provided docker-compose file to setup an LDAP server.
func runTests(t *testing.T, connMethod connectionMethod, config *Config, tests []subtest) {
ldapHost := os.Getenv("DEX_LDAP_HOST")
@@ -555,7 +616,7 @@ func runTests(t *testing.T, connMethod connectionMethod, config *Config, tests [
c.BindDN = "cn=admin,dc=example,dc=org"
c.BindPW = "admin"
- l := &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
+ l := slog.New(slog.DiscardHandler)
conn, err := c.openConnector(l)
if err != nil {
diff --git a/connector/ldap/testdata/schema.ldif b/connector/ldap/testdata/schema.ldif
index 69c7b3ff64..a7f1393d4e 100644
--- a/connector/ldap/testdata/schema.ldif
+++ b/connector/ldap/testdata/schema.ldif
@@ -445,3 +445,63 @@ sn: doe
cn: jane
mail: janedoe@example.com
userpassword: foo
+
+########################################################################
+
+dn: ou=TestNestedGroups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: TestNestedGroups
+
+dn: ou=People,ou=TestNestedGroups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: People
+
+dn: cn=jane,ou=People,ou=TestNestedGroups,dc=example,dc=org
+objectClass: person
+objectClass: inetOrgPerson
+sn: doe
+cn: jane
+mail: janedoe@example.com
+userpassword: foo
+
+dn: cn=john,ou=People,ou=TestNestedGroups,dc=example,dc=org
+objectClass: person
+objectClass: inetOrgPerson
+sn: doe
+cn: john
+mail: johndoe@example.com
+userpassword: bar
+
+# Group definitions.
+
+dn: ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: organizationalUnit
+ou: Groups
+
+dn: cn=childGroup,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: groupOfNames
+cn: childGroup
+member: cn=jane,ou=People,ou=TestNestedGroups,dc=example,dc=org
+
+dn: cn=intermediateGroup,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: groupOfNames
+cn: intermediateGroup
+member: cn=childGroup,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+member: cn=john,ou=People,ou=TestNestedGroups,dc=example,dc=org
+
+dn: cn=parentGroup,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: groupOfNames
+cn: parentGroup
+member: cn=intermediateGroup,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+
+dn: cn=circularGroup1,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: groupOfNames
+cn: circularGroup1
+member: cn=circularGroup2,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+member: cn=jane,ou=People,ou=TestNestedGroups,dc=example,dc=org
+
+dn: cn=circularGroup2,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+objectClass: groupOfNames
+cn: circularGroup2
+member: cn=circularGroup1,ou=Groups,ou=TestNestedGroups,dc=example,dc=org
+member: cn=john,ou=People,ou=TestNestedGroups,dc=example,dc=org
diff --git a/connector/linkedin/linkedin.go b/connector/linkedin/linkedin.go
index f79f1c49d8..f17d17cca1 100644
--- a/connector/linkedin/linkedin.go
+++ b/connector/linkedin/linkedin.go
@@ -6,13 +6,13 @@ import (
"encoding/json"
"fmt"
"io"
+ "log/slog"
"net/http"
"strings"
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
const (
@@ -29,7 +29,7 @@ type Config struct {
}
// Open returns a strategy for logging in through LinkedIn
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
return &linkedInConnector{
oauth2Config: &oauth2.Config{
ClientID: c.ClientID,
@@ -41,7 +41,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
Scopes: []string{"r_liteprofile", "r_emailaddress"},
RedirectURL: c.RedirectURI,
},
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "linkedin", "id", id)),
}, nil
}
@@ -51,7 +51,7 @@ type connectorData struct {
type linkedInConnector struct {
oauth2Config *oauth2.Config
- logger log.Logger
+ logger *slog.Logger
}
// LinkedIn doesn't provide refresh tokens, so refresh tokens issued by Dex
diff --git a/connector/microsoft/microsoft.go b/connector/microsoft/microsoft.go
index 3952c94be6..2fcf6a7515 100644
--- a/connector/microsoft/microsoft.go
+++ b/connector/microsoft/microsoft.go
@@ -8,6 +8,7 @@ import (
"errors"
"fmt"
"io"
+ "log/slog"
"net/http"
"strings"
"sync"
@@ -17,7 +18,6 @@ import (
"github.com/dexidp/dex/connector"
groups_pkg "github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
// GroupNameFormat represents the format of the group identifier
@@ -54,6 +54,9 @@ type Config struct {
UseGroupsAsWhitelist bool `json:"useGroupsAsWhitelist"`
EmailToLowercase bool `json:"emailToLowercase"`
+ APIURL string `json:"apiURL"`
+ GraphURL string `json:"graphURL"`
+
// PromptType is used for the prompt query parameter.
// For valid values, see https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code.
PromptType string `json:"promptType"`
@@ -63,10 +66,10 @@ type Config struct {
}
// Open returns a strategy for logging in through Microsoft.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
m := microsoftConnector{
- apiURL: "https://login.microsoftonline.com",
- graphURL: "https://graph.microsoft.com",
+ apiURL: strings.TrimSuffix(c.APIURL, "/"),
+ graphURL: strings.TrimSuffix(c.GraphURL, "/"),
redirectURI: c.RedirectURI,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
@@ -75,12 +78,21 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
groups: c.Groups,
groupNameFormat: c.GroupNameFormat,
useGroupsAsWhitelist: c.UseGroupsAsWhitelist,
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "microsoft", "id", id)),
emailToLowercase: c.EmailToLowercase,
promptType: c.PromptType,
domainHint: c.DomainHint,
scopes: c.Scopes,
}
+
+ if m.apiURL == "" {
+ m.apiURL = "https://login.microsoftonline.com"
+ }
+
+ if m.graphURL == "" {
+ m.graphURL = "https://graph.microsoft.com"
+ }
+
// By default allow logins from both personal and business/school
// accounts.
if m.tenant == "" {
@@ -121,7 +133,7 @@ type microsoftConnector struct {
groupNameFormat GroupNameFormat
groups []string
useGroupsAsWhitelist bool
- logger log.Logger
+ logger *slog.Logger
emailToLowercase bool
promptType string
domainHint string
@@ -316,22 +328,27 @@ func (c *microsoftConnector) Refresh(ctx context.Context, s connector.Scopes, id
// https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/resources/user
// id - The unique identifier for the user. Inherited from
-// directoryObject. Key. Not nullable. Read-only.
+//
+// directoryObject. Key. Not nullable. Read-only.
+//
// displayName - The name displayed in the address book for the user.
-// This is usually the combination of the user's first name,
-// middle initial and last name. This property is required
-// when a user is created and it cannot be cleared during
-// updates. Supports $filter and $orderby.
+//
+// This is usually the combination of the user's first name,
+// middle initial and last name. This property is required
+// when a user is created and it cannot be cleared during
+// updates. Supports $filter and $orderby.
+//
// userPrincipalName - The user principal name (UPN) of the user.
-// The UPN is an Internet-style login name for the user
-// based on the Internet standard RFC 822. By convention,
-// this should map to the user's email name. The general
-// format is alias@domain, where domain must be present in
-// the tenantâs collection of verified domains. This
-// property is required when a user is created. The
-// verified domains for the tenant can be accessed from the
-// verifiedDomains property of organization. Supports
-// $filter and $orderby.
+//
+// The UPN is an Internet-style login name for the user
+// based on the Internet standard RFC 822. By convention,
+// this should map to the user's email name. The general
+// format is alias@domain, where domain must be present in
+// the tenantâs collection of verified domains. This
+// property is required when a user is created. The
+// verified domains for the tenant can be accessed from the
+// verifiedDomains property of organization. Supports
+// $filter and $orderby.
type user struct {
ID string `json:"id"`
Name string `json:"displayName"`
@@ -364,8 +381,9 @@ func (c *microsoftConnector) user(ctx context.Context, client *http.Client) (u u
// https://developer.microsoft.com/en-us/graph/docs/api-reference/v1.0/resources/group
// displayName - The display name for the group. This property is required when
-// a group is created and it cannot be cleared during updates.
-// Supports $filter and $orderby.
+//
+// a group is created and it cannot be cleared during updates.
+// Supports $filter and $orderby.
type group struct {
Name string `json:"displayName"`
}
diff --git a/connector/mock/connectortest.go b/connector/mock/connectortest.go
index e7ee438625..7e5979a992 100644
--- a/connector/mock/connectortest.go
+++ b/connector/mock/connectortest.go
@@ -5,16 +5,16 @@ import (
"context"
"errors"
"fmt"
+ "log/slog"
"net/http"
"net/url"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
)
// NewCallbackConnector returns a mock connector which requires no user interaction. It always returns
// the same (fake) identity.
-func NewCallbackConnector(logger log.Logger) connector.Connector {
+func NewCallbackConnector(logger *slog.Logger) connector.Connector {
return &Callback{
Identity: connector.Identity{
UserID: "0-385-28089-0",
@@ -39,7 +39,7 @@ var (
type Callback struct {
// The returned identity.
Identity connector.Identity
- Logger log.Logger
+ Logger *slog.Logger
}
// LoginURL returns the URL to redirect the user to login with.
@@ -66,11 +66,16 @@ func (m *Callback) Refresh(ctx context.Context, s connector.Scopes, identity con
return m.Identity, nil
}
+func (m *Callback) TokenIdentity(ctx context.Context, subjectTokenType, subjectToken string) (connector.Identity, error) {
+ return m.Identity, nil
+}
+
// CallbackConfig holds the configuration parameters for a connector which requires no interaction.
type CallbackConfig struct{}
// Open returns an authentication strategy which requires no user interaction.
-func (c *CallbackConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *CallbackConfig) Open(id string, logger *slog.Logger) (connector.Connector, error) {
+ logger = logger.With(slog.Group("connector", "type", "callback", "id", id))
return NewCallbackConnector(logger), nil
}
@@ -82,7 +87,7 @@ type PasswordConfig struct {
}
// Open returns an authentication strategy which prompts for a predefined username and password.
-func (c *PasswordConfig) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *PasswordConfig) Open(id string, logger *slog.Logger) (connector.Connector, error) {
if c.Username == "" {
return nil, errors.New("no username supplied")
}
@@ -95,7 +100,7 @@ func (c *PasswordConfig) Open(id string, logger log.Logger) (connector.Connector
type passwordConnector struct {
username string
password string
- logger log.Logger
+ logger *slog.Logger
}
func (p passwordConnector) Close() error { return nil }
diff --git a/connector/oauth/oauth.go b/connector/oauth/oauth.go
index 237d075e83..413a813a08 100644
--- a/connector/oauth/oauth.go
+++ b/connector/oauth/oauth.go
@@ -2,22 +2,18 @@ package oauth
import (
"context"
- "crypto/tls"
- "crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
- "net"
+ "log/slog"
"net/http"
- "os"
"strings"
- "time"
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/httpclient"
)
type oauthConnector struct {
@@ -35,7 +31,7 @@ type oauthConnector struct {
emailVerifiedKey string
groupsKey string
httpClient *http.Client
- logger log.Logger
+ logger *slog.Logger
}
type connectorData struct {
@@ -62,7 +58,7 @@ type Config struct {
} `json:"claimMapping"`
}
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
var err error
userIDKey := c.UserIDKey
@@ -103,7 +99,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
userInfoURL: c.UserInfoURL,
scopes: c.Scopes,
redirectURI: c.RedirectURI,
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "oauth", "id", id)),
userIDKey: userIDKey,
userNameKey: userNameKey,
preferredUsernameKey: preferredUsernameKey,
@@ -112,7 +108,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
emailVerifiedKey: emailVerifiedKey,
}
- oauthConn.httpClient, err = newHTTPClient(c.RootCAs, c.InsecureSkipVerify)
+ oauthConn.httpClient, err = httpclient.NewHTTPClient(c.RootCAs, c.InsecureSkipVerify)
if err != nil {
return nil, err
}
@@ -120,40 +116,6 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
return oauthConn, err
}
-func newHTTPClient(rootCAs []string, insecureSkipVerify bool) (*http.Client, error) {
- pool, err := x509.SystemCertPool()
- if err != nil {
- return nil, err
- }
-
- tlsConfig := tls.Config{RootCAs: pool, InsecureSkipVerify: insecureSkipVerify}
- for _, rootCA := range rootCAs {
- rootCABytes, err := os.ReadFile(rootCA)
- if err != nil {
- return nil, fmt.Errorf("failed to read root-ca: %v", err)
- }
- if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCABytes) {
- return nil, fmt.Errorf("no certs found in root CA file %q", rootCA)
- }
- }
-
- return &http.Client{
- Transport: &http.Transport{
- TLSClientConfig: &tlsConfig,
- Proxy: http.ProxyFromEnvironment,
- DialContext: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- DualStack: true,
- }).DialContext,
- MaxIdleConns: 100,
- IdleConnTimeout: 90 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 1 * time.Second,
- },
- }, nil
-}
-
func (c *oauthConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) {
if c.redirectURI != callbackURL {
return "", fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI)
diff --git a/connector/oauth/oauth_test.go b/connector/oauth/oauth_test.go
index 3a5ec6bf59..2f6b0b95a3 100644
--- a/connector/oauth/oauth_test.go
+++ b/connector/oauth/oauth_test.go
@@ -6,15 +6,15 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log/slog"
"net/http"
"net/http/httptest"
"net/url"
"sort"
"testing"
- "github.com/sirupsen/logrus"
+ "github.com/go-jose/go-jose/v4"
"github.com/stretchr/testify/assert"
- jose "gopkg.in/square/go-jose.v2"
"github.com/dexidp/dex/connector"
)
@@ -270,7 +270,7 @@ func newConnector(t *testing.T, serverURL string) *oauthConnector {
testConfig.ClaimMapping.EmailKey = "mail"
testConfig.ClaimMapping.EmailVerifiedKey = "has_verified_email"
- log := logrus.New()
+ log := slog.New(slog.DiscardHandler)
conn, err := testConfig.Open("id", log)
if err != nil {
diff --git a/connector/oidc/oidc.go b/connector/oidc/oidc.go
index e345dca0b2..1cf2b62ad8 100644
--- a/connector/oidc/oidc.go
+++ b/connector/oidc/oidc.go
@@ -6,8 +6,10 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log/slog"
"net/http"
"net/url"
+ "regexp"
"strings"
"time"
@@ -15,16 +17,26 @@ import (
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
- "github.com/dexidp/dex/pkg/log"
+ groups_pkg "github.com/dexidp/dex/pkg/groups"
+ "github.com/dexidp/dex/pkg/httpclient"
)
// Config holds configuration options for OpenID Connect logins.
type Config struct {
- Issuer string `json:"issuer"`
+ Issuer string `json:"issuer"`
+ // Some offspec providers like Azure, Oracle IDCS have oidc discovery url
+ // different from issuer url which causes issuerValidation to fail
+ // IssuerAlias provides a way to override the Issuer url
+ // from the .well-known/openid-configuration issuer
+ IssuerAlias string `json:"issuerAlias"`
ClientID string `json:"clientID"`
ClientSecret string `json:"clientSecret"`
RedirectURI string `json:"redirectURI"`
+ // The section to override options discovered automatically from
+ // the providers' discovery URL (.well-known/openid-configuration).
+ ProviderDiscoveryOverrides ProviderDiscoveryOverrides `json:"providerDiscoveryOverrides"`
+
// Causes client_secret to be passed as POST parameters instead of basic
// auth. This is specifically "NOT RECOMMENDED" by the OAuth2 RFC, but some
// providers require it.
@@ -34,17 +46,32 @@ type Config struct {
Scopes []string `json:"scopes"` // defaults to "profile" and "email"
+ // HostedDomains was an optional list of whitelisted domains when using the OIDC connector with Google.
+ // Only users from a whitelisted domain were allowed to log in.
+ // Support for this option was removed from the OIDC connector.
+ // Consider switching to the Google connector which supports this option.
+ //
+ // Deprecated: will be removed in future releases.
+ HostedDomains []string `json:"hostedDomains"`
+
+ // Certificates for SSL validation
+ RootCAs []string `json:"rootCAs"`
+
// Override the value of email_verified to true in the returned claims
InsecureSkipEmailVerified bool `json:"insecureSkipEmailVerified"`
// InsecureEnableGroups enables groups claims. This is disabled by default until https://github.com/dexidp/dex/issues/1065 is resolved
- InsecureEnableGroups bool `json:"insecureEnableGroups"`
+ InsecureEnableGroups bool `json:"insecureEnableGroups"`
+ AllowedGroups []string `json:"allowedGroups"`
// AcrValues (Authentication Context Class Reference Values) that specifies the Authentication Context Class Values
// within the Authentication Request that the Authorization Server is being requested to use for
// processing requests from this Client, with the values appearing in order of preference.
AcrValues []string `json:"acrValues"`
+ // Disable certificate verification
+ InsecureSkipVerify bool `json:"insecureSkipVerify"`
+
// GetUserInfo uses the userinfo endpoint to get additional claims for
// the token. This is especially useful where upstreams return "thin"
// id tokens
@@ -54,8 +81,8 @@ type Config struct {
UserNameKey string `json:"userNameKey"`
- // PromptType will be used fot the prompt parameter (when offline_access, by default prompt=consent)
- PromptType string `json:"promptType"`
+ // PromptType will be used for the prompt parameter (when offline_access, by default prompt=consent)
+ PromptType *string `json:"promptType"`
// OverrideClaimMapping will be used to override the options defined in claimMappings.
// i.e. if there are 'email' and `preferred_email` claims available, by default Dex will always use the `email` claim independent of the ClaimMapping.EmailKey.
@@ -72,6 +99,101 @@ type Config struct {
// Configurable key which contains the groups claims
GroupsKey string `json:"groups"` // defaults to "groups"
} `json:"claimMapping"`
+
+ // ClaimMutations holds all claim mutations options
+ ClaimMutations struct {
+ NewGroupFromClaims []NewGroupFromClaims `json:"newGroupFromClaims"`
+ FilterGroupClaims FilterGroupClaims `json:"filterGroupClaims"`
+ ModifyGroupNames ModifyGroupNames `json:"modifyGroupNames"`
+ } `json:"claimModifications"`
+}
+
+type ProviderDiscoveryOverrides struct {
+ // TokenURL provides a way to user overwrite the Token URL
+ // from the .well-known/openid-configuration token_endpoint
+ TokenURL string `json:"tokenURL"`
+ // AuthURL provides a way to user overwrite the Auth URL
+ // from the .well-known/openid-configuration authorization_endpoint
+ AuthURL string `json:"authURL"`
+ // JWKSURL provides a way to user overwrite the JWKS URL
+ // from the .well-known/openid-configuration jwks_uri
+ JWKSURL string `json:"jwksURL"`
+}
+
+func (o *ProviderDiscoveryOverrides) Empty() bool {
+ return o.TokenURL == "" && o.AuthURL == "" && o.JWKSURL == ""
+}
+
+func getProvider(ctx context.Context, issuer string, overrides ProviderDiscoveryOverrides) (*oidc.Provider, error) {
+ provider, err := oidc.NewProvider(ctx, issuer)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get provider: %v", err)
+ }
+
+ if overrides.Empty() {
+ return provider, nil
+ }
+
+ v := &struct {
+ Issuer string `json:"issuer"`
+ AuthURL string `json:"authorization_endpoint"`
+ TokenURL string `json:"token_endpoint"`
+ DeviceAuthURL string `json:"device_authorization_endpoint"`
+ JWKSURL string `json:"jwks_uri"`
+ UserInfoURL string `json:"userinfo_endpoint"`
+ Algorithms []string `json:"id_token_signing_alg_values_supported"`
+ }{}
+ if err := provider.Claims(v); err != nil {
+ return nil, fmt.Errorf("failed to extract provider discovery claims: %v", err)
+ }
+ config := oidc.ProviderConfig{
+ IssuerURL: v.Issuer,
+ AuthURL: v.AuthURL,
+ TokenURL: v.TokenURL,
+ DeviceAuthURL: v.DeviceAuthURL,
+ JWKSURL: v.JWKSURL,
+ UserInfoURL: v.UserInfoURL,
+ Algorithms: v.Algorithms,
+ }
+
+ if overrides.TokenURL != "" {
+ config.TokenURL = overrides.TokenURL
+ }
+ if overrides.AuthURL != "" {
+ config.AuthURL = overrides.AuthURL
+ }
+ if overrides.JWKSURL != "" {
+ config.JWKSURL = overrides.JWKSURL
+ }
+ return config.NewProvider(context.Background()), nil
+}
+
+// NewGroupFromClaims creates a new group from a list of claims and appends it to the list of existing groups.
+type NewGroupFromClaims struct {
+ // List of claim to join together
+ Claims []string `json:"claims"`
+
+ // String to separate the claims
+ Delimiter string `json:"delimiter"`
+
+ // Should Dex remove the Delimiter string from claim values
+ // This is done to keep resulting claim structure in full control of the Dex operator
+ ClearDelimiter bool `json:"clearDelimiter"`
+
+ // String to place before the first claim
+ Prefix string `json:"prefix"`
+}
+
+// FilterGroupClaims is a regex filter for to keep only the matching groups.
+// This is useful when the groups list is too large to fit within an HTTP header.
+type FilterGroupClaims struct {
+ GroupsFilter string `json:"groupsFilter"`
+}
+
+// ModifyGroupNames allows to modify the group claims by adding a prefix and/or suffix to each group.
+type ModifyGroupNames struct {
+ Prefix string `json:"prefix"`
+ Suffix string `json:"suffix"`
}
// Domains that don't support basic auth. golang.org/x/oauth2 has an internal
@@ -104,13 +226,28 @@ func knownBrokenAuthHeaderProvider(issuerURL string) bool {
// Open returns a connector which can be used to login users through an upstream
// OpenID Connect provider.
-func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
- ctx, cancel := context.WithCancel(context.Background())
+func (c *Config) Open(id string, logger *slog.Logger) (conn connector.Connector, err error) {
+ if len(c.HostedDomains) > 0 {
+ return nil, fmt.Errorf("support for the Hosted domains option had been deprecated and removed, consider switching to the Google connector")
+ }
+
+ httpClient, err := httpclient.NewHTTPClient(c.RootCAs, c.InsecureSkipVerify)
+ if err != nil {
+ return nil, err
+ }
- provider, err := oidc.NewProvider(ctx, c.Issuer)
+ bgctx, cancel := context.WithCancel(context.Background())
+ ctx := context.WithValue(bgctx, oauth2.HTTPClient, httpClient)
+ if c.IssuerAlias != "" {
+ ctx = oidc.InsecureIssuerURLContext(ctx, c.IssuerAlias)
+ }
+ provider, err := getProvider(ctx, c.Issuer, c.ProviderDiscoveryOverrides)
if err != nil {
cancel()
- return nil, fmt.Errorf("failed to get provider: %v", err)
+ return nil, err
+ }
+ if !c.ProviderDiscoveryOverrides.Empty() {
+ logger.Warn("overrides for connector are set, this can be a vulnerability when not properly configured", "connector_id", id)
}
endpoint := provider.Endpoint()
@@ -132,8 +269,17 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
}
// PromptType should be "consent" by default, if not set
- if c.PromptType == "" {
- c.PromptType = "consent"
+ promptType := "consent"
+ if c.PromptType != nil {
+ promptType = *c.PromptType
+ }
+
+ var groupsFilter *regexp.Regexp
+ if c.ClaimMutations.FilterGroupClaims.GroupsFilter != "" {
+ groupsFilter, err = regexp.Compile(c.ClaimMutations.FilterGroupClaims.GroupsFilter)
+ if err != nil {
+ logger.Warn("ignoring invalid", "invalid_regex", c.ClaimMutations.FilterGroupClaims.GroupsFilter, "connector_id", id)
+ }
}
clientID := c.ClientID
@@ -147,22 +293,29 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
Scopes: scopes,
RedirectURL: c.RedirectURI,
},
- verifier: provider.Verifier(
+ verifier: provider.VerifierContext(
+ ctx, // Pass our ctx with customized http.Client
&oidc.Config{ClientID: clientID},
),
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "oidc", "id", id)),
cancel: cancel,
+ httpClient: httpClient,
insecureSkipEmailVerified: c.InsecureSkipEmailVerified,
insecureEnableGroups: c.InsecureEnableGroups,
+ allowedGroups: c.AllowedGroups,
acrValues: c.AcrValues,
getUserInfo: c.GetUserInfo,
- promptType: c.PromptType,
+ promptType: promptType,
userIDKey: c.UserIDKey,
userNameKey: c.UserNameKey,
overrideClaimMapping: c.OverrideClaimMapping,
preferredUsernameKey: c.ClaimMapping.PreferredUsernameKey,
emailKey: c.ClaimMapping.EmailKey,
groupsKey: c.ClaimMapping.GroupsKey,
+ newGroupFromClaims: c.ClaimMutations.NewGroupFromClaims,
+ groupsFilter: groupsFilter,
+ groupsPrefix: c.ClaimMutations.ModifyGroupNames.Prefix,
+ groupsSuffix: c.ClaimMutations.ModifyGroupNames.Suffix,
}, nil
}
@@ -177,9 +330,11 @@ type oidcConnector struct {
oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier
cancel context.CancelFunc
- logger log.Logger
+ logger *slog.Logger
+ httpClient *http.Client
insecureSkipEmailVerified bool
insecureEnableGroups bool
+ allowedGroups []string
acrValues []string
getUserInfo bool
promptType string
@@ -189,6 +344,10 @@ type oidcConnector struct {
preferredUsernameKey string
emailKey string
groupsKey string
+ newGroupFromClaims []NewGroupFromClaims
+ groupsFilter *regexp.Regexp
+ groupsPrefix string
+ groupsSuffix string
}
func (c *oidcConnector) Close() error {
@@ -231,6 +390,7 @@ type caller uint
const (
createCaller caller = iota
refreshCaller
+ exchangeCaller
)
func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
@@ -238,11 +398,14 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
if errType := q.Get("error"); errType != "" {
return identity, &oauth2Error{errType, q.Get("error_description")}
}
- token, err := c.oauth2Config.Exchange(r.Context(), q.Get("code"))
+
+ ctx := context.WithValue(r.Context(), oauth2.HTTPClient, c.httpClient)
+
+ token, err := c.oauth2Config.Exchange(ctx, q.Get("code"))
if err != nil {
return identity, fmt.Errorf("oidc: failed to get token: %v", err)
}
- return c.createIdentity(r.Context(), identity, token, createCaller)
+ return c.createIdentity(ctx, identity, token, createCaller)
}
// Refresh is used to refresh a session with the refresh token provided by the IdP
@@ -253,6 +416,8 @@ func (c *oidcConnector) Refresh(ctx context.Context, s connector.Scopes, identit
return identity, fmt.Errorf("oidc: failed to unmarshal connector data: %v", err)
}
+ ctx = context.WithValue(ctx, oauth2.HTTPClient, c.httpClient)
+
t := &oauth2.Token{
RefreshToken: string(cd.RefreshToken),
Expiry: time.Now().Add(-time.Hour),
@@ -264,11 +429,22 @@ func (c *oidcConnector) Refresh(ctx context.Context, s connector.Scopes, identit
return c.createIdentity(ctx, identity, token, refreshCaller)
}
+func (c *oidcConnector) TokenIdentity(ctx context.Context, subjectTokenType, subjectToken string) (connector.Identity, error) {
+ var identity connector.Identity
+
+ ctx = context.WithValue(ctx, oauth2.HTTPClient, c.httpClient)
+
+ token := &oauth2.Token{
+ AccessToken: subjectToken,
+ TokenType: subjectTokenType,
+ }
+ return c.createIdentity(ctx, identity, token, exchangeCaller)
+}
+
func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.Identity, token *oauth2.Token, caller caller) (connector.Identity, error) {
var claims map[string]interface{}
- rawIDToken, ok := token.Extra("id_token").(string)
- if ok {
+ if rawIDToken, ok := token.Extra("id_token").(string); ok {
idToken, err := c.verifier.Verify(ctx, rawIDToken)
if err != nil {
return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err)
@@ -277,14 +453,36 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
if err := idToken.Claims(&claims); err != nil {
return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
}
+ } else if caller == exchangeCaller {
+ switch token.TokenType {
+ case "urn:ietf:params:oauth:token-type:id_token":
+ // Verify only works on ID tokens
+ idToken, err := c.provider.Verifier(&oidc.Config{SkipClientIDCheck: true}).Verify(ctx, token.AccessToken)
+ if err != nil {
+ return identity, fmt.Errorf("oidc: failed to verify token: %v", err)
+ }
+ if err := idToken.Claims(&claims); err != nil {
+ return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
+ }
+ case "urn:ietf:params:oauth:token-type:access_token":
+ if !c.getUserInfo {
+ return identity, fmt.Errorf("oidc: getUserInfo is required for access token exchange")
+ }
+ default:
+ return identity, fmt.Errorf("unknown token type for token exchange: %s", token.TokenType)
+ }
} else if caller != refreshCaller {
// ID tokens aren't mandatory in the reply when using a refresh_token grant
return identity, errors.New("oidc: no id_token in token response")
}
- // We immediately want to run getUserInfo if configured before we validate the claims
+ // We immediately want to run getUserInfo if configured before we validate the claims.
+ // For token exchanges with access tokens, this is how we verify the token.
if c.getUserInfo {
- userInfo, err := c.provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
+ userInfo, err := c.provider.UserInfo(ctx, oauth2.StaticTokenSource(&oauth2.Token{
+ AccessToken: token.AccessToken,
+ TokenType: "Bearer", // The UserInfo endpoint requires a bearer token as per RFC6750
+ }))
if err != nil {
return identity, fmt.Errorf("oidc: error loading userinfo: %v", err)
}
@@ -359,12 +557,65 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
if found {
for _, v := range vs {
if s, ok := v.(string); ok {
+ if c.groupsFilter != nil && !c.groupsFilter.MatchString(s) {
+ continue
+ }
groups = append(groups, s)
+ } else if groupMap, ok := v.(map[string]interface{}); ok {
+ if s, ok := groupMap["name"].(string); ok {
+ if c.groupsFilter != nil && !c.groupsFilter.MatchString(s) {
+ continue
+ }
+ groups = append(groups, s)
+ }
} else {
return identity, fmt.Errorf("malformed \"%v\" claim", groupsKey)
}
}
}
+
+ // Validate that the user is part of allowedGroups
+ if len(c.allowedGroups) > 0 {
+ groupMatches := groups_pkg.Filter(groups, c.allowedGroups)
+
+ if len(groupMatches) == 0 {
+ // No group membership matches found, disallowing
+ return identity, fmt.Errorf("user not a member of allowed groups")
+ }
+
+ groups = groupMatches
+ }
+ }
+
+ // add prefix/suffix to groups
+ if c.groupsPrefix != "" || c.groupsSuffix != "" {
+ for i, group := range groups {
+ groups[i] = c.groupsPrefix + group + c.groupsSuffix
+ }
+ }
+
+ for _, config := range c.newGroupFromClaims {
+ newGroupSegments := []string{
+ config.Prefix,
+ }
+ for _, claimName := range config.Claims {
+ claimValue, ok := claims[claimName].(string)
+ if !ok { // Non string claim value are ignored, concatenating them doesn't really make any sense
+ continue
+ }
+
+ if config.ClearDelimiter {
+ // Removing the delimiter string from the concatenated claim to ensure resulting claim structure
+ // is in full control of Dex operator
+ claimValue = strings.ReplaceAll(claimValue, config.Delimiter, "")
+ }
+
+ newGroupSegments = append(newGroupSegments, claimValue)
+ }
+
+ if len(newGroupSegments) > 1 {
+ groups = append(groups, strings.Join(newGroupSegments, config.Delimiter))
+ }
}
cd := connectorData{
diff --git a/connector/oidc/oidc_test.go b/connector/oidc/oidc_test.go
index d94af79de8..9a3d712665 100644
--- a/connector/oidc/oidc_test.go
+++ b/connector/oidc/oidc_test.go
@@ -2,6 +2,7 @@ package oidc
import (
"bytes"
+ "context"
"crypto/rand"
"crypto/rsa"
"encoding/base64"
@@ -9,6 +10,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log/slog"
"net/http"
"net/http/httptest"
"reflect"
@@ -16,8 +18,8 @@ import (
"testing"
"time"
- "github.com/sirupsen/logrus"
- "gopkg.in/square/go-jose.v2"
+ "github.com/go-jose/go-jose/v4"
+ "github.com/stretchr/testify/require"
"github.com/dexidp/dex/connector"
)
@@ -61,6 +63,10 @@ func TestHandleCallback(t *testing.T) {
expectPreferredUsername string
expectedEmailField string
token map[string]interface{}
+ groupsRegex string
+ newGroupFromClaims []NewGroupFromClaims
+ groupsPrefix string
+ groupsSuffix string
}{
{
name: "simpleCase",
@@ -287,6 +293,197 @@ func TestHandleCallback(t *testing.T) {
"email_verified": true,
},
},
+ {
+ name: "singularGroupResponseAsMap",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1"},
+ expectedEmailField: "emailvalue",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []map[string]string{{"name": "group1"}},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "multipleGroupResponseAsMap",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1", "group2"},
+ expectedEmailField: "emailvalue",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []map[string]string{{"name": "group1"}, {"name": "group2"}},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "newGroupFromClaims",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1", "gh::acme::pipeline-one", "clr_delim-acme-foobar", "keep_delim-acme-foo-bar", "bk-emailvalue"},
+ expectedEmailField: "emailvalue",
+ newGroupFromClaims: []NewGroupFromClaims{
+ { // The basic functionality, should create "gh::acme::pipeline-one".
+ Claims: []string{
+ "organization",
+ "pipeline",
+ },
+ Delimiter: "::",
+ Prefix: "gh",
+ },
+ { // Non existing claims, should not generate any any new group claim.
+ Claims: []string{
+ "non-existing1",
+ "non-existing2",
+ },
+ Delimiter: "::",
+ Prefix: "tfe",
+ },
+ { // In this case the delimiter character("-") should be removed removed from "claim-with-delimiter" claim to ensure the resulting
+ // claim structure is in full control of the Dex operator and not the person creating a new pipeline.
+ // Should create "clr_delim-acme-foobar" and not "tfe-acme-foo-bar".
+ Claims: []string{
+ "organization",
+ "claim-with-delimiter",
+ },
+ Delimiter: "-",
+ ClearDelimiter: true,
+ Prefix: "clr_delim",
+ },
+ { // In this case the delimiter character("-") should be NOT removed from "claim-with-delimiter" claim.
+ // Should create "keep_delim-acme-foo-bar".
+ Claims: []string{
+ "organization",
+ "claim-with-delimiter",
+ },
+ Delimiter: "-",
+ // ClearDelimiter: false,
+ Prefix: "keep_delim",
+ },
+ { // Ignore non string claims (like arrays), this should result in "bk-emailvalue".
+ Claims: []string{
+ "non-string-claim",
+ "non-string-claim2",
+ "email",
+ },
+ Delimiter: "-",
+ Prefix: "bk",
+ },
+ },
+
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": "group1",
+ "organization": "acme",
+ "pipeline": "pipeline-one",
+ "email": "emailvalue",
+ "email_verified": true,
+ "claim-with-delimiter": "foo-bar",
+ "non-string-claim": []string{
+ "element1",
+ "element2",
+ },
+ "non-string-claim2": 666,
+ },
+ },
+ {
+ name: "prefixGroupNames",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"prefix-group1", "prefix-group2", "prefix-groupA", "prefix-groupB"},
+ expectedEmailField: "emailvalue",
+ groupsPrefix: "prefix-",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []string{"group1", "group2", "groupA", "groupB"},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "suffixGroupNames",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1-suffix", "group2-suffix", "groupA-suffix", "groupB-suffix"},
+ expectedEmailField: "emailvalue",
+ groupsSuffix: "-suffix",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []string{"group1", "group2", "groupA", "groupB"},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "preAndSuffixGroupNames",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"prefix-group1-suffix", "prefix-group2-suffix", "prefix-groupA-suffix", "prefix-groupB-suffix"},
+ expectedEmailField: "emailvalue",
+ groupsPrefix: "prefix-",
+ groupsSuffix: "-suffix",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []string{"group1", "group2", "groupA", "groupB"},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "filterGroupClaims",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ groupsRegex: `^.*\d$`,
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1", "group2"},
+ expectedEmailField: "emailvalue",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []string{"group1", "group2", "groupA", "groupB"},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
+ {
+ name: "filterGroupClaimsMap",
+ userIDKey: "", // not configured
+ userNameKey: "", // not configured
+ groupsRegex: `^.*\d$`,
+ expectUserID: "subvalue",
+ expectUserName: "namevalue",
+ expectGroups: []string{"group1", "group2"},
+ expectedEmailField: "emailvalue",
+ token: map[string]interface{}{
+ "sub": "subvalue",
+ "name": "namevalue",
+ "groups": []map[string]string{{"name": "group1"}, {"name": "group2"}, {"name": "groupA"}, {"name": "groupB"}},
+ "email": "emailvalue",
+ "email_verified": true,
+ },
+ },
}
for _, tc := range tests {
@@ -322,6 +519,10 @@ func TestHandleCallback(t *testing.T) {
config.ClaimMapping.PreferredUsernameKey = tc.preferredUsernameKey
config.ClaimMapping.EmailKey = tc.emailKey
config.ClaimMapping.GroupsKey = tc.groupsKey
+ config.ClaimMutations.NewGroupFromClaims = tc.newGroupFromClaims
+ config.ClaimMutations.FilterGroupClaims.GroupsFilter = tc.groupsRegex
+ config.ClaimMutations.ModifyGroupNames.Prefix = tc.groupsPrefix
+ config.ClaimMutations.ModifyGroupNames.Suffix = tc.groupsSuffix
conn, err := newConnector(config)
if err != nil {
@@ -428,6 +629,171 @@ func TestRefresh(t *testing.T) {
}
}
+func TestTokenIdentity(t *testing.T) {
+ tokenTypeAccess := "urn:ietf:params:oauth:token-type:access_token"
+ tokenTypeID := "urn:ietf:params:oauth:token-type:id_token"
+ long2short := map[string]string{
+ tokenTypeAccess: "access_token",
+ tokenTypeID: "id_token",
+ }
+
+ tests := []struct {
+ name string
+ subjectType string
+ userInfo bool
+ expectError bool
+ }{
+ {
+ name: "id_token",
+ subjectType: tokenTypeID,
+ }, {
+ name: "access_token",
+ subjectType: tokenTypeAccess,
+ expectError: true,
+ }, {
+ name: "id_token with user info",
+ subjectType: tokenTypeID,
+ userInfo: true,
+ }, {
+ name: "access_token with user info",
+ subjectType: tokenTypeAccess,
+ userInfo: true,
+ },
+ }
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ ctx := context.Background()
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
+ testServer, err := setupServer(map[string]any{
+ "sub": "subvalue",
+ "name": "namevalue",
+ }, true)
+ if err != nil {
+ t.Fatal("failed to setup test server", err)
+ }
+ conn, err := newConnector(Config{
+ Issuer: testServer.URL,
+ Scopes: []string{"openid", "groups"},
+ GetUserInfo: tc.userInfo,
+ })
+ if err != nil {
+ t.Fatal("failed to create new connector", err)
+ }
+
+ res, err := http.Get(testServer.URL + "/token")
+ if err != nil {
+ t.Fatal("failed to get initial token", err)
+ }
+ defer res.Body.Close()
+ var tokenResponse map[string]any
+ err = json.NewDecoder(res.Body).Decode(&tokenResponse)
+ if err != nil {
+ t.Fatal("failed to decode initial token", err)
+ }
+
+ origToken := tokenResponse[long2short[tc.subjectType]].(string)
+ identity, err := conn.TokenIdentity(ctx, tc.subjectType, origToken)
+ if err != nil {
+ if tc.expectError {
+ return
+ }
+ t.Fatal("failed to get token identity", err)
+ }
+
+ // assert identity
+ expectEquals(t, identity.UserID, "subvalue")
+ expectEquals(t, identity.Username, "namevalue")
+ })
+ }
+}
+
+func TestPromptType(t *testing.T) {
+ pointer := func(s string) *string {
+ return &s
+ }
+
+ tests := []struct {
+ name string
+ promptType *string
+ res string
+ }{
+ {name: "none", promptType: pointer("none"), res: "none"},
+ {name: "provided empty string", promptType: pointer(""), res: ""},
+ {name: "login", promptType: pointer("login"), res: "login"},
+ {name: "consent", promptType: pointer("consent"), res: "consent"},
+ {name: "default value", promptType: nil, res: "consent"},
+ }
+
+ testServer, err := setupServer(nil, true)
+ require.NoError(t, err)
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ conn, err := newConnector(Config{
+ Issuer: testServer.URL,
+ Scopes: []string{"openid", "groups"},
+ PromptType: tc.promptType,
+ })
+ require.NoError(t, err)
+
+ require.Equal(t, tc.res, conn.promptType)
+ })
+ }
+}
+
+func TestProviderOverride(t *testing.T) {
+ testServer, err := setupServer(map[string]any{
+ "sub": "subvalue",
+ "name": "namevalue",
+ }, true)
+ if err != nil {
+ t.Fatal("failed to setup test server", err)
+ }
+
+ t.Run("No override", func(t *testing.T) {
+ conn, err := newConnector(Config{
+ Issuer: testServer.URL,
+ Scopes: []string{"openid", "groups"},
+ })
+ if err != nil {
+ t.Fatal("failed to create new connector", err)
+ }
+
+ expAuth := fmt.Sprintf("%s/authorize", testServer.URL)
+ if conn.provider.Endpoint().AuthURL != expAuth {
+ t.Fatalf("unexpected auth URL: %s, expected: %s\n", conn.provider.Endpoint().AuthURL, expAuth)
+ }
+
+ expToken := fmt.Sprintf("%s/token", testServer.URL)
+ if conn.provider.Endpoint().TokenURL != expToken {
+ t.Fatalf("unexpected token URL: %s, expected: %s\n", conn.provider.Endpoint().TokenURL, expToken)
+ }
+ })
+
+ t.Run("Override", func(t *testing.T) {
+ conn, err := newConnector(Config{
+ Issuer: testServer.URL,
+ Scopes: []string{"openid", "groups"},
+ ProviderDiscoveryOverrides: ProviderDiscoveryOverrides{TokenURL: "/test1", AuthURL: "/test2"},
+ })
+ if err != nil {
+ t.Fatal("failed to create new connector", err)
+ }
+
+ expAuth := "/test2"
+ if conn.provider.Endpoint().AuthURL != expAuth {
+ t.Fatalf("unexpected auth URL: %s, expected: %s\n", conn.provider.Endpoint().AuthURL, expAuth)
+ }
+
+ expToken := "/test1"
+ if conn.provider.Endpoint().TokenURL != expToken {
+ t.Fatalf("unexpected token URL: %s, expected: %s\n", conn.provider.Endpoint().TokenURL, expToken)
+ }
+ })
+}
+
func setupServer(tok map[string]interface{}, idTokenDesired bool) (*httptest.Server, error) {
key, err := rsa.GenerateKey(rand.Reader, 1024)
if err != nil {
@@ -523,7 +889,7 @@ func newToken(key *jose.JSONWebKey, claims map[string]interface{}) (string, erro
}
func newConnector(config Config) (*oidcConnector, error) {
- logger := logrus.New()
+ logger := slog.New(slog.DiscardHandler)
conn, err := config.Open("id", logger)
if err != nil {
return nil, fmt.Errorf("unable to open: %v", err)
diff --git a/connector/openshift/openshift.go b/connector/openshift/openshift.go
index 81d2b35633..4519a85b6d 100644
--- a/connector/openshift/openshift.go
+++ b/connector/openshift/openshift.go
@@ -2,22 +2,18 @@ package openshift
import (
"context"
- "crypto/tls"
- "crypto/x509"
"encoding/json"
"fmt"
"io"
- "net"
+ "log/slog"
"net/http"
- "os"
"strings"
- "time"
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/httpclient"
"github.com/dexidp/dex/storage/kubernetes/k8sapi"
)
@@ -48,7 +44,7 @@ type openshiftConnector struct {
clientID string
clientSecret string
cancel context.CancelFunc
- logger log.Logger
+ logger *slog.Logger
httpClient *http.Client
oauth2Config *oauth2.Config
insecureCA bool
@@ -66,8 +62,13 @@ type user struct {
// Open returns a connector which can be used to login users through an upstream
// OpenShift OAuth2 provider.
-func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
- httpClient, err := newHTTPClient(c.InsecureCA, c.RootCA)
+func (c *Config) Open(id string, logger *slog.Logger) (conn connector.Connector, err error) {
+ var rootCAs []string
+ if c.RootCA != "" {
+ rootCAs = append(rootCAs, c.RootCA)
+ }
+
+ httpClient, err := httpclient.NewHTTPClient(rootCAs, c.InsecureCA)
if err != nil {
return nil, fmt.Errorf("failed to create HTTP client: %w", err)
}
@@ -77,13 +78,17 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
// OpenWithHTTPClient returns a connector which can be used to login users through an upstream
// OpenShift OAuth2 provider. It provides the ability to inject a http.Client.
-func (c *Config) OpenWithHTTPClient(id string, logger log.Logger,
+func (c *Config) OpenWithHTTPClient(id string, logger *slog.Logger,
httpClient *http.Client,
) (conn connector.Connector, err error) {
ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
wellKnownURL := strings.TrimSuffix(c.Issuer, "/") + wellKnownURLPath
req, err := http.NewRequest(http.MethodGet, wellKnownURL, nil)
+ if err != nil {
+ return nil, fmt.Errorf("failed to create a request to OpenShift endpoint %w", err)
+ }
openshiftConnector := openshiftConnector{
apiURL: c.Issuer,
@@ -91,7 +96,7 @@ func (c *Config) OpenWithHTTPClient(id string, logger log.Logger,
clientID: c.ClientID,
clientSecret: c.ClientSecret,
insecureCA: c.InsecureCA,
- logger: logger,
+ logger: logger.With(slog.Group("connector", "type", "openshift", "id", id)),
redirectURI: c.RedirectURI,
rootCA: c.RootCA,
groups: c.Groups,
@@ -105,14 +110,12 @@ func (c *Config) OpenWithHTTPClient(id string, logger log.Logger,
resp, err := openshiftConnector.httpClient.Do(req.WithContext(ctx))
if err != nil {
- cancel()
return nil, fmt.Errorf("failed to query OpenShift endpoint %w", err)
}
defer resp.Body.Close()
if err := json.NewDecoder(resp.Body).Decode(&metadata); err != nil {
- cancel()
return nil, fmt.Errorf("discovery through endpoint %s failed to decode body: %w",
wellKnownURL, err)
}
@@ -262,36 +265,3 @@ func validateAllowedGroups(userGroups, allowedGroups []string) bool {
return len(matchingGroups) != 0
}
-
-// newHTTPClient returns a new HTTP client
-func newHTTPClient(insecureCA bool, rootCA string) (*http.Client, error) {
- tlsConfig := tls.Config{}
- if insecureCA {
- tlsConfig = tls.Config{InsecureSkipVerify: true}
- } else if rootCA != "" {
- tlsConfig = tls.Config{RootCAs: x509.NewCertPool()}
- rootCABytes, err := os.ReadFile(rootCA)
- if err != nil {
- return nil, fmt.Errorf("failed to read root-ca: %w", err)
- }
- if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCABytes) {
- return nil, fmt.Errorf("no certs found in root CA file %q", rootCA)
- }
- }
-
- return &http.Client{
- Transport: &http.Transport{
- TLSClientConfig: &tlsConfig,
- Proxy: http.ProxyFromEnvironment,
- DialContext: (&net.Dialer{
- Timeout: 30 * time.Second,
- KeepAlive: 30 * time.Second,
- DualStack: true,
- }).DialContext,
- MaxIdleConns: 100,
- IdleConnTimeout: 90 * time.Second,
- TLSHandshakeTimeout: 10 * time.Second,
- ExpectContinueTimeout: 1 * time.Second,
- },
- }, nil
-}
diff --git a/connector/openshift/openshift_test.go b/connector/openshift/openshift_test.go
index 6280b831de..ef86e59314 100644
--- a/connector/openshift/openshift_test.go
+++ b/connector/openshift/openshift_test.go
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
+ "log/slog"
"net/http"
"net/http/httptest"
"net/url"
@@ -11,10 +12,10 @@ import (
"testing"
"time"
- "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"github.com/dexidp/dex/connector"
+ "github.com/dexidp/dex/pkg/httpclient"
"github.com/dexidp/dex/storage/kubernetes/k8sapi"
)
@@ -36,7 +37,7 @@ func TestOpen(t *testing.T) {
InsecureCA: true,
}
- logger := logrus.New()
+ logger := slog.New(slog.DiscardHandler)
oconfig, err := c.Open("id", logger)
@@ -70,7 +71,7 @@ func TestGetUser(t *testing.T) {
_, err = http.NewRequest("GET", hostURL.String(), nil)
expectNil(t, err)
- h, err := newHTTPClient(true, "")
+ h, err := httpclient.NewHTTPClient(nil, true)
expectNil(t, err)
@@ -128,7 +129,7 @@ func TestVerifyGroup(t *testing.T) {
_, err = http.NewRequest("GET", hostURL.String(), nil)
expectNil(t, err)
- h, err := newHTTPClient(true, "")
+ h, err := httpclient.NewHTTPClient(nil, true)
expectNil(t, err)
@@ -164,7 +165,7 @@ func TestCallbackIdentity(t *testing.T) {
req, err := http.NewRequest("GET", hostURL.String(), nil)
expectNil(t, err)
- h, err := newHTTPClient(true, "")
+ h, err := httpclient.NewHTTPClient(nil, true)
expectNil(t, err)
@@ -198,7 +199,7 @@ func TestRefreshIdentity(t *testing.T) {
})
defer s.Close()
- h, err := newHTTPClient(true, "")
+ h, err := httpclient.NewHTTPClient(nil, true)
expectNil(t, err)
oc := openshiftConnector{apiURL: s.URL, httpClient: h, oauth2Config: &oauth2.Config{
@@ -237,7 +238,7 @@ func TestRefreshIdentityFailure(t *testing.T) {
})
defer s.Close()
- h, err := newHTTPClient(true, "")
+ h, err := httpclient.NewHTTPClient(nil, true)
expectNil(t, err)
oc := openshiftConnector{apiURL: s.URL, httpClient: h, oauth2Config: &oauth2.Config{
diff --git a/connector/saml/saml.go b/connector/saml/saml.go
index 908ec703c9..3e44b477e3 100644
--- a/connector/saml/saml.go
+++ b/connector/saml/saml.go
@@ -8,6 +8,7 @@ import (
"encoding/pem"
"encoding/xml"
"fmt"
+ "log/slog"
"os"
"strings"
"sync"
@@ -21,10 +22,8 @@ import (
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/groups"
- "github.com/dexidp/dex/pkg/log"
)
-// nolint
const (
bindingRedirect = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
bindingPOST = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
@@ -120,11 +119,12 @@ func (c certStore) Certificates() (roots []*x509.Certificate, err error) {
// Open validates the config and returns a connector. It does not actually
// validate connectivity with the provider.
-func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
+func (c *Config) Open(id string, logger *slog.Logger) (connector.Connector, error) {
+ logger = logger.With(slog.Group("connector", "type", "saml", "id", id))
return c.openConnector(logger)
}
-func (c *Config) openConnector(logger log.Logger) (*provider, error) {
+func (c *Config) openConnector(logger *slog.Logger) (*provider, error) {
requiredFields := []struct {
name, val string
}{
@@ -252,7 +252,7 @@ type provider struct {
nameIDPolicyFormat string
- logger log.Logger
+ logger *slog.Logger
}
func (p *provider) POSTData(s connector.Scopes, id string) (action, value string, err error) {
@@ -292,7 +292,6 @@ func (p *provider) POSTData(s connector.Scopes, id string) (action, value string
// * Verify signature on XML document (or verify sig on assertion elements).
// * Verify various parts of the Assertion element. Conditions, audience, etc.
// * Map the Assertion's attribute elements to user info.
-//
func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo string) (ident connector.Identity, err error) {
rawResp, err := base64.StdEncoding.DecodeString(samlResponse)
if err != nil {
@@ -390,7 +389,7 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo str
// Log the actual attributes we got back from the server. This helps debug
// configuration errors on the server side, where the SAML server doesn't
// send us the correct attributes.
- p.logger.Infof("parsed and verified saml response attributes %s", attributes)
+ p.logger.Info("parsed and verified saml response attributes", "attributes", attributes)
// Grab the email.
if ident.Email, _ = attributes.get(p.emailAttr); ident.Email == "" {
@@ -468,7 +467,7 @@ func (p *provider) validateStatus(status *status) error {
if statusMessage != nil && statusMessage.Value != "" {
errorMessage += " -> " + statusMessage.Value
}
- return fmt.Errorf(errorMessage)
+ return errors.New(errorMessage)
}
return nil
}
@@ -531,7 +530,7 @@ func (p *provider) validateSubject(subject *subject, inResponseTo string) error
return fmt.Errorf("failed to validate subject confirmation: %v", errs)
}
-// validationConditions ensures that dex is the intended audience
+// validateConditions ensures that dex is the intended audience
// for the request, and not another service provider.
//
// See: https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
@@ -598,6 +597,9 @@ func verifyResponseSig(validator *dsig.ValidationContext, data []byte) (signed [
}
response := doc.Root()
+ if response == nil {
+ return nil, false, fmt.Errorf("parse document: empty root")
+ }
transformedResponse, err := validator.Validate(response)
if err == nil {
// Root element is verified, return it.
@@ -610,7 +612,7 @@ func verifyResponseSig(validator *dsig.ValidationContext, data []byte) (signed [
//
// TODO: Only select from child elements of the root.
assertion, err := etreeutils.NSSelectOne(response, "urn:oasis:names:tc:SAML:2.0:assertion", "Assertion")
- if err != nil {
+ if err != nil || assertion == nil {
return nil, false, fmt.Errorf("response does not contain an Assertion element")
}
transformedAssertion, err := validator.Validate(assertion)
diff --git a/connector/saml/saml_test.go b/connector/saml/saml_test.go
index 95d513ed19..03e891fe64 100644
--- a/connector/saml/saml_test.go
+++ b/connector/saml/saml_test.go
@@ -5,6 +5,7 @@ import (
"encoding/base64"
"encoding/pem"
"errors"
+ "log/slog"
"os"
"sort"
"testing"
@@ -12,7 +13,6 @@ import (
"github.com/kylelemons/godebug/pretty"
dsig "github.com/russellhaering/goxmldsig"
- "github.com/sirupsen/logrus"
"github.com/dexidp/dex/connector"
)
@@ -24,19 +24,18 @@ import (
// To add a new test, define a new, unsigned SAML 2.0 response that exercises some
// case, then sign it using the "testdata/gen.sh" script.
//
-// cp testdata/good-resp.tmpl testdata/( testname ).tmpl
-// vim ( testname ).tmpl # Modify your template for your test case.
-// vim testdata/gen.sh # Add a xmlsec1 command to the generation script.
-// ./testdata/gen.sh # Sign your template.
+// cp testdata/good-resp.tmpl testdata/( testname ).tmpl
+// vim ( testname ).tmpl # Modify your template for your test case.
+// vim testdata/gen.sh # Add a xmlsec1 command to the generation script.
+// ./testdata/gen.sh # Sign your template.
//
// To install xmlsec1 on Fedora run:
//
-// sudo dnf install xmlsec1 xmlsec1-openssl
+// sudo dnf install xmlsec1 xmlsec1-openssl
//
// On mac:
//
-// brew install Libxmlsec1
-//
+// brew install Libxmlsec1
type responseTest struct {
// CA file and XML file of the response.
caFile string
@@ -421,7 +420,7 @@ func (r responseTest) run(t *testing.T) {
t.Fatalf("parse test time: %v", err)
}
- conn, err := c.openConnector(logrus.New())
+ conn, err := c.openConnector(slog.New(slog.DiscardHandler))
if err != nil {
t.Fatal(err)
}
@@ -455,7 +454,7 @@ func (r responseTest) run(t *testing.T) {
}
func TestConfigCAData(t *testing.T) {
- logger := logrus.New()
+ logger := slog.New(slog.DiscardHandler)
validPEM, err := os.ReadFile("testdata/ca.crt")
if err != nil {
t.Fatal(err)
diff --git a/docker-compose.test.yaml b/docker-compose.test.yaml
index 46dfd84c4d..933ff80164 100644
--- a/docker-compose.test.yaml
+++ b/docker-compose.test.yaml
@@ -11,8 +11,8 @@ services:
LDAP_TLS: "true"
LDAP_TLS_VERIFY_CLIENT: try
ports:
- - 389:389
- - 636:636
+ - 3890:389
+ - 6360:636
volumes:
- ./connector/ldap/testdata/certs:/container/service/slapd/assets/certs
- ./connector/ldap/testdata/schema.ldif:/container/service/slapd/assets/config/bootstrap/ldif/99-schema.ldif
diff --git a/docs/enhancements/token-exchange-2023-02-03-#2812.md b/docs/enhancements/token-exchange-2023-02-03-#2812.md
new file mode 100644
index 0000000000..f9f556d26e
--- /dev/null
+++ b/docs/enhancements/token-exchange-2023-02-03-#2812.md
@@ -0,0 +1,175 @@
+# Dex Enhancement Proposal (DEP) 2812 - 2023-02-03 - Token Exchange
+
+## Table of Contents
+
+- [Summary](#summary)
+- [Motivation](#motivation)
+ - [Goals/Pain](#goals)
+ - [Non-Goals](#non-goals)
+- [Proposal](#proposal)
+ - [User Experience](#user-experience)
+ - [Implementation Details/Notes/Constraints](#implementation-detailsnotesconstraints)
+ - [Risks and Mitigations](#risks-and-mitigations)
+ - [Alternatives](#alternatives)
+- [Future Improvements](#future-improvements)
+
+## Summary
+
+[RFC 8693] specifies a new OAuth2 `grant_type` of `urn:ietf:params:oauth:grant-type:token-exchange`.
+Using this grant type, when clients start an authentication flow with Dex,
+in lieu of being redirected to their upstream IDP for authentication on demand,
+clients can present an independently obtained, valid token from their IDP to Dex.
+This is primarily useful in fully automated environments with job/machine identities,
+where there is no human in the loop to handle browser-based login flows.
+This DEP proposes to implement the new grant type for Dex.
+
+[RFC 8693]: https://www.rfc-editor.org/rfc/rfc8693.html
+
+## Context
+
+- [#1668 Question: non-web based clients?]
+ was closed with no real resolution
+- [#1484 Token exchange for external tokens]
+ mentions that Keycloak has a similar capability
+- [#2657 Get OIDC token issued by Dex using a token issued by one of the connectors]
+ is similar to the previous issue, but this time links to the new (January 2020) [RFC 8693].
+
+I believe the context for all of these are similar:
+a downstream project using Dex as its only IDP wants to grant access to programmatic clients
+without issuing long lived API tokens.
+
+Examples of downstream issues:
+
+- [argoproj/argo-cd#11632 ArgoCD SSO login via Azure AD Auth using OIDC not work for cli sso login]
+
+Other related Dex issues:
+
+- [#2450 Non-OIDC JWT Connector] is a functionally similar request, but expanded to arbitrary JWTs
+- [#1225 GitHub Non-Web application flow support] also asks for an exchange, but for an opaque GitHub PAT
+
+More broadly, this fits into recent movements to issue machine identities:
+
+- [GCP Service Identity](https://cloud.google.com/run/docs/securing/service-identity)
+- [AWS Execution Role](https://docs.aws.amazon.com/lambda/latest/dg/lambda-intro-execution-role.html)
+- [GitHub Actions OIDC](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
+- [CircleCI OIDC](https://circleci.com/docs/openid-connect-tokens/)
+- [Kubernetes Service Accounts](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/)
+- [SPIFFE](https://spiffe.io/)
+
+and granting access to resources based on trusting federated identities:
+
+- [GCP Workload Identity Federation](https://cloud.google.com/iam/docs/workload-identity-federation)
+- [AWS STS AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html)
+
+[#1484 Token exchange for external tokens]: https://github.com/dexidp/dex/issues/1484
+[#1668 Question: non-web based clients?]: https://github.com/dexidp/dex/issues/1668
+[#2657 Get OIDC token issued by Dex using a token issued by one of the connectors]: https://github.com/dexidp/dex/issues/2657
+[argoproj/argo-cd#11632 ArgoCD SSO login via Azure AD Auth using OIDC not work for cli sso login]: https://github.com/argoproj/argo-cd/issues/11632
+[#2450 Non-OIDC JWT Connector]: https://github.com/dexidp/dex/issues/2450
+[#1225 GitHub Non-Web application flow support]: https://github.com/dexidp/dex/issues/1225
+
+An initial attempt is at [#2806](https://github.com/dexidp/dex/pull/2806)
+
+## Motivation
+
+### Goals/Pain
+
+The goal is to allow programmatic access to Dex-protected resources
+without the use of static/long-lived secret tokens (API keys, username/password)
+or web-based redirect flows.
+Such scenarios are common in CI/CD workflows,
+and in general automation of common tasks.
+
+### Non-goals
+
+- Work will be scoped to just the OIDC connector
+- [RFC 8693 Section 2.1.1. Relationship between Resource, Audience, and Scope]
+ details more complex authorization checks based on targeted resources.
+ This is considered out of scope.
+
+[RFC 8693 Section 2.1.1. Relationship between Resource, Audience, and Scope]: https://www.rfc-editor.org/rfc/rfc8693.html#name-relationship-between-resour
+
+## Proposal
+
+### User Experience
+
+Clients can make `POST` requests with `application/x-www-form-urlencoded`
+parameters as specified by [RFC 8693] to Dex's `/token` endpoint.
+If successful, an access token will be returned,
+allowing direct authentication with Dex.
+No refresh tokens will be issued,
+perform a new exchange (possibly with refreshed upstream tokens) to obtain a new access token.
+
+The request parameters from [RFC 8693 Section 2.1](https://www.rfc-editor.org/rfc/rfc8693.html#name-request):
+
+- `grant_type`: REQUIRED - `urn:ietf:params:oauth:grant-type:token-exchange`
+- `resource`: OPTIONAL - the `audience` in the issued Dex token
+- `audience`: REQUIRED (RFC OPTIONAL) - the connector to verify the provided token against
+- `scope`: OPTIONAL - the `scope` in the issued Dex token
+- `requested_token_type`: OPTIONAL - one of `urn:ietf:params:oauth:token-type:access_token` or `urn:ietf:params:oauth:token-type:id_token`, defaulting to access token
+- `subject_token`: REQUIRED - the token issued by the upstream IDP
+- `subject_token_type`: REQUIRED - `urn:ietf:params:oauth:token-type:id_token` or `urn:ietf:params:oauth:token-type:access_token` if `getUserInfo` is `true`.
+- `actor_token`: OPTIONAL - unused
+- `actor_token_type`: OPTIONAL - unused
+
+The response parameters from [RFC 8693 Section 2.2](https://www.rfc-editor.org/rfc/rfc8693.html#name-response):
+
+- `access_token`: the issued token, the field is called `access_token` for legacy reasons
+- `issued_token_type`: the actual type of the issued token
+- `token_type`: the value `Bearer`
+- `expires_in`: validity lifetime in seconds
+- `scope`: the requested scope
+- `refresh_token`: unused
+
+The connector only needs to be configured with an issuer,
+no client ID / client secrets are necessary
+
+```yaml
+connectors:
+- type: oidc
+ id: my-platform
+ name: My Platform
+ config:
+ issuer: https://oidc.my-platform.example/
+```
+
+We expose a global and connector setting,
+`allowedGrantTypes: []string` defaulting to all implemented types.
+
+### Implementation Details/Notes/Constraints
+
+- Connectors expose a new interface `TokenIdentity` that will verify the given token and return the associated identity.
+ A Dex access/id token is then minted for the given identity.
+
+- `actor_token` and `actor_token_type` are "MUST ... if the actor token is present,
+ also perform the appropriate validation procedures for its indicated token type".
+ We will ignore these fields for the initial implementation.
+
+
+### Risks and Mitigations
+
+With token exchanges (sometimes known as identity impersonation),
+is they allow for easier lateral movement if an attacker gains access to an upstream token.
+We limit the potential impact by not issuing refresh tokens, preventing persistent access.
+Combined with short token lifetimes, it should limit the period of time between authentication to upstream IDPs.
+Additionally, a new `allowedGrantTypes` would allow for disabling exchanges if the functionality isn't needed.
+
+### Alternatives
+
+- Continue to use static keys -
+ this is a secret management nightmare
+ and quite painful when client storage of keys is [breached](https://circleci.com/blog/january-4-2023-security-alert/)
+
+## Future Improvements
+
+- Other connectors may wish to implement the same capability under Oauth
+- The password connector could be switch to support this new endpoint, submitting passwords as access tokens,
+ allowing for multiple password connectors to be configured
+- The `audience` field could be made optional if there is a single connector or the id token is inspected for issuer url
+- The `actor_token` and `actor_token_type` can be checked / validated if a suitable use case is determined.
+- A policy language like [cel] or [rego] as mentioned on [#1635 Connector Middleware]
+ would allow for stronger assertions of the provided identity against requested resource access.
+
+[cel]: https://github.com/google/cel-go
+[rego]: https://www.openpolicyagent.org/docs/latest/policy-language/
+[#1635 Connector Middleware]: https://github.com/dexidp/dex/issues/1635
diff --git a/examples/config-dev.yaml b/examples/config-dev.yaml
index bf11570a4e..0fdf350cab 100644
--- a/examples/config-dev.yaml
+++ b/examples/config-dev.yaml
@@ -52,6 +52,16 @@ web:
# https: 127.0.0.1:5554
# tlsCert: /etc/dex/tls.crt
# tlsKey: /etc/dex/tls.key
+ # headers:
+ # X-Frame-Options: "DENY"
+ # X-Content-Type-Options: "nosniff"
+ # X-XSS-Protection: "1; mode=block"
+ # Content-Security-Policy: "default-src 'self'"
+ # Strict-Transport-Security: "max-age=31536000; includeSubDomains"
+ # clientRemoteIP:
+ # header: X-Forwarded-For
+ # trustedProxies:
+ # - 10.0.0.0/8
# Configuration for dex appearance
# frontend:
@@ -91,7 +101,16 @@ telemetry:
# Default values shown below
# oauth2:
- # use ["code", "token", "id_token"] to enable implicit flow for web-only clients
+ # grantTypes determines the allowed set of authorization flows.
+# grantTypes:
+# - "authorization_code"
+# - "refresh_token"
+# - "implicit"
+# - "password"
+# - "urn:ietf:params:oauth:grant-type:device_code"
+# - "urn:ietf:params:oauth:grant-type:token-exchange"
+ # responseTypes determines the allowed response contents of a successful authorization flow.
+ # use ["code", "token", "id_token"] to enable implicit flow for web-only clients.
# responseTypes: [ "code" ] # also allowed are "token" and "id_token"
# By default, Dex will ask for approval to share data with application
# (approval for sharing data from connected IdP to Dex is separate process on IdP)
@@ -145,4 +164,8 @@ staticPasswords:
# bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
+ preferredUsername: "admin"
+ groups:
+ - "team-a"
+ - "team-a/admins"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
diff --git a/examples/example-app/main.go b/examples/example-app/main.go
index 451bea5b46..af56670402 100644
--- a/examples/example-app/main.go
+++ b/examples/example-app/main.go
@@ -3,8 +3,11 @@ package main
import (
"bytes"
"context"
+ "crypto/rand"
+ "crypto/sha256"
"crypto/tls"
"crypto/x509"
+ "encoding/base64"
"encoding/json"
"errors"
"fmt"
@@ -24,9 +27,20 @@ import (
const exampleAppState = "I wish to wash my irish wristwatch"
+var (
+ codeVerifier string
+ codeChallenge string
+)
+
+func init() {
+ codeVerifier = generateCodeVerifier()
+ codeChallenge = generateCodeChallenge(codeVerifier)
+}
+
type app struct {
clientID string
clientSecret string
+ pkce bool
redirectURI string
verifier *oidc.IDTokenVerifier
@@ -193,6 +207,7 @@ func cmd() *cobra.Command {
}
c.Flags().StringVar(&a.clientID, "client-id", "example-app", "OAuth2 client ID of this application.")
c.Flags().StringVar(&a.clientSecret, "client-secret", "ZXhhbXBsZS1hcHAtc2VjcmV0", "OAuth2 client secret of this application.")
+ c.Flags().BoolVar(&a.pkce, "pkce", true, "Use PKCE flow for the code exchange.")
c.Flags().StringVar(&a.redirectURI, "redirect-uri", "http://127.0.0.1:5555/callback", "Callback URL for OAuth2 responses.")
c.Flags().StringVar(&issuerURL, "issuer", "http://127.0.0.1:5556/dex", "URL of the OpenID Connect issuer.")
c.Flags().StringVar(&listen, "listen", "http://127.0.0.1:5555", "HTTP(S) address to listen at.")
@@ -243,14 +258,22 @@ func (a *app) handleLogin(w http.ResponseWriter, r *http.Request) {
authCodeURL := ""
scopes = append(scopes, "openid", "profile", "email")
- if r.FormValue("offline_access") != "yes" {
- authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
- } else if a.offlineAsScope {
+
+ var authCodeOptions []oauth2.AuthCodeOption
+
+ if a.pkce {
+ authCodeOptions = append(authCodeOptions, oauth2.SetAuthURLParam("code_challenge", codeChallenge))
+ authCodeOptions = append(authCodeOptions, oauth2.SetAuthURLParam("code_challenge_method", "S256"))
+ }
+
+ a.oauth2Config(scopes)
+ if r.FormValue("offline_access") == "yes" {
+ authCodeOptions = append(authCodeOptions, oauth2.AccessTypeOffline)
+ }
+ if a.offlineAsScope {
scopes = append(scopes, "offline_access")
- authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState)
- } else {
- authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState, oauth2.AccessTypeOffline)
}
+ authCodeURL = a.oauth2Config(scopes).AuthCodeURL(exampleAppState, authCodeOptions...)
if connectorID != "" {
authCodeURL = authCodeURL + "&connector_id=" + connectorID
}
@@ -282,7 +305,13 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("expected state %q got %q", exampleAppState, state), http.StatusBadRequest)
return
}
- token, err = oauth2Config.Exchange(ctx, code)
+
+ var authCodeOptions []oauth2.AuthCodeOption
+ if a.pkce {
+ authCodeOptions = append(authCodeOptions, oauth2.SetAuthURLParam("code_verifier", codeVerifier))
+ }
+
+ token, err = oauth2Config.Exchange(ctx, code, authCodeOptions...)
case http.MethodPost:
// Form request from frontend to refresh a token.
refresh := r.FormValue("refresh_token")
@@ -337,3 +366,16 @@ func (a *app) handleCallback(w http.ResponseWriter, r *http.Request) {
renderToken(w, a.redirectURI, rawIDToken, accessToken, token.RefreshToken, buff.String())
}
+
+func generateCodeVerifier() string {
+ bytes := make([]byte, 64) // 86 symbols Base64URL
+ if _, err := rand.Read(bytes); err != nil {
+ log.Fatalf("rand.Read error: %v", err)
+ }
+ return base64.RawURLEncoding.EncodeToString(bytes)
+}
+
+func generateCodeChallenge(verifier string) string {
+ hash := sha256.Sum256([]byte(verifier))
+ return base64.RawURLEncoding.EncodeToString(hash[:])
+}
diff --git a/examples/example-app/templates.go b/examples/example-app/templates.go
index a9425ead27..7107eb8739 100644
--- a/examples/example-app/templates.go
+++ b/examples/example-app/templates.go
@@ -6,38 +6,225 @@ import (
"net/http"
)
+const css = `
+ body {
+ font-family: Arial, sans-serif;
+ background-color: #f2f2f2;
+ margin: 0;
+ }
+
+ .header {
+ text-align: center;
+ margin-bottom: 20px;
+ }
+
+ .dex {
+ font-size: 2em;
+ font-weight: bold;
+ color: #3F9FD8; /* Main color */
+ }
+
+ .example-app {
+ font-size: 1em;
+ color: #EF4B5C; /* Secondary color */
+ }
+
+ .form-instructions {
+ text-align: center;
+ margin-bottom: 15px;
+ font-size: 1em;
+ color: #555;
+ }
+
+ hr {
+ border: none;
+ border-top: 1px solid #ccc;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ }
+
+ label {
+ flex: 1;
+ font-weight: bold;
+ color: #333;
+ }
+
+ p {
+ margin-bottom: 15px;
+ display: flex;
+ align-items: center;
+ }
+
+ input[type="text"] {
+ flex: 2;
+ padding: 8px;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ outline: none;
+ }
+
+ input[type="checkbox"] {
+ margin-left: 10px;
+ transform: scale(1.2);
+ }
+
+ .back-button {
+ display: inline-block;
+ padding: 8px 16px;
+ background-color: #EF4B5C; /* Secondary color */
+ color: white;
+ border: none;
+ border-radius: 4px;
+ cursor: pointer;
+ font-size: 12px;
+ text-decoration: none;
+ transition: background-color 0.3s ease, transform 0.2s ease;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ position: fixed;
+ right: 20px;
+ bottom: 20px;
+ }
+
+ .back-button:hover {
+ background-color: #C43B4B; /* Darker shade of secondary color */
+ }
+
+ .token-block {
+ background-color: #fff;
+ padding: 10px 15px;
+ border-radius: 8px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+ margin-bottom: 15px;
+ word-wrap: break-word;
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ position: relative;
+ }
+
+ .token-title {
+ font-weight: bold;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .token-title a {
+ font-size: 0.9em;
+ text-decoration: none;
+ color: #3F9FD8; /* Main color */
+ }
+
+ .token-title a:hover {
+ text-decoration: underline;
+ }
+
+ .token-code {
+ overflow-wrap: break-word;
+ word-break: break-all;
+ white-space: normal;
+ }
+
+ pre {
+ white-space: pre-wrap;
+ background-color: #f9f9f9;
+ padding: 8px;
+ border-radius: 4px;
+ border: 1px solid #ddd;
+ margin: 0;
+ font-family: 'Courier New', Courier, monospace;
+ overflow-x: auto;
+ font-size: 0.9em;
+ position: relative;
+ margin-top: 5px;
+ }
+
+ pre .key {
+ color: #c00;
+ }
+
+ pre .string {
+ color: #080;
+ }
+
+ pre .number {
+ color: #00f;
+ }
+`
+
var indexTmpl = template.Must(template.New("index.html").Parse(`
-
+
+
+
+
+
+ Example App - Login
-
-
+
+
+
-
+
`))
func renderIndex(w http.ResponseWriter) {
@@ -53,30 +240,116 @@ type tokenTmplData struct {
}
var tokenTmpl = template.Must(template.New("token.html").Parse(`
-
+
+
+
+
+
+ Tokens
-
-
- ID Token:
{{ .IDToken }}
- Access Token:
{{ .AccessToken }}
- Claims:
{{ .Claims }}
- {{ if .RefreshToken }}
- Refresh Token:
{{ .RefreshToken }}
-
- {{ end }}
-
+
+
+ {{ if .IDToken }}
+
+ {{ end }}
+
+ {{ if .AccessToken }}
+
+ {{ end }}
+
+ {{ if .Claims }}
+
+
Claims:
+
{{ .Claims }}
+
+ {{ end }}
+
+ {{ if .RefreshToken }}
+
+
Refresh Token:
+
{{ .RefreshToken }}
+
+
+ {{ end }}
+
+ Back to Home
+
+
+
`))
diff --git a/examples/go.mod b/examples/go.mod
index d66c118a7f..a7eed9e036 100644
--- a/examples/go.mod
+++ b/examples/go.mod
@@ -1,25 +1,22 @@
module github.com/dexidp/dex/examples
-go 1.17
+go 1.24.0
require (
- github.com/coreos/go-oidc/v3 v3.1.0
- github.com/dexidp/dex/api/v2 v2.0.0
- github.com/spf13/cobra v1.3.0
- golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
- google.golang.org/grpc v1.43.0
+ github.com/coreos/go-oidc/v3 v3.17.0
+ github.com/dexidp/dex/api/v2 v2.4.0
+ github.com/spf13/cobra v1.10.2
+ golang.org/x/oauth2 v0.34.0
+ google.golang.org/grpc v1.78.0
)
require (
- github.com/golang/protobuf v1.5.2 // indirect
- github.com/inconshreveable/mousetrap v1.0.0 // indirect
- github.com/spf13/pflag v1.0.5 // indirect
- golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce // indirect
- golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d // indirect
- golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 // indirect
- golang.org/x/text v0.3.7 // indirect
- google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 // indirect
- google.golang.org/protobuf v1.27.1 // indirect
- gopkg.in/square/go-jose.v2 v2.6.0 // indirect
+ github.com/go-jose/go-jose/v4 v4.1.3 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/spf13/pflag v1.0.9 // indirect
+ golang.org/x/net v0.47.0 // indirect
+ golang.org/x/sys v0.38.0 // indirect
+ golang.org/x/text v0.31.0 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda // indirect
+ google.golang.org/protobuf v1.36.10 // indirect
)
diff --git a/examples/go.sum b/examples/go.sum
index 7907afde92..9633cc0e0a 100644
--- a/examples/go.sum
+++ b/examples/go.sum
@@ -1,788 +1,54 @@
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
-github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/coreos/go-oidc/v3 v3.1.0 h1:6avEvcdvTa1qYsOZ6I5PRkSYHzpTNWgKYmaJfaYbrRw=
-github.com/coreos/go-oidc/v3 v3.1.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dexidp/dex/api/v2 v2.0.0 h1:bvge1sRmzVzWPWp4WlMzS04lcNQA+jFzHqKV3066bRw=
-github.com/dexidp/dex/api/v2 v2.0.0/go.mod h1:k5arBJT1QYvpsEY3sEd0NXJp3hKWKuUUfzJ3BlcqPdM=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
-github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
-github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
-github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
-github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
-github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
-github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
-github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
-github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
+github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
+github.com/dexidp/dex/api/v2 v2.4.0 h1:gNba7n6BKVp8X4Jp24cxYn5rIIGhM6kDOXcZoL6tr9A=
+github.com/dexidp/dex/api/v2 v2.4.0/go.mod h1:/p550ADvFFh7K95VmhUD+jgm15VdaNnab9td8DHOpyI=
+github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
+github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
-github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
-github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
-golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
-golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
-golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0 h1:aCsSLXylHWFno0r4S3joLpiaWayvqd2Mn4iSvx4WZZc=
-google.golang.org/genproto v0.0.0-20220114231437-d2e6a121cae0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
-google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
+github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
+go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
+go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
+golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
+golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
+golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
+golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
+golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda h1:i/Q+bfisr7gq6feoJnS/DlpdwEL4ihp41fvRiM3Ork0=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251029180050-ab9386a59fda/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
+google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
+google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
+google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
+google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
-gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
diff --git a/examples/grpc-client/README.md b/examples/grpc-client/README.md
index 59629e0590..6a78df9199 100644
--- a/examples/grpc-client/README.md
+++ b/examples/grpc-client/README.md
@@ -50,6 +50,9 @@ Running the gRPC client will cause the following API calls to be made to the ser
2. ListPasswords
3. VerifyPassword
4. DeletePassword
+5. CreateClient
+6. ListClients
+7. DeleteClient
## Cleaning up
diff --git a/examples/grpc-client/client.go b/examples/grpc-client/client.go
index fb8d4aaf06..7bbbac6494 100644
--- a/examples/grpc-client/client.go
+++ b/examples/grpc-client/client.go
@@ -58,7 +58,7 @@ func createPassword(cli api.DexClient) error {
// Create password.
if resp, err := cli.CreatePassword(context.TODO(), createReq); err != nil || resp.AlreadyExists {
- if resp != nil && resp.AlreadyExists {
+ if resp != nil && resp.AlreadyExists {
return fmt.Errorf("Password %s already exists", createReq.Password.Email)
}
return fmt.Errorf("failed to create password: %v", err)
@@ -125,6 +125,57 @@ func createPassword(cli api.DexClient) error {
return nil
}
+func createAndListClients(cli api.DexClient) error {
+ client := &api.Client{
+ Id: "example-client",
+ Secret: "example-secret",
+ RedirectUris: []string{"http://localhost:8080/callback"},
+ TrustedPeers: []string{},
+ Public: false,
+ Name: "Example Client",
+ LogoUrl: "http://example.com/logo.png",
+ }
+
+ createReq := &api.CreateClientReq{
+ Client: client,
+ }
+
+ if resp, err := cli.CreateClient(context.TODO(), createReq); err != nil || resp.AlreadyExists {
+ if resp != nil && resp.AlreadyExists {
+ log.Printf("Client %s already exists", createReq.Client.Id)
+ } else {
+ return fmt.Errorf("failed to create client: %v", err)
+ }
+ } else {
+ log.Printf("Created client with ID %s", createReq.Client.Id)
+ }
+
+ listResp, err := cli.ListClients(context.TODO(), &api.ListClientReq{})
+ if err != nil {
+ return fmt.Errorf("failed to list clients: %v", err)
+ }
+
+ log.Print("Listing Clients:\n")
+ for _, client := range listResp.Clients {
+ log.Printf("ID: %s, Name: %s, Public: %t, RedirectURIs: %v",
+ client.Id, client.Name, client.Public, client.RedirectUris)
+ }
+
+ deleteReq := &api.DeleteClientReq{
+ Id: client.Id,
+ }
+
+ if resp, err := cli.DeleteClient(context.TODO(), deleteReq); err != nil || resp.NotFound {
+ if resp != nil && resp.NotFound {
+ return fmt.Errorf("Client %s not found", deleteReq.Id)
+ }
+ return fmt.Errorf("failed to delete client: %v", err)
+ }
+ log.Printf("Deleted client with ID %s", deleteReq.Id)
+
+ return nil
+}
+
func main() {
caCrt := flag.String("ca-crt", "", "CA certificate")
clientCrt := flag.String("client-crt", "", "Client certificate")
@@ -143,4 +194,8 @@ func main() {
if err := createPassword(client); err != nil {
log.Fatalf("testPassword failed: %v", err)
}
+
+ if err := createAndListClients(client); err != nil {
+ log.Fatalf("testClients failed: %v", err)
+ }
}
diff --git a/examples/k8s/dex.yaml b/examples/k8s/dex.yaml
index 89ac40b223..41156af07f 100644
--- a/examples/k8s/dex.yaml
+++ b/examples/k8s/dex.yaml
@@ -23,7 +23,7 @@ spec:
spec:
serviceAccountName: dex # This is created below
containers:
- - image: ghcr.io/dexidp/dex:v2.30.0
+ - image: ghcr.io/dexidp/dex:v2.32.0
name: dex
command: ["/usr/local/bin/dex", "serve", "/etc/dex/cfg/config.yaml"]
@@ -106,6 +106,10 @@ data:
# bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
username: "admin"
+ preferredUsername: "admin"
+ groups:
+ - "team-a"
+ - "team-a/admins"
userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
---
apiVersion: v1
diff --git a/flake.lock b/flake.lock
index b67b61d98b..302d0832f1 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,39 +1,252 @@
{
"nodes": {
- "flake-utils": {
+ "cachix": {
+ "inputs": {
+ "devenv": [
+ "devenv"
+ ],
+ "flake-compat": [
+ "devenv"
+ ],
+ "git-hooks": [
+ "devenv",
+ "git-hooks"
+ ],
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1748883665,
+ "narHash": "sha256-R0W7uAg+BLoHjMRMQ8+oiSbTq8nkGz5RDpQ+ZfxxP3A=",
+ "owner": "cachix",
+ "repo": "cachix",
+ "rev": "f707778d902af4d62d8dd92c269f8e70de09acbe",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "ref": "latest",
+ "repo": "cachix",
+ "type": "github"
+ }
+ },
+ "devenv": {
+ "inputs": {
+ "cachix": "cachix",
+ "flake-compat": "flake-compat",
+ "git-hooks": "git-hooks",
+ "nix": "nix",
+ "nixpkgs": "nixpkgs"
+ },
+ "locked": {
+ "lastModified": 1755355634,
+ "narHash": "sha256-3UNeb5pBLHtTyYIkzF/3+2YlAKf6OuWQYUQO+qmInA4=",
+ "owner": "cachix",
+ "repo": "devenv",
+ "rev": "85e78cbe26467a2c23c9d34869235740132d749f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "devenv",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1747046372,
+ "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=",
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-parts": {
+ "inputs": {
+ "nixpkgs-lib": [
+ "devenv",
+ "nix",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1733312601,
+ "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "flake-parts_2": {
+ "inputs": {
+ "nixpkgs-lib": "nixpkgs-lib"
+ },
+ "locked": {
+ "lastModified": 1754487366,
+ "narHash": "sha256-pHYj8gUBapuUzKV/kN/tR3Zvqc7o6gdFB9XKXIp1SQ8=",
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "rev": "af66ad14b28a127c5c0f3bbb298218fc63528a18",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "flake-parts",
+ "type": "github"
+ }
+ },
+ "git-hooks": {
+ "inputs": {
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ]
+ },
"locked": {
- "lastModified": 1659877975,
- "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
- "owner": "numtide",
- "repo": "flake-utils",
- "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
+ "lastModified": 1750779888,
+ "narHash": "sha256-wibppH3g/E2lxU43ZQHC5yA/7kIKLGxVEnsnVK1BtRg=",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "rev": "16ec914f6fb6f599ce988427d9d94efddf25fe6d",
"type": "github"
},
"original": {
- "owner": "numtide",
- "repo": "flake-utils",
+ "owner": "cachix",
+ "repo": "git-hooks.nix",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "devenv",
+ "git-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "nix": {
+ "inputs": {
+ "flake-compat": [
+ "devenv",
+ "flake-compat"
+ ],
+ "flake-parts": "flake-parts",
+ "git-hooks-nix": [
+ "devenv",
+ "git-hooks"
+ ],
+ "nixpkgs": [
+ "devenv",
+ "nixpkgs"
+ ],
+ "nixpkgs-23-11": [
+ "devenv"
+ ],
+ "nixpkgs-regression": [
+ "devenv"
+ ]
+ },
+ "locked": {
+ "lastModified": 1755029779,
+ "narHash": "sha256-3+GHIYGg4U9XKUN4rg473frIVNn8YD06bjwxKS1IPrU=",
+ "owner": "cachix",
+ "repo": "nix",
+ "rev": "b0972b0eee6726081d10b1199f54de6d2917f861",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "ref": "devenv-2.30",
+ "repo": "nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
- "lastModified": 1662019588,
- "narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
+ "lastModified": 1750441195,
+ "narHash": "sha256-yke+pm+MdgRb6c0dPt8MgDhv7fcBbdjmv1ZceNTyzKg=",
+ "owner": "cachix",
+ "repo": "devenv-nixpkgs",
+ "rev": "0ceffe312871b443929ff3006960d29b120dc627",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "ref": "rolling",
+ "repo": "devenv-nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-lib": {
+ "locked": {
+ "lastModified": 1753579242,
+ "narHash": "sha256-zvaMGVn14/Zz8hnp4VWT9xVnhc8vuL3TStRqwk22biA=",
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "rev": "0f36c44e01a6129be94e3ade315a5883f0228a6e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-community",
+ "repo": "nixpkgs.lib",
+ "type": "github"
+ }
+ },
+ "nixpkgs_2": {
+ "locked": {
+ "lastModified": 1755268003,
+ "narHash": "sha256-nNaeJjo861wFR0tjHDyCnHs1rbRtrMgxAKMoig9Sj/w=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "2da64a81275b68fdad38af669afeda43d401e94b",
+ "rev": "32f313e49e42f715491e1ea7b306a87c16fe0388",
"type": "github"
},
"original": {
- "id": "nixpkgs",
- "ref": "nixos-unstable",
- "type": "indirect"
+ "owner": "NixOS",
+ "ref": "nixpkgs-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
}
},
"root": {
"inputs": {
- "flake-utils": "flake-utils",
- "nixpkgs": "nixpkgs"
+ "devenv": "devenv",
+ "flake-parts": "flake-parts_2",
+ "nixpkgs": "nixpkgs_2"
}
}
},
diff --git a/flake.nix b/flake.nix
index 155ebf99e3..214bf2337f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,27 +1,56 @@
{
- description = "OpenID Connect (OIDC) identity and OAuth 2.0 provider with pluggable connectors";
-
inputs = {
- nixpkgs.url = "nixpkgs/nixos-unstable";
- flake-utils.url = "github:numtide/flake-utils";
+ nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
+ flake-parts.url = "github:hercules-ci/flake-parts";
+ devenv.url = "github:cachix/devenv";
};
- outputs = { self, nixpkgs, flake-utils, ... }:
- flake-utils.lib.eachDefaultSystem (
- system:
- let
- pkgs = nixpkgs.legacyPackages.${system};
- buildDeps = with pkgs; [ git go_1_19 gnumake ];
- devDeps = with pkgs;
- buildDeps ++ [
- golangci-lint
- gotestsum
- protobuf
- protoc-gen-go
- protoc-gen-go-grpc
- kind
- ];
- in
- { devShell = pkgs.mkShell { buildInputs = devDeps; }; }
- );
+ outputs =
+ inputs@{ flake-parts, ... }:
+ flake-parts.lib.mkFlake { inherit inputs; } {
+ imports = [
+ inputs.devenv.flakeModule
+ ];
+
+ systems = [
+ "x86_64-linux"
+ "x86_64-darwin"
+ "aarch64-darwin"
+ "aarch64-linux"
+ ];
+
+ perSystem =
+ { pkgs, ... }:
+ rec {
+ devenv.shells = {
+ default = {
+ languages = {
+ go = {
+ enable = true;
+ package = pkgs.go_1_25;
+ };
+ };
+
+ packages = with pkgs; [
+ gnumake
+
+ # golangci-lint
+ (golangci-lint.override (o: {
+ buildGoModule = pkgs.buildGo125Module;
+ }))
+ gotestsum
+ protobuf
+ protoc-gen-go
+ protoc-gen-go-grpc
+ kind
+ ];
+
+ # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
+ containers = pkgs.lib.mkForce { };
+ };
+
+ ci = devenv.shells.default;
+ };
+ };
+ };
}
diff --git a/go.mod b/go.mod
index d15823aa1b..2c37d08c03 100644
--- a/go.mod
+++ b/go.mod
@@ -1,95 +1,115 @@
module github.com/dexidp/dex
-go 1.19
+go 1.25.0
require (
- entgo.io/ent v0.11.2
- github.com/AppsFlyer/go-sundheit v0.5.0
+ cloud.google.com/go/compute/metadata v0.9.0
+ entgo.io/ent v0.14.5
+ github.com/AppsFlyer/go-sundheit v0.6.0
github.com/Masterminds/semver v1.5.0
- github.com/Masterminds/sprig/v3 v3.2.2
- github.com/beevik/etree v1.1.0
- github.com/coreos/go-oidc/v3 v3.3.0
- github.com/dexidp/dex/api/v2 v2.1.0
- github.com/felixge/httpsnoop v1.0.3
+ github.com/Masterminds/sprig/v3 v3.3.0
+ github.com/beevik/etree v1.6.0
+ github.com/coreos/go-oidc/v3 v3.17.0
+ github.com/dexidp/dex/api/v2 v2.3.0
+ github.com/fsnotify/fsnotify v1.9.0
github.com/ghodss/yaml v1.0.0
- github.com/go-ldap/ldap/v3 v3.4.4
- github.com/go-sql-driver/mysql v1.6.0
- github.com/gorilla/handlers v1.5.1
- github.com/gorilla/mux v1.8.0
+ github.com/go-jose/go-jose/v4 v4.1.3
+ github.com/go-ldap/ldap/v3 v3.4.12
+ github.com/go-sql-driver/mysql v1.9.3
+ github.com/google/uuid v1.6.0
+ github.com/gorilla/handlers v1.5.2
+ github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/kylelemons/godebug v1.1.0
- github.com/lib/pq v1.10.5
+ github.com/lib/pq v1.10.9
github.com/mattermost/xml-roundtrip-validator v0.1.0
- github.com/mattn/go-sqlite3 v1.14.15
- github.com/oklog/run v1.1.0
+ github.com/mattn/go-sqlite3 v1.14.32
+ github.com/oklog/run v1.2.0
github.com/pkg/errors v0.9.1
- github.com/prometheus/client_golang v1.13.0
- github.com/russellhaering/goxmldsig v1.2.0
- github.com/sirupsen/logrus v1.9.0
- github.com/spf13/cobra v1.5.0
- github.com/stretchr/testify v1.8.0
- go.etcd.io/etcd/client/pkg/v3 v3.5.4
- go.etcd.io/etcd/client/v3 v3.5.4
- golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
- golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b
- golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
- google.golang.org/api v0.94.0
- google.golang.org/grpc v1.49.0
- google.golang.org/protobuf v1.28.1
- gopkg.in/square/go-jose.v2 v2.6.0
+ github.com/prometheus/client_golang v1.23.2
+ github.com/russellhaering/goxmldsig v1.5.0
+ github.com/spf13/cobra v1.10.2
+ github.com/stretchr/testify v1.11.1
+ go.etcd.io/etcd/client/pkg/v3 v3.6.7
+ go.etcd.io/etcd/client/v3 v3.6.7
+ golang.org/x/crypto v0.46.0
+ golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741
+ golang.org/x/net v0.48.0
+ golang.org/x/oauth2 v0.34.0
+ google.golang.org/api v0.257.0
+ google.golang.org/grpc v1.77.0
+ google.golang.org/protobuf v1.36.11
)
require (
- ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e // indirect
- cloud.google.com/go/compute v1.7.0 // indirect
- github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
+ ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 // indirect
+ cloud.google.com/go/auth v0.17.0 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
+ dario.cat/mergo v1.0.1 // indirect
+ filippo.io/edwards25519 v1.1.0 // indirect
+ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.1.1 // indirect
- github.com/agext/levenshtein v1.2.1 // indirect
- github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
+ github.com/Masterminds/semver/v3 v3.3.0 // indirect
+ github.com/agext/levenshtein v1.2.3 // indirect
+ github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cespare/xxhash/v2 v2.1.2 // indirect
- github.com/coreos/go-semver v0.3.0 // indirect
- github.com/coreos/go-systemd/v22 v22.3.2 // indirect
+ github.com/bmatcuk/doublestar v1.3.4 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/coreos/go-semver v0.3.1 // indirect
+ github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
+ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
+ github.com/go-logr/logr v1.4.3 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
- github.com/golang/protobuf v1.5.2 // indirect
- github.com/google/go-cmp v0.5.8 // indirect
- github.com/google/uuid v1.3.0 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.1.0 // indirect
- github.com/googleapis/gax-go/v2 v2.4.0 // indirect
- github.com/hashicorp/hcl/v2 v2.10.0 // indirect
- github.com/huandu/xstrings v1.3.1 // indirect
- github.com/imdario/mergo v0.3.11 // indirect
- github.com/inconshreveable/mousetrap v1.0.0 // indirect
- github.com/jonboulle/clockwork v0.2.2 // indirect
- github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
- github.com/mitchellh/copystructure v1.0.0 // indirect
- github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect
- github.com/mitchellh/reflectwalk v1.0.0 // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/go-cmp v0.7.0 // indirect
+ github.com/google/s2a-go v0.1.9 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect
+ github.com/googleapis/gax-go/v2 v2.15.0 // indirect
+ github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
+ github.com/hashicorp/hcl/v2 v2.18.1 // indirect
+ github.com/huandu/xstrings v1.5.0 // indirect
+ github.com/inconshreveable/mousetrap v1.1.0 // indirect
+ github.com/jonboulle/clockwork v0.5.0 // indirect
+ github.com/mattn/go-runewidth v0.0.9 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/go-wordwrap v1.0.1 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/client_model v0.2.0 // indirect
- github.com/prometheus/common v0.37.0 // indirect
- github.com/prometheus/procfs v0.8.0 // indirect
- github.com/shopspring/decimal v1.2.0 // indirect
- github.com/spf13/cast v1.4.1 // indirect
- github.com/spf13/pflag v1.0.5 // indirect
- github.com/zclconf/go-cty v1.8.0 // indirect
- go.etcd.io/etcd/api/v3 v3.5.4 // indirect
- go.opencensus.io v0.23.0 // indirect
- go.uber.org/atomic v1.7.0 // indirect
- go.uber.org/multierr v1.6.0 // indirect
- go.uber.org/zap v1.17.0 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
- golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
- golang.org/x/text v0.3.7 // indirect
- google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.66.1 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ github.com/shopspring/decimal v1.4.0 // indirect
+ github.com/spf13/cast v1.7.0 // indirect
+ github.com/spf13/pflag v1.0.9 // indirect
+ github.com/zclconf/go-cty v1.14.4 // indirect
+ github.com/zclconf/go-cty-yaml v1.1.0 // indirect
+ go.etcd.io/etcd/api/v3 v3.6.7 // indirect
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
+ go.opentelemetry.io/otel v1.38.0 // indirect
+ go.opentelemetry.io/otel/metric v1.38.0 // indirect
+ go.opentelemetry.io/otel/trace v1.38.0 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ go.uber.org/zap v1.27.0 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ golang.org/x/mod v0.30.0 // indirect
+ golang.org/x/sync v0.19.0 // indirect
+ golang.org/x/sys v0.39.0 // indirect
+ golang.org/x/text v0.32.0 // indirect
+ golang.org/x/tools v0.39.0 // indirect
+ golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/dexidp/dex/api/v2 => ./api/v2
+
+tool entgo.io/ent/cmd/ent
diff --git a/go.sum b/go.sum
index 27fdbb8eea..ad306a9ef9 100644
--- a/go.sum
+++ b/go.sum
@@ -1,923 +1,302 @@
-ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e h1:/r1xGMwmLg4LZ2V3/wWui9TtM3+STh1fp5ExSVRNFZo=
-ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e/go.mod h1:ofVetkJqlaWle3mvYmaS2uyFGFcc7dSq436tmxa/Mzk=
-cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
-cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
-cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
-cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
-cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
-cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
-cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
-cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
-cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
-cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
-cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
-cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
-cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
-cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
-cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
-cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
-cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
-cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
-cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
-cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
-cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
-cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
-cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
-cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
-cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
-cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
-cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
-cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
-cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
-cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
-cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-entgo.io/ent v0.11.2 h1:UM2/BUhF2FfsxPHRxLjQbhqJNaDdVlOwNIAMLs2jyto=
-entgo.io/ent v0.11.2/go.mod h1:YGHEQnmmIUgtD5b1ICD5vg74dS3npkNnmC5K+0J+IHU=
-github.com/AppsFlyer/go-sundheit v0.5.0 h1:/VxpyigCfJrq1r97mn9HPiAB2qrhcTFHwNIIDr15CZM=
-github.com/AppsFlyer/go-sundheit v0.5.0/go.mod h1:2ZM0BnfqT/mljBQO224VbL5XH06TgWuQ6Cn+cTtCpTY=
-github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
-github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9 h1:E0wvcUXTkgyN4wy4LGtNzMNGMytJN8afmIWXJVMi4cc=
+ariga.io/atlas v0.32.1-0.20250325101103-175b25e1c1b9/go.mod h1:Oe1xWPuu5q9LzyrWfbZmEZxFYeu4BHTyzfjeW2aZp/w=
+cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
+cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
+cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
+cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
+cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
+cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+entgo.io/ent v0.14.5 h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=
+entgo.io/ent v0.14.5/go.mod h1:zTzLmWtPvGpmSwtkaayM2cm5m819NdM7z7tYPq3vN0U=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
+github.com/AppsFlyer/go-sundheit v0.6.0 h1:d2hBvCjBSb2lUsEWGfPigr4MCOt04sxB+Rppl0yUMSk=
+github.com/AppsFlyer/go-sundheit v0.6.0/go.mod h1:LDdBHD6tQBtmHsdW+i1GwdTt6Wqc0qazf5ZEJVTbTME=
+github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
+github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
+github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q5eFN3EC/SaM=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
-github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
-github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
-github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
-github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
-github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
-github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
-github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
-github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
-github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
+github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
+github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
+github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
+github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
+github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI=
+github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
+github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
+github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
+github.com/beevik/etree v1.6.0 h1:u8Kwy8pp9D9XeITj2Z0XtA5qqZEmtJtuXZRQi+j03eE=
+github.com/beevik/etree v1.6.0/go.mod h1:bh4zJxiIr62SOf9pRzN7UUYaEDa9HEKafK25+sLc0Gc=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/coreos/go-oidc/v3 v3.3.0 h1:Y1LV3mP+QT3MEycATZpAiwfyN+uxZLqVbAHJUuOJEe4=
-github.com/coreos/go-oidc/v3 v3.3.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
-github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=
+github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
+github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
+github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
+github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
+github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
+github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
-github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
+github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
+github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
+github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
-github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
-github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
-github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo=
+github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
+github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
+github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
+github.com/go-ldap/ldap/v3 v3.4.12 h1:1b81mv7MagXZ7+1r7cLTWmyuTqVqdwbtJSjC0DAp9s4=
+github.com/go-ldap/ldap/v3 v3.4.12/go.mod h1:+SPAGcTtOfmGsCb3h1RFiq4xpp4N636G75OEace8lNo=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
+github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
-github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=
+github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
-github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
-github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
-github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
-github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
-github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
-github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
-github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
-github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
-github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
-github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
-github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
-github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
-github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
-github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
-github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
-github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
-github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ=
+github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
+github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
+github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
+github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
+github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
+github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
+github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg=
-github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg=
-github.com/huandu/xstrings v1.3.1 h1:4jgBlKK6tLKFvO8u5pmYjG91cqytmDCDvGh7ECVFfFs=
-github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
-github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
+github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
+github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo=
+github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
+github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
+github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
+github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
+github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
+github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
+github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
+github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
+github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
+github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
+github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
+github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
+github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
+github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
+github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
-github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
-github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
-github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
-github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
-github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
-github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
-github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
+github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
+github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
+github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
+github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
+github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
+github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
+github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
+github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/oklog/run v1.2.0 h1:O8x3yXwah4A73hJdlrwo/2X6J62gE5qTMusH0dvz60E=
+github.com/oklog/run v1.2.0/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk=
+github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
+github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
-github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
-github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
-github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
-github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
+github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
+github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
+github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
+github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=
+github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
-github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
-github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
-github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
-github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
-github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
+github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
+github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
+github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
+github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
+github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
+github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
+github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
+github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
-github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
-github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
-github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
-github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
-github.com/zclconf/go-cty v1.8.0 h1:s4AvqaeQzJIu3ndv4gVIhplVD0krU+bgrcLSVUnaWuA=
-github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk=
-github.com/zclconf/go-cty-debug v0.0.0-20191215020915-b22d67c1ba0b/go.mod h1:ZRKQfBXbGkpdV6QMzT3rU1kSTAnfu1dO8dPKjYprgj8=
-go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
-go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
-go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
-go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
-go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
-go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
-go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
-go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
-go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
-go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
+github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
+github.com/zclconf/go-cty-yaml v1.1.0 h1:nP+jp0qPHv2IhUVqmQSzjvqAWcObN0KBkUl2rWBdig0=
+github.com/zclconf/go-cty-yaml v1.1.0/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JAprQNgxSZ1Gyxs=
+go.etcd.io/etcd/api/v3 v3.6.7 h1:7BNJ2gQmc3DNM+9cRkv7KkGQDayElg8x3X+tFDYS+E0=
+go.etcd.io/etcd/api/v3 v3.6.7/go.mod h1:xJ81TLj9hxrYYEDmXTeKURMeY3qEDN24hqe+q7KhbnI=
+go.etcd.io/etcd/client/pkg/v3 v3.6.7 h1:vvzgyozz46q+TyeGBuFzVuI53/yd133CHceNb/AhBVs=
+go.etcd.io/etcd/client/pkg/v3 v3.6.7/go.mod h1:2IVulJ3FZ/czIGl9T4lMF1uxzrhRahLqe+hSgy+Kh7Q=
+go.etcd.io/etcd/client/v3 v3.6.7 h1:9WqA5RpIBtdMxAy1ukXLAdtg2pAxNqW5NUoO2wQrE6U=
+go.etcd.io/etcd/client/v3 v3.6.7/go.mod h1:2XfROY56AXnUqGsvl+6k29wrwsSbEh1lAouQB1vHpeE=
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
+go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
+go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
+go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
+go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
+go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
+go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
+go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
+go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
+go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
+go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
+go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
+go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
-golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
-golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
-golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
-golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
-golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
-golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
-golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
-golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
-golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
-golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
-golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
-golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
-golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
-golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
+golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
+golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741 h1:fGZugkZk2UgYBxtpKmvub51Yno1LJDeEsRp2xGD+0gY=
+golang.org/x/exp v0.0.0-20221004215720-b9f4876ce741/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
+golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
-golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
-golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
+golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
+golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
+golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
+golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
+golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
+golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
+golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
+golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
-golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
-golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
-golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
-golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
-golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
-golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
+golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
+golang.org/x/tools/go/expect v0.1.0-deprecated h1:jY2C5HGYR5lqex3gEniOQL0r7Dq5+VGVgY1nudX5lXY=
+golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
+golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM=
+golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
-google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
-google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
-google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
-google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
-google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
-google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
-google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
-google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
-google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
-google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.94.0 h1:KtKM9ru3nzQioV1HLlUf1cR7vMYJIpgls5VhAYQXIwA=
-google.golang.org/api v0.94.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
-google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
-google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
-google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
-google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
-google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
-google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
-google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
-google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
-google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
-google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f h1:hJ/Y5SqPXbarffmAsApliUlcvMU+wScNGfyop4bZm8o=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
-google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
-google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
-google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
-google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
-google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
-google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
-google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
-google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
-google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
-google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
-google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
-google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
-google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
-google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
-google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
+gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA=
+google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4=
+google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s=
+google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
+google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
+google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
+google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
-gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/pkg/featureflags/flag.go b/pkg/featureflags/flag.go
new file mode 100644
index 0000000000..98729ac9ed
--- /dev/null
+++ b/pkg/featureflags/flag.go
@@ -0,0 +1,33 @@
+package featureflags
+
+import (
+ "os"
+ "strconv"
+ "strings"
+)
+
+type flag struct {
+ Name string
+ Default bool
+}
+
+func (f *flag) env() string {
+ return "DEX_" + strings.ToUpper(f.Name)
+}
+
+func (f *flag) Enabled() bool {
+ raw := os.Getenv(f.env())
+ if raw == "" {
+ return f.Default
+ }
+
+ res, err := strconv.ParseBool(raw)
+ if err != nil {
+ return f.Default
+ }
+ return res
+}
+
+func newFlag(s string, d bool) *flag {
+ return &flag{Name: s, Default: d}
+}
diff --git a/pkg/featureflags/set.go b/pkg/featureflags/set.go
new file mode 100644
index 0000000000..bd86f1e749
--- /dev/null
+++ b/pkg/featureflags/set.go
@@ -0,0 +1,17 @@
+package featureflags
+
+var (
+ // EntEnabled enables experimental ent-based engine for the database storages.
+ // https://entgo.io/
+ EntEnabled = newFlag("ent_enabled", false)
+
+ // ExpandEnv can enable or disable env expansion in the config which can be useful in environments where, e.g.,
+ // $ sign is a part of the password for LDAP user.
+ ExpandEnv = newFlag("expand_env", true)
+
+ // APIConnectorsCRUD allows CRUD operations on connectors through the gRPC API
+ APIConnectorsCRUD = newFlag("api_connectors_crud", false)
+
+ // ContinueOnConnectorFailure allows the server to start even if some connectors fail to initialize.
+ ContinueOnConnectorFailure = newFlag("continue_on_connector_failure", false)
+)
diff --git a/pkg/httpclient/httpclient.go b/pkg/httpclient/httpclient.go
new file mode 100644
index 0000000000..671e0e7754
--- /dev/null
+++ b/pkg/httpclient/httpclient.go
@@ -0,0 +1,64 @@
+package httpclient
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "encoding/base64"
+ "fmt"
+ "net"
+ "net/http"
+ "os"
+ "time"
+)
+
+func extractCAs(input []string) [][]byte {
+ result := make([][]byte, 0, len(input))
+ for _, ca := range input {
+ if ca == "" {
+ continue
+ }
+
+ pemData, err := os.ReadFile(ca)
+ if err != nil {
+ pemData, err = base64.StdEncoding.DecodeString(ca)
+ if err != nil {
+ pemData = []byte(ca)
+ }
+ }
+
+ result = append(result, pemData)
+ }
+ return result
+}
+
+func NewHTTPClient(rootCAs []string, insecureSkipVerify bool) (*http.Client, error) {
+ pool, err := x509.SystemCertPool()
+ if err != nil {
+ return nil, err
+ }
+
+ tlsConfig := tls.Config{RootCAs: pool, InsecureSkipVerify: insecureSkipVerify}
+ for index, rootCABytes := range extractCAs(rootCAs) {
+ if !tlsConfig.RootCAs.AppendCertsFromPEM(rootCABytes) {
+ return nil, fmt.Errorf("rootCAs.%d is not in PEM format, certificate must be "+
+ "a PEM encoded string, a base64 encoded bytes that contain PEM encoded string, "+
+ "or a path to a PEM encoded certificate", index)
+ }
+ }
+
+ return &http.Client{
+ Transport: &http.Transport{
+ TLSClientConfig: &tlsConfig,
+ Proxy: http.ProxyFromEnvironment,
+ DialContext: (&net.Dialer{
+ Timeout: 30 * time.Second,
+ KeepAlive: 30 * time.Second,
+ DualStack: true,
+ }).DialContext,
+ MaxIdleConns: 100,
+ IdleConnTimeout: 90 * time.Second,
+ TLSHandshakeTimeout: 10 * time.Second,
+ ExpectContinueTimeout: 1 * time.Second,
+ },
+ }, nil
+}
diff --git a/pkg/httpclient/httpclient_test.go b/pkg/httpclient/httpclient_test.go
new file mode 100644
index 0000000000..6f561c1030
--- /dev/null
+++ b/pkg/httpclient/httpclient_test.go
@@ -0,0 +1,83 @@
+package httpclient_test
+
+import (
+ "crypto/tls"
+ "encoding/base64"
+ "fmt"
+ "io"
+ "net/http"
+ "net/http/httptest"
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/dexidp/dex/pkg/httpclient"
+)
+
+func TestRootCAs(t *testing.T) {
+ ts, err := NewLocalHTTPSTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprint(w, "Hello, client")
+ }))
+ assert.Nil(t, err)
+ defer ts.Close()
+
+ runTest := func(name string, certs []string) {
+ t.Run(name, func(t *testing.T) {
+ rootCAs := certs
+ testClient, err := httpclient.NewHTTPClient(rootCAs, false)
+ assert.Nil(t, err)
+
+ res, err := testClient.Get(ts.URL)
+ assert.Nil(t, err)
+
+ greeting, err := io.ReadAll(res.Body)
+ res.Body.Close()
+ assert.Nil(t, err)
+
+ assert.Equal(t, "Hello, client", string(greeting))
+ })
+ }
+
+ runTest("From file", []string{"testdata/rootCA.pem"})
+
+ content, err := os.ReadFile("testdata/rootCA.pem")
+ assert.NoError(t, err)
+ runTest("From string", []string{string(content)})
+
+ contentStr := base64.StdEncoding.EncodeToString(content)
+ runTest("From bytes", []string{contentStr})
+}
+
+func TestInsecureSkipVerify(t *testing.T) {
+ ts, err := NewLocalHTTPSTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fmt.Fprint(w, "Hello, client")
+ }))
+ assert.Nil(t, err)
+ defer ts.Close()
+
+ insecureSkipVerify := true
+
+ testClient, err := httpclient.NewHTTPClient(nil, insecureSkipVerify)
+ assert.Nil(t, err)
+
+ res, err := testClient.Get(ts.URL)
+ assert.Nil(t, err)
+
+ greeting, err := io.ReadAll(res.Body)
+ res.Body.Close()
+ assert.Nil(t, err)
+
+ assert.Equal(t, "Hello, client", string(greeting))
+}
+
+func NewLocalHTTPSTestServer(handler http.Handler) (*httptest.Server, error) {
+ ts := httptest.NewUnstartedServer(handler)
+ cert, err := tls.LoadX509KeyPair("testdata/server.crt", "testdata/server.key")
+ if err != nil {
+ return nil, err
+ }
+ ts.TLS = &tls.Config{Certificates: []tls.Certificate{cert}}
+ ts.StartTLS()
+ return ts, nil
+}
diff --git a/pkg/httpclient/readme.md b/pkg/httpclient/readme.md
new file mode 100644
index 0000000000..cc26252293
--- /dev/null
+++ b/pkg/httpclient/readme.md
@@ -0,0 +1,44 @@
+# Regenerate testdata
+
+### server.csr.cnf
+
+```
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+distinguished_name = dn
+
+[dn]
+C=US
+ST=RandomState
+L=RandomCity
+O=RandomOrganization
+OU=RandomOrganizationUnit
+emailAddress=hello@example.com
+CN = localhost
+```
+
+and
+
+### v3.ext
+```
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = localhost
+IP.1 = 127.0.0.1
+```
+
+### Then enter the following commands:
+
+`openssl genrsa -out rootCA.key 2048`
+
+`openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.pem -config server.csr.cnf`
+
+`openssl req -new -sha256 -nodes -out server.csr -newkey rsa:2048 -keyout server.key -config server.csr.cnf`
+
+`openssl x509 -req -in server.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out server.crt -days 3650 -sha256 -extfile v3.ext`
diff --git a/pkg/httpclient/testdata/rootCA.key b/pkg/httpclient/testdata/rootCA.key
new file mode 100644
index 0000000000..9c4eeee12a
--- /dev/null
+++ b/pkg/httpclient/testdata/rootCA.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEA4dB5aQCjCmMsW71u9F0WNm1TYjXQBZ4p7oNT+BQwCc/MZ2xc
+5NexS2O86nbRkw5jwyfAAMSMKRr9s2FluVTHqiln78rg+XUgmrmNT3ZroLmW6QL6
+Ca8dbMPky+tQclZsvMd3HAeCyyrs4pf7wM1AyUJD7H0xAlVD1fsohkg7jhBFUfV+
+q2VMMdnsaV5vFrW/2vPBWz1SNPW/Xm+Ilny7xg9njQLcPMNtVtF+7EPB6sxD6qrj
+BC+Kj5zQ3bZOfdrh7yy63dbh/Kh+3NScgO+k+x92HlAjRIvj5y4KrbGZl7CmOth5
+y7fPywApVbDfZRWJChI1PVflOyDdnC+vhMLbHQIDAQABAoIBAEmjrrQrXP/6L3EL
+aa+O27uME3Enk1sBpTL+6Ncx3iiU91eS4whNvqeTMvxTGy0VuDrgL6EQd5TAFJP2
+4zF5EFPRhO+R/aPcKnHKqOaM+7RCUZBTRC78SGA70dUeO/HNdVBqy9D8Mg8HRJDw
+d0z8om//iB8LBHx6SdDyQtjnnWRKFTzQRurBBoyLe2vPMFtINKtNUkahjc8HE4GO
+aIv1LICJUzf4ZnkntKd5cFHZ42R2Tmfj0Y9G9DyJbuSA3+0u5IhYB39Uy6jFxLi8
+I5PoIVhgYZ0aivsVBIviShwQ9kgv6807YBxt22eSNovBDrSp+cAnIF9+p0b3MnkU
+aCHSiBECgYEA84lssi6AqfCEsSiQMSM9kMCXJ4KQI/l7pmrIA50+V5HSEby9lg2Y
+N6XJ4V4q46t8FcZBjmMvzn9fwiPMRw5e995cVNBQ31a1FX/1Hy6RNtEiLZRnkHI5
+WznY9IxQ+c9JXJeFY1sO0BfO0TS3WvOf1rwqOb92q+cQaItnPQ+4Ya8CgYEA7V7e
+IqW3PpO4H+c5hH9egM0BjAxH71C9YpYzZpF9uiPIkuMnJ8nm9bB6RiuDaYCxvrfE
+A0h/SQewoYJKL4OfKGjrbG7U4zLMZHIWlf8Za55Zik5BNjvgBqFFrrSgLUGxdRTX
+N0+TlWlW1bvJblWpdjIbJbg/6kCU98TzK852fvMCgYAWYa/apElw1MjtGyQ9T9bN
+odWCbQ5gMAJ8Jd4h7uaW17DtrmHiE3fEzXjDPItGhzENMz49HsJ7ANvFFNMmSJzT
+vNzRcp+sFuTnh+34Iqh32DqC49usu8KnrqZQu0CJ5NICL26z1d+DolyAf47GThOH
+gZ2D1yPJ4p9wbDddtj8kwwKBgCFKB68mPG+rOcxHmjppvnAj0A66/i+izBySYf0F
+dHNxZ0SqVKhw2VIlgNBsc86M/OB5VyT6utccG/paklrdg6mgJTwcwwBl9GI12dMJ
+ZqBAIeCSnvSjKwTjAynALSKLrv5zgMdCArmWf1YUMuilXNG1rzb4AwawLfQdi9jd
+6KJfAoGBALFl6ldywl3sGPk9K2xCDYYhb1TNQyheA5YvoZzZ6XCo1q0Lbwy/FamZ
+0TSWkoEmGB/Hck3HgtZDRo3CTI1vYfbpAtgI7oD1NA1zMaLulNQxKjH3iVvyb+R7
+ZcIT7EVPZgkUwr0bsp22yVDekh/CHoB6FZPCyoAb8WnfJfooTBzB
+-----END RSA PRIVATE KEY-----
diff --git a/pkg/httpclient/testdata/rootCA.pem b/pkg/httpclient/testdata/rootCA.pem
new file mode 100644
index 0000000000..c03bdac0c0
--- /dev/null
+++ b/pkg/httpclient/testdata/rootCA.pem
@@ -0,0 +1,23 @@
+-----BEGIN CERTIFICATE-----
+MIID1jCCAr4CCQCG4JBeSi6cDjANBgkqhkiG9w0BAQsFADCBrDELMAkGA1UEBhMC
+VVMxFDASBgNVBAgMC1JhbmRvbVN0YXRlMRMwEQYDVQQHDApSYW5kb21DaXR5MRsw
+GQYDVQQKDBJSYW5kb21Pcmdhbml6YXRpb24xHzAdBgNVBAsMFlJhbmRvbU9yZ2Fu
+aXphdGlvblVuaXQxIDAeBgkqhkiG9w0BCQEWEWhlbGxvQGV4YW1wbGUuY29tMRIw
+EAYDVQQDDAlsb2NhbGhvc3QwHhcNMjIxMDA3MjIwNjQwWhcNMzIxMDA0MjIwNjQw
+WjCBrDELMAkGA1UEBhMCVVMxFDASBgNVBAgMC1JhbmRvbVN0YXRlMRMwEQYDVQQH
+DApSYW5kb21DaXR5MRswGQYDVQQKDBJSYW5kb21Pcmdhbml6YXRpb24xHzAdBgNV
+BAsMFlJhbmRvbU9yZ2FuaXphdGlvblVuaXQxIDAeBgkqhkiG9w0BCQEWEWhlbGxv
+QGV4YW1wbGUuY29tMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQDh0HlpAKMKYyxbvW70XRY2bVNiNdAFninug1P4FDAJ
+z8xnbFzk17FLY7zqdtGTDmPDJ8AAxIwpGv2zYWW5VMeqKWfvyuD5dSCauY1Pdmug
+uZbpAvoJrx1sw+TL61ByVmy8x3ccB4LLKuzil/vAzUDJQkPsfTECVUPV+yiGSDuO
+EEVR9X6rZUwx2expXm8Wtb/a88FbPVI09b9eb4iWfLvGD2eNAtw8w21W0X7sQ8Hq
+zEPqquMEL4qPnNDdtk592uHvLLrd1uH8qH7c1JyA76T7H3YeUCNEi+PnLgqtsZmX
+sKY62HnLt8/LAClVsN9lFYkKEjU9V+U7IN2cL6+EwtsdAgMBAAEwDQYJKoZIhvcN
+AQELBQADggEBAN6g0qit/3R2X+KdR0LgRXF/h4qQFgcV6cxnhRAmLIDNJlxKSHqN
+IE5+bxzCbkblzGfr/jNPqW0s+yaN4CyMgKNYSzkLBPE4FF+19Uv+dyYfFms3mDJ7
+0rGjS5bCscThWhpaSw20LcwQcr/+X+/fGzJ01dVFK1UOjBKg4d4dMwxklbIkZqIq
+siRW0GMy26mgVZ/BSjeh5kEjs6h6H3cJsGl7xYT+BI7wnxHwGeT9tkBgiyT5FwaS
+vtdZkBpQ9q8f7FwsEm3woLHdWuOnrtUtVpY/oc6WFGdROQdGzjSk0D3kHs9YhueC
+GSzZKrqX+TSIgpPrLYNHX4uxlo5TAwP/5GM=
+-----END CERTIFICATE-----
diff --git a/pkg/httpclient/testdata/rootCA.srl b/pkg/httpclient/testdata/rootCA.srl
new file mode 100644
index 0000000000..214ae68bf1
--- /dev/null
+++ b/pkg/httpclient/testdata/rootCA.srl
@@ -0,0 +1 @@
+C1B35F0051A641BB
diff --git a/pkg/httpclient/testdata/server.crt b/pkg/httpclient/testdata/server.crt
new file mode 100644
index 0000000000..9b0f12ec58
--- /dev/null
+++ b/pkg/httpclient/testdata/server.crt
@@ -0,0 +1,29 @@
+-----BEGIN CERTIFICATE-----
+MIIE5TCCA82gAwIBAgIJAMGzXwBRpkG7MA0GCSqGSIb3DQEBCwUAMIGsMQswCQYD
+VQQGEwJVUzEUMBIGA1UECAwLUmFuZG9tU3RhdGUxEzARBgNVBAcMClJhbmRvbUNp
+dHkxGzAZBgNVBAoMElJhbmRvbU9yZ2FuaXphdGlvbjEfMB0GA1UECwwWUmFuZG9t
+T3JnYW5pemF0aW9uVW5pdDEgMB4GCSqGSIb3DQEJARYRaGVsbG9AZXhhbXBsZS5j
+b20xEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0yMjEwMDcyMjA3MDhaFw0zMjEwMDQy
+MjA3MDhaMIGsMQswCQYDVQQGEwJVUzEUMBIGA1UECAwLUmFuZG9tU3RhdGUxEzAR
+BgNVBAcMClJhbmRvbUNpdHkxGzAZBgNVBAoMElJhbmRvbU9yZ2FuaXphdGlvbjEf
+MB0GA1UECwwWUmFuZG9tT3JnYW5pemF0aW9uVW5pdDEgMB4GCSqGSIb3DQEJARYR
+aGVsbG9AZXhhbXBsZS5jb20xEjAQBgNVBAMMCWxvY2FsaG9zdDCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAMuKdpXP87Q7Kg3iafXzvBuVIyV1K5UmMYiN
+koztkC5XrCzHaQRS/CoIb7/nUqmtAxx7RL0jzhZ93zBN4HY/Zcnrd9tXoPPxi0mG
+ZZWfFU6nN8nOkMHWzEbHVBmhxpfGtwmLcajQ4HrK1TZwJUn6GqclHQRy/gjxkiw5
+KPqzfVOVlA6ht4KdKstKazQkWZ5gdWT4d8yrEy/IT4oaW05xALBMQ7YGjkzWKsSF
+6ygXI7xqF9rg9jCnUsPYg4f8ut3N0c00KjsfKOOj2dF/ZyjedQ5c0u4hHmxSo3Ka
+0ZTmIrMfbVXgGjxRG2HZXLpPvQKoCf/fOX8Irdr+lahFVKASxN0CAwEAAaOCAQYw
+ggECMIHLBgNVHSMEgcMwgcChgbKkga8wgawxCzAJBgNVBAYTAlVTMRQwEgYDVQQI
+DAtSYW5kb21TdGF0ZTETMBEGA1UEBwwKUmFuZG9tQ2l0eTEbMBkGA1UECgwSUmFu
+ZG9tT3JnYW5pemF0aW9uMR8wHQYDVQQLDBZSYW5kb21Pcmdhbml6YXRpb25Vbml0
+MSAwHgYJKoZIhvcNAQkBFhFoZWxsb0BleGFtcGxlLmNvbTESMBAGA1UEAwwJbG9j
+YWxob3N0ggkAhuCQXkounA4wCQYDVR0TBAIwADALBgNVHQ8EBAMCBPAwGgYDVR0R
+BBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCWmh5ebpkm
+v2B1yQgarSCSSkLZ5DZSAJjrPgW2IJqCW2q2D1HworbW1Yn5jqrM9FKGnJfjCyve
+zBB5AOlGp+0bsZGgMRMCavgv4QhTThXUoJqqHcfEu4wHndcgrqSadxmV5aisSR4u
+gXnjW43o3akby+h1K40RR3vVkpzPaoC3/bgk7WVpfpPiP32E24a01gETozRb/of/
+ATN3JBe0xh+e63CrPX1sago5+u3UETIoOr0fW8M/gU9GApmJiFAXwHag6j54hLCG
+23EtVDwmlarG8Pj+i0yru8s22QqzAJi5E0OwR4aB8tqicLKYBVfzyLCOielIBUrK
+OkuFKp+VjxQX
+-----END CERTIFICATE-----
diff --git a/pkg/httpclient/testdata/server.csr b/pkg/httpclient/testdata/server.csr
new file mode 100644
index 0000000000..f422a853c3
--- /dev/null
+++ b/pkg/httpclient/testdata/server.csr
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIC8jCCAdoCAQAwgawxCzAJBgNVBAYTAlVTMRQwEgYDVQQIDAtSYW5kb21TdGF0
+ZTETMBEGA1UEBwwKUmFuZG9tQ2l0eTEbMBkGA1UECgwSUmFuZG9tT3JnYW5pemF0
+aW9uMR8wHQYDVQQLDBZSYW5kb21Pcmdhbml6YXRpb25Vbml0MSAwHgYJKoZIhvcN
+AQkBFhFoZWxsb0BleGFtcGxlLmNvbTESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjAN
+BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy4p2lc/ztDsqDeJp9fO8G5UjJXUr
+lSYxiI2SjO2QLlesLMdpBFL8Kghvv+dSqa0DHHtEvSPOFn3fME3gdj9lyet321eg
+8/GLSYZllZ8VTqc3yc6QwdbMRsdUGaHGl8a3CYtxqNDgesrVNnAlSfoapyUdBHL+
+CPGSLDko+rN9U5WUDqG3gp0qy0prNCRZnmB1ZPh3zKsTL8hPihpbTnEAsExDtgaO
+TNYqxIXrKBcjvGoX2uD2MKdSw9iDh/y63c3RzTQqOx8o46PZ0X9nKN51DlzS7iEe
+bFKjcprRlOYisx9tVeAaPFEbYdlcuk+9AqgJ/985fwit2v6VqEVUoBLE3QIDAQAB
+oAAwDQYJKoZIhvcNAQELBQADggEBADjuujIFoDJllR6Xo/w7j5vfNOeHO5GSgxF2
+XnuuDOI9Tomi7vURFZNbz3VAYiehpxRxYqLwFoQUwFtux2qRuGyg0P9fP1iQXPUE
+QUfFXmvB80uf2bG4lkbUwnmlZLFOEwhGZyPxpvsrxp2Ei2ppkUopCkzOMsSk3m0X
+MC50ZsTHOxfkA3r1WmS7oE2c0p0Fvyx+UJw0URAXFvDS1X0ONgww3FxqbBbm9W37
+5N4FZzGAK6j1wzuynKKXrn20YDCANXYH55PZyupfCeSZT0H0AZifWL7rz/G9uqme
+RzbIYc/CNQQTympjinBegQdVeB3yjVNZIvpGOuPSKQqhwFtmDFo=
+-----END CERTIFICATE REQUEST-----
diff --git a/pkg/httpclient/testdata/server.csr.cnf b/pkg/httpclient/testdata/server.csr.cnf
new file mode 100644
index 0000000000..6ff57d1a35
--- /dev/null
+++ b/pkg/httpclient/testdata/server.csr.cnf
@@ -0,0 +1,14 @@
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+distinguished_name = dn
+
+[dn]
+C=US
+ST=RandomState
+L=RandomCity
+O=RandomOrganization
+OU=RandomOrganizationUnit
+emailAddress=hello@example.com
+CN = localhost
diff --git a/pkg/httpclient/testdata/server.key b/pkg/httpclient/testdata/server.key
new file mode 100644
index 0000000000..9708e1e6ea
--- /dev/null
+++ b/pkg/httpclient/testdata/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDLinaVz/O0OyoN
+4mn187wblSMldSuVJjGIjZKM7ZAuV6wsx2kEUvwqCG+/51KprQMce0S9I84Wfd8w
+TeB2P2XJ63fbV6Dz8YtJhmWVnxVOpzfJzpDB1sxGx1QZocaXxrcJi3Go0OB6ytU2
+cCVJ+hqnJR0Ecv4I8ZIsOSj6s31TlZQOobeCnSrLSms0JFmeYHVk+HfMqxMvyE+K
+GltOcQCwTEO2Bo5M1irEhesoFyO8ahfa4PYwp1LD2IOH/LrdzdHNNCo7Hyjjo9nR
+f2co3nUOXNLuIR5sUqNymtGU5iKzH21V4Bo8URth2Vy6T70CqAn/3zl/CK3a/pWo
+RVSgEsTdAgMBAAECggEAU6cxu7q+54kVbKVsdThaTF/MFR4F7oPHAd9lpuQQSOuh
+iLngMHXGy6OyAgYZlEDWMYN8KdwoXFgZPaoUIaVGuWk8Vnq6XOgeHfbNk2PRhwT0
+yc1K80/Lnx9XMj2p+EEkgxi7eu12BSGN5ZTLzo6rG50GQwjb3WMjd2d6rybL0GjC
+wg2arcBk3sSMYmvZOqlAsaQmtgwkJhvhVkVfEQSD3VKF7g0dh/h3LIPyM0Ff4M67
+KpLMPPwzUJ/0Z4ewAP06mMKUA86R93M+dWs2eh1oBGnRkVQdhCJLXJpuGHZ6BTiB
+Ry0AeorHfnVXPbtpUeAq6m5/BBl6qX0ooB08BIFwAQKBgQDqJpTZS/ZzqL6Kcs14
+MyFu+7DungSxQ5oK9ju7EFSosanSk4UEa/lw992kM6nsIMwgSVQgba5zKcVMeSmk
+AVbpznegQD1BYCwOGwbGvkJ8jbhPy+WLbbRjWT/E6AItZgUK+fyTIcNvSehcQqsT
+fhgWsK7ueZCmLQfVhK1AxtvY3QKBgQDeiKuo8plsH/7IxDn7KVHBOHKPC2ZPzg03
+i7La6zomiRckwwPnhicRSYsjtfCCW6Ms+uzjTEItgFM+5PdrXheeku+z/sExRtZu
+emqPqDomixlXDRQ6RN3gnBSk4RU+ROB1u1uBLWXqRz8Gp2zJGRxhHfYt2zefBv4w
+/cIuPC3cAQKBgD2UsAkGJWb9tj8LOmama+CYaUwYWvuT3+uKHuNvxBQpxZQQICet
+jgjb53rL66Cib4z+PBXbQsoe7jjSlNUBVS5gkq2et31+IZgEG6AhYbMIQrUZ1uD4
+lTybuF289vWhoynj3T2E37VhJq89CWky/HrbNOabKiPKLAlHv5kNs7wxAoGBANEJ
+XQbU7J2O6Iy7FyQBSlTQq3wHX1Iz4mJ9DcNrFzK/sEfOEMrZT7WDefpPm984KW3F
+P+S766ZGVuxLtMbcmh9RM23HLr8VJbSdtZ/AjO9L1r/Y/1lE+49TzmibLpNRq++r
+0WbkuEl8J44ek6fLuMbZmDi3JeZycTCgDlnUGdgBAoGAYdliovtURZCm46t1uE3F
+idCLCXCccjkt1hcNGNjck/b0trHA7wOEqICIguoWDlEBTc0PDvHEq6PfKyqptGkj
+AgaZTMF/aZiGqlT7VRpBuzxM/uV5xzCg+i2ViaW/p3xq0z2PRljVZiEfe5aWcjiM
+ouTtnC3TgmcjhTgGmb48QQE=
+-----END PRIVATE KEY-----
diff --git a/pkg/httpclient/testdata/v3.ext b/pkg/httpclient/testdata/v3.ext
new file mode 100644
index 0000000000..68e35be863
--- /dev/null
+++ b/pkg/httpclient/testdata/v3.ext
@@ -0,0 +1,8 @@
+authorityKeyIdentifier=keyid,issuer
+basicConstraints=CA:FALSE
+keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
+subjectAltName = @alt_names
+
+[alt_names]
+DNS.1 = localhost
+IP.1 = 127.0.0.1
diff --git a/pkg/log/deprecated.go b/pkg/log/deprecated.go
deleted file mode 100644
index f20e8b4cb8..0000000000
--- a/pkg/log/deprecated.go
+++ /dev/null
@@ -1,5 +0,0 @@
-package log
-
-func Deprecated(logger Logger, f string, args ...interface{}) {
- logger.Warnf("Deprecated: "+f, args...)
-}
diff --git a/pkg/log/logger.go b/pkg/log/logger.go
deleted file mode 100644
index 4f3cdd3851..0000000000
--- a/pkg/log/logger.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// Package log provides a logger interface for logger libraries
-// so that dex does not depend on any of them directly.
-// It also includes a default implementation using Logrus (used by dex previously).
-package log
-
-// Logger serves as an adapter interface for logger libraries
-// so that dex does not depend on any of them directly.
-type Logger interface {
- Debug(args ...interface{})
- Info(args ...interface{})
- Warn(args ...interface{})
- Error(args ...interface{})
-
- Debugf(format string, args ...interface{})
- Infof(format string, args ...interface{})
- Warnf(format string, args ...interface{})
- Errorf(format string, args ...interface{})
-}
diff --git a/scripts/git-diff b/scripts/git-diff
deleted file mode 100755
index 302ac2ce3e..0000000000
--- a/scripts/git-diff
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash -e
-
-DIFF=$( git diff . )
-if [ "$DIFF" != "" ]; then
- echo "$DIFF" >&2
- exit 1
-fi
diff --git a/scripts/git-version b/scripts/git-version
index 936641cb0b..a60cdc405d 100755
--- a/scripts/git-version
+++ b/scripts/git-version
@@ -1,5 +1,4 @@
#!/bin/sh -e
-# Since this script will be run in a rkt container, use "/bin/sh" instead of "/bin/bash"
# parse the current git commit hash
COMMIT=`git rev-parse HEAD`
diff --git a/server/api.go b/server/api.go
index a68742b3cc..4fceae9617 100644
--- a/server/api.go
+++ b/server/api.go
@@ -2,20 +2,23 @@ package server
import (
"context"
+ "encoding/json"
"errors"
"fmt"
+ "log/slog"
+ "strconv"
"golang.org/x/crypto/bcrypt"
"github.com/dexidp/dex/api/v2"
- "github.com/dexidp/dex/pkg/log"
+ "github.com/dexidp/dex/pkg/featureflags"
"github.com/dexidp/dex/server/internal"
"github.com/dexidp/dex/storage"
)
// apiVersion increases every time a new call is added to the API. Clients should use this info
// to determine if the server supports specific features.
-const apiVersion = 2
+const apiVersion = 3
const (
// recCost is the recommended bcrypt cost, which balances hash strength and
@@ -29,11 +32,12 @@ const (
)
// NewAPI returns a server which implements the gRPC API interface.
-func NewAPI(s storage.Storage, logger log.Logger, version string) api.DexServer {
+func NewAPI(s storage.Storage, logger *slog.Logger, version string, server *Server) api.DexServer {
return dexAPI{
s: s,
- logger: logger,
+ logger: logger.With("component", "api"),
version: version,
+ server: server,
}
}
@@ -41,8 +45,28 @@ type dexAPI struct {
api.UnimplementedDexServer
s storage.Storage
- logger log.Logger
+ logger *slog.Logger
version string
+ server *Server
+}
+
+func (d dexAPI) GetClient(ctx context.Context, req *api.GetClientReq) (*api.GetClientResp, error) {
+ c, err := d.s.GetClient(ctx, req.Id)
+ if err != nil {
+ return nil, err
+ }
+
+ return &api.GetClientResp{
+ Client: &api.Client{
+ Id: c.ID,
+ Name: c.Name,
+ Secret: c.Secret,
+ RedirectUris: c.RedirectURIs,
+ TrustedPeers: c.TrustedPeers,
+ Public: c.Public,
+ LogoUrl: c.LogoURL,
+ },
+ }, nil
}
func (d dexAPI) CreateClient(ctx context.Context, req *api.CreateClientReq) (*api.CreateClientResp, error) {
@@ -66,11 +90,11 @@ func (d dexAPI) CreateClient(ctx context.Context, req *api.CreateClientReq) (*ap
Name: req.Client.Name,
LogoURL: req.Client.LogoUrl,
}
- if err := d.s.CreateClient(c); err != nil {
+ if err := d.s.CreateClient(ctx, c); err != nil {
if err == storage.ErrAlreadyExists {
return &api.CreateClientResp{AlreadyExists: true}, nil
}
- d.logger.Errorf("api: failed to create client: %v", err)
+ d.logger.Error("failed to create client", "err", err)
return nil, fmt.Errorf("create client: %v", err)
}
@@ -84,7 +108,7 @@ func (d dexAPI) UpdateClient(ctx context.Context, req *api.UpdateClientReq) (*ap
return nil, errors.New("update client: no client ID supplied")
}
- err := d.s.UpdateClient(req.Id, func(old storage.Client) (storage.Client, error) {
+ err := d.s.UpdateClient(ctx, req.Id, func(old storage.Client) (storage.Client, error) {
if req.RedirectUris != nil {
old.RedirectURIs = req.RedirectUris
}
@@ -103,24 +127,49 @@ func (d dexAPI) UpdateClient(ctx context.Context, req *api.UpdateClientReq) (*ap
if err == storage.ErrNotFound {
return &api.UpdateClientResp{NotFound: true}, nil
}
- d.logger.Errorf("api: failed to update the client: %v", err)
+ d.logger.Error("failed to update the client", "err", err)
return nil, fmt.Errorf("update client: %v", err)
}
return &api.UpdateClientResp{}, nil
}
func (d dexAPI) DeleteClient(ctx context.Context, req *api.DeleteClientReq) (*api.DeleteClientResp, error) {
- err := d.s.DeleteClient(req.Id)
+ err := d.s.DeleteClient(ctx, req.Id)
if err != nil {
if err == storage.ErrNotFound {
return &api.DeleteClientResp{NotFound: true}, nil
}
- d.logger.Errorf("api: failed to delete client: %v", err)
+ d.logger.Error("failed to delete client", "err", err)
return nil, fmt.Errorf("delete client: %v", err)
}
return &api.DeleteClientResp{}, nil
}
+func (d dexAPI) ListClients(ctx context.Context, req *api.ListClientReq) (*api.ListClientResp, error) {
+ clientList, err := d.s.ListClients(ctx)
+ if err != nil {
+ d.logger.Error("failed to list clients", "err", err)
+ return nil, fmt.Errorf("list clients: %v", err)
+ }
+
+ clients := make([]*api.ClientInfo, 0, len(clientList))
+ for _, client := range clientList {
+ c := api.ClientInfo{
+ Id: client.ID,
+ Name: client.Name,
+ RedirectUris: client.RedirectURIs,
+ TrustedPeers: client.TrustedPeers,
+ Public: client.Public,
+ LogoUrl: client.LogoURL,
+ }
+ clients = append(clients, &c)
+ }
+
+ return &api.ListClientResp{
+ Clients: clients,
+ }, nil
+}
+
// checkCost returns an error if the hash provided does not meet lower or upper
// bound cost requirements.
func checkCost(hash []byte) error {
@@ -158,11 +207,11 @@ func (d dexAPI) CreatePassword(ctx context.Context, req *api.CreatePasswordReq)
Username: req.Password.Username,
UserID: req.Password.UserId,
}
- if err := d.s.CreatePassword(p); err != nil {
+ if err := d.s.CreatePassword(ctx, p); err != nil {
if err == storage.ErrAlreadyExists {
return &api.CreatePasswordResp{AlreadyExists: true}, nil
}
- d.logger.Errorf("api: failed to create password: %v", err)
+ d.logger.Error("failed to create password", "err", err)
return nil, fmt.Errorf("create password: %v", err)
}
@@ -195,11 +244,11 @@ func (d dexAPI) UpdatePassword(ctx context.Context, req *api.UpdatePasswordReq)
return old, nil
}
- if err := d.s.UpdatePassword(req.Email, updater); err != nil {
+ if err := d.s.UpdatePassword(ctx, req.Email, updater); err != nil {
if err == storage.ErrNotFound {
return &api.UpdatePasswordResp{NotFound: true}, nil
}
- d.logger.Errorf("api: failed to update password: %v", err)
+ d.logger.Error("failed to update password", "err", err)
return nil, fmt.Errorf("update password: %v", err)
}
@@ -211,12 +260,12 @@ func (d dexAPI) DeletePassword(ctx context.Context, req *api.DeletePasswordReq)
return nil, errors.New("no email supplied")
}
- err := d.s.DeletePassword(req.Email)
+ err := d.s.DeletePassword(ctx, req.Email)
if err != nil {
if err == storage.ErrNotFound {
return &api.DeletePasswordResp{NotFound: true}, nil
}
- d.logger.Errorf("api: failed to delete password: %v", err)
+ d.logger.Error("failed to delete password", "err", err)
return nil, fmt.Errorf("delete password: %v", err)
}
return &api.DeletePasswordResp{}, nil
@@ -229,10 +278,24 @@ func (d dexAPI) GetVersion(ctx context.Context, req *api.VersionReq) (*api.Versi
}, nil
}
+func (d dexAPI) GetDiscovery(ctx context.Context, req *api.DiscoveryReq) (*api.DiscoveryResp, error) {
+ discoveryDoc := d.server.constructDiscovery()
+ data, err := json.Marshal(discoveryDoc)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal discovery data: %v", err)
+ }
+ resp := api.DiscoveryResp{}
+ err = json.Unmarshal(data, &resp)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal discovery data: %v", err)
+ }
+ return &resp, nil
+}
+
func (d dexAPI) ListPasswords(ctx context.Context, req *api.ListPasswordReq) (*api.ListPasswordResp, error) {
- passwordList, err := d.s.ListPasswords()
+ passwordList, err := d.s.ListPasswords(ctx)
if err != nil {
- d.logger.Errorf("api: failed to list passwords: %v", err)
+ d.logger.Error("failed to list passwords", "err", err)
return nil, fmt.Errorf("list passwords: %v", err)
}
@@ -260,19 +323,19 @@ func (d dexAPI) VerifyPassword(ctx context.Context, req *api.VerifyPasswordReq)
return nil, errors.New("no password to verify supplied")
}
- password, err := d.s.GetPassword(req.Email)
+ password, err := d.s.GetPassword(ctx, req.Email)
if err != nil {
if err == storage.ErrNotFound {
return &api.VerifyPasswordResp{
NotFound: true,
}, nil
}
- d.logger.Errorf("api: there was an error retrieving the password: %v", err)
+ d.logger.Error("there was an error retrieving the password", "err", err)
return nil, fmt.Errorf("verify password: %v", err)
}
if err := bcrypt.CompareHashAndPassword(password.Hash, []byte(req.Password)); err != nil {
- d.logger.Infof("api: password check failed: %v", err)
+ d.logger.Info("password check failed", "err", err)
return &api.VerifyPasswordResp{
Verified: false,
}, nil
@@ -285,18 +348,18 @@ func (d dexAPI) VerifyPassword(ctx context.Context, req *api.VerifyPasswordReq)
func (d dexAPI) ListRefresh(ctx context.Context, req *api.ListRefreshReq) (*api.ListRefreshResp, error) {
id := new(internal.IDTokenSubject)
if err := internal.Unmarshal(req.UserId, id); err != nil {
- d.logger.Errorf("api: failed to unmarshal ID Token subject: %v", err)
+ d.logger.Error("failed to unmarshal ID Token subject", "err", err)
return nil, err
}
- offlineSessions, err := d.s.GetOfflineSessions(id.UserId, id.ConnId)
+ offlineSessions, err := d.s.GetOfflineSessions(ctx, id.UserId, id.ConnId)
if err != nil {
if err == storage.ErrNotFound {
// This means that this user-client pair does not have a refresh token yet.
// An empty list should be returned instead of an error.
return &api.ListRefreshResp{}, nil
}
- d.logger.Errorf("api: failed to list refresh tokens %t here : %v", err == storage.ErrNotFound, err)
+ d.logger.Error("failed to list refresh tokens here", "err", err)
return nil, err
}
@@ -319,7 +382,7 @@ func (d dexAPI) ListRefresh(ctx context.Context, req *api.ListRefreshReq) (*api.
func (d dexAPI) RevokeRefresh(ctx context.Context, req *api.RevokeRefreshReq) (*api.RevokeRefreshResp, error) {
id := new(internal.IDTokenSubject)
if err := internal.Unmarshal(req.UserId, id); err != nil {
- d.logger.Errorf("api: failed to unmarshal ID Token subject: %v", err)
+ d.logger.Error("failed to unmarshal ID Token subject", "err", err)
return nil, err
}
@@ -330,7 +393,7 @@ func (d dexAPI) RevokeRefresh(ctx context.Context, req *api.RevokeRefreshReq) (*
updater := func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
refreshRef := old.Refresh[req.ClientId]
if refreshRef == nil || refreshRef.ID == "" {
- d.logger.Errorf("api: refresh token issued to client %q for user %q not found for deletion", req.ClientId, id.UserId)
+ d.logger.Error("refresh token issued to client not found for deletion", "client_id", req.ClientId, "user_id", id.UserId)
notFound = true
return old, storage.ErrNotFound
}
@@ -343,11 +406,11 @@ func (d dexAPI) RevokeRefresh(ctx context.Context, req *api.RevokeRefreshReq) (*
return old, nil
}
- if err := d.s.UpdateOfflineSessions(id.UserId, id.ConnId, updater); err != nil {
+ if err := d.s.UpdateOfflineSessions(ctx, id.UserId, id.ConnId, updater); err != nil {
if err == storage.ErrNotFound {
return &api.RevokeRefreshResp{NotFound: true}, nil
}
- d.logger.Errorf("api: failed to update offline session object: %v", err)
+ d.logger.Error("failed to update offline session object", "err", err)
return nil, err
}
@@ -359,10 +422,156 @@ func (d dexAPI) RevokeRefresh(ctx context.Context, req *api.RevokeRefreshReq) (*
//
// TODO(ericchiang): we don't have any good recourse if this call fails.
// Consider garbage collection of refresh tokens with no associated ref.
- if err := d.s.DeleteRefresh(refreshID); err != nil {
- d.logger.Errorf("failed to delete refresh token: %v", err)
+ if err := d.s.DeleteRefresh(ctx, refreshID); err != nil {
+ d.logger.Error("failed to delete refresh token", "err", err)
return nil, err
}
return &api.RevokeRefreshResp{}, nil
}
+
+func (d dexAPI) CreateConnector(ctx context.Context, req *api.CreateConnectorReq) (*api.CreateConnectorResp, error) {
+ if !featureflags.APIConnectorsCRUD.Enabled() {
+ return nil, fmt.Errorf("%s feature flag is not enabled", featureflags.APIConnectorsCRUD.Name)
+ }
+
+ if req.Connector.Id == "" {
+ return nil, errors.New("no id supplied")
+ }
+
+ if req.Connector.Type == "" {
+ return nil, errors.New("no type supplied")
+ }
+
+ if req.Connector.Name == "" {
+ return nil, errors.New("no name supplied")
+ }
+
+ if len(req.Connector.Config) == 0 {
+ return nil, errors.New("no config supplied")
+ }
+
+ if !json.Valid(req.Connector.Config) {
+ return nil, errors.New("invalid config supplied")
+ }
+
+ c := storage.Connector{
+ ID: req.Connector.Id,
+ Name: req.Connector.Name,
+ Type: req.Connector.Type,
+ ResourceVersion: "1",
+ Config: req.Connector.Config,
+ }
+ if err := d.s.CreateConnector(ctx, c); err != nil {
+ if err == storage.ErrAlreadyExists {
+ return &api.CreateConnectorResp{AlreadyExists: true}, nil
+ }
+ d.logger.Error("api: failed to create connector", "err", err)
+ return nil, fmt.Errorf("create connector: %v", err)
+ }
+
+ return &api.CreateConnectorResp{}, nil
+}
+
+func (d dexAPI) UpdateConnector(ctx context.Context, req *api.UpdateConnectorReq) (*api.UpdateConnectorResp, error) {
+ if !featureflags.APIConnectorsCRUD.Enabled() {
+ return nil, fmt.Errorf("%s feature flag is not enabled", featureflags.APIConnectorsCRUD.Name)
+ }
+
+ if req.Id == "" {
+ return nil, errors.New("no email supplied")
+ }
+
+ if len(req.NewConfig) == 0 && req.NewName == "" && req.NewType == "" {
+ return nil, errors.New("nothing to update")
+ }
+
+ if !json.Valid(req.NewConfig) {
+ return nil, errors.New("invalid config supplied")
+ }
+
+ updater := func(old storage.Connector) (storage.Connector, error) {
+ if req.NewType != "" {
+ old.Type = req.NewType
+ }
+
+ if req.NewName != "" {
+ old.Name = req.NewName
+ }
+
+ if len(req.NewConfig) != 0 {
+ old.Config = req.NewConfig
+ }
+
+ if rev, err := strconv.Atoi(defaultTo(old.ResourceVersion, "0")); err == nil {
+ old.ResourceVersion = strconv.Itoa(rev + 1)
+ }
+
+ return old, nil
+ }
+
+ if err := d.s.UpdateConnector(ctx, req.Id, updater); err != nil {
+ if err == storage.ErrNotFound {
+ return &api.UpdateConnectorResp{NotFound: true}, nil
+ }
+ d.logger.Error("api: failed to update connector", "err", err)
+ return nil, fmt.Errorf("update connector: %v", err)
+ }
+
+ return &api.UpdateConnectorResp{}, nil
+}
+
+func (d dexAPI) DeleteConnector(ctx context.Context, req *api.DeleteConnectorReq) (*api.DeleteConnectorResp, error) {
+ if !featureflags.APIConnectorsCRUD.Enabled() {
+ return nil, fmt.Errorf("%s feature flag is not enabled", featureflags.APIConnectorsCRUD.Name)
+ }
+
+ if req.Id == "" {
+ return nil, errors.New("no id supplied")
+ }
+
+ err := d.s.DeleteConnector(ctx, req.Id)
+ if err != nil {
+ if err == storage.ErrNotFound {
+ return &api.DeleteConnectorResp{NotFound: true}, nil
+ }
+ d.logger.Error("api: failed to delete connector", "err", err)
+ return nil, fmt.Errorf("delete connector: %v", err)
+ }
+ return &api.DeleteConnectorResp{}, nil
+}
+
+func (d dexAPI) ListConnectors(ctx context.Context, req *api.ListConnectorReq) (*api.ListConnectorResp, error) {
+ if !featureflags.APIConnectorsCRUD.Enabled() {
+ return nil, fmt.Errorf("%s feature flag is not enabled", featureflags.APIConnectorsCRUD.Name)
+ }
+
+ connectorList, err := d.s.ListConnectors(ctx)
+ if err != nil {
+ d.logger.Error("api: failed to list connectors", "err", err)
+ return nil, fmt.Errorf("list connectors: %v", err)
+ }
+
+ connectors := make([]*api.Connector, 0, len(connectorList))
+ for _, connector := range connectorList {
+ c := api.Connector{
+ Id: connector.ID,
+ Name: connector.Name,
+ Type: connector.Type,
+ Config: connector.Config,
+ }
+ connectors = append(connectors, &c)
+ }
+
+ return &api.ListConnectorResp{
+ Connectors: connectors,
+ }, nil
+}
+
+func defaultTo[T comparable](v, def T) T {
+ var zeroT T
+ if v == zeroT {
+ return def
+ }
+ return v
+}
diff --git a/server/api_test.go b/server/api_test.go
index 01c59cf875..5ddbcc4ac8 100644
--- a/server/api_test.go
+++ b/server/api_test.go
@@ -1,18 +1,17 @@
package server
import (
- "context"
+ "log/slog"
"net"
- "os"
+ "slices"
+ "strings"
"testing"
"time"
- "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"github.com/dexidp/dex/api/v2"
- "github.com/dexidp/dex/pkg/log"
"github.com/dexidp/dex/server/internal"
"github.com/dexidp/dex/storage"
"github.com/dexidp/dex/storage/memory"
@@ -29,20 +28,24 @@ type apiClient struct {
Close func()
}
+func newLogger(t *testing.T) *slog.Logger {
+ return slog.New(slog.NewTextHandler(t.Output(), &slog.HandlerOptions{Level: slog.LevelDebug}))
+}
+
// newAPI constructs a gRCP client connected to a backing server.
-func newAPI(s storage.Storage, logger log.Logger, t *testing.T) *apiClient {
+func newAPI(t *testing.T, s storage.Storage, logger *slog.Logger) *apiClient {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatal(err)
}
serv := grpc.NewServer()
- api.RegisterDexServer(serv, NewAPI(s, logger, "test"))
+ api.RegisterDexServer(serv, NewAPI(s, logger, "test", nil))
go serv.Serve(l)
- // Dial will retry automatically if the serv.Serve() goroutine
+ // NewClient will retry automatically if the serv.Serve() goroutine
// hasn't started yet.
- conn, err := grpc.Dial(l.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
+ conn, err := grpc.NewClient(l.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
t.Fatal(err)
}
@@ -59,17 +62,14 @@ func newAPI(s storage.Storage, logger log.Logger, t *testing.T) *apiClient {
// Attempts to create, update and delete a test Password
func TestPassword(t *testing.T) {
- logger := &logrus.Logger{
- Out: os.Stderr,
- Formatter: &logrus.TextFormatter{DisableColors: true},
- Level: logrus.DebugLevel,
- }
-
+ logger := newLogger(t)
s := memory.New(logger)
- client := newAPI(s, logger, t)
+
+ client := newAPI(t, s, logger)
defer client.Close()
- ctx := context.Background()
+ ctx := t.Context()
+
email := "test@example.com"
p := api.Password{
Email: email,
@@ -152,7 +152,7 @@ func TestPassword(t *testing.T) {
t.Fatalf("Unable to update password: %v", err)
}
- pass, err := s.GetPassword(updateReq.Email)
+ pass, err := s.GetPassword(ctx, updateReq.Email)
if err != nil {
t.Fatalf("Unable to retrieve password: %v", err)
}
@@ -172,14 +172,10 @@ func TestPassword(t *testing.T) {
// Ensures checkCost returns expected values
func TestCheckCost(t *testing.T) {
- logger := &logrus.Logger{
- Out: os.Stderr,
- Formatter: &logrus.TextFormatter{DisableColors: true},
- Level: logrus.DebugLevel,
- }
-
+ logger := newLogger(t)
s := memory.New(logger)
- client := newAPI(s, logger, t)
+
+ client := newAPI(t, s, logger)
defer client.Close()
tests := []struct {
@@ -229,17 +225,13 @@ func TestCheckCost(t *testing.T) {
// Attempts to list and revoke an existing refresh token.
func TestRefreshToken(t *testing.T) {
- logger := &logrus.Logger{
- Out: os.Stderr,
- Formatter: &logrus.TextFormatter{DisableColors: true},
- Level: logrus.DebugLevel,
- }
-
+ logger := newLogger(t)
s := memory.New(logger)
- client := newAPI(s, logger, t)
+
+ client := newAPI(t, s, logger)
defer client.Close()
- ctx := context.Background()
+ ctx := t.Context()
// Creating a storage with an existing refresh token and offline session for the user.
id := storage.NewID()
@@ -262,7 +254,7 @@ func TestRefreshToken(t *testing.T) {
ConnectorData: []byte(`{"some":"data"}`),
}
- if err := s.CreateRefresh(r); err != nil {
+ if err := s.CreateRefresh(ctx, r); err != nil {
t.Fatalf("create refresh token: %v", err)
}
@@ -280,7 +272,7 @@ func TestRefreshToken(t *testing.T) {
}
session.Refresh[tokenRef.ClientID] = &tokenRef
- if err := s.CreateOfflineSessions(session); err != nil {
+ if err := s.CreateOfflineSessions(ctx, session); err != nil {
t.Fatalf("create offline session: %v", err)
}
@@ -337,21 +329,18 @@ func TestRefreshToken(t *testing.T) {
}
if resp, _ := client.ListRefresh(ctx, &listReq); len(resp.RefreshTokens) != 0 {
- t.Fatalf("Refresh token returned inspite of revoking it.")
+ t.Fatalf("Refresh token returned in spite of revoking it.")
}
}
func TestUpdateClient(t *testing.T) {
- logger := &logrus.Logger{
- Out: os.Stderr,
- Formatter: &logrus.TextFormatter{DisableColors: true},
- Level: logrus.DebugLevel,
- }
-
+ logger := newLogger(t)
s := memory.New(logger)
- client := newAPI(s, logger, t)
+
+ client := newAPI(t, s, logger)
defer client.Close()
- ctx := context.Background()
+
+ ctx := t.Context()
createClient := func(t *testing.T, clientId string) {
resp, err := client.CreateClient(ctx, &api.CreateClientReq{
@@ -464,7 +453,7 @@ func TestUpdateClient(t *testing.T) {
t.Errorf("expected in response NotFound: %t", tc.want.NotFound)
}
- client, err := s.GetClient(tc.req.Id)
+ client, err := s.GetClient(ctx, tc.req.Id)
if err != nil {
t.Errorf("no client found in the storage: %v", err)
}
@@ -479,13 +468,13 @@ func TestUpdateClient(t *testing.T) {
t.Errorf("expected stored client with LogoURL: %s, found %s", tc.req.LogoUrl, client.LogoURL)
}
for _, redirectURI := range tc.req.RedirectUris {
- found := find(redirectURI, client.RedirectURIs)
+ found := slices.Contains(client.RedirectURIs, redirectURI)
if !found {
t.Errorf("expected redirect URI: %s", redirectURI)
}
}
for _, peer := range tc.req.TrustedPeers {
- found := find(peer, client.TrustedPeers)
+ found := slices.Contains(client.TrustedPeers, peer)
if !found {
t.Errorf("expected trusted peer: %s", peer)
}
@@ -499,11 +488,340 @@ func TestUpdateClient(t *testing.T) {
}
}
-func find(item string, items []string) bool {
- for _, i := range items {
- if item == i {
- return true
+func TestCreateConnector(t *testing.T) {
+ t.Setenv("DEX_API_CONNECTORS_CRUD", "true")
+
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ connectorID := "connector123"
+ connectorName := "TestConnector"
+ connectorType := "TestType"
+ connectorConfig := []byte(`{"key": "value"}`)
+
+ createReq := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: connectorID,
+ Name: connectorName,
+ Type: connectorType,
+ Config: connectorConfig,
+ },
+ }
+
+ // Test valid connector creation
+ if resp, err := client.CreateConnector(ctx, &createReq); err != nil || resp.AlreadyExists {
+ if err != nil {
+ t.Fatalf("Unable to create connector: %v", err)
+ } else if resp.AlreadyExists {
+ t.Fatalf("Unable to create connector since %s already exists", connectorID)
+ }
+ t.Fatalf("Unable to create connector: %v", err)
+ }
+
+ // Test creating the same connector again (expecting failure)
+ if resp, _ := client.CreateConnector(ctx, &createReq); !resp.AlreadyExists {
+ t.Fatalf("Created connector %s twice", connectorID)
+ }
+
+ createReq.Connector.Config = []byte("invalid_json")
+
+ // Test invalid JSON config
+ if _, err := client.CreateConnector(ctx, &createReq); err == nil {
+ t.Fatal("Expected an error for invalid JSON config, but none occurred")
+ } else if !strings.Contains(err.Error(), "invalid config supplied") {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestUpdateConnector(t *testing.T) {
+ t.Setenv("DEX_API_CONNECTORS_CRUD", "true")
+
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ connectorID := "connector123"
+ newConnectorName := "UpdatedConnector"
+ newConnectorType := "UpdatedType"
+ newConnectorConfig := []byte(`{"updated_key": "updated_value"}`)
+
+ // Create a connector for testing
+ createReq := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: connectorID,
+ Name: "TestConnector",
+ Type: "TestType",
+ Config: []byte(`{"key": "value"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq)
+
+ updateReq := api.UpdateConnectorReq{
+ Id: connectorID,
+ NewName: newConnectorName,
+ NewType: newConnectorType,
+ NewConfig: newConnectorConfig,
+ }
+
+ // Test valid connector update
+ if _, err := client.UpdateConnector(ctx, &updateReq); err != nil {
+ t.Fatalf("Unable to update connector: %v", err)
+ }
+
+ resp, err := client.ListConnectors(ctx, &api.ListConnectorReq{})
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ for _, connector := range resp.Connectors {
+ if connector.Id == connectorID {
+ if connector.Name != newConnectorName {
+ t.Fatal("connector name should have been updated")
+ }
+ if string(connector.Config) != string(newConnectorConfig) {
+ t.Fatal("connector config should have been updated")
+ }
+ if connector.Type != newConnectorType {
+ t.Fatal("connector type should have been updated")
+ }
+ }
+ }
+
+ updateReq.NewConfig = []byte("invalid_json")
+
+ // Test invalid JSON config in update request
+ if _, err := client.UpdateConnector(ctx, &updateReq); err == nil {
+ t.Fatal("Expected an error for invalid JSON config in update, but none occurred")
+ } else if !strings.Contains(err.Error(), "invalid config supplied") {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+}
+
+func TestDeleteConnector(t *testing.T) {
+ t.Setenv("DEX_API_CONNECTORS_CRUD", "true")
+
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ connectorID := "connector123"
+
+ // Create a connector for testing
+ createReq := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: connectorID,
+ Name: "TestConnector",
+ Type: "TestType",
+ Config: []byte(`{"key": "value"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq)
+
+ deleteReq := api.DeleteConnectorReq{
+ Id: connectorID,
+ }
+
+ // Test valid connector deletion
+ if _, err := client.DeleteConnector(ctx, &deleteReq); err != nil {
+ t.Fatalf("Unable to delete connector: %v", err)
+ }
+
+ // Test non existent connector deletion
+ resp, err := client.DeleteConnector(ctx, &deleteReq)
+ if err != nil {
+ t.Fatalf("Unable to delete connector: %v", err)
+ }
+
+ if !resp.NotFound {
+ t.Fatal("Should return not found")
+ }
+}
+
+func TestListConnectors(t *testing.T) {
+ t.Setenv("DEX_API_CONNECTORS_CRUD", "true")
+
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ // Create connectors for testing
+ createReq1 := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: "connector1",
+ Name: "Connector1",
+ Type: "Type1",
+ Config: []byte(`{"key": "value1"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq1)
+
+ createReq2 := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: "connector2",
+ Name: "Connector2",
+ Type: "Type2",
+ Config: []byte(`{"key": "value2"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq2)
+
+ listReq := api.ListConnectorReq{}
+
+ // Test listing connectors
+ if resp, err := client.ListConnectors(ctx, &listReq); err != nil {
+ t.Fatalf("Unable to list connectors: %v", err)
+ } else if len(resp.Connectors) != 2 { // Check the number of connectors in the response
+ t.Fatalf("Expected 2 connectors, found %d", len(resp.Connectors))
+ }
+}
+
+func TestMissingConnectorsCRUDFeatureFlag(t *testing.T) {
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ // Create connectors for testing
+ createReq1 := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: "connector1",
+ Name: "Connector1",
+ Type: "Type1",
+ Config: []byte(`{"key": "value1"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq1)
+
+ createReq2 := api.CreateConnectorReq{
+ Connector: &api.Connector{
+ Id: "connector2",
+ Name: "Connector2",
+ Type: "Type2",
+ Config: []byte(`{"key": "value2"}`),
+ },
+ }
+ client.CreateConnector(ctx, &createReq2)
+
+ listReq := api.ListConnectorReq{}
+
+ if _, err := client.ListConnectors(ctx, &listReq); err == nil {
+ t.Fatal("ListConnectors should have returned an error")
+ }
+}
+
+func TestListClients(t *testing.T) {
+ logger := newLogger(t)
+ s := memory.New(logger)
+
+ client := newAPI(t, s, logger)
+ defer client.Close()
+
+ ctx := t.Context()
+
+ // List Clients
+ listResp, err := client.ListClients(ctx, &api.ListClientReq{})
+ if err != nil {
+ t.Fatalf("Unable to list clients: %v", err)
+ }
+ if len(listResp.Clients) != 0 {
+ t.Fatalf("Expected 0 clients, got %d", len(listResp.Clients))
+ }
+
+ client1 := &api.Client{
+ Id: "client1",
+ Secret: "secret1",
+ RedirectUris: []string{"http://localhost:8080/callback"},
+ TrustedPeers: []string{"peer1"},
+ Public: false,
+ Name: "Test Client 1",
+ LogoUrl: "http://example.com/logo1.png",
+ }
+
+ client2 := &api.Client{
+ Id: "client2",
+ Secret: "secret2",
+ RedirectUris: []string{"http://localhost:8081/callback"},
+ TrustedPeers: []string{"peer2"},
+ Public: true,
+ Name: "Test Client 2",
+ LogoUrl: "http://example.com/logo2.png",
+ }
+
+ _, err = client.CreateClient(ctx, &api.CreateClientReq{Client: client1})
+ if err != nil {
+ t.Fatalf("Unable to create client1: %v", err)
+ }
+
+ _, err = client.CreateClient(ctx, &api.CreateClientReq{Client: client2})
+ if err != nil {
+ t.Fatalf("Unable to create client2: %v", err)
+ }
+
+ listResp, err = client.ListClients(ctx, &api.ListClientReq{})
+ if err != nil {
+ t.Fatalf("Unable to list clients: %v", err)
+ }
+
+ if len(listResp.Clients) != 2 {
+ t.Fatalf("Expected 2 clients, got %d", len(listResp.Clients))
+ }
+
+ clientMap := make(map[string]*api.ClientInfo)
+ for _, c := range listResp.Clients {
+ clientMap[c.Id] = c
+ }
+
+ if c1, exists := clientMap["client1"]; !exists {
+ t.Fatal("client1 not found in list")
+ } else {
+ if c1.Name != "Test Client 1" {
+ t.Errorf("Expected client1 name 'Test Client 1', got '%s'", c1.Name)
+ }
+ if len(c1.RedirectUris) != 1 || c1.RedirectUris[0] != "http://localhost:8080/callback" {
+ t.Errorf("Expected client1 redirect URIs ['http://localhost:8080/callback'], got %v", c1.RedirectUris)
+ }
+ if c1.Public != false {
+ t.Errorf("Expected client1 public false, got %v", c1.Public)
+ }
+ if c1.LogoUrl != "http://example.com/logo1.png" {
+ t.Errorf("Expected client1 logo URL 'http://example.com/logo1.png', got '%s'", c1.LogoUrl)
+ }
+ }
+
+ if c2, exists := clientMap["client2"]; !exists {
+ t.Fatal("client2 not found in list")
+ } else {
+ if c2.Name != "Test Client 2" {
+ t.Errorf("Expected client2 name 'Test Client 2', got '%s'", c2.Name)
+ }
+ if len(c2.RedirectUris) != 1 || c2.RedirectUris[0] != "http://localhost:8081/callback" {
+ t.Errorf("Expected client2 redirect URIs ['http://localhost:8081/callback'], got %v", c2.RedirectUris)
+ }
+ if c2.Public != true {
+ t.Errorf("Expected client2 public true, got %v", c2.Public)
+ }
+ if c2.LogoUrl != "http://example.com/logo2.png" {
+ t.Errorf("Expected client2 logo URL 'http://example.com/logo2.png', got '%s'", c2.LogoUrl)
}
}
- return false
}
diff --git a/server/deviceflowhandlers.go b/server/deviceflowhandlers.go
index 95fed3b3c3..ec5fb52b66 100644
--- a/server/deviceflowhandlers.go
+++ b/server/deviceflowhandlers.go
@@ -11,9 +11,6 @@ import (
"strings"
"time"
- "golang.org/x/net/html"
-
- "github.com/dexidp/dex/pkg/log"
"github.com/dexidp/dex/storage"
)
@@ -49,7 +46,7 @@ func (s *Server) handleDeviceExchange(w http.ResponseWriter, r *http.Request) {
invalidAttempt = false
}
if err := s.templates.device(r, w, s.getDeviceVerificationURI(), userCode, invalidAttempt); err != nil {
- s.logger.Errorf("Server template error: %v", err)
+ s.logger.ErrorContext(r.Context(), "server template error", "err", err)
s.renderError(r, w, http.StatusNotFound, "Page not found")
}
default:
@@ -58,13 +55,14 @@ func (s *Server) handleDeviceExchange(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
pollIntervalSeconds := 5
switch r.Method {
case http.MethodPost:
err := r.ParseForm()
if err != nil {
- s.logger.Errorf("Could not parse Device Request body: %v", err)
+ s.logger.ErrorContext(r.Context(), "could not parse Device Request body", "err", err)
s.tokenErrHelper(w, errInvalidRequest, "", http.StatusNotFound)
return
}
@@ -85,7 +83,13 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
return
}
- s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes)
+ if len(scopes) == 0 {
+ // per RFC8628 section 3.1, https://datatracker.ietf.org/doc/html/rfc8628#section-3.1
+ // scope is optional but dex requires that it is always at least 'openid' so default it
+ scopes = []string{"openid"}
+ }
+
+ s.logger.InfoContext(r.Context(), "received device request", "client_id", clientID, "scoped", scopes)
// Make device code
deviceCode := storage.NewDeviceCode()
@@ -106,8 +110,8 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
Expiry: expireTime,
}
- if err := s.storage.CreateDeviceRequest(deviceReq); err != nil {
- s.logger.Errorf("Failed to store device request; %v", err)
+ if err := s.storage.CreateDeviceRequest(ctx, deviceReq); err != nil {
+ s.logger.ErrorContext(r.Context(), "failed to store device request", "err", err)
s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError)
return
}
@@ -125,15 +129,15 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
},
}
- if err := s.storage.CreateDeviceToken(deviceToken); err != nil {
- s.logger.Errorf("Failed to store device token %v", err)
+ if err := s.storage.CreateDeviceToken(ctx, deviceToken); err != nil {
+ s.logger.ErrorContext(r.Context(), "failed to store device token", "err", err)
s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError)
return
}
u, err := url.Parse(s.issuerURL.String())
if err != nil {
- s.logger.Errorf("Could not parse issuer URL %v", err)
+ s.logger.ErrorContext(r.Context(), "could not parse issuer URL", "err", err)
s.tokenErrHelper(w, errInvalidRequest, "", http.StatusInternalServerError)
return
}
@@ -174,14 +178,14 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleDeviceTokenDeprecated(w http.ResponseWriter, r *http.Request) {
- log.Deprecated(s.logger, `The /device/token endpoint was called. It will be removed, use /token instead.`)
+ s.logger.Warn(`the /device/token endpoint was called. It will be removed, use /token instead.`, "deprecated", true)
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodPost:
err := r.ParseForm()
if err != nil {
- s.logger.Warnf("Could not parse Device Token Request body: %v", err)
+ s.logger.Warn("could not parse Device Token Request body", "err", err)
s.tokenErrHelper(w, errInvalidRequest, "", http.StatusBadRequest)
return
}
@@ -199,6 +203,7 @@ func (s *Server) handleDeviceTokenDeprecated(w http.ResponseWriter, r *http.Requ
}
func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
deviceCode := r.Form.Get("device_code")
if deviceCode == "" {
s.tokenErrHelper(w, errInvalidRequest, "No device code received", http.StatusBadRequest)
@@ -208,10 +213,10 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
now := s.now()
// Grab the device token, check validity
- deviceToken, err := s.storage.GetDeviceToken(deviceCode)
+ deviceToken, err := s.storage.GetDeviceToken(ctx, deviceCode)
if err != nil {
if err != storage.ErrNotFound {
- s.logger.Errorf("failed to get device code: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get device code", "err", err)
}
s.tokenErrHelper(w, errInvalidRequest, "Invalid Device code.", http.StatusBadRequest)
return
@@ -240,15 +245,15 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
return old, nil
}
// Update device token last request time in storage
- if err := s.storage.UpdateDeviceToken(deviceCode, updater); err != nil {
- s.logger.Errorf("failed to update device token: %v", err)
+ if err := s.storage.UpdateDeviceToken(ctx, deviceCode, updater); err != nil {
+ s.logger.ErrorContext(r.Context(), "failed to update device token", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "")
return
}
if slowDown {
s.tokenErrHelper(w, deviceTokenSlowDown, "", http.StatusBadRequest)
} else {
- s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized)
+ s.tokenErrHelper(w, deviceTokenPending, "", http.StatusBadRequest)
}
case deviceTokenComplete:
codeChallengeFromStorage := deviceToken.PKCE.CodeChallenge
@@ -258,7 +263,7 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
case providedCodeVerifier != "" && codeChallengeFromStorage != "":
calculatedCodeChallenge, err := s.calculateCodeChallenge(providedCodeVerifier, deviceToken.PKCE.CodeChallengeMethod)
if err != nil {
- s.logger.Error(err)
+ s.logger.ErrorContext(r.Context(), "failed to calculate code challenge", "err", err)
s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
return
}
@@ -280,6 +285,7 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
switch r.Method {
case http.MethodGet:
userCode := r.FormValue("state")
@@ -292,17 +298,19 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
// Authorization redirect callback from OAuth2 auth flow.
if errMsg := r.FormValue("error"); errMsg != "" {
- // escape the message to prevent cross-site scripting
- msg := html.EscapeString(errMsg + ": " + r.FormValue("error_description"))
- http.Error(w, msg, http.StatusBadRequest)
+ // Log the error details but don't expose them to the user
+ s.logger.ErrorContext(r.Context(), "OAuth2 authorization error",
+ "error", errMsg,
+ "error_description", r.FormValue("error_description"))
+ s.renderError(r, w, http.StatusBadRequest, "Authorization failed. Please try again.")
return
}
- authCode, err := s.storage.GetAuthCode(code)
+ authCode, err := s.storage.GetAuthCode(ctx, code)
if err != nil || s.now().After(authCode.Expiry) {
errCode := http.StatusBadRequest
if err != nil && err != storage.ErrNotFound {
- s.logger.Errorf("failed to get auth code: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get auth code", "err", err)
errCode = http.StatusInternalServerError
}
s.renderError(r, w, errCode, "Invalid or expired auth code.")
@@ -310,21 +318,21 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
}
// Grab the device request from storage
- deviceReq, err := s.storage.GetDeviceRequest(userCode)
+ deviceReq, err := s.storage.GetDeviceRequest(ctx, userCode)
if err != nil || s.now().After(deviceReq.Expiry) {
errCode := http.StatusBadRequest
if err != nil && err != storage.ErrNotFound {
- s.logger.Errorf("failed to get device code: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get device code", "err", err)
errCode = http.StatusInternalServerError
}
s.renderError(r, w, errCode, "Invalid or expired user code.")
return
}
- client, err := s.storage.GetClient(deviceReq.ClientID)
+ client, err := s.storage.GetClient(ctx, deviceReq.ClientID)
if err != nil {
if err != storage.ErrNotFound {
- s.logger.Errorf("failed to get client: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get client", "err", err)
s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
} else {
s.tokenErrHelper(w, errInvalidClient, "Invalid client credentials.", http.StatusUnauthorized)
@@ -336,19 +344,19 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
return
}
- resp, err := s.exchangeAuthCode(w, authCode, client)
+ resp, err := s.exchangeAuthCode(ctx, w, authCode, client)
if err != nil {
- s.logger.Errorf("Could not exchange auth code for client %q: %v", deviceReq.ClientID, err)
+ s.logger.ErrorContext(r.Context(), "could not exchange auth code for clien", "client_id", deviceReq.ClientID, "err", err)
s.renderError(r, w, http.StatusInternalServerError, "Failed to exchange auth code.")
return
}
// Grab the device token from storage
- old, err := s.storage.GetDeviceToken(deviceReq.DeviceCode)
+ old, err := s.storage.GetDeviceToken(ctx, deviceReq.DeviceCode)
if err != nil || s.now().After(old.Expiry) {
errCode := http.StatusBadRequest
if err != nil && err != storage.ErrNotFound {
- s.logger.Errorf("failed to get device token: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get device token", "err", err)
errCode = http.StatusInternalServerError
}
s.renderError(r, w, errCode, "Invalid or expired device code.")
@@ -361,7 +369,7 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
}
respStr, err := json.MarshalIndent(resp, "", " ")
if err != nil {
- s.logger.Errorf("failed to marshal device token response: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to marshal device token response", "err", err)
s.renderError(r, w, http.StatusInternalServerError, "")
return old, err
}
@@ -372,29 +380,31 @@ func (s *Server) handleDeviceCallback(w http.ResponseWriter, r *http.Request) {
}
// Update refresh token in the storage, store the token and mark as complete
- if err := s.storage.UpdateDeviceToken(deviceReq.DeviceCode, updater); err != nil {
- s.logger.Errorf("failed to update device token: %v", err)
+ if err := s.storage.UpdateDeviceToken(ctx, deviceReq.DeviceCode, updater); err != nil {
+ s.logger.ErrorContext(r.Context(), "failed to update device token", "err", err)
s.renderError(r, w, http.StatusBadRequest, "")
return
}
if err := s.templates.deviceSuccess(r, w, client.Name); err != nil {
- s.logger.Errorf("Server template error: %v", err)
+ s.logger.ErrorContext(r.Context(), "Server template error", "err", err)
s.renderError(r, w, http.StatusNotFound, "Page not found")
}
default:
- http.Error(w, fmt.Sprintf("method not implemented: %s", r.Method), http.StatusBadRequest)
+ s.logger.ErrorContext(r.Context(), "unsupported method in device callback", "method", r.Method)
+ s.renderError(r, w, http.StatusBadRequest, ErrMsgMethodNotAllowed)
return
}
}
func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
switch r.Method {
case http.MethodPost:
err := r.ParseForm()
if err != nil {
- s.logger.Warnf("Could not parse user code verification request body : %v", err)
+ s.logger.Warn("could not parse user code verification request body", "err", err)
s.renderError(r, w, http.StatusBadRequest, "")
return
}
@@ -408,13 +418,13 @@ func (s *Server) verifyUserCode(w http.ResponseWriter, r *http.Request) {
userCode = strings.ToUpper(userCode)
// Find the user code in the available requests
- deviceRequest, err := s.storage.GetDeviceRequest(userCode)
+ deviceRequest, err := s.storage.GetDeviceRequest(ctx, userCode)
if err != nil || s.now().After(deviceRequest.Expiry) {
if err != nil && err != storage.ErrNotFound {
- s.logger.Errorf("failed to get device request: %v", err)
+ s.logger.ErrorContext(r.Context(), "failed to get device request", "err", err)
}
if err := s.templates.device(r, w, s.getDeviceVerificationURI(), userCode, true); err != nil {
- s.logger.Errorf("Server template error: %v", err)
+ s.logger.ErrorContext(r.Context(), "Server template error", "err", err)
s.renderError(r, w, http.StatusNotFound, "Page not found")
}
return
diff --git a/server/deviceflowhandlers_test.go b/server/deviceflowhandlers_test.go
index 9a9f28584e..ec7bf29d68 100644
--- a/server/deviceflowhandlers_test.go
+++ b/server/deviceflowhandlers_test.go
@@ -2,7 +2,6 @@ package server
import (
"bytes"
- "context"
"encoding/json"
"io"
"net/http"
@@ -20,10 +19,8 @@ func TestDeviceVerificationURI(t *testing.T) {
t0 := time.Now()
now := func() time.Time { return t0 }
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
// Setup a dex server.
- httpServer, s := newTestServer(ctx, t, func(c *Config) {
+ httpServer, s := newTestServer(t, func(c *Config) {
c.Issuer += "/non-root-path"
c.Now = now
})
@@ -90,14 +87,19 @@ func TestHandleDeviceCode(t *testing.T) {
expectedResponseCode: http.StatusBadRequest,
expectedContentType: "application/json",
},
+ {
+ testName: "New Code without scope",
+ clientID: "test",
+ requestType: "POST",
+ scopes: []string{},
+ expectedResponseCode: http.StatusOK,
+ expectedContentType: "application/json",
+ },
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
-
// Setup a dex server.
- httpServer, s := newTestServer(ctx, t, func(c *Config) {
+ httpServer, s := newTestServer(t, func(c *Config) {
c.Issuer += "/non-root-path"
c.Now = now
})
@@ -220,8 +222,9 @@ func TestDeviceCallback(t *testing.T) {
code: "somecode",
error: "Error Condition",
},
- expectedResponseCode: http.StatusBadRequest,
- expectedServerResponse: "Error Condition: \n",
+ expectedResponseCode: http.StatusBadRequest,
+ // Note: Error details should NOT be displayed to user anymore.
+ // Instead, a safe generic message is shown.
},
{
testName: "Expired Auth Code",
@@ -350,31 +353,31 @@ func TestDeviceCallback(t *testing.T) {
code: "somecode",
error: "",
},
- expectedResponseCode: http.StatusBadRequest,
- expectedServerResponse: "<script>console.log(window);</script>: \n",
+ expectedResponseCode: http.StatusBadRequest,
+ // Note: XSS data should NOT be displayed to user anymore.
+ // Instead, a safe generic message is shown.
},
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
- ctx, cancel := context.WithCancel(context.Background())
- defer cancel()
+ ctx := t.Context()
// Setup a dex server.
- httpServer, s := newTestServer(ctx, t, func(c *Config) {
+ httpServer, s := newTestServer(t, func(c *Config) {
// c.Issuer = c.Issuer + "/non-root-path"
c.Now = now
})
defer httpServer.Close()
- if err := s.storage.CreateAuthCode(tc.testAuthCode); err != nil {
+ if err := s.storage.CreateAuthCode(ctx, tc.testAuthCode); err != nil {
t.Fatalf("failed to create auth code: %v", err)
}
- if err := s.storage.CreateDeviceRequest(tc.testDeviceRequest); err != nil {
+ if err := s.storage.CreateDeviceRequest(ctx, tc.testDeviceRequest); err != nil {
t.Fatalf("failed to create device request: %v", err)
}
- if err := s.storage.CreateDeviceToken(tc.testDeviceToken); err != nil {
+ if err := s.storage.CreateDeviceToken(ctx, tc.testDeviceToken); err != nil {
t.Fatalf("failed to create device token: %v", err)
}
@@ -383,7 +386,7 @@ func TestDeviceCallback(t *testing.T) {
Secret: "",
RedirectURIs: []string{deviceCallbackURI},
}
- if err := s.storage.CreateClient(client); err != nil {
+ if err := s.storage.CreateClient(ctx, client); err != nil {
t.Fatalf("failed to create client: %v", err)
}
@@ -412,6 +415,29 @@ func TestDeviceCallback(t *testing.T) {
t.Errorf("%s: Unexpected Response. Expected %q got %q", tc.testName, tc.expectedServerResponse, result)
}
}
+
+ // Special check for error message safety tests
+ if tc.testName == "Prevent cross-site scripting" || tc.testName == "Error During Authorization" {
+ result, _ := io.ReadAll(rr.Body)
+ responseBody := string(result)
+
+ // Error details should NOT be present in the response (for security)
+ if tc.testName == "Prevent cross-site scripting" {
+ if strings.Contains(responseBody, "
+
{{ template "footer.html" . }}
diff --git a/web/web.go b/web/web.go
index c5ff751490..0c7e9873a7 100644
--- a/web/web.go
+++ b/web/web.go
@@ -5,7 +5,7 @@ import (
"io/fs"
)
-//go:embed static/* templates/* themes/*
+//go:embed static/* templates/* themes/* robots.txt
var files embed.FS
// FS returns a filesystem with the default web assets.