mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-02 16:55:02 +00:00
Compare commits
854 Commits
improve-aw
...
docs/updat
Author | SHA1 | Date | |
---|---|---|---|
b12fe66871 | |||
28582d9134 | |||
04908edb5b | |||
e8753a3ce8 | |||
1947989ca5 | |||
c22e616771 | |||
40711ac707 | |||
a47e6910b1 | |||
78c4a591a9 | |||
f6b7717517 | |||
b21a5b6425 | |||
66a5691ffd | |||
6bdf62d453 | |||
652a48b520 | |||
3148c54e18 | |||
bd4cf64fc6 | |||
f4e3d7d576 | |||
8298f9974f | |||
da347e96e1 | |||
5df96234a0 | |||
e78682560c | |||
1602fac5ca | |||
0100bf7032 | |||
e2c49878c6 | |||
e74117b7fd | |||
335aada941 | |||
b949fe06c3 | |||
28e539c481 | |||
5c4c881b60 | |||
8ffb92bfb3 | |||
db9a1726c2 | |||
15986633c7 | |||
c4809bbb54 | |||
6305aab0d1 | |||
456493ff5a | |||
8cfaefcec5 | |||
e39e80a0e7 | |||
8cae92f29e | |||
918911f2e4 | |||
a1aee45eb2 | |||
5fe93dc35a | |||
5e0e7763a3 | |||
f663d1d4a6 | |||
650f6d9585 | |||
7994034639 | |||
48619ed24c | |||
21fb8df39b | |||
f03a7cc249 | |||
f2dcbfa91c | |||
d08510ebe4 | |||
767159bf8f | |||
98457cdb34 | |||
8ed8f1200d | |||
30252c2bcb | |||
9687f33122 | |||
a5282a56c9 | |||
cc3551c417 | |||
9e6fe39609 | |||
2bc91c42a7 | |||
c7ec825830 | |||
5b7f445e33 | |||
7fe53ab00e | |||
90c17820fc | |||
e739b29b3c | |||
1a89f2a479 | |||
78568bffe2 | |||
1407a122b9 | |||
8168b5faf8 | |||
8b9e035bf6 | |||
d36d0784ca | |||
e69354b546 | |||
64bd5ddcc8 | |||
72088634d8 | |||
f3a84f6001 | |||
13672481a8 | |||
058394f892 | |||
c623c615a1 | |||
034a8112b7 | |||
5fc6fd71ce | |||
be37e27dbf | |||
3b62f956e9 | |||
f49e3788cc | |||
1147f87eed | |||
995e3254ba | |||
67d0c53912 | |||
a6fbcb3e01 | |||
db1ca2b89f | |||
f91bbe1f31 | |||
e5f475e8d6 | |||
1e4ca2f48f | |||
8d5e7406c3 | |||
3b230dad9a | |||
782bf2cdc9 | |||
982b506eb8 | |||
e5bc609a2a | |||
b812761bdd | |||
14362dbe6a | |||
b7b90aea33 | |||
8d147867ed | |||
eb4e727922 | |||
bb276a0dba | |||
7cdb015b81 | |||
ce446fa723 | |||
82f6c9fb58 | |||
6369d13862 | |||
9f91970be2 | |||
c7398d924a | |||
df57364985 | |||
84322f4f68 | |||
5518df116f | |||
73c6c076e8 | |||
ba2a772247 | |||
8fbe46256b | |||
b75bb93d83 | |||
db4db04ba6 | |||
db44d958d3 | |||
12beb06682 | |||
804f8be07d | |||
e81991c545 | |||
28a3bf0b94 | |||
5712c24370 | |||
65bc522ae9 | |||
b950e07ad6 | |||
498bf8244c | |||
4a391c7ac2 | |||
d49c1e4b72 | |||
424e4670e5 | |||
5e803e76d7 | |||
6648397a64 | |||
85edbbcdc3 | |||
a64f8ac776 | |||
b46a0dfc21 | |||
95ef113aea | |||
07bf65b1c3 | |||
12071e4816 | |||
a40d4efa39 | |||
6d509d85f4 | |||
5b200f42a3 | |||
64f724ed95 | |||
b0d5be6221 | |||
2b21c9d348 | |||
f0a45fb7d8 | |||
40398efb06 | |||
a16c1336fc | |||
ef4df9691d | |||
6a23583391 | |||
e8d00161eb | |||
0a5a073db1 | |||
0f14685d54 | |||
d5888d5bbb | |||
8ff95aedd5 | |||
2b948a18f3 | |||
4d173ad163 | |||
7041b88b9d | |||
f06004370d | |||
c1fa344f02 | |||
df75b3b8d3 | |||
e0322c8a7f | |||
e3725dd3ab | |||
dc6a94ccda | |||
e5229a5377 | |||
2e8003ca95 | |||
04989372b1 | |||
d185dbb7ff | |||
77de085ffc | |||
afcae17e91 | |||
c985690e9a | |||
bb2a70b986 | |||
3ac3710273 | |||
92cb034155 | |||
2493bbbc97 | |||
77b42836e7 | |||
949615606f | |||
6cd7657e41 | |||
38bf5e8b1d | |||
4292cb2a04 | |||
051f53c66e | |||
a6bafb8adc | |||
99daa43fc6 | |||
e9e1f4ff5d | |||
13afc9c996 | |||
67d4da40ec | |||
27badad3d7 | |||
b5e3af6e7d | |||
280fbdfbb9 | |||
18fc10aaec | |||
b20e04bdeb | |||
10d14edc20 | |||
4abdd4216b | |||
332ed68c13 | |||
52feabd786 | |||
d7a99db66a | |||
fc0bdc25af | |||
ec633c3e3d | |||
5ffe45eaf5 | |||
8f795100ea | |||
1efdb31037 | |||
8d8a3efd77 | |||
44aa743d56 | |||
fefb71dd86 | |||
677180548b | |||
1748052cb0 | |||
293bea474e | |||
bc4fc9a1ca | |||
483850441d | |||
4355fd09cc | |||
1f85d9c486 | |||
c01a98ccf1 | |||
9ea9f90928 | |||
6319f53802 | |||
75d33820b3 | |||
074446df1f | |||
7ffa0ef8f5 | |||
5250e7c3d5 | |||
2deaa4eff3 | |||
0b6bc4c1f0 | |||
966294bd0e | |||
e1dee0678e | |||
8b25f202fe | |||
abbe7bbd0c | |||
565340dc50 | |||
36c428f152 | |||
f97826ea82 | |||
0f5cbf055c | |||
1345ff02e3 | |||
b960ee61d7 | |||
0b98a214a7 | |||
599c2226e4 | |||
8e24a4d3f8 | |||
27486e7600 | |||
979e9efbcb | |||
e06b5ecd1b | |||
1097ec64b2 | |||
93fe9929b7 | |||
aca654a993 | |||
b5cf237a4a | |||
6efb630200 | |||
151ede6cbf | |||
931ee1e8da | |||
0401793d38 | |||
eb31318d39 | |||
7f6dcd3afa | |||
2b4a6ad907 | |||
0613c12508 | |||
ba8fcb6891 | |||
c2df8cf869 | |||
e383872486 | |||
490c589a44 | |||
b358f2dbb7 | |||
10ed6f6b52 | |||
e0f1311f6d | |||
60d3ffac5d | |||
5e192539a1 | |||
021a8ddace | |||
f92aba14cd | |||
fdeefcdfcf | |||
645f70f770 | |||
923feb81f3 | |||
1cff92d000 | |||
db8f43385d | |||
41b45c212d | |||
ef9269fe10 | |||
4d95052896 | |||
260679b01d | |||
a77cc77be8 | |||
9bc5c55cd0 | |||
2cbad206b5 | |||
16c51af340 | |||
9fd37ca456 | |||
56b7328231 | |||
92bebf7d84 | |||
df053bbae9 | |||
42319f01a7 | |||
0ea9f9b60d | |||
33ce783fda | |||
63c48dc095 | |||
edefa7698c | |||
16eefe5bac | |||
b984111a73 | |||
677ff62b5c | |||
60ea4bb579 | |||
8cc2e08f24 | |||
04d553f052 | |||
d90178f49a | |||
ad50cff184 | |||
8e43d2a994 | |||
7074fdbac3 | |||
ef70de1e0b | |||
7e9ee7b5e3 | |||
517c613d05 | |||
ae8cf06ec6 | |||
818778ddc5 | |||
2e12d9a13c | |||
e678c9d1cf | |||
da0b07ce2a | |||
3306a9ca69 | |||
e9af34a6ba | |||
3de8ed169f | |||
d1eb350bdd | |||
0c1ccf7c2e | |||
d268f52a1c | |||
c519cee5d1 | |||
6d10afc9d2 | |||
b55a39dd24 | |||
7b880f85cc | |||
c7dc595e1a | |||
6e494f198b | |||
e1f3eaf1a0 | |||
be26dc9872 | |||
aaeb6e73fe | |||
1e11702c58 | |||
3b81cdb16e | |||
6584166815 | |||
827cb35194 | |||
89a6a0ba13 | |||
3b9a50d65d | |||
beb7200233 | |||
18e3d132a2 | |||
c2949964b3 | |||
52f8c6adba | |||
3d2b2cbbab | |||
1a82809bd5 | |||
c4f994750d | |||
fa7020949c | |||
eca2b3ccde | |||
67fc16ecd3 | |||
f85add7cca | |||
3f74d3a80d | |||
4a44dc6119 | |||
dd4bc4bc73 | |||
6188de43e4 | |||
36310387e0 | |||
43f3960225 | |||
2f0a442866 | |||
7e05bc86a9 | |||
b0c4fddf86 | |||
6faad102e2 | |||
f5578d39a6 | |||
8bfd3913da | |||
cd028ae133 | |||
63c71fabcd | |||
e90166f1f0 | |||
d1e5ae2d85 | |||
5a3fbc0401 | |||
7c52e000cd | |||
cccd4ba9e5 | |||
63f0f8e299 | |||
c8a3837432 | |||
2dd407b136 | |||
4e1a5565d8 | |||
bae62421ae | |||
d397002704 | |||
f5b1f671e3 | |||
0597c5f0c0 | |||
eb3afc8034 | |||
b67457fe93 | |||
75abdbe938 | |||
9b6a315825 | |||
13b2f65b7e | |||
6cf1e046b0 | |||
e5555ffd3f | |||
6b95bb0ceb | |||
f6e1441dc0 | |||
7ed96164e5 | |||
9eeb72ac80 | |||
f6e566a028 | |||
a34c74e958 | |||
eef7a875a1 | |||
09938a911b | |||
af08c41008 | |||
443c8854ea | |||
f7a25e7601 | |||
4c6e5c9c4c | |||
98a4e6c96d | |||
b0e25a8bd1 | |||
c93ce06409 | |||
672e4baec4 | |||
d483e70748 | |||
8adf4787b9 | |||
a12522db55 | |||
49ab487dc2 | |||
daf0731580 | |||
4b94848a79 | |||
b5ef2a6837 | |||
879b12002c | |||
9c611daada | |||
71edb08942 | |||
89d8261a43 | |||
a2b2b07185 | |||
76864ababa | |||
52858dad79 | |||
1d7a6ea50e | |||
c031233247 | |||
d17d40ebd9 | |||
70fff1f2da | |||
3f8eaa0679 | |||
50d0035d7b | |||
9743ad02d5 | |||
50f5248e3e | |||
8d7b573988 | |||
26d0ab1dc2 | |||
bc93db8603 | |||
4acdbd24e9 | |||
c3c907788a | |||
bf833a57cd | |||
e8519f6612 | |||
0b4675e7b5 | |||
091e521180 | |||
07df6803a5 | |||
d5dbc7d7e0 | |||
a09d0e8948 | |||
c43a87947f | |||
0af9415aa6 | |||
fb2b64cb19 | |||
ee598560ec | |||
2793ac22aa | |||
31fad03af8 | |||
ce612877b8 | |||
4ad8b468d5 | |||
5742fc648b | |||
c629705c9c | |||
aa68a3ef58 | |||
be10f6e52a | |||
40c5ff0ad6 | |||
8ecb5ca7bc | |||
ab6a2b7dbb | |||
81bfc04e7c | |||
a757fceaed | |||
ce8e18f620 | |||
d09c964647 | |||
eeddbde600 | |||
9e1d38a27b | |||
859b643e43 | |||
91f71e0ef6 | |||
4e9e31eeb7 | |||
f6bc99b964 | |||
679eb9dffc | |||
0754ae3aaf | |||
519a0c1bdf | |||
e9d8979cf4 | |||
486d975fa0 | |||
42c49949b4 | |||
aea44088db | |||
78d5bc823d | |||
578a0d7d93 | |||
cd71db416d | |||
9d682ca874 | |||
9054db80ad | |||
5bb8756c67 | |||
8b7cb4c4eb | |||
a6ee6fc4ea | |||
e584c9ea95 | |||
428c60880a | |||
2179b9a4d7 | |||
b21c17572d | |||
44c7be54cf | |||
45c08b3f09 | |||
57a29577fe | |||
2700a96df4 | |||
7457ef3b66 | |||
806df70dd7 | |||
8eda358c17 | |||
b34aabe72b | |||
1921763fa8 | |||
dfaed3c513 | |||
5408859a18 | |||
8dfc0cfbe0 | |||
060199e58c | |||
3b9b17f8d5 | |||
6addde2650 | |||
5b7627585f | |||
800ea5ce78 | |||
a6b3be72a9 | |||
394bd6755f | |||
c21873ac4b | |||
64b8c1a2de | |||
de443c5ea1 | |||
a3b7df4e6b | |||
531607dcb7 | |||
182de009b2 | |||
f1651ce171 | |||
e1f563dbd4 | |||
107cca0b62 | |||
72abc08f04 | |||
a4b648ad95 | |||
e8d424bbb0 | |||
04a8931cf6 | |||
ab0b8c0f10 | |||
258836a605 | |||
d6b31cde44 | |||
2c94f9ec3c | |||
42ad63b58d | |||
f2d5112585 | |||
9c7b25de49 | |||
0b31d7f860 | |||
5c91d380b8 | |||
b908893a68 | |||
4d0275e589 | |||
6ca7a990f3 | |||
befd77eec2 | |||
1d44774913 | |||
984552eea9 | |||
b6a957a30d | |||
36954a9df9 | |||
2f4efad8ae | |||
16c476d78c | |||
68c549f1c6 | |||
0610416677 | |||
4a37dc9cb7 | |||
7e432a4297 | |||
794fc9c2a2 | |||
d4e5d2c7ed | |||
581840a701 | |||
0c2e0bb0f9 | |||
e2a414ffff | |||
0ca3c2bb68 | |||
083581b51a | |||
40e976133c | |||
ad2f002822 | |||
8842dfe5d1 | |||
326742c2d5 | |||
f0c52cc8da | |||
e58dbe853e | |||
b1eea4ae9c | |||
a8e0a8aca3 | |||
b37058d0e2 | |||
c891b8f5d3 | |||
a32bb95703 | |||
334a05d5f1 | |||
12c813928c | |||
f493a617b1 | |||
521fef6fca | |||
8f8236c445 | |||
3cf5c534ff | |||
2b03c295f9 | |||
4fc7a52941 | |||
0ded2e51ba | |||
0d2b3adec7 | |||
e695203c05 | |||
f9d76aae5d | |||
1c280759d1 | |||
4562f57b54 | |||
6005dce44d | |||
0410c83cef | |||
cf4f2ea6b1 | |||
bf85df7e36 | |||
32a3e1d200 | |||
f7f7d2d528 | |||
57342cf2a0 | |||
d530604b51 | |||
229c7c0dcf | |||
6a79830e01 | |||
7447d17e94 | |||
4efa4ad8df | |||
722067f86c | |||
86bb2659b5 | |||
dc59f226b6 | |||
c6e56f0380 | |||
cd9792822b | |||
9175c1dffa | |||
210f1dc2a2 | |||
7851bb8710 | |||
f6e802c017 | |||
d28c87ee67 | |||
b6e6a3c6be | |||
54927454bf | |||
b9070a8fa3 | |||
1ce06891a5 | |||
3a8154eddc | |||
d61216ed62 | |||
95b6676976 | |||
15c0834d56 | |||
1e4dfd0c7c | |||
34b7d28e2f | |||
245a348517 | |||
e0fc582e2e | |||
68ef897b6a | |||
1b060e76de | |||
9f7599b2a1 | |||
edd415aed8 | |||
c816cbc9a9 | |||
416811d594 | |||
80a9d2bba9 | |||
f5e34ea59e | |||
9cbe70a6f3 | |||
f49fb534ab | |||
6eea4c8364 | |||
1e206ee441 | |||
bec3cec040 | |||
85c1a1081e | |||
d1122886fd | |||
3757f190f0 | |||
fec55bc9f8 | |||
a285a14fff | |||
9ec7d0d03e | |||
d5246c2891 | |||
dcb7215b7d | |||
c0f383ce1d | |||
0dcb223f80 | |||
877485b45a | |||
f9f098af86 | |||
6a5748150a | |||
3ef053f255 | |||
ed914d49ee | |||
8f7a652741 | |||
e43f583eb6 | |||
717c947e53 | |||
8ad334b3ab | |||
c7e707f20a | |||
d13e685a81 | |||
9849a5f136 | |||
26773a1444 | |||
3ea450e94a | |||
7d0574087c | |||
46755f724c | |||
e12f4ad253 | |||
36916704be | |||
5dbded60f4 | |||
a80d5f10e5 | |||
0faa8f4bb0 | |||
365b4b975e | |||
fbf634f7da | |||
47bb3c10fa | |||
1f3e7da3b7 | |||
81396f6b51 | |||
63279280fd | |||
66fbcc6806 | |||
a6f280197b | |||
346d2f213e | |||
f2d9593660 | |||
219964a242 | |||
9f1ac77afa | |||
240f558231 | |||
f3b3df1010 | |||
1fd6cd4787 | |||
a7d715ed08 | |||
a758503f40 | |||
550cb2b5ec | |||
75cb259c51 | |||
be2c5a9e57 | |||
a077a9d6f2 | |||
296493484f | |||
835b2fba9c | |||
92bc9d48af | |||
a9c1c197f7 | |||
5bd7dd4d65 | |||
8e2cfe2c03 | |||
0bb107d61d | |||
82c7dad6c8 | |||
83df0850ce | |||
ae43435509 | |||
580de0565b | |||
bbfd4a44c3 | |||
01e13ca7bd | |||
7811178261 | |||
f5fdd1a266 | |||
b21b0b340b | |||
fdbb930940 | |||
bda74ce13e | |||
9e56790886 | |||
e08c5f265e | |||
e7a55d8a27 | |||
35b8adb0f6 | |||
d161be1170 | |||
aabf933756 | |||
5d44d58ff4 | |||
1268bc1238 | |||
07e4bc8eed | |||
69ef7fdf3b | |||
ff294dab8d | |||
a01a9f3f77 | |||
6a973be6f3 | |||
c99440ba81 | |||
6d5a6f42e0 | |||
235be96ded | |||
d0a642a63a | |||
30471bfcad | |||
7f836ed9bc | |||
cf84dde0fa | |||
4d847ab2cb | |||
0c027fdc43 | |||
80cecbb937 | |||
727a6a7701 | |||
98bb5d7aa7 | |||
7f1f9e7fd0 | |||
b06eeb0d40 | |||
eedffffc38 | |||
5d366687a5 | |||
4720914839 | |||
9f487ad026 | |||
c70b9e665e | |||
d460e96052 | |||
e475774910 | |||
98f742a807 | |||
66f1967f88 | |||
da6cf85c8d | |||
e8b6eb0573 | |||
03ad5c5db0 | |||
e81c49500b | |||
e6c4c27a87 | |||
8b6c97d5bc | |||
5641d334cd | |||
2a28d74bde | |||
d4ac4f8d8f | |||
aedc6e16ad | |||
1ec7c67212 | |||
ff0ff622a6 | |||
511becabd8 | |||
f0229c5ecf | |||
8d711af23b | |||
7bd61d88fc | |||
a9a16c9bd1 | |||
929434d17f | |||
ba94b91974 | |||
b65f62fda8 | |||
c47d76a6c7 | |||
9138a9e71d | |||
8e4ad8baf8 | |||
9f158d5b3f | |||
0e1cb4ebb2 | |||
e959ed7fab | |||
4e4b1b689b | |||
8f07f43fbd | |||
023f5d1286 | |||
72b03d4bdf | |||
e870e35002 | |||
4544f621af | |||
ddb5098eda | |||
35749e8d12 | |||
ee2e2246da | |||
e30d400afa | |||
024ed0c0d8 | |||
e99e360339 | |||
85965184f8 | |||
a1bbd50c0b | |||
f9c936865a | |||
2be10b5f9d | |||
3b6e35e13c | |||
fcf984965e | |||
6bca854475 | |||
a69ce50da9 | |||
1b798bd5d5 | |||
bd3ebe75c9 | |||
0f2b8e4266 | |||
c4ae8f2987 | |||
b50a022d11 | |||
8a035c8d82 | |||
4fa7ba2ec7 | |||
03d7f9f786 | |||
1b3e8b0a1c | |||
6a26a11cbb | |||
d673c8d8e9 | |||
b39c7070b5 | |||
fa3dd03074 | |||
ee40ffd304 | |||
d3d76467ac | |||
58940f31e3 | |||
6d2175cf9f | |||
dbb0b28453 | |||
225862aed8 | |||
8d1bd6aabb | |||
740c650441 | |||
78ccb5acb7 | |||
e9aa8b317b | |||
7b42f666f9 | |||
8a0cfa34d2 | |||
ca9825c1fe | |||
1dfc9511c1 | |||
694ab35f53 | |||
f35cd2d6a6 | |||
b259428075 | |||
f54a10f626 | |||
63a3ce2dba | |||
9aabc3ced7 | |||
fe9ec6b030 | |||
44ae0519d1 | |||
3d89a7f45d | |||
de63c8cb6c | |||
632572f7c3 | |||
bef55043f7 | |||
0323d152da | |||
0a5f6274f5 | |||
11ee13676d | |||
e7783fe6cc | |||
a524690d01 | |||
c229d6888c | |||
b6566943c6 | |||
2e459c161d | |||
680f1a2230 | |||
68e21ba8ce | |||
1e9722474f | |||
f93edbb37f | |||
f345801bd6 | |||
f460acf9b4 | |||
4160009913 | |||
d5065af7e9 | |||
68e88ddef8 | |||
a2909b8030 | |||
3de5fa066b | |||
8987938642 | |||
3f00359459 | |||
a5b5b90ca1 | |||
fd0a00023b | |||
dd112b3850 | |||
b329b5aa4b | |||
c01c58fdcb | |||
4bba207552 | |||
8563eb850b | |||
4225bf6e0e | |||
fab385fdd9 | |||
a204629bef | |||
50679ba29d | |||
f5fa57d6c5 | |||
6088ae09ab | |||
0de15bf70c | |||
b5d229a7c5 | |||
92084ccd47 | |||
e0dc2dd6d8 | |||
418ac20f91 | |||
b377d2a6b1 | |||
350272aa57 | |||
95489e1b0a | |||
56b3e7a76d | |||
9ea6eca560 | |||
33dea34061 | |||
da68073e86 | |||
7bd312a287 | |||
d61e6752d6 | |||
636aee2ea9 | |||
d5888f9de7 | |||
1590b528bf | |||
e30a05e3e8 | |||
ce7798c48b | |||
75f1ce7b86 | |||
6ce1c4e19e | |||
f08de1599d | |||
a80520e425 | |||
4aa3552060 | |||
40781949a6 | |||
7d4f223174 | |||
ef47d0056f | |||
ccd7b0062e | |||
2ee423174a | |||
649f7b560f | |||
7219ba3b46 | |||
6e65656360 | |||
e0491c2056 | |||
b8db15563a | |||
9982ade219 | |||
9032bbe514 | |||
c403ffa9f6 | |||
1184ea1b11 | |||
7d97a76ecc | |||
a889f92528 |
27
.github/workflows/release_helm_gateway.yaml
vendored
Normal file
27
.github/workflows/release_helm_gateway.yaml
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
name: Release Gateway Helm Chart
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-helm:
|
||||
name: Release Helm Chart
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- 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-gateway-cloudsmith.sh
|
||||
env:
|
||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
@ -24,3 +24,19 @@ frontend/src/hooks/api/secretRotationsV2/types/index.ts:generic-api-key:65
|
||||
frontend/src/pages/secret-manager/SecretDashboardPage/components/SecretRotationListView/SecretRotationItem.tsx:generic-api-key:26
|
||||
docs/documentation/platform/kms/overview.mdx:generic-api-key:281
|
||||
docs/documentation/platform/kms/overview.mdx:generic-api-key:344
|
||||
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx:generic-api-key:85
|
||||
docs/cli/commands/user.mdx:generic-api-key:51
|
||||
frontend/src/pages/secret-manager/OverviewPage/components/SecretOverviewTableRow/SecretOverviewTableRow.tsx:generic-api-key:76
|
||||
docs/integrations/app-connections/hashicorp-vault.mdx:generic-api-key:188
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:567
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:569
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:570
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:572
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:574
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:575
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:576
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:577
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:578
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:579
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:581
|
||||
cli/detect/config/gitleaks.toml:gcp-api-key:582
|
||||
|
@ -133,8 +133,8 @@ RUN apt-get update && apt-get install -y \
|
||||
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsS.so\nFileUsage = 1\n" > /etc/odbcinst.ini
|
||||
|
||||
# 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.31.1 \
|
||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||
&& apt-get update && apt-get install -y infisical=0.41.2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -r -g 1001 nodejs && useradd -r -u 1001 -g nodejs non-root-user
|
||||
@ -171,6 +171,7 @@ ENV NODE_ENV production
|
||||
ENV STANDALONE_BUILD true
|
||||
ENV STANDALONE_MODE true
|
||||
ENV ChrystokiConfigurationPath=/usr/safenet/lunaclient/
|
||||
ENV NODE_OPTIONS="--max-old-space-size=1024"
|
||||
|
||||
WORKDIR /backend
|
||||
|
||||
|
@ -127,8 +127,8 @@ RUN apt-get update && apt-get install -y \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# 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.31.1 \
|
||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash \
|
||||
&& apt-get update && apt-get install -y infisical=0.41.2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
WORKDIR /
|
||||
@ -168,6 +168,7 @@ ENV HTTPS_ENABLED false
|
||||
ENV NODE_ENV production
|
||||
ENV STANDALONE_BUILD true
|
||||
ENV STANDALONE_MODE true
|
||||
ENV NODE_OPTIONS="--max-old-space-size=1024"
|
||||
|
||||
WORKDIR /backend
|
||||
|
||||
|
@ -69,6 +69,15 @@ module.exports = {
|
||||
["^\\."]
|
||||
]
|
||||
}
|
||||
],
|
||||
"import/extensions": [
|
||||
"error",
|
||||
"ignorePackages",
|
||||
{
|
||||
"": "never", // this is required to get the .tsx to work...
|
||||
ts: "never",
|
||||
tsx: "never"
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
@ -54,8 +54,8 @@ COPY --from=build /app .
|
||||
|
||||
# Install Infisical CLI
|
||||
RUN apt-get install -y curl bash && \
|
||||
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 git
|
||||
curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||
apt-get update && apt-get install -y infisical=0.41.2 git
|
||||
|
||||
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
|
||||
CMD node healthcheck.js
|
||||
|
@ -55,9 +55,9 @@ RUN mkdir -p /etc/softhsm2/tokens && \
|
||||
# ? App setup
|
||||
|
||||
# Install Infisical CLI
|
||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||
apt-get update && \
|
||||
apt-get install -y infisical=0.8.1
|
||||
apt-get install -y infisical=0.41.2
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -64,9 +64,9 @@ RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
|
||||
# ? App setup
|
||||
|
||||
# Install Infisical CLI
|
||||
RUN curl -1sLf 'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash && \
|
||||
RUN curl -1sLf 'https://artifacts-cli.infisical.com/setup.deb.sh' | bash && \
|
||||
apt-get update && \
|
||||
apt-get install -y infisical=0.8.1
|
||||
apt-get install -y infisical=0.41.2
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
import RE2 from "re2";
|
||||
|
||||
import { TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { applyJitter } from "@app/lib/dates";
|
||||
import { delay as delayMs } from "@app/lib/delay";
|
||||
import { Lock } from "@app/lib/red-lock";
|
||||
|
||||
export const mockKeyStore = (): TKeyStoreFactory => {
|
||||
@ -18,6 +22,27 @@ export const mockKeyStore = (): TKeyStoreFactory => {
|
||||
delete store[key];
|
||||
return 1;
|
||||
},
|
||||
deleteItems: async ({ pattern, batchSize = 500, delay = 1500, jitter = 200 }) => {
|
||||
const regex = new RE2(`^${pattern.replace(/[-[\]/{}()+?.\\^$|]/g, "\\$&").replace(/\*/g, ".*")}$`);
|
||||
let totalDeleted = 0;
|
||||
const keys = Object.keys(store);
|
||||
|
||||
for (let i = 0; i < keys.length; i += batchSize) {
|
||||
const batch = keys.slice(i, i + batchSize);
|
||||
|
||||
for (const key of batch) {
|
||||
if (regex.test(key)) {
|
||||
delete store[key];
|
||||
totalDeleted += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await delayMs(Math.max(0, applyJitter(delay, jitter)));
|
||||
}
|
||||
|
||||
return totalDeleted;
|
||||
},
|
||||
getItem: async (key) => {
|
||||
const value = store[key];
|
||||
if (typeof value === "string") {
|
||||
|
@ -15,8 +15,8 @@ import { mockSmtpServer } from "./mocks/smtp";
|
||||
import { initDbConnection } from "@app/db";
|
||||
import { queueServiceFactory } from "@app/queue";
|
||||
import { keyStoreFactory } from "@app/keystore/keystore";
|
||||
import { Redis } from "ioredis";
|
||||
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
|
||||
import { buildRedisFromConfig } from "@app/lib/config/redis";
|
||||
|
||||
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
|
||||
export default {
|
||||
@ -30,7 +30,7 @@ export default {
|
||||
dbRootCert: envConfig.DB_ROOT_CERT
|
||||
});
|
||||
|
||||
const redis = new Redis(envConfig.REDIS_URL);
|
||||
const redis = buildRedisFromConfig(envConfig);
|
||||
await redis.flushdb("SYNC");
|
||||
|
||||
try {
|
||||
@ -55,8 +55,8 @@ export default {
|
||||
});
|
||||
|
||||
const smtp = mockSmtpServer();
|
||||
const queue = queueServiceFactory(envConfig.REDIS_URL, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
||||
const keyStore = keyStoreFactory(envConfig.REDIS_URL);
|
||||
const queue = queueServiceFactory(envConfig, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
|
||||
const keyStore = keyStoreFactory(envConfig);
|
||||
|
||||
const hsmModule = initializeHsmModule(envConfig);
|
||||
hsmModule.initialize();
|
||||
|
7188
backend/package-lock.json
generated
7188
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -38,8 +38,8 @@
|
||||
"build:frontend": "npm run build --prefix ../frontend",
|
||||
"start": "node --enable-source-maps dist/main.mjs",
|
||||
"type:check": "tsc --noEmit",
|
||||
"lint:fix": "eslint --fix --ext js,ts ./src",
|
||||
"lint": "eslint 'src/**/*.ts'",
|
||||
"lint:fix": "node --max-old-space-size=8192 ./node_modules/.bin/eslint --fix --ext js,ts ./src",
|
||||
"lint": "node --max-old-space-size=8192 ./node_modules/.bin/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-watch": "vitest -c vitest.e2e.config.ts --bail=1",
|
||||
@ -72,7 +72,8 @@
|
||||
"seed:new": "tsx ./scripts/create-seed-file.ts",
|
||||
"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",
|
||||
"email:dev": "email dev --dir src/services/smtp/emails"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
@ -91,12 +92,12 @@
|
||||
"@types/lodash.isequal": "^4.5.8",
|
||||
"@types/node": "^20.17.30",
|
||||
"@types/nodemailer": "^6.4.14",
|
||||
"@types/passport-github": "^1.1.12",
|
||||
"@types/passport-google-oauth20": "^2.0.14",
|
||||
"@types/pg": "^8.10.9",
|
||||
"@types/picomatch": "^2.3.3",
|
||||
"@types/pkcs11js": "^1.0.4",
|
||||
"@types/prompt-sync": "^4.2.3",
|
||||
"@types/react": "^19.1.2",
|
||||
"@types/resolve": "^1.20.6",
|
||||
"@types/safe-regex": "^1.1.6",
|
||||
"@types/sjcl": "^1.0.34",
|
||||
@ -116,6 +117,7 @@
|
||||
"nodemon": "^3.0.2",
|
||||
"pino-pretty": "^10.2.3",
|
||||
"prompt-sync": "^4.2.0",
|
||||
"react-email": "4.0.7",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.2",
|
||||
"tsc-alias": "^1.8.8",
|
||||
@ -129,6 +131,7 @@
|
||||
"@aws-sdk/client-elasticache": "^3.637.0",
|
||||
"@aws-sdk/client-iam": "^3.525.0",
|
||||
"@aws-sdk/client-kms": "^3.609.0",
|
||||
"@aws-sdk/client-route-53": "^3.810.0",
|
||||
"@aws-sdk/client-secrets-manager": "^3.504.0",
|
||||
"@aws-sdk/client-sts": "^3.600.0",
|
||||
"@casl/ability": "^6.5.0",
|
||||
@ -150,6 +153,8 @@
|
||||
"@infisical/quic": "^1.0.8",
|
||||
"@node-saml/passport-saml": "^5.0.1",
|
||||
"@octokit/auth-app": "^7.1.1",
|
||||
"@octokit/core": "^5.2.1",
|
||||
"@octokit/plugin-paginate-graphql": "^4.0.1",
|
||||
"@octokit/plugin-retry": "^5.0.5",
|
||||
"@octokit/rest": "^20.0.2",
|
||||
"@octokit/webhooks-types": "^7.3.1",
|
||||
@ -164,17 +169,20 @@
|
||||
"@opentelemetry/semantic-conventions": "^1.27.0",
|
||||
"@peculiar/asn1-schema": "^2.3.8",
|
||||
"@peculiar/x509": "^1.12.1",
|
||||
"@react-email/components": "0.0.36",
|
||||
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
|
||||
"@sindresorhus/slugify": "1.1.0",
|
||||
"@slack/oauth": "^3.0.2",
|
||||
"@slack/web-api": "^7.8.0",
|
||||
"@ucast/mongo2js": "^1.3.4",
|
||||
"acme-client": "^5.4.0",
|
||||
"ajv": "^8.12.0",
|
||||
"argon2": "^0.31.2",
|
||||
"aws-sdk": "^2.1553.0",
|
||||
"axios": "^1.6.7",
|
||||
"axios-retry": "^4.0.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"botbuilder": "^4.23.2",
|
||||
"bullmq": "^5.4.2",
|
||||
"cassandra-driver": "^4.7.2",
|
||||
"connect-redis": "^7.1.1",
|
||||
@ -203,15 +211,16 @@
|
||||
"mysql2": "^3.9.8",
|
||||
"nanoid": "^3.3.8",
|
||||
"nodemailer": "^6.9.9",
|
||||
"oci-sdk": "^2.108.0",
|
||||
"odbc": "^2.4.9",
|
||||
"openid-client": "^5.6.5",
|
||||
"ora": "^7.0.1",
|
||||
"oracledb": "^6.4.0",
|
||||
"otplib": "^12.0.1",
|
||||
"passport-github": "^1.1.0",
|
||||
"passport-gitlab2": "^5.0.0",
|
||||
"passport-google-oauth20": "^2.0.0",
|
||||
"passport-ldapauth": "^3.0.1",
|
||||
"passport-oauth2": "^1.8.0",
|
||||
"pg": "^8.11.3",
|
||||
"pg-boss": "^10.1.5",
|
||||
"pg-query-stream": "^4.5.3",
|
||||
@ -222,6 +231,8 @@
|
||||
"posthog-node": "^3.6.2",
|
||||
"probot": "^13.3.8",
|
||||
"re2": "^1.21.4",
|
||||
"react": "19.1.0",
|
||||
"react-dom": "19.1.0",
|
||||
"safe-regex": "^2.1.1",
|
||||
"scim-patch": "^0.8.3",
|
||||
"scim2-parse-filter": "^0.2.10",
|
||||
@ -233,6 +244,6 @@
|
||||
"tweetnacl-util": "^0.15.1",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4",
|
||||
"zod-to-json-schema": "^3.22.4"
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
}
|
||||
}
|
||||
|
32
backend/src/@types/fastify.d.ts
vendored
32
backend/src/@types/fastify.d.ts
vendored
@ -5,6 +5,7 @@ import { Redis } from "ioredis";
|
||||
import { TUsers } from "@app/db/schemas";
|
||||
import { TAccessApprovalPolicyServiceFactory } from "@app/ee/services/access-approval-policy/access-approval-policy-service";
|
||||
import { TAccessApprovalRequestServiceFactory } from "@app/ee/services/access-approval-request/access-approval-request-service";
|
||||
import { TAssumePrivilegeServiceFactory } from "@app/ee/services/assume-privilege/assume-privilege-service";
|
||||
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { TAuditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service";
|
||||
@ -14,6 +15,7 @@ import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dy
|
||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
||||
import { TExternalKmsServiceFactory } from "@app/ee/services/external-kms/external-kms-service";
|
||||
import { TGatewayServiceFactory } from "@app/ee/services/gateway/gateway-service";
|
||||
import { TGithubOrgSyncServiceFactory } from "@app/ee/services/github-org-sync/github-org-sync-service";
|
||||
import { TGroupServiceFactory } from "@app/ee/services/group/group-service";
|
||||
import { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||
import { TIdentityProjectAdditionalPrivilegeV2ServiceFactory } from "@app/ee/services/identity-project-additional-privilege-v2/identity-project-additional-privilege-v2-service";
|
||||
@ -39,6 +41,7 @@ import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/
|
||||
import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
|
||||
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
|
||||
import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service";
|
||||
import { TSshHostGroupServiceFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-service";
|
||||
import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service";
|
||||
import { TAuthMode } from "@app/server/plugins/auth/inject-identity";
|
||||
import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service";
|
||||
@ -50,6 +53,7 @@ import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
|
||||
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
|
||||
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
|
||||
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
|
||||
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
|
||||
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
|
||||
@ -63,17 +67,22 @@ import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-a
|
||||
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||
import { TIdentityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/identity-jwt-auth-service";
|
||||
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||
import { TIdentityLdapAuthServiceFactory } from "@app/services/identity-ldap-auth/identity-ldap-auth-service";
|
||||
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
|
||||
import { TIdentityOciAuthServiceFactory } from "@app/services/identity-oci-auth/identity-oci-auth-service";
|
||||
import { TIdentityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
|
||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||
import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-auth/identity-token-auth-service";
|
||||
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
|
||||
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
|
||||
import { TOrgServiceFactory } from "@app/services/org/org-service";
|
||||
import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
|
||||
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
|
||||
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
|
||||
import { TPkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
|
||||
import { TProjectServiceFactory } from "@app/services/project/project-service";
|
||||
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
|
||||
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
|
||||
@ -102,6 +111,7 @@ import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integ
|
||||
declare module "@fastify/request-context" {
|
||||
interface RequestContextData {
|
||||
reqId: string;
|
||||
orgId?: string;
|
||||
identityAuthInfo?: {
|
||||
identityId: string;
|
||||
oidc?: {
|
||||
@ -109,12 +119,14 @@ declare module "@fastify/request-context" {
|
||||
};
|
||||
};
|
||||
identityPermissionMetadata?: Record<string, unknown>; // filled by permission service
|
||||
assumedPrivilegeDetails?: { requesterId: string; actorId: string; actorType: ActorType; projectId: string };
|
||||
}
|
||||
}
|
||||
|
||||
declare module "fastify" {
|
||||
interface Session {
|
||||
callbackPort: string;
|
||||
isAdminLogin: boolean;
|
||||
}
|
||||
|
||||
interface FastifyRequest {
|
||||
@ -138,6 +150,14 @@ declare module "fastify" {
|
||||
passportUser: {
|
||||
isUserCompleted: boolean;
|
||||
providerAuthToken: string;
|
||||
externalProviderAccessToken?: string;
|
||||
};
|
||||
passportMachineIdentity: {
|
||||
identityId: string;
|
||||
user: {
|
||||
uid: string;
|
||||
mail?: string;
|
||||
};
|
||||
};
|
||||
kmipUser: {
|
||||
projectId: string;
|
||||
@ -146,7 +166,9 @@ declare module "fastify" {
|
||||
};
|
||||
auditLogInfo: Pick<TCreateAuditLogDTO, "userAgent" | "userAgentType" | "ipAddress" | "actor">;
|
||||
ssoConfig: Awaited<ReturnType<TSamlConfigServiceFactory["getSaml"]>>;
|
||||
ldapConfig: Awaited<ReturnType<TLdapConfigServiceFactory["getLdapCfg"]>>;
|
||||
ldapConfig: Awaited<ReturnType<TLdapConfigServiceFactory["getLdapCfg"]>> & {
|
||||
allowedFields?: TAllowedFields[];
|
||||
};
|
||||
}
|
||||
|
||||
interface FastifyInstance {
|
||||
@ -190,8 +212,10 @@ declare module "fastify" {
|
||||
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
||||
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
||||
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
||||
identityOciAuth: TIdentityOciAuthServiceFactory;
|
||||
identityOidcAuth: TIdentityOidcAuthServiceFactory;
|
||||
identityJwtAuth: TIdentityJwtAuthServiceFactory;
|
||||
identityLdapAuth: TIdentityLdapAuthServiceFactory;
|
||||
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||
@ -208,10 +232,12 @@ declare module "fastify" {
|
||||
sshCertificateAuthority: TSshCertificateAuthorityServiceFactory;
|
||||
sshCertificateTemplate: TSshCertificateTemplateServiceFactory;
|
||||
sshHost: TSshHostServiceFactory;
|
||||
sshHostGroup: TSshHostGroupServiceFactory;
|
||||
certificateAuthority: TCertificateAuthorityServiceFactory;
|
||||
certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory;
|
||||
certificateEst: TCertificateEstServiceFactory;
|
||||
pkiCollection: TPkiCollectionServiceFactory;
|
||||
pkiSubscriber: TPkiSubscriberServiceFactory;
|
||||
secretScanning: TSecretScanningServiceFactory;
|
||||
license: TLicenseServiceFactory;
|
||||
trustedIp: TTrustedIpServiceFactory;
|
||||
@ -241,6 +267,10 @@ declare module "fastify" {
|
||||
kmipOperation: TKmipOperationServiceFactory;
|
||||
gateway: TGatewayServiceFactory;
|
||||
secretRotationV2: TSecretRotationV2ServiceFactory;
|
||||
microsoftTeams: TMicrosoftTeamsServiceFactory;
|
||||
assumePrivileges: TAssumePrivilegeServiceFactory;
|
||||
githubOrgSync: TGithubOrgSyncServiceFactory;
|
||||
internalCertificateAuthority: TInternalCertificateAuthorityServiceFactory;
|
||||
};
|
||||
// this is exclusive use for middlewares in which we need to inject data
|
||||
// everywhere else access using service layer
|
||||
|
96
backend/src/@types/knex.d.ts
vendored
96
backend/src/@types/knex.d.ts
vendored
@ -68,6 +68,9 @@ import {
|
||||
TDynamicSecrets,
|
||||
TDynamicSecretsInsert,
|
||||
TDynamicSecretsUpdate,
|
||||
TExternalCertificateAuthorities,
|
||||
TExternalCertificateAuthoritiesInsert,
|
||||
TExternalCertificateAuthoritiesUpdate,
|
||||
TExternalGroupOrgRoleMappings,
|
||||
TExternalGroupOrgRoleMappingsInsert,
|
||||
TExternalGroupOrgRoleMappingsUpdate,
|
||||
@ -83,6 +86,9 @@ import {
|
||||
TGitAppOrg,
|
||||
TGitAppOrgInsert,
|
||||
TGitAppOrgUpdate,
|
||||
TGithubOrgSyncConfigs,
|
||||
TGithubOrgSyncConfigsInsert,
|
||||
TGithubOrgSyncConfigsUpdate,
|
||||
TGroupProjectMembershipRoles,
|
||||
TGroupProjectMembershipRolesInsert,
|
||||
TGroupProjectMembershipRolesUpdate,
|
||||
@ -116,6 +122,9 @@ import {
|
||||
TIdentityMetadata,
|
||||
TIdentityMetadataInsert,
|
||||
TIdentityMetadataUpdate,
|
||||
TIdentityOciAuths,
|
||||
TIdentityOciAuthsInsert,
|
||||
TIdentityOciAuthsUpdate,
|
||||
TIdentityOidcAuths,
|
||||
TIdentityOidcAuthsInsert,
|
||||
TIdentityOidcAuthsUpdate,
|
||||
@ -149,6 +158,9 @@ import {
|
||||
TIntegrations,
|
||||
TIntegrationsInsert,
|
||||
TIntegrationsUpdate,
|
||||
TInternalCertificateAuthorities,
|
||||
TInternalCertificateAuthoritiesInsert,
|
||||
TInternalCertificateAuthoritiesUpdate,
|
||||
TInternalKms,
|
||||
TInternalKmsInsert,
|
||||
TInternalKmsUpdate,
|
||||
@ -206,6 +218,9 @@ import {
|
||||
TPkiCollections,
|
||||
TPkiCollectionsInsert,
|
||||
TPkiCollectionsUpdate,
|
||||
TPkiSubscribers,
|
||||
TPkiSubscribersInsert,
|
||||
TPkiSubscribersUpdate,
|
||||
TProjectBots,
|
||||
TProjectBotsInsert,
|
||||
TProjectBotsUpdate,
|
||||
@ -383,6 +398,12 @@ import {
|
||||
TSshCertificateTemplates,
|
||||
TSshCertificateTemplatesInsert,
|
||||
TSshCertificateTemplatesUpdate,
|
||||
TSshHostGroupMemberships,
|
||||
TSshHostGroupMembershipsInsert,
|
||||
TSshHostGroupMembershipsUpdate,
|
||||
TSshHostGroups,
|
||||
TSshHostGroupsInsert,
|
||||
TSshHostGroupsUpdate,
|
||||
TSshHostLoginUserMappings,
|
||||
TSshHostLoginUserMappingsInsert,
|
||||
TSshHostLoginUserMappingsUpdate,
|
||||
@ -423,6 +444,26 @@ import {
|
||||
TWorkflowIntegrationsInsert,
|
||||
TWorkflowIntegrationsUpdate
|
||||
} from "@app/db/schemas";
|
||||
import {
|
||||
TIdentityLdapAuths,
|
||||
TIdentityLdapAuthsInsert,
|
||||
TIdentityLdapAuthsUpdate
|
||||
} from "@app/db/schemas/identity-ldap-auths";
|
||||
import {
|
||||
TMicrosoftTeamsIntegrations,
|
||||
TMicrosoftTeamsIntegrationsInsert,
|
||||
TMicrosoftTeamsIntegrationsUpdate
|
||||
} from "@app/db/schemas/microsoft-teams-integrations";
|
||||
import {
|
||||
TProjectMicrosoftTeamsConfigs,
|
||||
TProjectMicrosoftTeamsConfigsInsert,
|
||||
TProjectMicrosoftTeamsConfigsUpdate
|
||||
} from "@app/db/schemas/project-microsoft-teams-configs";
|
||||
import {
|
||||
TSecretReminderRecipients,
|
||||
TSecretReminderRecipientsInsert,
|
||||
TSecretReminderRecipientsUpdate
|
||||
} from "@app/db/schemas/secret-reminder-recipients";
|
||||
|
||||
declare module "knex" {
|
||||
namespace Knex {
|
||||
@ -437,6 +478,16 @@ declare module "knex/types/tables" {
|
||||
interface Tables {
|
||||
[TableName.Users]: KnexOriginal.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
|
||||
[TableName.Groups]: KnexOriginal.CompositeTableType<TGroups, TGroupsInsert, TGroupsUpdate>;
|
||||
[TableName.SshHostGroup]: KnexOriginal.CompositeTableType<
|
||||
TSshHostGroups,
|
||||
TSshHostGroupsInsert,
|
||||
TSshHostGroupsUpdate
|
||||
>;
|
||||
[TableName.SshHostGroupMembership]: KnexOriginal.CompositeTableType<
|
||||
TSshHostGroupMemberships,
|
||||
TSshHostGroupMembershipsInsert,
|
||||
TSshHostGroupMembershipsUpdate
|
||||
>;
|
||||
[TableName.SshHost]: KnexOriginal.CompositeTableType<TSshHosts, TSshHostsInsert, TSshHostsUpdate>;
|
||||
[TableName.SshCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||
TSshCertificateAuthorities,
|
||||
@ -493,6 +544,16 @@ declare module "knex/types/tables" {
|
||||
TCertificateAuthorityCrlInsert,
|
||||
TCertificateAuthorityCrlUpdate
|
||||
>;
|
||||
[TableName.InternalCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||
TInternalCertificateAuthorities,
|
||||
TInternalCertificateAuthoritiesInsert,
|
||||
TInternalCertificateAuthoritiesUpdate
|
||||
>;
|
||||
[TableName.ExternalCertificateAuthority]: KnexOriginal.CompositeTableType<
|
||||
TExternalCertificateAuthorities,
|
||||
TExternalCertificateAuthoritiesInsert,
|
||||
TExternalCertificateAuthoritiesUpdate
|
||||
>;
|
||||
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
|
||||
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
|
||||
TCertificateTemplates,
|
||||
@ -525,6 +586,11 @@ declare module "knex/types/tables" {
|
||||
TPkiCollectionItemsInsert,
|
||||
TPkiCollectionItemsUpdate
|
||||
>;
|
||||
[TableName.PkiSubscriber]: KnexOriginal.CompositeTableType<
|
||||
TPkiSubscribers,
|
||||
TPkiSubscribersInsert,
|
||||
TPkiSubscribersUpdate
|
||||
>;
|
||||
[TableName.UserGroupMembership]: KnexOriginal.CompositeTableType<
|
||||
TUserGroupMembership,
|
||||
TUserGroupMembershipInsert,
|
||||
@ -691,6 +757,11 @@ declare module "knex/types/tables" {
|
||||
TIdentityAzureAuthsInsert,
|
||||
TIdentityAzureAuthsUpdate
|
||||
>;
|
||||
[TableName.IdentityOciAuth]: KnexOriginal.CompositeTableType<
|
||||
TIdentityOciAuths,
|
||||
TIdentityOciAuthsInsert,
|
||||
TIdentityOciAuthsUpdate
|
||||
>;
|
||||
[TableName.IdentityOidcAuth]: KnexOriginal.CompositeTableType<
|
||||
TIdentityOidcAuths,
|
||||
TIdentityOidcAuthsInsert,
|
||||
@ -701,6 +772,11 @@ declare module "knex/types/tables" {
|
||||
TIdentityJwtAuthsInsert,
|
||||
TIdentityJwtAuthsUpdate
|
||||
>;
|
||||
[TableName.IdentityLdapAuth]: KnexOriginal.CompositeTableType<
|
||||
TIdentityLdapAuths,
|
||||
TIdentityLdapAuthsInsert,
|
||||
TIdentityLdapAuthsUpdate
|
||||
>;
|
||||
[TableName.IdentityUaClientSecret]: KnexOriginal.CompositeTableType<
|
||||
TIdentityUaClientSecrets,
|
||||
TIdentityUaClientSecretsInsert,
|
||||
@ -994,5 +1070,25 @@ declare module "knex/types/tables" {
|
||||
TSecretRotationV2SecretMappingsInsert,
|
||||
TSecretRotationV2SecretMappingsUpdate
|
||||
>;
|
||||
[TableName.MicrosoftTeamsIntegrations]: KnexOriginal.CompositeTableType<
|
||||
TMicrosoftTeamsIntegrations,
|
||||
TMicrosoftTeamsIntegrationsInsert,
|
||||
TMicrosoftTeamsIntegrationsUpdate
|
||||
>;
|
||||
[TableName.ProjectMicrosoftTeamsConfigs]: KnexOriginal.CompositeTableType<
|
||||
TProjectMicrosoftTeamsConfigs,
|
||||
TProjectMicrosoftTeamsConfigsInsert,
|
||||
TProjectMicrosoftTeamsConfigsUpdate
|
||||
>;
|
||||
[TableName.SecretReminderRecipients]: KnexOriginal.CompositeTableType<
|
||||
TSecretReminderRecipients,
|
||||
TSecretReminderRecipientsInsert,
|
||||
TSecretReminderRecipientsUpdate
|
||||
>;
|
||||
[TableName.GithubOrgSyncConfig]: KnexOriginal.CompositeTableType<
|
||||
TGithubOrgSyncConfigs,
|
||||
TGithubOrgSyncConfigsInsert,
|
||||
TGithubOrgSyncConfigsUpdate
|
||||
>;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasSecretReminderRecipientsTable = await knex.schema.hasTable(TableName.SecretReminderRecipients);
|
||||
|
||||
if (!hasSecretReminderRecipientsTable) {
|
||||
await knex.schema.createTable(TableName.SecretReminderRecipients, (table) => {
|
||||
table.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
table.timestamps(true, true, true);
|
||||
table.uuid("secretId").notNullable();
|
||||
table.uuid("userId").notNullable();
|
||||
table.string("projectId").notNullable();
|
||||
|
||||
// Based on userId rather than project membership ID so we can easily extend group support in the future if need be.
|
||||
// This does however mean we need to manually clean up once a user is removed from a project.
|
||||
table.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||
table.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
|
||||
table.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
|
||||
table.index("secretId");
|
||||
table.unique(["secretId", "userId"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasSecretReminderRecipientsTable = await knex.schema.hasTable(TableName.SecretReminderRecipients);
|
||||
|
||||
if (hasSecretReminderRecipientsTable) {
|
||||
await knex.schema.dropTableIfExists(TableName.SecretReminderRecipients);
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const superAdminHasEncryptedMicrosoftTeamsClientIdColumn = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsAppId"
|
||||
);
|
||||
const superAdminHasEncryptedMicrosoftTeamsClientSecret = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsClientSecret"
|
||||
);
|
||||
const superAdminHasEncryptedMicrosoftTeamsBotId = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsBotId"
|
||||
);
|
||||
|
||||
if (
|
||||
!superAdminHasEncryptedMicrosoftTeamsClientIdColumn ||
|
||||
!superAdminHasEncryptedMicrosoftTeamsClientSecret ||
|
||||
!superAdminHasEncryptedMicrosoftTeamsBotId
|
||||
) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (table) => {
|
||||
if (!superAdminHasEncryptedMicrosoftTeamsClientIdColumn) {
|
||||
table.binary("encryptedMicrosoftTeamsAppId").nullable();
|
||||
}
|
||||
if (!superAdminHasEncryptedMicrosoftTeamsClientSecret) {
|
||||
table.binary("encryptedMicrosoftTeamsClientSecret").nullable();
|
||||
}
|
||||
if (!superAdminHasEncryptedMicrosoftTeamsBotId) {
|
||||
table.binary("encryptedMicrosoftTeamsBotId").nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.WorkflowIntegrations, "status"))) {
|
||||
await knex.schema.alterTable(TableName.WorkflowIntegrations, (table) => {
|
||||
table.enu("status", ["pending", "installed", "failed"]).notNullable().defaultTo("installed"); // defaults to installed so we can have backwards compatibility with existing workflow integrations
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasTable(TableName.MicrosoftTeamsIntegrations))) {
|
||||
await knex.schema.createTable(TableName.MicrosoftTeamsIntegrations, (table) => {
|
||||
table.uuid("id", { primaryKey: true }).notNullable();
|
||||
table.foreign("id").references("id").inTable(TableName.WorkflowIntegrations).onDelete("CASCADE"); // the ID itself is the workflow integration ID
|
||||
|
||||
table.string("internalTeamsAppId").nullable();
|
||||
table.string("tenantId").notNullable();
|
||||
table.binary("encryptedAccessToken").nullable();
|
||||
table.binary("encryptedBotAccessToken").nullable();
|
||||
|
||||
table.timestamp("accessTokenExpiresAt").nullable();
|
||||
table.timestamp("botAccessTokenExpiresAt").nullable();
|
||||
|
||||
table.timestamps(true, true, true);
|
||||
});
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.MicrosoftTeamsIntegrations);
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasTable(TableName.ProjectMicrosoftTeamsConfigs))) {
|
||||
await knex.schema.createTable(TableName.ProjectMicrosoftTeamsConfigs, (tb) => {
|
||||
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
tb.string("projectId").notNullable().unique();
|
||||
tb.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
tb.uuid("microsoftTeamsIntegrationId").notNullable();
|
||||
tb.foreign("microsoftTeamsIntegrationId")
|
||||
.references("id")
|
||||
.inTable(TableName.MicrosoftTeamsIntegrations)
|
||||
.onDelete("CASCADE");
|
||||
tb.boolean("isAccessRequestNotificationEnabled").notNullable().defaultTo(false);
|
||||
tb.boolean("isSecretRequestNotificationEnabled").notNullable().defaultTo(false);
|
||||
|
||||
tb.jsonb("accessRequestChannels").notNullable(); // {teamId: string, channelIds: string[]}
|
||||
tb.jsonb("secretRequestChannels").notNullable(); // {teamId: string, channelIds: string[]}
|
||||
tb.timestamps(true, true, true);
|
||||
});
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.ProjectMicrosoftTeamsConfigs);
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasEncryptedMicrosoftTeamsClientIdColumn = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsAppId"
|
||||
);
|
||||
const hasEncryptedMicrosoftTeamsClientSecret = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsClientSecret"
|
||||
);
|
||||
const hasEncryptedMicrosoftTeamsBotId = await knex.schema.hasColumn(
|
||||
TableName.SuperAdmin,
|
||||
"encryptedMicrosoftTeamsBotId"
|
||||
);
|
||||
|
||||
if (
|
||||
hasEncryptedMicrosoftTeamsClientIdColumn ||
|
||||
hasEncryptedMicrosoftTeamsClientSecret ||
|
||||
hasEncryptedMicrosoftTeamsBotId
|
||||
) {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (table) => {
|
||||
if (hasEncryptedMicrosoftTeamsClientIdColumn) {
|
||||
table.dropColumn("encryptedMicrosoftTeamsAppId");
|
||||
}
|
||||
if (hasEncryptedMicrosoftTeamsClientSecret) {
|
||||
table.dropColumn("encryptedMicrosoftTeamsClientSecret");
|
||||
}
|
||||
if (hasEncryptedMicrosoftTeamsBotId) {
|
||||
table.dropColumn("encryptedMicrosoftTeamsBotId");
|
||||
}
|
||||
});
|
||||
}
|
||||
if (await knex.schema.hasColumn(TableName.WorkflowIntegrations, "status")) {
|
||||
await knex.schema.alterTable(TableName.WorkflowIntegrations, (table) => {
|
||||
table.dropColumn("status");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.ProjectMicrosoftTeamsConfigs)) {
|
||||
await knex.schema.dropTableIfExists(TableName.ProjectMicrosoftTeamsConfigs);
|
||||
await dropOnUpdateTrigger(knex, TableName.ProjectMicrosoftTeamsConfigs);
|
||||
}
|
||||
if (await knex.schema.hasTable(TableName.MicrosoftTeamsIntegrations)) {
|
||||
await knex.schema.dropTableIfExists(TableName.MicrosoftTeamsIntegrations);
|
||||
await dropOnUpdateTrigger(knex, TableName.MicrosoftTeamsIntegrations);
|
||||
}
|
||||
}
|
23
backend/src/db/migrations/20250426044605_ssh-host-alias.ts
Normal file
23
backend/src/db/migrations/20250426044605_ssh-host-alias.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasAliasColumn = await knex.schema.hasColumn(TableName.SshHost, "alias");
|
||||
if (!hasAliasColumn) {
|
||||
await knex.schema.alterTable(TableName.SshHost, (t) => {
|
||||
t.string("alias").nullable();
|
||||
t.unique(["projectId", "alias"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasAliasColumn = await knex.schema.hasColumn(TableName.SshHost, "alias");
|
||||
if (hasAliasColumn) {
|
||||
await knex.schema.alterTable(TableName.SshHost, (t) => {
|
||||
t.dropUnique(["projectId", "alias"]);
|
||||
t.dropColumn("alias");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasTable = await knex.schema.hasTable(TableName.GithubOrgSyncConfig);
|
||||
if (!hasTable) {
|
||||
await knex.schema.createTable(TableName.GithubOrgSyncConfig, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("githubOrgName").notNullable();
|
||||
t.boolean("isActive").defaultTo(false);
|
||||
t.binary("encryptedGithubOrgAccessToken");
|
||||
t.uuid("orgId").notNullable().unique();
|
||||
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.GithubOrgSyncConfig);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.GithubOrgSyncConfig);
|
||||
await dropOnUpdateTrigger(knex, TableName.GithubOrgSyncConfig);
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const appCfg = getConfig();
|
||||
const tokenDuration = appCfg?.JWT_REFRESH_LIFETIME;
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.Organization, "userTokenExpiration"))) {
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
t.string("userTokenExpiration");
|
||||
});
|
||||
if (tokenDuration) {
|
||||
await knex(TableName.Organization).update({ userTokenExpiration: tokenDuration });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.Organization, "userTokenExpiration")) {
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
t.dropColumn("userTokenExpiration");
|
||||
});
|
||||
}
|
||||
}
|
55
backend/src/db/migrations/20250428173025_ssh-host-groups.ts
Normal file
55
backend/src/db/migrations/20250428173025_ssh-host-groups.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.SshHostGroup))) {
|
||||
await knex.schema.createTable(TableName.SshHostGroup, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.timestamps(true, true, true);
|
||||
t.string("projectId").notNullable();
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
t.string("name").notNullable();
|
||||
t.unique(["projectId", "name"]);
|
||||
});
|
||||
await createOnUpdateTrigger(knex, TableName.SshHostGroup);
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasTable(TableName.SshHostGroupMembership))) {
|
||||
await knex.schema.createTable(TableName.SshHostGroupMembership, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.timestamps(true, true, true);
|
||||
t.uuid("sshHostGroupId").notNullable();
|
||||
t.foreign("sshHostGroupId").references("id").inTable(TableName.SshHostGroup).onDelete("CASCADE");
|
||||
t.uuid("sshHostId").notNullable();
|
||||
t.foreign("sshHostId").references("id").inTable(TableName.SshHost).onDelete("CASCADE");
|
||||
t.unique(["sshHostGroupId", "sshHostId"]);
|
||||
});
|
||||
await createOnUpdateTrigger(knex, TableName.SshHostGroupMembership);
|
||||
}
|
||||
|
||||
const hasGroupColumn = await knex.schema.hasColumn(TableName.SshHostLoginUser, "sshHostGroupId");
|
||||
if (!hasGroupColumn) {
|
||||
await knex.schema.alterTable(TableName.SshHostLoginUser, (t) => {
|
||||
t.uuid("sshHostGroupId").nullable();
|
||||
t.foreign("sshHostGroupId").references("id").inTable(TableName.SshHostGroup).onDelete("CASCADE");
|
||||
t.uuid("sshHostId").nullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasGroupColumn = await knex.schema.hasColumn(TableName.SshHostLoginUser, "sshHostGroupId");
|
||||
if (hasGroupColumn) {
|
||||
await knex.schema.alterTable(TableName.SshHostLoginUser, (t) => {
|
||||
t.dropColumn("sshHostGroupId");
|
||||
});
|
||||
}
|
||||
|
||||
await knex.schema.dropTableIfExists(TableName.SshHostGroupMembership);
|
||||
await dropOnUpdateTrigger(knex, TableName.SshHostGroupMembership);
|
||||
|
||||
await knex.schema.dropTableIfExists(TableName.SshHostGroup);
|
||||
await dropOnUpdateTrigger(knex, TableName.SshHostGroup);
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||
const hasProjectIdColumn = await knex.schema.hasColumn(TableName.Certificate, "projectId");
|
||||
if (!hasProjectIdColumn) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.string("projectId", 36).nullable();
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
});
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE "${TableName.Certificate}" cert
|
||||
SET "projectId" = ca."projectId"
|
||||
FROM "${TableName.CertificateAuthority}" ca
|
||||
WHERE cert."caId" = ca.id
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.string("projectId").notNullable().alter();
|
||||
});
|
||||
}
|
||||
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.uuid("caId").nullable().alter();
|
||||
t.uuid("caCertId").nullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.Certificate)) {
|
||||
if (await knex.schema.hasColumn(TableName.Certificate, "projectId")) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.dropForeign("projectId");
|
||||
t.dropColumn("projectId");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Altering back to notNullable for caId and caCertId will fail
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.CertificateBody, "encryptedCertificateChain"))) {
|
||||
await knex.schema.alterTable(TableName.CertificateBody, (t) => {
|
||||
t.binary("encryptedCertificateChain").nullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasTable(TableName.CertificateSecret))) {
|
||||
await knex.schema.createTable(TableName.CertificateSecret, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.timestamps(true, true, true);
|
||||
t.uuid("certId").notNullable().unique();
|
||||
t.foreign("certId").references("id").inTable(TableName.Certificate).onDelete("CASCADE");
|
||||
t.binary("encryptedPrivateKey").notNullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.CertificateSecret)) {
|
||||
await knex.schema.dropTable(TableName.CertificateSecret);
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.CertificateBody, "encryptedCertificateChain")) {
|
||||
await knex.schema.alterTable(TableName.CertificateBody, (t) => {
|
||||
t.dropColumn("encryptedCertificateChain");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasEmail = await knex.schema.hasColumn(TableName.Users, "email");
|
||||
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
|
||||
if (hasEmail) {
|
||||
await knex(TableName.Users)
|
||||
.where({ isGhost: false })
|
||||
.update({
|
||||
// @ts-expect-error email assume string this is expected
|
||||
email: knex.raw("lower(email)")
|
||||
});
|
||||
}
|
||||
if (hasUsername) {
|
||||
await knex.schema.raw(`
|
||||
CREATE INDEX IF NOT EXISTS ${TableName.Users}_lower_username_idx
|
||||
ON ${TableName.Users} (LOWER(username))
|
||||
`);
|
||||
|
||||
const duplicatesSubquery = knex(TableName.Users)
|
||||
.select(knex.raw("lower(username) as lowercase_username"))
|
||||
.groupBy("lowercase_username")
|
||||
.having(knex.raw("count(*)"), ">", 1);
|
||||
|
||||
// Update usernames to lowercase where they won't create duplicates
|
||||
await knex(TableName.Users)
|
||||
.where({ isGhost: false })
|
||||
.whereRaw("username <> lower(username)") // Only update if not already lowercase
|
||||
// @ts-expect-error username assume string this is expected
|
||||
.whereNotIn(knex.raw("lower(username)"), duplicatesSubquery)
|
||||
.update({
|
||||
// @ts-expect-error username assume string this is expected
|
||||
username: knex.raw("lower(username)")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
|
||||
if (hasUsername) {
|
||||
await knex.schema.raw(`
|
||||
DROP INDEX IF EXISTS ${TableName.Users}_lower_username_idx
|
||||
`);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.SshHostLoginUserMapping, "groupId"))) {
|
||||
await knex.schema.alterTable(TableName.SshHostLoginUserMapping, (t) => {
|
||||
t.uuid("groupId").nullable();
|
||||
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||
t.unique(["sshHostLoginUserId", "groupId"]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.SshHostLoginUserMapping, "groupId")) {
|
||||
await knex.schema.alterTable(TableName.SshHostLoginUserMapping, (t) => {
|
||||
t.dropUnique(["sshHostLoginUserId", "groupId"]);
|
||||
t.dropColumn("groupId");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { ProjectType, TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.ProjectTemplates, "type"))) {
|
||||
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||
// defaulting to sm for migration to set existing, new ones will always be specified on creation
|
||||
t.string("type").defaultTo(ProjectType.SecretManager).notNullable();
|
||||
t.jsonb("environments").nullable().alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.ProjectTemplates, "type")) {
|
||||
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||
t.dropColumn("type");
|
||||
// not reverting nullable environments
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.IdentityLdapAuth))) {
|
||||
await knex.schema.createTable(TableName.IdentityLdapAuth, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
|
||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
||||
|
||||
t.uuid("identityId").notNullable().unique();
|
||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||
|
||||
t.binary("encryptedBindDN").notNullable();
|
||||
t.binary("encryptedBindPass").notNullable();
|
||||
t.binary("encryptedLdapCaCertificate").nullable();
|
||||
|
||||
t.string("url").notNullable();
|
||||
t.string("searchBase").notNullable();
|
||||
t.string("searchFilter").notNullable();
|
||||
|
||||
t.jsonb("allowedFields").nullable();
|
||||
|
||||
t.timestamps(true, true, true);
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.IdentityLdapAuth);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.IdentityLdapAuth);
|
||||
await dropOnUpdateTrigger(knex, TableName.IdentityLdapAuth);
|
||||
}
|
46
backend/src/db/migrations/20250508160957_pki-subscriber.ts
Normal file
46
backend/src/db/migrations/20250508160957_pki-subscriber.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.PkiSubscriber))) {
|
||||
await knex.schema.createTable(TableName.PkiSubscriber, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.timestamps(true, true, true);
|
||||
t.string("projectId").notNullable();
|
||||
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||
t.uuid("caId").nullable();
|
||||
t.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("SET NULL");
|
||||
t.string("name").notNullable();
|
||||
t.string("commonName").notNullable();
|
||||
t.specificType("subjectAlternativeNames", "text[]").notNullable();
|
||||
t.string("ttl").notNullable();
|
||||
t.specificType("keyUsages", "text[]").notNullable();
|
||||
t.specificType("extendedKeyUsages", "text[]").notNullable();
|
||||
t.string("status").notNullable(); // active / disabled
|
||||
t.unique(["projectId", "name"]);
|
||||
});
|
||||
await createOnUpdateTrigger(knex, TableName.PkiSubscriber);
|
||||
}
|
||||
|
||||
const hasSubscriberCol = await knex.schema.hasColumn(TableName.Certificate, "pkiSubscriberId");
|
||||
if (!hasSubscriberCol) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.uuid("pkiSubscriberId").nullable();
|
||||
t.foreign("pkiSubscriberId").references("id").inTable(TableName.PkiSubscriber).onDelete("SET NULL");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasSubscriberCol = await knex.schema.hasColumn(TableName.Certificate, "pkiSubscriberId");
|
||||
if (hasSubscriberCol) {
|
||||
await knex.schema.alterTable(TableName.Certificate, (t) => {
|
||||
t.dropColumn("pkiSubscriberId");
|
||||
});
|
||||
}
|
||||
|
||||
await knex.schema.dropTableIfExists(TableName.PkiSubscriber);
|
||||
await dropOnUpdateTrigger(knex, TableName.PkiSubscriber);
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasTable(TableName.IdentityOciAuth))) {
|
||||
await knex.schema.createTable(TableName.IdentityOciAuth, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
|
||||
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
|
||||
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
|
||||
t.jsonb("accessTokenTrustedIps").notNullable();
|
||||
t.timestamps(true, true, true);
|
||||
t.uuid("identityId").notNullable().unique();
|
||||
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
|
||||
t.string("type").notNullable();
|
||||
|
||||
t.string("tenancyOcid").notNullable();
|
||||
t.string("allowedUsernames").nullable();
|
||||
});
|
||||
}
|
||||
|
||||
await createOnUpdateTrigger(knex, TableName.IdentityOciAuth);
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
await knex.schema.dropTableIfExists(TableName.IdentityOciAuth);
|
||||
await dropOnUpdateTrigger(knex, TableName.IdentityOciAuth);
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
|
||||
|
||||
if (!hasGatewayIdColumn) {
|
||||
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||
table.uuid("gatewayId").nullable();
|
||||
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
|
||||
|
||||
if (hasGatewayIdColumn) {
|
||||
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
|
||||
table.dropForeign("gatewayId");
|
||||
table.dropColumn("gatewayId");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { inMemoryKeyStore } from "@app/keystore/memory";
|
||||
import { selectAllTableCols } from "@app/lib/knex";
|
||||
import { initLogger } from "@app/lib/logger";
|
||||
import { KmsDataKey } from "@app/services/kms/kms-types";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
import { getMigrationEnvConfig } from "./utils/env-config";
|
||||
import { getMigrationEncryptionServices } from "./utils/services";
|
||||
|
||||
// Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed.
|
||||
// In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely.
|
||||
|
||||
const BATCH_SIZE = 500;
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
knex.replicaNode = () => {
|
||||
return knex;
|
||||
};
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId"))) {
|
||||
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||
table.uuid("gatewayId").nullable();
|
||||
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
|
||||
|
||||
table.index("gatewayId");
|
||||
});
|
||||
|
||||
const existingDynamicSecretsWithProjectGatewayId = await knex(TableName.DynamicSecret)
|
||||
.select(selectAllTableCols(TableName.DynamicSecret))
|
||||
.whereNotNull(`${TableName.DynamicSecret}.projectGatewayId`)
|
||||
.join(TableName.ProjectGateway, `${TableName.ProjectGateway}.id`, `${TableName.DynamicSecret}.projectGatewayId`)
|
||||
.whereNotNull(`${TableName.ProjectGateway}.gatewayId`)
|
||||
.select(
|
||||
knex.ref("projectId").withSchema(TableName.ProjectGateway).as("projectId"),
|
||||
knex.ref("gatewayId").withSchema(TableName.ProjectGateway).as("projectGatewayGatewayId")
|
||||
);
|
||||
|
||||
initLogger();
|
||||
const envConfig = getMigrationEnvConfig();
|
||||
const keyStore = inMemoryKeyStore();
|
||||
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
|
||||
|
||||
const updatedDynamicSecrets = await Promise.all(
|
||||
existingDynamicSecretsWithProjectGatewayId.map(async (existingDynamicSecret) => {
|
||||
if (!existingDynamicSecret.projectGatewayGatewayId) {
|
||||
const result = {
|
||||
...existingDynamicSecret,
|
||||
gatewayId: null
|
||||
};
|
||||
|
||||
const { projectId, projectGatewayGatewayId, ...rest } = result;
|
||||
return rest;
|
||||
}
|
||||
|
||||
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId: existingDynamicSecret.projectId
|
||||
});
|
||||
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
|
||||
type: KmsDataKey.SecretManager,
|
||||
projectId: existingDynamicSecret.projectId
|
||||
});
|
||||
|
||||
let decryptedStoredInput = JSON.parse(
|
||||
secretManagerDecryptor({ cipherTextBlob: Buffer.from(existingDynamicSecret.encryptedInput) }).toString()
|
||||
) as object;
|
||||
|
||||
// We're not removing the existing projectGatewayId from the input so we can easily rollback without having to re-encrypt the input
|
||||
decryptedStoredInput = {
|
||||
...decryptedStoredInput,
|
||||
gatewayId: existingDynamicSecret.projectGatewayGatewayId
|
||||
};
|
||||
|
||||
const encryptedInput = secretManagerEncryptor({
|
||||
plainText: Buffer.from(JSON.stringify(decryptedStoredInput))
|
||||
}).cipherTextBlob;
|
||||
|
||||
const result = {
|
||||
...existingDynamicSecret,
|
||||
encryptedInput,
|
||||
gatewayId: existingDynamicSecret.projectGatewayGatewayId
|
||||
};
|
||||
|
||||
const { projectId, projectGatewayGatewayId, ...rest } = result;
|
||||
return rest;
|
||||
})
|
||||
);
|
||||
|
||||
for (let i = 0; i < updatedDynamicSecrets.length; i += BATCH_SIZE) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await knex(TableName.DynamicSecret)
|
||||
.insert(updatedDynamicSecrets.slice(i, i + BATCH_SIZE))
|
||||
.onConflict("id")
|
||||
.merge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
// no re-encryption needed as we keep the old projectGatewayId in the input
|
||||
if (await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId")) {
|
||||
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
|
||||
table.dropForeign("gatewayId");
|
||||
table.dropColumn("gatewayId");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const columns = await knex.table(TableName.Organization).columnInfo();
|
||||
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
if (!columns.secretsProductEnabled) {
|
||||
t.boolean("secretsProductEnabled").defaultTo(true);
|
||||
}
|
||||
if (!columns.pkiProductEnabled) {
|
||||
t.boolean("pkiProductEnabled").defaultTo(true);
|
||||
}
|
||||
if (!columns.kmsProductEnabled) {
|
||||
t.boolean("kmsProductEnabled").defaultTo(true);
|
||||
}
|
||||
if (!columns.sshProductEnabled) {
|
||||
t.boolean("sshProductEnabled").defaultTo(true);
|
||||
}
|
||||
if (!columns.scannerProductEnabled) {
|
||||
t.boolean("scannerProductEnabled").defaultTo(true);
|
||||
}
|
||||
if (!columns.shareSecretsProductEnabled) {
|
||||
t.boolean("shareSecretsProductEnabled").defaultTo(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const columns = await knex.table(TableName.Organization).columnInfo();
|
||||
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
if (columns.secretsProductEnabled) {
|
||||
t.dropColumn("secretsProductEnabled");
|
||||
}
|
||||
if (columns.pkiProductEnabled) {
|
||||
t.dropColumn("pkiProductEnabled");
|
||||
}
|
||||
if (columns.kmsProductEnabled) {
|
||||
t.dropColumn("kmsProductEnabled");
|
||||
}
|
||||
if (columns.sshProductEnabled) {
|
||||
t.dropColumn("sshProductEnabled");
|
||||
}
|
||||
if (columns.scannerProductEnabled) {
|
||||
t.dropColumn("scannerProductEnabled");
|
||||
}
|
||||
if (columns.shareSecretsProductEnabled) {
|
||||
t.dropColumn("shareSecretsProductEnabled");
|
||||
}
|
||||
});
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
|
||||
if (!hasSecretSharingColumn) {
|
||||
await knex.schema.table(TableName.Project, (table) => {
|
||||
table.boolean("secretSharing").notNullable().defaultTo(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
|
||||
if (hasSecretSharingColumn) {
|
||||
await knex.schema.table(TableName.Project, (table) => {
|
||||
table.dropColumn("secretSharing");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||
|
||||
if (!hasLifetimeColumn || !hasViewLimitColumn) {
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
if (!hasLifetimeColumn) {
|
||||
t.integer("maxSharedSecretLifetime").nullable().defaultTo(2592000); // 30 days in seconds
|
||||
}
|
||||
if (!hasViewLimitColumn) {
|
||||
t.integer("maxSharedSecretViewLimit").nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
|
||||
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
|
||||
|
||||
if (hasLifetimeColumn || hasViewLimitColumn) {
|
||||
await knex.schema.alterTable(TableName.Organization, (t) => {
|
||||
if (hasLifetimeColumn) {
|
||||
t.dropColumn("maxSharedSecretLifetime");
|
||||
}
|
||||
if (hasViewLimitColumn) {
|
||||
t.dropColumn("maxSharedSecretViewLimit");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
|
||||
|
||||
if (!hasEncryptedSalt || !hasAuthorizedEmails) {
|
||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||
// These two columns are only needed when secrets are shared with a specific list of emails
|
||||
|
||||
if (!hasEncryptedSalt) {
|
||||
t.binary("encryptedSalt").nullable();
|
||||
}
|
||||
|
||||
if (!hasAuthorizedEmails) {
|
||||
t.json("authorizedEmails").nullable();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
|
||||
|
||||
if (hasEncryptedSalt || hasAuthorizedEmails) {
|
||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||
if (hasEncryptedSalt) {
|
||||
t.dropColumn("encryptedSalt");
|
||||
}
|
||||
|
||||
if (hasAuthorizedEmails) {
|
||||
t.dropColumn("authorizedEmails");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.SecretSync, (t) => {
|
||||
t.string("name", 64).notNullable().alter();
|
||||
});
|
||||
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
|
||||
t.string("name", 64).notNullable().alter();
|
||||
});
|
||||
await knex.schema.alterTable(TableName.AppConnection, (t) => {
|
||||
t.string("name", 64).notNullable().alter();
|
||||
});
|
||||
await knex.schema.alterTable(TableName.SecretRotationV2, (t) => {
|
||||
t.string("name", 64).notNullable().alter();
|
||||
});
|
||||
}
|
||||
|
||||
export async function down(): Promise<void> {
|
||||
// No down migration or it will error
|
||||
}
|
205
backend/src/db/migrations/20250521110635_add-external-ca-pki.ts
Normal file
205
backend/src/db/migrations/20250521110635_add-external-ca-pki.ts
Normal file
@ -0,0 +1,205 @@
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
|
||||
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
|
||||
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
|
||||
|
||||
if (hasCATable && !hasInternalCATable) {
|
||||
await knex.schema.createTableLike(TableName.InternalCertificateAuthority, TableName.CertificateAuthority, (t) => {
|
||||
t.uuid("caId").nullable();
|
||||
});
|
||||
|
||||
// @ts-expect-error intentional: migration
|
||||
await knex(TableName.InternalCertificateAuthority).insert(knex(TableName.CertificateAuthority).select("*"));
|
||||
await knex(TableName.InternalCertificateAuthority).update("caId", knex.ref("id"));
|
||||
|
||||
await knex.schema.alterTable(TableName.InternalCertificateAuthority, (t) => {
|
||||
t.dropColumn("projectId");
|
||||
t.dropColumn("requireTemplateForIssuance");
|
||||
t.dropColumn("createdAt");
|
||||
t.dropColumn("updatedAt");
|
||||
t.dropColumn("status");
|
||||
t.uuid("parentCaId")
|
||||
.nullable()
|
||||
.references("id")
|
||||
.inTable(TableName.CertificateAuthority)
|
||||
.onDelete("CASCADE")
|
||||
.alter();
|
||||
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert).alter();
|
||||
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE").alter();
|
||||
});
|
||||
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.renameColumn("requireTemplateForIssuance", "enableDirectIssuance");
|
||||
t.string("name").nullable();
|
||||
});
|
||||
|
||||
// prefill name for existing internal CAs and flip enableDirectIssuance
|
||||
const cas = await knex(TableName.CertificateAuthority).select("id", "friendlyName", "enableDirectIssuance");
|
||||
await Promise.all(
|
||||
cas.map((ca) => {
|
||||
const slugifiedName = ca.friendlyName
|
||||
? slugify(`${ca.friendlyName.slice(0, 16)}-${alphaNumericNanoId(8)}`)
|
||||
: slugify(alphaNumericNanoId(12));
|
||||
|
||||
return knex(TableName.CertificateAuthority)
|
||||
.where({ id: ca.id })
|
||||
.update({ name: slugifiedName, enableDirectIssuance: !ca.enableDirectIssuance });
|
||||
})
|
||||
);
|
||||
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.dropColumn("parentCaId");
|
||||
t.dropColumn("type");
|
||||
t.dropColumn("friendlyName");
|
||||
t.dropColumn("organization");
|
||||
t.dropColumn("ou");
|
||||
t.dropColumn("country");
|
||||
t.dropColumn("province");
|
||||
t.dropColumn("locality");
|
||||
t.dropColumn("commonName");
|
||||
t.dropColumn("dn");
|
||||
t.dropColumn("serialNumber");
|
||||
t.dropColumn("maxPathLength");
|
||||
t.dropColumn("keyAlgorithm");
|
||||
t.dropColumn("notBefore");
|
||||
t.dropColumn("notAfter");
|
||||
t.dropColumn("activeCaCertId");
|
||||
t.boolean("enableDirectIssuance").notNullable().defaultTo(true).alter();
|
||||
t.string("name").notNullable().alter();
|
||||
t.unique(["name", "projectId"]);
|
||||
});
|
||||
}
|
||||
|
||||
if (!hasExternalCATable) {
|
||||
await knex.schema.createTable(TableName.ExternalCertificateAuthority, (t) => {
|
||||
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||
t.string("type").notNullable();
|
||||
t.uuid("appConnectionId").nullable();
|
||||
t.foreign("appConnectionId").references("id").inTable(TableName.AppConnection);
|
||||
t.uuid("dnsAppConnectionId").nullable();
|
||||
t.foreign("dnsAppConnectionId").references("id").inTable(TableName.AppConnection);
|
||||
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
||||
t.binary("credentials");
|
||||
t.json("configuration");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
|
||||
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
|
||||
t.string("ttl").nullable().alter();
|
||||
|
||||
t.boolean("enableAutoRenewal").notNullable().defaultTo(false);
|
||||
t.integer("autoRenewalPeriodInDays");
|
||||
t.datetime("lastAutoRenewAt");
|
||||
|
||||
t.string("lastOperationStatus");
|
||||
t.text("lastOperationMessage");
|
||||
t.dateTime("lastOperationAt");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
|
||||
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
|
||||
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
|
||||
|
||||
if (hasCATable && hasInternalCATable) {
|
||||
// First add all columns as nullable
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.uuid("parentCaId").nullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
|
||||
t.string("type").nullable();
|
||||
t.string("friendlyName").nullable();
|
||||
t.string("organization").nullable();
|
||||
t.string("ou").nullable();
|
||||
t.string("country").nullable();
|
||||
t.string("province").nullable();
|
||||
t.string("locality").nullable();
|
||||
t.string("commonName").nullable();
|
||||
t.string("dn").nullable();
|
||||
t.string("serialNumber").nullable().unique();
|
||||
t.integer("maxPathLength").nullable();
|
||||
t.string("keyAlgorithm").nullable();
|
||||
t.timestamp("notBefore").nullable();
|
||||
t.timestamp("notAfter").nullable();
|
||||
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert);
|
||||
t.renameColumn("enableDirectIssuance", "requireTemplateForIssuance");
|
||||
t.dropColumn("name");
|
||||
});
|
||||
|
||||
// flip requireTemplateForIssuance for existing internal CAs
|
||||
const cas = await knex(TableName.CertificateAuthority).select("id", "requireTemplateForIssuance");
|
||||
await Promise.all(
|
||||
cas.map((ca) => {
|
||||
return (
|
||||
knex(TableName.CertificateAuthority)
|
||||
.where({ id: ca.id })
|
||||
// @ts-expect-error intentional: migration
|
||||
.update({ requireTemplateForIssuance: !ca.requireTemplateForIssuance })
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
await knex.raw(`
|
||||
UPDATE ${TableName.CertificateAuthority} ca
|
||||
SET
|
||||
type = ica.type,
|
||||
"friendlyName" = ica."friendlyName",
|
||||
organization = ica.organization,
|
||||
ou = ica.ou,
|
||||
country = ica.country,
|
||||
province = ica.province,
|
||||
locality = ica.locality,
|
||||
"commonName" = ica."commonName",
|
||||
dn = ica.dn,
|
||||
"parentCaId" = ica."parentCaId",
|
||||
"serialNumber" = ica."serialNumber",
|
||||
"maxPathLength" = ica."maxPathLength",
|
||||
"keyAlgorithm" = ica."keyAlgorithm",
|
||||
"notBefore" = ica."notBefore",
|
||||
"notAfter" = ica."notAfter",
|
||||
"activeCaCertId" = ica."activeCaCertId"
|
||||
FROM ${TableName.InternalCertificateAuthority} ica
|
||||
WHERE ca.id = ica."caId"
|
||||
`);
|
||||
|
||||
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
|
||||
t.string("type").notNullable().alter();
|
||||
t.string("friendlyName").notNullable().alter();
|
||||
t.string("organization").notNullable().alter();
|
||||
t.string("ou").notNullable().alter();
|
||||
t.string("country").notNullable().alter();
|
||||
t.string("province").notNullable().alter();
|
||||
t.string("locality").notNullable().alter();
|
||||
t.string("commonName").notNullable().alter();
|
||||
t.string("dn").notNullable().alter();
|
||||
t.string("keyAlgorithm").notNullable().alter();
|
||||
t.boolean("requireTemplateForIssuance").notNullable().defaultTo(false).alter();
|
||||
});
|
||||
|
||||
await knex.schema.dropTable(TableName.InternalCertificateAuthority);
|
||||
}
|
||||
|
||||
if (hasExternalCATable) {
|
||||
await knex.schema.dropTable(TableName.ExternalCertificateAuthority);
|
||||
}
|
||||
|
||||
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
|
||||
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
|
||||
t.dropColumn("enableAutoRenewal");
|
||||
t.dropColumn("autoRenewalPeriodInDays");
|
||||
t.dropColumn("lastAutoRenewAt");
|
||||
|
||||
t.dropColumn("lastOperationStatus");
|
||||
t.dropColumn("lastOperationMessage");
|
||||
t.dropColumn("lastOperationAt");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
|
||||
if (!(await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod"))) {
|
||||
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
|
||||
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
|
||||
if (await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod")) {
|
||||
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
|
||||
t.dropColumn("accessTokenPeriod");
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||
|
||||
if (hasEncryptedSalt) {
|
||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||
t.dropColumn("encryptedSalt");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasTable(TableName.SecretSharing)) {
|
||||
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
|
||||
|
||||
if (!hasEncryptedSalt) {
|
||||
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
|
||||
t.binary("encryptedSalt").nullable();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -11,25 +11,10 @@ export const CertificateAuthoritiesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
parentCaId: z.string().uuid().nullable().optional(),
|
||||
projectId: z.string(),
|
||||
type: z.string(),
|
||||
enableDirectIssuance: z.boolean().default(true),
|
||||
status: z.string(),
|
||||
friendlyName: z.string(),
|
||||
organization: z.string(),
|
||||
ou: z.string(),
|
||||
country: z.string(),
|
||||
province: z.string(),
|
||||
locality: z.string(),
|
||||
commonName: z.string(),
|
||||
dn: z.string(),
|
||||
serialNumber: z.string().nullable().optional(),
|
||||
maxPathLength: z.number().nullable().optional(),
|
||||
keyAlgorithm: z.string(),
|
||||
notBefore: z.date().nullable().optional(),
|
||||
notAfter: z.date().nullable().optional(),
|
||||
activeCaCertId: z.string().uuid().nullable().optional(),
|
||||
requireTemplateForIssuance: z.boolean().default(false)
|
||||
name: z.string()
|
||||
});
|
||||
|
||||
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;
|
||||
|
@ -14,7 +14,8 @@ export const CertificateBodiesSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
certId: z.string().uuid(),
|
||||
encryptedCertificate: zodBuffer
|
||||
encryptedCertificate: zodBuffer,
|
||||
encryptedCertificateChain: zodBuffer.nullable().optional()
|
||||
});
|
||||
|
||||
export type TCertificateBodies = z.infer<typeof CertificateBodiesSchema>;
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
import { zodBuffer } from "@app/lib/zod";
|
||||
|
||||
import { TImmutableDBKeys } from "./models";
|
||||
|
||||
export const CertificateSecretsSchema = z.object({
|
||||
@ -12,8 +14,7 @@ export const CertificateSecretsSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
certId: z.string().uuid(),
|
||||
pk: z.string(),
|
||||
sk: z.string()
|
||||
encryptedPrivateKey: zodBuffer
|
||||
});
|
||||
|
||||
export type TCertificateSecrets = z.infer<typeof CertificateSecretsSchema>;
|
||||
|
@ -11,7 +11,7 @@ export const CertificatesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
caId: z.string().uuid(),
|
||||
caId: z.string().uuid().nullable().optional(),
|
||||
status: z.string(),
|
||||
serialNumber: z.string(),
|
||||
friendlyName: z.string(),
|
||||
@ -20,11 +20,13 @@ export const CertificatesSchema = z.object({
|
||||
notAfter: z.date(),
|
||||
revokedAt: z.date().nullable().optional(),
|
||||
revocationReason: z.number().nullable().optional(),
|
||||
altNames: z.string().default("").nullable().optional(),
|
||||
caCertId: z.string().uuid(),
|
||||
altNames: z.string().nullable().optional(),
|
||||
caCertId: z.string().uuid().nullable().optional(),
|
||||
certificateTemplateId: z.string().uuid().nullable().optional(),
|
||||
keyUsages: z.string().array().nullable().optional(),
|
||||
extendedKeyUsages: z.string().array().nullable().optional()
|
||||
extendedKeyUsages: z.string().array().nullable().optional(),
|
||||
pkiSubscriberId: z.string().uuid().nullable().optional(),
|
||||
projectId: z.string()
|
||||
});
|
||||
|
||||
export type TCertificates = z.infer<typeof CertificatesSchema>;
|
||||
|
@ -27,7 +27,8 @@ export const DynamicSecretsSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
encryptedInput: zodBuffer,
|
||||
projectGatewayId: z.string().uuid().nullable().optional()
|
||||
projectGatewayId: z.string().uuid().nullable().optional(),
|
||||
gatewayId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;
|
||||
|
29
backend/src/db/schemas/external-certificate-authorities.ts
Normal file
29
backend/src/db/schemas/external-certificate-authorities.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 ExternalCertificateAuthoritiesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
type: z.string(),
|
||||
appConnectionId: z.string().uuid().nullable().optional(),
|
||||
dnsAppConnectionId: z.string().uuid().nullable().optional(),
|
||||
caId: z.string().uuid(),
|
||||
credentials: zodBuffer.nullable().optional(),
|
||||
configuration: z.unknown().nullable().optional()
|
||||
});
|
||||
|
||||
export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>;
|
||||
export type TExternalCertificateAuthoritiesInsert = Omit<
|
||||
z.input<typeof ExternalCertificateAuthoritiesSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TExternalCertificateAuthoritiesUpdate = Partial<
|
||||
Omit<z.input<typeof ExternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
|
||||
>;
|
24
backend/src/db/schemas/github-org-sync-configs.ts
Normal file
24
backend/src/db/schemas/github-org-sync-configs.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// 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 GithubOrgSyncConfigsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
githubOrgName: z.string(),
|
||||
isActive: z.boolean().default(false).nullable().optional(),
|
||||
encryptedGithubOrgAccessToken: zodBuffer.nullable().optional(),
|
||||
orgId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TGithubOrgSyncConfigs = z.infer<typeof GithubOrgSyncConfigsSchema>;
|
||||
export type TGithubOrgSyncConfigsInsert = Omit<z.input<typeof GithubOrgSyncConfigsSchema>, TImmutableDBKeys>;
|
||||
export type TGithubOrgSyncConfigsUpdate = Partial<Omit<z.input<typeof GithubOrgSyncConfigsSchema>, TImmutableDBKeys>>;
|
@ -21,7 +21,8 @@ export const IdentityAccessTokensSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
name: z.string().nullable().optional(),
|
||||
authMethod: z.string()
|
||||
authMethod: z.string(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;
|
||||
|
@ -19,7 +19,8 @@ export const IdentityAwsAuthsSchema = z.object({
|
||||
type: z.string(),
|
||||
stsEndpoint: z.string(),
|
||||
allowedPrincipalArns: z.string(),
|
||||
allowedAccountIds: z.string()
|
||||
allowedAccountIds: z.string(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
|
||||
|
@ -18,7 +18,8 @@ export const IdentityAzureAuthsSchema = z.object({
|
||||
identityId: z.string().uuid(),
|
||||
tenantId: z.string(),
|
||||
resource: z.string(),
|
||||
allowedServicePrincipalIds: z.string()
|
||||
allowedServicePrincipalIds: z.string(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
|
||||
|
@ -19,7 +19,8 @@ export const IdentityGcpAuthsSchema = z.object({
|
||||
type: z.string(),
|
||||
allowedServiceAccounts: z.string().nullable().optional(),
|
||||
allowedProjects: z.string().nullable().optional(),
|
||||
allowedZones: z.string().nullable().optional()
|
||||
allowedZones: z.string().nullable().optional(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;
|
||||
|
@ -25,7 +25,8 @@ export const IdentityJwtAuthsSchema = z.object({
|
||||
boundClaims: z.unknown(),
|
||||
boundSubject: z.string(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityJwtAuths = z.infer<typeof IdentityJwtAuthsSchema>;
|
||||
|
@ -29,7 +29,9 @@ export const IdentityKubernetesAuthsSchema = z.object({
|
||||
allowedNames: z.string(),
|
||||
allowedAudience: z.string(),
|
||||
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
|
||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
|
||||
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
|
||||
gatewayId: z.string().uuid().nullable().optional(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
||||
|
33
backend/src/db/schemas/identity-ldap-auths.ts
Normal file
33
backend/src/db/schemas/identity-ldap-auths.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 IdentityLdapAuthsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
accessTokenTTL: z.coerce.number().default(7200),
|
||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
||||
accessTokenTrustedIps: z.unknown(),
|
||||
identityId: z.string().uuid(),
|
||||
encryptedBindDN: zodBuffer,
|
||||
encryptedBindPass: zodBuffer,
|
||||
encryptedLdapCaCertificate: zodBuffer.nullable().optional(),
|
||||
url: z.string(),
|
||||
searchBase: z.string(),
|
||||
searchFilter: z.string(),
|
||||
allowedFields: z.unknown().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityLdapAuths = z.infer<typeof IdentityLdapAuthsSchema>;
|
||||
export type TIdentityLdapAuthsInsert = Omit<z.input<typeof IdentityLdapAuthsSchema>, TImmutableDBKeys>;
|
||||
export type TIdentityLdapAuthsUpdate = Partial<Omit<z.input<typeof IdentityLdapAuthsSchema>, TImmutableDBKeys>>;
|
27
backend/src/db/schemas/identity-oci-auths.ts
Normal file
27
backend/src/db/schemas/identity-oci-auths.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// 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 IdentityOciAuthsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
accessTokenTTL: z.coerce.number().default(7200),
|
||||
accessTokenMaxTTL: z.coerce.number().default(7200),
|
||||
accessTokenNumUsesLimit: z.coerce.number().default(0),
|
||||
accessTokenTrustedIps: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
identityId: z.string().uuid(),
|
||||
type: z.string(),
|
||||
tenancyOcid: z.string(),
|
||||
allowedUsernames: z.string().nullable().optional(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityOciAuths = z.infer<typeof IdentityOciAuthsSchema>;
|
||||
export type TIdentityOciAuthsInsert = Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>;
|
||||
export type TIdentityOciAuthsUpdate = Partial<Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>>;
|
@ -27,7 +27,8 @@ export const IdentityOidcAuthsSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
encryptedCaCertificate: zodBuffer.nullable().optional(),
|
||||
claimMetadataMapping: z.unknown().nullable().optional()
|
||||
claimMetadataMapping: z.unknown().nullable().optional(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;
|
||||
|
@ -15,7 +15,8 @@ export const IdentityTokenAuthsSchema = z.object({
|
||||
accessTokenTrustedIps: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
identityId: z.string().uuid()
|
||||
identityId: z.string().uuid(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityTokenAuths = z.infer<typeof IdentityTokenAuthsSchema>;
|
||||
|
@ -17,7 +17,8 @@ export const IdentityUniversalAuthsSchema = z.object({
|
||||
accessTokenTrustedIps: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
identityId: z.string().uuid()
|
||||
identityId: z.string().uuid(),
|
||||
accessTokenPeriod: z.coerce.number().default(0)
|
||||
});
|
||||
|
||||
export type TIdentityUniversalAuths = z.infer<typeof IdentityUniversalAuthsSchema>;
|
||||
|
@ -20,11 +20,13 @@ export * from "./certificate-templates";
|
||||
export * from "./certificates";
|
||||
export * from "./dynamic-secret-leases";
|
||||
export * from "./dynamic-secrets";
|
||||
export * from "./external-certificate-authorities";
|
||||
export * from "./external-group-org-role-mappings";
|
||||
export * from "./external-kms";
|
||||
export * from "./gateways";
|
||||
export * from "./git-app-install-sessions";
|
||||
export * from "./git-app-org";
|
||||
export * from "./github-org-sync-configs";
|
||||
export * from "./group-project-membership-roles";
|
||||
export * from "./group-project-memberships";
|
||||
export * from "./groups";
|
||||
@ -36,6 +38,7 @@ export * from "./identity-gcp-auths";
|
||||
export * from "./identity-jwt-auths";
|
||||
export * from "./identity-kubernetes-auths";
|
||||
export * from "./identity-metadata";
|
||||
export * from "./identity-oci-auths";
|
||||
export * from "./identity-oidc-auths";
|
||||
export * from "./identity-org-memberships";
|
||||
export * from "./identity-project-additional-privilege";
|
||||
@ -47,6 +50,7 @@ export * from "./identity-universal-auths";
|
||||
export * from "./incident-contacts";
|
||||
export * from "./integration-auths";
|
||||
export * from "./integrations";
|
||||
export * from "./internal-certificate-authorities";
|
||||
export * from "./internal-kms";
|
||||
export * from "./kmip-client-certificates";
|
||||
export * from "./kmip-clients";
|
||||
@ -57,6 +61,7 @@ export * from "./kms-keys";
|
||||
export * from "./kms-root-config";
|
||||
export * from "./ldap-configs";
|
||||
export * from "./ldap-group-maps";
|
||||
export * from "./microsoft-teams-integrations";
|
||||
export * from "./models";
|
||||
export * from "./oidc-configs";
|
||||
export * from "./org-bots";
|
||||
@ -67,6 +72,7 @@ export * from "./organizations";
|
||||
export * from "./pki-alerts";
|
||||
export * from "./pki-collection-items";
|
||||
export * from "./pki-collections";
|
||||
export * from "./pki-subscribers";
|
||||
export * from "./project-bots";
|
||||
export * from "./project-environments";
|
||||
export * from "./project-gateways";
|
||||
@ -126,6 +132,8 @@ export * from "./ssh-certificate-authority-secrets";
|
||||
export * from "./ssh-certificate-bodies";
|
||||
export * from "./ssh-certificate-templates";
|
||||
export * from "./ssh-certificates";
|
||||
export * from "./ssh-host-group-memberships";
|
||||
export * from "./ssh-host-groups";
|
||||
export * from "./ssh-host-login-user-mappings";
|
||||
export * from "./ssh-host-login-users";
|
||||
export * from "./ssh-hosts";
|
||||
|
38
backend/src/db/schemas/internal-certificate-authorities.ts
Normal file
38
backend/src/db/schemas/internal-certificate-authorities.ts
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 InternalCertificateAuthoritiesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
parentCaId: z.string().uuid().nullable().optional(),
|
||||
type: z.string(),
|
||||
friendlyName: z.string(),
|
||||
organization: z.string(),
|
||||
ou: z.string(),
|
||||
country: z.string(),
|
||||
province: z.string(),
|
||||
locality: z.string(),
|
||||
commonName: z.string(),
|
||||
dn: z.string(),
|
||||
serialNumber: z.string().nullable().optional(),
|
||||
maxPathLength: z.number().nullable().optional(),
|
||||
keyAlgorithm: z.string(),
|
||||
notBefore: z.date().nullable().optional(),
|
||||
notAfter: z.date().nullable().optional(),
|
||||
activeCaCertId: z.string().uuid().nullable().optional(),
|
||||
caId: z.string().uuid()
|
||||
});
|
||||
|
||||
export type TInternalCertificateAuthorities = z.infer<typeof InternalCertificateAuthoritiesSchema>;
|
||||
export type TInternalCertificateAuthoritiesInsert = Omit<
|
||||
z.input<typeof InternalCertificateAuthoritiesSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TInternalCertificateAuthoritiesUpdate = Partial<
|
||||
Omit<z.input<typeof InternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -13,7 +13,7 @@ export const KmipOrgServerCertificatesSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
orgId: z.string().uuid(),
|
||||
commonName: z.string(),
|
||||
altNames: z.string(),
|
||||
altNames: z.string().nullable().optional(),
|
||||
serialNumber: z.string(),
|
||||
keyAlgorithm: z.string(),
|
||||
issuedAt: z.date(),
|
||||
|
31
backend/src/db/schemas/microsoft-teams-integrations.ts
Normal file
31
backend/src/db/schemas/microsoft-teams-integrations.ts
Normal file
@ -0,0 +1,31 @@
|
||||
// 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 MicrosoftTeamsIntegrationsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
internalTeamsAppId: z.string().nullable().optional(),
|
||||
tenantId: z.string(),
|
||||
encryptedAccessToken: zodBuffer.nullable().optional(),
|
||||
encryptedBotAccessToken: zodBuffer.nullable().optional(),
|
||||
accessTokenExpiresAt: z.date().nullable().optional(),
|
||||
botAccessTokenExpiresAt: z.date().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TMicrosoftTeamsIntegrations = z.infer<typeof MicrosoftTeamsIntegrationsSchema>;
|
||||
export type TMicrosoftTeamsIntegrationsInsert = Omit<
|
||||
z.input<typeof MicrosoftTeamsIntegrationsSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TMicrosoftTeamsIntegrationsUpdate = Partial<
|
||||
Omit<z.input<typeof MicrosoftTeamsIntegrationsSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -2,6 +2,8 @@ import { z } from "zod";
|
||||
|
||||
export enum TableName {
|
||||
Users = "users",
|
||||
SshHostGroup = "ssh_host_groups",
|
||||
SshHostGroupMembership = "ssh_host_group_memberships",
|
||||
SshHost = "ssh_hosts",
|
||||
SshHostLoginUser = "ssh_host_login_users",
|
||||
SshHostLoginUserMapping = "ssh_host_login_user_mappings",
|
||||
@ -11,6 +13,8 @@ export enum TableName {
|
||||
SshCertificate = "ssh_certificates",
|
||||
SshCertificateBody = "ssh_certificate_bodies",
|
||||
CertificateAuthority = "certificate_authorities",
|
||||
ExternalCertificateAuthority = "external_certificate_authorities",
|
||||
InternalCertificateAuthority = "internal_certificate_authorities",
|
||||
CertificateTemplateEstConfig = "certificate_template_est_configs",
|
||||
CertificateAuthorityCert = "certificate_authority_certs",
|
||||
CertificateAuthoritySecret = "certificate_authority_secret",
|
||||
@ -19,6 +23,7 @@ export enum TableName {
|
||||
CertificateBody = "certificate_bodies",
|
||||
CertificateSecret = "certificate_secrets",
|
||||
CertificateTemplate = "certificate_templates",
|
||||
PkiSubscriber = "pki_subscribers",
|
||||
PkiAlert = "pki_alerts",
|
||||
PkiCollection = "pki_collections",
|
||||
PkiCollectionItem = "pki_collection_items",
|
||||
@ -76,8 +81,10 @@ export enum TableName {
|
||||
IdentityAzureAuth = "identity_azure_auths",
|
||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||
IdentityAwsAuth = "identity_aws_auths",
|
||||
IdentityOciAuth = "identity_oci_auths",
|
||||
IdentityOidcAuth = "identity_oidc_auths",
|
||||
IdentityJwtAuth = "identity_jwt_auths",
|
||||
IdentityLdapAuth = "identity_ldap_auths",
|
||||
IdentityOrgMembership = "identity_org_memberships",
|
||||
IdentityProjectMembership = "identity_project_memberships",
|
||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||
@ -146,7 +153,11 @@ export enum TableName {
|
||||
KmipOrgServerCertificates = "kmip_org_server_certificates",
|
||||
KmipClientCertificates = "kmip_client_certificates",
|
||||
SecretRotationV2 = "secret_rotations_v2",
|
||||
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings"
|
||||
SecretRotationV2SecretMapping = "secret_rotation_v2_secret_mappings",
|
||||
MicrosoftTeamsIntegrations = "microsoft_teams_integrations",
|
||||
ProjectMicrosoftTeamsConfigs = "project_microsoft_teams_configs",
|
||||
SecretReminderRecipients = "secret_reminder_recipients",
|
||||
GithubOrgSyncConfig = "github_org_sync_configs"
|
||||
}
|
||||
|
||||
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
|
||||
@ -179,11 +190,16 @@ export enum OrgMembershipStatus {
|
||||
}
|
||||
|
||||
export enum ProjectMembershipRole {
|
||||
// general
|
||||
Admin = "admin",
|
||||
Member = "member",
|
||||
Custom = "custom",
|
||||
Viewer = "viewer",
|
||||
NoAccess = "no-access"
|
||||
NoAccess = "no-access",
|
||||
// ssh
|
||||
SshHostBootstrapper = "ssh-host-bootstrapper",
|
||||
// kms
|
||||
KmsCryptographicOperator = "cryptographic-operator"
|
||||
}
|
||||
|
||||
export enum SecretEncryptionAlgo {
|
||||
@ -220,8 +236,10 @@ export enum IdentityAuthMethod {
|
||||
GCP_AUTH = "gcp-auth",
|
||||
AWS_AUTH = "aws-auth",
|
||||
AZURE_AUTH = "azure-auth",
|
||||
OCI_AUTH = "oci-auth",
|
||||
OIDC_AUTH = "oidc-auth",
|
||||
JWT_AUTH = "jwt-auth"
|
||||
JWT_AUTH = "jwt-auth",
|
||||
LDAP_AUTH = "ldap-auth"
|
||||
}
|
||||
|
||||
export enum ProjectType {
|
||||
|
@ -30,9 +30,9 @@ export const OidcConfigsSchema = z.object({
|
||||
updatedAt: z.date(),
|
||||
orgId: z.string().uuid(),
|
||||
lastUsed: z.date().nullable().optional(),
|
||||
manageGroupMemberships: z.boolean().default(false),
|
||||
encryptedOidcClientId: zodBuffer,
|
||||
encryptedOidcClientSecret: zodBuffer,
|
||||
manageGroupMemberships: z.boolean().default(false),
|
||||
jwtSignatureAlgorithm: z.string().default("RS256")
|
||||
});
|
||||
|
||||
|
@ -27,7 +27,16 @@ export const OrganizationsSchema = z.object({
|
||||
shouldUseNewPrivilegeSystem: z.boolean().default(true),
|
||||
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
|
||||
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
|
||||
bypassOrgAuthEnabled: z.boolean().default(false)
|
||||
bypassOrgAuthEnabled: z.boolean().default(false),
|
||||
userTokenExpiration: z.string().nullable().optional(),
|
||||
secretsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
pkiProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
kmsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
sshProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
scannerProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
shareSecretsProductEnabled: z.boolean().default(true).nullable().optional(),
|
||||
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
|
||||
maxSharedSecretViewLimit: z.number().nullable().optional()
|
||||
});
|
||||
|
||||
export type TOrganizations = z.infer<typeof OrganizationsSchema>;
|
||||
|
33
backend/src/db/schemas/pki-subscribers.ts
Normal file
33
backend/src/db/schemas/pki-subscribers.ts
Normal file
@ -0,0 +1,33 @@
|
||||
// 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 PkiSubscribersSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
projectId: z.string(),
|
||||
caId: z.string().uuid().nullable().optional(),
|
||||
name: z.string(),
|
||||
commonName: z.string(),
|
||||
subjectAlternativeNames: z.string().array(),
|
||||
ttl: z.string().nullable().optional(),
|
||||
keyUsages: z.string().array(),
|
||||
extendedKeyUsages: z.string().array(),
|
||||
status: z.string(),
|
||||
enableAutoRenewal: z.boolean().default(false),
|
||||
autoRenewalPeriodInDays: z.number().nullable().optional(),
|
||||
lastAutoRenewAt: z.date().nullable().optional(),
|
||||
lastOperationStatus: z.string().nullable().optional(),
|
||||
lastOperationMessage: z.string().nullable().optional(),
|
||||
lastOperationAt: z.date().nullable().optional()
|
||||
});
|
||||
|
||||
export type TPkiSubscribers = z.infer<typeof PkiSubscribersSchema>;
|
||||
export type TPkiSubscribersInsert = Omit<z.input<typeof PkiSubscribersSchema>, TImmutableDBKeys>;
|
||||
export type TPkiSubscribersUpdate = Partial<Omit<z.input<typeof PkiSubscribersSchema>, TImmutableDBKeys>>;
|
29
backend/src/db/schemas/project-microsoft-teams-configs.ts
Normal file
29
backend/src/db/schemas/project-microsoft-teams-configs.ts
Normal file
@ -0,0 +1,29 @@
|
||||
// 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 ProjectMicrosoftTeamsConfigsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
projectId: z.string(),
|
||||
microsoftTeamsIntegrationId: z.string().uuid(),
|
||||
isAccessRequestNotificationEnabled: z.boolean().default(false),
|
||||
isSecretRequestNotificationEnabled: z.boolean().default(false),
|
||||
accessRequestChannels: z.unknown(),
|
||||
secretRequestChannels: z.unknown(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
});
|
||||
|
||||
export type TProjectMicrosoftTeamsConfigs = z.infer<typeof ProjectMicrosoftTeamsConfigsSchema>;
|
||||
export type TProjectMicrosoftTeamsConfigsInsert = Omit<
|
||||
z.input<typeof ProjectMicrosoftTeamsConfigsSchema>,
|
||||
TImmutableDBKeys
|
||||
>;
|
||||
export type TProjectMicrosoftTeamsConfigsUpdate = Partial<
|
||||
Omit<z.input<typeof ProjectMicrosoftTeamsConfigsSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -12,10 +12,11 @@ export const ProjectTemplatesSchema = z.object({
|
||||
name: z.string(),
|
||||
description: z.string().nullable().optional(),
|
||||
roles: z.unknown(),
|
||||
environments: z.unknown(),
|
||||
environments: z.unknown().nullable().optional(),
|
||||
orgId: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
type: z.string().default("secret-manager")
|
||||
});
|
||||
|
||||
export type TProjectTemplates = z.infer<typeof ProjectTemplatesSchema>;
|
||||
|
@ -27,7 +27,8 @@ export const ProjectsSchema = z.object({
|
||||
description: z.string().nullable().optional(),
|
||||
type: z.string(),
|
||||
enforceCapitalization: z.boolean().default(false),
|
||||
hasDeleteProtection: z.boolean().default(true).nullable().optional()
|
||||
hasDeleteProtection: z.boolean().default(false).nullable().optional(),
|
||||
secretSharing: z.boolean().default(true)
|
||||
});
|
||||
|
||||
export type TProjects = z.infer<typeof ProjectsSchema>;
|
||||
|
23
backend/src/db/schemas/secret-reminder-recipients.ts
Normal file
23
backend/src/db/schemas/secret-reminder-recipients.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 SecretReminderRecipientsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
secretId: z.string().uuid(),
|
||||
userId: z.string().uuid(),
|
||||
projectId: z.string()
|
||||
});
|
||||
|
||||
export type TSecretReminderRecipients = z.infer<typeof SecretReminderRecipientsSchema>;
|
||||
export type TSecretReminderRecipientsInsert = Omit<z.input<typeof SecretReminderRecipientsSchema>, TImmutableDBKeys>;
|
||||
export type TSecretReminderRecipientsUpdate = Partial<
|
||||
Omit<z.input<typeof SecretReminderRecipientsSchema>, TImmutableDBKeys>
|
||||
>;
|
@ -27,7 +27,8 @@ export const SecretSharingSchema = z.object({
|
||||
password: z.string().nullable().optional(),
|
||||
encryptedSecret: zodBuffer.nullable().optional(),
|
||||
identifier: z.string().nullable().optional(),
|
||||
type: z.string().default("share")
|
||||
type: z.string().default("share"),
|
||||
authorizedEmails: z.unknown().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
|
||||
|
22
backend/src/db/schemas/ssh-host-group-memberships.ts
Normal file
22
backend/src/db/schemas/ssh-host-group-memberships.ts
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 SshHostGroupMembershipsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
sshHostGroupId: z.string().uuid(),
|
||||
sshHostId: z.string().uuid()
|
||||
});
|
||||
|
||||
export type TSshHostGroupMemberships = z.infer<typeof SshHostGroupMembershipsSchema>;
|
||||
export type TSshHostGroupMembershipsInsert = Omit<z.input<typeof SshHostGroupMembershipsSchema>, TImmutableDBKeys>;
|
||||
export type TSshHostGroupMembershipsUpdate = Partial<
|
||||
Omit<z.input<typeof SshHostGroupMembershipsSchema>, TImmutableDBKeys>
|
||||
>;
|
20
backend/src/db/schemas/ssh-host-groups.ts
Normal file
20
backend/src/db/schemas/ssh-host-groups.ts
Normal file
@ -0,0 +1,20 @@
|
||||
// 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 SshHostGroupsSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
projectId: z.string(),
|
||||
name: z.string()
|
||||
});
|
||||
|
||||
export type TSshHostGroups = z.infer<typeof SshHostGroupsSchema>;
|
||||
export type TSshHostGroupsInsert = Omit<z.input<typeof SshHostGroupsSchema>, TImmutableDBKeys>;
|
||||
export type TSshHostGroupsUpdate = Partial<Omit<z.input<typeof SshHostGroupsSchema>, TImmutableDBKeys>>;
|
@ -12,7 +12,8 @@ export const SshHostLoginUserMappingsSchema = z.object({
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
sshHostLoginUserId: z.string().uuid(),
|
||||
userId: z.string().uuid().nullable().optional()
|
||||
userId: z.string().uuid().nullable().optional(),
|
||||
groupId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSshHostLoginUserMappings = z.infer<typeof SshHostLoginUserMappingsSchema>;
|
||||
|
@ -11,8 +11,9 @@ export const SshHostLoginUsersSchema = z.object({
|
||||
id: z.string().uuid(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
sshHostId: z.string().uuid(),
|
||||
loginUser: z.string()
|
||||
sshHostId: z.string().uuid().nullable().optional(),
|
||||
loginUser: z.string(),
|
||||
sshHostGroupId: z.string().uuid().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSshHostLoginUsers = z.infer<typeof SshHostLoginUsersSchema>;
|
||||
|
@ -16,7 +16,8 @@ export const SshHostsSchema = z.object({
|
||||
userCertTtl: z.string(),
|
||||
hostCertTtl: z.string(),
|
||||
userSshCaId: z.string().uuid(),
|
||||
hostSshCaId: z.string().uuid()
|
||||
hostSshCaId: z.string().uuid(),
|
||||
alias: z.string().nullable().optional()
|
||||
});
|
||||
|
||||
export type TSshHosts = z.infer<typeof SshHostsSchema>;
|
||||
|
@ -26,7 +26,10 @@ export const SuperAdminSchema = z.object({
|
||||
encryptedSlackClientSecret: zodBuffer.nullable().optional(),
|
||||
authConsentContent: z.string().nullable().optional(),
|
||||
pageFrameContent: z.string().nullable().optional(),
|
||||
adminIdentityIds: z.string().array().nullable().optional()
|
||||
adminIdentityIds: z.string().array().nullable().optional(),
|
||||
encryptedMicrosoftTeamsAppId: zodBuffer.nullable().optional(),
|
||||
encryptedMicrosoftTeamsClientSecret: zodBuffer.nullable().optional(),
|
||||
encryptedMicrosoftTeamsBotId: zodBuffer.nullable().optional()
|
||||
});
|
||||
|
||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||
|
@ -14,7 +14,8 @@ export const WorkflowIntegrationsSchema = z.object({
|
||||
orgId: z.string().uuid(),
|
||||
description: z.string().nullable().optional(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date()
|
||||
updatedAt: z.date(),
|
||||
status: z.string().default("installed")
|
||||
});
|
||||
|
||||
export type TWorkflowIntegrations = z.infer<typeof WorkflowIntegrationsSchema>;
|
||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
||||
|
||||
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema, UsersSchema } from "@app/db/schemas";
|
||||
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
@ -18,6 +19,9 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
permissions: z.any().array(),
|
||||
@ -150,7 +154,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
requestId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED])
|
||||
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]),
|
||||
bypassReason: z.string().min(10).max(1000).optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -166,7 +171,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
|
||||
actorOrgId: req.permission.orgId,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
requestId: req.params.requestId,
|
||||
status: req.body.status
|
||||
status: req.body.status,
|
||||
bypassReason: req.body.bypassReason
|
||||
});
|
||||
|
||||
return { review };
|
||||
|
@ -0,0 +1,123 @@
|
||||
import z from "zod";
|
||||
|
||||
import {
|
||||
CreateOCIConnectionSchema,
|
||||
SanitizedOCIConnectionSchema,
|
||||
UpdateOCIConnectionSchema
|
||||
} from "@app/ee/services/app-connections/oci";
|
||||
import { readLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "../../../../server/routes/v1/app-connection-routers/app-connection-endpoints";
|
||||
|
||||
export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.OCI,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedOCIConnectionSchema,
|
||||
createSchema: CreateOCIConnectionSchema,
|
||||
updateSchema: UpdateOCIConnectionSchema
|
||||
});
|
||||
|
||||
// The following endpoints are for internal Infisical App use only and not part of the public API
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/compartments`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
response: {
|
||||
200: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string()
|
||||
})
|
||||
.array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const compartments = await server.services.appConnection.oci.listCompartments(connectionId, req.permission);
|
||||
return compartments;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/vaults`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
querystring: z.object({
|
||||
compartmentOcid: z.string().min(1, "Compartment OCID required")
|
||||
}),
|
||||
response: {
|
||||
200: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
displayName: z.string()
|
||||
})
|
||||
.array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
const { compartmentOcid } = req.query;
|
||||
|
||||
const vaults = await server.services.appConnection.oci.listVaults(
|
||||
{ connectionId, compartmentOcid },
|
||||
req.permission
|
||||
);
|
||||
return vaults;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: `/:connectionId/vault-keys`,
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
connectionId: z.string().uuid()
|
||||
}),
|
||||
querystring: z.object({
|
||||
compartmentOcid: z.string().min(1, "Compartment OCID required"),
|
||||
vaultOcid: z.string().min(1, "Vault OCID required")
|
||||
}),
|
||||
response: {
|
||||
200: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
displayName: z.string()
|
||||
})
|
||||
.array()
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { connectionId } = req.params;
|
||||
const { compartmentOcid, vaultOcid } = req.query;
|
||||
|
||||
const keys = await server.services.appConnection.oci.listVaultKeys(
|
||||
{ connectionId, compartmentOcid, vaultOcid },
|
||||
req.permission
|
||||
);
|
||||
return keys;
|
||||
}
|
||||
});
|
||||
};
|
124
backend/src/ee/routes/v1/assume-privilege-router.ts
Normal file
124
backend/src/ee/routes/v1/assume-privilege-router.ts
Normal file
@ -0,0 +1,124 @@
|
||||
import { requestContext } from "@fastify/request-context";
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerAssumePrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:projectId/assume-privileges",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string()
|
||||
}),
|
||||
body: z.object({
|
||||
actorType: z.enum([ActorType.USER, ActorType.IDENTITY]),
|
||||
actorId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req, res) => {
|
||||
if (req.auth.authMode === AuthMode.JWT) {
|
||||
const payload = await server.services.assumePrivileges.assumeProjectPrivileges({
|
||||
targetActorType: req.body.actorType,
|
||||
targetActorId: req.body.actorId,
|
||||
projectId: req.params.projectId,
|
||||
actorPermissionDetails: req.permission,
|
||||
tokenVersionId: req.auth.tokenVersionId
|
||||
});
|
||||
|
||||
const appCfg = getConfig();
|
||||
void res.setCookie("infisical-project-assume-privileges", payload.assumePrivilegesToken, {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED,
|
||||
maxAge: 3600 // 1 hour in seconds
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: req.permission.orgId,
|
||||
event: {
|
||||
type: EventType.PROJECT_ASSUME_PRIVILEGE_SESSION_START,
|
||||
metadata: {
|
||||
projectId: req.params.projectId,
|
||||
requesterEmail: req.auth.user.username,
|
||||
requesterId: req.auth.user.id,
|
||||
targetActorType: req.body.actorType,
|
||||
targetActorId: req.body.actorId,
|
||||
duration: "1hr"
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { message: "Successfully assumed role" };
|
||||
}
|
||||
|
||||
throw new BadRequestError({ message: "Invalid auth mode" });
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:projectId/assume-privileges",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
projectId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req, res) => {
|
||||
const assumedPrivilegeDetails = requestContext.get("assumedPrivilegeDetails");
|
||||
if (req.auth.authMode === AuthMode.JWT && assumedPrivilegeDetails) {
|
||||
const appCfg = getConfig();
|
||||
void res.setCookie("infisical-project-assume-privileges", "", {
|
||||
httpOnly: true,
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: appCfg.HTTPS_ENABLED,
|
||||
expires: new Date(0)
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
orgId: req.permission.orgId,
|
||||
event: {
|
||||
type: EventType.PROJECT_ASSUME_PRIVILEGE_SESSION_END,
|
||||
metadata: {
|
||||
projectId: req.params.projectId,
|
||||
requesterEmail: req.auth.user.username,
|
||||
requesterId: req.auth.user.id,
|
||||
targetActorId: assumedPrivilegeDetails.actorId,
|
||||
targetActorType: assumedPrivilegeDetails.actorType
|
||||
}
|
||||
}
|
||||
});
|
||||
return { message: "Successfully exited assumed role" };
|
||||
}
|
||||
|
||||
throw new BadRequestError({ message: "Invalid auth mode" });
|
||||
}
|
||||
});
|
||||
};
|
@ -121,14 +121,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
||||
identity: z.object({
|
||||
name: z.string(),
|
||||
id: z.string()
|
||||
}),
|
||||
projects: z
|
||||
.object({
|
||||
name: z.string(),
|
||||
id: z.string(),
|
||||
slug: z.string()
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}).array()
|
||||
})
|
||||
}
|
||||
@ -158,17 +151,15 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
||||
identity: z.object({
|
||||
name: z.string(),
|
||||
id: z.string()
|
||||
}),
|
||||
projectGatewayId: z.string()
|
||||
})
|
||||
}).array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const gateways = await server.services.gateway.getProjectGateways({
|
||||
projectId: req.params.projectId,
|
||||
projectPermission: req.permission
|
||||
const gateways = await server.services.gateway.listGateways({
|
||||
orgPermission: req.permission
|
||||
});
|
||||
return { gateways };
|
||||
}
|
||||
@ -216,8 +207,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
||||
id: z.string()
|
||||
}),
|
||||
body: z.object({
|
||||
name: slugSchema({ field: "name" }).optional(),
|
||||
projectIds: z.string().array().optional()
|
||||
name: slugSchema({ field: "name" }).optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@ -230,8 +220,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
|
||||
const gateway = await server.services.gateway.updateGatewayById({
|
||||
orgPermission: req.permission,
|
||||
id: req.params.id,
|
||||
name: req.body.name,
|
||||
projectIds: req.body.projectIds
|
||||
name: req.body.name
|
||||
});
|
||||
return { gateway };
|
||||
}
|
||||
|
129
backend/src/ee/routes/v1/github-org-sync-router.ts
Normal file
129
backend/src/ee/routes/v1/github-org-sync-router.ts
Normal file
@ -0,0 +1,129 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { GithubOrgSyncConfigsSchema } from "@app/db/schemas";
|
||||
import { CharacterType, zodValidateCharacters } from "@app/lib/validator/validate-string";
|
||||
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";
|
||||
|
||||
const SanitizedGithubOrgSyncSchema = GithubOrgSyncConfigsSchema.pick({
|
||||
isActive: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
orgId: true,
|
||||
githubOrgName: true
|
||||
});
|
||||
|
||||
const githubOrgNameValidator = zodValidateCharacters([CharacterType.AlphaNumeric, CharacterType.Hyphen]);
|
||||
export const registerGithubOrgSyncRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z.object({
|
||||
githubOrgName: githubOrgNameValidator(z.string().trim(), "GitHub Org Name"),
|
||||
githubOrgAccessToken: z.string().trim().max(1000).optional(),
|
||||
isActive: z.boolean().default(false)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.createGithubOrgSync({
|
||||
orgPermission: req.permission,
|
||||
githubOrgName: req.body.githubOrgName,
|
||||
githubOrgAccessToken: req.body.githubOrgAccessToken,
|
||||
isActive: req.body.isActive
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "PATCH",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
body: z
|
||||
.object({
|
||||
githubOrgName: githubOrgNameValidator(z.string().trim(), "GitHub Org Name"),
|
||||
githubOrgAccessToken: z.string().trim().max(1000),
|
||||
isActive: z.boolean()
|
||||
})
|
||||
.partial(),
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.updateGithubOrgSync({
|
||||
orgPermission: req.permission,
|
||||
githubOrgName: req.body.githubOrgName,
|
||||
githubOrgAccessToken: req.body.githubOrgAccessToken,
|
||||
isActive: req.body.isActive
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "DELETE",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.deleteGithubOrgSync({
|
||||
orgPermission: req.permission
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
response: {
|
||||
200: z.object({
|
||||
githubOrgSyncConfig: SanitizedGithubOrgSyncSchema
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const githubOrgSyncConfig = await server.services.githubOrgSync.getGithubOrgSync({
|
||||
orgPermission: req.permission
|
||||
});
|
||||
|
||||
return { githubOrgSyncConfig };
|
||||
}
|
||||
});
|
||||
};
|
@ -2,12 +2,14 @@ import { registerProjectTemplateRouter } from "@app/ee/routes/v1/project-templat
|
||||
|
||||
import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router";
|
||||
import { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
|
||||
import { registerAssumePrivilegeRouter } from "./assume-privilege-router";
|
||||
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
|
||||
import { registerCaCrlRouter } from "./certificate-authority-crl-router";
|
||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||
import { registerExternalKmsRouter } from "./external-kms-router";
|
||||
import { registerGatewayRouter } from "./gateway-router";
|
||||
import { registerGithubOrgSyncRouter } from "./github-org-sync-router";
|
||||
import { registerGroupRouter } from "./group-router";
|
||||
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||
import { registerKmipRouter } from "./kmip-router";
|
||||
@ -32,6 +34,7 @@ import { registerSnapshotRouter } from "./snapshot-router";
|
||||
import { registerSshCaRouter } from "./ssh-certificate-authority-router";
|
||||
import { registerSshCertRouter } from "./ssh-certificate-router";
|
||||
import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router";
|
||||
import { registerSshHostGroupRouter } from "./ssh-host-group-router";
|
||||
import { registerSshHostRouter } from "./ssh-host-router";
|
||||
import { registerTrustedIpRouter } from "./trusted-ip-router";
|
||||
import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router";
|
||||
@ -45,6 +48,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
await projectRouter.register(registerProjectRoleRouter);
|
||||
await projectRouter.register(registerProjectRouter);
|
||||
await projectRouter.register(registerTrustedIpRouter);
|
||||
await projectRouter.register(registerAssumePrivilegeRouter);
|
||||
},
|
||||
{ prefix: "/workspace" }
|
||||
);
|
||||
@ -70,6 +74,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
);
|
||||
|
||||
await server.register(registerGatewayRouter, { prefix: "/gateways" });
|
||||
await server.register(registerGithubOrgSyncRouter, { prefix: "/github-org-sync-config" });
|
||||
|
||||
await server.register(
|
||||
async (pkiRouter) => {
|
||||
@ -84,6 +89,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
||||
await sshRouter.register(registerSshCertRouter, { prefix: "/certificates" });
|
||||
await sshRouter.register(registerSshCertificateTemplateRouter, { prefix: "/certificate-templates" });
|
||||
await sshRouter.register(registerSshHostRouter, { prefix: "/hosts" });
|
||||
await sshRouter.register(registerSshHostGroupRouter, { prefix: "/host-groups" });
|
||||
},
|
||||
{ prefix: "/ssh" }
|
||||
);
|
||||
|
@ -98,6 +98,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/login",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
organizationSlug: z.string().trim()
|
||||
|
@ -47,7 +47,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||
200: z.object({ plan: z.any() })
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const plan = await server.services.license.getOrgPlan({
|
||||
actorId: req.permission.id,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { packRules } from "@casl/ability/extra";
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||
import { ProjectMembershipRole, ProjectRolesSchema } from "@app/db/schemas";
|
||||
import {
|
||||
backfillPermissionV1SchemaToV2Schema,
|
||||
ProjectPermissionV1Schema
|
||||
@ -245,13 +245,22 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
response: {
|
||||
200: z.object({
|
||||
data: z.object({
|
||||
membership: ProjectMembershipsSchema.extend({
|
||||
membership: z.object({
|
||||
id: z.string(),
|
||||
roles: z
|
||||
.object({
|
||||
role: z.string()
|
||||
})
|
||||
.array()
|
||||
}),
|
||||
assumedPrivilegeDetails: z
|
||||
.object({
|
||||
actorId: z.string(),
|
||||
actorType: z.string(),
|
||||
actorName: z.string(),
|
||||
actorEmail: z.string().optional()
|
||||
})
|
||||
.optional(),
|
||||
permissions: z.any().array()
|
||||
})
|
||||
})
|
||||
@ -259,14 +268,20 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const { permissions, membership } = await server.services.projectRole.getUserPermission(
|
||||
const { permissions, membership, assumedPrivilegeDetails } = await server.services.projectRole.getUserPermission(
|
||||
req.permission.id,
|
||||
req.params.projectId,
|
||||
req.permission.authMethod,
|
||||
req.permission.orgId
|
||||
);
|
||||
|
||||
return { data: { permissions, membership } };
|
||||
return {
|
||||
data: {
|
||||
permissions,
|
||||
membership,
|
||||
assumedPrivilegeDetails
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { ProjectMembershipRole, ProjectTemplatesSchema } from "@app/db/schemas";
|
||||
import { ProjectMembershipRole, ProjectTemplatesSchema, ProjectType } from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { ProjectPermissionV2Schema } from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectTemplateDefaultEnvironments } from "@app/ee/services/project-template/project-template-constants";
|
||||
import { isInfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-fns";
|
||||
import { ApiDocsTags, ProjectTemplates } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
@ -35,6 +34,7 @@ const SanitizedProjectTemplateSchema = ProjectTemplatesSchema.extend({
|
||||
position: z.number().min(1)
|
||||
})
|
||||
.array()
|
||||
.nullable()
|
||||
});
|
||||
|
||||
const ProjectTemplateRolesSchema = z
|
||||
@ -104,6 +104,9 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.ProjectTemplates],
|
||||
description: "List project templates for the current organization.",
|
||||
querystring: z.object({
|
||||
type: z.nativeEnum(ProjectType).optional().describe(ProjectTemplates.LIST.type)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
projectTemplates: SanitizedProjectTemplateSchema.array()
|
||||
@ -112,7 +115,8 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission);
|
||||
const { type } = req.query;
|
||||
const projectTemplates = await server.services.projectTemplate.listProjectTemplatesByOrg(req.permission, type);
|
||||
|
||||
const auditTemplates = projectTemplates.filter((template) => !isInfisicalProjectTemplate(template.name));
|
||||
|
||||
@ -184,6 +188,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
||||
tags: [ApiDocsTags.ProjectTemplates],
|
||||
description: "Create a project template.",
|
||||
body: z.object({
|
||||
type: z.nativeEnum(ProjectType).describe(ProjectTemplates.CREATE.type),
|
||||
name: slugSchema({ field: "name" })
|
||||
.refine((val) => !isInfisicalProjectTemplate(val), {
|
||||
message: `The requested project template name is reserved.`
|
||||
@ -191,9 +196,7 @@ export const registerProjectTemplateRouter = async (server: FastifyZodProvider)
|
||||
.describe(ProjectTemplates.CREATE.name),
|
||||
description: z.string().max(256).trim().optional().describe(ProjectTemplates.CREATE.description),
|
||||
roles: ProjectTemplateRolesSchema.default([]).describe(ProjectTemplates.CREATE.roles),
|
||||
environments: ProjectTemplateEnvironmentsSchema.default(ProjectTemplateDefaultEnvironments).describe(
|
||||
ProjectTemplates.CREATE.environments
|
||||
)
|
||||
environments: ProjectTemplateEnvironmentsSchema.describe(ProjectTemplates.CREATE.environments).optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
|
@ -145,7 +145,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||
externalId: profile.nameID,
|
||||
email,
|
||||
email: email.toLowerCase(),
|
||||
firstName,
|
||||
lastName: lastName as string,
|
||||
relayState: (req.body as { RelayState?: string }).RelayState,
|
||||
@ -166,6 +166,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/redirect/saml2/organizations/:orgSlug",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
orgSlug: z.string().trim()
|
||||
@ -192,6 +195,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/redirect/saml2/:samlConfigId",
|
||||
method: "GET",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
samlConfigId: z.string().trim()
|
||||
@ -218,6 +224,9 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/saml2/:samlConfigId",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
samlConfigId: z.string().trim()
|
||||
|
@ -196,6 +196,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
url: "/Users",
|
||||
method: "POST",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
schemas: z.array(z.string()),
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
||||
import { canUseSecretScanning } from "@app/ee/services/secret-scanning/secret-scanning-fns";
|
||||
import {
|
||||
SecretScanningResolvedStatus,
|
||||
SecretScanningRiskStatus
|
||||
} from "@app/ee/services/secret-scanning/secret-scanning-types";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { OrderByDirection } from "@app/lib/types";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
@ -23,14 +23,14 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
||||
body: z.object({ organizationId: z.string().trim() }),
|
||||
response: {
|
||||
200: z.object({
|
||||
sessionId: z.string()
|
||||
sessionId: z.string(),
|
||||
gitAppSlug: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
const appCfg = getConfig();
|
||||
if (!appCfg.SECRET_SCANNING_ORG_WHITELIST?.includes(req.auth.orgId)) {
|
||||
if (!canUseSecretScanning(req.auth.orgId)) {
|
||||
throw new BadRequestError({
|
||||
message: "Secret scanning is temporarily unavailable."
|
||||
});
|
||||
|
@ -0,0 +1,16 @@
|
||||
import {
|
||||
CreateOCIVaultSyncSchema,
|
||||
OCIVaultSyncSchema,
|
||||
UpdateOCIVaultSyncSchema
|
||||
} from "@app/ee/services/secret-sync/oci-vault";
|
||||
import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints";
|
||||
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
|
||||
|
||||
export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) =>
|
||||
registerSyncSecretsEndpoints({
|
||||
destination: SecretSync.OCIVault,
|
||||
server,
|
||||
responseSchema: OCIVaultSyncSchema,
|
||||
createSchema: CreateOCIVaultSyncSchema,
|
||||
updateSchema: UpdateOCIVaultSyncSchema
|
||||
});
|
@ -97,7 +97,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
|
||||
allowCustomKeyIds: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowCustomKeyIds)
|
||||
})
|
||||
.refine((data) => ms(data.maxTTL) >= ms(data.ttl), {
|
||||
message: "Max TLL must be greater than or equal to TTL",
|
||||
message: "Max TTL must be greater than or equal to TTL",
|
||||
path: ["maxTTL"]
|
||||
}),
|
||||
response: {
|
||||
|
360
backend/src/ee/routes/v1/ssh-host-group-router.ts
Normal file
360
backend/src/ee/routes/v1/ssh-host-group-router.ts
Normal file
@ -0,0 +1,360 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema";
|
||||
import { sanitizedSshHostGroup } from "@app/ee/services/ssh-host-group/ssh-host-group-schema";
|
||||
import { EHostGroupMembershipFilter } from "@app/ee/services/ssh-host-group/ssh-host-group-types";
|
||||
import { ApiDocsTags, SSH_HOST_GROUPS } from "@app/lib/api-docs";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
export const registerSshHostGroupRouter = async (server: FastifyZodProvider) => {
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:sshHostGroupId",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Get SSH Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.GET.sshHostGroupId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHostGroup.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const sshHostGroup = await server.services.sshHostGroup.getSshHostGroup({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHostGroup.projectId,
|
||||
event: {
|
||||
type: EventType.GET_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
name: sshHostGroup.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHostGroup;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Create SSH Host Group",
|
||||
body: z.object({
|
||||
projectId: z.string().describe(SSH_HOST_GROUPS.CREATE.projectId),
|
||||
name: slugSchema({ min: 1, max: 64, field: "name" }).describe(SSH_HOST_GROUPS.CREATE.name),
|
||||
loginMappings: z.array(loginMappingSchema).default([]).describe(SSH_HOST_GROUPS.CREATE.loginMappings)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHostGroup.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const sshHostGroup = await server.services.sshHostGroup.createSshHostGroup({
|
||||
...req.body,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHostGroup.projectId,
|
||||
event: {
|
||||
type: EventType.CREATE_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
name: sshHostGroup.name,
|
||||
loginMappings: sshHostGroup.loginMappings
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHostGroup;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "PATCH",
|
||||
url: "/:sshHostGroupId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Update SSH Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().trim().describe(SSH_HOST_GROUPS.UPDATE.sshHostGroupId)
|
||||
}),
|
||||
body: z.object({
|
||||
name: slugSchema({ min: 1, max: 64, field: "name" }).describe(SSH_HOST_GROUPS.UPDATE.name).optional(),
|
||||
loginMappings: z.array(loginMappingSchema).optional().describe(SSH_HOST_GROUPS.UPDATE.loginMappings)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHostGroup.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
handler: async (req) => {
|
||||
const sshHostGroup = await server.services.sshHostGroup.updateSshHostGroup({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
...req.body,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHostGroup.projectId,
|
||||
event: {
|
||||
type: EventType.UPDATE_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
name: sshHostGroup.name,
|
||||
loginMappings: sshHostGroup.loginMappings
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHostGroup;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:sshHostGroupId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Delete SSH Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.DELETE.sshHostGroupId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHostGroup.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const sshHostGroup = await server.services.sshHostGroup.deleteSshHostGroup({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHostGroup.projectId,
|
||||
event: {
|
||||
type: EventType.DELETE_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
name: sshHostGroup.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHostGroup;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "GET",
|
||||
url: "/:sshHostGroupId/hosts",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Get SSH Hosts in a Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.GET.sshHostGroupId)
|
||||
}),
|
||||
querystring: z.object({
|
||||
filter: z.nativeEnum(EHostGroupMembershipFilter).optional().describe(SSH_HOST_GROUPS.GET.filter)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
hosts: sanitizedSshHost
|
||||
.pick({
|
||||
id: true,
|
||||
hostname: true,
|
||||
alias: true
|
||||
})
|
||||
.merge(
|
||||
z.object({
|
||||
isPartOfGroup: z.boolean(),
|
||||
joinedGroupAt: z.date().nullable()
|
||||
})
|
||||
)
|
||||
.array(),
|
||||
totalCount: z.number()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { sshHostGroup, hosts, totalCount } = await server.services.sshHostGroup.listSshHostGroupHosts({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
...req.query
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHostGroup.projectId,
|
||||
event: {
|
||||
type: EventType.GET_SSH_HOST_GROUP_HOSTS,
|
||||
metadata: {
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
name: sshHostGroup.name
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { hosts, totalCount };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:sshHostGroupId/hosts/:hostId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Add an SSH Host to a Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.ADD_HOST.sshHostGroupId),
|
||||
hostId: z.string().describe(SSH_HOST_GROUPS.ADD_HOST.hostId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.addHostToSshHostGroup({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
hostId: req.params.hostId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHost.projectId,
|
||||
event: {
|
||||
type: EventType.ADD_HOST_TO_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
sshHostId: sshHost.id,
|
||||
hostname: sshHost.hostname
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHost;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "DELETE",
|
||||
url: "/:sshHostGroupId/hosts/:hostId",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHostGroups],
|
||||
description: "Remove an SSH Host from a Host Group",
|
||||
params: z.object({
|
||||
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.DELETE_HOST.sshHostGroupId),
|
||||
hostId: z.string().describe(SSH_HOST_GROUPS.DELETE_HOST.hostId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.removeHostFromSshHostGroup({
|
||||
sshHostGroupId: req.params.sshHostGroupId,
|
||||
hostId: req.params.hostId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: sshHost.projectId,
|
||||
event: {
|
||||
type: EventType.REMOVE_HOST_FROM_SSH_HOST_GROUP,
|
||||
metadata: {
|
||||
sshHostGroupId: sshHostGroup.id,
|
||||
sshHostId: sshHost.id,
|
||||
hostname: sshHost.hostname
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return sshHost;
|
||||
}
|
||||
});
|
||||
};
|
@ -3,10 +3,12 @@ import { z } from "zod";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
|
||||
import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema";
|
||||
import { LoginMappingSource } from "@app/ee/services/ssh-host/ssh-host-types";
|
||||
import { isValidHostname } from "@app/ee/services/ssh-host/ssh-host-validators";
|
||||
import { SSH_HOSTS } from "@app/lib/api-docs";
|
||||
import { ApiDocsTags, SSH_HOSTS } from "@app/lib/api-docs";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { publicSshCaLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { slugSchema } from "@app/server/lib/schemas";
|
||||
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
@ -20,10 +22,16 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
response: {
|
||||
200: z.array(
|
||||
sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
loginMappings: loginMappingSchema
|
||||
.extend({
|
||||
source: z.nativeEnum(LoginMappingSource)
|
||||
})
|
||||
.array()
|
||||
})
|
||||
)
|
||||
}
|
||||
@ -48,18 +56,24 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
params: z.object({
|
||||
sshHostId: z.string().describe(SSH_HOSTS.GET.sshHostId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
loginMappings: loginMappingSchema
|
||||
.extend({
|
||||
source: z.nativeEnum(LoginMappingSource)
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const host = await server.services.sshHost.getSshHost({
|
||||
const host = await server.services.sshHost.getSshHostById({
|
||||
sshHostId: req.params.sshHostId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
@ -90,16 +104,20 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
description: "Add an SSH Host",
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Register SSH Host",
|
||||
body: z.object({
|
||||
projectId: z.string().describe(SSH_HOSTS.CREATE.projectId),
|
||||
hostname: z
|
||||
.string()
|
||||
.min(1)
|
||||
.trim()
|
||||
.refine((v) => isValidHostname(v), {
|
||||
message: "Hostname must be a valid hostname"
|
||||
})
|
||||
.describe(SSH_HOSTS.CREATE.hostname),
|
||||
alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.CREATE.alias).default(""),
|
||||
userCertTtl: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||
@ -116,7 +134,11 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
loginMappings: loginMappingSchema
|
||||
.extend({
|
||||
source: z.nativeEnum(LoginMappingSource)
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -138,6 +160,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
metadata: {
|
||||
sshHostId: host.id,
|
||||
hostname: host.hostname,
|
||||
alias: host.alias ?? null,
|
||||
userCertTtl: host.userCertTtl,
|
||||
hostCertTtl: host.hostCertTtl,
|
||||
loginMappings: host.loginMappings,
|
||||
@ -159,6 +182,8 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Update SSH Host",
|
||||
params: z.object({
|
||||
sshHostId: z.string().trim().describe(SSH_HOSTS.UPDATE.sshHostId)
|
||||
@ -166,12 +191,14 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
body: z.object({
|
||||
hostname: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.refine((v) => isValidHostname(v), {
|
||||
message: "Hostname must be a valid hostname"
|
||||
})
|
||||
.optional()
|
||||
.describe(SSH_HOSTS.UPDATE.hostname),
|
||||
alias: slugSchema({ min: 0, max: 64, field: "alias" }).describe(SSH_HOSTS.UPDATE.alias).optional(),
|
||||
userCertTtl: z
|
||||
.string()
|
||||
.refine((val) => ms(val) > 0, "TTL must be a positive number")
|
||||
@ -186,7 +213,11 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
loginMappings: loginMappingSchema
|
||||
.extend({
|
||||
source: z.nativeEnum(LoginMappingSource)
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -208,6 +239,7 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
metadata: {
|
||||
sshHostId: host.id,
|
||||
hostname: host.hostname,
|
||||
alias: host.alias,
|
||||
userCertTtl: host.userCertTtl,
|
||||
hostCertTtl: host.hostCertTtl,
|
||||
loginMappings: host.loginMappings,
|
||||
@ -228,12 +260,19 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Delete SSH Host",
|
||||
params: z.object({
|
||||
sshHostId: z.string().describe(SSH_HOSTS.DELETE.sshHostId)
|
||||
}),
|
||||
response: {
|
||||
200: sanitizedSshHost.extend({
|
||||
loginMappings: z.array(loginMappingSchema)
|
||||
loginMappings: loginMappingSchema
|
||||
.extend({
|
||||
source: z.nativeEnum(LoginMappingSource)
|
||||
})
|
||||
.array()
|
||||
})
|
||||
}
|
||||
},
|
||||
@ -271,6 +310,8 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Issue SSH certificate for user",
|
||||
params: z.object({
|
||||
sshHostId: z.string().describe(SSH_HOSTS.ISSUE_SSH_CREDENTIALS.sshHostId)
|
||||
@ -343,6 +384,8 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Issue SSH certificate for host",
|
||||
params: z.object({
|
||||
sshHostId: z.string().describe(SSH_HOSTS.ISSUE_HOST_CERT.sshHostId)
|
||||
@ -407,6 +450,8 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: publicSshCaLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Get public key of the user SSH CA linked to the host",
|
||||
params: z.object({
|
||||
sshHostId: z.string().trim().describe(SSH_HOSTS.GET_USER_CA_PUBLIC_KEY.sshHostId)
|
||||
@ -428,6 +473,8 @@ export const registerSshHostRouter = async (server: FastifyZodProvider) => {
|
||||
rateLimit: publicSshCaLimit
|
||||
},
|
||||
schema: {
|
||||
hide: false,
|
||||
tags: [ApiDocsTags.SshHosts],
|
||||
description: "Get public key of the host SSH CA linked to the host",
|
||||
params: z.object({
|
||||
sshHostId: z.string().trim().describe(SSH_HOSTS.GET_HOST_CA_PUBLIC_KEY.sshHostId)
|
||||
|
@ -0,0 +1,19 @@
|
||||
import {
|
||||
AzureClientSecretRotationGeneratedCredentialsSchema,
|
||||
AzureClientSecretRotationSchema,
|
||||
CreateAzureClientSecretRotationSchema,
|
||||
UpdateAzureClientSecretRotationSchema
|
||||
} from "@app/ee/services/secret-rotation-v2/azure-client-secret";
|
||||
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||
|
||||
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
|
||||
|
||||
export const registerAzureClientSecretRotationRouter = async (server: FastifyZodProvider) =>
|
||||
registerSecretRotationEndpoints({
|
||||
type: SecretRotation.AzureClientSecret,
|
||||
server,
|
||||
responseSchema: AzureClientSecretRotationSchema,
|
||||
createSchema: CreateAzureClientSecretRotationSchema,
|
||||
updateSchema: UpdateAzureClientSecretRotationSchema,
|
||||
generatedCredentialsSchema: AzureClientSecretRotationGeneratedCredentialsSchema
|
||||
});
|
@ -2,6 +2,8 @@ import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotat
|
||||
|
||||
import { registerAuth0ClientSecretRotationRouter } from "./auth0-client-secret-rotation-router";
|
||||
import { registerAwsIamUserSecretRotationRouter } from "./aws-iam-user-secret-rotation-router";
|
||||
import { registerAzureClientSecretRotationRouter } from "./azure-client-secret-rotation-router";
|
||||
import { registerLdapPasswordRotationRouter } from "./ldap-password-rotation-router";
|
||||
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
|
||||
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
|
||||
|
||||
@ -14,5 +16,7 @@ export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
|
||||
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
|
||||
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter,
|
||||
[SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter,
|
||||
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter
|
||||
[SecretRotation.AzureClientSecret]: registerAzureClientSecretRotationRouter,
|
||||
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter,
|
||||
[SecretRotation.LdapPassword]: registerLdapPasswordRotationRouter
|
||||
};
|
||||
|
@ -0,0 +1,19 @@
|
||||
import {
|
||||
CreateLdapPasswordRotationSchema,
|
||||
LdapPasswordRotationGeneratedCredentialsSchema,
|
||||
LdapPasswordRotationSchema,
|
||||
UpdateLdapPasswordRotationSchema
|
||||
} from "@app/ee/services/secret-rotation-v2/ldap-password";
|
||||
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
|
||||
|
||||
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
|
||||
|
||||
export const registerLdapPasswordRotationRouter = async (server: FastifyZodProvider) =>
|
||||
registerSecretRotationEndpoints({
|
||||
type: SecretRotation.LdapPassword,
|
||||
server,
|
||||
responseSchema: LdapPasswordRotationSchema,
|
||||
createSchema: CreateLdapPasswordRotationSchema,
|
||||
updateSchema: UpdateLdapPasswordRotationSchema,
|
||||
generatedCredentialsSchema: LdapPasswordRotationGeneratedCredentialsSchema
|
||||
});
|
@ -3,6 +3,8 @@ import { z } from "zod";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { Auth0ClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/auth0-client-secret";
|
||||
import { AwsIamUserSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/aws-iam-user-secret";
|
||||
import { AzureClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/azure-client-secret";
|
||||
import { LdapPasswordRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/ldap-password";
|
||||
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";
|
||||
@ -15,7 +17,9 @@ const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
|
||||
PostgresCredentialsRotationListItemSchema,
|
||||
MsSqlCredentialsRotationListItemSchema,
|
||||
Auth0ClientSecretRotationListItemSchema,
|
||||
AwsIamUserSecretRotationListItemSchema
|
||||
AzureClientSecretRotationListItemSchema,
|
||||
AwsIamUserSecretRotationListItemSchema,
|
||||
LdapPasswordRotationListItemSchema
|
||||
]);
|
||||
|
||||
export const registerSecretRotationV2Router = async (server: FastifyZodProvider) => {
|
||||
|
@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { ProjectPermissionApprovalActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||
@ -98,7 +98,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionApprovalActions.Create,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
);
|
||||
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
||||
@ -256,7 +256,10 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.SecretManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionApprovalActions.Edit,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
);
|
||||
|
||||
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||
const doc = await accessApprovalPolicyDAL.updateById(
|
||||
@ -341,7 +344,7 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.SecretManager
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionApprovalActions.Delete,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
);
|
||||
|
||||
@ -432,7 +435,10 @@ export const accessApprovalPolicyServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.SecretManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionApprovalActions.Read,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
);
|
||||
|
||||
return policy;
|
||||
};
|
||||
|
@ -6,13 +6,16 @@ import { getConfig } from "@app/lib/config/env";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { ms } from "@app/lib/ms";
|
||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||
import { EnforcementLevel } from "@app/lib/types";
|
||||
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
|
||||
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TMicrosoftTeamsServiceFactory } from "@app/services/microsoft-teams/microsoft-teams-service";
|
||||
import { TProjectMicrosoftTeamsConfigDALFactory } from "@app/services/microsoft-teams/project-microsoft-teams-config-dal";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
|
||||
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
|
||||
import { TProjectSlackConfigDALFactory } from "@app/services/slack/project-slack-config-dal";
|
||||
import { triggerSlackNotification } from "@app/services/slack/slack-fns";
|
||||
import { SlackTriggerFeature } from "@app/services/slack/slack-types";
|
||||
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
|
||||
import { TUserDALFactory } from "@app/services/user/user-dal";
|
||||
|
||||
@ -20,6 +23,7 @@ import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-poli
|
||||
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
|
||||
import { TGroupDALFactory } from "../group/group-dal";
|
||||
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||
import { ProjectPermissionApprovalActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
|
||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
|
||||
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
|
||||
@ -67,6 +71,8 @@ type TSecretApprovalRequestServiceFactoryDep = {
|
||||
>;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
projectSlackConfigDAL: Pick<TProjectSlackConfigDALFactory, "getIntegrationDetailsByProject">;
|
||||
microsoftTeamsService: Pick<TMicrosoftTeamsServiceFactory, "sendNotification">;
|
||||
projectMicrosoftTeamsConfigDAL: Pick<TProjectMicrosoftTeamsConfigDALFactory, "getIntegrationDetailsByProject">;
|
||||
};
|
||||
|
||||
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
|
||||
@ -84,6 +90,8 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
smtpService,
|
||||
userDAL,
|
||||
kmsService,
|
||||
microsoftTeamsService,
|
||||
projectMicrosoftTeamsConfigDAL,
|
||||
projectSlackConfigDAL
|
||||
}: TSecretApprovalRequestServiceFactoryDep) => {
|
||||
const createAccessApprovalRequest = async ({
|
||||
@ -219,24 +227,30 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
const requesterFullName = `${requestedByUser.firstName} ${requestedByUser.lastName}`;
|
||||
const approvalUrl = `${cfg.SITE_URL}/secret-manager/${project.id}/approval`;
|
||||
|
||||
await triggerSlackNotification({
|
||||
projectId: project.id,
|
||||
projectSlackConfigDAL,
|
||||
projectDAL,
|
||||
kmsService,
|
||||
notification: {
|
||||
type: SlackTriggerFeature.ACCESS_REQUEST,
|
||||
payload: {
|
||||
projectName: project.name,
|
||||
requesterFullName,
|
||||
isTemporary,
|
||||
requesterEmail: requestedByUser.email as string,
|
||||
secretPath,
|
||||
environment: envSlug,
|
||||
permissions: accessTypes,
|
||||
approvalUrl,
|
||||
note
|
||||
}
|
||||
await triggerWorkflowIntegrationNotification({
|
||||
input: {
|
||||
notification: {
|
||||
type: TriggerFeature.ACCESS_REQUEST,
|
||||
payload: {
|
||||
projectName: project.name,
|
||||
requesterFullName,
|
||||
isTemporary,
|
||||
requesterEmail: requestedByUser.email as string,
|
||||
secretPath,
|
||||
environment: envSlug,
|
||||
permissions: accessTypes,
|
||||
approvalUrl,
|
||||
note
|
||||
}
|
||||
},
|
||||
projectId: project.id
|
||||
},
|
||||
dependencies: {
|
||||
projectDAL,
|
||||
projectSlackConfigDAL,
|
||||
kmsService,
|
||||
microsoftTeamsService,
|
||||
projectMicrosoftTeamsConfigDAL
|
||||
}
|
||||
});
|
||||
|
||||
@ -311,26 +325,22 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
status,
|
||||
actorId,
|
||||
actorAuthMethod,
|
||||
actorOrgId
|
||||
actorOrgId,
|
||||
bypassReason
|
||||
}: TReviewAccessRequestDTO) => {
|
||||
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
|
||||
if (!accessApprovalRequest) {
|
||||
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
|
||||
}
|
||||
|
||||
const { policy } = accessApprovalRequest;
|
||||
const { policy, environment } = accessApprovalRequest;
|
||||
if (policy.deletedAt) {
|
||||
throw new BadRequestError({
|
||||
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, permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
projectId: accessApprovalRequest.projectId,
|
||||
@ -343,6 +353,20 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
|
||||
}
|
||||
|
||||
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
|
||||
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
|
||||
const canBypassApproval = permission.can(
|
||||
ProjectPermissionApprovalActions.AllowAccessBypass,
|
||||
ProjectPermissionSub.SecretApproval
|
||||
);
|
||||
const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypassApproval);
|
||||
|
||||
if (!policy.allowedSelfApprovals && isSelfApproval && cannotBypassUnderSoftEnforcement) {
|
||||
throw new BadRequestError({
|
||||
message: "Failed to review access approval request. Users are not authorized to review their own request."
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!hasRole(ProjectMembershipRole.Admin) &&
|
||||
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
|
||||
@ -351,21 +375,49 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
throw new ForbiddenRequestError({ message: "You are not authorized to approve this request" });
|
||||
}
|
||||
|
||||
const project = await projectDAL.findById(accessApprovalRequest.projectId);
|
||||
if (!project) {
|
||||
throw new NotFoundError({ message: "The project associated with this access request was not found." });
|
||||
}
|
||||
|
||||
const existingReviews = await accessApprovalRequestReviewerDAL.find({ requestId: accessApprovalRequest.id });
|
||||
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
|
||||
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
|
||||
}
|
||||
|
||||
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
|
||||
const review = await accessApprovalRequestReviewerDAL.findOne(
|
||||
const isBreakGlassApprovalAttempt =
|
||||
policy.enforcementLevel === EnforcementLevel.Soft &&
|
||||
actorId === accessApprovalRequest.requestedByUserId &&
|
||||
status === ApprovalStatus.APPROVED;
|
||||
|
||||
let reviewForThisActorProcessing: {
|
||||
id: string;
|
||||
requestId: string;
|
||||
reviewerUserId: string;
|
||||
status: string;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
};
|
||||
|
||||
const existingReviewByActorInTx = await accessApprovalRequestReviewerDAL.findOne(
|
||||
{
|
||||
requestId: accessApprovalRequest.id,
|
||||
reviewerUserId: actorId
|
||||
},
|
||||
tx
|
||||
);
|
||||
if (!review) {
|
||||
const newReview = await accessApprovalRequestReviewerDAL.create(
|
||||
|
||||
// Check if review exists for actor
|
||||
if (existingReviewByActorInTx) {
|
||||
// Check if breakglass re-approval
|
||||
if (isBreakGlassApprovalAttempt && existingReviewByActorInTx.status === ApprovalStatus.APPROVED) {
|
||||
reviewForThisActorProcessing = existingReviewByActorInTx;
|
||||
} else {
|
||||
throw new BadRequestError({ message: "You have already reviewed this request" });
|
||||
}
|
||||
} else {
|
||||
reviewForThisActorProcessing = await accessApprovalRequestReviewerDAL.create(
|
||||
{
|
||||
status,
|
||||
requestId: accessApprovalRequest.id,
|
||||
@ -373,19 +425,26 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
const allReviews = [...existingReviews, newReview];
|
||||
const otherReviews = existingReviews.filter((er) => er.reviewerUserId !== actorId);
|
||||
const allUniqueReviews = [...otherReviews, reviewForThisActorProcessing];
|
||||
|
||||
const approvedReviews = allReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
||||
const approvedReviews = allUniqueReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
|
||||
const meetsStandardApprovalThreshold = approvedReviews.length >= policy.approvals;
|
||||
|
||||
// approvals is the required number of approvals. If the number of approved reviews is equal to the number of required approvals, then the request is approved.
|
||||
if (approvedReviews.length === policy.approvals) {
|
||||
if (
|
||||
reviewForThisActorProcessing.status === ApprovalStatus.APPROVED &&
|
||||
(meetsStandardApprovalThreshold || isBreakGlassApprovalAttempt)
|
||||
) {
|
||||
const currentRequestState = await accessApprovalRequestDAL.findById(accessApprovalRequest.id, tx);
|
||||
let privilegeIdToSet = currentRequestState?.privilegeId || null;
|
||||
|
||||
if (!privilegeIdToSet) {
|
||||
if (accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
||||
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
|
||||
}
|
||||
|
||||
let privilegeId: string | null = null;
|
||||
|
||||
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
|
||||
// Permanent access
|
||||
const privilege = await additionalPrivilegeDAL.create(
|
||||
@ -397,7 +456,7 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
},
|
||||
tx
|
||||
);
|
||||
privilegeId = privilege.id;
|
||||
privilegeIdToSet = privilege.id;
|
||||
} else {
|
||||
// Temporary access
|
||||
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
|
||||
@ -409,23 +468,57 @@ export const accessApprovalRequestServiceFactory = ({
|
||||
projectId: accessApprovalRequest.projectId,
|
||||
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||
permissions: JSON.stringify(accessApprovalRequest.permissions),
|
||||
isTemporary: true,
|
||||
isTemporary: true, // Explicitly set to true for the privilege
|
||||
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
|
||||
temporaryRange: accessApprovalRequest.temporaryRange!,
|
||||
temporaryAccessStartTime: startTime,
|
||||
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
|
||||
temporaryAccessEndTime: new Date(startTime.getTime() + relativeTempAllocatedTimeInMs)
|
||||
},
|
||||
tx
|
||||
);
|
||||
privilegeId = privilege.id;
|
||||
privilegeIdToSet = privilege.id;
|
||||
}
|
||||
|
||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
|
||||
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId: privilegeIdToSet }, tx);
|
||||
}
|
||||
|
||||
return newReview;
|
||||
}
|
||||
throw new BadRequestError({ message: "You have already reviewed this request" });
|
||||
|
||||
// Send notification if this was a breakglass approval
|
||||
if (isBreakGlassApprovalAttempt) {
|
||||
const cfg = getConfig();
|
||||
const actingUser = await userDAL.findById(actorId, tx);
|
||||
|
||||
if (actingUser) {
|
||||
const policyApproverUserIds = policy.approvers
|
||||
.map((ap) => ap.userId)
|
||||
.filter((id): id is string => typeof id === "string");
|
||||
|
||||
if (policyApproverUserIds.length > 0) {
|
||||
const approverUsersForEmail = await userDAL.find({ $in: { id: policyApproverUserIds } }, { tx });
|
||||
const recipientEmails = approverUsersForEmail
|
||||
.map((appUser) => appUser.email)
|
||||
.filter((email): email is string => !!email);
|
||||
|
||||
if (recipientEmails.length > 0) {
|
||||
await smtpService.sendMail({
|
||||
recipients: recipientEmails,
|
||||
subjectLine: "Infisical Secret Access Policy Bypassed",
|
||||
substitutions: {
|
||||
projectName: project.name,
|
||||
requesterFullName: `${actingUser.firstName} ${actingUser.lastName}`,
|
||||
requesterEmail: actingUser.email,
|
||||
bypassReason: bypassReason || "No reason provided",
|
||||
secretPath: policy.secretPath || "/",
|
||||
environment,
|
||||
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`,
|
||||
requestType: "access"
|
||||
},
|
||||
template: SmtpTemplates.AccessSecretRequestBypassed
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return reviewForThisActorProcessing;
|
||||
});
|
||||
|
||||
return reviewStatus;
|
||||
|
@ -17,6 +17,8 @@ export type TGetAccessRequestCountDTO = {
|
||||
export type TReviewAccessRequestDTO = {
|
||||
requestId: string;
|
||||
status: ApprovalStatus;
|
||||
envName?: string;
|
||||
bypassReason?: string;
|
||||
} & Omit<TProjectPermission, "projectId">;
|
||||
|
||||
export type TCreateAccessApprovalRequestDTO = {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user