mirror of
https://github.com/Infisical/infisical.git
synced 2025-08-18 20:42:41 +00:00
Compare commits
575 Commits
misc/add-u
...
disable-ra
Author | SHA1 | Date | |
---|---|---|---|
|
2084539f61 | ||
|
34cf47a5eb | ||
|
b90c6cf3fc | ||
|
bbc94da522 | ||
|
8a241771ec | ||
|
1f23515aac | ||
|
63dc9ec35d | ||
|
1d083befe4 | ||
|
c01e29b932 | ||
|
3aed79071b | ||
|
140fa49871 | ||
|
03a3e80082 | ||
|
bfcfffbabf | ||
|
210bd220e5 | ||
|
7be2a10631 | ||
|
5753eb7d77 | ||
|
cb86aa40fa | ||
|
1131143a71 | ||
|
a50b8120fd | ||
|
f1ee53d417 | ||
|
229ad79f49 | ||
|
d7dbd01ecf | ||
|
026fd21fd4 | ||
|
9b9c1a52b3 | ||
|
98aa424e2e | ||
|
2cd5df1ab3 | ||
|
e0d863e06e | ||
|
d991af557b | ||
|
ae54d04357 | ||
|
fa590ba697 | ||
|
1da2896bb0 | ||
|
423a2f38ea | ||
|
db0a72f7b4 | ||
|
4a202d180a | ||
|
33103f1e95 | ||
|
ce8a4bc50e | ||
|
141a821091 | ||
|
b3dd5410d7 | ||
|
74574c6c29 | ||
|
4f32756951 | ||
|
961fe09a6e | ||
|
5ab853d3e6 | ||
|
0e073cc9fc | ||
|
433b1a49f0 | ||
|
b0b255461d | ||
|
c2f2dc1e72 | ||
|
0ee1b425df | ||
|
46e72e9fba | ||
|
06fc4e955d | ||
|
ece294c483 | ||
|
2e40ee76d0 | ||
|
9a712b5c85 | ||
|
1ec427053b | ||
|
6c636415bb | ||
|
9b083a5dfb | ||
|
e323cb4630 | ||
|
e87a1bd402 | ||
|
3b09173bb1 | ||
|
2a8e159f51 | ||
|
cd333a7923 | ||
|
e11fdf8f3a | ||
|
4725108319 | ||
|
715441908b | ||
|
3f190426fe | ||
|
954e94cd87 | ||
|
9dd2379fb3 | ||
|
6bf9ab5937 | ||
|
ee536717c0 | ||
|
a0cb4889ca | ||
|
271a8de4c0 | ||
|
b18f7b957d | ||
|
e6349474aa | ||
|
d6da108e32 | ||
|
577c81be65 | ||
|
93baf9728b | ||
|
064322936b | ||
|
7634fc94a6 | ||
|
ecd39abdc1 | ||
|
d8313a161e | ||
|
d82b06c72b | ||
|
b8e79f20dc | ||
|
0088217fa9 | ||
|
13485cecbb | ||
|
85e9952a4c | ||
|
3d072c2f48 | ||
|
ebcf4761b6 | ||
|
82b828c10e | ||
|
bf20556b17 | ||
|
dcde10a401 | ||
|
e0373cf416 | ||
|
ea038f26df | ||
|
f95c446651 | ||
|
59ab4de24a | ||
|
d2295c47f5 | ||
|
47dc4f0c47 | ||
|
4b0e0d4de5 | ||
|
6128301622 | ||
|
766c1242fd | ||
|
8c318f51e4 | ||
|
be51e358fc | ||
|
e8dd8a908d | ||
|
fd20cb1e38 | ||
|
a07f168c36 | ||
|
530045aaf2 | ||
|
cd4f2cccf8 | ||
|
ff4ff0588f | ||
|
993024662a | ||
|
a03c152abf | ||
|
45d2cc05b3 | ||
|
74200bf860 | ||
|
c59cecdb45 | ||
|
483f26d863 | ||
|
da094383b8 | ||
|
fce772bc20 | ||
|
5e1a7cfb6e | ||
|
323d5d2d27 | ||
|
dd79d0385a | ||
|
0a28ac4a7d | ||
|
196c616986 | ||
|
bf6060d353 | ||
|
438e2dfa07 | ||
|
3ad50a4386 | ||
|
ed94e7a8e7 | ||
|
09ad1cce96 | ||
|
d7f9cff43e | ||
|
5d8d75ac93 | ||
|
db5a85d3ca | ||
|
a1a931d3dd | ||
|
e639f5ee49 | ||
|
a2c9c4529b | ||
|
0a338ee539 | ||
|
2a7679005e | ||
|
838d132898 | ||
|
b0cacc5a4a | ||
|
68d07f0136 | ||
|
10a3c7015e | ||
|
03b0334fa0 | ||
|
10a3658328 | ||
|
e8ece6be3f | ||
|
c765c20539 | ||
|
5cdabd3e61 | ||
|
2f4c42482d | ||
|
75ca093b24 | ||
|
6c0889f117 | ||
|
8f49d45309 | ||
|
a4a162ab65 | ||
|
5b11232325 | ||
|
8b53f63d69 | ||
|
6c0975554d | ||
|
042a472f59 | ||
|
697543e4a2 | ||
|
53c015988d | ||
|
73b5ca5b4f | ||
|
a1318d54b1 | ||
|
44afe2fc1d | ||
|
956d0f6c5d | ||
|
c376add0fa | ||
|
fb0b6b00dd | ||
|
977c02357b | ||
|
d4125443a3 | ||
|
8e3ac6ca29 | ||
|
a5f198a3d5 | ||
|
fa9bdd21ff | ||
|
accf42de2e | ||
|
2f060407ab | ||
|
c516ce8196 | ||
|
95ccd35f61 | ||
|
348a412cda | ||
|
c5a5ad93a8 | ||
|
d55ddcd577 | ||
|
67a0e5ae68 | ||
|
37cbb4c55b | ||
|
d5741b4a72 | ||
|
506b56b657 | ||
|
351304fda6 | ||
|
2af515c486 | ||
|
cdfec32195 | ||
|
8d6bd5d537 | ||
|
4654a17e5f | ||
|
b6d67df966 | ||
|
3897f0ece5 | ||
|
7719ebb112 | ||
|
f03f02786d | ||
|
c60840e979 | ||
|
6fe7a5f069 | ||
|
14b7d763ad | ||
|
bc1b7ddcc5 | ||
|
dff729ffc1 | ||
|
786f5d9e09 | ||
|
ef6abedfe0 | ||
|
9a5633fda4 | ||
|
f8a96576c9 | ||
|
dd2fee3eca | ||
|
802cf79af5 | ||
|
88d3d62894 | ||
|
ac40dcc2c6 | ||
|
6482e88dfc | ||
|
a01249e903 | ||
|
7b3e1f12bd | ||
|
031c8d67b1 | ||
|
778b0d4368 | ||
|
95b57e144d | ||
|
1d26269993 | ||
|
ffee1701fc | ||
|
871be7132a | ||
|
5fe3c9868f | ||
|
c936aa7157 | ||
|
05005f4258 | ||
|
c179d7e5ae | ||
|
c8553fba2b | ||
|
26a9d68823 | ||
|
af5b3aa171 | ||
|
d4728e31c1 | ||
|
f9a5b46365 | ||
|
d65deab0af | ||
|
61591742e4 | ||
|
54b13a9daa | ||
|
4adf0aa1e2 | ||
|
3d3ee746cf | ||
|
07e4358d00 | ||
|
962dd5d919 | ||
|
52bd1afb0a | ||
|
d918dd8967 | ||
|
e2e0f6a346 | ||
|
326cb99732 | ||
|
341b63c61c | ||
|
81b026865c | ||
|
f50c72c033 | ||
|
e1046e2d56 | ||
|
ed3fa8add1 | ||
|
d123283849 | ||
|
d7fd44b845 | ||
|
cefcd872ee | ||
|
3ffee049ee | ||
|
9924ef3a71 | ||
|
524462d7bc | ||
|
4955e2064d | ||
|
6ebc766308 | ||
|
6f9a66a0d7 | ||
|
cca7b68dd0 | ||
|
ab39f13e03 | ||
|
351e573fea | ||
|
f1bc26e2e5 | ||
|
8aeb607f6e | ||
|
e530b7a788 | ||
|
bf61090b5a | ||
|
106b068a51 | ||
|
6f0a97a2fa | ||
|
5d604be091 | ||
|
905cf47d90 | ||
|
2c40d316f4 | ||
|
32521523c1 | ||
|
3a2e8939b1 | ||
|
e5947fcab9 | ||
|
a6d9c74054 | ||
|
07bd527cc1 | ||
|
f7cf2bb78f | ||
|
ff24e76a32 | ||
|
6ac802b6c9 | ||
|
ff92e00503 | ||
|
b20474c505 | ||
|
e19ffc91c6 | ||
|
61eb66efca | ||
|
fa7843983f | ||
|
2d5b7afda7 | ||
|
15999daa24 | ||
|
82520a7f0a | ||
|
af236ba892 | ||
|
ec31211bca | ||
|
0ecf6044d9 | ||
|
6c512f47bf | ||
|
c4b7d4618d | ||
|
003f2b003d | ||
|
33b135f02c | ||
|
eed7cc6408 | ||
|
440ada464f | ||
|
4f08801ae8 | ||
|
6b7abbbeb9 | ||
|
cfe2bbe125 | ||
|
29dcf229d8 | ||
|
3944e20a5b | ||
|
747b5ec68d | ||
|
2079913511 | ||
|
ed0dc324a3 | ||
|
1c13ed54af | ||
|
049f0f56a0 | ||
|
9ad725fd6c | ||
|
9a954c8f15 | ||
|
81a64d081c | ||
|
43804f62e6 | ||
|
67089af17a | ||
|
8abfea0409 | ||
|
ce4adccc80 | ||
|
dcd3b5df56 | ||
|
d83240749f | ||
|
36144d8c42 | ||
|
f6425480ca | ||
|
a3e9392a2f | ||
|
633a2ae985 | ||
|
4478dc8659 | ||
|
510ddf2b1a | ||
|
5363f8c6ff | ||
|
7d9de6acba | ||
|
bac944133a | ||
|
f059d65b45 | ||
|
c487b2b34a | ||
|
015a193330 | ||
|
8e20531b40 | ||
|
d91add2e7b | ||
|
8ead2aa774 | ||
|
1b2128e3cc | ||
|
6d72524896 | ||
|
1ec11d5963 | ||
|
ad6f285b59 | ||
|
d4842dd273 | ||
|
78f83cb478 | ||
|
e67a8f9c05 | ||
|
c8a871de7c | ||
|
64c0951df3 | ||
|
c185414a3c | ||
|
f9695741f1 | ||
|
b7c4b11260 | ||
|
ad110f490c | ||
|
81f3613393 | ||
|
a7fe79c046 | ||
|
ed6306747a | ||
|
64569ab44b | ||
|
9eb89bb46d | ||
|
c4da1ce32d | ||
|
2d1d6f5ce8 | ||
|
add97c9b38 | ||
|
768ba4f4dc | ||
|
18c32d872c | ||
|
1fd40ab6ab | ||
|
9d258f57ce | ||
|
45ccbaf4c9 | ||
|
6ef358b172 | ||
|
838c1af448 | ||
|
8de7261c9a | ||
|
67b1b79fe3 | ||
|
31477f4d2b | ||
|
f200372d74 | ||
|
f955b68519 | ||
|
9269b63943 | ||
|
8f96653273 | ||
|
7dffc08eba | ||
|
126b0ce7e7 | ||
|
0b71f7f297 | ||
|
e53439d586 | ||
|
c86e508817 | ||
|
6426b85c1e | ||
|
cc7d0d752f | ||
|
b89212a0c9 | ||
|
d4c69d8e5d | ||
|
3d6da1e548 | ||
|
7e46fe8148 | ||
|
3756a1901d | ||
|
9c8adf75ec | ||
|
f461eaa432 | ||
|
a1fbc140ee | ||
|
ea27870ce3 | ||
|
48943b4d78 | ||
|
fd1afc2cbe | ||
|
6905029455 | ||
|
3741201b87 | ||
|
e89fb33981 | ||
|
2ef77c737a | ||
|
0f31fa3128 | ||
|
1da5a5f417 | ||
|
5ebf142e3e | ||
|
94d7d2b029 | ||
|
e39d1a0530 | ||
|
4c5f3859d6 | ||
|
16866d46bf | ||
|
4f4764dfcd | ||
|
bdceea4c91 | ||
|
32fa6866e4 | ||
|
b4faef797c | ||
|
08732cab62 | ||
|
81d5f639ae | ||
|
25b83d4b86 | ||
|
155e59e571 | ||
|
8fbd3f2fce | ||
|
a500f00a49 | ||
|
63d325c208 | ||
|
2149c0a9d1 | ||
|
430f8458cb | ||
|
6842f7aa8b | ||
|
bdb7cb4cbf | ||
|
ad207786e2 | ||
|
ace8c37c25 | ||
|
f15e61dbd9 | ||
|
4c82408b51 | ||
|
8146dcef16 | ||
|
2e90addbc5 | ||
|
427201a634 | ||
|
0b55ac141c | ||
|
aecfa268ae | ||
|
fdfc020efc | ||
|
62aa80a104 | ||
|
cf9d8035bd | ||
|
d0c9f1ca53 | ||
|
2ecc7424d9 | ||
|
c04b97c689 | ||
|
7600a86dfc | ||
|
8924eaf251 | ||
|
82e9504285 | ||
|
c4e10df754 | ||
|
ce60e96008 | ||
|
54d002d718 | ||
|
dc2358bbaa | ||
|
930b59cb4f | ||
|
ec363a5ad4 | ||
|
c0de4ae3ee | ||
|
de7e92ccfc | ||
|
fc651f6645 | ||
|
522d81ae1a | ||
|
ef22b39421 | ||
|
02153ffb32 | ||
|
1d14cdf334 | ||
|
39b323dd9c | ||
|
b0b55344ce | ||
|
cc2c4b16bf | ||
|
d9d62384e7 | ||
|
76f34501dc | ||
|
7415bb93b8 | ||
|
7a1c08a7f2 | ||
|
568aadef75 | ||
|
84f9eb5f9f | ||
|
87ac723fcb | ||
|
a6dab47552 | ||
|
79d8a9debb | ||
|
08bac83bcc | ||
|
46c90f03f0 | ||
|
d7722f7587 | ||
|
a42bcb3393 | ||
|
192dba04a5 | ||
|
0cc3240956 | ||
|
667580546b | ||
|
9fd662b7f7 | ||
|
a56cbbc02f | ||
|
dc30465afb | ||
|
f1caab2d00 | ||
|
1d186b1950 | ||
|
9cf5908cc1 | ||
|
f1b6c3764f | ||
|
4e6c860c69 | ||
|
eda9ed257e | ||
|
38cf43176e | ||
|
f5c7943f2f | ||
|
3c59f7f350 | ||
|
84cc7bcd6c | ||
|
159c27ac67 | ||
|
de5a432745 | ||
|
387780aa94 | ||
|
3887ce800b | ||
|
1a06b3e1f5 | ||
|
5f0dd31334 | ||
|
7e14c58931 | ||
|
627e17b3ae | ||
|
39b7a4a111 | ||
|
e7c512999e | ||
|
c19016e6e6 | ||
|
20477ce2b0 | ||
|
e04b2220be | ||
|
edf6a37fe5 | ||
|
f5749e326a | ||
|
75e0a68b68 | ||
|
71b8e3dbce | ||
|
4dc56033b1 | ||
|
ed37b99756 | ||
|
6fa41a609b | ||
|
e46f10292c | ||
|
acb22cdf36 | ||
|
c9da8477c8 | ||
|
5e4b478b74 | ||
|
765be2d99d | ||
|
719a18c218 | ||
|
1b05b7cf2c | ||
|
16d3bbb67a | ||
|
872a3fe48d | ||
|
c7414e00f9 | ||
|
ad1dd55b8b | ||
|
497761a0e5 | ||
|
483fb458dd | ||
|
17cf602a65 | ||
|
23f6f5dfd4 | ||
|
b9b76579ac | ||
|
761965696b | ||
|
ace2500885 | ||
|
4eff7d8ea5 | ||
|
c4512ae111 | ||
|
78c349c09a | ||
|
09df440613 | ||
|
a8fc0e540a | ||
|
46ce46b5a0 | ||
|
dc88115d43 | ||
|
955657e172 | ||
|
f1ba64aa66 | ||
|
d74197aeb4 | ||
|
97567d06d4 | ||
|
3986df8e8a | ||
|
3fcd84b592 | ||
|
29e39b558b | ||
|
9458c8b04f | ||
|
3b95c5d859 | ||
|
de8f315211 | ||
|
9960d58e1b | ||
|
0057404562 | ||
|
47ca1b3011 | ||
|
716cd090c4 | ||
|
e870bb3ade | ||
|
98c9e98082 | ||
|
a814f459ab | ||
|
dcc3509a33 | ||
|
9dbe45a730 | ||
|
66817a40db | ||
|
20bd2ca71c | ||
|
004a8b71a2 | ||
|
7875bcc067 | ||
|
9c702b27b2 | ||
|
db8a4bd26d | ||
|
f0fce3086e | ||
|
a9e7db6fc0 | ||
|
2bd681d58f | ||
|
51fef3ce60 | ||
|
df9e7bf6ee | ||
|
04479bb70a | ||
|
cdc90411e5 | ||
|
dcb05a3093 | ||
|
b055cda64d | ||
|
f68602280e | ||
|
f9483afe95 | ||
|
d742534f6a | ||
|
99eb8eb8ed | ||
|
1dea024880 | ||
|
699e03c1a9 | ||
|
f6372249b4 | ||
|
2b7e1b465f | ||
|
0f42fcd688 | ||
|
b7b294f024 | ||
|
2e02f8bea8 | ||
|
8203158c63 | ||
|
ada04ed4fc | ||
|
cc9cc70125 | ||
|
795d9e4413 | ||
|
67f2e4671a | ||
|
cbe3acde74 | ||
|
de480b5771 | ||
|
07b93c5cec | ||
|
77431b4719 | ||
|
50610945be | ||
|
57f54440d6 | ||
|
9711e73a06 | ||
|
214f837041 | ||
|
65ddddb6de | ||
|
a55b26164a | ||
|
6cd448b8a5 | ||
|
c48c9ae628 | ||
|
7003ad608a | ||
|
104edca6f1 | ||
|
75345d91c0 | ||
|
abc2ffca57 | ||
|
a3fb7c9f00 | ||
|
7f6715643d | ||
|
5ed164de24 | ||
|
28c2f1874e | ||
|
596378208e | ||
|
c54eafc128 | ||
|
757942aefc | ||
|
1d57629036 | ||
|
8061066e27 | ||
|
c993b1bbe3 | ||
|
2cbf33ac14 | ||
|
943d0ddb69 |
3
.envrc
Normal file
3
.envrc
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Learn more at https://direnv.net
|
||||||
|
# We instruct direnv to use our Nix flake for a consistent development environment.
|
||||||
|
use flake
|
@@ -92,7 +92,7 @@ jobs:
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
- name: Install openapi-diff
|
- name: Install openapi-diff
|
||||||
run: go install github.com/tufin/oasdiff@latest
|
run: go install github.com/oasdiff/oasdiff@latest
|
||||||
- name: Running OpenAPI Spec diff action
|
- name: Running OpenAPI Spec diff action
|
||||||
run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR
|
run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR
|
||||||
- name: cleanup
|
- name: cleanup
|
||||||
|
27
.github/workflows/release-k8-operator-helm.yml
vendored
Normal file
27
.github/workflows/release-k8-operator-helm.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
name: Release K8 Operator Helm Chart
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release-helm:
|
||||||
|
name: Release Helm Chart
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Install Helm
|
||||||
|
uses: azure/setup-helm@v3
|
||||||
|
with:
|
||||||
|
version: v3.10.0
|
||||||
|
|
||||||
|
- name: Install python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
|
||||||
|
- name: Install Cloudsmith CLI
|
||||||
|
run: pip install --upgrade cloudsmith-cli
|
||||||
|
|
||||||
|
- name: Build and push helm package to CloudSmith
|
||||||
|
run: cd helm-charts && sh upload-k8s-operator-cloudsmith.sh
|
||||||
|
env:
|
||||||
|
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
@@ -126,7 +126,22 @@ jobs:
|
|||||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
- uses: actions/setup-python@v4
|
- uses: actions/setup-python@v4
|
||||||
- run: pip install --upgrade cloudsmith-cli
|
- run: pip install --upgrade cloudsmith-cli
|
||||||
|
- uses: ruby/setup-ruby@354a1ad156761f5ee2b7b13fa8e09943a5e8d252
|
||||||
|
with:
|
||||||
|
ruby-version: "3.3" # Not needed with a .ruby-version, .tool-versions or mise.toml
|
||||||
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
||||||
|
- name: Install deb-s3
|
||||||
|
run: gem install deb-s3
|
||||||
|
- name: Configure GPG Key
|
||||||
|
run: echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import
|
||||||
|
env:
|
||||||
|
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||||
|
GPG_SIGNING_KEY_PASSPHRASE: ${{ secrets.GPG_SIGNING_KEY_PASSPHRASE }}
|
||||||
- name: Publish to CloudSmith
|
- name: Publish to CloudSmith
|
||||||
run: sh cli/upload_to_cloudsmith.sh
|
run: sh cli/upload_to_cloudsmith.sh
|
||||||
env:
|
env:
|
||||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||||||
|
INFISICAL_CLI_S3_BUCKET: ${{ secrets.INFISICAL_CLI_S3_BUCKET }}
|
||||||
|
INFISICAL_CLI_REPO_SIGNING_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_SIGNING_KEY_ID }}
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.INFISICAL_CLI_REPO_AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.INFISICAL_CLI_REPO_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
@@ -1,17 +1,87 @@
|
|||||||
name: Release image + Helm chart K8s Operator
|
name: Release K8 Operator Docker Image
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- "infisical-k8-operator/v*.*.*"
|
- "infisical-k8-operator/v*.*.*"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release-image:
|
||||||
|
name: Generate Helm Chart PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
pr_number: ${{ steps.create-pr.outputs.pull-request-number }}
|
||||||
steps:
|
steps:
|
||||||
- name: Extract version from tag
|
- name: Extract version from tag
|
||||||
id: extract_version
|
id: extract_version
|
||||||
run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical-k8-operator/}"
|
run: echo "::set-output name=version::${GITHUB_REF_NAME#infisical-k8-operator/}"
|
||||||
- uses: actions/checkout@v2
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Dependency for helm generation
|
||||||
|
- name: Install Helm
|
||||||
|
uses: azure/setup-helm@v3
|
||||||
|
with:
|
||||||
|
version: v3.10.0
|
||||||
|
|
||||||
|
# Dependency for helm generation
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: 1.21
|
||||||
|
|
||||||
|
# Install binaries for helm generation
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: k8-operator
|
||||||
|
run: |
|
||||||
|
make helmify
|
||||||
|
make kustomize
|
||||||
|
make controller-gen
|
||||||
|
|
||||||
|
- name: Generate Helm Chart
|
||||||
|
working-directory: k8-operator
|
||||||
|
run: make helm
|
||||||
|
|
||||||
|
- name: Update Helm Chart Version
|
||||||
|
run: ./k8-operator/scripts/update-version.sh ${{ steps.extract_version.outputs.version }}
|
||||||
|
|
||||||
|
- name: Debug - Check file changes
|
||||||
|
run: |
|
||||||
|
echo "Current git status:"
|
||||||
|
git status
|
||||||
|
echo ""
|
||||||
|
echo "Modified files:"
|
||||||
|
git diff --name-only
|
||||||
|
|
||||||
|
# If there is no diff, exit with error. Version should always be changed, so if there is no diff, something is wrong and we should exit.
|
||||||
|
if [ -z "$(git diff --name-only)" ]; then
|
||||||
|
echo "No helm changes or version changes. Invalid release detected, Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Create Helm Chart PR
|
||||||
|
id: create-pr
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
|
||||||
|
committer: GitHub <noreply@github.com>
|
||||||
|
author: ${{ github.actor }} <${{ github.actor }}@users.noreply.github.com>
|
||||||
|
branch: helm-update-${{ steps.extract_version.outputs.version }}
|
||||||
|
delete-branch: true
|
||||||
|
title: "Update Helm chart to version ${{ steps.extract_version.outputs.version }}"
|
||||||
|
body: |
|
||||||
|
This PR updates the Helm chart to version `${{ steps.extract_version.outputs.version }}`.
|
||||||
|
Additionally the helm chart has been updated to match the latest operator code changes.
|
||||||
|
|
||||||
|
Associated Release Workflow: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
|
|
||||||
|
Once you have approved this PR, you can trigger the helm release workflow manually.
|
||||||
|
base: main
|
||||||
|
|
||||||
- name: 🔧 Set up QEMU
|
- name: 🔧 Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v1
|
uses: docker/setup-qemu-action@v1
|
||||||
@@ -35,18 +105,3 @@ jobs:
|
|||||||
tags: |
|
tags: |
|
||||||
infisical/kubernetes-operator:latest
|
infisical/kubernetes-operator:latest
|
||||||
infisical/kubernetes-operator:${{ steps.extract_version.outputs.version }}
|
infisical/kubernetes-operator:${{ steps.extract_version.outputs.version }}
|
||||||
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Install Helm
|
|
||||||
uses: azure/setup-helm@v3
|
|
||||||
with:
|
|
||||||
version: v3.10.0
|
|
||||||
- name: Install python
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
- name: Install Cloudsmith CLI
|
|
||||||
run: pip install --upgrade cloudsmith-cli
|
|
||||||
- name: Build and push helm package to Cloudsmith
|
|
||||||
run: cd helm-charts && sh upload-k8s-operator-cloudsmith.sh
|
|
||||||
env:
|
|
||||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
|
||||||
|
6
.github/workflows/run-backend-tests.yml
vendored
6
.github/workflows/run-backend-tests.yml
vendored
@@ -34,7 +34,10 @@ jobs:
|
|||||||
working-directory: backend
|
working-directory: backend
|
||||||
- name: Start postgres and redis
|
- name: Start postgres and redis
|
||||||
run: touch .env && docker compose -f docker-compose.dev.yml up -d db redis
|
run: touch .env && docker compose -f docker-compose.dev.yml up -d db redis
|
||||||
- name: Start integration test
|
- name: Run unit test
|
||||||
|
run: npm run test:unit
|
||||||
|
working-directory: backend
|
||||||
|
- name: Run integration test
|
||||||
run: npm run test:e2e
|
run: npm run test:e2e
|
||||||
working-directory: backend
|
working-directory: backend
|
||||||
env:
|
env:
|
||||||
@@ -45,3 +48,4 @@ jobs:
|
|||||||
- name: cleanup
|
- name: cleanup
|
||||||
run: |
|
run: |
|
||||||
docker compose -f "docker-compose.dev.yml" down
|
docker compose -f "docker-compose.dev.yml" down
|
||||||
|
|
||||||
|
@@ -162,6 +162,24 @@ scoop:
|
|||||||
description: "The official Infisical CLI"
|
description: "The official Infisical CLI"
|
||||||
license: MIT
|
license: MIT
|
||||||
|
|
||||||
|
winget:
|
||||||
|
- name: infisical
|
||||||
|
publisher: infisical
|
||||||
|
license: MIT
|
||||||
|
homepage: https://infisical.com
|
||||||
|
short_description: "The official Infisical CLI"
|
||||||
|
repository:
|
||||||
|
owner: infisical
|
||||||
|
name: winget-pkgs
|
||||||
|
branch: "infisical-{{.Version}}"
|
||||||
|
pull_request:
|
||||||
|
enabled: true
|
||||||
|
draft: false
|
||||||
|
base:
|
||||||
|
owner: microsoft
|
||||||
|
name: winget-pkgs
|
||||||
|
branch: master
|
||||||
|
|
||||||
aurs:
|
aurs:
|
||||||
- name: infisical-bin
|
- name: infisical-bin
|
||||||
homepage: "https://infisical.com"
|
homepage: "https://infisical.com"
|
||||||
|
@@ -8,3 +8,9 @@ frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/S
|
|||||||
docs/mint.json:generic-api-key:651
|
docs/mint.json:generic-api-key:651
|
||||||
backend/src/ee/services/hsm/hsm-service.ts:generic-api-key:134
|
backend/src/ee/services/hsm/hsm-service.ts:generic-api-key:134
|
||||||
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:104
|
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:104
|
||||||
|
docs/cli/commands/bootstrap.mdx:jwt:86
|
||||||
|
docs/documentation/platform/audit-log-streams/audit-log-streams.mdx:generic-api-key:102
|
||||||
|
docs/self-hosting/guides/automated-bootstrapping.mdx:jwt:74
|
||||||
|
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretListView/SecretDetailSidebar.tsx:generic-api-key:72
|
||||||
|
k8-operator/config/samples/crd/pushsecret/source-secret-with-templating.yaml:private-key:11
|
||||||
|
k8-operator/config/samples/crd/pushsecret/push-secret-with-template.yaml:private-key:52
|
||||||
|
85
backend/Dockerfile.dev.fips
Normal file
85
backend/Dockerfile.dev.fips
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
FROM node:20-slim
|
||||||
|
|
||||||
|
# ? Setup a test SoftHSM module. In production a real HSM is used.
|
||||||
|
|
||||||
|
ARG SOFTHSM2_VERSION=2.5.0
|
||||||
|
|
||||||
|
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
|
||||||
|
SOFTHSM2_SOURCES=/tmp/softhsm2
|
||||||
|
|
||||||
|
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
autoconf \
|
||||||
|
automake \
|
||||||
|
git \
|
||||||
|
libtool \
|
||||||
|
libssl-dev \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
openssh-client \
|
||||||
|
curl \
|
||||||
|
pkg-config \
|
||||||
|
perl \
|
||||||
|
wget
|
||||||
|
|
||||||
|
# Install dependencies for TDS driver (required for SAP ASE dynamic secrets)
|
||||||
|
RUN apt-get install -y \
|
||||||
|
unixodbc \
|
||||||
|
unixodbc-dev \
|
||||||
|
freetds-dev \
|
||||||
|
freetds-bin \
|
||||||
|
tdsodbc
|
||||||
|
|
||||||
|
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
||||||
|
|
||||||
|
# Build and install SoftHSM2
|
||||||
|
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
|
||||||
|
WORKDIR ${SOFTHSM2_SOURCES}
|
||||||
|
|
||||||
|
RUN git checkout ${SOFTHSM2_VERSION} -b ${SOFTHSM2_VERSION} \
|
||||||
|
&& sh autogen.sh \
|
||||||
|
&& ./configure --prefix=/usr/local --disable-gost \
|
||||||
|
&& make \
|
||||||
|
&& make install
|
||||||
|
|
||||||
|
WORKDIR /root
|
||||||
|
RUN rm -fr ${SOFTHSM2_SOURCES}
|
||||||
|
|
||||||
|
# Install pkcs11-tool
|
||||||
|
RUN apt-get install -y opensc
|
||||||
|
|
||||||
|
RUN mkdir -p /etc/softhsm2/tokens && \
|
||||||
|
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
|
||||||
|
|
||||||
|
WORKDIR /openssl-build
|
||||||
|
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||||
|
&& tar -xf openssl-3.1.2.tar.gz \
|
||||||
|
&& cd openssl-3.1.2 \
|
||||||
|
&& ./Configure enable-fips \
|
||||||
|
&& make \
|
||||||
|
&& make install_fips
|
||||||
|
|
||||||
|
# ? App setup
|
||||||
|
|
||||||
|
# Install Infisical CLI
|
||||||
|
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
||||||
|
apt-get update && \
|
||||||
|
apt-get install -y infisical=0.8.1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY package.json package.json
|
||||||
|
COPY package-lock.json package-lock.json
|
||||||
|
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
ENV HOST=0.0.0.0
|
||||||
|
ENV OPENSSL_CONF=/app/nodejs.cnf
|
||||||
|
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
|
||||||
|
ENV NODE_OPTIONS=--force-fips
|
||||||
|
|
||||||
|
CMD ["npm", "run", "dev:docker"]
|
@@ -11,6 +11,7 @@ export const mockQueue = (): TQueueServiceFactory => {
|
|||||||
job[name] = jobData;
|
job[name] = jobData;
|
||||||
},
|
},
|
||||||
queuePg: async () => {},
|
queuePg: async () => {},
|
||||||
|
schedulePg: async () => {},
|
||||||
initialize: async () => {},
|
initialize: async () => {},
|
||||||
shutdown: async () => undefined,
|
shutdown: async () => undefined,
|
||||||
stopRepeatableJob: async () => true,
|
stopRepeatableJob: async () => true,
|
||||||
|
@@ -120,4 +120,3 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
16
backend/nodejs.cnf
Normal file
16
backend/nodejs.cnf
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
nodejs_conf = nodejs_init
|
||||||
|
|
||||||
|
.include /usr/local/ssl/fipsmodule.cnf
|
||||||
|
|
||||||
|
[nodejs_init]
|
||||||
|
providers = provider_sect
|
||||||
|
|
||||||
|
[provider_sect]
|
||||||
|
default = default_sect
|
||||||
|
fips = fips_sect
|
||||||
|
|
||||||
|
[default_sect]
|
||||||
|
activate = 1
|
||||||
|
|
||||||
|
[algorithm_sect]
|
||||||
|
default_properties = fips=yes
|
166
backend/package-lock.json
generated
166
backend/package-lock.json
generated
@@ -31,7 +31,7 @@
|
|||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
"@infisical/quic": "^1.0.8",
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
@@ -6747,32 +6747,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml": {
|
"node_modules/@node-saml/node-saml": {
|
||||||
"version": "4.0.5",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@node-saml/node-saml/-/node-saml-5.0.1.tgz",
|
||||||
"integrity": "sha512-J5DglElbY1tjOuaR1NPtjOXkXY5bpUhDoKVoeucYN98A3w4fwgjIOPqIGcb6cQsqFq2zZ6vTCeKn5C/hvefSaw==",
|
"integrity": "sha512-YQzFPEC+CnsfO9AFYnwfYZKIzOLx3kITaC1HrjHVLTo6hxcQhc+LgHODOMvW4VCV95Gwrz1MshRUWCPzkDqmnA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/debug": "^4.1.7",
|
"@types/debug": "^4.1.12",
|
||||||
"@types/passport": "^1.0.11",
|
"@types/qs": "^6.9.11",
|
||||||
"@types/xml-crypto": "^1.4.2",
|
"@types/xml-encryption": "^1.2.4",
|
||||||
"@types/xml-encryption": "^1.2.1",
|
"@types/xml2js": "^0.4.14",
|
||||||
"@types/xml2js": "^0.4.11",
|
"@xmldom/is-dom-node": "^1.0.1",
|
||||||
"@xmldom/xmldom": "^0.8.6",
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
"debug": "^4.3.4",
|
"debug": "^4.3.4",
|
||||||
"xml-crypto": "^3.0.1",
|
"xml-crypto": "^6.0.1",
|
||||||
"xml-encryption": "^3.0.2",
|
"xml-encryption": "^3.0.2",
|
||||||
"xml2js": "^0.5.0",
|
"xml2js": "^0.6.2",
|
||||||
"xmlbuilder": "^15.1.1"
|
"xmlbuilder": "^15.1.1",
|
||||||
|
"xpath": "^0.0.34"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml/node_modules/debug": {
|
"node_modules/@node-saml/node-saml/node_modules/debug": {
|
||||||
"version": "4.3.4",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "2.1.2"
|
"ms": "^2.1.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0"
|
"node": ">=6.0"
|
||||||
@@ -6783,25 +6786,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/node-saml/node_modules/ms": {
|
"node_modules/@node-saml/node-saml/node_modules/xml2js": {
|
||||||
"version": "2.1.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
"integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"sax": ">=0.6.0",
|
||||||
|
"xmlbuilder": "~11.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@node-saml/node-saml/node_modules/xml2js/node_modules/xmlbuilder": {
|
||||||
|
"version": "11.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
|
||||||
|
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@node-saml/passport-saml": {
|
"node_modules/@node-saml/passport-saml": {
|
||||||
"version": "4.0.4",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/@node-saml/passport-saml/-/passport-saml-5.0.1.tgz",
|
||||||
"integrity": "sha512-xFw3gw0yo+K1mzlkW15NeBF7cVpRHN/4vpjmBKzov5YFImCWh/G0LcTZ8krH3yk2/eRPc3Or8LRPudVJBjmYaw==",
|
"integrity": "sha512-fMztg3zfSnjLEgxvpl6HaDMNeh0xeQX4QHiF9e2Lsie2dc4qFE37XYbQZhVmn8XJ2awPpSWLQ736UskYgGU8lQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@node-saml/node-saml": "^4.0.4",
|
"@node-saml/node-saml": "^5.0.1",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.21",
|
||||||
"@types/passport": "^1.0.11",
|
"@types/passport": "^1.0.16",
|
||||||
"@types/passport-strategy": "^0.2.35",
|
"@types/passport-strategy": "^0.2.38",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.7.0",
|
||||||
"passport-strategy": "^1.0.0"
|
"passport-strategy": "^1.0.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
@@ -9606,6 +9627,7 @@
|
|||||||
"version": "4.1.12",
|
"version": "4.1.12",
|
||||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
|
||||||
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
"integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/ms": "*"
|
"@types/ms": "*"
|
||||||
}
|
}
|
||||||
@@ -9725,9 +9747,10 @@
|
|||||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w=="
|
||||||
},
|
},
|
||||||
"node_modules/@types/ms": {
|
"node_modules/@types/ms": {
|
||||||
"version": "0.7.34",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz",
|
"resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
|
||||||
"integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="
|
"integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.9.5",
|
"version": "20.9.5",
|
||||||
@@ -9907,9 +9930,10 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/qs": {
|
"node_modules/@types/qs": {
|
||||||
"version": "6.9.10",
|
"version": "6.9.18",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||||
"integrity": "sha512-3Gnx08Ns1sEoCrWssEgTSJs/rsT2vhGP+Ja9cnnk9k4ALxinORlQneLXFeFKOTJMOeZUFD1s7w+w2AphTpvzZw=="
|
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/range-parser": {
|
"node_modules/@types/range-parser": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
@@ -10058,19 +10082,11 @@
|
|||||||
"@types/webidl-conversions": "*"
|
"@types/webidl-conversions": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@types/xml-crypto": {
|
|
||||||
"version": "1.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml-crypto/-/xml-crypto-1.4.6.tgz",
|
|
||||||
"integrity": "sha512-A6jEW2FxLZo1CXsRWnZHUX2wzR3uDju2Bozt6rDbSmU/W8gkilaVbwFEVN0/NhnUdMVzwYobWtM6bU1QJJFb7Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"@types/node": "*",
|
|
||||||
"xpath": "0.0.27"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/xml-encryption": {
|
"node_modules/@types/xml-encryption": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@types/xml-encryption/-/xml-encryption-1.2.4.tgz",
|
||||||
"integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==",
|
"integrity": "sha512-I69K/WW1Dv7j6O3jh13z0X8sLWJRXbu5xnHDl9yHzUNDUBtUoBY058eb5s+x/WG6yZC1h8aKdI2EoyEPjyEh+Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -10079,6 +10095,7 @@
|
|||||||
"version": "0.4.14",
|
"version": "0.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz",
|
||||||
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
"integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
@@ -10522,10 +10539,20 @@
|
|||||||
"url": "https://opencollective.com/vitest"
|
"url": "https://opencollective.com/vitest"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@xmldom/is-dom-node": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/is-dom-node/-/is-dom-node-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-CJDxIgE5I0FH+ttq/Fxy6nRpxP70+e2O048EPe85J2use3XKdatVM7dDVvFNjQudd9B49NPoZ+8PG49zj4Er8Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@xmldom/xmldom": {
|
"node_modules/@xmldom/xmldom": {
|
||||||
"version": "0.8.10",
|
"version": "0.8.10",
|
||||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
|
||||||
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
"integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
}
|
}
|
||||||
@@ -18222,9 +18249,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/passport": {
|
"node_modules/passport": {
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
|
||||||
"integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
|
"integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"passport-strategy": "1.x.x",
|
"passport-strategy": "1.x.x",
|
||||||
"pause": "0.0.1",
|
"pause": "0.0.1",
|
||||||
@@ -23692,42 +23720,44 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-crypto": {
|
"node_modules/xml-crypto": {
|
||||||
"version": "3.2.0",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-3.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-6.0.1.tgz",
|
||||||
"integrity": "sha512-qVurBUOQrmvlgmZqIVBqmb06TD2a/PpEUfFPgD7BuBfjmoH4zgkqaWSIJrnymlCvM2GGt9x+XtJFA+ttoAufqg==",
|
"integrity": "sha512-v05aU7NS03z4jlZ0iZGRFeZsuKO1UfEbbYiaeRMiATBFs6Jq9+wqKquEMTn4UTrYZ9iGD8yz3KT4L9o2iF682w==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.8.8",
|
"@xmldom/is-dom-node": "^1.0.1",
|
||||||
"xpath": "0.0.32"
|
"@xmldom/xmldom": "^0.8.10",
|
||||||
|
"xpath": "^0.0.33"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0.0"
|
"node": ">=16"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-crypto/node_modules/xpath": {
|
"node_modules/xml-crypto/node_modules/xpath": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.33",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.33.tgz",
|
||||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
"integrity": "sha512-NNXnzrkDrAzalLhIUc01jO2mOzXGXh1JwPgkihcLLzw98c0WgYDmmjSh1Kl3wzaxSVWMuA+fe0WTWOBDWCBmNA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-encryption": {
|
"node_modules/xml-encryption": {
|
||||||
"version": "3.0.2",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xml-encryption/-/xml-encryption-3.1.0.tgz",
|
||||||
"integrity": "sha512-VxYXPvsWB01/aqVLd6ZMPWZ+qaj0aIdF+cStrVJMcFj3iymwZeI0ABzB3VqMYv48DkSpRhnrXqTUkR34j+UDyg==",
|
"integrity": "sha512-PV7qnYpoAMXbf1kvQkqMScLeQpjCMixddAKq9PtqVrho8HnYbBOWNfG0kA4R7zxQDo7w9kiYAyzS/ullAyO55Q==",
|
||||||
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xmldom/xmldom": "^0.8.5",
|
"@xmldom/xmldom": "^0.8.5",
|
||||||
"escape-html": "^1.0.3",
|
"escape-html": "^1.0.3",
|
||||||
"xpath": "0.0.32"
|
"xpath": "0.0.32"
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/xml-encryption/node_modules/xpath": {
|
"node_modules/xml-encryption/node_modules/xpath": {
|
||||||
"version": "0.0.32",
|
"version": "0.0.32",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz",
|
||||||
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
"integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
@@ -23764,6 +23794,7 @@
|
|||||||
"version": "15.1.1",
|
"version": "15.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||||
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
"integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
@@ -23774,9 +23805,10 @@
|
|||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
},
|
},
|
||||||
"node_modules/xpath": {
|
"node_modules/xpath": {
|
||||||
"version": "0.0.27",
|
"version": "0.0.34",
|
||||||
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.27.tgz",
|
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz",
|
||||||
"integrity": "sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==",
|
"integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6.0"
|
"node": ">=0.6.0"
|
||||||
}
|
}
|
||||||
|
@@ -40,6 +40,7 @@
|
|||||||
"type:check": "tsc --noEmit",
|
"type:check": "tsc --noEmit",
|
||||||
"lint:fix": "eslint --fix --ext js,ts ./src",
|
"lint:fix": "eslint --fix --ext js,ts ./src",
|
||||||
"lint": "eslint 'src/**/*.ts'",
|
"lint": "eslint 'src/**/*.ts'",
|
||||||
|
"test:unit": "vitest run -c vitest.unit.config.ts",
|
||||||
"test:e2e": "vitest run -c vitest.e2e.config.ts --bail=1",
|
"test:e2e": "vitest run -c vitest.e2e.config.ts --bail=1",
|
||||||
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
||||||
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
|
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
"migrate:org": "tsx ./scripts/migrate-organization.ts",
|
"migrate:org": "tsx ./scripts/migrate-organization.ts",
|
||||||
"seed:new": "tsx ./scripts/create-seed-file.ts",
|
"seed:new": "tsx ./scripts/create-seed-file.ts",
|
||||||
"seed": "knex --knexfile ./dist/db/knexfile.ts --client pg seed:run",
|
"seed": "knex --knexfile ./dist/db/knexfile.ts --client pg seed:run",
|
||||||
|
"seed-dev": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run",
|
||||||
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest"
|
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
@@ -146,7 +148,7 @@
|
|||||||
"@fastify/swagger-ui": "^2.1.0",
|
"@fastify/swagger-ui": "^2.1.0",
|
||||||
"@google-cloud/kms": "^4.5.0",
|
"@google-cloud/kms": "^4.5.0",
|
||||||
"@infisical/quic": "^1.0.8",
|
"@infisical/quic": "^1.0.8",
|
||||||
"@node-saml/passport-saml": "^4.0.4",
|
"@node-saml/passport-saml": "^5.0.1",
|
||||||
"@octokit/auth-app": "^7.1.1",
|
"@octokit/auth-app": "^7.1.1",
|
||||||
"@octokit/plugin-retry": "^5.0.5",
|
"@octokit/plugin-retry": "^5.0.5",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^20.0.2",
|
||||||
|
@@ -1,7 +0,0 @@
|
|||||||
import "@fastify/request-context";
|
|
||||||
|
|
||||||
declare module "@fastify/request-context" {
|
|
||||||
interface RequestContextData {
|
|
||||||
reqId: string;
|
|
||||||
}
|
|
||||||
}
|
|
9
backend/src/@types/fastify.d.ts
vendored
9
backend/src/@types/fastify.d.ts
vendored
@@ -33,6 +33,7 @@ import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
|
|||||||
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
|
||||||
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
|
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
|
||||||
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
|
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
|
||||||
|
import { TSecretRotationV2ServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-service";
|
||||||
import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service";
|
import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service";
|
||||||
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
|
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
|
||||||
import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
|
import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
|
||||||
@@ -100,6 +101,13 @@ import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integ
|
|||||||
declare module "@fastify/request-context" {
|
declare module "@fastify/request-context" {
|
||||||
interface RequestContextData {
|
interface RequestContextData {
|
||||||
reqId: string;
|
reqId: string;
|
||||||
|
identityAuthInfo?: {
|
||||||
|
identityId: string;
|
||||||
|
oidc?: {
|
||||||
|
claims: Record<string, string>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,6 +238,7 @@ declare module "fastify" {
|
|||||||
kmip: TKmipServiceFactory;
|
kmip: TKmipServiceFactory;
|
||||||
kmipOperation: TKmipOperationServiceFactory;
|
kmipOperation: TKmipOperationServiceFactory;
|
||||||
gateway: TGatewayServiceFactory;
|
gateway: TGatewayServiceFactory;
|
||||||
|
secretRotationV2: TSecretRotationV2ServiceFactory;
|
||||||
};
|
};
|
||||||
// this is exclusive use for middlewares in which we need to inject data
|
// this is exclusive use for middlewares in which we need to inject data
|
||||||
// everywhere else access using service layer
|
// everywhere else access using service layer
|
||||||
|
52
backend/src/@types/knex.d.ts
vendored
52
backend/src/@types/knex.d.ts
vendored
@@ -17,6 +17,9 @@ import {
|
|||||||
TApiKeys,
|
TApiKeys,
|
||||||
TApiKeysInsert,
|
TApiKeysInsert,
|
||||||
TApiKeysUpdate,
|
TApiKeysUpdate,
|
||||||
|
TAppConnections,
|
||||||
|
TAppConnectionsInsert,
|
||||||
|
TAppConnectionsUpdate,
|
||||||
TAuditLogs,
|
TAuditLogs,
|
||||||
TAuditLogsInsert,
|
TAuditLogsInsert,
|
||||||
TAuditLogStreams,
|
TAuditLogStreams,
|
||||||
@@ -65,6 +68,9 @@ import {
|
|||||||
TDynamicSecrets,
|
TDynamicSecrets,
|
||||||
TDynamicSecretsInsert,
|
TDynamicSecretsInsert,
|
||||||
TDynamicSecretsUpdate,
|
TDynamicSecretsUpdate,
|
||||||
|
TExternalGroupOrgRoleMappings,
|
||||||
|
TExternalGroupOrgRoleMappingsInsert,
|
||||||
|
TExternalGroupOrgRoleMappingsUpdate,
|
||||||
TExternalKms,
|
TExternalKms,
|
||||||
TExternalKmsInsert,
|
TExternalKmsInsert,
|
||||||
TExternalKmsUpdate,
|
TExternalKmsUpdate,
|
||||||
@@ -299,6 +305,12 @@ import {
|
|||||||
TSecretRotations,
|
TSecretRotations,
|
||||||
TSecretRotationsInsert,
|
TSecretRotationsInsert,
|
||||||
TSecretRotationsUpdate,
|
TSecretRotationsUpdate,
|
||||||
|
TSecretRotationsV2,
|
||||||
|
TSecretRotationsV2Insert,
|
||||||
|
TSecretRotationsV2Update,
|
||||||
|
TSecretRotationV2SecretMappings,
|
||||||
|
TSecretRotationV2SecretMappingsInsert,
|
||||||
|
TSecretRotationV2SecretMappingsUpdate,
|
||||||
TSecrets,
|
TSecrets,
|
||||||
TSecretScanningGitRisks,
|
TSecretScanningGitRisks,
|
||||||
TSecretScanningGitRisksInsert,
|
TSecretScanningGitRisksInsert,
|
||||||
@@ -320,15 +332,27 @@ import {
|
|||||||
TSecretSnapshotsInsert,
|
TSecretSnapshotsInsert,
|
||||||
TSecretSnapshotsUpdate,
|
TSecretSnapshotsUpdate,
|
||||||
TSecretsUpdate,
|
TSecretsUpdate,
|
||||||
|
TSecretsV2,
|
||||||
|
TSecretsV2Insert,
|
||||||
|
TSecretsV2Update,
|
||||||
|
TSecretSyncs,
|
||||||
|
TSecretSyncsInsert,
|
||||||
|
TSecretSyncsUpdate,
|
||||||
TSecretTagJunction,
|
TSecretTagJunction,
|
||||||
TSecretTagJunctionInsert,
|
TSecretTagJunctionInsert,
|
||||||
TSecretTagJunctionUpdate,
|
TSecretTagJunctionUpdate,
|
||||||
TSecretTags,
|
TSecretTags,
|
||||||
TSecretTagsInsert,
|
TSecretTagsInsert,
|
||||||
TSecretTagsUpdate,
|
TSecretTagsUpdate,
|
||||||
|
TSecretV2TagJunction,
|
||||||
|
TSecretV2TagJunctionInsert,
|
||||||
|
TSecretV2TagJunctionUpdate,
|
||||||
TSecretVersions,
|
TSecretVersions,
|
||||||
TSecretVersionsInsert,
|
TSecretVersionsInsert,
|
||||||
TSecretVersionsUpdate,
|
TSecretVersionsUpdate,
|
||||||
|
TSecretVersionsV2,
|
||||||
|
TSecretVersionsV2Insert,
|
||||||
|
TSecretVersionsV2Update,
|
||||||
TSecretVersionTagJunction,
|
TSecretVersionTagJunction,
|
||||||
TSecretVersionTagJunctionInsert,
|
TSecretVersionTagJunctionInsert,
|
||||||
TSecretVersionTagJunctionUpdate,
|
TSecretVersionTagJunctionUpdate,
|
||||||
@@ -387,24 +411,6 @@ import {
|
|||||||
TWorkflowIntegrationsInsert,
|
TWorkflowIntegrationsInsert,
|
||||||
TWorkflowIntegrationsUpdate
|
TWorkflowIntegrationsUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { TAppConnections, TAppConnectionsInsert, TAppConnectionsUpdate } from "@app/db/schemas/app-connections";
|
|
||||||
import {
|
|
||||||
TExternalGroupOrgRoleMappings,
|
|
||||||
TExternalGroupOrgRoleMappingsInsert,
|
|
||||||
TExternalGroupOrgRoleMappingsUpdate
|
|
||||||
} from "@app/db/schemas/external-group-org-role-mappings";
|
|
||||||
import { TSecretSyncs, TSecretSyncsInsert, TSecretSyncsUpdate } from "@app/db/schemas/secret-syncs";
|
|
||||||
import {
|
|
||||||
TSecretV2TagJunction,
|
|
||||||
TSecretV2TagJunctionInsert,
|
|
||||||
TSecretV2TagJunctionUpdate
|
|
||||||
} from "@app/db/schemas/secret-v2-tag-junction";
|
|
||||||
import {
|
|
||||||
TSecretVersionsV2,
|
|
||||||
TSecretVersionsV2Insert,
|
|
||||||
TSecretVersionsV2Update
|
|
||||||
} from "@app/db/schemas/secret-versions-v2";
|
|
||||||
import { TSecretsV2, TSecretsV2Insert, TSecretsV2Update } from "@app/db/schemas/secrets-v2";
|
|
||||||
|
|
||||||
declare module "knex" {
|
declare module "knex" {
|
||||||
namespace Knex {
|
namespace Knex {
|
||||||
@@ -950,5 +956,15 @@ declare module "knex/types/tables" {
|
|||||||
TOrgGatewayConfigInsert,
|
TOrgGatewayConfigInsert,
|
||||||
TOrgGatewayConfigUpdate
|
TOrgGatewayConfigUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.SecretRotationV2]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretRotationsV2,
|
||||||
|
TSecretRotationsV2Insert,
|
||||||
|
TSecretRotationsV2Update
|
||||||
|
>;
|
||||||
|
[TableName.SecretRotationV2SecretMapping]: KnexOriginal.CompositeTableType<
|
||||||
|
TSecretRotationV2SecretMappings,
|
||||||
|
TSecretRotationV2SecretMappingsInsert,
|
||||||
|
TSecretRotationV2SecretMappingsUpdate
|
||||||
|
>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,7 +16,7 @@ const createAuditLogPartition = async (knex: Knex, startDate: Date, endDate: Dat
|
|||||||
const startDateStr = formatPartitionDate(startDate);
|
const startDateStr = formatPartitionDate(startDate);
|
||||||
const endDateStr = formatPartitionDate(endDate);
|
const endDateStr = formatPartitionDate(endDate);
|
||||||
|
|
||||||
const partitionName = `${TableName.AuditLog}_${startDateStr.replace(/-/g, "")}_${endDateStr.replace(/-/g, "")}`;
|
const partitionName = `${TableName.AuditLog}_${startDateStr.replaceAll("-", "")}_${endDateStr.replaceAll("-", "")}`;
|
||||||
|
|
||||||
await knex.schema.raw(
|
await knex.schema.raw(
|
||||||
`CREATE TABLE ${partitionName} PARTITION OF ${TableName.AuditLog} FOR VALUES FROM ('${startDateStr}') TO ('${endDateStr}')`
|
`CREATE TABLE ${partitionName} PARTITION OF ${TableName.AuditLog} FOR VALUES FROM ('${startDateStr}') TO ('${endDateStr}')`
|
||||||
|
@@ -85,7 +85,7 @@ export async function up(knex: Knex): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
if (await knex.schema.hasTable(TableName.DynamicSecret)) {
|
||||||
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId");
|
const doesGatewayColExist = await knex.schema.hasColumn(TableName.DynamicSecret, "projectGatewayId");
|
||||||
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
|
||||||
// not setting a foreign constraint so that cascade effects are not triggered
|
// not setting a foreign constraint so that cascade effects are not triggered
|
||||||
if (!doesGatewayColExist) {
|
if (!doesGatewayColExist) {
|
||||||
|
@@ -0,0 +1,32 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Organization)) {
|
||||||
|
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
|
||||||
|
TableName.Organization,
|
||||||
|
"allowSecretSharingOutsideOrganization"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!hasSecretShareToAnyoneCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.boolean("allowSecretSharingOutsideOrganization").defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasTable(TableName.Organization)) {
|
||||||
|
const hasSecretShareToAnyoneCol = await knex.schema.hasColumn(
|
||||||
|
TableName.Organization,
|
||||||
|
"allowSecretSharingOutsideOrganization"
|
||||||
|
);
|
||||||
|
if (hasSecretShareToAnyoneCol) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.dropColumn("allowSecretSharingOutsideOrganization");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,31 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem"))) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.boolean("shouldUseNewPrivilegeSystem");
|
||||||
|
t.string("privilegeUpgradeInitiatedByUsername");
|
||||||
|
t.dateTime("privilegeUpgradeInitiatedAt");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex(TableName.Organization).update({
|
||||||
|
shouldUseNewPrivilegeSystem: false
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.boolean("shouldUseNewPrivilegeSystem").defaultTo(true).notNullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Organization, "shouldUseNewPrivilegeSystem")) {
|
||||||
|
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||||
|
t.dropColumn("shouldUseNewPrivilegeSystem");
|
||||||
|
t.dropColumn("privilegeUpgradeInitiatedByUsername");
|
||||||
|
t.dropColumn("privilegeUpgradeInitiatedAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasMappingField = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "claimMetadataMapping");
|
||||||
|
if (!hasMappingField) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.jsonb("claimMetadataMapping");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasMappingField = await knex.schema.hasColumn(TableName.IdentityOidcAuth, "claimMetadataMapping");
|
||||||
|
if (hasMappingField) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||||
|
t.dropColumn("claimMetadataMapping");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas/models";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.specificType("adminIdentityIds", "text[]");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "adminIdentityIds")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("adminIdentityIds");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,23 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||||
|
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||||
|
if (doesParentColumExist && doesNameColumnExist) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.index(["parentId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesParentColumExist = await knex.schema.hasColumn(TableName.SecretFolder, "parentId");
|
||||||
|
const doesNameColumnExist = await knex.schema.hasColumn(TableName.SecretFolder, "name");
|
||||||
|
if (doesParentColumExist && doesNameColumnExist) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.dropIndex(["parentId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasReviewerJwtCol = await knex.schema.hasColumn(
|
||||||
|
TableName.IdentityKubernetesAuth,
|
||||||
|
"encryptedKubernetesTokenReviewerJwt"
|
||||||
|
);
|
||||||
|
if (hasReviewerJwtCol) {
|
||||||
|
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
|
||||||
|
t.binary("encryptedKubernetesTokenReviewerJwt").nullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// we can't make it back to non nullable, it will fail
|
||||||
|
}
|
@@ -0,0 +1,29 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas/models";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "allowedSelfApprovals"))) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
|
||||||
|
t.boolean("allowedSelfApprovals").notNullable().defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "allowedSelfApprovals"))) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||||
|
t.boolean("allowedSelfApprovals").notNullable().defaultTo(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "allowedSelfApprovals")) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretApprovalPolicy, (t) => {
|
||||||
|
t.dropColumn("allowedSelfApprovals");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "allowedSelfApprovals")) {
|
||||||
|
await knex.schema.alterTable(TableName.AccessApprovalPolicy, (t) => {
|
||||||
|
t.dropColumn("allowedSelfApprovals");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,19 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasColumn(TableName.AppConnection, "isPlatformManagedCredentials"))) {
|
||||||
|
await knex.schema.alterTable(TableName.AppConnection, (t) => {
|
||||||
|
t.boolean("isPlatformManagedCredentials").defaultTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.AppConnection, "isPlatformManagedCredentials")) {
|
||||||
|
await knex.schema.alterTable(TableName.AppConnection, (t) => {
|
||||||
|
t.dropColumn("isPlatformManagedCredentials");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,58 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "@app/db/utils";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
if (!(await knex.schema.hasTable(TableName.SecretRotationV2))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretRotationV2, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("name", 32).notNullable();
|
||||||
|
t.string("description");
|
||||||
|
t.string("type").notNullable();
|
||||||
|
t.jsonb("parameters").notNullable();
|
||||||
|
t.jsonb("secretsMapping").notNullable();
|
||||||
|
t.binary("encryptedGeneratedCredentials").notNullable();
|
||||||
|
t.boolean("isAutoRotationEnabled").notNullable().defaultTo(true);
|
||||||
|
t.integer("activeIndex").notNullable().defaultTo(0);
|
||||||
|
t.uuid("folderId").notNullable();
|
||||||
|
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
|
||||||
|
t.uuid("connectionId").notNullable();
|
||||||
|
t.foreign("connectionId").references("id").inTable(TableName.AppConnection);
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
t.integer("rotationInterval").notNullable();
|
||||||
|
t.jsonb("rotateAtUtc").notNullable(); // { hours: number; minutes: number }
|
||||||
|
t.string("rotationStatus").notNullable();
|
||||||
|
t.datetime("lastRotationAttemptedAt").notNullable();
|
||||||
|
t.datetime("lastRotatedAt").notNullable();
|
||||||
|
t.binary("encryptedLastRotationMessage"); // we encrypt this because it may contain sensitive info (SQL errors showing credentials)
|
||||||
|
t.string("lastRotationJobId");
|
||||||
|
t.datetime("nextRotationAt");
|
||||||
|
t.boolean("isLastRotationManual").notNullable().defaultTo(true); // creation is considered a "manual" rotation
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.SecretRotationV2);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.SecretRotationV2, (t) => {
|
||||||
|
t.unique(["folderId", "name"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.SecretRotationV2SecretMapping))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretRotationV2SecretMapping, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("secretId").notNullable();
|
||||||
|
// scott: this is deferred to block secret deletion but not prevent folder/environment/project deletion
|
||||||
|
// ie, if rotation is being deleted as well we permit it, otherwise throw
|
||||||
|
t.foreign("secretId").references("id").inTable(TableName.SecretV2).deferrable("deferred");
|
||||||
|
t.uuid("rotationId").notNullable();
|
||||||
|
t.foreign("rotationId").references("id").inTable(TableName.SecretRotationV2).onDelete("CASCADE");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretRotationV2SecretMapping);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretRotationV2);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.SecretRotationV2);
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified");
|
||||||
|
if (!hasCol) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.datetime("lastSecretModified");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasCol = await knex.schema.hasColumn(TableName.SecretFolder, "lastSecretModified");
|
||||||
|
if (hasCol) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
|
||||||
|
t.dropColumn("lastSecretModified");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,8 @@ export const AccessApprovalPoliciesSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
enforcementLevel: z.string().default("hard"),
|
enforcementLevel: z.string().default("hard"),
|
||||||
deletedAt: z.date().nullable().optional()
|
deletedAt: z.date().nullable().optional(),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;
|
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;
|
||||||
|
@@ -19,7 +19,8 @@ export const AppConnectionsSchema = z.object({
|
|||||||
version: z.number().default(1),
|
version: z.number().default(1),
|
||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
isPlatformManagedCredentials: z.boolean().default(false).nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TAppConnections = z.infer<typeof AppConnectionsSchema>;
|
export type TAppConnections = z.infer<typeof AppConnectionsSchema>;
|
||||||
|
@@ -28,7 +28,7 @@ export const IdentityKubernetesAuthsSchema = z.object({
|
|||||||
allowedNamespaces: z.string(),
|
allowedNamespaces: z.string(),
|
||||||
allowedNames: z.string(),
|
allowedNames: z.string(),
|
||||||
allowedAudience: z.string(),
|
allowedAudience: z.string(),
|
||||||
encryptedKubernetesTokenReviewerJwt: zodBuffer,
|
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
||||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
|
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -26,7 +26,8 @@ export const IdentityOidcAuthsSchema = z.object({
|
|||||||
boundSubject: z.string().nullable().optional(),
|
boundSubject: z.string().nullable().optional(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
encryptedCaCertificate: zodBuffer.nullable().optional()
|
encryptedCaCertificate: zodBuffer.nullable().optional(),
|
||||||
|
claimMetadataMapping: z.unknown().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
||||||
|
@@ -3,6 +3,7 @@ export * from "./access-approval-policies-approvers";
|
|||||||
export * from "./access-approval-requests";
|
export * from "./access-approval-requests";
|
||||||
export * from "./access-approval-requests-reviewers";
|
export * from "./access-approval-requests-reviewers";
|
||||||
export * from "./api-keys";
|
export * from "./api-keys";
|
||||||
|
export * from "./app-connections";
|
||||||
export * from "./audit-log-streams";
|
export * from "./audit-log-streams";
|
||||||
export * from "./audit-logs";
|
export * from "./audit-logs";
|
||||||
export * from "./auth-token-sessions";
|
export * from "./auth-token-sessions";
|
||||||
@@ -19,6 +20,7 @@ export * from "./certificate-templates";
|
|||||||
export * from "./certificates";
|
export * from "./certificates";
|
||||||
export * from "./dynamic-secret-leases";
|
export * from "./dynamic-secret-leases";
|
||||||
export * from "./dynamic-secrets";
|
export * from "./dynamic-secrets";
|
||||||
|
export * from "./external-group-org-role-mappings";
|
||||||
export * from "./external-kms";
|
export * from "./external-kms";
|
||||||
export * from "./gateways";
|
export * from "./gateways";
|
||||||
export * from "./git-app-install-sessions";
|
export * from "./git-app-install-sessions";
|
||||||
@@ -97,13 +99,16 @@ export * from "./secret-references";
|
|||||||
export * from "./secret-references-v2";
|
export * from "./secret-references-v2";
|
||||||
export * from "./secret-rotation-output-v2";
|
export * from "./secret-rotation-output-v2";
|
||||||
export * from "./secret-rotation-outputs";
|
export * from "./secret-rotation-outputs";
|
||||||
|
export * from "./secret-rotation-v2-secret-mappings";
|
||||||
export * from "./secret-rotations";
|
export * from "./secret-rotations";
|
||||||
|
export * from "./secret-rotations-v2";
|
||||||
export * from "./secret-scanning-git-risks";
|
export * from "./secret-scanning-git-risks";
|
||||||
export * from "./secret-sharing";
|
export * from "./secret-sharing";
|
||||||
export * from "./secret-snapshot-folders";
|
export * from "./secret-snapshot-folders";
|
||||||
export * from "./secret-snapshot-secrets";
|
export * from "./secret-snapshot-secrets";
|
||||||
export * from "./secret-snapshot-secrets-v2";
|
export * from "./secret-snapshot-secrets-v2";
|
||||||
export * from "./secret-snapshots";
|
export * from "./secret-snapshots";
|
||||||
|
export * from "./secret-syncs";
|
||||||
export * from "./secret-tag-junction";
|
export * from "./secret-tag-junction";
|
||||||
export * from "./secret-tags";
|
export * from "./secret-tags";
|
||||||
export * from "./secret-v2-tag-junction";
|
export * from "./secret-v2-tag-junction";
|
||||||
|
@@ -140,7 +140,9 @@ export enum TableName {
|
|||||||
KmipClient = "kmip_clients",
|
KmipClient = "kmip_clients",
|
||||||
KmipOrgConfig = "kmip_org_configs",
|
KmipOrgConfig = "kmip_org_configs",
|
||||||
KmipOrgServerCertificates = "kmip_org_server_certificates",
|
KmipOrgServerCertificates = "kmip_org_server_certificates",
|
||||||
KmipClientCertificates = "kmip_client_certificates"
|
KmipClientCertificates = "kmip_client_certificates",
|
||||||
|
SecretRotationV2 = "secret_rotations_v2",
|
||||||
|
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||||
@@ -233,3 +235,8 @@ export enum ActionProjectType {
|
|||||||
// project operations that happen on all types
|
// project operations that happen on all types
|
||||||
Any = "any"
|
Any = "any"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum SortDirection {
|
||||||
|
ASC = "asc",
|
||||||
|
DESC = "desc"
|
||||||
|
}
|
||||||
|
@@ -22,7 +22,11 @@ export const OrganizationsSchema = z.object({
|
|||||||
kmsEncryptedDataKey: zodBuffer.nullable().optional(),
|
kmsEncryptedDataKey: zodBuffer.nullable().optional(),
|
||||||
defaultMembershipRole: z.string().default("member"),
|
defaultMembershipRole: z.string().default("member"),
|
||||||
enforceMfa: z.boolean().default(false),
|
enforceMfa: z.boolean().default(false),
|
||||||
selectedMfaMethod: z.string().nullable().optional()
|
selectedMfaMethod: z.string().nullable().optional(),
|
||||||
|
allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional(),
|
||||||
|
shouldUseNewPrivilegeSystem: z.boolean().default(true),
|
||||||
|
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
||||||
|
privilegeUpgradeInitiatedAt: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||||
|
@@ -16,7 +16,8 @@ export const SecretApprovalPoliciesSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
enforcementLevel: z.string().default("hard"),
|
enforcementLevel: z.string().default("hard"),
|
||||||
deletedAt: z.date().nullable().optional()
|
deletedAt: z.date().nullable().optional(),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalPolicies = z.infer<typeof SecretApprovalPoliciesSchema>;
|
export type TSecretApprovalPolicies = z.infer<typeof SecretApprovalPoliciesSchema>;
|
||||||
|
@@ -16,7 +16,8 @@ export const SecretFoldersSchema = z.object({
|
|||||||
envId: z.string().uuid(),
|
envId: z.string().uuid(),
|
||||||
parentId: z.string().uuid().nullable().optional(),
|
parentId: z.string().uuid().nullable().optional(),
|
||||||
isReserved: z.boolean().default(false).nullable().optional(),
|
isReserved: z.boolean().default(false).nullable().optional(),
|
||||||
description: z.string().nullable().optional()
|
description: z.string().nullable().optional(),
|
||||||
|
lastSecretModified: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;
|
||||||
|
23
backend/src/db/schemas/secret-rotation-v2-secret-mappings.ts
Normal file
23
backend/src/db/schemas/secret-rotation-v2-secret-mappings.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const SecretRotationV2SecretMappingsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
secretId: z.string().uuid(),
|
||||||
|
rotationId: z.string().uuid()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretRotationV2SecretMappings = z.infer<typeof SecretRotationV2SecretMappingsSchema>;
|
||||||
|
export type TSecretRotationV2SecretMappingsInsert = Omit<
|
||||||
|
z.input<typeof SecretRotationV2SecretMappingsSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TSecretRotationV2SecretMappingsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof SecretRotationV2SecretMappingsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
39
backend/src/db/schemas/secret-rotations-v2.ts
Normal file
39
backend/src/db/schemas/secret-rotations-v2.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
// Code generated by automation script, DO NOT EDIT.
|
||||||
|
// Automated by pulling database and generating zod schema
|
||||||
|
// To update. Just run npm run generate:schema
|
||||||
|
// Written by akhilmhdh.
|
||||||
|
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { zodBuffer } from "@app/lib/zod";
|
||||||
|
|
||||||
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const SecretRotationsV2Schema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
description: z.string().nullable().optional(),
|
||||||
|
type: z.string(),
|
||||||
|
parameters: z.unknown(),
|
||||||
|
secretsMapping: z.unknown(),
|
||||||
|
encryptedGeneratedCredentials: zodBuffer,
|
||||||
|
isAutoRotationEnabled: z.boolean().default(true),
|
||||||
|
activeIndex: z.number().default(0),
|
||||||
|
folderId: z.string().uuid(),
|
||||||
|
connectionId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
rotationInterval: z.number(),
|
||||||
|
rotateAtUtc: z.unknown(),
|
||||||
|
rotationStatus: z.string(),
|
||||||
|
lastRotationAttemptedAt: z.date(),
|
||||||
|
lastRotatedAt: z.date(),
|
||||||
|
encryptedLastRotationMessage: zodBuffer.nullable().optional(),
|
||||||
|
lastRotationJobId: z.string().nullable().optional(),
|
||||||
|
nextRotationAt: z.date().nullable().optional(),
|
||||||
|
isLastRotationManual: z.boolean().default(true)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretRotationsV2 = z.infer<typeof SecretRotationsV2Schema>;
|
||||||
|
export type TSecretRotationsV2Insert = Omit<z.input<typeof SecretRotationsV2Schema>, TImmutableDBKeys>;
|
||||||
|
export type TSecretRotationsV2Update = Partial<Omit<z.input<typeof SecretRotationsV2Schema>, TImmutableDBKeys>>;
|
@@ -12,7 +12,6 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const SecretSharingSchema = z.object({
|
export const SecretSharingSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
encryptedValue: z.string().nullable().optional(),
|
encryptedValue: z.string().nullable().optional(),
|
||||||
type: z.string(),
|
|
||||||
iv: z.string().nullable().optional(),
|
iv: z.string().nullable().optional(),
|
||||||
tag: z.string().nullable().optional(),
|
tag: z.string().nullable().optional(),
|
||||||
hashedHex: z.string().nullable().optional(),
|
hashedHex: z.string().nullable().optional(),
|
||||||
@@ -27,7 +26,8 @@ export const SecretSharingSchema = z.object({
|
|||||||
lastViewedAt: z.date().nullable().optional(),
|
lastViewedAt: z.date().nullable().optional(),
|
||||||
password: z.string().nullable().optional(),
|
password: z.string().nullable().optional(),
|
||||||
encryptedSecret: zodBuffer.nullable().optional(),
|
encryptedSecret: zodBuffer.nullable().optional(),
|
||||||
identifier: z.string().nullable().optional()
|
identifier: z.string().nullable().optional(),
|
||||||
|
type: z.string().default("share")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
||||||
|
@@ -25,7 +25,8 @@ export const SuperAdminSchema = z.object({
|
|||||||
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
encryptedSlackClientId: zodBuffer.nullable().optional(),
|
||||||
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
||||||
authConsentContent: z.string().nullable().optional(),
|
authConsentContent: z.string().nullable().optional(),
|
||||||
pageFrameContent: z.string().nullable().optional()
|
pageFrameContent: z.string().nullable().optional(),
|
||||||
|
adminIdentityIds: z.string().array().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
@@ -16,7 +16,7 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
|
|||||||
// for CSRs sent in PEM, we leave them as is
|
// for CSRs sent in PEM, we leave them as is
|
||||||
// for CSRs sent in base64, we preprocess them to remove new lines and spaces
|
// for CSRs sent in base64, we preprocess them to remove new lines and spaces
|
||||||
if (!csrBody.includes("BEGIN CERTIFICATE REQUEST")) {
|
if (!csrBody.includes("BEGIN CERTIFICATE REQUEST")) {
|
||||||
csrBody = csrBody.replace(/\n/g, "").replace(/ /g, "");
|
csrBody = csrBody.replaceAll("\n", "").replaceAll(" ", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
done(null, csrBody);
|
done(null, csrBody);
|
||||||
|
@@ -29,7 +29,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" }),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -147,7 +148,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" }),
|
||||||
approvals: z.number().min(1).optional(),
|
approvals: z.number().min(1).optional(),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@@ -110,7 +110,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
secretPath: z.string().nullish(),
|
secretPath: z.string().nullish(),
|
||||||
envId: z.string(),
|
envId: z.string(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
deletedAt: z.date().nullish()
|
deletedAt: z.date().nullish(),
|
||||||
|
allowedSelfApprovals: z.boolean()
|
||||||
}),
|
}),
|
||||||
reviewers: z
|
reviewers: z
|
||||||
.object({
|
.object({
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||||
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
||||||
@@ -6,6 +5,7 @@ import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/pro
|
|||||||
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
import { backfillPermissionV1SchemaToV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { UnauthorizedError } from "@app/lib/errors";
|
import { UnauthorizedError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { KmipClientsSchema } from "@app/db/schemas";
|
import { KmipClientsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { KmipPermission } from "@app/ee/services/kmip/kmip-enum";
|
import { KmipPermission } from "@app/ee/services/kmip/kmip-enum";
|
||||||
import { KmipClientOrderBy } from "@app/ee/services/kmip/kmip-types";
|
import { KmipClientOrderBy } from "@app/ee/services/kmip/kmip-types";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { OrderByDirection } from "@app/lib/types";
|
import { OrderByDirection } from "@app/lib/types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
@@ -61,8 +61,8 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
if (ldapConfig.groupSearchBase) {
|
if (ldapConfig.groupSearchBase) {
|
||||||
const groupFilter = "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))";
|
const groupFilter = "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))";
|
||||||
const groupSearchFilter = (ldapConfig.groupSearchFilter || groupFilter)
|
const groupSearchFilter = (ldapConfig.groupSearchFilter || groupFilter)
|
||||||
.replace(/{{\.Username}}/g, user.uid)
|
.replaceAll("{{.Username}}", user.uid)
|
||||||
.replace(/{{\.UserDN}}/g, user.dn);
|
.replaceAll("{{.UserDN}}", user.dn);
|
||||||
|
|
||||||
if (!isValidLdapFilter(groupSearchFilter)) {
|
if (!isValidLdapFilter(groupSearchFilter)) {
|
||||||
throw new Error("Generated LDAP search filter is invalid.");
|
throw new Error("Generated LDAP search filter is invalid.");
|
||||||
|
@@ -25,7 +25,7 @@ type TSAMLConfig = {
|
|||||||
callbackUrl: string;
|
callbackUrl: string;
|
||||||
entryPoint: string;
|
entryPoint: string;
|
||||||
issuer: string;
|
issuer: string;
|
||||||
cert: string;
|
idpCert: string;
|
||||||
audience: string;
|
audience: string;
|
||||||
wantAuthnResponseSigned?: boolean;
|
wantAuthnResponseSigned?: boolean;
|
||||||
wantAssertionsSigned?: boolean;
|
wantAssertionsSigned?: boolean;
|
||||||
@@ -72,7 +72,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
|
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
|
||||||
entryPoint: ssoConfig.entryPoint,
|
entryPoint: ssoConfig.entryPoint,
|
||||||
issuer: ssoConfig.issuer,
|
issuer: ssoConfig.issuer,
|
||||||
cert: ssoConfig.cert,
|
idpCert: ssoConfig.cert,
|
||||||
audience: appCfg.SITE_URL || ""
|
audience: appCfg.SITE_URL || ""
|
||||||
};
|
};
|
||||||
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
if (ssoConfig.authProvider === SamlProviders.JUMPCLOUD_SAML) {
|
||||||
@@ -302,15 +302,21 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const saml = await server.services.saml.createSamlCfg({
|
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||||
actor: req.permission.type,
|
const { permission } = req;
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
return server.services.saml.createSamlCfg({
|
||||||
actorOrgId: req.permission.orgId,
|
isActive,
|
||||||
orgId: req.body.organizationId,
|
authProvider,
|
||||||
...req.body
|
issuer,
|
||||||
|
entryPoint,
|
||||||
|
idpCert: cert,
|
||||||
|
actor: permission.type,
|
||||||
|
actorId: permission.id,
|
||||||
|
actorAuthMethod: permission.authMethod,
|
||||||
|
actorOrgId: permission.orgId,
|
||||||
|
orgId: req.body.organizationId
|
||||||
});
|
});
|
||||||
return saml;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -337,15 +343,21 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const saml = await server.services.saml.updateSamlCfg({
|
const { isActive, authProvider, issuer, entryPoint, cert } = req.body;
|
||||||
actor: req.permission.type,
|
const { permission } = req;
|
||||||
actorId: req.permission.id,
|
|
||||||
actorAuthMethod: req.permission.authMethod,
|
return server.services.saml.updateSamlCfg({
|
||||||
actorOrgId: req.permission.orgId,
|
isActive,
|
||||||
orgId: req.body.organizationId,
|
authProvider,
|
||||||
...req.body
|
issuer,
|
||||||
|
entryPoint,
|
||||||
|
idpCert: cert,
|
||||||
|
actor: permission.type,
|
||||||
|
actorId: permission.id,
|
||||||
|
actorAuthMethod: permission.authMethod,
|
||||||
|
actorOrgId: permission.orgId,
|
||||||
|
orgId: req.body.organizationId
|
||||||
});
|
});
|
||||||
return saml;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@@ -35,7 +35,8 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.array()
|
.array()
|
||||||
.min(1, { message: "At least one approver should be provided" }),
|
.min(1, { message: "At least one approver should be provided" }),
|
||||||
approvals: z.number().min(1).default(1),
|
approvals: z.number().min(1).default(1),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
|
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -85,7 +86,8 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
.nullable()
|
.nullable()
|
||||||
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
.transform((val) => (val ? removeTrailingSlash(val) : val))
|
||||||
.transform((val) => (val === "" ? "/" : val)),
|
.transform((val) => (val === "" ? "/" : val)),
|
||||||
enforcementLevel: z.nativeEnum(EnforcementLevel).optional()
|
enforcementLevel: z.nativeEnum(EnforcementLevel).optional(),
|
||||||
|
allowedSelfApprovals: z.boolean().default(true)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
@@ -1,16 +1,11 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import {
|
import { SecretApprovalRequestsReviewersSchema, SecretApprovalRequestsSchema, UsersSchema } from "@app/db/schemas";
|
||||||
SecretApprovalRequestsReviewersSchema,
|
|
||||||
SecretApprovalRequestsSchema,
|
|
||||||
SecretTagsSchema,
|
|
||||||
UsersSchema
|
|
||||||
} from "@app/db/schemas";
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
|
||||||
|
|
||||||
@@ -54,7 +49,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
.array(),
|
.array(),
|
||||||
secretPath: z.string().optional().nullable(),
|
secretPath: z.string().optional().nullable(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
deletedAt: z.date().nullish()
|
deletedAt: z.date().nullish(),
|
||||||
|
allowedSelfApprovals: z.boolean()
|
||||||
}),
|
}),
|
||||||
committerUser: approvalRequestUser,
|
committerUser: approvalRequestUser,
|
||||||
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
|
||||||
@@ -250,14 +246,6 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const tagSchema = SecretTagsSchema.pick({
|
|
||||||
id: true,
|
|
||||||
slug: true,
|
|
||||||
color: true
|
|
||||||
})
|
|
||||||
.array()
|
|
||||||
.optional();
|
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:id",
|
url: "/:id",
|
||||||
@@ -280,7 +268,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
approvers: approvalRequestUser.array(),
|
approvers: approvalRequestUser.array(),
|
||||||
secretPath: z.string().optional().nullable(),
|
secretPath: z.string().optional().nullable(),
|
||||||
enforcementLevel: z.string(),
|
enforcementLevel: z.string(),
|
||||||
deletedAt: z.date().nullish()
|
deletedAt: z.date().nullish(),
|
||||||
|
allowedSelfApprovals: z.boolean()
|
||||||
}),
|
}),
|
||||||
environment: z.string(),
|
environment: z.string(),
|
||||||
statusChangedByUser: approvalRequestUser.optional(),
|
statusChangedByUser: approvalRequestUser.optional(),
|
||||||
@@ -288,10 +277,12 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
reviewers: approvalRequestUser.extend({ status: z.string(), comment: z.string().optional() }).array(),
|
reviewers: approvalRequestUser.extend({ status: z.string(), comment: z.string().optional() }).array(),
|
||||||
secretPath: z.string(),
|
secretPath: z.string(),
|
||||||
commits: secretRawSchema
|
commits: secretRawSchema
|
||||||
.omit({ _id: true, environment: true, workspace: true, type: true, version: true })
|
.omit({ _id: true, environment: true, workspace: true, type: true, version: true, secretValue: true })
|
||||||
.extend({
|
.extend({
|
||||||
|
secretValue: z.string().optional(),
|
||||||
|
isRotatedSecret: z.boolean().optional(),
|
||||||
op: z.string(),
|
op: z.string(),
|
||||||
tags: tagSchema,
|
tags: SanitizedTagSchema.array().optional(),
|
||||||
secretMetadata: ResourceMetadataSchema.nullish(),
|
secretMetadata: ResourceMetadataSchema.nullish(),
|
||||||
secret: z
|
secret: z
|
||||||
.object({
|
.object({
|
||||||
@@ -310,7 +301,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
secretKey: z.string(),
|
secretKey: z.string(),
|
||||||
secretValue: z.string().optional(),
|
secretValue: z.string().optional(),
|
||||||
secretComment: z.string().optional(),
|
secretComment: z.string().optional(),
|
||||||
tags: tagSchema,
|
tags: SanitizedTagSchema.array().optional(),
|
||||||
secretMetadata: ResourceMetadataSchema.nullish()
|
secretMetadata: ResourceMetadataSchema.nullish()
|
||||||
})
|
})
|
||||||
.optional()
|
.optional()
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretRotationOutputsSchema, SecretRotationsSchema } from "@app/db/schemas";
|
import { SecretRotationOutputsSchema, SecretRotationsSchema } from "@app/db/schemas";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
@@ -40,16 +41,10 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
handler: async (req) => {
|
handler: async () => {
|
||||||
const secretRotation = await server.services.secretRotation.createRotation({
|
throw new BadRequestError({
|
||||||
actor: req.permission.type,
|
message: `This version of Secret Rotations has been deprecated. Please see docs for new version.`
|
||||||
actorAuthMethod: req.permission.authMethod,
|
|
||||||
actorId: req.permission.id,
|
|
||||||
actorOrgId: req.permission.orgId,
|
|
||||||
...req.body,
|
|
||||||
projectId: req.body.workspaceId
|
|
||||||
});
|
});
|
||||||
return { secretRotation };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import z from "zod";
|
import z from "zod";
|
||||||
|
|
||||||
import { ProjectPermissionActions } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionSecretActions } from "@app/ee/services/permission/project-permission";
|
||||||
import { RAW_SECRETS } from "@app/lib/api-docs";
|
import { RAW_SECRETS } from "@app/lib/api-docs";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
import { readLimit } from "@app/server/config/rateLimiter";
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -9,7 +9,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
|
|||||||
|
|
||||||
const AccessListEntrySchema = z
|
const AccessListEntrySchema = z
|
||||||
.object({
|
.object({
|
||||||
allowedActions: z.nativeEnum(ProjectPermissionActions).array(),
|
allowedActions: z.nativeEnum(ProjectPermissionSecretActions).array(),
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
membershipId: z.string(),
|
membershipId: z.string(),
|
||||||
name: z.string()
|
name: z.string()
|
||||||
|
@@ -22,7 +22,11 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
secretVersions: secretRawSchema.array()
|
secretVersions: secretRawSchema
|
||||||
|
.extend({
|
||||||
|
secretValueHidden: z.boolean()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -37,6 +41,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
|
|||||||
offset: req.query.offset,
|
offset: req.query.offset,
|
||||||
secretId: req.params.secretId
|
secretId: req.params.secretId
|
||||||
});
|
});
|
||||||
|
|
||||||
return { secretVersions };
|
return { secretVersions };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,10 +1,10 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretSnapshotsSchema, SecretTagsSchema } from "@app/db/schemas";
|
import { SecretSnapshotsSchema } from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
import { PROJECTS } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedTagSchema, secretRawSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
||||||
@@ -31,12 +31,10 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
|||||||
secretVersions: secretRawSchema
|
secretVersions: secretRawSchema
|
||||||
.omit({ _id: true, environment: true, workspace: true, type: true })
|
.omit({ _id: true, environment: true, workspace: true, type: true })
|
||||||
.extend({
|
.extend({
|
||||||
|
secretValueHidden: z.boolean(),
|
||||||
secretId: z.string(),
|
secretId: z.string(),
|
||||||
tags: SecretTagsSchema.pick({
|
tags: SanitizedTagSchema.array(),
|
||||||
id: true,
|
isRotatedSecret: z.boolean().optional()
|
||||||
slug: true,
|
|
||||||
color: true
|
|
||||||
}).array()
|
|
||||||
})
|
})
|
||||||
.array(),
|
.array(),
|
||||||
folderVersion: z.object({ id: z.string(), name: z.string() }).array(),
|
folderVersion: z.object({ id: z.string(), name: z.string() }).array(),
|
||||||
@@ -55,6 +53,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
id: req.params.secretSnapshotId
|
id: req.params.secretSnapshotId
|
||||||
});
|
});
|
||||||
|
|
||||||
return { secretSnapshot };
|
return { secretSnapshot };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -1,13 +1,15 @@
|
|||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
import { SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||||
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
import { SSH_CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
|
||||||
|
import { PostHogEventTypes } from "@app/services/telemetry/telemetry-types";
|
||||||
|
|
||||||
export const registerSshCertRouter = async (server: FastifyZodProvider) => {
|
export const registerSshCertRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
@@ -73,6 +75,16 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.telemetry.sendPostHogEvents({
|
||||||
|
event: PostHogEventTypes.SignSshKey,
|
||||||
|
distinctId: getTelemetryDistinctId(req),
|
||||||
|
properties: {
|
||||||
|
certificateTemplateId: req.body.certificateTemplateId,
|
||||||
|
principals: req.body.principals,
|
||||||
|
...req.auditLogInfo
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
serialNumber,
|
serialNumber,
|
||||||
signedKey: signedPublicKey
|
signedKey: signedPublicKey
|
||||||
@@ -152,6 +164,16 @@ export const registerSshCertRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.services.telemetry.sendPostHogEvents({
|
||||||
|
event: PostHogEventTypes.IssueSshCreds,
|
||||||
|
distinctId: getTelemetryDistinctId(req),
|
||||||
|
properties: {
|
||||||
|
certificateTemplateId: req.body.certificateTemplateId,
|
||||||
|
principals: req.body.principals,
|
||||||
|
...req.auditLogInfo
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
serialNumber,
|
serialNumber,
|
||||||
signedKey: signedPublicKey,
|
signedKey: signedPublicKey,
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
isValidUserPattern
|
isValidUserPattern
|
||||||
} from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-validators";
|
} from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-validators";
|
||||||
import { SSH_CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
import { SSH_CERTIFICATE_TEMPLATES } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
@@ -23,7 +24,9 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
body: z.object({
|
body: z.object({
|
||||||
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
||||||
slug: slugSchema({ min: 1, max: 60 }).optional().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
slug: slugSchema({ min: 1, max: 60 }).optional().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: ProjectPermissionV2Schema.array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||||
|
.refine(checkForInvalidPermissionCombination),
|
||||||
type: z.discriminatedUnion("isTemporary", [
|
type: z.discriminatedUnion("isTemporary", [
|
||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(false)
|
isTemporary: z.literal(false)
|
||||||
@@ -81,7 +84,8 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
slug: slugSchema({ min: 1, max: 60 }).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
slug: slugSchema({ min: 1, max: 60 }).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
||||||
permissions: ProjectPermissionV2Schema.array()
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
.optional()
|
.optional()
|
||||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.permissions)
|
||||||
|
.refine(checkForInvalidPermissionCombination),
|
||||||
type: z.discriminatedUnion("isTemporary", [
|
type: z.discriminatedUnion("isTemporary", [
|
||||||
z.object({ isTemporary: z.literal(false).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary) }),
|
z.object({ isTemporary: z.literal(false).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary) }),
|
||||||
z.object({
|
z.object({
|
||||||
|
@@ -1,10 +1,11 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-types";
|
||||||
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE_V2 } from "@app/lib/api-docs";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { slugSchema } from "@app/server/lib/schemas";
|
import { slugSchema } from "@app/server/lib/schemas";
|
||||||
@@ -30,7 +31,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.identityId),
|
||||||
projectId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.projectId),
|
projectId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.projectId),
|
||||||
slug: slugSchema({ min: 1, max: 60 }).optional().describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.slug),
|
slug: slugSchema({ min: 1, max: 60 }).optional().describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.slug),
|
||||||
permissions: ProjectPermissionV2Schema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.permission),
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.CREATE.permission)
|
||||||
|
.refine(checkForInvalidPermissionCombination),
|
||||||
type: z.discriminatedUnion("isTemporary", [
|
type: z.discriminatedUnion("isTemporary", [
|
||||||
z.object({
|
z.object({
|
||||||
isTemporary: z.literal(false)
|
isTemporary: z.literal(false)
|
||||||
@@ -94,7 +97,8 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
slug: slugSchema({ min: 1, max: 60 }).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.slug),
|
slug: slugSchema({ min: 1, max: 60 }).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.slug),
|
||||||
permissions: ProjectPermissionV2Schema.array()
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
.optional()
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.privilegePermission),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.privilegePermission)
|
||||||
|
.refine(checkForInvalidPermissionCombination),
|
||||||
type: z.discriminatedUnion("isTemporary", [
|
type: z.discriminatedUnion("isTemporary", [
|
||||||
z.object({ isTemporary: z.literal(false).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.isTemporary) }),
|
z.object({ isTemporary: z.literal(false).describe(IDENTITY_ADDITIONAL_PRIVILEGE_V2.UPDATE.isTemporary) }),
|
||||||
z.object({
|
z.object({
|
||||||
|
@@ -1,3 +1,8 @@
|
|||||||
|
import {
|
||||||
|
registerSecretRotationV2Router,
|
||||||
|
SECRET_ROTATION_REGISTER_ROUTER_MAP
|
||||||
|
} from "@app/ee/routes/v2/secret-rotation-v2-routers";
|
||||||
|
|
||||||
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||||
import { registerProjectRoleRouter } from "./project-role-router";
|
import { registerProjectRoleRouter } from "./project-role-router";
|
||||||
|
|
||||||
@@ -13,4 +18,17 @@ export const registerV2EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerIdentityProjectAdditionalPrivilegeRouter, {
|
await server.register(registerIdentityProjectAdditionalPrivilegeRouter, {
|
||||||
prefix: "/identity-project-additional-privilege"
|
prefix: "/identity-project-additional-privilege"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.register(
|
||||||
|
async (secretRotationV2Router) => {
|
||||||
|
// register generic secret rotation endpoints
|
||||||
|
await secretRotationV2Router.register(registerSecretRotationV2Router);
|
||||||
|
|
||||||
|
// register service specific secret rotation endpoints (secret-rotations/postgres-credentials, etc.)
|
||||||
|
for await (const [type, router] of Object.entries(SECRET_ROTATION_REGISTER_ROUTER_MAP)) {
|
||||||
|
await secretRotationV2Router.register(router, { prefix: `/${type}` });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ prefix: "/secret-rotations" }
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@@ -2,6 +2,7 @@ import { packRules } from "@casl/ability/extra";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
|
import { checkForInvalidPermissionCombination } from "@app/ee/services/permission/permission-fns";
|
||||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||||
import { PROJECT_ROLE } from "@app/lib/api-docs";
|
import { PROJECT_ROLE } from "@app/lib/api-docs";
|
||||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
@@ -37,7 +38,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.describe(PROJECT_ROLE.CREATE.slug),
|
.describe(PROJECT_ROLE.CREATE.slug),
|
||||||
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
|
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
|
||||||
description: z.string().trim().nullish().describe(PROJECT_ROLE.CREATE.description),
|
description: z.string().trim().nullish().describe(PROJECT_ROLE.CREATE.description),
|
||||||
permissions: ProjectPermissionV2Schema.array().describe(PROJECT_ROLE.CREATE.permissions)
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
|
.describe(PROJECT_ROLE.CREATE.permissions)
|
||||||
|
.refine(checkForInvalidPermissionCombination)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
@@ -92,7 +95,10 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
.describe(PROJECT_ROLE.UPDATE.slug),
|
.describe(PROJECT_ROLE.UPDATE.slug),
|
||||||
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
|
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
|
||||||
description: z.string().trim().nullish().describe(PROJECT_ROLE.UPDATE.description),
|
description: z.string().trim().nullish().describe(PROJECT_ROLE.UPDATE.description),
|
||||||
permissions: ProjectPermissionV2Schema.array().describe(PROJECT_ROLE.UPDATE.permissions).optional()
|
permissions: ProjectPermissionV2Schema.array()
|
||||||
|
.describe(PROJECT_ROLE.UPDATE.permissions)
|
||||||
|
.optional()
|
||||||
|
.superRefine(checkForInvalidPermissionCombination)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
|
14
backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts
Normal file
14
backend/src/ee/routes/v2/secret-rotation-v2-routers/index.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
|
||||||
|
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
|
||||||
|
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
|
||||||
|
|
||||||
|
export * from "./secret-rotation-v2-router";
|
||||||
|
|
||||||
|
export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
|
||||||
|
SecretRotation,
|
||||||
|
(server: FastifyZodProvider) => Promise<void>
|
||||||
|
> = {
|
||||||
|
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
|
||||||
|
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter
|
||||||
|
};
|
@@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
CreateMsSqlCredentialsRotationSchema,
|
||||||
|
MsSqlCredentialsRotationSchema,
|
||||||
|
UpdateMsSqlCredentialsRotationSchema
|
||||||
|
} from "@app/ee/services/secret-rotation-v2/mssql-credentials";
|
||||||
|
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
|
||||||
|
|
||||||
|
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
|
||||||
|
|
||||||
|
export const registerMsSqlCredentialsRotationRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSecretRotationEndpoints({
|
||||||
|
type: SecretRotation.MsSqlCredentials,
|
||||||
|
server,
|
||||||
|
responseSchema: MsSqlCredentialsRotationSchema,
|
||||||
|
createSchema: CreateMsSqlCredentialsRotationSchema,
|
||||||
|
updateSchema: UpdateMsSqlCredentialsRotationSchema,
|
||||||
|
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
|
||||||
|
});
|
@@ -0,0 +1,19 @@
|
|||||||
|
import {
|
||||||
|
CreatePostgresCredentialsRotationSchema,
|
||||||
|
PostgresCredentialsRotationSchema,
|
||||||
|
UpdatePostgresCredentialsRotationSchema
|
||||||
|
} from "@app/ee/services/secret-rotation-v2/postgres-credentials";
|
||||||
|
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
|
||||||
|
|
||||||
|
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
|
||||||
|
|
||||||
|
export const registerPostgresCredentialsRotationRouter = async (server: FastifyZodProvider) =>
|
||||||
|
registerSecretRotationEndpoints({
|
||||||
|
type: SecretRotation.PostgresCredentials,
|
||||||
|
server,
|
||||||
|
responseSchema: PostgresCredentialsRotationSchema,
|
||||||
|
createSchema: CreatePostgresCredentialsRotationSchema,
|
||||||
|
updateSchema: UpdatePostgresCredentialsRotationSchema,
|
||||||
|
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
|
||||||
|
});
|
@@ -0,0 +1,429 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
import { SECRET_ROTATION_NAME_MAP } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-maps";
|
||||||
|
import {
|
||||||
|
TRotateAtUtc,
|
||||||
|
TSecretRotationV2,
|
||||||
|
TSecretRotationV2GeneratedCredentials,
|
||||||
|
TSecretRotationV2Input
|
||||||
|
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
|
||||||
|
import { SecretRotations } from "@app/lib/api-docs";
|
||||||
|
import { startsWithVowel } from "@app/lib/fn";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerSecretRotationEndpoints = <
|
||||||
|
T extends TSecretRotationV2,
|
||||||
|
I extends TSecretRotationV2Input,
|
||||||
|
C extends TSecretRotationV2GeneratedCredentials
|
||||||
|
>({
|
||||||
|
server,
|
||||||
|
type,
|
||||||
|
createSchema,
|
||||||
|
updateSchema,
|
||||||
|
responseSchema,
|
||||||
|
generatedCredentialsSchema
|
||||||
|
}: {
|
||||||
|
type: SecretRotation;
|
||||||
|
server: FastifyZodProvider;
|
||||||
|
createSchema: z.ZodType<{
|
||||||
|
name: string;
|
||||||
|
environment: string;
|
||||||
|
secretPath: string;
|
||||||
|
projectId: string;
|
||||||
|
connectionId: string;
|
||||||
|
parameters: I["parameters"];
|
||||||
|
secretsMapping: I["secretsMapping"];
|
||||||
|
description?: string | null;
|
||||||
|
isAutoRotationEnabled?: boolean;
|
||||||
|
rotationInterval: number;
|
||||||
|
rotateAtUtc?: TRotateAtUtc;
|
||||||
|
}>;
|
||||||
|
updateSchema: z.ZodType<{
|
||||||
|
connectionId?: string;
|
||||||
|
name?: string;
|
||||||
|
environment?: string;
|
||||||
|
secretPath?: string;
|
||||||
|
parameters?: I["parameters"];
|
||||||
|
secretsMapping?: I["secretsMapping"];
|
||||||
|
description?: string | null;
|
||||||
|
isAutoRotationEnabled?: boolean;
|
||||||
|
rotationInterval?: number;
|
||||||
|
rotateAtUtc?: TRotateAtUtc;
|
||||||
|
}>;
|
||||||
|
responseSchema: z.ZodTypeAny;
|
||||||
|
generatedCredentialsSchema: z.ZodTypeAny;
|
||||||
|
}) => {
|
||||||
|
const rotationType = SECRET_ROTATION_NAME_MAP[type];
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `List the ${rotationType} Rotations for the specified project.`,
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required").describe(SecretRotations.LIST(type).projectId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotations: responseSchema.array() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
query: { projectId }
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const secretRotations = (await server.services.secretRotationV2.listSecretRotationsByProjectId(
|
||||||
|
{ projectId, type },
|
||||||
|
req.permission
|
||||||
|
)) as T[];
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_ROTATIONS,
|
||||||
|
metadata: {
|
||||||
|
type,
|
||||||
|
count: secretRotations.length,
|
||||||
|
rotationIds: secretRotations.map((rotation) => rotation.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:rotationId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Get the specified ${rotationType} Rotation by ID.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationId: z.string().uuid().describe(SecretRotations.GET_BY_ID(type).rotationId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationId } = req.params;
|
||||||
|
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.findSecretRotationById(
|
||||||
|
{ rotationId, type },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretRotation.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
rotationId,
|
||||||
|
type,
|
||||||
|
secretPath: secretRotation.folder.path,
|
||||||
|
environment: secretRotation.environment.slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: `/rotation-name/:rotationName`,
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Get the specified ${rotationType} Rotation by name, secret path, environment and project ID.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationName: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Rotation name required")
|
||||||
|
.describe(SecretRotations.GET_BY_NAME(type).rotationName)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Project ID required")
|
||||||
|
.describe(SecretRotations.GET_BY_NAME(type).projectId),
|
||||||
|
secretPath: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Secret path required")
|
||||||
|
.describe(SecretRotations.GET_BY_NAME(type).secretPath),
|
||||||
|
environment: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.min(1, "Environment required")
|
||||||
|
.describe(SecretRotations.GET_BY_NAME(type).environment)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationName } = req.params;
|
||||||
|
const { projectId, secretPath, environment } = req.query;
|
||||||
|
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.findSecretRotationByName(
|
||||||
|
{ rotationName, projectId, type, secretPath, environment },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
rotationId: secretRotation.id,
|
||||||
|
type,
|
||||||
|
secretPath,
|
||||||
|
environment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Create ${
|
||||||
|
startsWithVowel(rotationType) ? "an" : "a"
|
||||||
|
} ${rotationType} Rotation for the specified project.`,
|
||||||
|
body: createSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.createSecretRotation(
|
||||||
|
{ ...req.body, type },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretRotation.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.CREATE_SECRET_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
rotationId: secretRotation.id,
|
||||||
|
type,
|
||||||
|
...req.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:rotationId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Update the specified ${rotationType} Rotation.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationId: z.string().uuid().describe(SecretRotations.UPDATE(type).rotationId)
|
||||||
|
}),
|
||||||
|
body: updateSchema,
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationId } = req.params;
|
||||||
|
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.updateSecretRotation(
|
||||||
|
{ ...req.body, rotationId, type },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretRotation.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.UPDATE_SECRET_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
rotationId,
|
||||||
|
type,
|
||||||
|
...req.body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/:rotationId`,
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Delete the specified ${rotationType} Rotation.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationId: z.string().uuid().describe(SecretRotations.DELETE(type).rotationId)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
deleteSecrets: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
.describe(SecretRotations.DELETE(type).deleteSecrets),
|
||||||
|
revokeGeneratedCredentials: z
|
||||||
|
.enum(["true", "false"])
|
||||||
|
.transform((value) => value === "true")
|
||||||
|
.describe(SecretRotations.DELETE(type).revokeGeneratedCredentials)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationId } = req.params;
|
||||||
|
const { deleteSecrets, revokeGeneratedCredentials } = req.query;
|
||||||
|
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.deleteSecretRotation(
|
||||||
|
{ type, rotationId, deleteSecrets, revokeGeneratedCredentials },
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId: secretRotation.projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.DELETE_SECRET_ROTATION,
|
||||||
|
metadata: {
|
||||||
|
type,
|
||||||
|
rotationId,
|
||||||
|
deleteSecrets,
|
||||||
|
revokeGeneratedCredentials
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:rotationId/generated-credentials",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Get the generated credentials for the specified ${rotationType} Rotation.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationId: z.string().uuid().describe(SecretRotations.GET_GENERATED_CREDENTIALS_BY_ID(type).rotationId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
generatedCredentials: generatedCredentialsSchema,
|
||||||
|
activeIndex: z.number(),
|
||||||
|
rotationId: z.string().uuid(),
|
||||||
|
type: z.literal(type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationId } = req.params;
|
||||||
|
|
||||||
|
const {
|
||||||
|
generatedCredentials,
|
||||||
|
secretRotation: { activeIndex, projectId, folder, environment }
|
||||||
|
} = await server.services.secretRotationV2.findSecretRotationGeneratedCredentialsById(
|
||||||
|
{
|
||||||
|
rotationId,
|
||||||
|
type
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
);
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_ROTATION_GENERATED_CREDENTIALS,
|
||||||
|
metadata: {
|
||||||
|
type,
|
||||||
|
rotationId,
|
||||||
|
secretPath: folder.path,
|
||||||
|
environment: environment.slug
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { generatedCredentials: generatedCredentials as C, activeIndex, rotationId, type };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:rotationId/rotate-secrets",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: `Rotate the generated credentials for the specified ${rotationType} Rotation.`,
|
||||||
|
params: z.object({
|
||||||
|
rotationId: z.string().uuid().describe(SecretRotations.ROTATE(type).rotationId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotation: responseSchema })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { rotationId } = req.params;
|
||||||
|
|
||||||
|
const secretRotation = (await server.services.secretRotationV2.rotateSecretRotation(
|
||||||
|
{
|
||||||
|
rotationId,
|
||||||
|
type,
|
||||||
|
auditLogInfo: req.auditLogInfo
|
||||||
|
},
|
||||||
|
req.permission
|
||||||
|
)) as T;
|
||||||
|
|
||||||
|
return { secretRotation };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -0,0 +1,81 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
|
||||||
|
import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials";
|
||||||
|
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
|
||||||
|
import { SecretRotations } from "@app/lib/api-docs";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
|
||||||
|
PostgresCredentialsRotationListItemSchema,
|
||||||
|
MsSqlCredentialsRotationListItemSchema
|
||||||
|
]);
|
||||||
|
|
||||||
|
export const registerSecretRotationV2Router = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/options",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "List the available Secret Rotation Options.",
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
secretRotationOptions: SecretRotationV2OptionsSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: () => {
|
||||||
|
const secretRotationOptions = server.services.secretRotationV2.listSecretRotationOptions();
|
||||||
|
return { secretRotationOptions };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "List all the Secret Rotations for the specified project.",
|
||||||
|
querystring: z.object({
|
||||||
|
projectId: z.string().trim().min(1, "Project ID required").describe(SecretRotations.LIST().projectId)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({ secretRotations: SecretRotationV2Schema.array() })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const {
|
||||||
|
query: { projectId },
|
||||||
|
permission
|
||||||
|
} = req;
|
||||||
|
|
||||||
|
const secretRotations = await server.services.secretRotationV2.listSecretRotationsByProjectId(
|
||||||
|
{ projectId },
|
||||||
|
permission
|
||||||
|
);
|
||||||
|
|
||||||
|
await server.services.auditLog.createAuditLog({
|
||||||
|
...req.auditLogInfo,
|
||||||
|
projectId,
|
||||||
|
event: {
|
||||||
|
type: EventType.GET_SECRET_ROTATIONS,
|
||||||
|
metadata: {
|
||||||
|
rotationIds: secretRotations.map((sync) => sync.id),
|
||||||
|
count: secretRotations.length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { secretRotations };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -65,7 +65,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
approvers,
|
approvers,
|
||||||
projectSlug,
|
projectSlug,
|
||||||
environment,
|
environment,
|
||||||
enforcementLevel
|
enforcementLevel,
|
||||||
|
allowedSelfApprovals
|
||||||
}: TCreateAccessApprovalPolicy) => {
|
}: TCreateAccessApprovalPolicy) => {
|
||||||
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
|
||||||
@@ -153,7 +154,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
approvals,
|
approvals,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
enforcementLevel
|
enforcementLevel,
|
||||||
|
allowedSelfApprovals
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
@@ -216,7 +218,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
approvals,
|
approvals,
|
||||||
enforcementLevel
|
enforcementLevel,
|
||||||
|
allowedSelfApprovals
|
||||||
}: TUpdateAccessApprovalPolicy) => {
|
}: TUpdateAccessApprovalPolicy) => {
|
||||||
const groupApprovers = approvers
|
const groupApprovers = approvers
|
||||||
.filter((approver) => approver.type === ApproverType.Group)
|
.filter((approver) => approver.type === ApproverType.Group)
|
||||||
@@ -262,7 +265,8 @@ export const accessApprovalPolicyServiceFactory = ({
|
|||||||
approvals,
|
approvals,
|
||||||
secretPath,
|
secretPath,
|
||||||
name,
|
name,
|
||||||
enforcementLevel
|
enforcementLevel,
|
||||||
|
allowedSelfApprovals
|
||||||
},
|
},
|
||||||
tx
|
tx
|
||||||
);
|
);
|
||||||
|
@@ -26,6 +26,7 @@ export type TCreateAccessApprovalPolicy = {
|
|||||||
projectSlug: string;
|
projectSlug: string;
|
||||||
name: string;
|
name: string;
|
||||||
enforcementLevel: EnforcementLevel;
|
enforcementLevel: EnforcementLevel;
|
||||||
|
allowedSelfApprovals: boolean;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TUpdateAccessApprovalPolicy = {
|
export type TUpdateAccessApprovalPolicy = {
|
||||||
@@ -35,6 +36,7 @@ export type TUpdateAccessApprovalPolicy = {
|
|||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
name?: string;
|
name?: string;
|
||||||
enforcementLevel?: EnforcementLevel;
|
enforcementLevel?: EnforcementLevel;
|
||||||
|
allowedSelfApprovals: boolean;
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
export type TDeleteAccessApprovalPolicy = {
|
export type TDeleteAccessApprovalPolicy = {
|
||||||
|
@@ -61,6 +61,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
db.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
||||||
db.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
db.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
||||||
db.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
|
db.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
|
||||||
|
db.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"),
|
||||||
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"),
|
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId"),
|
||||||
db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
db.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
||||||
)
|
)
|
||||||
@@ -119,6 +120,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
approvals: doc.policyApprovals,
|
approvals: doc.policyApprovals,
|
||||||
secretPath: doc.policySecretPath,
|
secretPath: doc.policySecretPath,
|
||||||
enforcementLevel: doc.policyEnforcementLevel,
|
enforcementLevel: doc.policyEnforcementLevel,
|
||||||
|
allowedSelfApprovals: doc.policyAllowedSelfApprovals,
|
||||||
envId: doc.policyEnvId,
|
envId: doc.policyEnvId,
|
||||||
deletedAt: doc.policyDeletedAt
|
deletedAt: doc.policyDeletedAt
|
||||||
},
|
},
|
||||||
@@ -254,6 +256,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
|
||||||
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
|
||||||
tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
|
tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
|
||||||
|
tx.ref("allowedSelfApprovals").withSchema(TableName.AccessApprovalPolicy).as("policyAllowedSelfApprovals"),
|
||||||
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
|
||||||
tx.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
tx.ref("deletedAt").withSchema(TableName.AccessApprovalPolicy).as("policyDeletedAt")
|
||||||
);
|
);
|
||||||
@@ -275,6 +278,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
|
|||||||
approvals: el.policyApprovals,
|
approvals: el.policyApprovals,
|
||||||
secretPath: el.policySecretPath,
|
secretPath: el.policySecretPath,
|
||||||
enforcementLevel: el.policyEnforcementLevel,
|
enforcementLevel: el.policyEnforcementLevel,
|
||||||
|
allowedSelfApprovals: el.policyAllowedSelfApprovals,
|
||||||
deletedAt: el.policyDeletedAt
|
deletedAt: el.policyDeletedAt
|
||||||
},
|
},
|
||||||
requestedByUser: {
|
requestedByUser: {
|
||||||
|
@@ -1,9 +1,10 @@
|
|||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import msFn from "ms";
|
||||||
|
|
||||||
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
@@ -246,7 +247,7 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
requesterEmail: requestedByUser.email,
|
requesterEmail: requestedByUser.email,
|
||||||
isTemporary,
|
isTemporary,
|
||||||
...(isTemporary && {
|
...(isTemporary && {
|
||||||
expiresIn: ms(ms(temporaryRange || ""), { long: true })
|
expiresIn: msFn(ms(temporaryRange || ""), { long: true })
|
||||||
}),
|
}),
|
||||||
secretPath,
|
secretPath,
|
||||||
environment: envSlug,
|
environment: envSlug,
|
||||||
@@ -319,6 +320,11 @@ export const accessApprovalRequestServiceFactory = ({
|
|||||||
message: "The policy associated with this access request has been deleted."
|
message: "The policy associated with this access request has been deleted."
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (!policy.allowedSelfApprovals && actorId === accessApprovalRequest.requestedByUserId) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { membership, hasRole } = await permissionService.getProjectPermission({
|
const { membership, hasRole } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
|
@@ -45,7 +45,6 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
}: TCreateAuditLogStreamDTO) => {
|
}: TCreateAuditLogStreamDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID attached to authentication token" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID attached to authentication token" });
|
||||||
|
|
||||||
const appCfg = getConfig();
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
if (!plan.auditLogStreams) {
|
if (!plan.auditLogStreams) {
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
@@ -62,9 +61,8 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
if (appCfg.isCloud) {
|
const appCfg = getConfig();
|
||||||
blockLocalAndPrivateIpAddresses(url);
|
if (appCfg.isCloud) await blockLocalAndPrivateIpAddresses(url);
|
||||||
}
|
|
||||||
|
|
||||||
const totalStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
const totalStreams = await auditLogStreamDAL.find({ orgId: actorOrgId });
|
||||||
if (totalStreams.length >= plan.auditLogStreamLimit) {
|
if (totalStreams.length >= plan.auditLogStreamLimit) {
|
||||||
@@ -135,9 +133,8 @@ export const auditLogStreamServiceFactory = ({
|
|||||||
const { orgId } = logStream;
|
const { orgId } = logStream;
|
||||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
if (url && appCfg.isCloud) blockLocalAndPrivateIpAddresses(url);
|
if (url && appCfg.isCloud) await blockLocalAndPrivateIpAddresses(url);
|
||||||
|
|
||||||
// testing connection first
|
// testing connection first
|
||||||
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
|
const streamHeaders: RawAxiosRequestHeaders = { "Content-Type": "application/json" };
|
||||||
|
@@ -9,13 +9,14 @@ import { logger } from "@app/lib/logger";
|
|||||||
import { QueueName } from "@app/queue";
|
import { QueueName } from "@app/queue";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { EventType } from "./audit-log-types";
|
import { EventType, filterableSecretEvents } from "./audit-log-types";
|
||||||
|
|
||||||
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
export type TAuditLogDALFactory = ReturnType<typeof auditLogDALFactory>;
|
||||||
|
|
||||||
type TFindQuery = {
|
type TFindQuery = {
|
||||||
actor?: string;
|
actor?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
environment?: string;
|
||||||
orgId?: string;
|
orgId?: string;
|
||||||
eventType?: string;
|
eventType?: string;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
@@ -32,6 +33,7 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
{
|
{
|
||||||
orgId,
|
orgId,
|
||||||
projectId,
|
projectId,
|
||||||
|
environment,
|
||||||
userAgentType,
|
userAgentType,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
@@ -40,12 +42,14 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
actorId,
|
actorId,
|
||||||
actorType,
|
actorType,
|
||||||
secretPath,
|
secretPath,
|
||||||
|
secretKey,
|
||||||
eventType,
|
eventType,
|
||||||
eventMetadata
|
eventMetadata
|
||||||
}: Omit<TFindQuery, "actor" | "eventType"> & {
|
}: Omit<TFindQuery, "actor" | "eventType"> & {
|
||||||
actorId?: string;
|
actorId?: string;
|
||||||
actorType?: ActorType;
|
actorType?: ActorType;
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
secretKey?: string;
|
||||||
eventType?: EventType[];
|
eventType?: EventType[];
|
||||||
eventMetadata?: Record<string, string>;
|
eventMetadata?: Record<string, string>;
|
||||||
},
|
},
|
||||||
@@ -90,8 +94,29 @@ export const auditLogDALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (projectId && secretPath) {
|
const eventIsSecretType = !eventType?.length || eventType.some((event) => filterableSecretEvents.includes(event));
|
||||||
void sqlQuery.whereRaw(`"eventMetadata" @> jsonb_build_object('secretPath', ?::text)`, [secretPath]);
|
// We only want to filter for environment/secretPath/secretKey if the user is either checking for all event types
|
||||||
|
|
||||||
|
// ? Note(daniel): use the `eventMetadata" @> ?::jsonb` approach to properly use our GIN index
|
||||||
|
if (projectId && eventIsSecretType) {
|
||||||
|
if (environment || secretPath) {
|
||||||
|
// Handle both environment and secret path together to only use the GIN index once
|
||||||
|
void sqlQuery.whereRaw(`"eventMetadata" @> ?::jsonb`, [
|
||||||
|
JSON.stringify({
|
||||||
|
...(environment && { environment }),
|
||||||
|
...(secretPath && { secretPath })
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle secret key separately to include the OR condition
|
||||||
|
if (secretKey) {
|
||||||
|
void sqlQuery.whereRaw(
|
||||||
|
`("eventMetadata" @> ?::jsonb
|
||||||
|
OR "eventMetadata"->'secrets' @> ?::jsonb)`,
|
||||||
|
[JSON.stringify({ secretKey }), JSON.stringify([{ secretKey }])]
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter by actor type
|
// Filter by actor type
|
||||||
|
@@ -1,8 +1,10 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { requestContext } from "@fastify/request-context";
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
@@ -61,6 +63,8 @@ export const auditLogServiceFactory = ({
|
|||||||
actorType: filter.actorType,
|
actorType: filter.actorType,
|
||||||
eventMetadata: filter.eventMetadata,
|
eventMetadata: filter.eventMetadata,
|
||||||
secretPath: filter.secretPath,
|
secretPath: filter.secretPath,
|
||||||
|
secretKey: filter.secretKey,
|
||||||
|
environment: filter.environment,
|
||||||
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
|
...(filter.projectId ? { projectId: filter.projectId } : { orgId: actorOrgId })
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -81,8 +85,12 @@ export const auditLogServiceFactory = ({
|
|||||||
if (!data.projectId && !data.orgId)
|
if (!data.projectId && !data.orgId)
|
||||||
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
throw new BadRequestError({ message: "Must specify either project id or org id" });
|
||||||
}
|
}
|
||||||
|
const el = { ...data };
|
||||||
return auditLogQueue.pushToLog(data);
|
if (el.actor.type === ActorType.USER || el.actor.type === ActorType.IDENTITY) {
|
||||||
|
const permissionMetadata = requestContext.get("identityPermissionMetadata");
|
||||||
|
el.actor.metadata.permission = permissionMetadata;
|
||||||
|
}
|
||||||
|
return auditLogQueue.pushToLog(el);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@@ -2,6 +2,13 @@ import {
|
|||||||
TCreateProjectTemplateDTO,
|
TCreateProjectTemplateDTO,
|
||||||
TUpdateProjectTemplateDTO
|
TUpdateProjectTemplateDTO
|
||||||
} from "@app/ee/services/project-template/project-template-types";
|
} from "@app/ee/services/project-template/project-template-types";
|
||||||
|
import { SecretRotation, SecretRotationStatus } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||||
|
import {
|
||||||
|
TCreateSecretRotationV2DTO,
|
||||||
|
TDeleteSecretRotationV2DTO,
|
||||||
|
TSecretRotationV2Raw,
|
||||||
|
TUpdateSecretRotationV2DTO
|
||||||
|
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
|
||||||
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
|
||||||
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
|
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
|
||||||
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
import { SymmetricEncryption } from "@app/lib/crypto/cipher";
|
||||||
@@ -33,9 +40,11 @@ export type TListProjectAuditLogDTO = {
|
|||||||
endDate?: string;
|
endDate?: string;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
projectId?: string;
|
projectId?: string;
|
||||||
|
environment?: string;
|
||||||
auditLogActorId?: string;
|
auditLogActorId?: string;
|
||||||
actorType?: ActorType;
|
actorType?: ActorType;
|
||||||
secretPath?: string;
|
secretPath?: string;
|
||||||
|
secretKey?: string;
|
||||||
eventMetadata?: Record<string, string>;
|
eventMetadata?: Record<string, string>;
|
||||||
};
|
};
|
||||||
} & Omit<TProjectPermission, "projectId">;
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
@@ -54,6 +63,8 @@ export type TCreateAuditLogDTO = {
|
|||||||
projectId?: string;
|
projectId?: string;
|
||||||
} & BaseAuthData;
|
} & BaseAuthData;
|
||||||
|
|
||||||
|
export type AuditLogInfo = Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
|
||||||
|
|
||||||
interface BaseAuthData {
|
interface BaseAuthData {
|
||||||
ipAddress?: string;
|
ipAddress?: string;
|
||||||
userAgent?: string;
|
userAgent?: string;
|
||||||
@@ -283,13 +294,34 @@ export enum EventType {
|
|||||||
KMIP_OPERATION_ACTIVATE = "kmip-operation-activate",
|
KMIP_OPERATION_ACTIVATE = "kmip-operation-activate",
|
||||||
KMIP_OPERATION_REVOKE = "kmip-operation-revoke",
|
KMIP_OPERATION_REVOKE = "kmip-operation-revoke",
|
||||||
KMIP_OPERATION_LOCATE = "kmip-operation-locate",
|
KMIP_OPERATION_LOCATE = "kmip-operation-locate",
|
||||||
KMIP_OPERATION_REGISTER = "kmip-operation-register"
|
KMIP_OPERATION_REGISTER = "kmip-operation-register",
|
||||||
|
|
||||||
|
GET_SECRET_ROTATIONS = "get-secret-rotations",
|
||||||
|
GET_SECRET_ROTATION = "get-secret-rotation",
|
||||||
|
GET_SECRET_ROTATION_GENERATED_CREDENTIALS = "get-secret-rotation-generated-credentials",
|
||||||
|
CREATE_SECRET_ROTATION = "create-secret-rotation",
|
||||||
|
UPDATE_SECRET_ROTATION = "update-secret-rotation",
|
||||||
|
DELETE_SECRET_ROTATION = "delete-secret-rotation",
|
||||||
|
SECRET_ROTATION_ROTATE_SECRETS = "secret-rotation-rotate-secrets",
|
||||||
|
|
||||||
|
PROJECT_ACCESS_REQUEST = "project-access-request"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const filterableSecretEvents: EventType[] = [
|
||||||
|
EventType.GET_SECRET,
|
||||||
|
EventType.DELETE_SECRETS,
|
||||||
|
EventType.CREATE_SECRETS,
|
||||||
|
EventType.UPDATE_SECRETS,
|
||||||
|
EventType.CREATE_SECRET,
|
||||||
|
EventType.UPDATE_SECRET,
|
||||||
|
EventType.DELETE_SECRET
|
||||||
|
];
|
||||||
|
|
||||||
interface UserActorMetadata {
|
interface UserActorMetadata {
|
||||||
userId: string;
|
userId: string;
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
username: string;
|
username: string;
|
||||||
|
permission?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ServiceActorMetadata {
|
interface ServiceActorMetadata {
|
||||||
@@ -300,6 +332,7 @@ interface ServiceActorMetadata {
|
|||||||
interface IdentityActorMetadata {
|
interface IdentityActorMetadata {
|
||||||
identityId: string;
|
identityId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
permission?: Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ScimClientActorMetadata {}
|
interface ScimClientActorMetadata {}
|
||||||
@@ -966,6 +999,7 @@ interface LoginIdentityOidcAuthEvent {
|
|||||||
identityId: string;
|
identityId: string;
|
||||||
identityOidcAuthId: string;
|
identityOidcAuthId: string;
|
||||||
identityAccessTokenId: string;
|
identityAccessTokenId: string;
|
||||||
|
oidcClaimsReceived: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -978,6 +1012,7 @@ interface AddIdentityOidcAuthEvent {
|
|||||||
boundIssuer: string;
|
boundIssuer: string;
|
||||||
boundAudiences: string;
|
boundAudiences: string;
|
||||||
boundClaims: Record<string, string>;
|
boundClaims: Record<string, string>;
|
||||||
|
claimMetadataMapping: Record<string, string>;
|
||||||
boundSubject: string;
|
boundSubject: string;
|
||||||
accessTokenTTL: number;
|
accessTokenTTL: number;
|
||||||
accessTokenMaxTTL: number;
|
accessTokenMaxTTL: number;
|
||||||
@@ -1002,6 +1037,7 @@ interface UpdateIdentityOidcAuthEvent {
|
|||||||
boundIssuer?: string;
|
boundIssuer?: string;
|
||||||
boundAudiences?: string;
|
boundAudiences?: string;
|
||||||
boundClaims?: Record<string, string>;
|
boundClaims?: Record<string, string>;
|
||||||
|
claimMetadataMapping?: Record<string, string>;
|
||||||
boundSubject?: string;
|
boundSubject?: string;
|
||||||
accessTokenTTL?: number;
|
accessTokenTTL?: number;
|
||||||
accessTokenMaxTTL?: number;
|
accessTokenMaxTTL?: number;
|
||||||
@@ -2260,6 +2296,15 @@ interface KmipOperationRegisterEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProjectAccessRequestEvent {
|
||||||
|
type: EventType.PROJECT_ACCESS_REQUEST;
|
||||||
|
metadata: {
|
||||||
|
projectId: string;
|
||||||
|
requesterId: string;
|
||||||
|
requesterEmail: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
interface SetupKmipEvent {
|
interface SetupKmipEvent {
|
||||||
type: EventType.SETUP_KMIP;
|
type: EventType.SETUP_KMIP;
|
||||||
metadata: {
|
metadata: {
|
||||||
@@ -2285,6 +2330,63 @@ interface RegisterKmipServerEvent {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GetSecretRotationsEvent {
|
||||||
|
type: EventType.GET_SECRET_ROTATIONS;
|
||||||
|
metadata: {
|
||||||
|
type?: SecretRotation;
|
||||||
|
count: number;
|
||||||
|
rotationIds: string[];
|
||||||
|
secretPath?: string;
|
||||||
|
environment?: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetSecretRotationEvent {
|
||||||
|
type: EventType.GET_SECRET_ROTATION;
|
||||||
|
metadata: {
|
||||||
|
type: SecretRotation;
|
||||||
|
rotationId: string;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GetSecretRotationCredentialsEvent {
|
||||||
|
type: EventType.GET_SECRET_ROTATION_GENERATED_CREDENTIALS;
|
||||||
|
metadata: {
|
||||||
|
type: SecretRotation;
|
||||||
|
rotationId: string;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateSecretRotationEvent {
|
||||||
|
type: EventType.CREATE_SECRET_ROTATION;
|
||||||
|
metadata: Omit<TCreateSecretRotationV2DTO, "projectId"> & { rotationId: string };
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UpdateSecretRotationEvent {
|
||||||
|
type: EventType.UPDATE_SECRET_ROTATION;
|
||||||
|
metadata: TUpdateSecretRotationV2DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface DeleteSecretRotationEvent {
|
||||||
|
type: EventType.DELETE_SECRET_ROTATION;
|
||||||
|
metadata: TDeleteSecretRotationV2DTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RotateSecretRotationEvent {
|
||||||
|
type: EventType.SECRET_ROTATION_ROTATE_SECRETS;
|
||||||
|
metadata: Pick<TSecretRotationV2Raw, "parameters" | "secretsMapping" | "type" | "connectionId" | "folderId"> & {
|
||||||
|
status: SecretRotationStatus;
|
||||||
|
rotationId: string;
|
||||||
|
jobId?: string | undefined;
|
||||||
|
occurredAt: Date;
|
||||||
|
message?: string | null | undefined;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export type Event =
|
export type Event =
|
||||||
| GetSecretsEvent
|
| GetSecretsEvent
|
||||||
| GetSecretEvent
|
| GetSecretEvent
|
||||||
@@ -2494,5 +2596,13 @@ export type Event =
|
|||||||
| KmipOperationRevokeEvent
|
| KmipOperationRevokeEvent
|
||||||
| KmipOperationLocateEvent
|
| KmipOperationLocateEvent
|
||||||
| KmipOperationRegisterEvent
|
| KmipOperationRegisterEvent
|
||||||
|
| ProjectAccessRequestEvent
|
||||||
| CreateSecretRequestEvent
|
| CreateSecretRequestEvent
|
||||||
| SecretApprovalRequestReview;
|
| SecretApprovalRequestReview
|
||||||
|
| GetSecretRotationsEvent
|
||||||
|
| GetSecretRotationEvent
|
||||||
|
| GetSecretRotationCredentialsEvent
|
||||||
|
| CreateSecretRotationEvent
|
||||||
|
| UpdateSecretRotationEvent
|
||||||
|
| DeleteSecretRotationEvent
|
||||||
|
| RotateSecretRotationEvent;
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
|
|
||||||
|
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
|
||||||
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { isCertChainValid } from "@app/services/certificate/certificate-fns";
|
import { isCertChainValid } from "@app/services/certificate/certificate-fns";
|
||||||
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
|
||||||
@@ -67,9 +68,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
|
|
||||||
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
|
const certTemplate = await certificateTemplateDAL.findById(certificateTemplateId);
|
||||||
|
|
||||||
const leafCertificate = decodeURIComponent(sslClientCert).match(
|
const leafCertificate = extractX509CertFromChain(decodeURIComponent(sslClientCert))?.[0];
|
||||||
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
|
||||||
)?.[0];
|
|
||||||
|
|
||||||
if (!leafCertificate) {
|
if (!leafCertificate) {
|
||||||
throw new UnauthorizedError({ message: "Missing client certificate" });
|
throw new UnauthorizedError({ message: "Missing client certificate" });
|
||||||
@@ -88,10 +87,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
const verifiedChains = await Promise.all(
|
const verifiedChains = await Promise.all(
|
||||||
caCertChains.map((chain) => {
|
caCertChains.map((chain) => {
|
||||||
const caCert = new x509.X509Certificate(chain.certificate);
|
const caCert = new x509.X509Certificate(chain.certificate);
|
||||||
const caChain =
|
const caChain = extractX509CertFromChain(chain.certificateChain)?.map((c) => new x509.X509Certificate(c)) || [];
|
||||||
chain.certificateChain
|
|
||||||
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
|
||||||
?.map((c) => new x509.X509Certificate(c)) || [];
|
|
||||||
|
|
||||||
return isCertChainValid([cert, caCert, ...caChain]);
|
return isCertChainValid([cert, caCert, ...caChain]);
|
||||||
})
|
})
|
||||||
@@ -172,9 +168,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!estConfig.disableBootstrapCertValidation) {
|
if (!estConfig.disableBootstrapCertValidation) {
|
||||||
const caCerts = estConfig.caChain
|
const caCerts = extractX509CertFromChain(estConfig.caChain)?.map((cert) => {
|
||||||
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
|
||||||
?.map((cert) => {
|
|
||||||
return new x509.X509Certificate(cert);
|
return new x509.X509Certificate(cert);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,9 +176,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const leafCertificate = decodeURIComponent(sslClientCert).match(
|
const leafCertificate = extractX509CertFromChain(decodeURIComponent(sslClientCert))?.[0];
|
||||||
/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g
|
|
||||||
)?.[0];
|
|
||||||
|
|
||||||
if (!leafCertificate) {
|
if (!leafCertificate) {
|
||||||
throw new BadRequestError({ message: "Missing client certificate" });
|
throw new BadRequestError({ message: "Missing client certificate" });
|
||||||
@@ -250,13 +242,7 @@ export const certificateEstServiceFactory = ({
|
|||||||
kmsService
|
kmsService
|
||||||
});
|
});
|
||||||
|
|
||||||
const certificates = caCertChain
|
const certificates = extractX509CertFromChain(caCertChain).map((cert) => new x509.X509Certificate(cert));
|
||||||
.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)
|
|
||||||
?.map((cert) => new x509.X509Certificate(cert));
|
|
||||||
|
|
||||||
if (!certificates) {
|
|
||||||
throw new BadRequestError({ message: "Failed to parse certificate chain" });
|
|
||||||
}
|
|
||||||
|
|
||||||
const caCertificate = new x509.X509Certificate(caCert);
|
const caCertificate = new x509.X509Certificate(caCert);
|
||||||
return convertRawCertsToPkcs7([caCertificate.rawData, ...certificates.map((cert) => cert.rawData)]);
|
return convertRawCertsToPkcs7([caCertificate.rawData, ...certificates.map((cert) => cert.rawData)]);
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@@ -11,6 +10,7 @@ import {
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
@@ -183,7 +183,7 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
|
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
|
||||||
if (!dynamicSecretLease) {
|
if (!dynamicSecretLease || dynamicSecretLease.dynamicSecret.folderId !== folder.id) {
|
||||||
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
|
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,7 +256,7 @@ export const dynamicSecretLeaseServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
|
const dynamicSecretLease = await dynamicSecretLeaseDAL.findById(leaseId);
|
||||||
if (!dynamicSecretLease)
|
if (!dynamicSecretLease || dynamicSecretLease.dynamicSecret.folderId !== folder.id)
|
||||||
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
|
throw new NotFoundError({ message: `Dynamic secret lease with ID '${leaseId}' not found` });
|
||||||
|
|
||||||
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
|
const dynamicSecretCfg = dynamicSecretLease.dynamicSecret;
|
||||||
|
@@ -1,31 +1,53 @@
|
|||||||
import crypto from "node:crypto";
|
import dns from "node:dns/promises";
|
||||||
|
import net from "node:net";
|
||||||
|
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { isPrivateIp } from "@app/lib/ip/ipRange";
|
||||||
import { getDbConnectionHost } from "@app/lib/knex";
|
import { getDbConnectionHost } from "@app/lib/knex";
|
||||||
|
|
||||||
export const verifyHostInputValidity = (host: string, isGateway = false) => {
|
export const verifyHostInputValidity = async (host: string, isGateway = false) => {
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const dbHost = appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI);
|
|
||||||
// no need for validation when it's dev
|
|
||||||
if (appCfg.NODE_ENV === "development") return;
|
|
||||||
|
|
||||||
if (host === "host.docker.internal") throw new BadRequestError({ message: "Invalid db host" });
|
if (appCfg.isDevelopmentMode) return [host];
|
||||||
|
|
||||||
if (
|
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
|
||||||
appCfg.isCloud &&
|
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
|
||||||
!isGateway &&
|
getDbConnectionHost(appCfg.REDIS_URL),
|
||||||
// localhost
|
getDbConnectionHost(appCfg.AUDIT_LOGS_DB_CONNECTION_URI)
|
||||||
// internal ips
|
);
|
||||||
(host.match(/^10\.\d+\.\d+\.\d+/) || host.match(/^192\.168\.\d+\.\d+/))
|
|
||||||
)
|
|
||||||
throw new BadRequestError({ message: "Invalid db host" });
|
|
||||||
|
|
||||||
if (
|
// get host db ip
|
||||||
host === "localhost" ||
|
const exclusiveIps: string[] = [];
|
||||||
host === "127.0.0.1" ||
|
for await (const el of reservedHosts) {
|
||||||
(dbHost?.length === host.length && crypto.timingSafeEqual(Buffer.from(dbHost || ""), Buffer.from(host)))
|
if (el) {
|
||||||
) {
|
if (net.isIPv4(el)) {
|
||||||
|
exclusiveIps.push(el);
|
||||||
|
} else {
|
||||||
|
const resolvedIps = await dns.resolve4(el);
|
||||||
|
exclusiveIps.push(...resolvedIps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizedHost = host.split(":")[0];
|
||||||
|
const inputHostIps: string[] = [];
|
||||||
|
if (net.isIPv4(host)) {
|
||||||
|
inputHostIps.push(host);
|
||||||
|
} else {
|
||||||
|
if (normalizedHost === "localhost" || normalizedHost === "host.docker.internal") {
|
||||||
throw new BadRequestError({ message: "Invalid db host" });
|
throw new BadRequestError({ message: "Invalid db host" });
|
||||||
}
|
}
|
||||||
|
const resolvedIps = await dns.resolve4(host);
|
||||||
|
inputHostIps.push(...resolvedIps);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isGateway && !appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP) {
|
||||||
|
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
|
||||||
|
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
|
||||||
|
}
|
||||||
|
|
||||||
|
const isAppUsedIps = inputHostIps.some((el) => exclusiveIps.includes(el));
|
||||||
|
if (isAppUsedIps) throw new BadRequestError({ message: "Invalid db host" });
|
||||||
|
return inputHostIps;
|
||||||
};
|
};
|
||||||
|
@@ -13,6 +13,7 @@ import { customAlphabet } from "nanoid";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretAwsElastiCacheSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
@@ -144,6 +145,14 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
|
|||||||
// We can't return the parsed statements here because we need to use the handlebars template to generate the username and password, before we can use the parsed statements.
|
// We can't return the parsed statements here because we need to use the handlebars template to generate the username and password, before we can use the parsed statements.
|
||||||
CreateElastiCacheUserSchema.parse(JSON.parse(providerInputs.creationStatement));
|
CreateElastiCacheUserSchema.parse(JSON.parse(providerInputs.creationStatement));
|
||||||
DeleteElasticCacheUserSchema.parse(JSON.parse(providerInputs.revocationStatement));
|
DeleteElasticCacheUserSchema.parse(JSON.parse(providerInputs.revocationStatement));
|
||||||
|
validateHandlebarTemplate("AWS ElastiCache creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.revocationStatement) {
|
||||||
|
validateHandlebarTemplate("AWS ElastiCache revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return providerInputs;
|
return providerInputs;
|
||||||
};
|
};
|
||||||
|
@@ -3,9 +3,10 @@ import handlebars from "handlebars";
|
|||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretCassandraSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
const generatePassword = (size = 48) => {
|
const generatePassword = (size = 48) => {
|
||||||
@@ -20,14 +21,28 @@ const generateUsername = () => {
|
|||||||
export const CassandraProvider = (): TDynamicProviderFns => {
|
export const CassandraProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretCassandraSchema.parseAsync(inputs);
|
||||||
if (providerInputs.host === "localhost" || providerInputs.host === "127.0.0.1") {
|
const hostIps = await Promise.all(
|
||||||
throw new BadRequestError({ message: "Invalid db host" });
|
providerInputs.host
|
||||||
|
.split(",")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((el) => verifyHostInputValidity(el).then((ip) => ip[0]))
|
||||||
|
);
|
||||||
|
validateHandlebarTemplate("Cassandra creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration", "keyspace"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.renewStatement) {
|
||||||
|
validateHandlebarTemplate("Cassandra renew", providerInputs.renewStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "expiration", "keyspace"].includes(val)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
validateHandlebarTemplate("Cassandra revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
|
||||||
return providerInputs;
|
return { ...providerInputs, hostIps };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretCassandraSchema> & { hostIps: string[] }) => {
|
||||||
const sslOptions = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
|
const sslOptions = providerInputs.ca ? { rejectUnauthorized: false, ca: providerInputs.ca } : undefined;
|
||||||
const client = new cassandra.Client({
|
const client = new cassandra.Client({
|
||||||
sslOptions,
|
sslOptions,
|
||||||
@@ -40,7 +55,7 @@ export const CassandraProvider = (): TDynamicProviderFns => {
|
|||||||
},
|
},
|
||||||
keyspace: providerInputs.keyspace,
|
keyspace: providerInputs.keyspace,
|
||||||
localDataCenter: providerInputs?.localDataCenter,
|
localDataCenter: providerInputs?.localDataCenter,
|
||||||
contactPoints: providerInputs.host.split(",").filter(Boolean)
|
contactPoints: providerInputs.hostIps
|
||||||
});
|
});
|
||||||
return client;
|
return client;
|
||||||
};
|
};
|
||||||
|
@@ -19,15 +19,14 @@ const generateUsername = () => {
|
|||||||
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
export const ElasticSearchProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretElasticSearchSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretElasticSearchSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
return providerInputs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretElasticSearchSchema> & { hostIp: string }) => {
|
||||||
const connection = new ElasticSearchClient({
|
const connection = new ElasticSearchClient({
|
||||||
node: {
|
node: {
|
||||||
url: new URL(`${providerInputs.host}:${providerInputs.port}`),
|
url: new URL(`${providerInputs.hostIp}:${providerInputs.port}`),
|
||||||
...(providerInputs.ca && {
|
...(providerInputs.ca && {
|
||||||
ssl: {
|
ssl: {
|
||||||
rejectUnauthorized: false,
|
rejectUnauthorized: false,
|
||||||
|
@@ -1,5 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
export type PasswordRequirements = {
|
||||||
|
length: number;
|
||||||
|
required: {
|
||||||
|
lowercase: number;
|
||||||
|
uppercase: number;
|
||||||
|
digits: number;
|
||||||
|
symbols: number;
|
||||||
|
};
|
||||||
|
allowedSymbols?: string;
|
||||||
|
};
|
||||||
|
|
||||||
export enum SqlProviders {
|
export enum SqlProviders {
|
||||||
Postgres = "postgres",
|
Postgres = "postgres",
|
||||||
MySQL = "mysql2",
|
MySQL = "mysql2",
|
||||||
@@ -100,6 +111,28 @@ export const DynamicSecretSqlDBSchema = z.object({
|
|||||||
database: z.string().trim(),
|
database: z.string().trim(),
|
||||||
username: z.string().trim(),
|
username: z.string().trim(),
|
||||||
password: z.string().trim(),
|
password: z.string().trim(),
|
||||||
|
passwordRequirements: z
|
||||||
|
.object({
|
||||||
|
length: z.number().min(1).max(250),
|
||||||
|
required: z
|
||||||
|
.object({
|
||||||
|
lowercase: z.number().min(0),
|
||||||
|
uppercase: z.number().min(0),
|
||||||
|
digits: z.number().min(0),
|
||||||
|
symbols: z.number().min(0)
|
||||||
|
})
|
||||||
|
.refine((data) => {
|
||||||
|
const total = Object.values(data).reduce((sum, count) => sum + count, 0);
|
||||||
|
return total <= 250;
|
||||||
|
}, "Sum of required characters cannot exceed 250"),
|
||||||
|
allowedSymbols: z.string().optional()
|
||||||
|
})
|
||||||
|
.refine((data) => {
|
||||||
|
const total = Object.values(data.required).reduce((sum, count) => sum + count, 0);
|
||||||
|
return total <= data.length;
|
||||||
|
}, "Sum of required characters cannot exceed the total length")
|
||||||
|
.optional()
|
||||||
|
.describe("Password generation requirements"),
|
||||||
creationStatement: z.string().trim(),
|
creationStatement: z.string().trim(),
|
||||||
revocationStatement: z.string().trim(),
|
revocationStatement: z.string().trim(),
|
||||||
renewStatement: z.string().trim().optional(),
|
renewStatement: z.string().trim().optional(),
|
||||||
|
@@ -19,15 +19,15 @@ const generateUsername = () => {
|
|||||||
export const MongoDBProvider = (): TDynamicProviderFns => {
|
export const MongoDBProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretMongoDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretMongoDBSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
return providerInputs;
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretMongoDBSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretMongoDBSchema> & { hostIp: string }) => {
|
||||||
const isSrv = !providerInputs.port;
|
const isSrv = !providerInputs.port;
|
||||||
const uri = isSrv
|
const uri = isSrv
|
||||||
? `mongodb+srv://${providerInputs.host}`
|
? `mongodb+srv://${providerInputs.hostIp}`
|
||||||
: `mongodb://${providerInputs.host}:${providerInputs.port}`;
|
: `mongodb://${providerInputs.hostIp}:${providerInputs.port}`;
|
||||||
|
|
||||||
const client = new MongoClient(uri, {
|
const client = new MongoClient(uri, {
|
||||||
auth: {
|
auth: {
|
||||||
|
@@ -3,7 +3,6 @@ import https from "https";
|
|||||||
import { customAlphabet } from "nanoid";
|
import { customAlphabet } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
@@ -79,14 +78,13 @@ async function deleteRabbitMqUser({ axiosInstance, usernameToDelete }: TDeleteRa
|
|||||||
export const RabbitMqProvider = (): TDynamicProviderFns => {
|
export const RabbitMqProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretRabbitMqSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretRabbitMqSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
return providerInputs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRabbitMqSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRabbitMqSchema> & { hostIp: string }) => {
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
baseURL: `${removeTrailingSlash(providerInputs.host)}:${providerInputs.port}/api`,
|
baseURL: `${providerInputs.hostIp}:${providerInputs.port}/api`,
|
||||||
auth: {
|
auth: {
|
||||||
username: providerInputs.username,
|
username: providerInputs.username,
|
||||||
password: providerInputs.password
|
password: providerInputs.password
|
||||||
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretRedisDBSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretRedisDBSchema, TDynamicProviderFns } from "./models";
|
||||||
@@ -51,16 +52,28 @@ const executeTransactions = async (connection: Redis, commands: string[]): Promi
|
|||||||
export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
export const RedisDatabaseProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretRedisDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretRedisDBSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
return providerInputs;
|
validateHandlebarTemplate("Redis creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.renewStatement) {
|
||||||
|
validateHandlebarTemplate("Redis renew", providerInputs.renewStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validateHandlebarTemplate("Redis revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRedisDBSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretRedisDBSchema> & { hostIp: string }) => {
|
||||||
let connection: Redis | null = null;
|
let connection: Redis | null = null;
|
||||||
try {
|
try {
|
||||||
connection = new Redis({
|
connection = new Redis({
|
||||||
username: providerInputs.username,
|
username: providerInputs.username,
|
||||||
host: providerInputs.host,
|
host: providerInputs.hostIp,
|
||||||
port: providerInputs.port,
|
port: providerInputs.port,
|
||||||
password: providerInputs.password,
|
password: providerInputs.password,
|
||||||
...(providerInputs.ca && {
|
...(providerInputs.ca && {
|
||||||
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretSapAseSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretSapAseSchema, TDynamicProviderFns } from "./models";
|
||||||
@@ -27,14 +28,25 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
|||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSapAseSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSapAseSchema.parseAsync(inputs);
|
||||||
|
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
return providerInputs;
|
validateHandlebarTemplate("SAP ASE creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.revocationStatement) {
|
||||||
|
validateHandlebarTemplate("SAP ASE revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapAseSchema>, useMaster?: boolean) => {
|
const $getClient = async (
|
||||||
|
providerInputs: z.infer<typeof DynamicSecretSapAseSchema> & { hostIp: string },
|
||||||
|
useMaster?: boolean
|
||||||
|
) => {
|
||||||
const connectionString =
|
const connectionString =
|
||||||
`DRIVER={FreeTDS};` +
|
`DRIVER={FreeTDS};` +
|
||||||
`SERVER=${providerInputs.host};` +
|
`SERVER=${providerInputs.hostIp};` +
|
||||||
`PORT=${providerInputs.port};` +
|
`PORT=${providerInputs.port};` +
|
||||||
`DATABASE=${useMaster ? "master" : providerInputs.database};` +
|
`DATABASE=${useMaster ? "master" : providerInputs.database};` +
|
||||||
`UID=${providerInputs.username};` +
|
`UID=${providerInputs.username};` +
|
||||||
@@ -83,7 +95,7 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
|||||||
password
|
password
|
||||||
});
|
});
|
||||||
|
|
||||||
const queries = creationStatement.trim().replace(/\n/g, "").split(";").filter(Boolean);
|
const queries = creationStatement.trim().replaceAll("\n", "").split(";").filter(Boolean);
|
||||||
|
|
||||||
for await (const query of queries) {
|
for await (const query of queries) {
|
||||||
// If it's an adduser query, we need to first call sp_addlogin on the MASTER database.
|
// If it's an adduser query, we need to first call sp_addlogin on the MASTER database.
|
||||||
@@ -104,7 +116,7 @@ export const SapAseProvider = (): TDynamicProviderFns => {
|
|||||||
username
|
username
|
||||||
});
|
});
|
||||||
|
|
||||||
const queries = revokeStatement.trim().replace(/\n/g, "").split(";").filter(Boolean);
|
const queries = revokeStatement.trim().replaceAll("\n", "").split(";").filter(Boolean);
|
||||||
|
|
||||||
const client = await $getClient(providerInputs);
|
const client = await $getClient(providerInputs);
|
||||||
const masterClient = await $getClient(providerInputs, true);
|
const masterClient = await $getClient(providerInputs, true);
|
||||||
|
@@ -11,6 +11,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretSapHanaSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretSapHanaSchema, TDynamicProviderFns } from "./models";
|
||||||
@@ -28,13 +29,24 @@ export const SapHanaProvider = (): TDynamicProviderFns => {
|
|||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSapHanaSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSapHanaSchema.parseAsync(inputs);
|
||||||
|
|
||||||
verifyHostInputValidity(providerInputs.host);
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host);
|
||||||
return providerInputs;
|
validateHandlebarTemplate("SAP Hana creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.renewStatement) {
|
||||||
|
validateHandlebarTemplate("SAP Hana renew", providerInputs.renewStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validateHandlebarTemplate("SAP Hana revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapHanaSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSapHanaSchema> & { hostIp: string }) => {
|
||||||
const client = hdb.createClient({
|
const client = hdb.createClient({
|
||||||
host: providerInputs.host,
|
host: providerInputs.hostIp,
|
||||||
port: providerInputs.port,
|
port: providerInputs.port,
|
||||||
user: providerInputs.username,
|
user: providerInputs.username,
|
||||||
password: providerInputs.password,
|
password: providerInputs.password,
|
||||||
|
@@ -5,6 +5,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { DynamicSecretSnowflakeSchema, TDynamicProviderFns } from "./models";
|
import { DynamicSecretSnowflakeSchema, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
@@ -31,6 +32,18 @@ const getDaysToExpiry = (expiryDate: Date) => {
|
|||||||
export const SnowflakeProvider = (): TDynamicProviderFns => {
|
export const SnowflakeProvider = (): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSnowflakeSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSnowflakeSchema.parseAsync(inputs);
|
||||||
|
validateHandlebarTemplate("Snowflake creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.renewStatement) {
|
||||||
|
validateHandlebarTemplate("Snowflake renew", providerInputs.renewStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "expiration"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validateHandlebarTemplate("Snowflake revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username"].includes(val)
|
||||||
|
});
|
||||||
|
|
||||||
return providerInputs;
|
return providerInputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,23 +1,107 @@
|
|||||||
|
import { randomInt } from "crypto";
|
||||||
import handlebars from "handlebars";
|
import handlebars from "handlebars";
|
||||||
import knex from "knex";
|
import knex from "knex";
|
||||||
import { customAlphabet } from "nanoid";
|
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { withGatewayProxy } from "@app/lib/gateway";
|
import { withGatewayProxy } from "@app/lib/gateway";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
|
|
||||||
import { TGatewayServiceFactory } from "../../gateway/gateway-service";
|
import { TGatewayServiceFactory } from "../../gateway/gateway-service";
|
||||||
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
import { verifyHostInputValidity } from "../dynamic-secret-fns";
|
||||||
import { DynamicSecretSqlDBSchema, SqlProviders, TDynamicProviderFns } from "./models";
|
import { DynamicSecretSqlDBSchema, PasswordRequirements, SqlProviders, TDynamicProviderFns } from "./models";
|
||||||
|
|
||||||
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
|
const EXTERNAL_REQUEST_TIMEOUT = 10 * 1000;
|
||||||
|
|
||||||
const generatePassword = (provider: SqlProviders) => {
|
const DEFAULT_PASSWORD_REQUIREMENTS = {
|
||||||
// oracle has limit of 48 password length
|
length: 48,
|
||||||
const size = provider === SqlProviders.Oracle ? 30 : 48;
|
required: {
|
||||||
|
lowercase: 1,
|
||||||
|
uppercase: 1,
|
||||||
|
digits: 1,
|
||||||
|
symbols: 0
|
||||||
|
},
|
||||||
|
allowedSymbols: "-_.~!*"
|
||||||
|
};
|
||||||
|
|
||||||
const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~!*";
|
const ORACLE_PASSWORD_REQUIREMENTS = {
|
||||||
return customAlphabet(charset, 48)(size);
|
...DEFAULT_PASSWORD_REQUIREMENTS,
|
||||||
|
length: 30
|
||||||
|
};
|
||||||
|
|
||||||
|
const generatePassword = (provider: SqlProviders, requirements?: PasswordRequirements) => {
|
||||||
|
const defaultReqs = provider === SqlProviders.Oracle ? ORACLE_PASSWORD_REQUIREMENTS : DEFAULT_PASSWORD_REQUIREMENTS;
|
||||||
|
const finalReqs = requirements || defaultReqs;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { length, required, allowedSymbols } = finalReqs;
|
||||||
|
|
||||||
|
const chars = {
|
||||||
|
lowercase: "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
uppercase: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
|
||||||
|
digits: "0123456789",
|
||||||
|
symbols: allowedSymbols || "-_.~!*"
|
||||||
|
};
|
||||||
|
|
||||||
|
const parts: string[] = [];
|
||||||
|
|
||||||
|
if (required.lowercase > 0) {
|
||||||
|
parts.push(
|
||||||
|
...Array(required.lowercase)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => chars.lowercase[randomInt(chars.lowercase.length)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (required.uppercase > 0) {
|
||||||
|
parts.push(
|
||||||
|
...Array(required.uppercase)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => chars.uppercase[randomInt(chars.uppercase.length)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (required.digits > 0) {
|
||||||
|
parts.push(
|
||||||
|
...Array(required.digits)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => chars.digits[randomInt(chars.digits.length)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (required.symbols > 0) {
|
||||||
|
parts.push(
|
||||||
|
...Array(required.symbols)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => chars.symbols[randomInt(chars.symbols.length)])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const requiredTotal = Object.values(required).reduce<number>((a, b) => a + b, 0);
|
||||||
|
const remainingLength = Math.max(length - requiredTotal, 0);
|
||||||
|
|
||||||
|
const allowedChars = Object.entries(chars)
|
||||||
|
.filter(([key]) => required[key as keyof typeof required] > 0)
|
||||||
|
.map(([, value]) => value)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
parts.push(
|
||||||
|
...Array(remainingLength)
|
||||||
|
.fill(0)
|
||||||
|
.map(() => allowedChars[randomInt(allowedChars.length)])
|
||||||
|
);
|
||||||
|
|
||||||
|
// shuffle the array to mix up the characters
|
||||||
|
for (let i = parts.length - 1; i > 0; i -= 1) {
|
||||||
|
const j = randomInt(i + 1);
|
||||||
|
[parts[i], parts[j]] = [parts[j], parts[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join("");
|
||||||
|
} catch (error: unknown) {
|
||||||
|
const message = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
throw new Error(`Failed to generate password: ${message}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateUsername = (provider: SqlProviders) => {
|
const generateUsername = (provider: SqlProviders) => {
|
||||||
@@ -34,8 +118,21 @@ type TSqlDatabaseProviderDTO = {
|
|||||||
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO): TDynamicProviderFns => {
|
||||||
const validateProviderInputs = async (inputs: unknown) => {
|
const validateProviderInputs = async (inputs: unknown) => {
|
||||||
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
const providerInputs = await DynamicSecretSqlDBSchema.parseAsync(inputs);
|
||||||
verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.projectGatewayId));
|
|
||||||
return providerInputs;
|
const [hostIp] = await verifyHostInputValidity(providerInputs.host, Boolean(providerInputs.projectGatewayId));
|
||||||
|
validateHandlebarTemplate("SQL creation", providerInputs.creationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "password", "expiration", "database"].includes(val)
|
||||||
|
});
|
||||||
|
if (providerInputs.renewStatement) {
|
||||||
|
validateHandlebarTemplate("SQL renew", providerInputs.renewStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "expiration", "database"].includes(val)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
validateHandlebarTemplate("SQL revoke", providerInputs.revocationStatement, {
|
||||||
|
allowedExpressions: (val) => ["username", "database"].includes(val)
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...providerInputs, hostIp };
|
||||||
};
|
};
|
||||||
|
|
||||||
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
|
const $getClient = async (providerInputs: z.infer<typeof DynamicSecretSqlDBSchema>) => {
|
||||||
@@ -61,7 +158,8 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
},
|
},
|
||||||
acquireConnectionTimeout: EXTERNAL_REQUEST_TIMEOUT
|
acquireConnectionTimeout: EXTERNAL_REQUEST_TIMEOUT,
|
||||||
|
pool: { min: 0, max: 7 }
|
||||||
});
|
});
|
||||||
return db;
|
return db;
|
||||||
};
|
};
|
||||||
@@ -95,7 +193,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
const validateConnection = async (inputs: unknown) => {
|
const validateConnection = async (inputs: unknown) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
let isConnected = false;
|
let isConnected = false;
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.hostIp, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({ ...providerInputs, port, host });
|
||||||
// oracle needs from keyword
|
// oracle needs from keyword
|
||||||
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
const testStatement = providerInputs.client === SqlProviders.Oracle ? "SELECT 1 FROM DUAL" : "SELECT 1";
|
||||||
@@ -115,7 +213,7 @@ export const SqlDatabaseProvider = ({ gatewayService }: TSqlDatabaseProviderDTO)
|
|||||||
const create = async (inputs: unknown, expireAt: number) => {
|
const create = async (inputs: unknown, expireAt: number) => {
|
||||||
const providerInputs = await validateProviderInputs(inputs);
|
const providerInputs = await validateProviderInputs(inputs);
|
||||||
const username = generateUsername(providerInputs.client);
|
const username = generateUsername(providerInputs.client);
|
||||||
const password = generatePassword(providerInputs.client);
|
const password = generatePassword(providerInputs.client, providerInputs.passwordRequirements);
|
||||||
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
const gatewayCallback = async (host = providerInputs.host, port = providerInputs.port) => {
|
||||||
const db = await $getClient({ ...providerInputs, port, host });
|
const db = await $getClient({ ...providerInputs, port, host });
|
||||||
try {
|
try {
|
||||||
|
@@ -3,8 +3,7 @@ import slugify from "@sindresorhus/slugify";
|
|||||||
|
|
||||||
import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas";
|
import { OrgMembershipRole, TOrgRoles } from "@app/db/schemas";
|
||||||
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
import { TOidcConfigDALFactory } from "@app/ee/services/oidc/oidc-config-dal";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
|
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
@@ -14,7 +13,8 @@ import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal
|
|||||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||||
|
|
||||||
import { TLicenseServiceFactory } from "../license/license-service";
|
import { TLicenseServiceFactory } from "../license/license-service";
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionGroupActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
|
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { TGroupDALFactory } from "./group-dal";
|
import { TGroupDALFactory } from "./group-dal";
|
||||||
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
|
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
|
||||||
@@ -67,14 +67,14 @@ export const groupServiceFactory = ({
|
|||||||
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
const createGroup = async ({ name, slug, role, actor, actorId, actorAuthMethod, actorOrgId }: TCreateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission, membership } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Create, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
if (!plan.groups)
|
if (!plan.groups)
|
||||||
@@ -87,9 +87,26 @@ export const groupServiceFactory = ({
|
|||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
const isCustomRole = Boolean(customRole);
|
const isCustomRole = Boolean(customRole);
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, rolePermission);
|
if (role !== OrgMembershipRole.NoAccess) {
|
||||||
if (!hasRequiredPriviledges)
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
throw new ForbiddenRequestError({ message: "Failed to create a more privileged group" });
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
|
OrgPermissionSubjects.Groups,
|
||||||
|
permission,
|
||||||
|
rolePermission
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to create group",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
|
OrgPermissionSubjects.Groups
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const group = await groupDAL.transaction(async (tx) => {
|
const group = await groupDAL.transaction(async (tx) => {
|
||||||
const existingGroup = await groupDAL.findOne({ orgId: actorOrgId, name }, tx);
|
const existingGroup = await groupDAL.findOne({ orgId: actorOrgId, name }, tx);
|
||||||
@@ -128,14 +145,15 @@ export const groupServiceFactory = ({
|
|||||||
}: TUpdateGroupDTO) => {
|
}: TUpdateGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission, membership } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Groups);
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
if (!plan.groups)
|
if (!plan.groups)
|
||||||
@@ -156,9 +174,23 @@ export const groupServiceFactory = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const isCustomRole = Boolean(customOrgRole);
|
const isCustomRole = Boolean(customOrgRole);
|
||||||
const hasRequiredNewRolePermission = isAtLeastAsPrivileged(permission, rolePermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredNewRolePermission)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to create a more privileged group" });
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
|
OrgPermissionSubjects.Groups,
|
||||||
|
permission,
|
||||||
|
rolePermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update group",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
OrgPermissionGroupActions.GrantPrivileges,
|
||||||
|
OrgPermissionSubjects.Groups
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
if (isCustomRole) customRole = customOrgRole;
|
if (isCustomRole) customRole = customOrgRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +238,7 @@ export const groupServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Delete, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const plan = await licenseService.getPlan(actorOrgId);
|
const plan = await licenseService.getPlan(actorOrgId);
|
||||||
|
|
||||||
@@ -233,7 +265,7 @@ export const groupServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findById(id);
|
const group = await groupDAL.findById(id);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
@@ -266,7 +298,7 @@ export const groupServiceFactory = ({
|
|||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
const group = await groupDAL.findOne({
|
const group = await groupDAL.findOne({
|
||||||
orgId: actorOrgId,
|
orgId: actorOrgId,
|
||||||
@@ -294,14 +326,14 @@ export const groupServiceFactory = ({
|
|||||||
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
const addUserToGroup = async ({ id, username, actor, actorId, actorAuthMethod, actorOrgId }: TAddUserToGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission, membership } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
// check if group with slug exists
|
// check if group with slug exists
|
||||||
const group = await groupDAL.findOne({
|
const group = await groupDAL.findOne({
|
||||||
@@ -329,9 +361,24 @@ export const groupServiceFactory = ({
|
|||||||
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
||||||
|
|
||||||
// check if user has broader or equal to privileges than group
|
// check if user has broader or equal to privileges than group
|
||||||
const hasRequiredPrivileges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPrivileges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to add user to more privileged group" });
|
OrgPermissionGroupActions.AddMembers,
|
||||||
|
OrgPermissionSubjects.Groups,
|
||||||
|
permission,
|
||||||
|
groupRolePermission
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to add user to more privileged group",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
OrgPermissionGroupActions.AddMembers,
|
||||||
|
OrgPermissionSubjects.Groups
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const user = await userDAL.findOne({ username });
|
const user = await userDAL.findOne({ username });
|
||||||
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
||||||
@@ -361,14 +408,14 @@ export const groupServiceFactory = ({
|
|||||||
}: TRemoveUserFromGroupDTO) => {
|
}: TRemoveUserFromGroupDTO) => {
|
||||||
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
if (!actorOrgId) throw new UnauthorizedError({ message: "No organization ID provided in request" });
|
||||||
|
|
||||||
const { permission } = await permissionService.getOrgPermission(
|
const { permission, membership } = await permissionService.getOrgPermission(
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorOrgId,
|
actorOrgId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId
|
||||||
);
|
);
|
||||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Groups);
|
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
// check if group with slug exists
|
// check if group with slug exists
|
||||||
const group = await groupDAL.findOne({
|
const group = await groupDAL.findOne({
|
||||||
@@ -396,9 +443,23 @@ export const groupServiceFactory = ({
|
|||||||
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
const { permission: groupRolePermission } = await permissionService.getOrgPermissionByRole(group.role, actorOrgId);
|
||||||
|
|
||||||
// check if user has broader or equal to privileges than group
|
// check if user has broader or equal to privileges than group
|
||||||
const hasRequiredPrivileges = isAtLeastAsPrivileged(permission, groupRolePermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPrivileges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to delete user from more privileged group" });
|
OrgPermissionGroupActions.RemoveMembers,
|
||||||
|
OrgPermissionSubjects.Groups,
|
||||||
|
permission,
|
||||||
|
groupRolePermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to delete user from more privileged group",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
OrgPermissionGroupActions.RemoveMembers,
|
||||||
|
OrgPermissionSubjects.Groups
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const user = await userDAL.findOne({ username });
|
const user = await userDAL.findOne({ username });
|
||||||
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
if (!user) throw new NotFoundError({ message: `Failed to find user with username ${username}` });
|
||||||
|
@@ -1,17 +1,18 @@
|
|||||||
import { ForbiddenError, subject } from "@casl/ability";
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
import { packRules } from "@casl/ability/extra";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { ms } from "@app/lib/ms";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
import { unpackPermissions } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
|
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
import { ProjectPermissionIdentityActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
import { TIdentityProjectAdditionalPrivilegeV2DALFactory } from "./identity-project-additional-privilege-v2-dal";
|
import { TIdentityProjectAdditionalPrivilegeV2DALFactory } from "./identity-project-additional-privilege-v2-dal";
|
||||||
import {
|
import {
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||||
@@ -64,10 +65,10 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
const { permission: targetIdentityPermission, membership } = await permissionService.getProjectPermission({
|
||||||
actor: ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
actorId: identityId,
|
actorId: identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -79,9 +80,26 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
targetIdentityPermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
validateHandlebarTemplate("Identity Additional Privilege Create", JSON.stringify(customPermission || []), {
|
||||||
|
allowedExpressions: (val) => val.includes("identity.")
|
||||||
|
});
|
||||||
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
slug,
|
slug,
|
||||||
@@ -146,10 +164,10 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
const { permission: targetIdentityPermission } = await permissionService.getProjectPermission({
|
const { permission: targetIdentityPermission, membership } = await permissionService.getProjectPermission({
|
||||||
actor: ActorType.IDENTITY,
|
actor: ActorType.IDENTITY,
|
||||||
actorId: identityProjectMembership.identityId,
|
actorId: identityProjectMembership.identityId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -161,9 +179,27 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
targetIdentityPermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
|
validateHandlebarTemplate("Identity Additional Privilege Update", JSON.stringify(data.permissions || []), {
|
||||||
|
allowedExpressions: (val) => val.includes("identity.")
|
||||||
|
});
|
||||||
|
|
||||||
if (data?.slug) {
|
if (data?.slug) {
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
@@ -219,7 +255,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
message: `Failed to find identity with membership ${identityPrivilege.projectMembershipId}`
|
||||||
});
|
});
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission, membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -228,7 +264,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
const { permission: identityRolePermission } = await permissionService.getProjectPermission({
|
||||||
@@ -239,9 +275,23 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
identityRolePermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
|
||||||
return {
|
return {
|
||||||
@@ -275,7 +325,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -310,7 +360,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -346,7 +396,7 @@ export const identityProjectAdditionalPrivilegeV2ServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
subject(ProjectPermissionSub.Identity, { identityId: identityProjectMembership.identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,17 +1,22 @@
|
|||||||
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
import { ForbiddenError, MongoAbility, RawRuleOf, subject } from "@casl/ability";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { isAtLeastAsPrivileged } from "@app/lib/casl";
|
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
|
||||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
import { ms } from "@app/lib/ms";
|
||||||
|
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
|
||||||
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
import { UnpackedPermissionSchema } from "@app/server/routes/sanitizedSchema/permission";
|
||||||
import { ActorType } from "@app/services/auth/auth-type";
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
import { TIdentityProjectDALFactory } from "@app/services/identity-project/identity-project-dal";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
|
import { constructPermissionErrorMessage, validatePrivilegeChangeOperation } from "../permission/permission-fns";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
import { ProjectPermissionActions, ProjectPermissionSet, ProjectPermissionSub } from "../permission/project-permission";
|
import {
|
||||||
|
ProjectPermissionIdentityActions,
|
||||||
|
ProjectPermissionSet,
|
||||||
|
ProjectPermissionSub
|
||||||
|
} from "../permission/project-permission";
|
||||||
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
|
||||||
import {
|
import {
|
||||||
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
IdentityProjectAdditionalPrivilegeTemporaryMode,
|
||||||
@@ -63,7 +68,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission, membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -71,8 +76,9 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -88,9 +94,23 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
targetIdentityPermission.update(targetIdentityPermission.rules.concat(customPermission));
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
targetIdentityPermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
const existingSlug = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
slug,
|
slug,
|
||||||
@@ -98,6 +118,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
if (existingSlug) throw new BadRequestError({ message: "Additional privilege of provided slug exist" });
|
||||||
|
|
||||||
|
validateHandlebarTemplate("Identity Additional Privilege Create", JSON.stringify(customPermission || []), {
|
||||||
|
allowedExpressions: (val) => val.includes("identity.")
|
||||||
|
});
|
||||||
|
|
||||||
const packedPermission = JSON.stringify(packRules(customPermission));
|
const packedPermission = JSON.stringify(packRules(customPermission));
|
||||||
if (!dto.isTemporary) {
|
if (!dto.isTemporary) {
|
||||||
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({
|
||||||
@@ -146,7 +170,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission, membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -156,7 +180,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -172,9 +196,23 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
// we need to validate that the privilege given is not higher than the assigning users permission
|
// we need to validate that the privilege given is not higher than the assigning users permission
|
||||||
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
// @ts-expect-error this is expected error because of one being really accurate rule definition other being a bit more broader. Both are valid casl rules
|
||||||
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
targetIdentityPermission.update(targetIdentityPermission.rules.concat(data.permissions || []));
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, targetIdentityPermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to update more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
targetIdentityPermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to update more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
slug,
|
slug,
|
||||||
@@ -195,6 +233,9 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
|
const isTemporary = typeof data?.isTemporary !== "undefined" ? data.isTemporary : identityPrivilege.isTemporary;
|
||||||
|
validateHandlebarTemplate("Identity Additional Privilege Update", JSON.stringify(data.permissions || []), {
|
||||||
|
allowedExpressions: (val) => val.includes("identity.")
|
||||||
|
});
|
||||||
|
|
||||||
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
|
const packedPermission = data.permissions ? JSON.stringify(packRules(data.permissions)) : undefined;
|
||||||
if (isTemporary) {
|
if (isTemporary) {
|
||||||
@@ -247,7 +288,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
if (!identityProjectMembership)
|
if (!identityProjectMembership)
|
||||||
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
throw new NotFoundError({ message: `Failed to find identity with id ${identityId}` });
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
const { permission, membership } = await permissionService.getProjectPermission({
|
||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
projectId: identityProjectMembership.projectId,
|
projectId: identityProjectMembership.projectId,
|
||||||
@@ -256,7 +297,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Edit,
|
ProjectPermissionIdentityActions.Edit,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -268,9 +309,23 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actorOrgId,
|
actorOrgId,
|
||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission);
|
const permissionBoundary = validatePrivilegeChangeOperation(
|
||||||
if (!hasRequiredPriviledges)
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
throw new ForbiddenRequestError({ message: "Failed to edit more privileged identity" });
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity,
|
||||||
|
permission,
|
||||||
|
identityRolePermission
|
||||||
|
);
|
||||||
|
if (!permissionBoundary.isValid)
|
||||||
|
throw new PermissionBoundaryError({
|
||||||
|
message: constructPermissionErrorMessage(
|
||||||
|
"Failed to edit more privileged identity",
|
||||||
|
membership.shouldUseNewPrivilegeSystem,
|
||||||
|
ProjectPermissionIdentityActions.GrantPrivileges,
|
||||||
|
ProjectPermissionSub.Identity
|
||||||
|
),
|
||||||
|
details: { missingPermissions: permissionBoundary.missingPermissions }
|
||||||
|
});
|
||||||
|
|
||||||
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findOne({
|
||||||
slug,
|
slug,
|
||||||
@@ -315,7 +370,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
actionProjectType: ActionProjectType.Any
|
actionProjectType: ActionProjectType.Any
|
||||||
});
|
});
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -359,7 +414,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionActions.Read,
|
ProjectPermissionIdentityActions.Read,
|
||||||
subject(ProjectPermissionSub.Identity, { identityId })
|
subject(ProjectPermissionSub.Identity, { identityId })
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
import * as x509 from "@peculiar/x509";
|
import * as x509 from "@peculiar/x509";
|
||||||
import crypto, { KeyObject } from "crypto";
|
import crypto, { KeyObject } from "crypto";
|
||||||
import ms from "ms";
|
|
||||||
|
|
||||||
import { ActionProjectType } from "@app/db/schemas";
|
import { ActionProjectType } from "@app/db/schemas";
|
||||||
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
|
||||||
import { isValidHostname, isValidIp } from "@app/lib/ip";
|
import { isValidIp } from "@app/lib/ip";
|
||||||
|
import { ms } from "@app/lib/ms";
|
||||||
|
import { isFQDN } from "@app/lib/validator/validate-url";
|
||||||
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
import { constructPemChainFromCerts } from "@app/services/certificate/certificate-fns";
|
||||||
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
|
||||||
import {
|
import {
|
||||||
@@ -665,7 +666,7 @@ export const kmipServiceFactory = ({
|
|||||||
.split(",")
|
.split(",")
|
||||||
.map((name) => name.trim())
|
.map((name) => name.trim())
|
||||||
.map((altName) => {
|
.map((altName) => {
|
||||||
if (isValidHostname(altName)) {
|
if (isFQDN(altName, { allow_wildcard: true })) {
|
||||||
return {
|
return {
|
||||||
type: "dns",
|
type: "dns",
|
||||||
value: altName
|
value: altName
|
||||||
|
@@ -97,12 +97,14 @@ export const searchGroups = async (
|
|||||||
|
|
||||||
res.on("searchEntry", (entry) => {
|
res.on("searchEntry", (entry) => {
|
||||||
const dn = entry.dn.toString();
|
const dn = entry.dn.toString();
|
||||||
const regex = /cn=([^,]+)/;
|
const cnStartIndex = dn.indexOf("cn=");
|
||||||
const match = dn.match(regex);
|
|
||||||
// parse the cn from the dn
|
|
||||||
const cn = (match && match[1]) as string;
|
|
||||||
|
|
||||||
|
if (cnStartIndex !== -1) {
|
||||||
|
const valueStartIndex = cnStartIndex + 3;
|
||||||
|
const commaIndex = dn.indexOf(",", valueStartIndex);
|
||||||
|
const cn = dn.substring(valueStartIndex, commaIndex === -1 ? undefined : commaIndex);
|
||||||
groups.push({ dn, cn });
|
groups.push({ dn, cn });
|
||||||
|
}
|
||||||
});
|
});
|
||||||
res.on("error", (error) => {
|
res.on("error", (error) => {
|
||||||
ldapClient.unbind();
|
ldapClient.unbind();
|
||||||
|
24
backend/src/ee/services/license/licence-enums.ts
Normal file
24
backend/src/ee/services/license/licence-enums.ts
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
export const BillingPlanRows = {
|
||||||
|
MemberLimit: { name: "Organization member limit", field: "memberLimit" },
|
||||||
|
IdentityLimit: { name: "Organization identity limit", field: "identityLimit" },
|
||||||
|
WorkspaceLimit: { name: "Project limit", field: "workspaceLimit" },
|
||||||
|
EnvironmentLimit: { name: "Environment limit", field: "environmentLimit" },
|
||||||
|
SecretVersioning: { name: "Secret versioning", field: "secretVersioning" },
|
||||||
|
PitRecovery: { name: "Point in time recovery", field: "pitRecovery" },
|
||||||
|
Rbac: { name: "RBAC", field: "rbac" },
|
||||||
|
CustomRateLimits: { name: "Custom rate limits", field: "customRateLimits" },
|
||||||
|
CustomAlerts: { name: "Custom alerts", field: "customAlerts" },
|
||||||
|
AuditLogs: { name: "Audit logs", field: "auditLogs" },
|
||||||
|
SamlSSO: { name: "SAML SSO", field: "samlSSO" },
|
||||||
|
Hsm: { name: "Hardware Security Module (HSM)", field: "hsm" },
|
||||||
|
OidcSSO: { name: "OIDC SSO", field: "oidcSSO" },
|
||||||
|
SecretApproval: { name: "Secret approvals", field: "secretApproval" },
|
||||||
|
SecretRotation: { name: "Secret rotation", field: "secretRotation" },
|
||||||
|
InstanceUserManagement: { name: "Instance User Management", field: "instanceUserManagement" },
|
||||||
|
ExternalKms: { name: "External KMS", field: "externalKms" }
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export const BillingPlanTableHead = {
|
||||||
|
Allowed: { name: "Allowed" },
|
||||||
|
Used: { name: "Used" }
|
||||||
|
} as const;
|
@@ -39,7 +39,7 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
|
|||||||
trial_end: null,
|
trial_end: null,
|
||||||
has_used_trial: true,
|
has_used_trial: true,
|
||||||
secretApproval: false,
|
secretApproval: false,
|
||||||
secretRotation: true,
|
secretRotation: false,
|
||||||
caCrl: false,
|
caCrl: false,
|
||||||
instanceUserManagement: false,
|
instanceUserManagement: false,
|
||||||
externalKms: false,
|
externalKms: false,
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
// TODO(akhilmhdh): With tony find out the api structure and fill it here
|
// TODO(akhilmhdh): With tony find out the api structure and fill it here
|
||||||
|
|
||||||
import { ForbiddenError } from "@casl/ability";
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
import { CronJob } from "cron";
|
||||||
import { Knex } from "knex";
|
import { Knex } from "knex";
|
||||||
|
|
||||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||||
@@ -12,10 +13,13 @@ import { getConfig } from "@app/lib/config/env";
|
|||||||
import { verifyOfflineLicense } from "@app/lib/crypto";
|
import { verifyOfflineLicense } from "@app/lib/crypto";
|
||||||
import { NotFoundError } from "@app/lib/errors";
|
import { NotFoundError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { TIdentityOrgDALFactory } from "@app/services/identity/identity-org-dal";
|
||||||
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
import { TOrgDALFactory } from "@app/services/org/org-dal";
|
||||||
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
import { BillingPlanRows, BillingPlanTableHead } from "./licence-enums";
|
||||||
import { TLicenseDALFactory } from "./license-dal";
|
import { TLicenseDALFactory } from "./license-dal";
|
||||||
import { getDefaultOnPremFeatures, setupLicenseRequestWithStore } from "./license-fns";
|
import { getDefaultOnPremFeatures, setupLicenseRequestWithStore } from "./license-fns";
|
||||||
import {
|
import {
|
||||||
@@ -28,6 +32,7 @@ import {
|
|||||||
TFeatureSet,
|
TFeatureSet,
|
||||||
TGetOrgBillInfoDTO,
|
TGetOrgBillInfoDTO,
|
||||||
TGetOrgTaxIdDTO,
|
TGetOrgTaxIdDTO,
|
||||||
|
TOfflineLicense,
|
||||||
TOfflineLicenseContents,
|
TOfflineLicenseContents,
|
||||||
TOrgInvoiceDTO,
|
TOrgInvoiceDTO,
|
||||||
TOrgLicensesDTO,
|
TOrgLicensesDTO,
|
||||||
@@ -39,10 +44,12 @@ import {
|
|||||||
} from "./license-types";
|
} from "./license-types";
|
||||||
|
|
||||||
type TLicenseServiceFactoryDep = {
|
type TLicenseServiceFactoryDep = {
|
||||||
orgDAL: Pick<TOrgDALFactory, "findOrgById">;
|
orgDAL: Pick<TOrgDALFactory, "findOrgById" | "countAllOrgMembers">;
|
||||||
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
|
||||||
licenseDAL: TLicenseDALFactory;
|
licenseDAL: TLicenseDALFactory;
|
||||||
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
keyStore: Pick<TKeyStoreFactory, "setItemWithExpiry" | "getItem" | "deleteItem">;
|
||||||
|
identityOrgMembershipDAL: TIdentityOrgDALFactory;
|
||||||
|
projectDAL: TProjectDALFactory;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>;
|
export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>;
|
||||||
@@ -50,18 +57,21 @@ export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>;
|
|||||||
const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login";
|
const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login";
|
||||||
const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login";
|
const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login";
|
||||||
|
|
||||||
const LICENSE_SERVER_CLOUD_PLAN_TTL = 30; // 30 second
|
const LICENSE_SERVER_CLOUD_PLAN_TTL = 5 * 60; // 5 mins
|
||||||
const FEATURE_CACHE_KEY = (orgId: string) => `infisical-cloud-plan-${orgId}`;
|
const FEATURE_CACHE_KEY = (orgId: string) => `infisical-cloud-plan-${orgId}`;
|
||||||
|
|
||||||
export const licenseServiceFactory = ({
|
export const licenseServiceFactory = ({
|
||||||
orgDAL,
|
orgDAL,
|
||||||
permissionService,
|
permissionService,
|
||||||
licenseDAL,
|
licenseDAL,
|
||||||
keyStore
|
keyStore,
|
||||||
|
identityOrgMembershipDAL,
|
||||||
|
projectDAL
|
||||||
}: TLicenseServiceFactoryDep) => {
|
}: TLicenseServiceFactoryDep) => {
|
||||||
let isValidLicense = false;
|
let isValidLicense = false;
|
||||||
let instanceType = InstanceType.OnPrem;
|
let instanceType = InstanceType.OnPrem;
|
||||||
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
let onPremFeatures: TFeatureSet = getDefaultOnPremFeatures();
|
||||||
|
let selfHostedLicense: TOfflineLicense | null = null;
|
||||||
|
|
||||||
const appCfg = getConfig();
|
const appCfg = getConfig();
|
||||||
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
const licenseServerCloudApi = setupLicenseRequestWithStore(
|
||||||
@@ -76,6 +86,20 @@ export const licenseServiceFactory = ({
|
|||||||
appCfg.LICENSE_KEY || ""
|
appCfg.LICENSE_KEY || ""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const syncLicenseKeyOnPremFeatures = async (shouldThrow: boolean = false) => {
|
||||||
|
logger.info("Start syncing license key features");
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { currentPlan }
|
||||||
|
} = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan");
|
||||||
|
onPremFeatures = currentPlan;
|
||||||
|
logger.info("Successfully synchronized license key features");
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(error, "Failed to synchronize license key features");
|
||||||
|
if (shouldThrow) throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const init = async () => {
|
const init = async () => {
|
||||||
try {
|
try {
|
||||||
if (appCfg.LICENSE_SERVER_KEY) {
|
if (appCfg.LICENSE_SERVER_KEY) {
|
||||||
@@ -89,10 +113,7 @@ export const licenseServiceFactory = ({
|
|||||||
if (appCfg.LICENSE_KEY) {
|
if (appCfg.LICENSE_KEY) {
|
||||||
const token = await licenseServerOnPremApi.refreshLicense();
|
const token = await licenseServerOnPremApi.refreshLicense();
|
||||||
if (token) {
|
if (token) {
|
||||||
const {
|
await syncLicenseKeyOnPremFeatures(true);
|
||||||
data: { currentPlan }
|
|
||||||
} = await licenseServerOnPremApi.request.get<{ currentPlan: TFeatureSet }>("/api/license/v1/plan");
|
|
||||||
onPremFeatures = currentPlan;
|
|
||||||
instanceType = InstanceType.EnterpriseOnPrem;
|
instanceType = InstanceType.EnterpriseOnPrem;
|
||||||
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
|
logger.info(`Instance type: ${InstanceType.EnterpriseOnPrem}`);
|
||||||
isValidLicense = true;
|
isValidLicense = true;
|
||||||
@@ -125,6 +146,7 @@ export const licenseServiceFactory = ({
|
|||||||
instanceType = InstanceType.EnterpriseOnPremOffline;
|
instanceType = InstanceType.EnterpriseOnPremOffline;
|
||||||
logger.info(`Instance type: ${InstanceType.EnterpriseOnPremOffline}`);
|
logger.info(`Instance type: ${InstanceType.EnterpriseOnPremOffline}`);
|
||||||
isValidLicense = true;
|
isValidLicense = true;
|
||||||
|
selfHostedLicense = contents.license;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,12 +159,24 @@ export const licenseServiceFactory = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const initializeBackgroundSync = async () => {
|
||||||
|
if (appCfg.LICENSE_KEY) {
|
||||||
|
logger.info("Setting up background sync process for refresh onPremFeatures");
|
||||||
|
const job = new CronJob("*/10 * * * *", syncLicenseKeyOnPremFeatures);
|
||||||
|
job.start();
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getPlan = async (orgId: string, projectId?: string) => {
|
const getPlan = async (orgId: string, projectId?: string) => {
|
||||||
logger.info(`getPlan: attempting to fetch plan for [orgId=${orgId}] [projectId=${projectId}]`);
|
logger.info(`getPlan: attempting to fetch plan for [orgId=${orgId}] [projectId=${projectId}]`);
|
||||||
try {
|
try {
|
||||||
if (instanceType === InstanceType.Cloud) {
|
if (instanceType === InstanceType.Cloud) {
|
||||||
const cachedPlan = await keyStore.getItem(FEATURE_CACHE_KEY(orgId));
|
const cachedPlan = await keyStore.getItem(FEATURE_CACHE_KEY(orgId));
|
||||||
if (cachedPlan) return JSON.parse(cachedPlan) as TFeatureSet;
|
if (cachedPlan) {
|
||||||
|
logger.info(`getPlan: plan fetched from cache [orgId=${orgId}] [projectId=${projectId}]`);
|
||||||
|
return JSON.parse(cachedPlan) as TFeatureSet;
|
||||||
|
}
|
||||||
|
|
||||||
const org = await orgDAL.findOrgById(orgId);
|
const org = await orgDAL.findOrgById(orgId);
|
||||||
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
if (!org) throw new NotFoundError({ message: `Organization with ID '${orgId}' not found` });
|
||||||
@@ -170,6 +204,8 @@ export const licenseServiceFactory = ({
|
|||||||
JSON.stringify(onPremFeatures)
|
JSON.stringify(onPremFeatures)
|
||||||
);
|
);
|
||||||
return onPremFeatures;
|
return onPremFeatures;
|
||||||
|
} finally {
|
||||||
|
logger.info(`getPlan: Process done for [orgId=${orgId}] [projectId=${projectId}]`);
|
||||||
}
|
}
|
||||||
return onPremFeatures;
|
return onPremFeatures;
|
||||||
};
|
};
|
||||||
@@ -343,10 +379,21 @@ export const licenseServiceFactory = ({
|
|||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) {
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/billing`
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentPeriodStart: selfHostedLicense?.issuedAt ? Date.parse(selfHostedLicense?.issuedAt) / 1000 : undefined,
|
||||||
|
currentPeriodEnd: selfHostedLicense?.expiresAt ? Date.parse(selfHostedLicense?.expiresAt) / 1000 : undefined,
|
||||||
|
interval: "month",
|
||||||
|
intervalCount: 1,
|
||||||
|
amount: 0,
|
||||||
|
quantity: 1
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// returns org current plan feature table
|
// returns org current plan feature table
|
||||||
@@ -360,10 +407,41 @@ export const licenseServiceFactory = ({
|
|||||||
message: `Organization with ID '${orgId}' not found`
|
message: `Organization with ID '${orgId}' not found`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (instanceType !== InstanceType.OnPrem && instanceType !== InstanceType.EnterpriseOnPremOffline) {
|
||||||
const { data } = await licenseServerCloudApi.request.get(
|
const { data } = await licenseServerCloudApi.request.get(
|
||||||
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table`
|
`/api/license-server/v1/customers/${organization.customerId}/cloud-plan/table`
|
||||||
);
|
);
|
||||||
return data;
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedRows = await Promise.all(
|
||||||
|
Object.values(BillingPlanRows).map(async ({ name, field }: { name: string; field: string }) => {
|
||||||
|
const allowed = onPremFeatures[field as keyof TFeatureSet];
|
||||||
|
let used = "-";
|
||||||
|
|
||||||
|
if (field === BillingPlanRows.MemberLimit.field) {
|
||||||
|
const orgMemberships = await orgDAL.countAllOrgMembers(orgId);
|
||||||
|
used = orgMemberships.toString();
|
||||||
|
} else if (field === BillingPlanRows.WorkspaceLimit.field) {
|
||||||
|
const projects = await projectDAL.find({ orgId });
|
||||||
|
used = projects.length.toString();
|
||||||
|
} else if (field === BillingPlanRows.IdentityLimit.field) {
|
||||||
|
const identities = await identityOrgMembershipDAL.countAllOrgIdentities({ orgId });
|
||||||
|
used = identities.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
allowed,
|
||||||
|
used
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
head: Object.values(BillingPlanTableHead),
|
||||||
|
rows: mappedRows
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
const getOrgBillingDetails = async ({ orgId, actor, actorId, actorAuthMethod, actorOrgId }: TGetOrgBillInfoDTO) => {
|
||||||
@@ -605,6 +683,7 @@ export const licenseServiceFactory = ({
|
|||||||
getOrgTaxInvoices,
|
getOrgTaxInvoices,
|
||||||
getOrgTaxIds,
|
getOrgTaxIds,
|
||||||
addOrgTaxId,
|
addOrgTaxId,
|
||||||
delOrgTaxId
|
delOrgTaxId,
|
||||||
|
initializeBackgroundSync
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -56,7 +56,7 @@ export type TFeatureSet = {
|
|||||||
trial_end: null;
|
trial_end: null;
|
||||||
has_used_trial: true;
|
has_used_trial: true;
|
||||||
secretApproval: false;
|
secretApproval: false;
|
||||||
secretRotation: true;
|
secretRotation: false;
|
||||||
caCrl: false;
|
caCrl: false;
|
||||||
instanceUserManagement: false;
|
instanceUserManagement: false;
|
||||||
externalKms: false;
|
externalKms: false;
|
||||||
|
@@ -32,6 +32,10 @@ export enum OrgPermissionAdminConsoleAction {
|
|||||||
AccessAllProjects = "access-all-projects"
|
AccessAllProjects = "access-all-projects"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionSecretShareAction {
|
||||||
|
ManageSettings = "manage-settings"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionGatewayActions {
|
export enum OrgPermissionGatewayActions {
|
||||||
// is there a better word for this. This mean can an identity be a gateway
|
// is there a better word for this. This mean can an identity be a gateway
|
||||||
CreateGateways = "create-gateways",
|
CreateGateways = "create-gateways",
|
||||||
@@ -40,6 +44,28 @@ export enum OrgPermissionGatewayActions {
|
|||||||
DeleteGateways = "delete-gateways"
|
DeleteGateways = "delete-gateways"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionIdentityActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
GrantPrivileges = "grant-privileges",
|
||||||
|
RevokeAuth = "revoke-auth",
|
||||||
|
CreateToken = "create-token",
|
||||||
|
GetToken = "get-token",
|
||||||
|
DeleteToken = "delete-token"
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum OrgPermissionGroupActions {
|
||||||
|
Read = "read",
|
||||||
|
Create = "create",
|
||||||
|
Edit = "edit",
|
||||||
|
Delete = "delete",
|
||||||
|
GrantPrivileges = "grant-privileges",
|
||||||
|
AddMembers = "add-members",
|
||||||
|
RemoveMembers = "remove-members"
|
||||||
|
}
|
||||||
|
|
||||||
export enum OrgPermissionSubjects {
|
export enum OrgPermissionSubjects {
|
||||||
Workspace = "workspace",
|
Workspace = "workspace",
|
||||||
Role = "role",
|
Role = "role",
|
||||||
@@ -59,7 +85,8 @@ export enum OrgPermissionSubjects {
|
|||||||
ProjectTemplates = "project-templates",
|
ProjectTemplates = "project-templates",
|
||||||
AppConnections = "app-connections",
|
AppConnections = "app-connections",
|
||||||
Kmip = "kmip",
|
Kmip = "kmip",
|
||||||
Gateway = "gateway"
|
Gateway = "gateway",
|
||||||
|
SecretShare = "secret-share"
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppConnectionSubjectFields = {
|
export type AppConnectionSubjectFields = {
|
||||||
@@ -75,10 +102,10 @@ export type OrgPermissionSet =
|
|||||||
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
|
| [OrgPermissionActions, OrgPermissionSubjects.Sso]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Scim]
|
| [OrgPermissionActions, OrgPermissionSubjects.Scim]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Ldap]
|
| [OrgPermissionActions, OrgPermissionSubjects.Ldap]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Groups]
|
| [OrgPermissionGroupActions, OrgPermissionSubjects.Groups]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
|
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
|
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Identity]
|
| [OrgPermissionIdentityActions, OrgPermissionSubjects.Identity]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
|
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
|
||||||
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
|
||||||
@@ -91,7 +118,8 @@ export type OrgPermissionSet =
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
|
||||||
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip];
|
| [OrgPermissionKmipActions, OrgPermissionSubjects.Kmip]
|
||||||
|
| [OrgPermissionSecretShareAction, OrgPermissionSubjects.SecretShare];
|
||||||
|
|
||||||
const AppConnectionConditionSchema = z
|
const AppConnectionConditionSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -185,6 +213,12 @@ export const OrgPermissionSchema = z.discriminatedUnion("subject", [
|
|||||||
"Describe what action an entity can take."
|
"Describe what action an entity can take."
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
z.object({
|
||||||
|
subject: z.literal(OrgPermissionSubjects.SecretShare).describe("The entity this permission pertains to."),
|
||||||
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionSecretShareAction).describe(
|
||||||
|
"Describe what action an entity can take."
|
||||||
|
)
|
||||||
|
}),
|
||||||
z.object({
|
z.object({
|
||||||
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
|
subject: z.literal(OrgPermissionSubjects.Kmip).describe("The entity this permission pertains to."),
|
||||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
|
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(OrgPermissionKmipActions).describe(
|
||||||
@@ -244,20 +278,28 @@ const buildAdminPermission = () => {
|
|||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Ldap);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Ldap);
|
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Ldap);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
can(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Groups);
|
can(OrgPermissionGroupActions.Create, OrgPermissionSubjects.Groups);
|
||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Groups);
|
can(OrgPermissionGroupActions.Edit, OrgPermissionSubjects.Groups);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Groups);
|
can(OrgPermissionGroupActions.Delete, OrgPermissionSubjects.Groups);
|
||||||
|
can(OrgPermissionGroupActions.GrantPrivileges, OrgPermissionSubjects.Groups);
|
||||||
|
can(OrgPermissionGroupActions.AddMembers, OrgPermissionSubjects.Groups);
|
||||||
|
can(OrgPermissionGroupActions.RemoveMembers, OrgPermissionSubjects.Groups);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
|
||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Billing);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||||
|
can(OrgPermissionIdentityActions.GrantPrivileges, OrgPermissionSubjects.Identity);
|
||||||
|
can(OrgPermissionIdentityActions.RevokeAuth, OrgPermissionSubjects.Identity);
|
||||||
|
can(OrgPermissionIdentityActions.CreateToken, OrgPermissionSubjects.Identity);
|
||||||
|
can(OrgPermissionIdentityActions.GetToken, OrgPermissionSubjects.Identity);
|
||||||
|
can(OrgPermissionIdentityActions.DeleteToken, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
|
||||||
@@ -292,6 +334,8 @@ const buildAdminPermission = () => {
|
|||||||
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
|
// the proxy assignment is temporary in order to prevent "more privilege" error during role assignment to MI
|
||||||
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
can(OrgPermissionKmipActions.Proxy, OrgPermissionSubjects.Kmip);
|
||||||
|
|
||||||
|
can(OrgPermissionSecretShareAction.ManageSettings, OrgPermissionSubjects.SecretShare);
|
||||||
|
|
||||||
return rules;
|
return rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,7 +346,7 @@ const buildMemberPermission = () => {
|
|||||||
|
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Member);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Groups);
|
can(OrgPermissionGroupActions.Read, OrgPermissionSubjects.Groups);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Role);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Settings);
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
|
||||||
@@ -313,10 +357,10 @@ const buildMemberPermission = () => {
|
|||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
can(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.SecretScanning);
|
can(OrgPermissionActions.Delete, OrgPermissionSubjects.SecretScanning);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Read, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Create, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Create, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Edit, OrgPermissionSubjects.Identity);
|
||||||
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
|
can(OrgPermissionIdentityActions.Delete, OrgPermissionSubjects.Identity);
|
||||||
|
|
||||||
can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs);
|
can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs);
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user