├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── acc-tests.yaml │ ├── actionlint.yaml │ ├── bulk-dep-upgrades.yaml │ ├── go-checks.yaml │ └── jira.yaml ├── .gitignore ├── .go-version ├── CHANGELOG.md ├── CODEOWNERS ├── LICENSE ├── Makefile ├── README.md ├── cmd └── vault-plugin-database-mongodbatlas │ └── main.go ├── connection_producer.go ├── docker-test.sh ├── go.mod ├── go.sum ├── mongodbatlas.go ├── mongodbatlas_test.go ├── scripts ├── build.sh └── gofmtcheck.sh └── website └── source ├── api └── secret │ └── databases │ └── mongodbatlas.html.md └── docs └── secrets └── databases └── mongodbatlas.html.md /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | A high level description of the contribution, including: 3 | Who the change affects or is for (stakeholders)? 4 | What is the change? 5 | Why is the change needed? 6 | How does this change affect the user experience (if at all)? 7 | 8 | # Design of Change 9 | How was this change implemented? 10 | 11 | # Related Issues/Pull Requests 12 | [ ] [Issue #1234](https://github.com/hashicorp/vault/issues/1234) 13 | [ ] [PR #1234](https://github.com/hashicorp/vault/pr/1234) 14 | 15 | # Contributor Checklist 16 | [ ] Add relevant docs to upstream Vault repository, or sufficient reasoning why docs won’t be added yet 17 | [My Docs PR Link](link) 18 | [Example](https://github.com/hashicorp/vault/commit/2715f5cec982aabc7b7a6ae878c547f6f475bba6) 19 | [ ] Add output for any tests not ran in CI to the PR description (eg, acceptance tests) 20 | [ ] Backwards compatible 21 | -------------------------------------------------------------------------------- /.github/workflows/acc-tests.yaml: -------------------------------------------------------------------------------- 1 | name: Run Acceptance Tests 2 | on: 3 | push: 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | run-acc-tests: 10 | runs-on: ubuntu-latest 11 | env: 12 | ATLAS_PUBLIC_KEY: ${{ secrets.ATLAS_PUBLIC_KEY }} 13 | ATLAS_PRIVATE_KEY: ${{ secrets.ATLAS_PRIVATE_KEY }} 14 | ATLAS_PROJECT_ID: ${{ secrets.ATLAS_PROJECT_ID }} 15 | ATLAS_CLUSTER_NAME: ${{ secrets.ATLAS_CLUSTER_NAME }} 16 | steps: 17 | - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 18 | - uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1 19 | with: 20 | go-version-file: .go-version 21 | cache: true 22 | - name: Get IP 23 | id: getip 24 | run: echo "ip_address=$(curl -s https://api.ipify.org?format=json | jq -r .ip)" >> "$GITHUB_OUTPUT" 25 | - name: Check IP 26 | run: | 27 | if [[ -z "${{ steps.getip.outputs.ip_address }}" ]]; then 28 | echo "Unable to get runner IP address. Cannot update Network Permission List for acceptance tests." 29 | exit 1 30 | fi 31 | - name: Run Acceptance Tests 32 | env: 33 | ATLAS_ALLOWLIST_IP: ${{ steps.getip.outputs.ip_address }} 34 | run: make testacc 35 | -------------------------------------------------------------------------------- /.github/workflows/actionlint.yaml: -------------------------------------------------------------------------------- 1 | name: Lint GitHub Actions Workflows 2 | on: 3 | push: 4 | paths: 5 | - '.github/workflows/**' 6 | jobs: 7 | actionlint: 8 | # using `main` as the ref will keep your workflow up-to-date 9 | uses: hashicorp/vault-workflows-common/.github/workflows/actionlint.yaml@main 10 | -------------------------------------------------------------------------------- /.github/workflows/bulk-dep-upgrades.yaml: -------------------------------------------------------------------------------- 1 | name: Upgrade dependencies 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | # Runs 12:00AM on the first of every month 6 | - cron: '0 0 1 * *' 7 | jobs: 8 | upgrade: 9 | # using `main` as the ref will keep your workflow up-to-date 10 | uses: hashicorp/vault-workflows-common/.github/workflows/bulk-dependency-updates.yaml@main 11 | secrets: 12 | VAULT_ECO_GITHUB_TOKEN: ${{ secrets.VAULT_ECO_GITHUB_TOKEN }} 13 | with: 14 | # either hashicorp/vault-ecosystem-applications or hashicorp/vault-ecosystem-foundations 15 | reviewer-team: hashicorp/vault-ecosystem-applications 16 | repository: ${{ github.repository }} 17 | run-id: ${{ github.run_id }} 18 | -------------------------------------------------------------------------------- /.github/workflows/go-checks.yaml: -------------------------------------------------------------------------------- 1 | name: Go checks 2 | on: 3 | push: 4 | jobs: 5 | go-checks: 6 | # using `main` as the ref will keep your workflow up-to-date 7 | uses: hashicorp/vault-workflows-common/.github/workflows/go-checks.yaml@main 8 | -------------------------------------------------------------------------------- /.github/workflows/jira.yaml: -------------------------------------------------------------------------------- 1 | name: Jira Sync 2 | on: 3 | issues: 4 | types: [opened, closed, deleted, reopened] 5 | pull_request_target: 6 | types: [opened, closed, reopened] 7 | issue_comment: # Also triggers when commenting on a PR from the conversation view 8 | types: [created] 9 | jobs: 10 | sync: 11 | uses: hashicorp/vault-workflows-common/.github/workflows/jira.yaml@main 12 | # assuming you use Vault to get secrets 13 | # if you use GitHub secrets, use secrets.XYZ instead of steps.secrets.outputs.XYZ 14 | secrets: 15 | JIRA_SYNC_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }} 16 | JIRA_SYNC_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }} 17 | JIRA_SYNC_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }} 18 | with: 19 | teams-array: '["ecosystem", "applications"]' 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/go,macos 3 | # Edit at https://www.gitignore.io/?templates=go,macos 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | 13 | # Test binary, built with `go test -c` 14 | *.test 15 | 16 | # Output of the go coverage tool, specifically when used with LiteIDE 17 | *.out 18 | 19 | ### macOS ### 20 | # General 21 | .DS_Store 22 | .AppleDouble 23 | .LSOverride 24 | 25 | # Icon must end with two \r 26 | Icon 27 | 28 | # Thumbnails 29 | ._* 30 | 31 | # Files that might appear in the root of a volume 32 | .DocumentRevisions-V100 33 | .fseventsd 34 | .Spotlight-V100 35 | .TemporaryItems 36 | .Trashes 37 | .VolumeIcon.icns 38 | .com.apple.timemachine.donotpresent 39 | 40 | # Directories potentially created on remote AFP share 41 | .AppleDB 42 | .AppleDesktop 43 | Network Trash Folder 44 | Temporary Items 45 | .apdisk 46 | 47 | /vault-plugin-secrets-mongodbatlas 48 | /vault-plugin-database-mongodbatlas 49 | /vault* 50 | /pkg/*/vault-plugin-database-mongodbatlas 51 | /bin/ 52 | 53 | .idea/ 54 | # End of https://www.gitignore.io/api/go,macos 55 | # 56 | 57 | -------------------------------------------------------------------------------- /.go-version: -------------------------------------------------------------------------------- 1 | 1.24.3 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Unreleased 2 | 3 | ## v0.15.0 4 | IMPROVEMENTS: 5 | * Bump Go version to 1.24.3 6 | * Updated dependencies [[GH-81]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/81): 7 | * `github.com/hashicorp/vault/sdk` v0.15.0 -> v0.17.0 8 | * `go.mongodb.org/atlas` v0.37.0 -> v0.38.0 9 | * `go.mongodb.org/mongo-driver` v1.17.2 -> v1.17.3 10 | 11 | ## v0.14.0 12 | 13 | IMPROVEMENTS: 14 | * Bump Go version to 1.23.3 15 | * Updated dependencies [[GH-89]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/89) 16 | 17 | ## v0.13.0 18 | 19 | IMPROVEMENTS: 20 | * Bump Go version to 1.22.6 21 | * Updated dependencies [[GH-81]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/81): 22 | * `github.com/hashicorp/go-hclog` v1.6.2 -> v1.6.3 23 | * `github.com/hashicorp/vault/sdk` v0.11.0 -> v0.13.0 24 | * `github.com/mongodb-forks/digest` v1.0.5 -> v1.1.0 25 | * `go.mongodb.org/atlas` v0.36.0 -> v0.37.0 26 | * `go.mongodb.org/mongo-driver` v1.14.0 -> v1.16.1 27 | * `github.com/docker/docker` 24.0.9+incompatible -> 25.0.6+incompatible [[GH-83]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/83) 28 | * `google.golang.org/grpc` v1.64.0 -> v1.64.1 [[GH-84]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/84) 29 | 30 | ## v0.12.0 31 | 32 | IMPROVEMENTS: 33 | * Updated dependencies: 34 | * `github.com/hashicorp/vault/sdk` v0.10.2 -> v0.11.0 35 | * `github.com/jackc/pgx/v4` v4.18.1 -> v4.18.2 36 | * `go.mongodb.org/mongo-driver` v1.13.1 -> v1.14.0 37 | 38 | ## v0.11.0 39 | 40 | CHANGES: 41 | * Building with go 1.21.7 42 | 43 | IMPROVEMENTS: 44 | * Updated dependencies: 45 | * `github.com/hashicorp/go-hclog` v1.5.0 -> v1.6.2 46 | * `github.com/hashicorp/vault/sdk` v0.9.2 -> v0.10.2 47 | * `go.mongodb.org/atlas` v0.33.0 -> v0.36.0 48 | * `go.mongodb.org/mongo-driver` v1.12.1 -> v1.13.1 49 | * `golang.org/x/net` v0.8.0 -> v0.17.0 50 | * `google.golang.org/grpc` v1.53.0 -> 1.57.1 51 | * `golang.org/x/crypto` v0.6.0 -> v0.17.0 52 | * `github.com/docker/docker` v24.0.5 -> v24.0.7 53 | 54 | ## v0.10.1 55 | 56 | IMPROVEMENTS: 57 | * Updated dependencies: 58 | * `github.com/hashicorp/vault/sdk` v0.9.2-0.20230530190758-08ee474850e0 -> v0.9.2 59 | * `github.com/mongodb-forks/digest` v1.0.4 -> v1.0.5 60 | * `go.mongodb.org/atlas` v0.28.0 -> v0.33.0 61 | * `go.mongodb.org/mongo-driver` v1.11.6 -> v1.12.1 62 | 63 | ## v0.10.0 64 | 65 | CHANGES: 66 | 67 | - Dependency upgrades 68 | - Add support for X509 client certificate credentials [[GH-57]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/57) 69 | 70 | ## v0.9.0 71 | 72 | CHANGES: 73 | 74 | - Replace usage of useragent.String with useragent.PluginString [[GH-42]](https://github.com/hashicorp/vault-plugin-database-mongodbatlas/pull/42) 75 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @hashicorp/vault-ecosystem 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 HashiCorp, Inc. 2 | 3 | Mozilla Public License, version 2.0 4 | 5 | 1. Definitions 6 | 7 | 1.1. "Contributor" 8 | 9 | means each individual or legal entity that creates, contributes to the 10 | creation of, or owns Covered Software. 11 | 12 | 1.2. "Contributor Version" 13 | 14 | means the combination of the Contributions of others (if any) used by a 15 | Contributor and that particular Contributor's Contribution. 16 | 17 | 1.3. "Contribution" 18 | 19 | means Covered Software of a particular Contributor. 20 | 21 | 1.4. "Covered Software" 22 | 23 | means Source Code Form to which the initial Contributor has attached the 24 | notice in Exhibit A, the Executable Form of such Source Code Form, and 25 | Modifications of such Source Code Form, in each case including portions 26 | thereof. 27 | 28 | 1.5. "Incompatible With Secondary Licenses" 29 | means 30 | 31 | a. that the initial Contributor has attached the notice described in 32 | Exhibit B to the Covered Software; or 33 | 34 | b. that the Covered Software was made available under the terms of 35 | version 1.1 or earlier of the License, but not also under the terms of 36 | a Secondary License. 37 | 38 | 1.6. "Executable Form" 39 | 40 | means any form of the work other than Source Code Form. 41 | 42 | 1.7. "Larger Work" 43 | 44 | means a work that combines Covered Software with other material, in a 45 | separate file or files, that is not Covered Software. 46 | 47 | 1.8. "License" 48 | 49 | means this document. 50 | 51 | 1.9. "Licensable" 52 | 53 | means having the right to grant, to the maximum extent possible, whether 54 | at the time of the initial grant or subsequently, any and all of the 55 | rights conveyed by this License. 56 | 57 | 1.10. "Modifications" 58 | 59 | means any of the following: 60 | 61 | a. any file in Source Code Form that results from an addition to, 62 | deletion from, or modification of the contents of Covered Software; or 63 | 64 | b. any new file in Source Code Form that contains any Covered Software. 65 | 66 | 1.11. "Patent Claims" of a Contributor 67 | 68 | means any patent claim(s), including without limitation, method, 69 | process, and apparatus claims, in any patent Licensable by such 70 | Contributor that would be infringed, but for the grant of the License, 71 | by the making, using, selling, offering for sale, having made, import, 72 | or transfer of either its Contributions or its Contributor Version. 73 | 74 | 1.12. "Secondary License" 75 | 76 | means either the GNU General Public License, Version 2.0, the GNU Lesser 77 | General Public License, Version 2.1, the GNU Affero General Public 78 | License, Version 3.0, or any later versions of those licenses. 79 | 80 | 1.13. "Source Code Form" 81 | 82 | means the form of the work preferred for making modifications. 83 | 84 | 1.14. "You" (or "Your") 85 | 86 | means an individual or a legal entity exercising rights under this 87 | License. For legal entities, "You" includes any entity that controls, is 88 | controlled by, or is under common control with You. For purposes of this 89 | definition, "control" means (a) the power, direct or indirect, to cause 90 | the direction or management of such entity, whether by contract or 91 | otherwise, or (b) ownership of more than fifty percent (50%) of the 92 | outstanding shares or beneficial ownership of such entity. 93 | 94 | 95 | 2. License Grants and Conditions 96 | 97 | 2.1. Grants 98 | 99 | Each Contributor hereby grants You a world-wide, royalty-free, 100 | non-exclusive license: 101 | 102 | a. under intellectual property rights (other than patent or trademark) 103 | Licensable by such Contributor to use, reproduce, make available, 104 | modify, display, perform, distribute, and otherwise exploit its 105 | Contributions, either on an unmodified basis, with Modifications, or 106 | as part of a Larger Work; and 107 | 108 | b. under Patent Claims of such Contributor to make, use, sell, offer for 109 | sale, have made, import, and otherwise transfer either its 110 | Contributions or its Contributor Version. 111 | 112 | 2.2. Effective Date 113 | 114 | The licenses granted in Section 2.1 with respect to any Contribution 115 | become effective for each Contribution on the date the Contributor first 116 | distributes such Contribution. 117 | 118 | 2.3. Limitations on Grant Scope 119 | 120 | The licenses granted in this Section 2 are the only rights granted under 121 | this License. No additional rights or licenses will be implied from the 122 | distribution or licensing of Covered Software under this License. 123 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 124 | Contributor: 125 | 126 | a. for any code that a Contributor has removed from Covered Software; or 127 | 128 | b. for infringements caused by: (i) Your and any other third party's 129 | modifications of Covered Software, or (ii) the combination of its 130 | Contributions with other software (except as part of its Contributor 131 | Version); or 132 | 133 | c. under Patent Claims infringed by Covered Software in the absence of 134 | its Contributions. 135 | 136 | This License does not grant any rights in the trademarks, service marks, 137 | or logos of any Contributor (except as may be necessary to comply with 138 | the notice requirements in Section 3.4). 139 | 140 | 2.4. Subsequent Licenses 141 | 142 | No Contributor makes additional grants as a result of Your choice to 143 | distribute the Covered Software under a subsequent version of this 144 | License (see Section 10.2) or under the terms of a Secondary License (if 145 | permitted under the terms of Section 3.3). 146 | 147 | 2.5. Representation 148 | 149 | Each Contributor represents that the Contributor believes its 150 | Contributions are its original creation(s) or it has sufficient rights to 151 | grant the rights to its Contributions conveyed by this License. 152 | 153 | 2.6. Fair Use 154 | 155 | This License is not intended to limit any rights You have under 156 | applicable copyright doctrines of fair use, fair dealing, or other 157 | equivalents. 158 | 159 | 2.7. Conditions 160 | 161 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 162 | Section 2.1. 163 | 164 | 165 | 3. Responsibilities 166 | 167 | 3.1. Distribution of Source Form 168 | 169 | All distribution of Covered Software in Source Code Form, including any 170 | Modifications that You create or to which You contribute, must be under 171 | the terms of this License. You must inform recipients that the Source 172 | Code Form of the Covered Software is governed by the terms of this 173 | License, and how they can obtain a copy of this License. You may not 174 | attempt to alter or restrict the recipients' rights in the Source Code 175 | Form. 176 | 177 | 3.2. Distribution of Executable Form 178 | 179 | If You distribute Covered Software in Executable Form then: 180 | 181 | a. such Covered Software must also be made available in Source Code Form, 182 | as described in Section 3.1, and You must inform recipients of the 183 | Executable Form how they can obtain a copy of such Source Code Form by 184 | reasonable means in a timely manner, at a charge no more than the cost 185 | of distribution to the recipient; and 186 | 187 | b. You may distribute such Executable Form under the terms of this 188 | License, or sublicense it under different terms, provided that the 189 | license for the Executable Form does not attempt to limit or alter the 190 | recipients' rights in the Source Code Form under this License. 191 | 192 | 3.3. Distribution of a Larger Work 193 | 194 | You may create and distribute a Larger Work under terms of Your choice, 195 | provided that You also comply with the requirements of this License for 196 | the Covered Software. If the Larger Work is a combination of Covered 197 | Software with a work governed by one or more Secondary Licenses, and the 198 | Covered Software is not Incompatible With Secondary Licenses, this 199 | License permits You to additionally distribute such Covered Software 200 | under the terms of such Secondary License(s), so that the recipient of 201 | the Larger Work may, at their option, further distribute the Covered 202 | Software under the terms of either this License or such Secondary 203 | License(s). 204 | 205 | 3.4. Notices 206 | 207 | You may not remove or alter the substance of any license notices 208 | (including copyright notices, patent notices, disclaimers of warranty, or 209 | limitations of liability) contained within the Source Code Form of the 210 | Covered Software, except that You may alter any license notices to the 211 | extent required to remedy known factual inaccuracies. 212 | 213 | 3.5. Application of Additional Terms 214 | 215 | You may choose to offer, and to charge a fee for, warranty, support, 216 | indemnity or liability obligations to one or more recipients of Covered 217 | Software. However, You may do so only on Your own behalf, and not on 218 | behalf of any Contributor. You must make it absolutely clear that any 219 | such warranty, support, indemnity, or liability obligation is offered by 220 | You alone, and You hereby agree to indemnify every Contributor for any 221 | liability incurred by such Contributor as a result of warranty, support, 222 | indemnity or liability terms You offer. You may include additional 223 | disclaimers of warranty and limitations of liability specific to any 224 | jurisdiction. 225 | 226 | 4. Inability to Comply Due to Statute or Regulation 227 | 228 | If it is impossible for You to comply with any of the terms of this License 229 | with respect to some or all of the Covered Software due to statute, 230 | judicial order, or regulation then You must: (a) comply with the terms of 231 | this License to the maximum extent possible; and (b) describe the 232 | limitations and the code they affect. Such description must be placed in a 233 | text file included with all distributions of the Covered Software under 234 | this License. Except to the extent prohibited by statute or regulation, 235 | such description must be sufficiently detailed for a recipient of ordinary 236 | skill to be able to understand it. 237 | 238 | 5. Termination 239 | 240 | 5.1. The rights granted under this License will terminate automatically if You 241 | fail to comply with any of its terms. However, if You become compliant, 242 | then the rights granted under this License from a particular Contributor 243 | are reinstated (a) provisionally, unless and until such Contributor 244 | explicitly and finally terminates Your grants, and (b) on an ongoing 245 | basis, if such Contributor fails to notify You of the non-compliance by 246 | some reasonable means prior to 60 days after You have come back into 247 | compliance. Moreover, Your grants from a particular Contributor are 248 | reinstated on an ongoing basis if such Contributor notifies You of the 249 | non-compliance by some reasonable means, this is the first time You have 250 | received notice of non-compliance with this License from such 251 | Contributor, and You become compliant prior to 30 days after Your receipt 252 | of the notice. 253 | 254 | 5.2. If You initiate litigation against any entity by asserting a patent 255 | infringement claim (excluding declaratory judgment actions, 256 | counter-claims, and cross-claims) alleging that a Contributor Version 257 | directly or indirectly infringes any patent, then the rights granted to 258 | You by any and all Contributors for the Covered Software under Section 259 | 2.1 of this License shall terminate. 260 | 261 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 262 | license agreements (excluding distributors and resellers) which have been 263 | validly granted by You or Your distributors under this License prior to 264 | termination shall survive termination. 265 | 266 | 6. Disclaimer of Warranty 267 | 268 | Covered Software is provided under this License on an "as is" basis, 269 | without warranty of any kind, either expressed, implied, or statutory, 270 | including, without limitation, warranties that the Covered Software is free 271 | of defects, merchantable, fit for a particular purpose or non-infringing. 272 | The entire risk as to the quality and performance of the Covered Software 273 | is with You. Should any Covered Software prove defective in any respect, 274 | You (not any Contributor) assume the cost of any necessary servicing, 275 | repair, or correction. This disclaimer of warranty constitutes an essential 276 | part of this License. No use of any Covered Software is authorized under 277 | this License except under this disclaimer. 278 | 279 | 7. Limitation of Liability 280 | 281 | Under no circumstances and under no legal theory, whether tort (including 282 | negligence), contract, or otherwise, shall any Contributor, or anyone who 283 | distributes Covered Software as permitted above, be liable to You for any 284 | direct, indirect, special, incidental, or consequential damages of any 285 | character including, without limitation, damages for lost profits, loss of 286 | goodwill, work stoppage, computer failure or malfunction, or any and all 287 | other commercial damages or losses, even if such party shall have been 288 | informed of the possibility of such damages. This limitation of liability 289 | shall not apply to liability for death or personal injury resulting from 290 | such party's negligence to the extent applicable law prohibits such 291 | limitation. Some jurisdictions do not allow the exclusion or limitation of 292 | incidental or consequential damages, so this exclusion and limitation may 293 | not apply to You. 294 | 295 | 8. Litigation 296 | 297 | Any litigation relating to this License may be brought only in the courts 298 | of a jurisdiction where the defendant maintains its principal place of 299 | business and such litigation shall be governed by laws of that 300 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 301 | in this Section shall prevent a party's ability to bring cross-claims or 302 | counter-claims. 303 | 304 | 9. Miscellaneous 305 | 306 | This License represents the complete agreement concerning the subject 307 | matter hereof. If any provision of this License is held to be 308 | unenforceable, such provision shall be reformed only to the extent 309 | necessary to make it enforceable. Any law or regulation which provides that 310 | the language of a contract shall be construed against the drafter shall not 311 | be used to construe this License against a Contributor. 312 | 313 | 314 | 10. Versions of the License 315 | 316 | 10.1. New Versions 317 | 318 | Mozilla Foundation is the license steward. Except as provided in Section 319 | 10.3, no one other than the license steward has the right to modify or 320 | publish new versions of this License. Each version will be given a 321 | distinguishing version number. 322 | 323 | 10.2. Effect of New Versions 324 | 325 | You may distribute the Covered Software under the terms of the version 326 | of the License under which You originally received the Covered Software, 327 | or under the terms of any subsequent version published by the license 328 | steward. 329 | 330 | 10.3. Modified Versions 331 | 332 | If you create software not governed by this License, and you want to 333 | create a new license for such software, you may create and use a 334 | modified version of this License if you rename the license and remove 335 | any references to the name of the license steward (except to note that 336 | such modified license differs from this License). 337 | 338 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 339 | Licenses If You choose to distribute Source Code Form that is 340 | Incompatible With Secondary Licenses under the terms of this version of 341 | the License, the notice described in Exhibit B of this License must be 342 | attached. 343 | 344 | Exhibit A - Source Code Form License Notice 345 | 346 | This Source Code Form is subject to the 347 | terms of the Mozilla Public License, v. 348 | 2.0. If a copy of the MPL was not 349 | distributed with this file, You can 350 | obtain one at 351 | http://mozilla.org/MPL/2.0/. 352 | 353 | If it is not possible or desirable to put the notice in a particular file, 354 | then You may include the notice in a location (such as a LICENSE file in a 355 | relevant directory) where a recipient would be likely to look for such a 356 | notice. 357 | 358 | You may add additional accurate notices of copyright ownership. 359 | 360 | Exhibit B - "Incompatible With Secondary Licenses" Notice 361 | 362 | This Source Code Form is "Incompatible 363 | With Secondary Licenses", as defined by 364 | the Mozilla Public License, v. 2.0. 365 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TOOL?=vault-plugin-database-mongodbatlas 2 | TEST?=$$(go list ./... | grep -v /vendor/) 3 | EXTERNAL_TOOLS= 4 | BUILD_TAGS?=${TOOL} 5 | GOFMT_FILES?=$$(find . -name '*.go' | grep -v vendor) 6 | 7 | default: dev 8 | 9 | # bin generates the releaseable binaries for this plugin 10 | bin: fmtcheck generate 11 | @CGO_ENABLED=0 BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/build.sh'" 12 | 13 | # dev creates binaries for testing Vault locally. These are put 14 | # into ./bin/ as well as $GOPATH/bin. 15 | dev: fmtcheck generate 16 | @CGO_ENABLED=0 BUILD_TAGS='$(BUILD_TAGS)' VAULT_DEV_BUILD=1 sh -c "'$(CURDIR)/scripts/build.sh'" 17 | 18 | # test runs the unit tests and vets the code 19 | test: fmtcheck generate 20 | CGO_ENABLED=0 VAULT_TOKEN= VAULT_ACC= go test -v -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -count=1 -timeout=20m -parallel=4 21 | 22 | testacc: fmtcheck generate 23 | CGO_ENABLED=0 VAULT_TOKEN= VAULT_ACC=1 go test -v -tags='$(BUILD_TAGS)' $(TEST) $(TESTARGS) -count=1 -timeout=20m -parallel=4 24 | 25 | testcompile: fmtcheck generate 26 | @for pkg in $(TEST) ; do \ 27 | go test -v -c -tags='$(BUILD_TAGS)' $$pkg -parallel=4 ; \ 28 | done 29 | 30 | # generate runs `go generate` to build the dynamically generated 31 | # source files. 32 | generate: 33 | go generate $(go list ./... | grep -v /vendor/) 34 | 35 | # bootstrap the build by downloading additional tools 36 | bootstrap: 37 | @for tool in $(EXTERNAL_TOOLS) ; do \ 38 | echo "Installing/Updating $$tool" ; \ 39 | go get -u $$tool; \ 40 | done 41 | 42 | fmtcheck: 43 | @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" 44 | 45 | fmt: 46 | gofmt -w $(GOFMT_FILES) 47 | 48 | proto: 49 | protoc *.proto --go_out=plugins=grpc:. 50 | 51 | .PHONY: bin default generate test testacc bootstrap fmt fmtcheck 52 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HashiCorp Vault Database Secrets Engine - MongoDB Atlas plugin 2 | 3 | MongoDB Atlas is one of the supported plugins for the HashiCorp Vault Database Secrets Engine and allows for the programmatic generation of unique, ephemeral MongoDB [Database User](https://docs.atlas.mongodb.com/reference/api/database-users/) credentials in MongoDB Atlas Projects. 4 | 5 | **The plugin is included from version 1.4 of Vault.** 6 | 7 | **Please note:** If you would like to install a different version of this plugin than the one that is bundled with Vault, versions v0.2.0 onwards of this plugin are incompatible with Vault versions before 1.6.0 due to an update of the database plugin interface. 8 | 9 | ## Support, Bugs and Feature Requests 10 | 11 | Support for the HashiCorp Vault Database Secrets Engine - MongoDB Atlas is provided under MongoDB Atlas support plans. Please submit support questions within the Atlas UI. Vault support is via HashiCorp. 12 | 13 | Bugs should be filed under the Issues section of this repo. 14 | 15 | Feature requests can be submitted in the Issues section or [directly to MongoDB](https://feedback.mongodb.com/forums/924145-atlas) - just select the Vault plugin as the category or vote for an already suggested feature. 16 | 17 | ## Quick Links 18 | 19 | - [Database Secrets Engine for MongoDB Atlas - Docs](https://developer.hashicorp.com/vault/docs/secrets/databases/mongodbatlas) 20 | - [Database Secrets Engine for MongoDB Atlas - API Docs](https://developer.hashicorp.com/vault/api-docs/secret/databases/mongodbatlas) 21 | - [MongoDB Atlas Website](https://www.mongodb.com/cloud/atlas) 22 | - [Vault Website](https://www.vaultproject.io) 23 | 24 | **Please note**: HashiCorp takes Vault's security and their users' trust very seriously, as does MongoDB. 25 | 26 | If you believe you have found a security issue in Vault or with this plugin, _please responsibly disclose_ by 27 | contacting HashiCorp at [security@hashicorp.com](mailto:security@hashicorp.com) and contact MongoDB 28 | directly via [security@mongodb.com](mailto:security@mongodb.com) or 29 | [open a ticket](https://jira.mongodb.org/plugins/servlet/samlsso?redirectTo=%2Fbrowse%2FSECURITY) (link is external). 30 | 31 | ## Acceptance Testing 32 | 33 | In order to perform acceptance testing, you need to provide all of the necessary information to 34 | connect to a MongoDB Atlas Project. All `ATLAS_*` environment variables must be 35 | provided in order for the acceptance tests to run properly. A cluster must be 36 | available during the test. A 37 | [free tier cluster](https://docs.atlas.mongodb.com/tutorial/deploy-free-tier-cluster/) 38 | can be provisioned manually to test. 39 | 40 | | Environment variable | Description | 41 | |----------------------|------------------------------------------------------------------| 42 | | ATLAS_PUBLIC_KEY | The Atlas API public key | 43 | | ATLAS_PRIVATE_KEY | The Atlas API private key | 44 | | ATLAS_PROJECT_ID | The desired project ID or group ID | 45 | | ATLAS_CLUSTER_NAME | The desired cluster's name, e.g., vault-project.xyz.mongodb.net | 46 | | ATLAS_ALLOWLIST_IP | The public IP of the machine that the test is being performed | 47 | 48 | Then you can run `make testacc` to execute the tests. 49 | -------------------------------------------------------------------------------- /cmd/vault-plugin-database-mongodbatlas/main.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package main 5 | 6 | import ( 7 | "os" 8 | 9 | hclog "github.com/hashicorp/go-hclog" 10 | mongodbatlas "github.com/hashicorp/vault-plugin-database-mongodbatlas" 11 | dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" 12 | ) 13 | 14 | func main() { 15 | err := Run() 16 | if err != nil { 17 | logger := hclog.New(&hclog.LoggerOptions{}) 18 | 19 | logger.Error("plugin shutting down", "error", err) 20 | os.Exit(1) 21 | } 22 | } 23 | 24 | // Run instantiates a MongoDBAtlas object, and runs the RPC server for the plugin 25 | func Run() error { 26 | dbplugin.ServeMultiplex(mongodbatlas.New) 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /connection_producer.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package mongodbatlas 5 | 6 | import ( 7 | "context" 8 | "errors" 9 | "sync" 10 | 11 | dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" 12 | "github.com/hashicorp/vault/sdk/database/helper/connutil" 13 | "github.com/hashicorp/vault/sdk/helper/useragent" 14 | "github.com/hashicorp/vault/sdk/logical" 15 | "github.com/mitchellh/mapstructure" 16 | "github.com/mongodb-forks/digest" 17 | "go.mongodb.org/atlas/mongodbatlas" 18 | ) 19 | 20 | const ( 21 | // TODO: The Vault version used in the user agent string is hard coded until 22 | // it's possible for database plugins to use the system view to obtain correct 23 | // Vault version information via the plugin environment. 24 | userAgentVaultVersion = "1.13.0" 25 | userAgentPluginName = "database-mongodbatlas" 26 | ) 27 | 28 | type mongoDBAtlasConnectionProducer struct { 29 | PublicKey string `json:"public_key" structs:"public_key" mapstructure:"public_key"` 30 | PrivateKey string `json:"private_key" structs:"private_key" mapstructure:"private_key"` 31 | ProjectID string `json:"project_id" structs:"project_id" mapstructure:"project_id"` 32 | 33 | Initialized bool 34 | RawConfig map[string]interface{} 35 | Type string 36 | client *mongodbatlas.Client 37 | sync.Mutex 38 | } 39 | 40 | func (c *mongoDBAtlasConnectionProducer) secretValues() map[string]string { 41 | return map[string]string{ 42 | c.PrivateKey: "[private_key]", 43 | } 44 | } 45 | 46 | // Close terminates the database connection. 47 | func (c *mongoDBAtlasConnectionProducer) Close() error { 48 | c.Lock() 49 | defer c.Unlock() 50 | 51 | c.client = nil 52 | 53 | return nil 54 | } 55 | 56 | func (c *mongoDBAtlasConnectionProducer) Connection(_ context.Context) (interface{}, error) { 57 | // This is intentionally not grabbing the lock since the calling functions (e.g. CreateUser) 58 | // are claiming it. (The locking patterns could be refactored to be more consistent/clear.) 59 | 60 | if !c.Initialized { 61 | return nil, connutil.ErrNotInitialized 62 | } 63 | 64 | if c.client != nil { 65 | return c.client, nil 66 | } 67 | 68 | transport := digest.NewTransport(c.PublicKey, c.PrivateKey) 69 | cl, err := transport.Client() 70 | if err != nil { 71 | return nil, err 72 | } 73 | 74 | client, err := mongodbatlas.New(cl) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | // TODO: Obtain the plugin environment from the system view. 80 | env := &logical.PluginEnvironment{ 81 | VaultVersion: userAgentVaultVersion, 82 | } 83 | client.UserAgent = useragent.PluginString(env, userAgentPluginName) 84 | 85 | c.client = client 86 | 87 | return c.client, nil 88 | } 89 | 90 | func (m *mongoDBAtlasConnectionProducer) Initialize(ctx context.Context, req dbplugin.InitializeRequest) error { 91 | m.Lock() 92 | defer m.Unlock() 93 | 94 | m.RawConfig = req.Config 95 | 96 | err := mapstructure.WeakDecode(req.Config, m) 97 | if err != nil { 98 | return err 99 | } 100 | 101 | if len(m.PublicKey) == 0 { 102 | return errors.New("public Key is not set") 103 | } 104 | 105 | if len(m.PrivateKey) == 0 { 106 | return errors.New("private Key is not set") 107 | } 108 | 109 | // Set initialized to true at this point since all fields are set, 110 | // and the connection can be established at a later time. 111 | m.Initialized = true 112 | 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /docker-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | set -ex 7 | 8 | make dockerbuild 9 | 10 | docker kill vaultplg 2>/dev/null || true 11 | tmpdir=$(mktemp -d vaultplgXXXXXX) 12 | mkdir "$tmpdir/data" 13 | docker run --rm -d -p8200:8200 --name vaultplg -v "$(pwd)/$tmpdir/data":/data -v $(pwd)/bin:/example --cap-add=IPC_LOCK -e 'VAULT_LOCAL_CONFIG= 14 | { 15 | "backend": {"file": {"path": "/data"}}, 16 | "listener": [{"tcp": {"address": "0.0.0.0:8200", "tls_disable": true}}], 17 | "plugin_directory": "/example", 18 | "log_level": "debug", 19 | "disable_mlock": true, 20 | "api_addr": "http://localhost:8200" 21 | } 22 | ' vault server 23 | sleep 1 24 | 25 | export VAULT_ADDR=http://localhost:8200 26 | 27 | initoutput=$(vault operator init -key-shares=1 -key-threshold=1 -format=json) 28 | vault operator unseal $(echo "$initoutput" | jq -r .unseal_keys_hex[0]) 29 | 30 | export VAULT_TOKEN=$(echo "$initoutput" | jq -r .root_token) 31 | 32 | vault write sys/plugins/catalog/database/mongodbatlas-database-plugin \ 33 | sha256=$(shasum -a 256 bin/vault-plugin-database-mongodbatlas | cut -d' ' -f1) \ 34 | command="vault-plugin-database-mongodbatlas" 35 | 36 | vault secrets enable database 37 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/hashicorp/vault-plugin-database-mongodbatlas 2 | 3 | go 1.24.0 4 | 5 | toolchain go1.24.3 6 | 7 | require ( 8 | github.com/hashicorp/go-hclog v1.6.3 9 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 10 | github.com/hashicorp/vault/sdk v0.17.0 11 | github.com/hashicorp/yamux v0.1.2 // indirect 12 | github.com/mitchellh/mapstructure v1.5.0 13 | github.com/mongodb-forks/digest v1.1.0 14 | github.com/stretchr/testify v1.10.0 15 | go.mongodb.org/atlas v0.38.0 16 | go.mongodb.org/mongo-driver v1.17.3 17 | ) 18 | 19 | require ( 20 | cloud.google.com/go/auth v0.14.1 // indirect 21 | cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect 22 | cloud.google.com/go/cloudsqlconn v1.4.3 // indirect 23 | cloud.google.com/go/compute/metadata v0.6.0 // indirect 24 | github.com/Microsoft/go-winio v0.6.2 // indirect 25 | github.com/armon/go-metrics v0.4.1 // indirect 26 | github.com/armon/go-radix v1.0.0 // indirect 27 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 28 | github.com/distribution/reference v0.6.0 // indirect 29 | github.com/docker/docker v27.2.1+incompatible // indirect 30 | github.com/docker/go-connections v0.5.0 // indirect 31 | github.com/docker/go-units v0.5.0 // indirect 32 | github.com/fatih/color v1.18.0 // indirect 33 | github.com/felixge/httpsnoop v1.0.4 // indirect 34 | github.com/go-logr/logr v1.4.2 // indirect 35 | github.com/go-logr/stdr v1.2.2 // indirect 36 | github.com/gogo/protobuf v1.3.2 // indirect 37 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect 38 | github.com/golang/protobuf v1.5.4 // indirect 39 | github.com/golang/snappy v0.0.4 // indirect 40 | github.com/google/certificate-transparency-go v1.3.1 // indirect 41 | github.com/google/go-querystring v1.1.0 // indirect 42 | github.com/google/s2a-go v0.1.9 // indirect 43 | github.com/google/uuid v1.6.0 // indirect 44 | github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect 45 | github.com/googleapis/gax-go/v2 v2.14.1 // indirect 46 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect 47 | github.com/hashicorp/errwrap v1.1.0 // indirect 48 | github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6 // indirect 49 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect 50 | github.com/hashicorp/go-kms-wrapping/v2 v2.0.18 // indirect 51 | github.com/hashicorp/go-metrics v0.5.4 // indirect 52 | github.com/hashicorp/go-multierror v1.1.1 // indirect 53 | github.com/hashicorp/go-plugin v1.6.1 // indirect 54 | github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 // indirect 55 | github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.1 // indirect 56 | github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 // indirect 57 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 // indirect 58 | github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0 // indirect 59 | github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1 // indirect 60 | github.com/hashicorp/go-sockaddr v1.0.7 // indirect 61 | github.com/hashicorp/go-uuid v1.0.3 // indirect 62 | github.com/hashicorp/go-version v1.7.0 // indirect 63 | github.com/hashicorp/golang-lru v1.0.2 // indirect 64 | github.com/jackc/chunkreader/v2 v2.0.1 // indirect 65 | github.com/jackc/pgconn v1.14.3 // indirect 66 | github.com/jackc/pgio v1.0.0 // indirect 67 | github.com/jackc/pgpassfile v1.0.0 // indirect 68 | github.com/jackc/pgproto3/v2 v2.3.3 // indirect 69 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect 70 | github.com/jackc/pgtype v1.14.3 // indirect 71 | github.com/jackc/pgx/v4 v4.18.3 // indirect 72 | github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 // indirect 73 | github.com/klauspost/compress v1.17.11 // indirect 74 | github.com/mattn/go-colorable v0.1.14 // indirect 75 | github.com/mattn/go-isatty v0.0.20 // indirect 76 | github.com/mitchellh/copystructure v1.2.0 // indirect 77 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 78 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 79 | github.com/moby/docker-image-spec v1.3.1 // indirect 80 | github.com/montanaflynn/stats v0.7.1 // indirect 81 | github.com/oklog/run v1.1.0 // indirect 82 | github.com/opencontainers/go-digest v1.0.0 // indirect 83 | github.com/opencontainers/image-spec v1.1.0 // indirect 84 | github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect 85 | github.com/pierrec/lz4 v2.6.1+incompatible // indirect 86 | github.com/pkg/errors v0.9.1 // indirect 87 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 88 | github.com/robfig/cron/v3 v3.0.1 // indirect 89 | github.com/ryanuber/go-glob v1.0.0 // indirect 90 | github.com/sasha-s/go-deadlock v0.3.5 // indirect 91 | github.com/xdg-go/pbkdf2 v1.0.0 // indirect 92 | github.com/xdg-go/scram v1.1.2 // indirect 93 | github.com/xdg-go/stringprep v1.0.4 // indirect 94 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect 95 | go.opencensus.io v0.24.0 // indirect 96 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 97 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect 98 | go.opentelemetry.io/otel v1.35.0 // indirect 99 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect 100 | go.opentelemetry.io/otel/metric v1.35.0 // indirect 101 | go.opentelemetry.io/otel/trace v1.35.0 // indirect 102 | go.opentelemetry.io/proto/otlp v1.3.1 // indirect 103 | go.uber.org/atomic v1.11.0 // indirect 104 | golang.org/x/crypto v0.36.0 // indirect 105 | golang.org/x/net v0.38.0 // indirect 106 | golang.org/x/oauth2 v0.28.0 // indirect 107 | golang.org/x/sync v0.12.0 // indirect 108 | golang.org/x/sys v0.31.0 // indirect 109 | golang.org/x/text v0.23.0 // indirect 110 | golang.org/x/time v0.10.0 // indirect 111 | google.golang.org/api v0.221.0 // indirect 112 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect 113 | google.golang.org/grpc v1.70.0 // indirect 114 | google.golang.org/protobuf v1.36.5 // indirect 115 | gopkg.in/yaml.v3 v3.0.1 // indirect 116 | ) 117 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go/auth v0.14.1 h1:AwoJbzUdxA/whv1qj3TLKwh3XX5sikny2fc40wUl+h0= 4 | cloud.google.com/go/auth v0.14.1/go.mod h1:4JHUxlGXisL0AW8kXPtUF6ztuOksyfUQNFjfsOCXkPM= 5 | cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= 6 | cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= 7 | cloud.google.com/go/cloudsqlconn v1.4.3 h1:/WYFbB1NtMtoMxCbqpzzTFPDkxxlLTPme390KEGaEPc= 8 | cloud.google.com/go/cloudsqlconn v1.4.3/go.mod h1:QL3tuStVOO70txb3rs4G8j5uMfo5ztZii8K3oGD3VYA= 9 | cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= 10 | cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= 11 | filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= 12 | filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= 13 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= 14 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 17 | github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= 18 | github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= 19 | github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 20 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 21 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 22 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 23 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 24 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 25 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 26 | github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= 27 | github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= 28 | github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= 29 | github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= 30 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 31 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 32 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 33 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 34 | github.com/bufbuild/protocompile v0.10.0 h1:+jW/wnLMLxaCEG8AX9lD0bQ5v9h1RUiMKOBOT5ll9dM= 35 | github.com/bufbuild/protocompile v0.10.0/go.mod h1:G9qQIQo0xZ6Uyj6CMNz0saGmx2so+KONo8/KrELABiY= 36 | github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 37 | github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 38 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 39 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 40 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= 41 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 42 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 43 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 44 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 45 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 46 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 47 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 48 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 49 | github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 50 | github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= 51 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 52 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 54 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 56 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 57 | github.com/docker/docker v27.2.1+incompatible h1:fQdiLfW7VLscyoeYEBz7/J8soYFDZV1u6VW6gJEjNMI= 58 | github.com/docker/docker v27.2.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 59 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 60 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 61 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 62 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 63 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 64 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 65 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 66 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 67 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 68 | github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= 69 | github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= 70 | github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= 71 | github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= 72 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 73 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 74 | github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= 75 | github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= 76 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 77 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 78 | github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= 79 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 80 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 81 | github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= 82 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 83 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 84 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 85 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 86 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 87 | github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= 88 | github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 89 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 90 | github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= 91 | github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= 92 | github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 93 | github.com/gofrs/uuid v4.3.0+incompatible h1:CaSVZxm5B+7o45rtab4jC2G37WGYX1zQfuU2i6DSvnc= 94 | github.com/gofrs/uuid v4.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 95 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 96 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 97 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 98 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= 99 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 100 | github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= 101 | github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= 102 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 103 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 104 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 105 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 106 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 107 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 108 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 109 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 110 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 111 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 112 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 113 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 114 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 115 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 116 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 117 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 118 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 119 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 120 | github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= 121 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 122 | github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go= 123 | github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= 124 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 125 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 126 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 127 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 128 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 129 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 130 | github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 131 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 132 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 133 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 134 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 135 | github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= 136 | github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= 137 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 138 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 139 | github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= 140 | github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= 141 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 142 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 143 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 144 | github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= 145 | github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= 146 | github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= 147 | github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= 148 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= 149 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= 150 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 151 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 152 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 153 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 154 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 155 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 156 | github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6 h1:kBoJV4Xl5FLtBfnBjDvBxeNSy2IRITSGs73HQsFUEjY= 157 | github.com/hashicorp/go-hmac-drbg v0.0.0-20210916214228-a6e5a68489f6/go.mod h1:y+HSOcOGB48PkUxNyLAiCiY6rEENu+E+Ss4LG8QHwf4= 158 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 159 | github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= 160 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 161 | github.com/hashicorp/go-kms-wrapping/v2 v2.0.18 h1:DLfC677GfKEpSAFpEWvl1vXsGpEcSHmbhBaPLrdDQHc= 162 | github.com/hashicorp/go-kms-wrapping/v2 v2.0.18/go.mod h1:t/eaR/mi2mw3klfl1WEAuiLKrlZ/Q8cosmsT+RIPLu0= 163 | github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY= 164 | github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI= 165 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 166 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 167 | github.com/hashicorp/go-plugin v1.6.1 h1:P7MR2UP6gNKGPp+y7EZw2kOiq4IR9WiqLvp0XOsVdwI= 168 | github.com/hashicorp/go-plugin v1.6.1/go.mod h1:XPHFku2tFo3o3QKFgSYo+cghcUhw1NA1hZyMK0PWAw0= 169 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 170 | github.com/hashicorp/go-secure-stdlib/base62 v0.1.2 h1:ET4pqyjiGmY09R5y+rSd70J2w45CtbWDNvGqWp/R3Ng= 171 | github.com/hashicorp/go-secure-stdlib/base62 v0.1.2/go.mod h1:EdWO6czbmthiwZ3/PUsDV+UD1D5IRU4ActiaWGwt0Yw= 172 | github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.1 h1:VaLXp47MqD1Y2K6QVrA9RooQiPyCgAbnfeJg44wKuJk= 173 | github.com/hashicorp/go-secure-stdlib/cryptoutil v0.1.1/go.mod h1:hH8rgXHh9fPSDPerG6WzABHsHF+9ZpLhRI1LPk4JZ8c= 174 | github.com/hashicorp/go-secure-stdlib/mlock v0.1.3 h1:kH3Rhiht36xhAfhuHyWJDgdXXEx9IIZhDGRk24CDhzg= 175 | github.com/hashicorp/go-secure-stdlib/mlock v0.1.3/go.mod h1:ov1Q0oEDjC3+A4BwsG2YdKltrmEw8sf9Pau4V9JQ4Vo= 176 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ= 177 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0= 178 | github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0 h1:U6y5MXGiDVOOtkWJ6o/tu1TxABnI0yKTQWJr7z6BpNk= 179 | github.com/hashicorp/go-secure-stdlib/permitpool v1.0.0/go.mod h1:ecDb3o+8D4xtP0nTCufJaAVawHavy5M2eZ64Nq/8/LM= 180 | github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1 h1:JY+zGg8gOmslwif1fiCqT5Hu1SikLZQcHkmQhCoA9gY= 181 | github.com/hashicorp/go-secure-stdlib/plugincontainer v0.4.1/go.mod h1:jW3KCTvdPyAdVecOUwiiO2XaYgUJ/isigt++ISkszkY= 182 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= 183 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= 184 | github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw= 185 | github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw= 186 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 187 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 188 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 189 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 190 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= 191 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 192 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 193 | github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= 194 | github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 195 | github.com/hashicorp/vault/sdk v0.17.0 h1:uqqhFs6omGUtcWg3QejPvIs2a06VfUgkUr5yHdUeTJs= 196 | github.com/hashicorp/vault/sdk v0.17.0/go.mod h1:LeU/f5riOSiM012EqXlU+Le++2csDwgiuWWVNH9EWFs= 197 | github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8= 198 | github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns= 199 | github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= 200 | github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 201 | github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= 202 | github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= 203 | github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= 204 | github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= 205 | github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= 206 | github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= 207 | github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= 208 | github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= 209 | github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w= 210 | github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM= 211 | github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= 212 | github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= 213 | github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= 214 | github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= 215 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc= 216 | github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= 217 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 218 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 219 | github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= 220 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= 221 | github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= 222 | github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 223 | github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= 224 | github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 225 | github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 226 | github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag= 227 | github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= 228 | github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= 229 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 230 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= 231 | github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 232 | github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= 233 | github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= 234 | github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= 235 | github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= 236 | github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= 237 | github.com/jackc/pgtype v1.14.3 h1:h6W9cPuHsRWQFTWUZMAKMgG5jSwQI0Zurzdvlx3Plus= 238 | github.com/jackc/pgtype v1.14.3/go.mod h1:aKeozOde08iifGosdJpz9MBZonJOUJxqNpPBcMJTlVA= 239 | github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= 240 | github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= 241 | github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= 242 | github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= 243 | github.com/jackc/pgx/v4 v4.18.2/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= 244 | github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA= 245 | github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw= 246 | github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 247 | github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 248 | github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 249 | github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= 250 | github.com/jhump/protoreflect v1.16.0 h1:54fZg+49widqXYQ0b+usAFHbMkBGR4PpXrsHc8+TBDg= 251 | github.com/jhump/protoreflect v1.16.0/go.mod h1:oYPd7nPvcBw/5wlDfm/AVmU9zH9BgqGCI469pGxfj/8= 252 | github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531 h1:hgVxRoDDPtQE68PT4LFvNlPz2nBKd3OMlGKIQ69OmR4= 253 | github.com/joshlf/go-acl v0.0.0-20200411065538-eae00ae38531/go.mod h1:fqTUQpVYBvhCNIsMXGl2GE9q6z94DIP6NtFKXCSTVbg= 254 | github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d h1:J8tJzRyiddAFF65YVgxli+TyWBi0f79Sld6rJP6CBcY= 255 | github.com/joshlf/testutil v0.0.0-20170608050642-b5d8aa79d93d/go.mod h1:b+Q3v8Yrg5o15d71PSUraUzYb+jWl6wQMSBXSGS/hv0= 256 | github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= 257 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 258 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 259 | github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 260 | github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 261 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 262 | github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 263 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 264 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 265 | github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= 266 | github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= 267 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 268 | github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 269 | github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 270 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 271 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 272 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 273 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 274 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 275 | github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 276 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 277 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 278 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 279 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 280 | github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 281 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 282 | github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 283 | github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= 284 | github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= 285 | github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= 286 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 287 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 288 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 289 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= 290 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= 291 | github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 292 | github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 293 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 294 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 295 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 296 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 297 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 298 | github.com/microsoft/go-mssqldb v1.5.0 h1:CgENxkwtOBNj3Jg6T1X209y2blCfTTcwuOlznd2k9fk= 299 | github.com/microsoft/go-mssqldb v1.5.0/go.mod h1:lmWsjHD8XX/Txr0f8ZqgbEZSC+BZjmEQy/Ms+rLrvho= 300 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 301 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 302 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 303 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 304 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 305 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 306 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 307 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 308 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 309 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 310 | github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= 311 | github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= 312 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 313 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 314 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 315 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 316 | github.com/mongodb-forks/digest v1.1.0 h1:7eUdsR1BtqLv0mdNm4OXs6ddWvR4X2/OsLwdKksrOoc= 317 | github.com/mongodb-forks/digest v1.1.0/go.mod h1:rb+EX8zotClD5Dj4NdgxnJXG9nwrlx3NWKJ8xttz1Dg= 318 | github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= 319 | github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= 320 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 321 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 322 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 323 | github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 324 | github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= 325 | github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= 326 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 327 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 328 | github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= 329 | github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= 330 | github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= 331 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 332 | github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= 333 | github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= 334 | github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM= 335 | github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 336 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 337 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 338 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 339 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 340 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 341 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 342 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 343 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 344 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 345 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 346 | github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= 347 | github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= 348 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 349 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 350 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 351 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 352 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 353 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 354 | github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= 355 | github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= 356 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 357 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 358 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 359 | github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= 360 | github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= 361 | github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= 362 | github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= 363 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 364 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 365 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 366 | github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= 367 | github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= 368 | github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= 369 | github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= 370 | github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= 371 | github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= 372 | github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= 373 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 374 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 375 | github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= 376 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 377 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 378 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 379 | github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= 380 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 381 | github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= 382 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 383 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 384 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 385 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 386 | github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 387 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 388 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 389 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 390 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 391 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 392 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 393 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 394 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 395 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 396 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 397 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 398 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 399 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 400 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 401 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 402 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 403 | github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= 404 | github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= 405 | github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= 406 | github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= 407 | github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= 408 | github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= 409 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= 410 | github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= 411 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 412 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 413 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 414 | github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= 415 | go.mongodb.org/atlas v0.38.0 h1:zfwymq20GqivGwxPZfypfUDry+WwMGVui97z1d8V4bU= 416 | go.mongodb.org/atlas v0.38.0/go.mod h1:DJYtM+vsEpPEMSkQzJnFHrT0sP7ev6cseZc/GGjJYG8= 417 | go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ= 418 | go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ= 419 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 420 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 421 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 422 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 423 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= 424 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= 425 | go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= 426 | go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= 427 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= 428 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= 429 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0 h1:umZgi92IyxfXd/l4kaDhnKgY8rnN/cZcF1LKc6I8OQ8= 430 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.30.0/go.mod h1:4lVs6obhSVRb1EW5FhOuBTyiQhtRtAnnva9vD3yRfq8= 431 | go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= 432 | go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= 433 | go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= 434 | go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= 435 | go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= 436 | go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= 437 | go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= 438 | go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= 439 | go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= 440 | go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= 441 | go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 442 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 443 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 444 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= 445 | go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= 446 | go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= 447 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 448 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= 449 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= 450 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= 451 | go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 452 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 453 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= 454 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 455 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 456 | golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= 457 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 458 | golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 459 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 460 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 461 | golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 462 | golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 463 | golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 464 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 465 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 466 | golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= 467 | golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= 468 | golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 469 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 470 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 471 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 472 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 473 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 474 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 475 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 476 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 477 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 478 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 479 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 480 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 481 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 482 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 483 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 484 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 485 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 486 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 487 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 488 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 489 | golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 490 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 491 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 492 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 493 | golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 494 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 495 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 496 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 497 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 498 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 499 | golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= 500 | golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 501 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 502 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 503 | golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= 504 | golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= 505 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 506 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 507 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 508 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 509 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 510 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 511 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 512 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 513 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 514 | golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= 515 | golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 516 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 517 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 518 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 519 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 520 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 521 | golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 522 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 523 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 524 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 525 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 526 | golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 527 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 528 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 529 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 530 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 531 | golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 532 | golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 533 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 534 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 535 | golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 536 | golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 537 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 538 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 539 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 540 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 541 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 542 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 543 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 544 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 545 | golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 546 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 547 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 548 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 549 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 550 | golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= 551 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 552 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 553 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 554 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 555 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 556 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 557 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 558 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 559 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 560 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 561 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 562 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 563 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 564 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 565 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 566 | golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= 567 | golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 568 | golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= 569 | golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 570 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 571 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 572 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 573 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 574 | golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 575 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 576 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 577 | golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 578 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 579 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 580 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 581 | golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 582 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 583 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 584 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 585 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 586 | golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 587 | golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 588 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 589 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 590 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 591 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 592 | google.golang.org/api v0.221.0 h1:qzaJfLhDsbMeFee8zBRdt/Nc+xmOuafD/dbdgGfutOU= 593 | google.golang.org/api v0.221.0/go.mod h1:7sOU2+TL4TxUTdbi0gWgAIg7tH5qBXxoyhtL+9x3biQ= 594 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 595 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 596 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 597 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 598 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 599 | google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= 600 | google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= 601 | google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= 602 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= 603 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= 604 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 605 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 606 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 607 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 608 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 609 | google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= 610 | google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= 611 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 612 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 613 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 614 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 615 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 616 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 617 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 618 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 619 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 620 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 621 | google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= 622 | google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 623 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 624 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 625 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 626 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 627 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 628 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 629 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 630 | gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= 631 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 632 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 633 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 634 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 635 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 636 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 637 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 638 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 639 | gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= 640 | gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 641 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 642 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 643 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 644 | -------------------------------------------------------------------------------- /mongodbatlas.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package mongodbatlas 5 | 6 | import ( 7 | "context" 8 | "encoding/json" 9 | "fmt" 10 | "strings" 11 | 12 | "github.com/hashicorp/go-secure-stdlib/strutil" 13 | dbplugin "github.com/hashicorp/vault/sdk/database/dbplugin/v5" 14 | "github.com/hashicorp/vault/sdk/database/helper/dbutil" 15 | "github.com/hashicorp/vault/sdk/helper/template" 16 | "go.mongodb.org/atlas/mongodbatlas" 17 | ) 18 | 19 | const ( 20 | mongoDBAtlasTypeName = "mongodbatlas" 21 | defaultUserNameTemplate = `{{ printf "v-%s-%s" (.RoleName | truncate 15) (random 20) | truncate 20 }}` 22 | ) 23 | 24 | // Verify interface is implemented 25 | var _ dbplugin.Database = (*MongoDBAtlas)(nil) 26 | 27 | type MongoDBAtlas struct { 28 | *mongoDBAtlasConnectionProducer 29 | 30 | usernameProducer template.StringTemplate 31 | } 32 | 33 | func New() (interface{}, error) { 34 | db := new() 35 | dbType := dbplugin.NewDatabaseErrorSanitizerMiddleware(db, db.secretValues) 36 | return dbType, nil 37 | } 38 | 39 | func new() *MongoDBAtlas { 40 | connProducer := &mongoDBAtlasConnectionProducer{ 41 | Type: mongoDBAtlasTypeName, 42 | } 43 | 44 | return &MongoDBAtlas{ 45 | mongoDBAtlasConnectionProducer: connProducer, 46 | } 47 | } 48 | 49 | func (m *MongoDBAtlas) Initialize(ctx context.Context, req dbplugin.InitializeRequest) (dbplugin.InitializeResponse, error) { 50 | usernameTemplate, err := strutil.GetString(req.Config, "username_template") 51 | if err != nil { 52 | return dbplugin.InitializeResponse{}, fmt.Errorf("failed to retrieve username_template: %w", err) 53 | } 54 | if usernameTemplate == "" { 55 | usernameTemplate = defaultUserNameTemplate 56 | } 57 | 58 | up, err := template.NewTemplate(template.Template(usernameTemplate)) 59 | if err != nil { 60 | return dbplugin.InitializeResponse{}, fmt.Errorf("unable to initialize username template: %w", err) 61 | } 62 | m.usernameProducer = up 63 | 64 | _, err = m.usernameProducer.Generate(dbplugin.UsernameMetadata{}) 65 | if err != nil { 66 | return dbplugin.InitializeResponse{}, fmt.Errorf("invalid username template: %w", err) 67 | } 68 | 69 | err = m.mongoDBAtlasConnectionProducer.Initialize(ctx, req) 70 | if err != nil { 71 | return dbplugin.InitializeResponse{}, fmt.Errorf("failed to initialize: %w", err) 72 | } 73 | 74 | resp := dbplugin.InitializeResponse{ 75 | Config: req.Config, 76 | } 77 | resp.SetSupportedCredentialTypes([]dbplugin.CredentialType{ 78 | dbplugin.CredentialTypePassword, 79 | dbplugin.CredentialTypeClientCertificate, 80 | }) 81 | return resp, nil 82 | } 83 | 84 | func (m *MongoDBAtlas) NewUser(ctx context.Context, req dbplugin.NewUserRequest) (dbplugin.NewUserResponse, error) { 85 | // Grab the lock 86 | m.Lock() 87 | defer m.Unlock() 88 | 89 | // Statement length checks 90 | if len(req.Statements.Commands) == 0 { 91 | return dbplugin.NewUserResponse{}, dbutil.ErrEmptyCreationStatement 92 | } 93 | if len(req.Statements.Commands) > 1 { 94 | return dbplugin.NewUserResponse{}, fmt.Errorf("only 1 creation statement supported for creation") 95 | } 96 | 97 | client, err := m.getConnection(ctx) 98 | if err != nil { 99 | return dbplugin.NewUserResponse{}, err 100 | } 101 | 102 | var username string 103 | switch req.CredentialType { 104 | case dbplugin.CredentialTypePassword: 105 | username, err = m.usernameProducer.Generate(req.UsernameConfig) 106 | if err != nil { 107 | return dbplugin.NewUserResponse{}, err 108 | } 109 | case dbplugin.CredentialTypeClientCertificate: 110 | // MongoDb Atlas expects the username to equal the client certificate subject 111 | // https://www.mongodb.com/docs/manual/tutorial/configure-x509-client-authentication/ 112 | username = req.Subject 113 | default: 114 | return dbplugin.NewUserResponse{}, fmt.Errorf("unsupported credential type %q", 115 | req.CredentialType) 116 | } 117 | 118 | // Unmarshal creation statements into mongodb roles 119 | var databaseUser mongoDBAtlasStatement 120 | err = json.Unmarshal([]byte(req.Statements.Commands[0]), &databaseUser) 121 | if err != nil { 122 | return dbplugin.NewUserResponse{}, fmt.Errorf("error unmarshalling statement %s", err) 123 | } 124 | 125 | // Default to "admin" if no db provided 126 | if databaseUser.DatabaseName == "" { 127 | databaseUser.DatabaseName = "admin" 128 | } 129 | 130 | if len(databaseUser.Roles) == 0 { 131 | return dbplugin.NewUserResponse{}, fmt.Errorf("roles array is required in creation statement") 132 | } 133 | 134 | databaseUserRequest := &mongodbatlas.DatabaseUser{ 135 | Username: username, 136 | Password: req.Password, 137 | DatabaseName: databaseUser.DatabaseName, 138 | Roles: databaseUser.Roles, 139 | Scopes: databaseUser.Scopes, 140 | X509Type: databaseUser.X509Type, 141 | } 142 | 143 | _, _, err = client.DatabaseUsers.Create(ctx, m.ProjectID, databaseUserRequest) 144 | if err != nil { 145 | return dbplugin.NewUserResponse{}, err 146 | } 147 | 148 | resp := dbplugin.NewUserResponse{ 149 | Username: username, 150 | } 151 | 152 | return resp, nil 153 | } 154 | 155 | func (m *MongoDBAtlas) UpdateUser(ctx context.Context, req dbplugin.UpdateUserRequest) (dbplugin.UpdateUserResponse, error) { 156 | if req.Password != nil { 157 | err := m.changePassword(ctx, req.Username, req.Password.NewPassword) 158 | return dbplugin.UpdateUserResponse{}, err 159 | } 160 | 161 | // This also results in an no-op if the expiration is updated due to renewal. 162 | return dbplugin.UpdateUserResponse{}, nil 163 | } 164 | 165 | func (m *MongoDBAtlas) changePassword(ctx context.Context, username, password string) error { 166 | m.Lock() 167 | defer m.Unlock() 168 | 169 | client, err := m.getConnection(ctx) 170 | if err != nil { 171 | return err 172 | } 173 | 174 | databaseUserRequest := &mongodbatlas.DatabaseUser{ 175 | Password: password, 176 | } 177 | 178 | _, _, err = client.DatabaseUsers.Update(context.Background(), m.ProjectID, username, databaseUserRequest) 179 | return err 180 | } 181 | 182 | func (m *MongoDBAtlas) DeleteUser(ctx context.Context, req dbplugin.DeleteUserRequest) (dbplugin.DeleteUserResponse, error) { 183 | m.Lock() 184 | defer m.Unlock() 185 | 186 | client, err := m.getConnection(ctx) 187 | if err != nil { 188 | return dbplugin.DeleteUserResponse{}, err 189 | } 190 | 191 | var databaseUser mongoDBAtlasStatement 192 | if len(req.Statements.Commands) > 0 { 193 | err = json.Unmarshal([]byte(req.Statements.Commands[0]), &databaseUser) 194 | if err != nil { 195 | return dbplugin.DeleteUserResponse{}, fmt.Errorf("error unmarshalling statement %w", err) 196 | } 197 | } 198 | 199 | // If the user is an X.509 user, delete the user from the $external database 200 | if isX509User(req.Username) { 201 | if databaseUser.DatabaseName == "" { 202 | databaseUser.DatabaseName = "$external" 203 | } 204 | } else { 205 | // If the user is not an X.509 user, delete the user from the MongoDB Atlas project 206 | if databaseUser.DatabaseName == "" { 207 | databaseUser.DatabaseName = "admin" 208 | } 209 | } 210 | 211 | _, err = client.DatabaseUsers.Delete(ctx, databaseUser.DatabaseName, m.ProjectID, req.Username) 212 | if err != nil { 213 | return dbplugin.DeleteUserResponse{}, fmt.Errorf("error deleting user from project: %w", err) 214 | } 215 | return dbplugin.DeleteUserResponse{}, nil 216 | } 217 | 218 | func (m *MongoDBAtlas) getConnection(ctx context.Context) (*mongodbatlas.Client, error) { 219 | client, err := m.Connection(ctx) 220 | if err != nil { 221 | return nil, err 222 | } 223 | 224 | return client.(*mongodbatlas.Client), nil 225 | } 226 | 227 | // Type returns the TypeName for this backend 228 | func (m *MongoDBAtlas) Type() (string, error) { 229 | return mongoDBAtlasTypeName, nil 230 | } 231 | 232 | // Check to see if the user is a X509 user 233 | func isX509User(username string) bool { 234 | return strings.HasPrefix(username, "CN=") 235 | } 236 | 237 | type mongoDBAtlasStatement struct { 238 | DatabaseName string `json:"database_name"` 239 | Roles []mongodbatlas.Role `json:"roles,omitempty"` 240 | Scopes []mongodbatlas.Scope `json:"scopes,omitempty"` 241 | X509Type string `json:"x509Type,omitempty"` 242 | } 243 | -------------------------------------------------------------------------------- /mongodbatlas_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) HashiCorp, Inc. 2 | // SPDX-License-Identifier: MPL-2.0 3 | 4 | package mongodbatlas 5 | 6 | import ( 7 | "context" 8 | "encoding/json" 9 | "flag" 10 | "fmt" 11 | "log" 12 | "net/url" 13 | "os" 14 | "reflect" 15 | "testing" 16 | "time" 17 | 18 | "go.mongodb.org/mongo-driver/mongo/readpref" 19 | 20 | "github.com/hashicorp/vault/sdk/database/dbplugin/v5" 21 | dbtesting "github.com/hashicorp/vault/sdk/database/dbplugin/v5/testing" 22 | "github.com/mongodb-forks/digest" 23 | "github.com/stretchr/testify/require" 24 | "go.mongodb.org/atlas/mongodbatlas" 25 | "go.mongodb.org/mongo-driver/mongo" 26 | "go.mongodb.org/mongo-driver/mongo/options" 27 | ) 28 | 29 | const ( 30 | testMongoDBAtlasRole = `{"roles": [{"databaseName":"admin","roleName":"readWriteAnyDatabase"}], "scopes": [{"name": "vault-test-free-cluster", "type": "CLUSTER"}]}` 31 | testMongoDBAtlasX509Role = `{"database_name": "$external", "x509Type": "CUSTOMER", "roles": [{"databaseName":"admin","roleName":"readWriteAnyDatabase"}], "scopes": [{"name": "vault-test-free-cluster", "type": "CLUSTER"}]}` 32 | 33 | envVarAtlasPublicKey = "ATLAS_PUBLIC_KEY" 34 | envVarAtlasPrivateKey = "ATLAS_PRIVATE_KEY" 35 | envVarAtlasProjectID = "ATLAS_PROJECT_ID" 36 | envVarAtlasClusterName = "ATLAS_CLUSTER_NAME" 37 | envVarAtlasAllowListIP = "ATLAS_ALLOWLIST_IP" 38 | 39 | envVarRunAccTests = "VAULT_ACC" 40 | ) 41 | 42 | var runAcceptanceTests = os.Getenv(envVarRunAccTests) == "1" 43 | 44 | func TestMain(m *testing.M) { 45 | flag.Parse() 46 | 47 | controller, err := newTestController() 48 | if err != nil { 49 | log.Fatal(err) 50 | } 51 | 52 | if err := controller.Setup(); err != nil { 53 | log.Fatal(err) 54 | } 55 | 56 | // Run the actual tests 57 | code := m.Run() 58 | 59 | if err := controller.Teardown(); err != nil { 60 | log.Fatal(err) 61 | } 62 | 63 | os.Exit(code) 64 | } 65 | 66 | // testController takes care of performing one-time setup and teardown tasks per 67 | // test run, such as adding the IP of the machine to Atlas' allowlist. This is 68 | // only applicable when running acceptance tests. 69 | type testController struct { 70 | client *mongodbatlas.Client 71 | ip string 72 | projectID string 73 | } 74 | 75 | func newTestController() (testController, error) { 76 | if !runAcceptanceTests { 77 | return testController{}, nil 78 | } 79 | 80 | publicKey := os.Getenv(envVarAtlasPublicKey) 81 | privateKey := os.Getenv(envVarAtlasPrivateKey) 82 | projectID := os.Getenv(envVarAtlasProjectID) 83 | 84 | // This is the public IP of your machine so that it gets allow listed 85 | // for the project during the test run 86 | ip := os.Getenv(envVarAtlasAllowListIP) 87 | 88 | client, err := getClient(publicKey, privateKey) 89 | if err != nil { 90 | return testController{}, err 91 | } 92 | 93 | controller := testController{ 94 | client: client, 95 | ip: ip, 96 | projectID: projectID, 97 | } 98 | 99 | return controller, nil 100 | } 101 | 102 | func (c testController) Setup() error { 103 | if !runAcceptanceTests { 104 | return nil 105 | } 106 | 107 | allowList := []*mongodbatlas.ProjectIPAccessList{ 108 | { 109 | IPAddress: c.ip, 110 | }, 111 | } 112 | _, _, err := c.client.ProjectIPAccessList.Create(context.Background(), c.projectID, allowList) 113 | return err 114 | } 115 | 116 | func (c testController) Teardown() error { 117 | if !runAcceptanceTests { 118 | return nil 119 | } 120 | 121 | _, err := c.client.ProjectIPAccessList.Delete(context.Background(), c.projectID, c.ip) 122 | return err 123 | } 124 | 125 | func TestIntegrationDatabaseUser_Initialize(t *testing.T) { 126 | connectionDetails := map[string]interface{}{ 127 | "public_key": "aspergesme", 128 | "private_key": "domine", 129 | } 130 | db := new() 131 | defer dbtesting.AssertClose(t, db) 132 | 133 | req := dbplugin.InitializeRequest{ 134 | Config: connectionDetails, 135 | VerifyConnection: true, 136 | } 137 | 138 | expectedConfig := map[string]interface{}{ 139 | "public_key": "aspergesme", 140 | "private_key": "domine", 141 | dbplugin.SupportedCredentialTypesKey: []interface{}{ 142 | dbplugin.CredentialTypePassword.String(), 143 | dbplugin.CredentialTypeClientCertificate.String(), 144 | }, 145 | } 146 | 147 | resp := dbtesting.AssertInitialize(t, db, req) 148 | 149 | if !reflect.DeepEqual(resp.Config, expectedConfig) { 150 | t.Fatalf("Actual config: %#v\nExpected config: %#v", resp.Config, expectedConfig) 151 | } 152 | 153 | if !db.Initialized { 154 | t.Fatal("Database should be initialized") 155 | } 156 | } 157 | 158 | func TestAcceptanceDatabaseUser_CreateUser(t *testing.T) { 159 | if !runAcceptanceTests { 160 | t.SkipNow() 161 | } 162 | 163 | publicKey := os.Getenv(envVarAtlasPublicKey) 164 | privateKey := os.Getenv(envVarAtlasPrivateKey) 165 | projectID := os.Getenv(envVarAtlasProjectID) 166 | connURL := os.Getenv(envVarAtlasClusterName) 167 | 168 | connectionDetails := map[string]interface{}{ 169 | "public_key": publicKey, 170 | "private_key": privateKey, 171 | "project_id": projectID, 172 | } 173 | 174 | db := new() 175 | defer dbtesting.AssertClose(t, db) 176 | 177 | initReq := dbplugin.InitializeRequest{ 178 | Config: connectionDetails, 179 | } 180 | 181 | dbtesting.AssertInitialize(t, db, initReq) 182 | 183 | password := "myreallysecurepassword" 184 | createReq := dbplugin.NewUserRequest{ 185 | UsernameConfig: dbplugin.UsernameMetadata{ 186 | DisplayName: "testcreate", 187 | RoleName: "test", 188 | }, 189 | Statements: dbplugin.Statements{ 190 | Commands: []string{testMongoDBAtlasRole}, 191 | }, 192 | Password: password, 193 | Expiration: time.Now().Add(time.Minute), 194 | } 195 | 196 | createResp := dbtesting.AssertNewUser(t, db, createReq) 197 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 198 | 199 | assertCredsExists(t, projectID, publicKey, privateKey, createResp.Username, password, connURL, testMongoDBAtlasRole) 200 | } 201 | 202 | func TestAcceptanceDatabaseUser_CreateX509User(t *testing.T) { 203 | if !runAcceptanceTests { 204 | t.SkipNow() 205 | } 206 | 207 | publicKey := os.Getenv(envVarAtlasPublicKey) 208 | privateKey := os.Getenv(envVarAtlasPrivateKey) 209 | projectID := os.Getenv(envVarAtlasProjectID) 210 | connURL := os.Getenv(envVarAtlasClusterName) 211 | 212 | connectionDetails := map[string]interface{}{ 213 | "public_key": publicKey, 214 | "private_key": privateKey, 215 | "project_id": projectID, 216 | } 217 | 218 | db := new() 219 | defer dbtesting.AssertClose(t, db) 220 | 221 | initReq := dbplugin.InitializeRequest{ 222 | Config: connectionDetails, 223 | } 224 | 225 | dbtesting.AssertInitialize(t, db, initReq) 226 | 227 | subject := "CN=token_my-test-role_1684350512" 228 | createReq := dbplugin.NewUserRequest{ 229 | CredentialType: dbplugin.CredentialTypeClientCertificate, 230 | UsernameConfig: dbplugin.UsernameMetadata{ 231 | DisplayName: "testcreate", 232 | RoleName: "test", 233 | }, 234 | Statements: dbplugin.Statements{ 235 | Commands: []string{testMongoDBAtlasX509Role}, 236 | }, 237 | Subject: subject, 238 | Expiration: time.Now().Add(time.Minute), 239 | } 240 | 241 | createResp := dbtesting.AssertNewUser(t, db, createReq) 242 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 243 | 244 | assertX509UserExist(t, projectID, publicKey, privateKey, subject, connURL, testMongoDBAtlasX509Role) 245 | } 246 | 247 | func TestAcceptanceDatabaseUser_CreateUserDefaultTemplate(t *testing.T) { 248 | if !runAcceptanceTests { 249 | t.SkipNow() 250 | } 251 | 252 | publicKey := os.Getenv(envVarAtlasPublicKey) 253 | privateKey := os.Getenv(envVarAtlasPrivateKey) 254 | projectID := os.Getenv(envVarAtlasProjectID) 255 | connURL := os.Getenv(envVarAtlasClusterName) 256 | 257 | connectionDetails := map[string]interface{}{ 258 | "public_key": publicKey, 259 | "private_key": privateKey, 260 | "project_id": projectID, 261 | } 262 | 263 | db := new() 264 | defer dbtesting.AssertClose(t, db) 265 | 266 | initReq := dbplugin.InitializeRequest{ 267 | Config: connectionDetails, 268 | } 269 | 270 | dbtesting.AssertInitialize(t, db, initReq) 271 | 272 | password := "myreallysecurepassword" 273 | roleName := "test" 274 | createReq := dbplugin.NewUserRequest{ 275 | UsernameConfig: dbplugin.UsernameMetadata{ 276 | DisplayName: "testcreate", 277 | RoleName: roleName, 278 | }, 279 | Statements: dbplugin.Statements{ 280 | Commands: []string{testMongoDBAtlasRole}, 281 | }, 282 | Password: password, 283 | Expiration: time.Now().Add(time.Minute), 284 | } 285 | 286 | createResp := dbtesting.AssertNewUser(t, db, createReq) 287 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 288 | 289 | assertCredsExists(t, projectID, publicKey, privateKey, createResp.Username, password, connURL, testMongoDBAtlasRole) 290 | if len(createResp.Username) != 20 { 291 | t.Fatalf("Username did not match template, username: %s, defaultUserNameTemplate: %s", createResp.Username, defaultUserNameTemplate) 292 | } 293 | 294 | expectedUsernameRegex := `^v-test-[a-zA-Z0-9]{13}$` 295 | require.Regexp(t, expectedUsernameRegex, createResp.Username) 296 | } 297 | 298 | func TestAcceptanceDatabaseUser_CreateUserWithTemplate(t *testing.T) { 299 | if !runAcceptanceTests { 300 | t.SkipNow() 301 | } 302 | 303 | publicKey := os.Getenv(envVarAtlasPublicKey) 304 | privateKey := os.Getenv(envVarAtlasPrivateKey) 305 | projectID := os.Getenv(envVarAtlasProjectID) 306 | connURL := os.Getenv(envVarAtlasClusterName) 307 | 308 | connectionDetails := map[string]interface{}{ 309 | "public_key": publicKey, 310 | "private_key": privateKey, 311 | "project_id": projectID, 312 | "username_template": "begin_{{.RoleName}}_end", 313 | } 314 | 315 | db := new() 316 | defer dbtesting.AssertClose(t, db) 317 | 318 | initReq := dbplugin.InitializeRequest{ 319 | Config: connectionDetails, 320 | } 321 | 322 | dbtesting.AssertInitialize(t, db, initReq) 323 | 324 | password := "myreallysecurepassword" 325 | roleName := "test" 326 | createReq := dbplugin.NewUserRequest{ 327 | UsernameConfig: dbplugin.UsernameMetadata{ 328 | DisplayName: "testcreate", 329 | RoleName: roleName, 330 | }, 331 | Statements: dbplugin.Statements{ 332 | Commands: []string{testMongoDBAtlasRole}, 333 | }, 334 | Password: password, 335 | Expiration: time.Now().Add(time.Minute), 336 | } 337 | 338 | createResp := dbtesting.AssertNewUser(t, db, createReq) 339 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 340 | 341 | assertCredsExists(t, projectID, publicKey, privateKey, createResp.Username, password, connURL, testMongoDBAtlasRole) 342 | 343 | expectedUsername := "begin_" + roleName + "_end" 344 | if createResp.Username != expectedUsername { 345 | t.Fatalf("Username did not match template, username: %s, expectedUsername: %s", createResp.Username, expectedUsername) 346 | } 347 | } 348 | 349 | func TestAcceptanceDatabaseUser_CreateUserWithSpecialChar(t *testing.T) { 350 | if !runAcceptanceTests { 351 | t.SkipNow() 352 | } 353 | 354 | publicKey := os.Getenv(envVarAtlasPublicKey) 355 | privateKey := os.Getenv(envVarAtlasPrivateKey) 356 | projectID := os.Getenv(envVarAtlasProjectID) 357 | connURL := os.Getenv(envVarAtlasClusterName) 358 | 359 | connectionDetails := map[string]interface{}{ 360 | "public_key": publicKey, 361 | "private_key": privateKey, 362 | "project_id": projectID, 363 | } 364 | 365 | db := new() 366 | defer dbtesting.AssertClose(t, db) 367 | 368 | initReq := dbplugin.InitializeRequest{ 369 | Config: connectionDetails, 370 | } 371 | 372 | dbtesting.AssertInitialize(t, db, initReq) 373 | 374 | password := "myreallysecurepassword" 375 | createReq := dbplugin.NewUserRequest{ 376 | UsernameConfig: dbplugin.UsernameMetadata{ 377 | DisplayName: "test.special", 378 | RoleName: "test", 379 | }, 380 | Statements: dbplugin.Statements{ 381 | Commands: []string{testMongoDBAtlasRole}, 382 | }, 383 | Password: password, 384 | Expiration: time.Now().Add(time.Minute), 385 | } 386 | 387 | createResp := dbtesting.AssertNewUser(t, db, createReq) 388 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 389 | 390 | assertCredsExists(t, projectID, publicKey, privateKey, createResp.Username, password, connURL, testMongoDBAtlasRole) 391 | } 392 | 393 | func TestAcceptanceDatabaseUser_DeleteUser(t *testing.T) { 394 | if !runAcceptanceTests { 395 | t.SkipNow() 396 | } 397 | 398 | publicKey := os.Getenv(envVarAtlasPublicKey) 399 | privateKey := os.Getenv(envVarAtlasPrivateKey) 400 | projectID := os.Getenv(envVarAtlasProjectID) 401 | connURL := os.Getenv(envVarAtlasClusterName) 402 | 403 | connectionDetails := map[string]interface{}{ 404 | "public_key": publicKey, 405 | "private_key": privateKey, 406 | "project_id": projectID, 407 | } 408 | 409 | db := new() 410 | defer dbtesting.AssertClose(t, db) 411 | 412 | initReq := dbplugin.InitializeRequest{ 413 | Config: connectionDetails, 414 | } 415 | 416 | dbtesting.AssertInitialize(t, db, initReq) 417 | 418 | password := "myreallysecurepassword" 419 | createReq := dbplugin.NewUserRequest{ 420 | UsernameConfig: dbplugin.UsernameMetadata{ 421 | DisplayName: "testdelete", 422 | RoleName: "test", 423 | }, 424 | Statements: dbplugin.Statements{ 425 | Commands: []string{testMongoDBAtlasRole}, 426 | }, 427 | Password: password, 428 | Expiration: time.Now().Add(time.Minute), 429 | } 430 | 431 | createResp := dbtesting.AssertNewUser(t, db, createReq) 432 | defer func() { 433 | // Delete user directly if the test failed for any reason after this. 434 | if t.Failed() { 435 | deleteAtlasDBUser(t, projectID, publicKey, privateKey, createResp.Username) 436 | } 437 | }() 438 | 439 | assertCredsExists(t, projectID, publicKey, privateKey, createResp.Username, password, connURL, testMongoDBAtlasRole) 440 | 441 | // Test default revocation statement 442 | delReq := dbplugin.DeleteUserRequest{ 443 | Username: createResp.Username, 444 | } 445 | 446 | dbtesting.AssertDeleteUser(t, db, delReq) 447 | 448 | assertCredsDoNotExist(t, projectID, publicKey, privateKey, createResp.Username) 449 | } 450 | 451 | func TestAcceptanceDatabaseUser_UpdateUser_Password(t *testing.T) { 452 | if !runAcceptanceTests { 453 | t.SkipNow() 454 | } 455 | 456 | publicKey := os.Getenv(envVarAtlasPublicKey) 457 | privateKey := os.Getenv(envVarAtlasPrivateKey) 458 | projectID := os.Getenv(envVarAtlasProjectID) 459 | connURL := os.Getenv(envVarAtlasClusterName) 460 | 461 | connectionDetails := map[string]interface{}{ 462 | "public_key": publicKey, 463 | "private_key": privateKey, 464 | "project_id": projectID, 465 | } 466 | 467 | db := new() 468 | defer dbtesting.AssertClose(t, db) 469 | 470 | initReq := dbplugin.InitializeRequest{ 471 | Config: connectionDetails, 472 | } 473 | 474 | dbtesting.AssertInitialize(t, db, initReq) 475 | 476 | // create the database user in advance, and test the connection 477 | dbUser := "testmongouser" 478 | startingPassword := "myreallysecurepassword" 479 | 480 | createAtlasDBUser(t, projectID, publicKey, privateKey, dbUser, startingPassword) 481 | defer deleteAtlasDBUser(t, projectID, publicKey, privateKey, dbUser) 482 | 483 | assertCredsExists(t, projectID, publicKey, privateKey, dbUser, startingPassword, connURL, "") 484 | 485 | newPassword := "some-other-password" 486 | 487 | updateReq := dbplugin.UpdateUserRequest{ 488 | Username: dbUser, 489 | Password: &dbplugin.ChangePassword{ 490 | NewPassword: newPassword, 491 | }, 492 | } 493 | 494 | dbtesting.AssertUpdateUser(t, db, updateReq) 495 | 496 | assertCredsExists(t, projectID, publicKey, privateKey, dbUser, newPassword, connURL, "") 497 | } 498 | 499 | func assertX509UserExist(t testing.TB, projectID, publicKey, privateKey, subject, connURL, expectedRolesAndScopesJSON string) { 500 | t.Helper() 501 | 502 | client, err := getClient(publicKey, privateKey) 503 | if err != nil { 504 | t.Fatalf("Failed to get an API client: %s", err) 505 | } 506 | 507 | dbUser, _, err := client.DatabaseUsers.Get(context.Background(), "$external", projectID, subject) 508 | if err != nil { 509 | t.Fatalf("Failed to retrieve user from from MongoDB Atlas: %s", err) 510 | } 511 | if dbUser.X509Type != "CUSTOMER" { 512 | t.Fatalf("Error! dbUser is expected to be CUSTOMER: %s", err) 513 | } 514 | if expectedRolesAndScopesJSON != "" { 515 | var expectedRolesAndScopes mongoDBAtlasStatement 516 | err = json.Unmarshal([]byte(expectedRolesAndScopesJSON), &expectedRolesAndScopes) 517 | if err != nil { 518 | t.Fatalf("Failed to unmarshal database user: %s", err) 519 | } 520 | if len(dbUser.Roles) != len(expectedRolesAndScopes.Roles) || len(dbUser.Scopes) != len(expectedRolesAndScopes.Scopes) { 521 | t.Fatalf("Mismatch in roles or scopes, expected %+v but got %+v", expectedRolesAndScopes, dbUser) 522 | } 523 | for i := range dbUser.Roles { 524 | if dbUser.Roles[i] != expectedRolesAndScopes.Roles[i] { 525 | t.Fatalf("Mismatch in roles, expected %+v but got %+v", expectedRolesAndScopes.Roles[i], dbUser.Roles[i]) 526 | } 527 | } 528 | for i := range dbUser.Scopes { 529 | if dbUser.Scopes[i] != expectedRolesAndScopes.Scopes[i] { 530 | t.Fatalf("Mismatch in scopes, expected %+v but got %+v", expectedRolesAndScopes.Scopes[i], dbUser.Scopes[i]) 531 | } 532 | } 533 | } 534 | } 535 | 536 | func assertCredsExists(t testing.TB, projectID, publicKey, privateKey, username, password, connURL, expectedRolesAndScopesJSON string) { 537 | t.Helper() 538 | 539 | t.Logf("Asserting username: %s", username) 540 | 541 | client, err := getClient(publicKey, privateKey) 542 | if err != nil { 543 | t.Fatalf("Failed to get an API client: %s", err) 544 | } 545 | 546 | dbUser, _, err := client.DatabaseUsers.Get(context.Background(), "admin", projectID, username) 547 | if err != nil { 548 | t.Fatalf("Failed to retrieve user from from MongoDB Atlas: %s", err) 549 | } 550 | if expectedRolesAndScopesJSON != "" { 551 | var expectedRolesAndScopes mongoDBAtlasStatement 552 | err = json.Unmarshal([]byte(expectedRolesAndScopesJSON), &expectedRolesAndScopes) 553 | if err != nil { 554 | t.Fatalf("Failed to unmarshal database user: %s", err) 555 | } 556 | if len(dbUser.Roles) != len(expectedRolesAndScopes.Roles) || len(dbUser.Scopes) != len(expectedRolesAndScopes.Scopes) { 557 | t.Fatalf("Mismatch in roles or scopes, expected %+v but got %+v", expectedRolesAndScopes, dbUser) 558 | } 559 | for i := range dbUser.Roles { 560 | if dbUser.Roles[i] != expectedRolesAndScopes.Roles[i] { 561 | t.Fatalf("Mismatch in roles, expected %+v but got %+v", expectedRolesAndScopes.Roles[i], dbUser.Roles[i]) 562 | } 563 | } 564 | for i := range dbUser.Scopes { 565 | if dbUser.Scopes[i] != expectedRolesAndScopes.Scopes[i] { 566 | t.Fatalf("Mismatch in scopes, expected %+v but got %+v", expectedRolesAndScopes.Scopes[i], dbUser.Scopes[i]) 567 | } 568 | } 569 | } 570 | 571 | // Connect to the cluster to verify user password 572 | mongoURI := fmt.Sprintf("mongodb+srv://%s", connURL) 573 | ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 574 | defer cancel() 575 | 576 | credential := options.Credential{ 577 | Username: username, 578 | Password: url.QueryEscape(password), 579 | } 580 | clientOpts := options.Client().ApplyURI(mongoURI).SetAuth(credential) 581 | 582 | mClient, err := mongo.Connect(context.Background(), clientOpts) 583 | if err != nil { 584 | t.Fatalf("Failed to connect to mongo: %s", err) 585 | } 586 | defer mClient.Disconnect(context.Background()) 587 | 588 | ticker := time.NewTicker(5 * time.Second) 589 | defer ticker.Stop() 590 | 591 | for { 592 | select { 593 | case <-ctx.Done(): 594 | t.Fatalf("Timed out waiting for user %s to ping database", username) 595 | case <-ticker.C: 596 | err = mClient.Ping(ctx, readpref.Primary()) 597 | if err == nil { 598 | return 599 | } 600 | } 601 | } 602 | } 603 | 604 | func assertCredsDoNotExist(t testing.TB, projectID, publicKey, privateKey, username string) { 605 | t.Helper() 606 | 607 | client, err := getClient(publicKey, privateKey) 608 | if err != nil { 609 | t.Fatalf("Error creating client: %s", err) 610 | } 611 | 612 | dbUser, _, err := client.DatabaseUsers.Get(context.Background(), "admin", projectID, username) 613 | if err == nil && dbUser != nil { 614 | t.Fatal("Expected user to not exist") 615 | } 616 | } 617 | 618 | func createAtlasDBUser(t testing.TB, projectID, publicKey, privateKey, username, startingPassword string) { 619 | t.Helper() 620 | 621 | client, err := getClient(publicKey, privateKey) 622 | if err != nil { 623 | t.Fatalf("Error creating client: %s", err) 624 | } 625 | 626 | databaseUserRequest := &mongodbatlas.DatabaseUser{ 627 | Username: username, 628 | Password: startingPassword, 629 | DatabaseName: "admin", 630 | Roles: []mongodbatlas.Role{ 631 | { 632 | DatabaseName: "admin", 633 | RoleName: "readWriteAnyDatabase", 634 | }, 635 | }, 636 | } 637 | 638 | _, _, err = client.DatabaseUsers.Create(context.Background(), projectID, databaseUserRequest) 639 | if err != nil { 640 | t.Fatalf("Error creating user %s", err) 641 | } 642 | } 643 | 644 | func deleteAtlasDBUser(t testing.TB, projectID, publicKey, privateKey, username string) { 645 | t.Helper() 646 | 647 | client, err := getClient(publicKey, privateKey) 648 | if err != nil { 649 | t.Fatalf("Error creating client: %s", err) 650 | } 651 | 652 | var databaseName string 653 | if isX509User(username) { 654 | databaseName = "$external" 655 | } else { 656 | databaseName = "admin" 657 | } 658 | 659 | _, err = client.DatabaseUsers.Delete(context.Background(), databaseName, projectID, username) 660 | if err != nil { 661 | t.Fatalf("Error deleting database user: %s", err) 662 | } 663 | } 664 | 665 | func getClient(publicKey, privateKey string) (*mongodbatlas.Client, error) { 666 | transport := digest.NewTransport(publicKey, privateKey) 667 | cl, err := transport.Client() 668 | if err != nil { 669 | return nil, err 670 | } 671 | 672 | return mongodbatlas.New(cl) 673 | } 674 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | TOOL=vault-plugin-database-mongodbatlas 7 | 8 | 9 | # This script builds the application from source for a platform. 10 | set -e 11 | 12 | GO_CMD=${GO_CMD:-go} 13 | 14 | # Get the parent directory of where this script is. 15 | SOURCE="${BASH_SOURCE[0]}" 16 | while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done 17 | DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" 18 | 19 | # Change into that directory 20 | cd "$DIR" 21 | 22 | # Set build tags 23 | BUILD_TAGS="${BUILD_TAGS}:-${TOOL}" 24 | 25 | # Get the git commit 26 | GIT_COMMIT="$(git rev-parse HEAD)" 27 | GIT_DIRTY="$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)" 28 | 29 | GOPATH=${GOPATH:-$(go env GOPATH)} 30 | case $(uname) in 31 | CYGWIN*) 32 | GOPATH="$(cygpath $GOPATH)" 33 | ;; 34 | esac 35 | 36 | # Delete the old dir 37 | echo "==> Removing old directory..." 38 | rm -f bin/* 39 | rm -rf pkg/* 40 | mkdir -p bin/ 41 | 42 | # Build! 43 | echo "==> Building..." 44 | ${GO_CMD} build \ 45 | -gcflags "${GCFLAGS}" \ 46 | -ldflags "-X github.com/hashicorp/${TOOL}/version.GitCommit='${GIT_COMMIT}${GIT_DIRTY}'" \ 47 | -o "bin/${TOOL}" \ 48 | -tags "${BUILD_TAGS}" \ 49 | "${DIR}/cmd/${TOOL}" 50 | 51 | # Move all the compiled things to the $GOPATH/bin 52 | OLDIFS=$IFS 53 | IFS=: MAIN_GOPATH=($GOPATH) 54 | IFS=$OLDIFS 55 | 56 | rm -f ${MAIN_GOPATH}/bin/${TOOL} 57 | cp bin/${TOOL} ${MAIN_GOPATH}/bin/ 58 | 59 | # Done! 60 | echo 61 | echo "==> Results:" 62 | ls -hl bin/ 63 | -------------------------------------------------------------------------------- /scripts/gofmtcheck.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright (c) HashiCorp, Inc. 3 | # SPDX-License-Identifier: MPL-2.0 4 | 5 | 6 | echo "==> Checking that code complies with gofmt requirements..." 7 | 8 | gofmt_files=$(gofmt -l `find . -name '*.go' | grep -v vendor`) 9 | if [[ -n ${gofmt_files} ]]; then 10 | echo 'gofmt needs running on the following files:' 11 | echo "${gofmt_files}" 12 | echo "You can use the command: \`make fmt\` to reformat code." 13 | exit 1 14 | fi -------------------------------------------------------------------------------- /website/source/api/secret/databases/mongodbatlas.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "api" 3 | page_title: "MongoDB Atlas - Database - Secrets Engines - HTTP API" 4 | sidebar_title: "MongoDB Atlas" 5 | sidebar_current: "api-http-secret-databases-mongodbatlas" 6 | description: |- 7 | The MongoDB Atlas plugin for Vault's Database Secrets Engine generates MongoDB Database User credentials for MongoDB Atlas. 8 | --- 9 | 10 | # MongoDB Atlas Database Plugin HTTP API 11 | 12 | The MongoDB Atlas plugin is one of the supported plugins for the Database 13 | Secrets Engine. This plugin generates MongoDB Atlas Database User credentials dynamically based on 14 | configured roles. 15 | 16 | ~> **Notice:** The following will be accurate after review and approval by Hashicorp, which is in 17 | progress. Until then follow the instructions in the [README developing section](./../../../../../README.md). 18 | 19 | 20 | ## Configure Connection 21 | 22 | In addition to the parameters defined by the [Database 23 | Backend](/api/secret/databases/index.html#configure-connection), this plugin 24 | has a number of parameters to further configure a connection. 25 | 26 | | Method | Path | 27 | | :--------------------------- | :--------------------- | 28 | | `POST` | `/database/config/:name` | 29 | 30 | ### Parameters 31 | 32 | - `public_key` `(string: )` – The Public Programmatic API Key used to authenticate with the MongoDB Atlas API. 33 | - `private_key` `(string: )` - The Private Programmatic API Key used to connect with MongoDB Atlas API. 34 | - `project_id` `(string: )` - The [Project ID](https://docs.atlas.mongodb.com/api/#group-id) the Database User should be created within. 35 | 36 | ### Sample Payload 37 | 38 | ```json 39 | { 40 | "plugin_name": "mongodbatlas-database-plugin", 41 | "allowed_roles": "readonly", 42 | "public_key": "aPublicKey", 43 | "private_key": "aPrivateKey", 44 | "project_id": "aProjectID" 45 | } 46 | ``` 47 | 48 | ### Sample Request 49 | 50 | ``` 51 | $ curl \ 52 | --header "X-Vault-Token: ..." \ 53 | --request POST \ 54 | --data @payload.json \ 55 | http://127.0.0.1:8200/v1/database/config/mongodbatlas 56 | ``` 57 | 58 | ## Statements 59 | 60 | Statements are configured during Vault role creation and are used by the plugin to 61 | determine what is sent to MongoDB Atlas upon user creation, renewal, and 62 | revocation. For more information on configuring roles see the [Role API](/api/secret/databases/index.html#create-role) 63 | in the Database Secrets Engine docs. 64 | 65 | ### Parameters 66 | 67 | The following are the statements used by this plugin. If not mentioned in this 68 | list the plugin does not support that statement type. 69 | 70 | - `creation_statements` `(string: )` – Specifies the database 71 | statements executed to create and configure a user. Must be a 72 | serialized JSON object, or a base64-encoded serialized JSON object. 73 | The object can optionally contain a "database_name", the name of 74 | the authentication database to log into MongoDB. In Atlas deployments of 75 | MongoDB, the authentication database is always the admin database. And 76 | also must contain a "roles" array. This array contains objects that holds 77 | a series of roles "roleName", an optional "databaseName" and "collectionName" 78 | value. For more information regarding the `roles` field, refer to 79 | [MongoDB Atlas documentation](https://docs.atlas.mongodb.com/reference/api/database-users-create-a-user/). 80 | - `default_ttl` `(string/int): 0` - Specifies the TTL for the leases associated with this role. 81 | Accepts time suffixed strings ("1h") or an integer number of seconds. Defaults to system/engine default TTL time. 82 | `max_ttl` `(string/int): 0` - Specifies the maximum TTL for the leases associated with this role. Accepts time 83 | suffixed strings ("1h") or an integer number of seconds. Defaults to system/mount default TTL time; this value 84 | is allowed to be less than the mount max TTL (or, if not set, the system max TTL), 85 | but it is not allowed to be longer. See also [The TTL General Case](https://www.vaultproject.io/docs/concepts/tokens.html#the-general-case). 86 | 87 | 88 | ### Sample Creation Statement 89 | 90 | ```json 91 | { 92 | "database_name": "admin", 93 | "roles": [ 94 | { 95 | "databaseName": "admin", 96 | "roleName": "atlasAdmin" 97 | }, 98 | { 99 | "collectionName": "acollection", 100 | "roleName": "read" 101 | } 102 | ] 103 | } 104 | ``` 105 | -------------------------------------------------------------------------------- /website/source/docs/secrets/databases/mongodbatlas.html.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: "docs" 3 | page_title: "MongoDB Atlas - Database - Secrets Engines" 4 | sidebar_title: "MongoDB Atlas" 5 | sidebar_current: "docs-secrets-databases-mongodb-atlas" 6 | description: |- 7 | MongoDB Atlas is one of the supported plugins for the Database Secrets Engine. This 8 | plugin generates MongoDB Atlas Database User credentials dynamically based on configured Vault roles for 9 | the MongoDB Atlas. 10 | --- 11 | 12 | # MongoDB Atlas Database Secrets Engine 13 | 14 | MongoDB Atlas is one of the supported plugins for the database secrets engine. This 15 | plugin generates database credentials dynamically based on configured roles for 16 | the MongoDB database. 17 | 18 | ## Setup 19 | 20 | 1. Enable the database secrets engine if it is not already enabled: 21 | 22 | ```text 23 | $ vault secrets enable database 24 | Success! Enabled the database secrets engine at: database/ 25 | ``` 26 | 27 | The secrets engine will be enabled at the default path which is name of the engine. To 28 | enable the secrets engine at a different path use the `-path` argument. 29 | 30 | 1. Configure Vault with the proper plugin and connection information: 31 | 32 | ```text 33 | $ vault write database/config/my-mongodbatlas-database \ 34 | plugin_name=mongodbatlas-database-plugin \ 35 | allowed_roles="my-role" \ 36 | public_key="a-public-key" \ 37 | private_key="a-private-key!" \ 38 | project_id="a-project-id" 39 | ``` 40 | 41 | 2. Configure a role that maps a name in Vault to a MongoDB Atlas command that executes and 42 | creates the Database User credential: 43 | 44 | ```text 45 | $ vault write database/roles/my-role \ 46 | db_name=my-mongodbatlas-database \ 47 | creation_statements='{ "database_name": "admin", "roles": [{"databaseName":"admin","roleName":"atlasAdmin"}]}' \ 48 | default_ttl="1h" \ 49 | max_ttl="24h" 50 | Success! Data written to: database/roles/my-role 51 | ``` 52 | 53 | ## Usage 54 | 55 | After the secrets engine is configured and a user/machine has a Vault token with 56 | the proper permission, it can generate credentials. 57 | 58 | 1. Generate a new credential by reading from the `/creds` endpoint with the name 59 | of the role: 60 | 61 | ```text 62 | $ vault read database/creds/my-role 63 | Key Value 64 | --- ----- 65 | lease_id database/creds/my-role/2f6a614c-4aa2-7b19-24b9-ad944a8d4de6 66 | lease_duration 1h 67 | lease_renewable true 68 | password A1a-QwxApKgnfCp1AJYN 69 | username v-root-my-role-5WFTBKdwOTLOqWLgsjvH-1565815206 70 | ``` 71 | 72 | ## API 73 | 74 | The full list of configurable options can be seen in the [MongoDB database 75 | plugin API](/api/secret/databases/mongodb.html) page. 76 | 77 | For more information on the database secrets engine's HTTP API please see the 78 | [Database secrets engine API](/api/secret/databases/index.html) page. 79 | --------------------------------------------------------------------------------