mirror of
https://github.com/bartvdbraak/keyweave.git
synced 2025-04-27 23:01:20 +00:00
Merge pull request #33 from bartvdbraak/feat/31-e2e-tests-and-workflows
End-to-end test infrastructure and workflow
This commit is contained in:
commit
bbeb3a3fe3
10 changed files with 864 additions and 8 deletions
4
.github/workflows/checks.yml
vendored
4
.github/workflows/checks.yml
vendored
|
@ -39,8 +39,8 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Run tests
|
||||
run: cargo test --all
|
||||
- name: Run unit tests
|
||||
run: cargo test --bins
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
|
101
.github/workflows/tests.yml
vendored
Normal file
101
.github/workflows/tests.yml
vendored
Normal file
|
@ -0,0 +1,101 @@
|
|||
name: Tests
|
||||
|
||||
permissions:
|
||||
id-token: write
|
||||
contents: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
bicep:
|
||||
name: Deploy Azure resources
|
||||
environment: bicep
|
||||
runs-on: ubuntu-latest
|
||||
concurrency:
|
||||
group: bicep
|
||||
env:
|
||||
LOCATION: eastus
|
||||
DEPLOYMENT_NAME: keyweave-${{ github.run_id }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Deploy Bicep template
|
||||
uses: azure/arm-deploy@v1
|
||||
with:
|
||||
scope: subscription
|
||||
region: ${{ env.LOCATION }}
|
||||
template: bicep/main.bicep
|
||||
deploymentName: ${{ env.DEPLOYMENT_NAME }}
|
||||
|
||||
tests-no-access:
|
||||
name: Tests with No Access
|
||||
needs: bicep
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 'Az CLI login'
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID_NO_ACCESS }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Run all tests
|
||||
run: cargo test no_access
|
||||
tests-get:
|
||||
name: Tests with Get
|
||||
needs: bicep
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 'Az CLI login'
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID_GET }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Run all tests
|
||||
run: cargo test only_get
|
||||
tests-list:
|
||||
name: Tests with List
|
||||
needs: bicep
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 'Az CLI login'
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID_LIST }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Run all tests
|
||||
run: cargo test only_list
|
||||
tests-get-list:
|
||||
name: Tests with Get and List
|
||||
needs: bicep
|
||||
runs-on: ubuntu-latest
|
||||
environment: test
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: 'Az CLI login'
|
||||
uses: azure/login@v1
|
||||
with:
|
||||
client-id: ${{ secrets.AZURE_CLIENT_ID_GET_LIST }}
|
||||
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
|
||||
- name: Run all tests
|
||||
run: cargo test get_and_list_access
|
297
Cargo.lock
generated
297
Cargo.lock
generated
|
@ -17,6 +17,15 @@ version = "1.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android-tzdata"
|
||||
version = "0.1.1"
|
||||
|
@ -86,6 +95,36 @@ version = "1.0.75"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
|
||||
[[package]]
|
||||
name = "assert_cmd"
|
||||
version = "2.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"bstr",
|
||||
"doc-comment",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"wait-timeout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert_fs"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f070617a68e5c2ed5d06ee8dd620ee18fb72b99f6c094bed34cf8ab07c875b48"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"doc-comment",
|
||||
"globwalk",
|
||||
"predicates",
|
||||
"predicates-core",
|
||||
"predicates-tree",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-channel"
|
||||
version = "1.9.0"
|
||||
|
@ -235,6 +274,17 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.14.0"
|
||||
|
@ -382,6 +432,19 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown 0.14.2",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.3.9"
|
||||
|
@ -392,6 +455,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "difflib"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
@ -402,12 +471,24 @@ dependencies = [
|
|||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "doc-comment"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
|
@ -469,6 +550,15 @@ version = "2.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -643,6 +733,30 @@ version = "0.28.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"fnv",
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globwalk"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"ignore",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.21"
|
||||
|
@ -668,6 +782,12 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
|
@ -804,6 +924,23 @@ dependencies = [
|
|||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
|
||||
dependencies = [
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex",
|
||||
"same-file",
|
||||
"thread_local",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.3"
|
||||
|
@ -811,7 +948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -835,6 +972,15 @@ version = "2.9.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.9"
|
||||
|
@ -855,12 +1001,17 @@ name = "keyweave"
|
|||
version = "0.2.3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"assert_cmd",
|
||||
"assert_fs",
|
||||
"azure_core",
|
||||
"azure_identity",
|
||||
"azure_security_keyvault",
|
||||
"clap",
|
||||
"futures",
|
||||
"openssl",
|
||||
"paris",
|
||||
"predicates",
|
||||
"serial_test",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
|
@ -948,6 +1099,12 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.17"
|
||||
|
@ -1161,6 +1318,37 @@ version = "0.2.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "predicates"
|
||||
version = "3.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"difflib",
|
||||
"float-cmp",
|
||||
"itertools",
|
||||
"normalize-line-endings",
|
||||
"predicates-core",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "predicates-core"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174"
|
||||
|
||||
[[package]]
|
||||
name = "predicates-tree"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf"
|
||||
dependencies = [
|
||||
"predicates-core",
|
||||
"termtree",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.69"
|
||||
|
@ -1259,6 +1447,35 @@ dependencies = [
|
|||
"bitflags 1.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.22"
|
||||
|
@ -1333,6 +1550,15 @@ version = "1.0.15"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.22"
|
||||
|
@ -1441,6 +1667,31 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
|
@ -1547,6 +1798,12 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termtree"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76"
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.50"
|
||||
|
@ -1567,6 +1824,16 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.30"
|
||||
|
@ -1774,12 +2041,31 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "waker-fn"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
|
@ -1906,6 +2192,15 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
|
|
|
@ -11,6 +11,7 @@ repository = "https://github.com/bartvdbraak/keyweave/"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.75"
|
||||
azure_core = "0.17.0"
|
||||
azure_identity = "0.17.0"
|
||||
azure_security_keyvault = "0.17.0"
|
||||
clap = { version = "4.4.8", features = ["derive"] }
|
||||
|
@ -20,3 +21,9 @@ tokio = {version = "1.34.0", features = ["full"]}
|
|||
|
||||
[target.'cfg(all(target_os = "linux", any(target_env = "musl", target_arch = "arm", target_arch = "aarch64")))'.dependencies]
|
||||
openssl = { version = "0.10", features = ["vendored"] }
|
||||
|
||||
[dev-dependencies]
|
||||
assert_cmd = "2.0.12"
|
||||
assert_fs = "1.0.13"
|
||||
predicates = "3.0.4"
|
||||
serial_test = "2.0.0"
|
||||
|
|
85
bicep/main.bicep
Normal file
85
bicep/main.bicep
Normal file
|
@ -0,0 +1,85 @@
|
|||
targetScope = 'subscription'
|
||||
|
||||
/*
|
||||
Parameters
|
||||
*/
|
||||
|
||||
@allowed([
|
||||
'D' // Development
|
||||
'T' // Test
|
||||
'A' // Acceptance
|
||||
'P' // Production
|
||||
])
|
||||
param environment string = 'T'
|
||||
param location string = 'westeurope'
|
||||
param name object = {
|
||||
tenantId: 'BVDB'
|
||||
projectId: 'KEYWEAVE'
|
||||
region: 'WEU'
|
||||
}
|
||||
|
||||
/*
|
||||
Variables
|
||||
*/
|
||||
|
||||
var tags = {
|
||||
project: 'keyweave'
|
||||
}
|
||||
var nameFormat = '${name.tenantId}-${name.projectId}-${environment}-${name.region}-{0}-{1:N0}'
|
||||
|
||||
/*
|
||||
Resource Group
|
||||
*/
|
||||
|
||||
resource ResourceGroup 'Microsoft.Resources/resourceGroups@2022-09-01' = {
|
||||
name: format(nameFormat, 'RG', 1)
|
||||
location: location
|
||||
tags: tags
|
||||
}
|
||||
|
||||
/*
|
||||
Module for Log Analytics Workspace
|
||||
*/
|
||||
|
||||
module LogAnalyticsWorkspace 'modules/law.bicep' = {
|
||||
name: 'LogAnalyticsWorkspace'
|
||||
scope: ResourceGroup
|
||||
params: {
|
||||
nameFormat: nameFormat
|
||||
location: location
|
||||
tags: tags
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Module for Managed Identities
|
||||
*/
|
||||
|
||||
module ManagedIdentities 'modules/id.bicep' = {
|
||||
name: 'ManagedIdentities'
|
||||
scope: ResourceGroup
|
||||
params: {
|
||||
nameFormat: nameFormat
|
||||
location: location
|
||||
tags: tags
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Module for KeyVault
|
||||
*/
|
||||
|
||||
module KeyVault 'modules/kv.bicep' = {
|
||||
name: 'KeyVault'
|
||||
scope: ResourceGroup
|
||||
dependsOn: [
|
||||
LogAnalyticsWorkspace
|
||||
]
|
||||
params: {
|
||||
nameFormat: nameFormat
|
||||
location: location
|
||||
tags: tags
|
||||
|
||||
identities: ManagedIdentities.outputs.identities
|
||||
}
|
||||
}
|
33
bicep/modules/id.bicep
Normal file
33
bicep/modules/id.bicep
Normal file
|
@ -0,0 +1,33 @@
|
|||
param nameFormat string
|
||||
param location string
|
||||
param tags object
|
||||
|
||||
param identityEnvironments array = [
|
||||
'none'
|
||||
'get'
|
||||
'list'
|
||||
'getlist'
|
||||
]
|
||||
|
||||
resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = [for (environment, index) in identityEnvironments: {
|
||||
name: format(nameFormat, 'ID', index+1)
|
||||
location: location
|
||||
tags: tags
|
||||
}]
|
||||
|
||||
resource federatedCredential 'Microsoft.ManagedIdentity/userAssignedIdentities/federatedIdentityCredentials@2023-01-31' = [for (environment, index) in identityEnvironments: {
|
||||
name: environment
|
||||
parent: managedIdentity[index]
|
||||
properties: {
|
||||
issuer: 'https://token.actions.githubusercontent.com'
|
||||
subject: 'repo:bartvdbraak/keyweave:environment:test'
|
||||
audiences: [
|
||||
'api://AzureADTokenExchange'
|
||||
]
|
||||
}
|
||||
}]
|
||||
|
||||
output identities array = [for (environment, index) in identityEnvironments: {
|
||||
name: environment
|
||||
id: managedIdentity[index].properties.principalId
|
||||
}]
|
121
bicep/modules/kv.bicep
Normal file
121
bicep/modules/kv.bicep
Normal file
|
@ -0,0 +1,121 @@
|
|||
param nameFormat string
|
||||
param location string
|
||||
param tags object
|
||||
|
||||
param identities array
|
||||
|
||||
var accessPolicies = [for identity in identities: {
|
||||
tenantId: tenant().tenantId
|
||||
objectId: identity.id
|
||||
permissions: {
|
||||
secrets: contains(identity.name, 'get') && contains(identity.name, 'list') ? ['Get', 'List'] : contains(identity.name, 'get') ? ['Get'] : contains(identity.name, 'list') ? ['List'] : []
|
||||
}
|
||||
}]
|
||||
|
||||
/*
|
||||
Log Analytics Workspace (existing)
|
||||
*/
|
||||
|
||||
resource _logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
|
||||
name: format(nameFormat, 'LAW', 1)
|
||||
}
|
||||
|
||||
/*
|
||||
Key Vault
|
||||
*/
|
||||
|
||||
resource keyVault 'Microsoft.KeyVault/vaults@2023-02-01' = {
|
||||
name: replace(toLower(format(nameFormat, 'KVT', 1)), '-', '')
|
||||
location: location
|
||||
tags: tags
|
||||
properties: {
|
||||
sku: {
|
||||
family: 'A'
|
||||
name: 'standard'
|
||||
}
|
||||
tenantId: tenant().tenantId
|
||||
enableSoftDelete: true
|
||||
enablePurgeProtection: true
|
||||
accessPolicies: accessPolicies
|
||||
}
|
||||
resource testSecret 'secrets' = {
|
||||
name: 'testSecret'
|
||||
properties: {
|
||||
value: 'testSecretValue'
|
||||
}
|
||||
}
|
||||
resource filterTestSecret 'secrets' = {
|
||||
name: 'filterTestSecret'
|
||||
properties: {
|
||||
value: 'filterTestSecretValue'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Key Vault
|
||||
*/
|
||||
|
||||
resource keyVaultWithFirewall 'Microsoft.KeyVault/vaults@2023-02-01' = {
|
||||
name: replace(toLower(format(nameFormat, 'KVT', 2)), '-', '')
|
||||
location: location
|
||||
tags: tags
|
||||
properties: {
|
||||
sku: {
|
||||
family: 'A'
|
||||
name: 'standard'
|
||||
}
|
||||
tenantId: tenant().tenantId
|
||||
enableSoftDelete: true
|
||||
enablePurgeProtection: true
|
||||
accessPolicies: accessPolicies
|
||||
networkAcls: {
|
||||
defaultAction: 'Deny'
|
||||
ipRules: []
|
||||
}
|
||||
}
|
||||
resource testSecret 'secrets' = {
|
||||
name: 'testSecret'
|
||||
properties: {
|
||||
value: 'testSecretValue'
|
||||
}
|
||||
}
|
||||
resource filterTestSecret 'secrets' = {
|
||||
name: 'filterTestSecret'
|
||||
properties: {
|
||||
value: 'filterTestSecretValue'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Diagnostic Settings for Key Vaults
|
||||
*/
|
||||
|
||||
resource keyVaultDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
|
||||
name: 'keyVaultLogging'
|
||||
scope: keyVault
|
||||
properties: {
|
||||
workspaceId: _logAnalyticsWorkspace.id
|
||||
logs: [
|
||||
{
|
||||
category: 'AuditEvent'
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
resource keyVaultWithFirewallDiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
|
||||
name: 'keyVaultLogging'
|
||||
scope: keyVaultWithFirewall
|
||||
properties: {
|
||||
workspaceId: _logAnalyticsWorkspace.id
|
||||
logs: [
|
||||
{
|
||||
category: 'AuditEvent'
|
||||
enabled: true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
24
bicep/modules/law.bicep
Normal file
24
bicep/modules/law.bicep
Normal file
|
@ -0,0 +1,24 @@
|
|||
param nameFormat string
|
||||
param location string
|
||||
param tags object
|
||||
|
||||
/*
|
||||
Log Analytics Workspace
|
||||
*/
|
||||
|
||||
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
|
||||
name: format(nameFormat, 'LAW', 1)
|
||||
location: location
|
||||
tags: tags
|
||||
properties: {
|
||||
sku: {
|
||||
name: 'PerGB2018'
|
||||
}
|
||||
features: {
|
||||
enableLogAccessUsingOnlyResourcePermissions: true
|
||||
}
|
||||
workspaceCapping: {
|
||||
dailyQuotaGb: json('0.025')
|
||||
}
|
||||
}
|
||||
}
|
49
src/main.rs
49
src/main.rs
|
@ -1,4 +1,5 @@
|
|||
use anyhow::Result;
|
||||
use azure_core::error::HttpError;
|
||||
use azure_identity::DefaultAzureCredential;
|
||||
use azure_security_keyvault::prelude::KeyVaultGetSecretsResponse;
|
||||
use azure_security_keyvault::KeyvaultClient;
|
||||
|
@ -27,6 +28,23 @@ struct Opts {
|
|||
filter: Option<String>,
|
||||
}
|
||||
|
||||
async fn check_vault_dns(vault_name: &str) -> Result<()> {
|
||||
let vault_host = format!("{}.vault.azure.net", vault_name);
|
||||
|
||||
let lookup_result = { tokio::net::lookup_host((vault_host.as_str(), 443)).await };
|
||||
|
||||
match lookup_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => {
|
||||
error!("DNS lookup failed for Key Vault: {}", vault_name);
|
||||
error!(
|
||||
"Please check that the Key Vault exists or that you have no connectivity issues."
|
||||
);
|
||||
Err(err.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn fetch_secrets_from_key_vault(
|
||||
client: &KeyvaultClient,
|
||||
filter: Option<&str>,
|
||||
|
@ -38,8 +56,27 @@ async fn fetch_secrets_from_key_vault(
|
|||
let page = match page {
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
error!("Failed to fetch secrets page: {}", err);
|
||||
return Err(err.into()); // Convert the error into anyhow::Error
|
||||
error!("\n");
|
||||
error!("Failed to fetch secrets.");
|
||||
let specific_error = err.downcast_ref::<HttpError>();
|
||||
if let Some(specific_error) = specific_error {
|
||||
if specific_error
|
||||
.error_message()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.contains("does not have secrets list permission on key vault")
|
||||
{
|
||||
error!("Make sure you have List permissions on the Key Vault.");
|
||||
} else if specific_error
|
||||
.error_message()
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.contains("is not authorized and caller is not a trusted service")
|
||||
{
|
||||
error!("Make sure you're on the Key Vaults Firewall allowlist.");
|
||||
}
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
secret_values
|
||||
|
@ -105,8 +142,8 @@ async fn fetch_and_send_secret(
|
|||
let _ = tx.send((secret_id.clone(), bundle.value.clone())).await;
|
||||
(secret_id, bundle.value)
|
||||
}
|
||||
Err(err) => {
|
||||
error!("Error fetching secret: {}", err);
|
||||
Err(_err) => {
|
||||
error!("Error fetching secret. Make sure you have Get permissions on the Key Vault.");
|
||||
(secret_id, String::new())
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +195,7 @@ mod tests {
|
|||
vec!["SECRET_KEY=secret_value1", "API_KEY=secret_value2",]
|
||||
);
|
||||
|
||||
fs::remove_file(test_file)?; // Clean up the test file
|
||||
fs::remove_file(test_file)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -181,6 +218,8 @@ async fn main() -> Result<()> {
|
|||
};
|
||||
log.success("Detected credentials.");
|
||||
|
||||
check_vault_dns(&opts.vault_name).await?;
|
||||
|
||||
log.loading(format!(
|
||||
"Fetching secrets from Key Vault: <blue>{}</>",
|
||||
opts.vault_name
|
||||
|
|
151
tests/e2e.rs
Normal file
151
tests/e2e.rs
Normal file
|
@ -0,0 +1,151 @@
|
|||
use assert_cmd::prelude::*;
|
||||
use assert_fs::prelude::*;
|
||||
use assert_fs::TempDir;
|
||||
use predicates::prelude::*;
|
||||
use serial_test::serial;
|
||||
use std::process::Command;
|
||||
|
||||
static BINARY: &str = "keyweave";
|
||||
static KEYVAULT: &str = "bvdbkeyweavetweukvt1";
|
||||
static FIREWALL_KEYVAULT: &str = "bvdbkeyweavetweukvt2";
|
||||
static NON_EXISTENT_KEYVAULT: &str = "bvdbkeyweavetweukvt3";
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_no_access_policies() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().failure().stderr(predicate::str::contains(
|
||||
"Make sure you have List permissions on the Key Vault.",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_only_get_access_policy() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().failure().stderr(predicate::str::contains(
|
||||
"Make sure you have List permissions on the Key Vault.",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
/// Test with only List access policy - expected to succeed with get errors.
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_only_list_access_policy() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().success().stderr(predicate::str::contains(
|
||||
"Make sure you have Get permissions on the Key Vault.",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
/// Test with both Get and List access policies - expected to pass.
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_get_and_list_access_policies() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().success();
|
||||
|
||||
output_path.assert(predicate::path::is_file());
|
||||
output_path.assert(predicate::str::contains("testSecret=testSecretValue"));
|
||||
output_path.assert(predicate::str::contains(
|
||||
"filterTestSecret=filterTestSecretValue",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
/// Test with both Get and List access policies and filter - expected to pass.
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_get_and_list_access_policies_filter() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path())
|
||||
.arg("--filter")
|
||||
.arg("filter");
|
||||
cmd.assert().success();
|
||||
|
||||
output_path.assert(predicate::path::is_file());
|
||||
output_path.assert(predicate::str::contains(
|
||||
"filterTestSecret=filterTestSecretValue",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
/// Test with both Get and List access policies on a Key Vault with Firewall - expected to fail.
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_get_and_list_access_policies_firewall() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(FIREWALL_KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().failure().stderr(predicate::str::contains(
|
||||
"Make sure you're on the Key Vaults Firewall allowlist.",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
||||
|
||||
/// Test with both Get and List access policies on a non-existent Key Vault - expected to fail.
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_get_and_list_access_policies_non_existent() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let output_path = temp_dir.child(".env");
|
||||
|
||||
let mut cmd = Command::cargo_bin(BINARY).unwrap();
|
||||
cmd.arg("--vault-name")
|
||||
.arg(NON_EXISTENT_KEYVAULT)
|
||||
.arg("--output")
|
||||
.arg(output_path.path());
|
||||
cmd.assert().failure().stderr(predicate::str::contains(
|
||||
"Please check that the Key Vault exists or that you have no connectivity issues.",
|
||||
));
|
||||
|
||||
temp_dir.close().unwrap();
|
||||
}
|
Loading…
Reference in a new issue