mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-05 04:29:09 +00:00
Compare commits
1071 Commits
infisical/
...
daniel/fix
Author | SHA1 | Date | |
---|---|---|---|
60178a6ba6 | |||
3e6d43e4df | |||
01ea41611b | |||
dc7bf9674a | |||
b6814b67b0 | |||
5234a89612 | |||
45bb2f0fcc | |||
4c7e218d0d | |||
0371a57548 | |||
7d0eb9a0fd | |||
44b14756b1 | |||
51f4047207 | |||
a618e0ebf2 | |||
4567e505ec | |||
c638caede5 | |||
300deb5607 | |||
0fc4fb8858 | |||
1e63604f1e | |||
6ce86c4240 | |||
fd65936ae7 | |||
c894a18797 | |||
c170ba6249 | |||
c344330c93 | |||
a6dd36f684 | |||
eb8acba037 | |||
c7a8e1102e | |||
aca71a7b6f | |||
ae075df0ec | |||
75927f711c | |||
b1b1ce07a3 | |||
fe4cc950d3 | |||
81f7884d03 | |||
b8c35fbf15 | |||
42e73d66fc | |||
a0f678a295 | |||
fe40e4f475 | |||
b9782c1a85 | |||
a0be2985dd | |||
86d16c5b9f | |||
c1c1471439 | |||
3639a7fc18 | |||
59c8dc3cda | |||
527e1d6b79 | |||
3e32915a82 | |||
4faa9ced04 | |||
b6ff07b605 | |||
1753cd76be | |||
f75fc54e10 | |||
b9a6f94eea | |||
966bd77234 | |||
c782df1176 | |||
c0daa11aeb | |||
9b2b6d61be | |||
efe10e361f | |||
e9c5b7f846 | |||
008b37c0f4 | |||
c9b234dbea | |||
049df6abec | |||
8497182a7b | |||
133841c322 | |||
e7c5645aa9 | |||
0bc778b9bf | |||
b0bc41da14 | |||
a234b686c2 | |||
6230167794 | |||
68d1849ba0 | |||
5c10427eaf | |||
290d99e02c | |||
b75d601754 | |||
de2a5b4255 | |||
3d65d121c0 | |||
663f8abc51 | |||
941a71efaf | |||
19bbc2ab26 | |||
f4de52e714 | |||
0b87121b67 | |||
e649667da8 | |||
6af4b3f64c | |||
efcc248486 | |||
82eeae6030 | |||
a0d9331e67 | |||
8ec8b1ce2f | |||
e3dae9d498 | |||
41d72d5dc6 | |||
440c77965c | |||
880289217e | |||
d0947f1040 | |||
303edadb1e | |||
50155a610d | |||
c2830a56b6 | |||
b9a9b6b4d9 | |||
e7f7f271c8 | |||
b26e96c5a2 | |||
9b404c215b | |||
d6dae04959 | |||
629bd9b7c6 | |||
4e06fa3a0c | |||
0f827fc31a | |||
3d4aa0fdc9 | |||
711e30a6be | |||
7b1462fdee | |||
50915833ff | |||
44e37fd531 | |||
fa3f957738 | |||
224b26ced6 | |||
e833d9e67c | |||
dc08edb7d2 | |||
0b78e30848 | |||
9253c69325 | |||
7189544705 | |||
a724ab101c | |||
7d3a62cc4c | |||
dea67e3cb0 | |||
ce66cccd8b | |||
7e2147f14e | |||
91eda2419a | |||
32f39c98a7 | |||
ddf6db5a7e | |||
554dbf6c23 | |||
d1997f04c0 | |||
deefaa0961 | |||
a392c9f022 | |||
34222b83ee | |||
b350eef2b9 | |||
85725215f2 | |||
ef36852a47 | |||
d79fd826a4 | |||
18aaa423a9 | |||
32c33eaf6e | |||
702699b4f0 | |||
35ee03d347 | |||
9c5deee688 | |||
ce4cb39a2d | |||
84724e5f65 | |||
56c2e12760 | |||
21656a7ab6 | |||
2ccc77ef40 | |||
1438415d0c | |||
eca0e62764 | |||
e4186f0317 | |||
704c630797 | |||
f398fee2b8 | |||
7fce51e8c1 | |||
76c9d642a9 | |||
3ed5dd6109 | |||
08e7815ec1 | |||
04d961b832 | |||
a6fe233122 | |||
9c0a1b7089 | |||
9352e8bca0 | |||
5e678b1ad2 | |||
cf453e87d8 | |||
4af703df5b | |||
75b8b521b3 | |||
58c1d3b0ac | |||
6b5cafa631 | |||
4a35623956 | |||
74fe673724 | |||
265932df20 | |||
2f92719771 | |||
399ca7a221 | |||
29f37295e1 | |||
e3184a5f40 | |||
ace008f44e | |||
4afd95fe1a | |||
3cd719f6b0 | |||
c6352cc970 | |||
d4555f9698 | |||
393964c4ae | |||
e4afbe8662 | |||
0d89aa8607 | |||
2b91ec5ae9 | |||
c438479246 | |||
9828cbbfbe | |||
cd910a2fac | |||
fc1dffd7e2 | |||
55f8198a2d | |||
4d166402df | |||
19edf83dbc | |||
13f6b238e7 | |||
8dee1f8fc7 | |||
3b23035dfb | |||
0c8ef13d8d | |||
389d51fa5c | |||
638208e9fa | |||
c176d1e4f7 | |||
91a23a608e | |||
c6a25271dd | |||
0f5c1340d3 | |||
ecbdae110d | |||
8ef727b4ec | |||
c6f24dbb5e | |||
c45dae4137 | |||
18c0d2fd6f | |||
c1fb8f47bf | |||
bd57a068d1 | |||
990eddeb32 | |||
ce01f8d099 | |||
faf6708b00 | |||
a58d6ebdac | |||
818b136836 | |||
0cdade6a2d | |||
bcf9b68e2b | |||
6aa9fb6ecd | |||
38e7382d85 | |||
95e12287c2 | |||
c6d14a4bea | |||
0a91586904 | |||
6561a9c7be | |||
86aaa486b4 | |||
9880977098 | |||
b93aaffe77 | |||
1ea0d55dd1 | |||
0866a90c8e | |||
3fff272cb3 | |||
2559809eac | |||
f32abbdc25 | |||
a6f750fafb | |||
610f474ecc | |||
03f4a699e6 | |||
533d49304a | |||
184b59ad1d | |||
b4a2123fa3 | |||
79cacfa89c | |||
44531487d6 | |||
7c77a4f049 | |||
9dfb587032 | |||
3952ad9a2e | |||
9c15cb407d | |||
cb17efa10b | |||
4adc2c4927 | |||
1a26b34ad8 | |||
21c339d27a | |||
1da4cf85f8 | |||
20f29c752d | |||
29ea12f8b1 | |||
b4f1cce587 | |||
5a92520ca3 | |||
42471b22bb | |||
79704e9c98 | |||
1165d11816 | |||
15ea96815c | |||
86d4d88b58 | |||
a12ad91e59 | |||
3113e40d0b | |||
2406d3d904 | |||
e99182c141 | |||
f23056bcbc | |||
522dd0836e | |||
e461787c78 | |||
f74993e850 | |||
d0036a5656 | |||
e7f19421ef | |||
e18d830fe8 | |||
be2fc4fec4 | |||
829dbb9970 | |||
0b012c5dfb | |||
b0421ccad0 | |||
6b83326d00 | |||
1f6abc7f27 | |||
4a02520147 | |||
14f38eb961 | |||
ac469dbe4f | |||
d98430fe07 | |||
82bafd02bb | |||
37a59b2576 | |||
cebd22da8e | |||
d200405c6e | |||
3a1cdc4f44 | |||
1d40d9e448 | |||
e96ca8d355 | |||
2929d94f0a | |||
0383ae9e8b | |||
00faa6257f | |||
183bde55ca | |||
c96fc1f798 | |||
80f7ff1ea8 | |||
c87620109b | |||
02c158b4ed | |||
588f4bdb09 | |||
4d74d264dd | |||
ddfa64eb33 | |||
7fdaa1543a | |||
c8433f39ed | |||
ba238a8f3b | |||
dd89a80449 | |||
a1585db76a | |||
f5f0bf3c83 | |||
3638645b8a | |||
f957b9d970 | |||
b461697fbf | |||
3ce91b8a20 | |||
8bab14a672 | |||
78922a80e2 | |||
0181007c66 | |||
306cf8733e | |||
6e829516db | |||
c08fcc6f5e | |||
9a585ad930 | |||
95c1fff7d3 | |||
9c2591f3a6 | |||
a579598b6d | |||
af0d31db2c | |||
fb6c4acf31 | |||
551ca0fa8c | |||
4a0ccbe69e | |||
f5a463ddea | |||
ce1ad6f32e | |||
56c8b4f5e5 | |||
29b26e3158 | |||
6e209bf099 | |||
949d210263 | |||
1a2d8e96f3 | |||
9198eb5fba | |||
0580f37c5e | |||
e53d40f0e5 | |||
801c0c5ada | |||
7b8af89bee | |||
ef7f5c9eac | |||
db0b4a5ad1 | |||
cb505d1525 | |||
c66476e2b4 | |||
60a06edd9b | |||
e8e1d46f0e | |||
038fe3508c | |||
7d1dff9e5a | |||
5117f5d3c1 | |||
350dd97b98 | |||
121902e51f | |||
923bf02046 | |||
27447ddc88 | |||
a3b4b650d1 | |||
3f0f45e853 | |||
3bb50b235d | |||
1afd120e8e | |||
ab3593af37 | |||
2c2afbea7a | |||
4eabbb3ac5 | |||
1ccd74e1a5 | |||
812cced9d5 | |||
cd6be68461 | |||
5c69bbf515 | |||
448f89fd1c | |||
3331699f56 | |||
810f670e64 | |||
5894df4370 | |||
2aacd54116 | |||
73d9fcc0de | |||
7ac3bb20df | |||
d659b5a624 | |||
0bbdf2a8f4 | |||
a8eba9cfbf | |||
a3d7c5f599 | |||
c325674da0 | |||
3637152a6b | |||
8ed3c0cd68 | |||
cdd836d58f | |||
3d3b1eb21a | |||
6aab28c4c7 | |||
f038b28c1c | |||
24a286e898 | |||
0c1103e778 | |||
2c1eecaf85 | |||
5884565de7 | |||
dd43268506 | |||
9d362b8597 | |||
972ecc3e92 | |||
dc3014409f | |||
4e449f62c0 | |||
c911a7cd81 | |||
44370d49e3 | |||
c7d2dfd351 | |||
1785548a40 | |||
2baf9e0739 | |||
01e7ed23ba | |||
1f789110e3 | |||
c874c943c1 | |||
dab69dcb51 | |||
8e82bfae86 | |||
bc810ea567 | |||
22470376d9 | |||
bb9503471f | |||
a687b1d0db | |||
0aa77f90c8 | |||
5a04371fb0 | |||
70c06c91c8 | |||
926d324ae3 | |||
e48377dea9 | |||
5e1484bd05 | |||
6d9de752d7 | |||
f9a9b1222e | |||
4326ce970a | |||
7a3a9ca9ea | |||
32a110e0ca | |||
da5278f6bf | |||
7e765681cb | |||
0990ce1f92 | |||
2369ff6813 | |||
478520f090 | |||
54313f9c08 | |||
cb8763bc9c | |||
c5d11eee7f | |||
8e1d19c041 | |||
608c7a4dee | |||
c7b60bcf0e | |||
6ae62675be | |||
fb2ab200b9 | |||
f1428d72c2 | |||
4cb51805f0 | |||
8c40918cef | |||
3a002b921a | |||
299653528c | |||
8c256bd9c8 | |||
f8e0e01bb8 | |||
b59413ded0 | |||
15c747e8e8 | |||
073a9ee6a4 | |||
d371c568f1 | |||
e6c086ab09 | |||
890c8b89be | |||
6f4b62cfbb | |||
076c70f6ff | |||
aedc1f2441 | |||
352d363bd4 | |||
ac92a916b4 | |||
17587ff1b8 | |||
7f1c8d9ff6 | |||
ac24c0f760 | |||
0e95c1bcee | |||
447630135b | |||
ddd6adf804 | |||
a4b6d2650a | |||
2f5d6b11da | |||
d380b7f788 | |||
7aee4fdfcd | |||
83bd3a0bf4 | |||
1f68730aa3 | |||
7fd1d72985 | |||
b298eec9db | |||
696479a2ef | |||
ad6e2aeb9e | |||
ad405109a0 | |||
992a82015a | |||
317956a038 | |||
5255c4075a | |||
eca36f1993 | |||
7e29a6a656 | |||
f458e34c37 | |||
99f5ed1f4b | |||
f981c59b5c | |||
a528d011c0 | |||
d337118803 | |||
68a11db1c6 | |||
91bf6a6dad | |||
12c655a152 | |||
1d2f10178f | |||
c5cd5047d7 | |||
06c103c10a | |||
b6a73459a8 | |||
536f51f6ba | |||
a9b72b2da3 | |||
e3c80309c3 | |||
ec3d6c20e8 | |||
5d7c0f30c8 | |||
a3552d00d1 | |||
c9f0ba08e1 | |||
308e605b6c | |||
4d8965eb82 | |||
0357e7c80e | |||
ba1b223655 | |||
0b089e6fa6 | |||
3b88a2759b | |||
42383d5643 | |||
d198ba1a79 | |||
b3579cb271 | |||
30ccb78c81 | |||
fdd67c89b3 | |||
79e9b1b2ae | |||
86fd4d5fba | |||
4692aa12bd | |||
61a0997adc | |||
c276c44c08 | |||
b4f1bec1a9 | |||
ab79342743 | |||
1957531ac4 | |||
61ae0e2fc7 | |||
cbf8e041e9 | |||
87b571d6ff | |||
1e6af8ad8f | |||
a771ddf859 | |||
c4cd6909bb | |||
49642480d3 | |||
b667dccc0d | |||
fdda247120 | |||
ee8a88d062 | |||
33349839cd | |||
8f3883c7d4 | |||
38cfb7fd41 | |||
a331eb8dc4 | |||
2dcb409d3b | |||
39bcb73f3d | |||
52189111d7 | |||
5c4d35e30a | |||
d5c74d558a | |||
9c002ad645 | |||
f369761920 | |||
8eb22630b6 | |||
d650fd68c0 | |||
387c899193 | |||
37882e6344 | |||
68a1aa6f46 | |||
fa18ca41ac | |||
8485fdc1cd | |||
49ae2386c0 | |||
f2b1f3f0e7 | |||
69aa20e35c | |||
524c7ae78f | |||
e13f7a7486 | |||
1867fb2fc4 | |||
5dd144b97b | |||
b1b430e003 | |||
fb09980413 | |||
3b36cb8b3d | |||
be6a98d0bb | |||
f8e1ed09d2 | |||
5c71116be6 | |||
07cc4fd1ab | |||
ea4ef7f7ef | |||
0482424a1c | |||
74bdbc0724 | |||
a0d5c67456 | |||
db4f4d8f28 | |||
d6f6f51d16 | |||
79a0f3d701 | |||
46912c4c3c | |||
6636377cb5 | |||
26320ddce4 | |||
f5964040d7 | |||
dcaa7f1fce | |||
a4119ee1bb | |||
74f866715f | |||
667f696d26 | |||
5f3938c33d | |||
07845ad6af | |||
17fa72be13 | |||
bf3e93460a | |||
306709cde6 | |||
c41518c822 | |||
f0f2905789 | |||
212a7b49f0 | |||
22e3fcb43c | |||
93b65a1534 | |||
039882e78b | |||
f0f51089fe | |||
447141ab1f | |||
d2ba436338 | |||
ad0d281629 | |||
ce2a9c8640 | |||
ac97f273e3 | |||
69c50af14e | |||
c8638479a8 | |||
8aa75484f3 | |||
66d70f5a25 | |||
8e7cf5f9ac | |||
f9f79cb69e | |||
4235be4be9 | |||
5c3f2e66fd | |||
a37b3ccede | |||
d64eb4b901 | |||
519403023a | |||
b2a976f3d4 | |||
6e882aa46e | |||
bf4db0a9ff | |||
3a3e3a7afc | |||
a7af3a48d9 | |||
cdba78b51d | |||
0c324e804c | |||
47aca3f3e2 | |||
80da2a19aa | |||
858a35812a | |||
31ef1a2183 | |||
66a6f9de71 | |||
6333eccc4a | |||
0af2b113df | |||
63a7941047 | |||
edeac08cb5 | |||
019b0ae09a | |||
1d00bb0a64 | |||
d96f1320ed | |||
50dbefeb48 | |||
56ac2c6780 | |||
c2f16da411 | |||
8223aee2ef | |||
5bd2af9621 | |||
b3df6ce6b5 | |||
e12eb5347d | |||
83a4426d31 | |||
3fd1fbc355 | |||
306d2b4bd9 | |||
c2c66af1f9 | |||
7ae65478aa | |||
b1594e65c6 | |||
0bce5b1daa | |||
207db93483 | |||
972f6a4887 | |||
6e1bece9d9 | |||
63e8bc1845 | |||
4f92663b66 | |||
a66a6790c0 | |||
bde853d280 | |||
acda627236 | |||
875afbb4d6 | |||
56f50a18dc | |||
801c438d05 | |||
baba411502 | |||
4c20ac6564 | |||
4e8556dec2 | |||
2d7b9ec1e4 | |||
8bb9ed4394 | |||
e4246ae85f | |||
f24067542f | |||
a7f5a61f37 | |||
b5fd7698d8 | |||
61c3102573 | |||
d6a5bf9d50 | |||
d0cb06d875 | |||
70f63b3190 | |||
d42f620e1b | |||
2b0670a409 | |||
cc25639157 | |||
5ff30aed10 | |||
656ec4bf16 | |||
0bac9a8e02 | |||
5142e6e5f6 | |||
5c0e5a8ae0 | |||
71e309bbcb | |||
8ff407927c | |||
49c735caf9 | |||
b4de2ea85d | |||
8b8baf1ef2 | |||
2a89b872c5 | |||
2d2d9a5987 | |||
a20a60850b | |||
35e38c23dd | |||
b79e61c86b | |||
e555d3129d | |||
a41883137c | |||
c414bf6c39 | |||
9b782a9da6 | |||
497c0cf63d | |||
93761f37ea | |||
68e530e5d2 | |||
d9005e8665 | |||
20b1cdf909 | |||
4bae65cc55 | |||
6da5f12855 | |||
7a242c4976 | |||
b01d381993 | |||
1ac18fcf0c | |||
8d5ef5f4d9 | |||
35b5253853 | |||
99d59a38d5 | |||
9ab1fce0e0 | |||
9992fbf3dd | |||
3ca596d4af | |||
5e0d64525f | |||
8bcf936b91 | |||
1a2508d91a | |||
e81a77652f | |||
1c95b3abe7 | |||
1f3c72b997 | |||
e55b981cea | |||
49d4e67e07 | |||
a54d156bf0 | |||
f3fc898232 | |||
c61602370e | |||
5178663797 | |||
f04f3aee25 | |||
e5333e2718 | |||
f27d9f8cee | |||
cbd568b714 | |||
b330c5570d | |||
d222bbf131 | |||
961c6391a8 | |||
d68d7df0f8 | |||
c44c7810ce | |||
b7893a6a72 | |||
7a3d425b0e | |||
bd570bd02f | |||
b94ffb8a82 | |||
246b8728a4 | |||
00415e1a87 | |||
ad354c106e | |||
26778d92d3 | |||
b135ba263c | |||
9b7ef55ad7 | |||
872f8bdad8 | |||
80b0dc6895 | |||
b067751027 | |||
f2b3b7b726 | |||
2d51445dd9 | |||
20898c00c6 | |||
2200bd646e | |||
fb69236f47 | |||
918734b26b | |||
729c75112b | |||
738e8cfc5c | |||
1ba7a31e0d | |||
233a4f7d77 | |||
44ff1abd74 | |||
08cb105fe4 | |||
62aebe2fd4 | |||
5c0542c5a3 | |||
fdf5fcad0a | |||
a85c59e3e2 | |||
6874bff302 | |||
e1b8aa8347 | |||
a041fd4762 | |||
1534ba516a | |||
f7183347dc | |||
105b8d6493 | |||
b9d35058bf | |||
22a3c46902 | |||
be8232dc93 | |||
8c566a5ff7 | |||
0a124093d6 | |||
088cb72621 | |||
de21b44486 | |||
6daeed68a0 | |||
31a499c9cd | |||
c88923e0c6 | |||
04491ee1b7 | |||
ad79ee56e4 | |||
519d6f98a2 | |||
973ed37018 | |||
c72280e9ab | |||
032c5b5620 | |||
aa5cd0fd0f | |||
358ca3decd | |||
5bad4adbdf | |||
0899fdb7d5 | |||
e008fb26a2 | |||
34543ef127 | |||
83107f56bb | |||
35071af478 | |||
eb5f71cb05 | |||
9cf1dd38a6 | |||
144a563609 | |||
ca0062f049 | |||
2ed9aa888e | |||
8c7d329f8f | |||
a0aa06e2f5 | |||
1dd0167ac8 | |||
55aea364da | |||
afee47ab45 | |||
9387d9aaac | |||
2b215a510c | |||
89ff6a6c93 | |||
3bcf406688 | |||
580b86cde8 | |||
7a20251261 | |||
ae63898d5e | |||
d4d3c2b10f | |||
0e3cc4fdeb | |||
b893c3e690 | |||
cee13a0e8b | |||
3745b65148 | |||
a0f0593e2d | |||
ea6e739b46 | |||
12f4868957 | |||
4d43a77f6c | |||
3f3c15d715 | |||
ca453df9e9 | |||
c959fa6fdd | |||
d11ded9abc | |||
714a3186a9 | |||
20d1572220 | |||
21290d8e6c | |||
54fcc23a6c | |||
a339c473d5 | |||
718cabe49b | |||
a087deb1eb | |||
7ce283e891 | |||
52cf38449b | |||
8d6f76698a | |||
71cc84c9a5 | |||
5d95d7f31d | |||
2f15e0e767 | |||
6e1b29025b | |||
1dd451f221 | |||
fcc18996d3 | |||
bcaafcb49f | |||
b4558981c1 | |||
64099908eb | |||
98e0c1b4ca | |||
4050e56e60 | |||
4d1a41e24e | |||
43f676b078 | |||
130ec68288 | |||
c4d5c1a454 | |||
e1407cc093 | |||
1b38d969df | |||
6e3d5a8c7c | |||
e2a447dd05 | |||
2522cc1ede | |||
56876a77e4 | |||
0111ee9efb | |||
581ffc613c | |||
03848b30a2 | |||
5537b00a26 | |||
d71d59e399 | |||
8f8553760a | |||
708c2af979 | |||
fa7587900e | |||
e453ddf937 | |||
3f68807179 | |||
ba42aca069 | |||
22c589e2cf | |||
943945f6d7 | |||
b598dd3d47 | |||
ad6d18a905 | |||
46a91515b1 | |||
b79ce8a880 | |||
d31d98b5e0 | |||
afa1e7e139 | |||
2aea73861b | |||
2002db2007 | |||
26148b633b | |||
4b463c6fde | |||
e6823c520e | |||
ab83e61068 | |||
cb6cbafcae | |||
bcb3eaab74 | |||
12d5fb1043 | |||
8bf09789d6 | |||
7ab8db0471 | |||
6b473d2b36 | |||
7581b33b3b | |||
be74f4d34c | |||
e973a62753 | |||
08420cc38d | |||
94fa294455 | |||
be63e538d7 | |||
62aa23a059 | |||
02e423f52c | |||
3cb226908b | |||
ba37b1c083 | |||
d23b39abba | |||
de92ba157a | |||
dadea751e3 | |||
0ff0357a7c | |||
85f257b4db | |||
18d7a14e3f | |||
ff4d932631 | |||
519f0025c0 | |||
d8d6d7dc1b | |||
a975fbd8a4 | |||
3a6ec3717b | |||
a4a961996b | |||
5b4777c1a5 | |||
2f526850d6 | |||
4f5d31d06f | |||
a8264b17e4 | |||
cb66733e6d | |||
40a0691ccb | |||
6410d51033 | |||
bc30ba9ad1 | |||
a0259712df | |||
1132d07dea | |||
1f0b1964b9 | |||
690e72b44c | |||
e2967f5e61 | |||
97afc4ff51 | |||
c47a91715f | |||
fbc7b34786 | |||
9e6641c058 | |||
d035403af1 | |||
1af0d958dd | |||
66a51658d7 | |||
28dc3a4b1c | |||
b27cadb651 | |||
3dca82ad2f | |||
1c90df9dd4 | |||
e15c9e72c6 | |||
71575b1d2e | |||
51f164c399 | |||
702cd0d403 | |||
75267987fc | |||
d734a3f6f4 | |||
cbb749e34a | |||
4535c1069a | |||
747acfe070 | |||
fa1b236f26 | |||
c98ef0eca8 | |||
9f23106c6c | |||
1e7744b498 | |||
44c736facd | |||
51928ddb47 | |||
c7cded4af6 | |||
8b56e20b42 | |||
39c2c37cc0 | |||
3131ae7dae | |||
5315a67d74 | |||
79de7f9f5b | |||
71ffed026d | |||
ee98b15e2b | |||
945d81ad4b | |||
ff8354605c | |||
09b63eee90 | |||
d175256bb4 | |||
ee0c79d018 | |||
d5d7564550 | |||
0db682c5f0 | |||
a01a995585 | |||
2ac785493a | |||
85489a81ff | |||
7116c85f2c | |||
31e4da0dd3 | |||
f255d891ae | |||
4774469244 | |||
e143a31e79 | |||
0baea4c5fd | |||
f6cc20b08b | |||
90e125454e | |||
fbdf3dc9ce | |||
f333c905d9 | |||
71e60df39a | |||
8b4d050d05 | |||
3b4bb591a3 | |||
54f1a4416b | |||
47e3f1b510 | |||
5810b76027 | |||
246e6c64d1 | |||
4e836c5dca | |||
63a289c3be | |||
0a52bbd55d | |||
593bdf74b8 | |||
1f3742e619 | |||
d6e5ac2133 | |||
fea48518a3 | |||
dde24d4c71 | |||
94d509eb01 | |||
8f1e662688 | |||
dcbbb67f03 | |||
055fd34c33 | |||
dc0d3b860e | |||
c0fb3c905e | |||
18b0766d96 | |||
b423696630 | |||
bf60489fde | |||
85ea6d2585 | |||
a97737ab90 | |||
3793858f0a | |||
66c48fbff8 | |||
b6b040375b | |||
9ad5e082e2 | |||
f1805811aa | |||
b135258cce | |||
a651de53d1 | |||
7d0a535f46 | |||
c4e3dd84e3 | |||
9193f13970 | |||
016f22c295 | |||
4d7182c9b1 | |||
6ea7b04efa | |||
3981d61853 | |||
3d391b4e2d | |||
4123177133 | |||
4d61188d0f | |||
fa33f35fcd | |||
13629223fb | |||
74fefa9879 | |||
ff2c8d017f | |||
ba1f8f4564 | |||
e26df005c2 | |||
aca9b47f82 | |||
a16ce8899b | |||
b61511d100 | |||
f8ea421a0e | |||
a945bdfc4c | |||
f7b8345da4 | |||
f6d7ec52c2 | |||
3f6999b2e3 | |||
9128461409 | |||
893235c40f | |||
d3cdaa8449 | |||
e0f655ae30 | |||
93aeca3a38 | |||
1edebdf8a5 | |||
1017707642 | |||
5639306303 | |||
b3a9661755 | |||
72f50ec399 | |||
effc7a3627 | |||
175ce865aa | |||
51f220ba2c | |||
51819e57d1 | |||
510c91cef1 | |||
9be5d89fcf | |||
94f4497903 | |||
e1d9f779b2 | |||
b5af5646ee | |||
1554618167 | |||
5fbfcdda30 | |||
cdbb3b9c47 | |||
0042a95b21 | |||
53233e05d4 | |||
4f15f9c8d3 | |||
97223fabe6 | |||
04b312cbe4 | |||
40bb9668fe | |||
97e5069cf5 | |||
93146fcd96 | |||
87d98de4c1 | |||
26f647b948 | |||
80b3cdd128 | |||
8dd85a0d65 | |||
17995d301a | |||
094b48a2b1 | |||
abd62867eb | |||
179573a269 | |||
457edef5fe | |||
f0b84d5bc9 | |||
36bf1b2abc | |||
42fb732955 | |||
da2dcb347a | |||
b9482966cf | |||
9fddcea3db | |||
4c496d5e3d | |||
0c2e566184 | |||
38adc83f2b | |||
f2e5f1bb10 | |||
9460eafd91 | |||
8afecac7d8 | |||
bf13b81c0f | |||
c753a91958 | |||
695a4a34b5 | |||
372f71f2b0 | |||
0da6262ead | |||
4f05e4ce93 | |||
2e8680c5d4 | |||
e5136c9ef5 | |||
812fe5cf31 | |||
50082e192c | |||
1e1b5d655e | |||
3befd90723 | |||
88549f4030 | |||
46a638cc63 | |||
566f7e4c61 | |||
9ff3210ed6 | |||
f91a6683c2 | |||
c29cb667d7 | |||
8ffbaa2f6c | |||
796d5e3540 | |||
686b88fc97 | |||
2a134b9dc2 | |||
d8d63ecaec | |||
efc186ae6c | |||
f9957e111c | |||
1193e33890 | |||
ec64753795 | |||
c908310f6e | |||
ee2b8a594a | |||
3ae27e088f | |||
393c0c9e90 | |||
5e453ab8a6 | |||
273c78c0a5 | |||
1bcc742466 | |||
1fc9e60254 | |||
126e385046 | |||
2f932ad103 |
26
.github/resources/rename_migration_files.py
vendored
Normal file
26
.github/resources/rename_migration_files.py
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import os
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
def rename_migrations():
|
||||||
|
migration_folder = "./backend/src/db/migrations"
|
||||||
|
with open("added_files.txt", "r") as file:
|
||||||
|
changed_files = file.readlines()
|
||||||
|
|
||||||
|
# Find the latest file among the changed files
|
||||||
|
latest_timestamp = datetime.now() # utc time
|
||||||
|
for file_path in changed_files:
|
||||||
|
file_path = file_path.strip()
|
||||||
|
# each new file bump by 1s
|
||||||
|
latest_timestamp = latest_timestamp + timedelta(seconds=1)
|
||||||
|
|
||||||
|
new_filename = os.path.join(migration_folder, latest_timestamp.strftime("%Y%m%d%H%M%S") + f"_{file_path.split('_')[1]}")
|
||||||
|
old_filename = os.path.join(migration_folder, file_path)
|
||||||
|
os.rename(old_filename, new_filename)
|
||||||
|
print(f"Renamed {old_filename} to {new_filename}")
|
||||||
|
|
||||||
|
if len(changed_files) == 0:
|
||||||
|
print("No new files added to migration folder")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
rename_migrations()
|
||||||
|
|
38
.github/workflows/build-patroni-docker-img.yml
vendored
Normal file
38
.github/workflows/build-patroni-docker-img.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Build patroni
|
||||||
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
patroni-image:
|
||||||
|
name: Build patroni
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: ☁️ Checkout source
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: 'zalando/patroni'
|
||||||
|
- name: Save commit hashes for tag
|
||||||
|
id: commit
|
||||||
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
|
- name: 🔧 Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v2
|
||||||
|
- name: 🐋 Login to Docker Hub
|
||||||
|
uses: docker/login-action@v2
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
- name: Set up Depot CLI
|
||||||
|
uses: depot/setup-action@v1
|
||||||
|
- name: 🏗️ Build backend and push to docker hub
|
||||||
|
uses: depot/build-push-action@v1
|
||||||
|
with:
|
||||||
|
project: 64mmf0n610
|
||||||
|
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
|
||||||
|
push: true
|
||||||
|
context: .
|
||||||
|
file: Dockerfile
|
||||||
|
tags: |
|
||||||
|
infisical/patroni:${{ steps.commit.outputs.short }}
|
||||||
|
infisical/patroni:latest
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|
||||||
|
|
@ -74,21 +74,21 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-prod-platform
|
container-name: infisical-core-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-prod-platform
|
service: infisical-core-platform
|
||||||
cluster: infisical-prod-platform
|
cluster: infisical-core-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
|
||||||
production-postgres-deployment:
|
production-postgres-deployment:
|
||||||
@ -122,19 +122,19 @@ jobs:
|
|||||||
uses: pr-mpt/actions-commit-hash@v2
|
uses: pr-mpt/actions-commit-hash@v2
|
||||||
- name: Download task definition
|
- name: Download task definition
|
||||||
run: |
|
run: |
|
||||||
aws ecs describe-task-definition --task-definition infisical-prod-platform --query taskDefinition > task-definition.json
|
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
|
||||||
- name: Render Amazon ECS task definition
|
- name: Render Amazon ECS task definition
|
||||||
id: render-web-container
|
id: render-web-container
|
||||||
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
uses: aws-actions/amazon-ecs-render-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: task-definition.json
|
task-definition: task-definition.json
|
||||||
container-name: infisical-prod-platform
|
container-name: infisical-core-platform
|
||||||
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
|
||||||
environment-variables: "LOG_LEVEL=info"
|
environment-variables: "LOG_LEVEL=info"
|
||||||
- name: Deploy to Amazon ECS service
|
- name: Deploy to Amazon ECS service
|
||||||
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
|
||||||
with:
|
with:
|
||||||
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
|
||||||
service: infisical-prod-platform
|
service: infisical-core-platform
|
||||||
cluster: infisical-prod-platform
|
cluster: infisical-core-platform
|
||||||
wait-for-service-stability: true
|
wait-for-service-stability: true
|
||||||
|
@ -5,6 +5,7 @@ on:
|
|||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
paths:
|
paths:
|
||||||
- "backend/src/server/routes/**"
|
- "backend/src/server/routes/**"
|
||||||
|
- "backend/src/ee/routes/**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
check-be-api-changes:
|
check-be-api-changes:
|
||||||
|
120
.github/workflows/release_build_infisical_cli.yml
vendored
120
.github/workflows/release_build_infisical_cli.yml
vendored
@ -1,60 +1,72 @@
|
|||||||
name: Build and release CLI
|
name: Build and release CLI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
# run only against tags
|
|
||||||
tags:
|
push:
|
||||||
- "infisical-cli/v*.*.*"
|
# run only against tags
|
||||||
|
tags:
|
||||||
|
- "infisical-cli/v*.*.*"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
# packages: write
|
# packages: write
|
||||||
# issues: write
|
# issues: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
goreleaser:
|
cli-integration-tests:
|
||||||
runs-on: ubuntu-20.04
|
name: Run tests before deployment
|
||||||
steps:
|
uses: ./.github/workflows/run-cli-tests.yml
|
||||||
- uses: actions/checkout@v3
|
secrets:
|
||||||
with:
|
CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }}
|
||||||
fetch-depth: 0
|
CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }}
|
||||||
- name: 🐋 Login to Docker Hub
|
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
|
||||||
uses: docker/login-action@v2
|
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
|
||||||
with:
|
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
goreleaser:
|
||||||
- name: 🔧 Set up Docker Buildx
|
runs-on: ubuntu-20.04
|
||||||
uses: docker/setup-buildx-action@v2
|
needs: [cli-integration-tests]
|
||||||
- run: git fetch --force --tags
|
steps:
|
||||||
- run: echo "Ref name ${{github.ref_name}}"
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-go@v3
|
with:
|
||||||
with:
|
fetch-depth: 0
|
||||||
go-version: ">=1.19.3"
|
- name: 🐋 Login to Docker Hub
|
||||||
cache: true
|
uses: docker/login-action@v2
|
||||||
cache-dependency-path: cli/go.sum
|
with:
|
||||||
- name: libssl1.1 => libssl1.0-dev for OSXCross
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
run: |
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list
|
- name: 🔧 Set up Docker Buildx
|
||||||
sudo apt update && apt-cache policy libssl1.0-dev
|
uses: docker/setup-buildx-action@v2
|
||||||
sudo apt-get install libssl1.0-dev
|
- run: git fetch --force --tags
|
||||||
- name: OSXCross for CGO Support
|
- run: echo "Ref name ${{github.ref_name}}"
|
||||||
run: |
|
- uses: actions/setup-go@v3
|
||||||
mkdir ../../osxcross
|
with:
|
||||||
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
|
go-version: ">=1.19.3"
|
||||||
- uses: goreleaser/goreleaser-action@v4
|
cache: true
|
||||||
with:
|
cache-dependency-path: cli/go.sum
|
||||||
distribution: goreleaser-pro
|
- name: libssl1.1 => libssl1.0-dev for OSXCross
|
||||||
version: latest
|
run: |
|
||||||
args: release --clean
|
echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list
|
||||||
env:
|
sudo apt update && apt-cache policy libssl1.0-dev
|
||||||
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
|
sudo apt-get install libssl1.0-dev
|
||||||
POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }}
|
- name: OSXCross for CGO Support
|
||||||
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
|
run: |
|
||||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
mkdir ../../osxcross
|
||||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
|
||||||
- uses: actions/setup-python@v4
|
- uses: goreleaser/goreleaser-action@v4
|
||||||
- run: pip install --upgrade cloudsmith-cli
|
with:
|
||||||
- name: Publish to CloudSmith
|
distribution: goreleaser-pro
|
||||||
run: sh cli/upload_to_cloudsmith.sh
|
version: latest
|
||||||
env:
|
args: release --clean
|
||||||
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
|
||||||
|
POSTHOG_API_KEY_FOR_CLI: ${{ secrets.POSTHOG_API_KEY_FOR_CLI }}
|
||||||
|
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
|
||||||
|
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||||
|
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||||
|
- uses: actions/setup-python@v4
|
||||||
|
- run: pip install --upgrade cloudsmith-cli
|
||||||
|
- name: Publish to CloudSmith
|
||||||
|
run: sh cli/upload_to_cloudsmith.sh
|
||||||
|
env:
|
||||||
|
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}
|
||||||
|
47
.github/workflows/run-cli-tests.yml
vendored
Normal file
47
.github/workflows/run-cli-tests.yml
vendored
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
name: Go CLI Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
paths:
|
||||||
|
- "cli/**"
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
CLI_TESTS_UA_CLIENT_ID:
|
||||||
|
required: true
|
||||||
|
CLI_TESTS_UA_CLIENT_SECRET:
|
||||||
|
required: true
|
||||||
|
CLI_TESTS_SERVICE_TOKEN:
|
||||||
|
required: true
|
||||||
|
CLI_TESTS_PROJECT_ID:
|
||||||
|
required: true
|
||||||
|
CLI_TESTS_ENV_SLUG:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./cli
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v4
|
||||||
|
with:
|
||||||
|
go-version: "1.21.x"
|
||||||
|
- name: Install dependencies
|
||||||
|
run: go get .
|
||||||
|
- name: Test with the Go CLI
|
||||||
|
env:
|
||||||
|
CLI_TESTS_UA_CLIENT_ID: ${{ secrets.CLI_TESTS_UA_CLIENT_ID }}
|
||||||
|
CLI_TESTS_UA_CLIENT_SECRET: ${{ secrets.CLI_TESTS_UA_CLIENT_SECRET }}
|
||||||
|
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
|
||||||
|
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
|
||||||
|
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
|
||||||
|
|
||||||
|
run: go test -v -count=1 ./test
|
59
.github/workflows/update-be-new-migration-latest-timestamp.yml
vendored
Normal file
59
.github/workflows/update-be-new-migration-latest-timestamp.yml
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
name: Rename Migrations
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
paths:
|
||||||
|
- 'backend/src/db/migrations/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rename:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.event.pull_request.merged == true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Get list of newly added files in migration folder
|
||||||
|
run: |
|
||||||
|
git diff --name-status HEAD^ HEAD backend/src/db/migrations | grep '^A' | cut -f2 | xargs -n1 basename > added_files.txt
|
||||||
|
if [ ! -s added_files.txt ]; then
|
||||||
|
echo "No new files added. Skipping"
|
||||||
|
echo "SKIP_RENAME=true" >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Script to rename migrations
|
||||||
|
if: env.SKIP_RENAME != 'true'
|
||||||
|
run: python .github/resources/rename_migration_files.py
|
||||||
|
|
||||||
|
- name: Commit and push changes
|
||||||
|
if: env.SKIP_RENAME != 'true'
|
||||||
|
run: |
|
||||||
|
git config user.name github-actions
|
||||||
|
git config user.email github-actions@github.com
|
||||||
|
git add ./backend/src/db/migrations
|
||||||
|
rm added_files.txt
|
||||||
|
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
|
||||||
|
|
||||||
|
- name: Get PR details
|
||||||
|
id: pr_details
|
||||||
|
run: |
|
||||||
|
PR_NUMBER=${{ github.event.pull_request.number }}
|
||||||
|
PR_MERGER=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.merged_by.login')
|
||||||
|
|
||||||
|
echo "PR Number: $PR_NUMBER"
|
||||||
|
echo "PR Merger: $PR_MERGER"
|
||||||
|
echo "pr_merger=$PR_MERGER" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
if: env.SKIP_RENAME != 'true'
|
||||||
|
uses: peter-evans/create-pull-request@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
|
||||||
|
title: 'GH Action: rename new migration file timestamp'
|
||||||
|
branch-suffix: timestamp
|
||||||
|
reviewers: ${{ steps.pr_details.outputs.pr_merger }}
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -59,9 +59,13 @@ yarn-error.log*
|
|||||||
# Infisical init
|
# Infisical init
|
||||||
.infisical.json
|
.infisical.json
|
||||||
|
|
||||||
|
.infisicalignore
|
||||||
|
|
||||||
# Editor specific
|
# Editor specific
|
||||||
.vscode/*
|
.vscode/*
|
||||||
|
|
||||||
frontend-build
|
frontend-build
|
||||||
|
|
||||||
*.tgz
|
*.tgz
|
||||||
|
cli/infisical-merge
|
||||||
|
cli/test/infisical-merge
|
||||||
|
@ -1 +1,7 @@
|
|||||||
.github/resources/docker-compose.be-test.yml:generic-api-key:16
|
.github/resources/docker-compose.be-test.yml:generic-api-key:16
|
||||||
|
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/IdentityRbacSection.tsx:generic-api-key:206
|
||||||
|
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
|
||||||
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
|
||||||
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
|
||||||
|
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
|
||||||
|
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
ARG POSTHOG_HOST=https://app.posthog.com
|
ARG POSTHOG_HOST=https://app.posthog.com
|
||||||
ARG POSTHOG_API_KEY=posthog-api-key
|
ARG POSTHOG_API_KEY=posthog-api-key
|
||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
|
ARG SAML_ORG_SLUG=saml-org-slug-default
|
||||||
|
|
||||||
FROM node:20-alpine AS base
|
FROM node:20-alpine AS base
|
||||||
|
|
||||||
@ -35,6 +36,8 @@ ARG INTERCOM_ID
|
|||||||
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
|
||||||
ARG INFISICAL_PLATFORM_VERSION
|
ARG INFISICAL_PLATFORM_VERSION
|
||||||
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
|
||||||
|
ARG SAML_ORG_SLUG
|
||||||
|
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
@ -100,6 +103,9 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
|
|||||||
ARG INTERCOM_ID=intercom-id
|
ARG INTERCOM_ID=intercom-id
|
||||||
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
|
||||||
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
BAKED_NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID
|
||||||
|
ARG SAML_ORG_SLUG
|
||||||
|
ENV NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG \
|
||||||
|
BAKED_NEXT_PUBLIC_SAML_ORG_SLUG=$SAML_ORG_SLUG
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ Check out the [Quickstart Guides](https://infisical.com/docs/getting-started/int
|
|||||||
|
|
||||||
| Use Infisical Cloud | Deploy Infisical on premise |
|
| Use Infisical Cloud | Deploy Infisical on premise |
|
||||||
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| The fastest and most reliable way to <br> get started with Infisical is signing up <br> for free to [Infisical Cloud](https://app.infisical.com/login). | <a href="https://infisical.com/docs/self-hosting/deployment-options/aws-ec2"><img src=".github/images/deploy-to-aws.png" width="150" width="300" /></a> <a href="https://infisical.com/docs/self-hosting/deployment-options/digital-ocean-marketplace" alt="Deploy to DigitalOcean"> <img width="217" alt="Deploy to DO" src="https://www.deploytodo.com/do-btn-blue.svg"/> </a> <br> View all [deployment options](https://infisical.com/docs/self-hosting/overview) |
|
| The fastest and most reliable way to <br> get started with Infisical is signing up <br> for free to [Infisical Cloud](https://app.infisical.com/login). | <br> View all [deployment options](https://infisical.com/docs/self-hosting/overview) |
|
||||||
|
|
||||||
### Run Infisical locally
|
### Run Infisical locally
|
||||||
|
|
||||||
|
@ -23,16 +23,17 @@ module.exports = {
|
|||||||
root: true,
|
root: true,
|
||||||
overrides: [
|
overrides: [
|
||||||
{
|
{
|
||||||
files: ["./e2e-test/**/*"],
|
files: ["./e2e-test/**/*", "./src/db/migrations/**/*"],
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-unsafe-member-access": "off",
|
"@typescript-eslint/no-unsafe-member-access": "off",
|
||||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||||
"@typescript-eslint/no-unsafe-argument": "off",
|
"@typescript-eslint/no-unsafe-argument": "off",
|
||||||
"@typescript-eslint/no-unsafe-return": "off",
|
"@typescript-eslint/no-unsafe-return": "off",
|
||||||
"@typescript-eslint/no-unsafe-call": "off",
|
"@typescript-eslint/no-unsafe-call": "off"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
"@typescript-eslint/no-empty-function": "off",
|
"@typescript-eslint/no-empty-function": "off",
|
||||||
"@typescript-eslint/no-unsafe-enum-comparison": "off",
|
"@typescript-eslint/no-unsafe-enum-comparison": "off",
|
||||||
|
@ -46,7 +46,7 @@ const deleteSecretImport = async (id: string) => {
|
|||||||
|
|
||||||
describe("Secret Import Router", async () => {
|
describe("Secret Import Router", async () => {
|
||||||
test.each([
|
test.each([
|
||||||
{ importEnv: "dev", importPath: "/" }, // one in root
|
{ importEnv: "prod", importPath: "/" }, // one in root
|
||||||
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
|
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
|
||||||
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
|
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
|
||||||
// check for default environments
|
// check for default environments
|
||||||
@ -66,7 +66,7 @@ describe("Secret Import Router", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Get secret imports", async () => {
|
test("Get secret imports", async () => {
|
||||||
const createdImport1 = await createSecretImport("/", "dev");
|
const createdImport1 = await createSecretImport("/", "prod");
|
||||||
const createdImport2 = await createSecretImport("/", "staging");
|
const createdImport2 = await createSecretImport("/", "staging");
|
||||||
const res = await testServer.inject({
|
const res = await testServer.inject({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
@ -103,10 +103,10 @@ describe("Secret Import Router", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Update secret import position", async () => {
|
test("Update secret import position", async () => {
|
||||||
const devImportDetails = { path: "/", envSlug: "dev" };
|
const prodImportDetails = { path: "/", envSlug: "prod" };
|
||||||
const stagingImportDetails = { path: "/", envSlug: "staging" };
|
const stagingImportDetails = { path: "/", envSlug: "staging" };
|
||||||
|
|
||||||
const createdImport1 = await createSecretImport(devImportDetails.path, devImportDetails.envSlug);
|
const createdImport1 = await createSecretImport(prodImportDetails.path, prodImportDetails.envSlug);
|
||||||
const createdImport2 = await createSecretImport(stagingImportDetails.path, stagingImportDetails.envSlug);
|
const createdImport2 = await createSecretImport(stagingImportDetails.path, stagingImportDetails.envSlug);
|
||||||
|
|
||||||
const updateImportRes = await testServer.inject({
|
const updateImportRes = await testServer.inject({
|
||||||
@ -136,7 +136,7 @@ describe("Secret Import Router", async () => {
|
|||||||
position: 2,
|
position: 2,
|
||||||
importEnv: expect.objectContaining({
|
importEnv: expect.objectContaining({
|
||||||
name: expect.any(String),
|
name: expect.any(String),
|
||||||
slug: expect.stringMatching(devImportDetails.envSlug),
|
slug: expect.stringMatching(prodImportDetails.envSlug),
|
||||||
id: expect.any(String)
|
id: expect.any(String)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -166,7 +166,7 @@ describe("Secret Import Router", async () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Delete secret import position", async () => {
|
test("Delete secret import position", async () => {
|
||||||
const createdImport1 = await createSecretImport("/", "dev");
|
const createdImport1 = await createSecretImport("/", "prod");
|
||||||
const createdImport2 = await createSecretImport("/", "staging");
|
const createdImport2 = await createSecretImport("/", "staging");
|
||||||
const deletedImport = await deleteSecretImport(createdImport1.id);
|
const deletedImport = await deleteSecretImport(createdImport1.id);
|
||||||
// check for default environments
|
// check for default environments
|
||||||
|
@ -942,6 +942,113 @@ describe.each([{ auth: AuthMode.JWT }, { auth: AuthMode.IDENTITY_ACCESS_TOKEN }]
|
|||||||
const secrets = await getSecrets(seedData1.environment.slug, path);
|
const secrets = await getSecrets(seedData1.environment.slug, path);
|
||||||
expect(secrets).toEqual([]);
|
expect(secrets).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.each(testRawSecrets)("Bulk create secret raw in path $path", async ({ path, secret }) => {
|
||||||
|
const createSecretReqBody = {
|
||||||
|
projectSlug: seedData1.project.slug,
|
||||||
|
environment: seedData1.environment.slug,
|
||||||
|
secretPath: path,
|
||||||
|
secrets: [
|
||||||
|
{
|
||||||
|
secretKey: secret.key,
|
||||||
|
secretValue: secret.value,
|
||||||
|
secretComment: secret.comment
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const createSecRes = await testServer.inject({
|
||||||
|
method: "POST",
|
||||||
|
url: `/api/v3/secrets/batch/raw`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${authToken}`
|
||||||
|
},
|
||||||
|
body: createSecretReqBody
|
||||||
|
});
|
||||||
|
expect(createSecRes.statusCode).toBe(200);
|
||||||
|
const createdSecretPayload = JSON.parse(createSecRes.payload);
|
||||||
|
expect(createdSecretPayload).toHaveProperty("secrets");
|
||||||
|
|
||||||
|
// fetch secrets
|
||||||
|
const secrets = await getSecrets(seedData1.environment.slug, path);
|
||||||
|
expect(secrets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: secret.key,
|
||||||
|
value: secret.value,
|
||||||
|
type: SecretType.Shared
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteRawSecret({ path, key: secret.key });
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(testRawSecrets)("Bulk update secret raw in path $path", async ({ secret, path }) => {
|
||||||
|
await createRawSecret({ path, ...secret });
|
||||||
|
const updateSecretReqBody = {
|
||||||
|
projectSlug: seedData1.project.slug,
|
||||||
|
environment: seedData1.environment.slug,
|
||||||
|
secretPath: path,
|
||||||
|
secrets: [
|
||||||
|
{
|
||||||
|
secretValue: "new-value",
|
||||||
|
secretKey: secret.key
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
const updateSecRes = await testServer.inject({
|
||||||
|
method: "PATCH",
|
||||||
|
url: `/api/v3/secrets/batch/raw`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${authToken}`
|
||||||
|
},
|
||||||
|
body: updateSecretReqBody
|
||||||
|
});
|
||||||
|
expect(updateSecRes.statusCode).toBe(200);
|
||||||
|
const updatedSecretPayload = JSON.parse(updateSecRes.payload);
|
||||||
|
expect(updatedSecretPayload).toHaveProperty("secrets");
|
||||||
|
|
||||||
|
// fetch secrets
|
||||||
|
const secrets = await getSecrets(seedData1.environment.slug, path);
|
||||||
|
expect(secrets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
key: secret.key,
|
||||||
|
value: "new-value",
|
||||||
|
version: 2,
|
||||||
|
type: SecretType.Shared
|
||||||
|
})
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
await deleteRawSecret({ path, key: secret.key });
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(testRawSecrets)("Bulk delete secret raw in path $path", async ({ path, secret }) => {
|
||||||
|
await createRawSecret({ path, ...secret });
|
||||||
|
|
||||||
|
const deletedSecretReqBody = {
|
||||||
|
projectSlug: seedData1.project.slug,
|
||||||
|
environment: seedData1.environment.slug,
|
||||||
|
secretPath: path,
|
||||||
|
secrets: [{ secretKey: secret.key }]
|
||||||
|
};
|
||||||
|
const deletedSecRes = await testServer.inject({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `/api/v3/secrets/batch/raw`,
|
||||||
|
headers: {
|
||||||
|
authorization: `Bearer ${authToken}`
|
||||||
|
},
|
||||||
|
body: deletedSecretReqBody
|
||||||
|
});
|
||||||
|
expect(deletedSecRes.statusCode).toBe(200);
|
||||||
|
const deletedSecretPayload = JSON.parse(deletedSecRes.payload);
|
||||||
|
expect(deletedSecretPayload).toHaveProperty("secrets");
|
||||||
|
|
||||||
|
// fetch secrets
|
||||||
|
const secrets = await getSecrets(seedData1.environment.slug, path);
|
||||||
|
expect(secrets).toEqual([]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
1509
backend/package-lock.json
generated
1509
backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -95,23 +95,28 @@
|
|||||||
"axios": "^1.6.7",
|
"axios": "^1.6.7",
|
||||||
"axios-retry": "^4.0.0",
|
"axios-retry": "^4.0.0",
|
||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"bullmq": "^5.3.3",
|
"bullmq": "^5.4.2",
|
||||||
|
"cassandra-driver": "^4.7.2",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
"fastify": "^4.26.0",
|
"fastify": "^4.26.0",
|
||||||
"fastify-plugin": "^4.5.1",
|
"fastify-plugin": "^4.5.1",
|
||||||
|
"google-auth-library": "^9.9.0",
|
||||||
|
"googleapis": "^137.1.0",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"ioredis": "^5.3.2",
|
"ioredis": "^5.3.2",
|
||||||
"jmespath": "^0.16.0",
|
"jmespath": "^0.16.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
"knex": "^3.0.1",
|
"knex": "^3.0.1",
|
||||||
|
"ldapjs": "^3.0.7",
|
||||||
"libsodium-wrappers": "^0.7.13",
|
"libsodium-wrappers": "^0.7.13",
|
||||||
"lodash.isequal": "^4.5.0",
|
"lodash.isequal": "^4.5.0",
|
||||||
"ms": "^2.1.3",
|
"ms": "^2.1.3",
|
||||||
"mysql2": "^3.9.1",
|
"mysql2": "^3.9.7",
|
||||||
"nanoid": "^5.0.4",
|
"nanoid": "^5.0.4",
|
||||||
"nodemailer": "^6.9.9",
|
"nodemailer": "^6.9.9",
|
||||||
"ora": "^7.0.1",
|
"ora": "^7.0.1",
|
||||||
|
"oracledb": "^6.4.0",
|
||||||
"passport-github": "^1.1.0",
|
"passport-github": "^1.1.0",
|
||||||
"passport-gitlab2": "^5.0.0",
|
"passport-gitlab2": "^5.0.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
|
@ -103,11 +103,15 @@ export const ${dalName} = (db: TDbClient) => {
|
|||||||
`import { z } from "zod";
|
`import { z } from "zod";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
|
||||||
export const register${pascalCase}Router = async (server: FastifyZodProvider) => {
|
export const register${pascalCase}Router = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({}),
|
params: z.object({}),
|
||||||
response: {
|
response: {
|
||||||
|
@ -7,10 +7,10 @@ const prompt = promptSync({ sigint: true });
|
|||||||
|
|
||||||
const migrationName = prompt("Enter name for migration: ");
|
const migrationName = prompt("Enter name for migration: ");
|
||||||
|
|
||||||
|
// Remove spaces from migration name and replace with hyphens
|
||||||
|
const formattedMigrationName = migrationName.replace(/\s+/g, "-");
|
||||||
|
|
||||||
execSync(
|
execSync(
|
||||||
`npx knex migrate:make --knexfile ${path.join(
|
`npx knex migrate:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${formattedMigrationName}`,
|
||||||
__dirname,
|
|
||||||
"../src/db/knexfile.ts"
|
|
||||||
)} -x ts ${migrationName}`,
|
|
||||||
{ stdio: "inherit" }
|
{ stdio: "inherit" }
|
||||||
);
|
);
|
||||||
|
18
backend/src/@types/fastify.d.ts
vendored
18
backend/src/@types/fastify.d.ts
vendored
@ -1,10 +1,14 @@
|
|||||||
import "fastify";
|
import "fastify";
|
||||||
|
|
||||||
import { TUsers } from "@app/db/schemas";
|
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 { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
import { TAuditLogServiceFactory } from "@app/ee/services/audit-log/audit-log-service";
|
||||||
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
import { TCreateAuditLogDTO } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { TAuditLogStreamServiceFactory } from "@app/ee/services/audit-log-stream/audit-log-stream-service";
|
||||||
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
import { TDynamicSecretServiceFactory } from "@app/ee/services/dynamic-secret/dynamic-secret-service";
|
||||||
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-service";
|
import { TDynamicSecretLeaseServiceFactory } from "@app/ee/services/dynamic-secret-lease/dynamic-secret-lease-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 { TIdentityProjectAdditionalPrivilegeServiceFactory } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service";
|
||||||
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-config-service";
|
||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
@ -25,8 +29,13 @@ import { TAuthPasswordFactory } from "@app/services/auth/auth-password-service";
|
|||||||
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
import { TAuthSignupFactory } from "@app/services/auth/auth-signup-service";
|
||||||
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
|
||||||
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
|
||||||
|
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
|
||||||
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
|
||||||
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
|
||||||
|
import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
|
||||||
|
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
|
||||||
|
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
|
||||||
|
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
|
||||||
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
|
||||||
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
|
||||||
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
|
||||||
@ -89,6 +98,8 @@ declare module "fastify" {
|
|||||||
orgRole: TOrgRoleServiceFactory;
|
orgRole: TOrgRoleServiceFactory;
|
||||||
superAdmin: TSuperAdminServiceFactory;
|
superAdmin: TSuperAdminServiceFactory;
|
||||||
user: TUserServiceFactory;
|
user: TUserServiceFactory;
|
||||||
|
group: TGroupServiceFactory;
|
||||||
|
groupProject: TGroupProjectServiceFactory;
|
||||||
apiKey: TApiKeyServiceFactory;
|
apiKey: TApiKeyServiceFactory;
|
||||||
project: TProjectServiceFactory;
|
project: TProjectServiceFactory;
|
||||||
projectMembership: TProjectMembershipServiceFactory;
|
projectMembership: TProjectMembershipServiceFactory;
|
||||||
@ -108,6 +119,12 @@ declare module "fastify" {
|
|||||||
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
identityAccessToken: TIdentityAccessTokenServiceFactory;
|
||||||
identityProject: TIdentityProjectServiceFactory;
|
identityProject: TIdentityProjectServiceFactory;
|
||||||
identityUa: TIdentityUaServiceFactory;
|
identityUa: TIdentityUaServiceFactory;
|
||||||
|
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
|
||||||
|
identityGcpAuth: TIdentityGcpAuthServiceFactory;
|
||||||
|
identityAwsAuth: TIdentityAwsAuthServiceFactory;
|
||||||
|
identityAzureAuth: TIdentityAzureAuthServiceFactory;
|
||||||
|
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
|
||||||
|
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
|
||||||
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
|
||||||
secretApprovalRequest: TSecretApprovalRequestServiceFactory;
|
secretApprovalRequest: TSecretApprovalRequestServiceFactory;
|
||||||
secretRotation: TSecretRotationServiceFactory;
|
secretRotation: TSecretRotationServiceFactory;
|
||||||
@ -116,6 +133,7 @@ declare module "fastify" {
|
|||||||
scim: TScimServiceFactory;
|
scim: TScimServiceFactory;
|
||||||
ldap: TLdapConfigServiceFactory;
|
ldap: TLdapConfigServiceFactory;
|
||||||
auditLog: TAuditLogServiceFactory;
|
auditLog: TAuditLogServiceFactory;
|
||||||
|
auditLogStream: TAuditLogStreamServiceFactory;
|
||||||
secretScanning: TSecretScanningServiceFactory;
|
secretScanning: TSecretScanningServiceFactory;
|
||||||
license: TLicenseServiceFactory;
|
license: TLicenseServiceFactory;
|
||||||
trustedIp: TTrustedIpServiceFactory;
|
trustedIp: TTrustedIpServiceFactory;
|
||||||
|
115
backend/src/@types/knex.d.ts
vendored
115
backend/src/@types/knex.d.ts
vendored
@ -2,11 +2,26 @@ import { Knex } from "knex";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
TableName,
|
TableName,
|
||||||
|
TAccessApprovalPolicies,
|
||||||
|
TAccessApprovalPoliciesApprovers,
|
||||||
|
TAccessApprovalPoliciesApproversInsert,
|
||||||
|
TAccessApprovalPoliciesApproversUpdate,
|
||||||
|
TAccessApprovalPoliciesInsert,
|
||||||
|
TAccessApprovalPoliciesUpdate,
|
||||||
|
TAccessApprovalRequests,
|
||||||
|
TAccessApprovalRequestsInsert,
|
||||||
|
TAccessApprovalRequestsReviewers,
|
||||||
|
TAccessApprovalRequestsReviewersInsert,
|
||||||
|
TAccessApprovalRequestsReviewersUpdate,
|
||||||
|
TAccessApprovalRequestsUpdate,
|
||||||
TApiKeys,
|
TApiKeys,
|
||||||
TApiKeysInsert,
|
TApiKeysInsert,
|
||||||
TApiKeysUpdate,
|
TApiKeysUpdate,
|
||||||
TAuditLogs,
|
TAuditLogs,
|
||||||
TAuditLogsInsert,
|
TAuditLogsInsert,
|
||||||
|
TAuditLogStreams,
|
||||||
|
TAuditLogStreamsInsert,
|
||||||
|
TAuditLogStreamsUpdate,
|
||||||
TAuditLogsUpdate,
|
TAuditLogsUpdate,
|
||||||
TAuthTokens,
|
TAuthTokens,
|
||||||
TAuthTokenSessions,
|
TAuthTokenSessions,
|
||||||
@ -29,12 +44,33 @@ import {
|
|||||||
TGitAppOrg,
|
TGitAppOrg,
|
||||||
TGitAppOrgInsert,
|
TGitAppOrgInsert,
|
||||||
TGitAppOrgUpdate,
|
TGitAppOrgUpdate,
|
||||||
|
TGroupProjectMembershipRoles,
|
||||||
|
TGroupProjectMembershipRolesInsert,
|
||||||
|
TGroupProjectMembershipRolesUpdate,
|
||||||
|
TGroupProjectMemberships,
|
||||||
|
TGroupProjectMembershipsInsert,
|
||||||
|
TGroupProjectMembershipsUpdate,
|
||||||
|
TGroups,
|
||||||
|
TGroupsInsert,
|
||||||
|
TGroupsUpdate,
|
||||||
TIdentities,
|
TIdentities,
|
||||||
TIdentitiesInsert,
|
TIdentitiesInsert,
|
||||||
TIdentitiesUpdate,
|
TIdentitiesUpdate,
|
||||||
TIdentityAccessTokens,
|
TIdentityAccessTokens,
|
||||||
TIdentityAccessTokensInsert,
|
TIdentityAccessTokensInsert,
|
||||||
TIdentityAccessTokensUpdate,
|
TIdentityAccessTokensUpdate,
|
||||||
|
TIdentityAwsAuths,
|
||||||
|
TIdentityAwsAuthsInsert,
|
||||||
|
TIdentityAwsAuthsUpdate,
|
||||||
|
TIdentityAzureAuths,
|
||||||
|
TIdentityAzureAuthsInsert,
|
||||||
|
TIdentityAzureAuthsUpdate,
|
||||||
|
TIdentityGcpAuths,
|
||||||
|
TIdentityGcpAuthsInsert,
|
||||||
|
TIdentityGcpAuthsUpdate,
|
||||||
|
TIdentityKubernetesAuths,
|
||||||
|
TIdentityKubernetesAuthsInsert,
|
||||||
|
TIdentityKubernetesAuthsUpdate,
|
||||||
TIdentityOrgMemberships,
|
TIdentityOrgMemberships,
|
||||||
TIdentityOrgMembershipsInsert,
|
TIdentityOrgMembershipsInsert,
|
||||||
TIdentityOrgMembershipsUpdate,
|
TIdentityOrgMembershipsUpdate,
|
||||||
@ -65,6 +101,9 @@ import {
|
|||||||
TLdapConfigs,
|
TLdapConfigs,
|
||||||
TLdapConfigsInsert,
|
TLdapConfigsInsert,
|
||||||
TLdapConfigsUpdate,
|
TLdapConfigsUpdate,
|
||||||
|
TLdapGroupMaps,
|
||||||
|
TLdapGroupMapsInsert,
|
||||||
|
TLdapGroupMapsUpdate,
|
||||||
TOrganizations,
|
TOrganizations,
|
||||||
TOrganizationsInsert,
|
TOrganizationsInsert,
|
||||||
TOrganizationsUpdate,
|
TOrganizationsUpdate,
|
||||||
@ -188,6 +227,9 @@ import {
|
|||||||
TUserEncryptionKeys,
|
TUserEncryptionKeys,
|
||||||
TUserEncryptionKeysInsert,
|
TUserEncryptionKeysInsert,
|
||||||
TUserEncryptionKeysUpdate,
|
TUserEncryptionKeysUpdate,
|
||||||
|
TUserGroupMembership,
|
||||||
|
TUserGroupMembershipInsert,
|
||||||
|
TUserGroupMembershipUpdate,
|
||||||
TUsers,
|
TUsers,
|
||||||
TUsersInsert,
|
TUsersInsert,
|
||||||
TUsersUpdate,
|
TUsersUpdate,
|
||||||
@ -195,10 +237,27 @@ import {
|
|||||||
TWebhooksInsert,
|
TWebhooksInsert,
|
||||||
TWebhooksUpdate
|
TWebhooksUpdate
|
||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
|
import { TSecretReferences, TSecretReferencesInsert, TSecretReferencesUpdate } from "@app/db/schemas/secret-references";
|
||||||
|
|
||||||
declare module "knex/types/tables" {
|
declare module "knex/types/tables" {
|
||||||
interface Tables {
|
interface Tables {
|
||||||
[TableName.Users]: Knex.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
|
[TableName.Users]: Knex.CompositeTableType<TUsers, TUsersInsert, TUsersUpdate>;
|
||||||
|
[TableName.Groups]: Knex.CompositeTableType<TGroups, TGroupsInsert, TGroupsUpdate>;
|
||||||
|
[TableName.UserGroupMembership]: Knex.CompositeTableType<
|
||||||
|
TUserGroupMembership,
|
||||||
|
TUserGroupMembershipInsert,
|
||||||
|
TUserGroupMembershipUpdate
|
||||||
|
>;
|
||||||
|
[TableName.GroupProjectMembership]: Knex.CompositeTableType<
|
||||||
|
TGroupProjectMemberships,
|
||||||
|
TGroupProjectMembershipsInsert,
|
||||||
|
TGroupProjectMembershipsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.GroupProjectMembershipRole]: Knex.CompositeTableType<
|
||||||
|
TGroupProjectMembershipRoles,
|
||||||
|
TGroupProjectMembershipRolesInsert,
|
||||||
|
TGroupProjectMembershipRolesUpdate
|
||||||
|
>;
|
||||||
[TableName.UserAliases]: Knex.CompositeTableType<TUserAliases, TUserAliasesInsert, TUserAliasesUpdate>;
|
[TableName.UserAliases]: Knex.CompositeTableType<TUserAliases, TUserAliasesInsert, TUserAliasesUpdate>;
|
||||||
[TableName.UserEncryptionKey]: Knex.CompositeTableType<
|
[TableName.UserEncryptionKey]: Knex.CompositeTableType<
|
||||||
TUserEncryptionKeys,
|
TUserEncryptionKeys,
|
||||||
@ -252,6 +311,11 @@ declare module "knex/types/tables" {
|
|||||||
>;
|
>;
|
||||||
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
|
||||||
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
|
||||||
|
[TableName.SecretReference]: Knex.CompositeTableType<
|
||||||
|
TSecretReferences,
|
||||||
|
TSecretReferencesInsert,
|
||||||
|
TSecretReferencesUpdate
|
||||||
|
>;
|
||||||
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
|
||||||
TSecretBlindIndexes,
|
TSecretBlindIndexes,
|
||||||
TSecretBlindIndexesInsert,
|
TSecretBlindIndexesInsert,
|
||||||
@ -280,6 +344,26 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityUniversalAuthsInsert,
|
TIdentityUniversalAuthsInsert,
|
||||||
TIdentityUniversalAuthsUpdate
|
TIdentityUniversalAuthsUpdate
|
||||||
>;
|
>;
|
||||||
|
[TableName.IdentityKubernetesAuth]: Knex.CompositeTableType<
|
||||||
|
TIdentityKubernetesAuths,
|
||||||
|
TIdentityKubernetesAuthsInsert,
|
||||||
|
TIdentityKubernetesAuthsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.IdentityGcpAuth]: Knex.CompositeTableType<
|
||||||
|
TIdentityGcpAuths,
|
||||||
|
TIdentityGcpAuthsInsert,
|
||||||
|
TIdentityGcpAuthsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
|
||||||
|
TIdentityAwsAuths,
|
||||||
|
TIdentityAwsAuthsInsert,
|
||||||
|
TIdentityAwsAuthsUpdate
|
||||||
|
>;
|
||||||
|
[TableName.IdentityAzureAuth]: Knex.CompositeTableType<
|
||||||
|
TIdentityAzureAuths,
|
||||||
|
TIdentityAzureAuthsInsert,
|
||||||
|
TIdentityAzureAuthsUpdate
|
||||||
|
>;
|
||||||
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
|
||||||
TIdentityUaClientSecrets,
|
TIdentityUaClientSecrets,
|
||||||
TIdentityUaClientSecretsInsert,
|
TIdentityUaClientSecretsInsert,
|
||||||
@ -310,6 +394,31 @@ declare module "knex/types/tables" {
|
|||||||
TIdentityProjectAdditionalPrivilegeInsert,
|
TIdentityProjectAdditionalPrivilegeInsert,
|
||||||
TIdentityProjectAdditionalPrivilegeUpdate
|
TIdentityProjectAdditionalPrivilegeUpdate
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalPolicy]: Knex.CompositeTableType<
|
||||||
|
TAccessApprovalPolicies,
|
||||||
|
TAccessApprovalPoliciesInsert,
|
||||||
|
TAccessApprovalPoliciesUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalPolicyApprover]: Knex.CompositeTableType<
|
||||||
|
TAccessApprovalPoliciesApprovers,
|
||||||
|
TAccessApprovalPoliciesApproversInsert,
|
||||||
|
TAccessApprovalPoliciesApproversUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalRequest]: Knex.CompositeTableType<
|
||||||
|
TAccessApprovalRequests,
|
||||||
|
TAccessApprovalRequestsInsert,
|
||||||
|
TAccessApprovalRequestsUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
|
[TableName.AccessApprovalRequestReviewer]: Knex.CompositeTableType<
|
||||||
|
TAccessApprovalRequestsReviewers,
|
||||||
|
TAccessApprovalRequestsReviewersInsert,
|
||||||
|
TAccessApprovalRequestsReviewersUpdate
|
||||||
|
>;
|
||||||
|
|
||||||
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
[TableName.ScimToken]: Knex.CompositeTableType<TScimTokens, TScimTokensInsert, TScimTokensUpdate>;
|
||||||
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
[TableName.SecretApprovalPolicy]: Knex.CompositeTableType<
|
||||||
TSecretApprovalPolicies,
|
TSecretApprovalPolicies,
|
||||||
@ -370,8 +479,14 @@ declare module "knex/types/tables" {
|
|||||||
>;
|
>;
|
||||||
[TableName.SamlConfig]: Knex.CompositeTableType<TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate>;
|
[TableName.SamlConfig]: Knex.CompositeTableType<TSamlConfigs, TSamlConfigsInsert, TSamlConfigsUpdate>;
|
||||||
[TableName.LdapConfig]: Knex.CompositeTableType<TLdapConfigs, TLdapConfigsInsert, TLdapConfigsUpdate>;
|
[TableName.LdapConfig]: Knex.CompositeTableType<TLdapConfigs, TLdapConfigsInsert, TLdapConfigsUpdate>;
|
||||||
|
[TableName.LdapGroupMap]: Knex.CompositeTableType<TLdapGroupMaps, TLdapGroupMapsInsert, TLdapGroupMapsUpdate>;
|
||||||
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
[TableName.OrgBot]: Knex.CompositeTableType<TOrgBots, TOrgBotsInsert, TOrgBotsUpdate>;
|
||||||
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
[TableName.AuditLog]: Knex.CompositeTableType<TAuditLogs, TAuditLogsInsert, TAuditLogsUpdate>;
|
||||||
|
[TableName.AuditLogStream]: Knex.CompositeTableType<
|
||||||
|
TAuditLogStreams,
|
||||||
|
TAuditLogStreamsInsert,
|
||||||
|
TAuditLogStreamsUpdate
|
||||||
|
>;
|
||||||
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
|
[TableName.GitAppInstallSession]: Knex.CompositeTableType<
|
||||||
TGitAppInstallSessions,
|
TGitAppInstallSessions,
|
||||||
TGitAppInstallSessionsInsert,
|
TGitAppInstallSessionsInsert,
|
||||||
|
@ -0,0 +1,112 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { TableName, TOrgMemberships } from "../schemas";
|
||||||
|
|
||||||
|
const validateOrgMembership = (membershipToValidate: TOrgMemberships, firstMembership: TOrgMemberships) => {
|
||||||
|
const firstOrgId = firstMembership.orgId;
|
||||||
|
const firstUserId = firstMembership.userId;
|
||||||
|
|
||||||
|
if (membershipToValidate.id === firstMembership.id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (membershipToValidate.inviteEmail !== firstMembership.inviteEmail) {
|
||||||
|
throw new Error(`Invite emails are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
if (membershipToValidate.orgId !== firstMembership.orgId) {
|
||||||
|
throw new Error(`OrgIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
if (membershipToValidate.role !== firstMembership.role) {
|
||||||
|
throw new Error(`Roles are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
if (membershipToValidate.roleId !== firstMembership.roleId) {
|
||||||
|
throw new Error(`RoleIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
if (membershipToValidate.status !== firstMembership.status) {
|
||||||
|
throw new Error(`Statuses are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
if (membershipToValidate.userId !== firstMembership.userId) {
|
||||||
|
throw new Error(`UserIds are different for the same userId and orgId: ${firstUserId}, ${firstOrgId}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const RowSchema = z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
orgId: z.string(),
|
||||||
|
cnt: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Transactional find and delete duplicate rows
|
||||||
|
await knex.transaction(async (tx) => {
|
||||||
|
const duplicateRows = await tx(TableName.OrgMembership)
|
||||||
|
.select("userId", "orgId") // Select the userId and orgId so we can group by them
|
||||||
|
.whereNotNull("userId") // Ensure that the userId is not null
|
||||||
|
.count("* as cnt") // Count the number of rows for each userId and orgId, so we can make sure there are more than 1 row (a duplicate)
|
||||||
|
.groupBy("userId", "orgId")
|
||||||
|
.havingRaw("count(*) > ?", [1]); // Using havingRaw for direct SQL expressions
|
||||||
|
|
||||||
|
// Parse the rows to ensure they are in the correct format, and for type safety
|
||||||
|
const parsedRows = RowSchema.array().parse(duplicateRows);
|
||||||
|
|
||||||
|
// For each of the duplicate rows, loop through and find the actual memberships to delete
|
||||||
|
for (const row of parsedRows) {
|
||||||
|
const count = Number(row.cnt);
|
||||||
|
|
||||||
|
// An extra check to ensure that the count is actually a number, and the number is greater than 2
|
||||||
|
if (typeof count !== "number" || count < 2) {
|
||||||
|
// eslint-disable-next-line no-continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all the organization memberships that have the same userId and orgId
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const rowsToDelete = await tx(TableName.OrgMembership).where({
|
||||||
|
userId: row.userId,
|
||||||
|
orgId: row.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure that all the rows have exactly the same value, except id, createdAt, updatedAt
|
||||||
|
for (const rowToDelete of rowsToDelete) {
|
||||||
|
validateOrgMembership(rowToDelete, rowsToDelete[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the row with the latest createdAt, which we will keep
|
||||||
|
|
||||||
|
let lowestCreatedAt: number | null = null;
|
||||||
|
let latestCreatedRow: TOrgMemberships | null = null;
|
||||||
|
|
||||||
|
for (const rowToDelete of rowsToDelete) {
|
||||||
|
if (lowestCreatedAt === null || rowToDelete.createdAt.getTime() < lowestCreatedAt) {
|
||||||
|
lowestCreatedAt = rowToDelete.createdAt.getTime();
|
||||||
|
latestCreatedRow = rowToDelete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!latestCreatedRow) {
|
||||||
|
throw new Error("Failed to find last created membership");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out the latest row from the rows to delete
|
||||||
|
const membershipIdsToDelete = rowsToDelete.map((r) => r.id).filter((id) => id !== latestCreatedRow!.id);
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-await-in-loop
|
||||||
|
const numberOfRowsDeleted = await tx(TableName.OrgMembership).whereIn("id", membershipIdsToDelete).delete();
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.log(
|
||||||
|
`Deleted ${numberOfRowsDeleted} duplicate organization memberships for ${row.userId} and ${row.orgId}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||||
|
table.unique(["userId", "orgId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.OrgMembership, (table) => {
|
||||||
|
table.dropUnique(["userId", "orgId"]);
|
||||||
|
});
|
||||||
|
}
|
82
backend/src/db/migrations/20240412174842_group.ts
Normal file
82
backend/src/db/migrations/20240412174842_group.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
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.Groups))) {
|
||||||
|
await knex.schema.createTable(TableName.Groups, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("orgId").notNullable();
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
t.string("name").notNullable();
|
||||||
|
t.string("slug").notNullable();
|
||||||
|
t.unique(["orgId", "slug"]);
|
||||||
|
t.string("role").notNullable();
|
||||||
|
t.uuid("roleId");
|
||||||
|
t.foreign("roleId").references("id").inTable(TableName.OrgRoles);
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.Groups);
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.UserGroupMembership))) {
|
||||||
|
await knex.schema.createTable(TableName.UserGroupMembership, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); // link to user and link to groups cascade on groups
|
||||||
|
t.uuid("userId").notNullable();
|
||||||
|
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
|
||||||
|
t.uuid("groupId").notNullable();
|
||||||
|
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.UserGroupMembership);
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.GroupProjectMembership))) {
|
||||||
|
await knex.schema.createTable(TableName.GroupProjectMembership, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("projectId").notNullable();
|
||||||
|
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
|
||||||
|
t.uuid("groupId").notNullable();
|
||||||
|
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await createOnUpdateTrigger(knex, TableName.GroupProjectMembership);
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.GroupProjectMembershipRole))) {
|
||||||
|
await knex.schema.createTable(TableName.GroupProjectMembershipRole, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("role").notNullable();
|
||||||
|
t.uuid("projectMembershipId").notNullable();
|
||||||
|
t.foreign("projectMembershipId").references("id").inTable(TableName.GroupProjectMembership).onDelete("CASCADE");
|
||||||
|
// until role is changed/removed the role should not deleted
|
||||||
|
t.uuid("customRoleId");
|
||||||
|
t.foreign("customRoleId").references("id").inTable(TableName.ProjectRoles);
|
||||||
|
t.boolean("isTemporary").notNullable().defaultTo(false);
|
||||||
|
t.string("temporaryMode");
|
||||||
|
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
|
||||||
|
t.datetime("temporaryAccessStartTime");
|
||||||
|
t.datetime("temporaryAccessEndTime");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.GroupProjectMembershipRole);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.GroupProjectMembershipRole);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.GroupProjectMembershipRole);
|
||||||
|
|
||||||
|
await knex.schema.dropTableIfExists(TableName.UserGroupMembership);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.UserGroupMembership);
|
||||||
|
|
||||||
|
await knex.schema.dropTableIfExists(TableName.GroupProjectMembership);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.GroupProjectMembership);
|
||||||
|
|
||||||
|
await knex.schema.dropTableIfExists(TableName.Groups);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.Groups);
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { ProjectMembershipRole, TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesProjectRoleFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "role");
|
||||||
|
const doesProjectRoleIdFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "roleId");
|
||||||
|
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||||
|
if (doesProjectRoleFieldExist) t.dropColumn("roleId");
|
||||||
|
if (doesProjectRoleIdFieldExist) t.dropColumn("role");
|
||||||
|
});
|
||||||
|
|
||||||
|
const doesIdentityProjectRoleFieldExist = await knex.schema.hasColumn(TableName.IdentityProjectMembership, "role");
|
||||||
|
const doesIdentityProjectRoleIdFieldExist = await knex.schema.hasColumn(
|
||||||
|
TableName.IdentityProjectMembership,
|
||||||
|
"roleId"
|
||||||
|
);
|
||||||
|
await knex.schema.alterTable(TableName.IdentityProjectMembership, (t) => {
|
||||||
|
if (doesIdentityProjectRoleFieldExist) t.dropColumn("roleId");
|
||||||
|
if (doesIdentityProjectRoleIdFieldExist) t.dropColumn("role");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesProjectRoleFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "role");
|
||||||
|
const doesProjectRoleIdFieldExist = await knex.schema.hasColumn(TableName.ProjectMembership, "roleId");
|
||||||
|
await knex.schema.alterTable(TableName.ProjectMembership, (t) => {
|
||||||
|
if (!doesProjectRoleFieldExist) t.string("role").defaultTo(ProjectMembershipRole.Member);
|
||||||
|
if (!doesProjectRoleIdFieldExist) {
|
||||||
|
t.uuid("roleId");
|
||||||
|
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const doesIdentityProjectRoleFieldExist = await knex.schema.hasColumn(TableName.IdentityProjectMembership, "role");
|
||||||
|
const doesIdentityProjectRoleIdFieldExist = await knex.schema.hasColumn(
|
||||||
|
TableName.IdentityProjectMembership,
|
||||||
|
"roleId"
|
||||||
|
);
|
||||||
|
await knex.schema.alterTable(TableName.IdentityProjectMembership, (t) => {
|
||||||
|
if (!doesIdentityProjectRoleFieldExist) t.string("role").defaultTo(ProjectMembershipRole.Member);
|
||||||
|
if (!doesIdentityProjectRoleIdFieldExist) {
|
||||||
|
t.uuid("roleId");
|
||||||
|
t.foreign("roleId").references("id").inTable(TableName.ProjectRoles);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.UserGroupMembership, (t) => {
|
||||||
|
t.boolean("isPending").notNullable().defaultTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.UserGroupMembership, (t) => {
|
||||||
|
t.dropColumn("isPending");
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,34 @@
|
|||||||
|
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.LdapGroupMap))) {
|
||||||
|
await knex.schema.createTable(TableName.LdapGroupMap, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("ldapConfigId").notNullable();
|
||||||
|
t.foreign("ldapConfigId").references("id").inTable(TableName.LdapConfig).onDelete("CASCADE");
|
||||||
|
t.string("ldapGroupCN").notNullable();
|
||||||
|
t.uuid("groupId").notNullable();
|
||||||
|
t.foreign("groupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
|
||||||
|
t.unique(["ldapGroupCN", "groupId", "ldapConfigId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.LdapGroupMap);
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
|
||||||
|
t.string("groupSearchBase").notNullable().defaultTo("");
|
||||||
|
t.string("groupSearchFilter").notNullable().defaultTo("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.LdapGroupMap);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.LdapGroupMap);
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
|
||||||
|
t.dropColumn("groupSearchBase");
|
||||||
|
t.dropColumn("groupSearchFilter");
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
|
||||||
|
t.string("searchFilter").notNullable().defaultTo("");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.alterTable(TableName.LdapConfig, (t) => {
|
||||||
|
t.dropColumn("searchFilter");
|
||||||
|
});
|
||||||
|
}
|
28
backend/src/db/migrations/20240429154610_audit-log-index.ts
Normal file
28
backend/src/db/migrations/20240429154610_audit-log-index.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
||||||
|
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
||||||
|
const doesCreatedAtExist = await knex.schema.hasColumn(TableName.AuditLog, "createdAt");
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesProjectIdExist && doesCreatedAtExist) t.index(["projectId", "createdAt"]);
|
||||||
|
if (doesOrgIdExist && doesCreatedAtExist) t.index(["orgId", "createdAt"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
||||||
|
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
||||||
|
const doesCreatedAtExist = await knex.schema.hasColumn(TableName.AuditLog, "createdAt");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesProjectIdExist && doesCreatedAtExist) t.dropIndex(["projectId", "createdAt"]);
|
||||||
|
if (doesOrgIdExist && doesCreatedAtExist) t.dropIndex(["orgId", "createdAt"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
28
backend/src/db/migrations/20240503101144_audit-log-stream.ts
Normal file
28
backend/src/db/migrations/20240503101144_audit-log-stream.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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.AuditLogStream))) {
|
||||||
|
await knex.schema.createTable(TableName.AuditLogStream, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("url").notNullable();
|
||||||
|
t.text("encryptedHeadersCiphertext");
|
||||||
|
t.text("encryptedHeadersIV");
|
||||||
|
t.text("encryptedHeadersTag");
|
||||||
|
t.string("encryptedHeadersAlgorithm");
|
||||||
|
t.string("encryptedHeadersKeyEncoding");
|
||||||
|
t.uuid("orgId").notNullable();
|
||||||
|
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AuditLogStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AuditLogStream);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AuditLogStream);
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const isUsersTablePresent = await knex.schema.hasTable(TableName.Users);
|
||||||
|
if (isUsersTablePresent) {
|
||||||
|
const hasIsEmailVerifiedColumn = await knex.schema.hasColumn(TableName.Users, "isEmailVerified");
|
||||||
|
|
||||||
|
if (!hasIsEmailVerifiedColumn) {
|
||||||
|
await knex.schema.alterTable(TableName.Users, (t) => {
|
||||||
|
t.boolean("isEmailVerified").defaultTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backfilling the isEmailVerified to true where isAccepted is true
|
||||||
|
await knex(TableName.Users).update({ isEmailVerified: true }).where("isAccepted", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUserAliasTablePresent = await knex.schema.hasTable(TableName.UserAliases);
|
||||||
|
if (isUserAliasTablePresent) {
|
||||||
|
await knex.schema.alterTable(TableName.UserAliases, (t) => {
|
||||||
|
t.string("username").nullable().alter();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSuperAdminTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
|
||||||
|
if (isSuperAdminTablePresent) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.boolean("trustSamlEmails").defaultTo(false);
|
||||||
|
t.boolean("trustLdapEmails").defaultTo(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
if (await knex.schema.hasColumn(TableName.Users, "isEmailVerified")) {
|
||||||
|
await knex.schema.alterTable(TableName.Users, (t) => {
|
||||||
|
t.dropColumn("isEmailVerified");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustSamlEmails")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("trustSamlEmails");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustLdapEmails")) {
|
||||||
|
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||||
|
t.dropColumn("trustLdapEmails");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
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.AccessApprovalPolicy))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalPolicy, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("name").notNullable();
|
||||||
|
t.integer("approvals").defaultTo(1).notNullable();
|
||||||
|
t.string("secretPath");
|
||||||
|
|
||||||
|
t.uuid("envId").notNullable();
|
||||||
|
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicy);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyApprover))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalPolicyApprover, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("approverId").notNullable();
|
||||||
|
t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicyApprover);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicyApprover);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicy);
|
||||||
|
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicyApprover);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicy);
|
||||||
|
}
|
51
backend/src/db/migrations/20240507162141_access.ts
Normal file
51
backend/src/db/migrations/20240507162141_access.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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.AccessApprovalRequest))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalRequest, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
|
||||||
|
t.uuid("policyId").notNullable();
|
||||||
|
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("privilegeId").nullable();
|
||||||
|
t.foreign("privilegeId").references("id").inTable(TableName.ProjectUserAdditionalPrivilege).onDelete("CASCADE");
|
||||||
|
|
||||||
|
t.uuid("requestedBy").notNullable();
|
||||||
|
t.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
|
||||||
|
// We use these values to create the actual privilege at a later point in time.
|
||||||
|
t.boolean("isTemporary").notNullable();
|
||||||
|
t.string("temporaryRange").nullable();
|
||||||
|
|
||||||
|
t.jsonb("permissions").notNullable();
|
||||||
|
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequest);
|
||||||
|
|
||||||
|
if (!(await knex.schema.hasTable(TableName.AccessApprovalRequestReviewer))) {
|
||||||
|
await knex.schema.createTable(TableName.AccessApprovalRequestReviewer, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.uuid("member").notNullable();
|
||||||
|
t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
|
||||||
|
t.string("status").notNullable();
|
||||||
|
t.uuid("requestId").notNullable();
|
||||||
|
t.foreign("requestId").references("id").inTable(TableName.AccessApprovalRequest).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalRequestReviewer);
|
||||||
|
await knex.schema.dropTableIfExists(TableName.AccessApprovalRequest);
|
||||||
|
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.AccessApprovalRequest);
|
||||||
|
}
|
@ -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.IdentityAwsAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityAwsAuth, (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("stsEndpoint").notNullable();
|
||||||
|
t.string("allowedPrincipalArns").notNullable();
|
||||||
|
t.string("allowedAccountIds").notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityAwsAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityAwsAuth);
|
||||||
|
}
|
@ -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.IdentityGcpAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityGcpAuth, (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("allowedServiceAccounts").notNullable();
|
||||||
|
t.string("allowedProjects").notNullable();
|
||||||
|
t.string("allowedZones").notNullable(); // GCE only (fully qualified zone names)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityGcpAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityGcpAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityGcpAuth);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
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.SecretReference))) {
|
||||||
|
await knex.schema.createTable(TableName.SecretReference, (t) => {
|
||||||
|
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
|
||||||
|
t.string("environment").notNullable();
|
||||||
|
t.string("secretPath").notNullable();
|
||||||
|
t.uuid("secretId").notNullable();
|
||||||
|
t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE");
|
||||||
|
t.timestamps(true, true, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.SecretReference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.SecretReference);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.SecretReference);
|
||||||
|
}
|
36
backend/src/db/migrations/20240518142614_kubernetes-auth.ts
Normal file
36
backend/src/db/migrations/20240518142614_kubernetes-auth.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
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.IdentityKubernetesAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityKubernetesAuth, (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("kubernetesHost").notNullable();
|
||||||
|
t.text("encryptedCaCert").notNullable();
|
||||||
|
t.string("caCertIV").notNullable();
|
||||||
|
t.string("caCertTag").notNullable();
|
||||||
|
t.text("encryptedTokenReviewerJwt").notNullable();
|
||||||
|
t.string("tokenReviewerJwtIV").notNullable();
|
||||||
|
t.string("tokenReviewerJwtTag").notNullable();
|
||||||
|
t.string("allowedNamespaces").notNullable();
|
||||||
|
t.string("allowedNames").notNullable();
|
||||||
|
t.string("allowedAudience").notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityKubernetesAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
|
||||||
|
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
|
||||||
|
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Integration, (t) => {
|
||||||
|
if (!hasIsSyncedColumn) {
|
||||||
|
t.boolean("isSynced").nullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasSyncMessageColumn) {
|
||||||
|
t.text("syncMessage").nullable();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLastSyncJobId) {
|
||||||
|
t.string("lastSyncJobId").nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
|
||||||
|
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
|
||||||
|
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Integration, (t) => {
|
||||||
|
if (hasIsSyncedColumn) {
|
||||||
|
t.dropColumn("isSynced");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasSyncMessageColumn) {
|
||||||
|
t.dropColumn("syncMessage");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasLastSyncJobId) {
|
||||||
|
t.dropColumn("lastSyncJobId");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
||||||
|
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesProjectIdExist) t.index("projectId");
|
||||||
|
if (doesOrgIdExist) t.index("orgId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesOrgIdExist = await knex.schema.hasColumn(TableName.AuditLog, "orgId");
|
||||||
|
const doesProjectIdExist = await knex.schema.hasColumn(TableName.AuditLog, "projectId");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesProjectIdExist) t.dropIndex("projectId");
|
||||||
|
if (doesOrgIdExist) t.dropIndex("orgId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "envId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
||||||
|
if (doesEnvIdExist) t.index("envId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "envId");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
||||||
|
if (doesEnvIdExist) t.dropIndex("envId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SecretVersion, "envId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretVersion)) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretVersion, (t) => {
|
||||||
|
if (doesEnvIdExist) t.index("envId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesEnvIdExist = await knex.schema.hasColumn(TableName.SecretVersion, "envId");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.SecretVersion)) {
|
||||||
|
await knex.schema.alterTable(TableName.SecretVersion, (t) => {
|
||||||
|
if (doesEnvIdExist) t.dropIndex("envId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "snapshotId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
||||||
|
if (doesSnapshotIdExist) t.index("snapshotId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "snapshotId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
|
||||||
|
if (doesSnapshotIdExist) t.dropIndex("snapshotId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotFolder, "snapshotId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotFolder)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotFolder, (t) => {
|
||||||
|
if (doesSnapshotIdExist) t.index("snapshotId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesSnapshotIdExist = await knex.schema.hasColumn(TableName.SnapshotFolder, "snapshotId");
|
||||||
|
if (await knex.schema.hasTable(TableName.SnapshotFolder)) {
|
||||||
|
await knex.schema.alterTable(TableName.SnapshotFolder, (t) => {
|
||||||
|
if (doesSnapshotIdExist) t.dropIndex("snapshotId");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
|
||||||
|
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
|
||||||
|
if (await knex.schema.hasTable(TableName.Secret)) {
|
||||||
|
await knex.schema.alterTable(TableName.Secret, (t) => {
|
||||||
|
if (doesFolderIdExist && doesUserIdExist) t.index(["folderId", "userId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
|
||||||
|
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.Secret)) {
|
||||||
|
await knex.schema.alterTable(TableName.Secret, (t) => {
|
||||||
|
if (doesUserIdExist && doesFolderIdExist) t.dropIndex(["folderId", "userId"]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const doesExpireAtExist = await knex.schema.hasColumn(TableName.AuditLog, "expiresAt");
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesExpireAtExist) t.index("expiresAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const doesExpireAtExist = await knex.schema.hasColumn(TableName.AuditLog, "expiresAt");
|
||||||
|
|
||||||
|
if (await knex.schema.hasTable(TableName.AuditLog)) {
|
||||||
|
await knex.schema.alterTable(TableName.AuditLog, (t) => {
|
||||||
|
if (doesExpireAtExist) t.dropIndex("expiresAt");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
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.IdentityAzureAuth))) {
|
||||||
|
await knex.schema.createTable(TableName.IdentityAzureAuth, (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("tenantId").notNullable();
|
||||||
|
t.string("resource").notNullable();
|
||||||
|
t.string("allowedServicePrincipalIds").notNullable();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await createOnUpdateTrigger(knex, TableName.IdentityAzureAuth);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
await knex.schema.dropTableIfExists(TableName.IdentityAzureAuth);
|
||||||
|
await dropOnUpdateTrigger(knex, TableName.IdentityAzureAuth);
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TableName } from "../schemas";
|
||||||
|
|
||||||
|
export async function up(knex: Knex): Promise<void> {
|
||||||
|
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
|
||||||
|
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
|
||||||
|
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Users, (t) => {
|
||||||
|
if (!hasConsecutiveFailedMfaAttempts) {
|
||||||
|
t.integer("consecutiveFailedMfaAttempts").defaultTo(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasIsLocked) {
|
||||||
|
t.boolean("isLocked").defaultTo(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasTemporaryLockDateEnd) {
|
||||||
|
t.dateTime("temporaryLockDateEnd").nullable();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(knex: Knex): Promise<void> {
|
||||||
|
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
|
||||||
|
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
|
||||||
|
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
|
||||||
|
|
||||||
|
await knex.schema.alterTable(TableName.Users, (t) => {
|
||||||
|
if (hasConsecutiveFailedMfaAttempts) {
|
||||||
|
t.dropColumn("consecutiveFailedMfaAttempts");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasIsLocked) {
|
||||||
|
t.dropColumn("isLocked");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasTemporaryLockDateEnd) {
|
||||||
|
t.dropColumn("temporaryLockDateEnd");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
25
backend/src/db/schemas/access-approval-policies-approvers.ts
Normal file
25
backend/src/db/schemas/access-approval-policies-approvers.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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 AccessApprovalPoliciesApproversSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
approverId: z.string().uuid(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAccessApprovalPoliciesApprovers = z.infer<typeof AccessApprovalPoliciesApproversSchema>;
|
||||||
|
export type TAccessApprovalPoliciesApproversInsert = Omit<
|
||||||
|
z.input<typeof AccessApprovalPoliciesApproversSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TAccessApprovalPoliciesApproversUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalPoliciesApproversSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@ -7,16 +7,18 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const SecretApprovalPoliciesSchema = z.object({
|
export const AccessApprovalPoliciesSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
secretPath: z.string().nullable().optional(),
|
|
||||||
approvals: z.number().default(1),
|
approvals: z.number().default(1),
|
||||||
|
secretPath: z.string().nullable().optional(),
|
||||||
envId: z.string().uuid(),
|
envId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSecretApprovalPolicies = z.infer<typeof SecretApprovalPoliciesSchema>;
|
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;
|
||||||
export type TSecretApprovalPoliciesInsert = Omit<TSecretApprovalPolicies, TImmutableDBKeys>;
|
export type TAccessApprovalPoliciesInsert = Omit<z.input<typeof AccessApprovalPoliciesSchema>, TImmutableDBKeys>;
|
||||||
export type TSecretApprovalPoliciesUpdate = Partial<Omit<TSecretApprovalPolicies, TImmutableDBKeys>>;
|
export type TAccessApprovalPoliciesUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalPoliciesSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
26
backend/src/db/schemas/access-approval-requests-reviewers.ts
Normal file
26
backend/src/db/schemas/access-approval-requests-reviewers.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 AccessApprovalRequestsReviewersSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
member: z.string().uuid(),
|
||||||
|
status: z.string(),
|
||||||
|
requestId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAccessApprovalRequestsReviewers = z.infer<typeof AccessApprovalRequestsReviewersSchema>;
|
||||||
|
export type TAccessApprovalRequestsReviewersInsert = Omit<
|
||||||
|
z.input<typeof AccessApprovalRequestsReviewersSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TAccessApprovalRequestsReviewersUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalRequestsReviewersSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
26
backend/src/db/schemas/access-approval-requests.ts
Normal file
26
backend/src/db/schemas/access-approval-requests.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 AccessApprovalRequestsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
policyId: z.string().uuid(),
|
||||||
|
privilegeId: z.string().uuid().nullable().optional(),
|
||||||
|
requestedBy: z.string().uuid(),
|
||||||
|
isTemporary: z.boolean(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
permissions: z.unknown(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;
|
||||||
|
export type TAccessApprovalRequestsInsert = Omit<z.input<typeof AccessApprovalRequestsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TAccessApprovalRequestsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof AccessApprovalRequestsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
25
backend/src/db/schemas/audit-log-streams.ts
Normal file
25
backend/src/db/schemas/audit-log-streams.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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 AuditLogStreamsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
url: z.string(),
|
||||||
|
encryptedHeadersCiphertext: z.string().nullable().optional(),
|
||||||
|
encryptedHeadersIV: z.string().nullable().optional(),
|
||||||
|
encryptedHeadersTag: z.string().nullable().optional(),
|
||||||
|
encryptedHeadersAlgorithm: z.string().nullable().optional(),
|
||||||
|
encryptedHeadersKeyEncoding: z.string().nullable().optional(),
|
||||||
|
orgId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TAuditLogStreams = z.infer<typeof AuditLogStreamsSchema>;
|
||||||
|
export type TAuditLogStreamsInsert = Omit<z.input<typeof AuditLogStreamsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TAuditLogStreamsUpdate = Partial<Omit<z.input<typeof AuditLogStreamsSchema>, TImmutableDBKeys>>;
|
31
backend/src/db/schemas/group-project-membership-roles.ts
Normal file
31
backend/src/db/schemas/group-project-membership-roles.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 { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
|
export const GroupProjectMembershipRolesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
role: z.string(),
|
||||||
|
projectMembershipId: z.string().uuid(),
|
||||||
|
customRoleId: z.string().uuid().nullable().optional(),
|
||||||
|
isTemporary: z.boolean().default(false),
|
||||||
|
temporaryMode: z.string().nullable().optional(),
|
||||||
|
temporaryRange: z.string().nullable().optional(),
|
||||||
|
temporaryAccessStartTime: z.date().nullable().optional(),
|
||||||
|
temporaryAccessEndTime: z.date().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TGroupProjectMembershipRoles = z.infer<typeof GroupProjectMembershipRolesSchema>;
|
||||||
|
export type TGroupProjectMembershipRolesInsert = Omit<
|
||||||
|
z.input<typeof GroupProjectMembershipRolesSchema>,
|
||||||
|
TImmutableDBKeys
|
||||||
|
>;
|
||||||
|
export type TGroupProjectMembershipRolesUpdate = Partial<
|
||||||
|
Omit<z.input<typeof GroupProjectMembershipRolesSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
22
backend/src/db/schemas/group-project-memberships.ts
Normal file
22
backend/src/db/schemas/group-project-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 GroupProjectMembershipsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
projectId: z.string(),
|
||||||
|
groupId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TGroupProjectMemberships = z.infer<typeof GroupProjectMembershipsSchema>;
|
||||||
|
export type TGroupProjectMembershipsInsert = Omit<z.input<typeof GroupProjectMembershipsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TGroupProjectMembershipsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof GroupProjectMembershipsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@ -7,17 +7,17 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TImmutableDBKeys } from "./models";
|
import { TImmutableDBKeys } from "./models";
|
||||||
|
|
||||||
export const OrgRolesSchema = z.object({
|
export const GroupsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
name: z.string(),
|
|
||||||
description: z.string().nullable().optional(),
|
|
||||||
slug: z.string(),
|
|
||||||
permissions: z.unknown(),
|
|
||||||
createdAt: z.date(),
|
|
||||||
updatedAt: z.date(),
|
|
||||||
orgId: z.string().uuid(),
|
orgId: z.string().uuid(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
role: z.string(),
|
||||||
|
roleId: z.string().uuid().nullable().optional(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TOrgRoles = z.infer<typeof OrgRolesSchema>;
|
export type TGroups = z.infer<typeof GroupsSchema>;
|
||||||
export type TOrgRolesInsert = Omit<TOrgRoles, TImmutableDBKeys>;
|
export type TGroupsInsert = Omit<z.input<typeof GroupsSchema>, TImmutableDBKeys>;
|
||||||
export type TOrgRolesUpdate = Partial<Omit<TOrgRoles, TImmutableDBKeys>>;
|
export type TGroupsUpdate = Partial<Omit<z.input<typeof GroupsSchema>, TImmutableDBKeys>>;
|
27
backend/src/db/schemas/identity-aws-auths.ts
Normal file
27
backend/src/db/schemas/identity-aws-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 IdentityAwsAuthsSchema = 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(),
|
||||||
|
stsEndpoint: z.string(),
|
||||||
|
allowedPrincipalArns: z.string(),
|
||||||
|
allowedAccountIds: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;
|
||||||
|
export type TIdentityAwsAuthsInsert = Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityAwsAuthsUpdate = Partial<Omit<z.input<typeof IdentityAwsAuthsSchema>, TImmutableDBKeys>>;
|
26
backend/src/db/schemas/identity-azure-auths.ts
Normal file
26
backend/src/db/schemas/identity-azure-auths.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
// 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 IdentityAzureAuthsSchema = 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(),
|
||||||
|
tenantId: z.string(),
|
||||||
|
resource: z.string(),
|
||||||
|
allowedServicePrincipalIds: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
|
||||||
|
export type TIdentityAzureAuthsInsert = Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityAzureAuthsUpdate = Partial<Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>>;
|
27
backend/src/db/schemas/identity-gcp-auths.ts
Normal file
27
backend/src/db/schemas/identity-gcp-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 IdentityGcpAuthsSchema = 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(),
|
||||||
|
allowedServiceAccounts: z.string(),
|
||||||
|
allowedProjects: z.string(),
|
||||||
|
allowedZones: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;
|
||||||
|
export type TIdentityGcpAuthsInsert = Omit<z.input<typeof IdentityGcpAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityGcpAuthsUpdate = Partial<Omit<z.input<typeof IdentityGcpAuthsSchema>, TImmutableDBKeys>>;
|
35
backend/src/db/schemas/identity-kubernetes-auths.ts
Normal file
35
backend/src/db/schemas/identity-kubernetes-auths.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// 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 IdentityKubernetesAuthsSchema = 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(),
|
||||||
|
kubernetesHost: z.string(),
|
||||||
|
encryptedCaCert: z.string(),
|
||||||
|
caCertIV: z.string(),
|
||||||
|
caCertTag: z.string(),
|
||||||
|
encryptedTokenReviewerJwt: z.string(),
|
||||||
|
tokenReviewerJwtIV: z.string(),
|
||||||
|
tokenReviewerJwtTag: z.string(),
|
||||||
|
allowedNamespaces: z.string(),
|
||||||
|
allowedNames: z.string(),
|
||||||
|
allowedAudience: z.string()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
|
||||||
|
export type TIdentityKubernetesAuthsInsert = Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TIdentityKubernetesAuthsUpdate = Partial<
|
||||||
|
Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>
|
||||||
|
>;
|
@ -9,8 +9,6 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const IdentityProjectMembershipsSchema = z.object({
|
export const IdentityProjectMembershipsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
role: z.string(),
|
|
||||||
roleId: z.string().uuid().nullable().optional(),
|
|
||||||
projectId: z.string(),
|
projectId: z.string(),
|
||||||
identityId: z.string().uuid(),
|
identityId: z.string().uuid(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
|
export * from "./access-approval-policies";
|
||||||
|
export * from "./access-approval-policies-approvers";
|
||||||
|
export * from "./access-approval-requests";
|
||||||
|
export * from "./access-approval-requests-reviewers";
|
||||||
export * from "./api-keys";
|
export * from "./api-keys";
|
||||||
|
export * from "./audit-log-streams";
|
||||||
export * from "./audit-logs";
|
export * from "./audit-logs";
|
||||||
export * from "./auth-token-sessions";
|
export * from "./auth-token-sessions";
|
||||||
export * from "./auth-tokens";
|
export * from "./auth-tokens";
|
||||||
@ -7,8 +12,15 @@ export * from "./dynamic-secret-leases";
|
|||||||
export * from "./dynamic-secrets";
|
export * from "./dynamic-secrets";
|
||||||
export * from "./git-app-install-sessions";
|
export * from "./git-app-install-sessions";
|
||||||
export * from "./git-app-org";
|
export * from "./git-app-org";
|
||||||
|
export * from "./group-project-membership-roles";
|
||||||
|
export * from "./group-project-memberships";
|
||||||
|
export * from "./groups";
|
||||||
export * from "./identities";
|
export * from "./identities";
|
||||||
export * from "./identity-access-tokens";
|
export * from "./identity-access-tokens";
|
||||||
|
export * from "./identity-aws-auths";
|
||||||
|
export * from "./identity-azure-auths";
|
||||||
|
export * from "./identity-gcp-auths";
|
||||||
|
export * from "./identity-kubernetes-auths";
|
||||||
export * from "./identity-org-memberships";
|
export * from "./identity-org-memberships";
|
||||||
export * from "./identity-project-additional-privilege";
|
export * from "./identity-project-additional-privilege";
|
||||||
export * from "./identity-project-membership-role";
|
export * from "./identity-project-membership-role";
|
||||||
@ -19,6 +31,7 @@ export * from "./incident-contacts";
|
|||||||
export * from "./integration-auths";
|
export * from "./integration-auths";
|
||||||
export * from "./integrations";
|
export * from "./integrations";
|
||||||
export * from "./ldap-configs";
|
export * from "./ldap-configs";
|
||||||
|
export * from "./ldap-group-maps";
|
||||||
export * from "./models";
|
export * from "./models";
|
||||||
export * from "./org-bots";
|
export * from "./org-bots";
|
||||||
export * from "./org-memberships";
|
export * from "./org-memberships";
|
||||||
@ -61,5 +74,6 @@ export * from "./trusted-ips";
|
|||||||
export * from "./user-actions";
|
export * from "./user-actions";
|
||||||
export * from "./user-aliases";
|
export * from "./user-aliases";
|
||||||
export * from "./user-encryption-keys";
|
export * from "./user-encryption-keys";
|
||||||
|
export * from "./user-group-membership";
|
||||||
export * from "./users";
|
export * from "./users";
|
||||||
export * from "./webhooks";
|
export * from "./webhooks";
|
||||||
|
@ -28,7 +28,10 @@ export const IntegrationsSchema = z.object({
|
|||||||
secretPath: z.string().default("/"),
|
secretPath: z.string().default("/"),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
lastUsed: z.date().nullable().optional()
|
lastUsed: z.date().nullable().optional(),
|
||||||
|
isSynced: z.boolean().nullable().optional(),
|
||||||
|
syncMessage: z.string().nullable().optional(),
|
||||||
|
lastSyncJobId: z.string().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
export type TIntegrations = z.infer<typeof IntegrationsSchema>;
|
||||||
|
@ -23,7 +23,10 @@ export const LdapConfigsSchema = z.object({
|
|||||||
caCertIV: z.string(),
|
caCertIV: z.string(),
|
||||||
caCertTag: z.string(),
|
caCertTag: z.string(),
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date()
|
updatedAt: z.date(),
|
||||||
|
groupSearchBase: z.string().default(""),
|
||||||
|
groupSearchFilter: z.string().default(""),
|
||||||
|
searchFilter: z.string().default("")
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
|
export type TLdapConfigs = z.infer<typeof LdapConfigsSchema>;
|
||||||
|
19
backend/src/db/schemas/ldap-group-maps.ts
Normal file
19
backend/src/db/schemas/ldap-group-maps.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// 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 LdapGroupMapsSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
ldapConfigId: z.string().uuid(),
|
||||||
|
ldapGroupCN: z.string(),
|
||||||
|
groupId: z.string().uuid()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TLdapGroupMaps = z.infer<typeof LdapGroupMapsSchema>;
|
||||||
|
export type TLdapGroupMapsInsert = Omit<z.input<typeof LdapGroupMapsSchema>, TImmutableDBKeys>;
|
||||||
|
export type TLdapGroupMapsUpdate = Partial<Omit<z.input<typeof LdapGroupMapsSchema>, TImmutableDBKeys>>;
|
@ -2,6 +2,10 @@ import { z } from "zod";
|
|||||||
|
|
||||||
export enum TableName {
|
export enum TableName {
|
||||||
Users = "users",
|
Users = "users",
|
||||||
|
Groups = "groups",
|
||||||
|
GroupProjectMembership = "group_project_memberships",
|
||||||
|
GroupProjectMembershipRole = "group_project_membership_roles",
|
||||||
|
UserGroupMembership = "user_group_membership",
|
||||||
UserAliases = "user_aliases",
|
UserAliases = "user_aliases",
|
||||||
UserEncryptionKey = "user_encryption_keys",
|
UserEncryptionKey = "user_encryption_keys",
|
||||||
AuthTokens = "auth_tokens",
|
AuthTokens = "auth_tokens",
|
||||||
@ -24,6 +28,7 @@ export enum TableName {
|
|||||||
ProjectUserMembershipRole = "project_user_membership_roles",
|
ProjectUserMembershipRole = "project_user_membership_roles",
|
||||||
ProjectKeys = "project_keys",
|
ProjectKeys = "project_keys",
|
||||||
Secret = "secrets",
|
Secret = "secrets",
|
||||||
|
SecretReference = "secret_references",
|
||||||
SecretBlindIndex = "secret_blind_indexes",
|
SecretBlindIndex = "secret_blind_indexes",
|
||||||
SecretVersion = "secret_versions",
|
SecretVersion = "secret_versions",
|
||||||
SecretFolder = "secret_folders",
|
SecretFolder = "secret_folders",
|
||||||
@ -40,12 +45,20 @@ export enum TableName {
|
|||||||
Identity = "identities",
|
Identity = "identities",
|
||||||
IdentityAccessToken = "identity_access_tokens",
|
IdentityAccessToken = "identity_access_tokens",
|
||||||
IdentityUniversalAuth = "identity_universal_auths",
|
IdentityUniversalAuth = "identity_universal_auths",
|
||||||
|
IdentityKubernetesAuth = "identity_kubernetes_auths",
|
||||||
|
IdentityGcpAuth = "identity_gcp_auths",
|
||||||
|
IdentityAzureAuth = "identity_azure_auths",
|
||||||
IdentityUaClientSecret = "identity_ua_client_secrets",
|
IdentityUaClientSecret = "identity_ua_client_secrets",
|
||||||
|
IdentityAwsAuth = "identity_aws_auths",
|
||||||
IdentityOrgMembership = "identity_org_memberships",
|
IdentityOrgMembership = "identity_org_memberships",
|
||||||
IdentityProjectMembership = "identity_project_memberships",
|
IdentityProjectMembership = "identity_project_memberships",
|
||||||
IdentityProjectMembershipRole = "identity_project_membership_role",
|
IdentityProjectMembershipRole = "identity_project_membership_role",
|
||||||
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
IdentityProjectAdditionalPrivilege = "identity_project_additional_privilege",
|
||||||
ScimToken = "scim_tokens",
|
ScimToken = "scim_tokens",
|
||||||
|
AccessApprovalPolicy = "access_approval_policies",
|
||||||
|
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
|
||||||
|
AccessApprovalRequest = "access_approval_requests",
|
||||||
|
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
|
||||||
SecretApprovalPolicy = "secret_approval_policies",
|
SecretApprovalPolicy = "secret_approval_policies",
|
||||||
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
|
||||||
SecretApprovalRequest = "secret_approval_requests",
|
SecretApprovalRequest = "secret_approval_requests",
|
||||||
@ -56,7 +69,9 @@ export enum TableName {
|
|||||||
SecretRotationOutput = "secret_rotation_outputs",
|
SecretRotationOutput = "secret_rotation_outputs",
|
||||||
SamlConfig = "saml_configs",
|
SamlConfig = "saml_configs",
|
||||||
LdapConfig = "ldap_configs",
|
LdapConfig = "ldap_configs",
|
||||||
|
LdapGroupMap = "ldap_group_maps",
|
||||||
AuditLog = "audit_logs",
|
AuditLog = "audit_logs",
|
||||||
|
AuditLogStream = "audit_log_streams",
|
||||||
GitAppInstallSession = "git_app_install_sessions",
|
GitAppInstallSession = "git_app_install_sessions",
|
||||||
GitAppOrg = "git_app_org",
|
GitAppOrg = "git_app_org",
|
||||||
SecretScanningGitRisk = "secret_scanning_git_risks",
|
SecretScanningGitRisk = "secret_scanning_git_risks",
|
||||||
@ -132,5 +147,9 @@ export enum ProjectUpgradeStatus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum IdentityAuthMethod {
|
export enum IdentityAuthMethod {
|
||||||
Univeral = "universal-auth"
|
Univeral = "universal-auth",
|
||||||
|
KUBERNETES_AUTH = "kubernetes-auth",
|
||||||
|
GCP_AUTH = "gcp-auth",
|
||||||
|
AWS_AUTH = "aws-auth",
|
||||||
|
AZURE_AUTH = "azure-auth"
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,10 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
|
|
||||||
export const ProjectMembershipsSchema = z.object({
|
export const ProjectMembershipsSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
role: z.string(),
|
|
||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
userId: z.string().uuid(),
|
userId: z.string().uuid(),
|
||||||
projectId: z.string(),
|
projectId: z.string()
|
||||||
roleId: z.string().uuid().nullable().optional()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TProjectMemberships = z.infer<typeof ProjectMembershipsSchema>;
|
export type TProjectMemberships = z.infer<typeof ProjectMembershipsSchema>;
|
||||||
|
21
backend/src/db/schemas/secret-references.ts
Normal file
21
backend/src/db/schemas/secret-references.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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 SecretReferencesSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
environment: z.string(),
|
||||||
|
secretPath: z.string(),
|
||||||
|
secretId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date()
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TSecretReferences = z.infer<typeof SecretReferencesSchema>;
|
||||||
|
export type TSecretReferencesInsert = Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>;
|
||||||
|
export type TSecretReferencesUpdate = Partial<Omit<z.input<typeof SecretReferencesSchema>, TImmutableDBKeys>>;
|
@ -14,7 +14,9 @@ export const SuperAdminSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
allowedSignUpDomain: z.string().nullable().optional(),
|
allowedSignUpDomain: z.string().nullable().optional(),
|
||||||
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000")
|
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"),
|
||||||
|
trustSamlEmails: z.boolean().default(false).nullable().optional(),
|
||||||
|
trustLdapEmails: z.boolean().default(false).nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;
|
||||||
|
@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
|
|||||||
export const UserAliasesSchema = z.object({
|
export const UserAliasesSchema = z.object({
|
||||||
id: z.string().uuid(),
|
id: z.string().uuid(),
|
||||||
userId: z.string().uuid(),
|
userId: z.string().uuid(),
|
||||||
username: z.string(),
|
username: z.string().nullable().optional(),
|
||||||
aliasType: z.string(),
|
aliasType: z.string(),
|
||||||
externalId: z.string(),
|
externalId: z.string(),
|
||||||
emails: z.string().array().nullable().optional(),
|
emails: z.string().array().nullable().optional(),
|
||||||
|
21
backend/src/db/schemas/user-group-membership.ts
Normal file
21
backend/src/db/schemas/user-group-membership.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// 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 UserGroupMembershipSchema = z.object({
|
||||||
|
id: z.string().uuid(),
|
||||||
|
userId: z.string().uuid(),
|
||||||
|
groupId: z.string().uuid(),
|
||||||
|
createdAt: z.date(),
|
||||||
|
updatedAt: z.date(),
|
||||||
|
isPending: z.boolean().default(false)
|
||||||
|
});
|
||||||
|
|
||||||
|
export type TUserGroupMembership = z.infer<typeof UserGroupMembershipSchema>;
|
||||||
|
export type TUserGroupMembershipInsert = Omit<z.input<typeof UserGroupMembershipSchema>, TImmutableDBKeys>;
|
||||||
|
export type TUserGroupMembershipUpdate = Partial<Omit<z.input<typeof UserGroupMembershipSchema>, TImmutableDBKeys>>;
|
@ -21,7 +21,11 @@ export const UsersSchema = z.object({
|
|||||||
createdAt: z.date(),
|
createdAt: z.date(),
|
||||||
updatedAt: z.date(),
|
updatedAt: z.date(),
|
||||||
isGhost: z.boolean().default(false),
|
isGhost: z.boolean().default(false),
|
||||||
username: z.string()
|
username: z.string(),
|
||||||
|
isEmailVerified: z.boolean().default(false).nullable().optional(),
|
||||||
|
consecutiveFailedMfaAttempts: z.number().optional(),
|
||||||
|
isLocked: z.boolean().optional(),
|
||||||
|
temporaryLockDateEnd: z.date().nullable().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TUsers = z.infer<typeof UsersSchema>;
|
export type TUsers = z.infer<typeof UsersSchema>;
|
||||||
|
@ -33,8 +33,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
const projectMembership = await knex(TableName.ProjectMembership)
|
const projectMembership = await knex(TableName.ProjectMembership)
|
||||||
.insert({
|
.insert({
|
||||||
projectId: project.id,
|
projectId: project.id,
|
||||||
userId: seedData1.id,
|
userId: seedData1.id
|
||||||
role: ProjectMembershipRole.Admin
|
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
await knex(TableName.ProjectUserMembershipRole).insert({
|
await knex(TableName.ProjectUserMembershipRole).insert({
|
||||||
|
@ -78,8 +78,7 @@ export async function seed(knex: Knex): Promise<void> {
|
|||||||
const identityProjectMembership = await knex(TableName.IdentityProjectMembership)
|
const identityProjectMembership = await knex(TableName.IdentityProjectMembership)
|
||||||
.insert({
|
.insert({
|
||||||
identityId: seedData1.machineIdentity.id,
|
identityId: seedData1.machineIdentity.id,
|
||||||
projectId: seedData1.project.id,
|
projectId: seedData1.project.id
|
||||||
role: ProjectMembershipRole.Admin
|
|
||||||
})
|
})
|
||||||
.returning("*");
|
.returning("*");
|
||||||
|
|
||||||
|
168
backend/src/ee/routes/v1/access-approval-policy-router.ts
Normal file
168
backend/src/ee/routes/v1/access-approval-policy-router.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import { nanoid } from "nanoid";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
projectSlug: z.string().trim(),
|
||||||
|
name: z.string().optional(),
|
||||||
|
secretPath: z.string().trim().default("/"),
|
||||||
|
environment: z.string(),
|
||||||
|
approvers: z.string().array().min(1),
|
||||||
|
approvals: z.number().min(1).default(1)
|
||||||
|
})
|
||||||
|
.refine((data) => data.approvals <= data.approvers.length, {
|
||||||
|
path: ["approvals"],
|
||||||
|
message: "The number of approvals should be lower than the number of approvers."
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
approval: sapPubSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const approval = await server.services.accessApprovalPolicy.createAccessApprovalPolicy({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body,
|
||||||
|
projectSlug: req.body.projectSlug,
|
||||||
|
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`
|
||||||
|
});
|
||||||
|
return { approval };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
approvals: sapPubSchema.extend({ approvers: z.string().array(), secretPath: z.string().optional() }).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const approvals = await server.services.accessApprovalPolicy.getAccessApprovalPolicyByProjectSlug({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectSlug: req.query.projectSlug
|
||||||
|
});
|
||||||
|
return { approvals };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/count",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string(),
|
||||||
|
envSlug: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
count: z.number()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { count } = await server.services.accessApprovalPolicy.getAccessPolicyCountByEnvSlug({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
projectSlug: req.query.projectSlug,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
envSlug: req.query.envSlug
|
||||||
|
});
|
||||||
|
return { count };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:policyId",
|
||||||
|
method: "PATCH",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
policyId: z.string()
|
||||||
|
}),
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
name: z.string().optional(),
|
||||||
|
secretPath: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.optional()
|
||||||
|
.transform((val) => (val === "" ? "/" : val)),
|
||||||
|
approvers: z.string().array().min(1),
|
||||||
|
approvals: z.number().min(1).default(1)
|
||||||
|
})
|
||||||
|
.refine((data) => data.approvals <= data.approvers.length, {
|
||||||
|
path: ["approvals"],
|
||||||
|
message: "The number of approvals should be lower than the number of approvers."
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
approval: sapPubSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
await server.services.accessApprovalPolicy.updateAccessApprovalPolicy({
|
||||||
|
policyId: req.params.policyId,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:policyId",
|
||||||
|
method: "DELETE",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
policyId: z.string()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
approval: sapPubSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const approval = await server.services.accessApprovalPolicy.deleteAccessApprovalPolicy({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
policyId: req.params.policyId
|
||||||
|
});
|
||||||
|
return { approval };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
160
backend/src/ee/routes/v1/access-approval-request-router.ts
Normal file
160
backend/src/ee/routes/v1/access-approval-request-router.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema } from "@app/db/schemas";
|
||||||
|
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerAccessApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
permissions: z.any().array(),
|
||||||
|
isTemporary: z.boolean(),
|
||||||
|
temporaryRange: z.string().optional()
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
approval: AccessApprovalRequestsSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { request } = await server.services.accessApprovalRequest.createAccessApprovalRequest({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
permissions: req.body.permissions,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
projectSlug: req.query.projectSlug,
|
||||||
|
temporaryRange: req.body.temporaryRange,
|
||||||
|
isTemporary: req.body.isTemporary
|
||||||
|
});
|
||||||
|
return { approval: request };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/count",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
pendingCount: z.number(),
|
||||||
|
finalizedCount: z.number()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { count } = await server.services.accessApprovalRequest.getCount({
|
||||||
|
projectSlug: req.query.projectSlug,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return { ...count };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
projectSlug: z.string().trim(),
|
||||||
|
authorProjectMembershipId: z.string().trim().optional(),
|
||||||
|
envSlug: z.string().trim().optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
requests: AccessApprovalRequestsSchema.extend({
|
||||||
|
environmentName: z.string(),
|
||||||
|
isApproved: z.boolean(),
|
||||||
|
privilege: z
|
||||||
|
.object({
|
||||||
|
membershipId: z.string(),
|
||||||
|
isTemporary: z.boolean(),
|
||||||
|
temporaryMode: z.string().nullish(),
|
||||||
|
temporaryRange: z.string().nullish(),
|
||||||
|
temporaryAccessStartTime: z.date().nullish(),
|
||||||
|
temporaryAccessEndTime: z.date().nullish(),
|
||||||
|
permissions: z.unknown()
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
policy: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
approvals: z.number(),
|
||||||
|
approvers: z.string().array(),
|
||||||
|
secretPath: z.string().nullish(),
|
||||||
|
envId: z.string()
|
||||||
|
}),
|
||||||
|
reviewers: z
|
||||||
|
.object({
|
||||||
|
member: z.string(),
|
||||||
|
status: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}).array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
|
||||||
|
projectSlug: req.query.projectSlug,
|
||||||
|
authorProjectMembershipId: req.query.authorProjectMembershipId,
|
||||||
|
envSlug: req.query.envSlug,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return { requests };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:requestId/review",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
requestId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED])
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
review: AccessApprovalRequestsReviewersSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const review = await server.services.accessApprovalRequest.reviewAccessRequest({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
requestId: req.params.requestId,
|
||||||
|
status: req.body.status
|
||||||
|
});
|
||||||
|
|
||||||
|
return { review };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
215
backend/src/ee/routes/v1/audit-log-stream-router.ts
Normal file
215
backend/src/ee/routes/v1/audit-log-stream-router.ts
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { AUDIT_LOG_STREAMS } from "@app/lib/api-docs";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { SanitizedAuditLogStreamSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerAuditLogStreamRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Create an Audit Log Stream.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
body: z.object({
|
||||||
|
url: z.string().min(1).describe(AUDIT_LOG_STREAMS.CREATE.url),
|
||||||
|
headers: z
|
||||||
|
.object({
|
||||||
|
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.key),
|
||||||
|
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.CREATE.headers.value)
|
||||||
|
})
|
||||||
|
.describe(AUDIT_LOG_STREAMS.CREATE.headers.desc)
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
auditLogStream: SanitizedAuditLogStreamSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const auditLogStream = await server.services.auditLogStream.create({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
url: req.body.url,
|
||||||
|
headers: req.body.headers
|
||||||
|
});
|
||||||
|
|
||||||
|
return { auditLogStream };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "PATCH",
|
||||||
|
url: "/:id",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Update an Audit Log Stream by ID.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
id: z.string().describe(AUDIT_LOG_STREAMS.UPDATE.id)
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
url: z.string().optional().describe(AUDIT_LOG_STREAMS.UPDATE.url),
|
||||||
|
headers: z
|
||||||
|
.object({
|
||||||
|
key: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.key),
|
||||||
|
value: z.string().min(1).trim().describe(AUDIT_LOG_STREAMS.UPDATE.headers.value)
|
||||||
|
})
|
||||||
|
.describe(AUDIT_LOG_STREAMS.UPDATE.headers.desc)
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
auditLogStream: SanitizedAuditLogStreamSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const auditLogStream = await server.services.auditLogStream.updateById({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
id: req.params.id,
|
||||||
|
url: req.body.url,
|
||||||
|
headers: req.body.headers
|
||||||
|
});
|
||||||
|
|
||||||
|
return { auditLogStream };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/:id",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Delete an Audit Log Stream by ID.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
id: z.string().describe(AUDIT_LOG_STREAMS.DELETE.id)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
auditLogStream: SanitizedAuditLogStreamSchema
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const auditLogStream = await server.services.auditLogStream.deleteById({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
id: req.params.id
|
||||||
|
});
|
||||||
|
|
||||||
|
return { auditLogStream };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:id",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "Get an Audit Log Stream by ID.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
params: z.object({
|
||||||
|
id: z.string().describe(AUDIT_LOG_STREAMS.GET_BY_ID.id)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
auditLogStream: SanitizedAuditLogStreamSchema.extend({
|
||||||
|
headers: z
|
||||||
|
.object({
|
||||||
|
key: z.string(),
|
||||||
|
value: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const auditLogStream = await server.services.auditLogStream.getById({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
id: req.params.id
|
||||||
|
});
|
||||||
|
|
||||||
|
return { auditLogStream };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
schema: {
|
||||||
|
description: "List Audit Log Streams.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
auditLogStreams: SanitizedAuditLogStreamSchema.array()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const auditLogStreams = await server.services.auditLogStream.list({
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod
|
||||||
|
});
|
||||||
|
|
||||||
|
return { auditLogStreams };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -5,14 +5,18 @@ import { DynamicSecretLeasesSchema } from "@app/db/schemas";
|
|||||||
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRET_LEASES } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => {
|
export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(),
|
dynamicSecretName: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.CREATE.dynamicSecretName).toLowerCase(),
|
||||||
@ -55,8 +59,11 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:leaseId",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:leaseId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId)
|
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.DELETE.leaseId)
|
||||||
@ -94,8 +101,11 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:leaseId/renew",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:leaseId/renew",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId)
|
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.leaseId)
|
||||||
@ -146,6 +156,9 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:leaseId",
|
url: "/:leaseId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId)
|
leaseId: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.GET_BY_LEASEID.leaseId)
|
||||||
|
@ -7,14 +7,18 @@ import { DynamicSecretProviderSchema } from "@app/ee/services/dynamic-secret/pro
|
|||||||
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
import { DYNAMIC_SECRETS } from "@app/lib/api-docs";
|
||||||
import { daysToMillisecond } from "@app/lib/dates";
|
import { daysToMillisecond } from "@app/lib/dates";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug),
|
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.CREATE.projectSlug),
|
||||||
@ -74,8 +78,11 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:name",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/:name",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name)
|
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.UPDATE.name)
|
||||||
@ -138,8 +145,11 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:name",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:name",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name)
|
name: z.string().toLowerCase().describe(DYNAMIC_SECRETS.DELETE.name)
|
||||||
@ -173,6 +183,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:name",
|
url: "/:name",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name)
|
name: z.string().min(1).describe(DYNAMIC_SECRETS.GET_BY_NAME.name)
|
||||||
@ -207,6 +220,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
url: "/",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug),
|
projectSlug: z.string().min(1).describe(DYNAMIC_SECRETS.LIST.projectSlug),
|
||||||
@ -235,6 +251,9 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:name/leases",
|
url: "/:name/leases",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name)
|
name: z.string().min(1).describe(DYNAMIC_SECRETS.LIST_LEAES_BY_NAME.name)
|
||||||
|
220
backend/src/ee/routes/v1/group-router.ts
Normal file
220
backend/src/ee/routes/v1/group-router.ts
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
import slugify from "@sindresorhus/slugify";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { GroupsSchema, OrgMembershipRole, UsersSchema } from "@app/db/schemas";
|
||||||
|
import { GROUPS } from "@app/lib/api-docs";
|
||||||
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
export const registerGroupRouter = async (server: FastifyZodProvider) => {
|
||||||
|
server.route({
|
||||||
|
url: "/",
|
||||||
|
method: "POST",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
name: z.string().trim().min(1).max(50).describe(GROUPS.CREATE.name),
|
||||||
|
slug: z
|
||||||
|
.string()
|
||||||
|
.min(5)
|
||||||
|
.max(36)
|
||||||
|
.refine((v) => slugify(v) === v, {
|
||||||
|
message: "Slug must be a valid slug"
|
||||||
|
})
|
||||||
|
.optional()
|
||||||
|
.describe(GROUPS.CREATE.slug),
|
||||||
|
role: z.string().trim().min(1).default(OrgMembershipRole.NoAccess).describe(GROUPS.CREATE.role)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: GroupsSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await server.services.group.createGroup({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:currentSlug",
|
||||||
|
method: "PATCH",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
currentSlug: z.string().trim().describe(GROUPS.UPDATE.currentSlug)
|
||||||
|
}),
|
||||||
|
body: z
|
||||||
|
.object({
|
||||||
|
name: z.string().trim().min(1).describe(GROUPS.UPDATE.name),
|
||||||
|
slug: z
|
||||||
|
.string()
|
||||||
|
.min(5)
|
||||||
|
.max(36)
|
||||||
|
.refine((v) => slugify(v) === v, {
|
||||||
|
message: "Slug must be a valid slug"
|
||||||
|
})
|
||||||
|
.describe(GROUPS.UPDATE.slug),
|
||||||
|
role: z.string().trim().min(1).describe(GROUPS.UPDATE.role)
|
||||||
|
})
|
||||||
|
.partial(),
|
||||||
|
response: {
|
||||||
|
200: GroupsSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await server.services.group.updateGroup({
|
||||||
|
currentSlug: req.params.currentSlug,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/:slug",
|
||||||
|
method: "DELETE",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
slug: z.string().trim().describe(GROUPS.DELETE.slug)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: GroupsSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await server.services.group.deleteGroup({
|
||||||
|
groupSlug: req.params.slug,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/:slug/users",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
slug: z.string().trim().describe(GROUPS.LIST_USERS.slug)
|
||||||
|
}),
|
||||||
|
querystring: z.object({
|
||||||
|
offset: z.coerce.number().min(0).max(100).default(0).describe(GROUPS.LIST_USERS.offset),
|
||||||
|
limit: z.coerce.number().min(1).max(100).default(10).describe(GROUPS.LIST_USERS.limit),
|
||||||
|
username: z.string().optional().describe(GROUPS.LIST_USERS.username)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
users: UsersSchema.pick({
|
||||||
|
email: true,
|
||||||
|
username: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
id: true
|
||||||
|
})
|
||||||
|
.merge(
|
||||||
|
z.object({
|
||||||
|
isPartOfGroup: z.boolean()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.array(),
|
||||||
|
totalCount: z.number()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const { users, totalCount } = await server.services.group.listGroupUsers({
|
||||||
|
groupSlug: req.params.slug,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
...req.query
|
||||||
|
});
|
||||||
|
return { users, totalCount };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/:slug/users/:username",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
slug: z.string().trim().describe(GROUPS.ADD_USER.slug),
|
||||||
|
username: z.string().trim().describe(GROUPS.ADD_USER.username)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: UsersSchema.pick({
|
||||||
|
email: true,
|
||||||
|
username: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
id: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const user = await server.services.group.addUserToGroup({
|
||||||
|
groupSlug: req.params.slug,
|
||||||
|
username: req.params.username,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/:slug/users/:username",
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
slug: z.string().trim().describe(GROUPS.DELETE_USER.slug),
|
||||||
|
username: z.string().trim().describe(GROUPS.DELETE_USER.username)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: UsersSchema.pick({
|
||||||
|
email: true,
|
||||||
|
username: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
id: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const user = await server.services.group.removeUserFromGroup({
|
||||||
|
groupSlug: req.params.slug,
|
||||||
|
username: req.params.username,
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -1,22 +1,30 @@
|
|||||||
import { MongoAbility, RawRuleOf } from "@casl/ability";
|
import { packRules } from "@casl/ability/extra";
|
||||||
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
|
|
||||||
import slugify from "@sindresorhus/slugify";
|
import slugify from "@sindresorhus/slugify";
|
||||||
import ms from "ms";
|
import ms from "ms";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { IdentityProjectAdditionalPrivilegeSchema } from "@app/db/schemas";
|
|
||||||
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
import { IdentityProjectAdditionalPrivilegeTemporaryMode } from "@app/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types";
|
||||||
import { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
|
|
||||||
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
|
import { ProjectPermissionSchema, SanitizedIdentityPrivilegeSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/permanent",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/permanent",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Create a permanent or a non expiry specific privilege for identity.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
||||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
||||||
@ -25,17 +33,17 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(60)
|
.max(60)
|
||||||
.trim()
|
.trim()
|
||||||
.default(slugify(alphaNumericNanoId(12)))
|
|
||||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
privilege: SanitizedIdentityPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -47,6 +55,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
...req.body,
|
...req.body,
|
||||||
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: false,
|
isTemporary: false,
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
@ -55,9 +64,18 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/temporary",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/temporary",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Create a temporary or a expiring specific privilege for identity.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.identityId),
|
||||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.projectSlug),
|
||||||
@ -66,13 +84,13 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(60)
|
.max(60)
|
||||||
.trim()
|
.trim()
|
||||||
.default(slugify(alphaNumericNanoId(12)))
|
|
||||||
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
.refine((val) => val.toLowerCase() === val, "Must be lowercase")
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
|
.optional()
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
|
||||||
@ -87,7 +105,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
privilege: SanitizedIdentityPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -99,6 +117,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
...req.body,
|
...req.body,
|
||||||
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: true,
|
isTemporary: true,
|
||||||
permissions: JSON.stringify(packRules(req.body.permissions))
|
permissions: JSON.stringify(packRules(req.body.permissions))
|
||||||
});
|
});
|
||||||
@ -107,9 +126,18 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Update a specific privilege of an identity.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
// disallow empty string
|
// disallow empty string
|
||||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.slug),
|
||||||
@ -127,7 +155,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.newSlug),
|
||||||
permissions: z.any().array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
permissions: ProjectPermissionSchema.array().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.permissions),
|
||||||
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
|
||||||
@ -145,7 +173,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
privilege: SanitizedIdentityPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -170,9 +198,18 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Delete a specific privilege of an identity.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
body: z.object({
|
body: z.object({
|
||||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.slug),
|
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.slug),
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.DELETE.identityId),
|
||||||
@ -180,7 +217,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
privilege: SanitizedIdentityPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -200,9 +237,18 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:privilegeSlug",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:privilegeSlug",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "Retrieve details of a specific privilege by privilege slug.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.slug)
|
privilegeSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.GET_BY_SLUG.slug)
|
||||||
}),
|
}),
|
||||||
@ -212,7 +258,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privilege: IdentityProjectAdditionalPrivilegeSchema
|
privilege: SanitizedIdentityPrivilegeSchema
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -231,21 +277,25 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
|
description: "List of a specific privilege of an identity in a project.",
|
||||||
|
security: [
|
||||||
|
{
|
||||||
|
bearerAuth: []
|
||||||
|
}
|
||||||
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
|
||||||
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug),
|
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug)
|
||||||
unpacked: z
|
|
||||||
.enum(["false", "true"])
|
|
||||||
.transform((el) => el === "true")
|
|
||||||
.default("true")
|
|
||||||
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.unpacked)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
|
privileges: SanitizedIdentityPrivilegeSchema.array()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -258,15 +308,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
...req.query
|
...req.query
|
||||||
});
|
});
|
||||||
if (req.query.unpacked) {
|
return {
|
||||||
return {
|
privileges
|
||||||
privileges: privileges.map(({ permissions, ...el }) => ({
|
};
|
||||||
...el,
|
|
||||||
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return { privileges };
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
|
import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router";
|
||||||
|
import { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
|
||||||
|
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
|
||||||
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
|
||||||
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
|
||||||
|
import { registerGroupRouter } from "./group-router";
|
||||||
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
|
||||||
import { registerLdapRouter } from "./ldap-router";
|
import { registerLdapRouter } from "./ldap-router";
|
||||||
import { registerLicenseRouter } from "./license-router";
|
import { registerLicenseRouter } from "./license-router";
|
||||||
@ -39,6 +43,9 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
prefix: "/secret-rotation-providers"
|
prefix: "/secret-rotation-providers"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await server.register(registerAccessApprovalPolicyRouter, { prefix: "/access-approvals/policies" });
|
||||||
|
await server.register(registerAccessApprovalRequestRouter, { prefix: "/access-approvals/requests" });
|
||||||
|
|
||||||
await server.register(
|
await server.register(
|
||||||
async (dynamicSecretRouter) => {
|
async (dynamicSecretRouter) => {
|
||||||
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
await dynamicSecretRouter.register(registerDynamicSecretRouter);
|
||||||
@ -53,6 +60,8 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
|
|||||||
await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" });
|
await server.register(registerSecretScanningRouter, { prefix: "/secret-scanning" });
|
||||||
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
await server.register(registerSecretRotationRouter, { prefix: "/secret-rotations" });
|
||||||
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
|
await server.register(registerSecretVersionRouter, { prefix: "/secret" });
|
||||||
|
await server.register(registerGroupRouter, { prefix: "/groups" });
|
||||||
|
await server.register(registerAuditLogStreamRouter, { prefix: "/audit-log-streams" });
|
||||||
await server.register(
|
await server.register(
|
||||||
async (privilegeRouter) => {
|
async (privilegeRouter) => {
|
||||||
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
await privilegeRouter.register(registerUserAdditionalPrivilegeRouter, { prefix: "/users" });
|
||||||
|
@ -14,9 +14,13 @@ import { FastifyRequest } from "fastify";
|
|||||||
import LdapStrategy from "passport-ldapauth";
|
import LdapStrategy from "passport-ldapauth";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { LdapConfigsSchema } from "@app/db/schemas";
|
import { LdapConfigsSchema, LdapGroupMapsSchema } from "@app/db/schemas";
|
||||||
|
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
|
||||||
|
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -49,20 +53,39 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
async (req: IncomingMessage, user, cb) => {
|
async (req: IncomingMessage, user, cb) => {
|
||||||
try {
|
try {
|
||||||
|
if (!user.email) throw new BadRequestError({ message: "Invalid request. Missing email." });
|
||||||
|
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
|
||||||
|
|
||||||
|
let groups: { dn: string; cn: string }[] | undefined;
|
||||||
|
if (ldapConfig.groupSearchBase) {
|
||||||
|
const groupFilter = "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))";
|
||||||
|
const groupSearchFilter = (ldapConfig.groupSearchFilter || groupFilter)
|
||||||
|
.replace(/{{\.Username}}/g, user.uid)
|
||||||
|
.replace(/{{\.UserDN}}/g, user.dn);
|
||||||
|
|
||||||
|
if (!isValidLdapFilter(groupSearchFilter)) {
|
||||||
|
throw new Error("Generated LDAP search filter is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
groups = await searchGroups(ldapConfig, groupSearchFilter, ldapConfig.groupSearchBase);
|
||||||
|
}
|
||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.ldap.ldapLogin({
|
||||||
|
ldapConfigId: ldapConfig.id,
|
||||||
externalId: user.uidNumber,
|
externalId: user.uidNumber,
|
||||||
username: user.uid,
|
username: user.uid,
|
||||||
firstName: user.givenName,
|
firstName: user.givenName ?? user.cn ?? "",
|
||||||
lastName: user.sn,
|
lastName: user.sn ?? "",
|
||||||
emails: user.mail ? [user.mail] : [],
|
email: user.mail,
|
||||||
|
groups,
|
||||||
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
|
||||||
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
orgId: (req as unknown as FastifyRequest).ldapConfig.organization
|
||||||
});
|
});
|
||||||
|
|
||||||
return cb(null, { isUserCompleted, providerAuthToken });
|
return cb(null, { isUserCompleted, providerAuthToken });
|
||||||
} catch (err) {
|
} catch (error) {
|
||||||
logger.error(err);
|
logger.error(error);
|
||||||
return cb(err, false);
|
return cb(error, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -97,8 +120,11 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
@ -113,6 +139,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
bindDN: z.string(),
|
bindDN: z.string(),
|
||||||
bindPass: z.string(),
|
bindPass: z.string(),
|
||||||
searchBase: z.string(),
|
searchBase: z.string(),
|
||||||
|
searchFilter: z.string(),
|
||||||
|
groupSearchBase: z.string(),
|
||||||
|
groupSearchFilter: z.string(),
|
||||||
caCert: z.string()
|
caCert: z.string()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -130,8 +159,11 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@ -141,6 +173,12 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
bindDN: z.string().trim(),
|
bindDN: z.string().trim(),
|
||||||
bindPass: z.string().trim(),
|
bindPass: z.string().trim(),
|
||||||
searchBase: z.string().trim(),
|
searchBase: z.string().trim(),
|
||||||
|
searchFilter: z.string().trim().default("(uid={{username}})"),
|
||||||
|
groupSearchBase: z.string().trim(),
|
||||||
|
groupSearchFilter: z
|
||||||
|
.string()
|
||||||
|
.trim()
|
||||||
|
.default("(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))"),
|
||||||
caCert: z.string().trim().default("")
|
caCert: z.string().trim().default("")
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
@ -164,6 +202,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
url: "/config",
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z
|
body: z
|
||||||
@ -173,6 +214,9 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
bindDN: z.string().trim(),
|
bindDN: z.string().trim(),
|
||||||
bindPass: z.string().trim(),
|
bindPass: z.string().trim(),
|
||||||
searchBase: z.string().trim(),
|
searchBase: z.string().trim(),
|
||||||
|
searchFilter: z.string().trim(),
|
||||||
|
groupSearchBase: z.string().trim(),
|
||||||
|
groupSearchFilter: z.string().trim(),
|
||||||
caCert: z.string().trim()
|
caCert: z.string().trim()
|
||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
@ -194,4 +238,134 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
|
|||||||
return ldap;
|
return ldap;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "GET",
|
||||||
|
url: "/config/:configId/group-maps",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
configId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.array(
|
||||||
|
z.object({
|
||||||
|
id: z.string(),
|
||||||
|
ldapConfigId: z.string(),
|
||||||
|
ldapGroupCN: z.string(),
|
||||||
|
group: z.object({
|
||||||
|
id: z.string(),
|
||||||
|
name: z.string(),
|
||||||
|
slug: z.string()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const ldapGroupMaps = await server.services.ldap.getLdapGroupMaps({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
ldapConfigId: req.params.configId
|
||||||
|
});
|
||||||
|
return ldapGroupMaps;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/config/:configId/group-maps",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
configId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
ldapGroupCN: z.string().trim(),
|
||||||
|
groupSlug: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: LdapGroupMapsSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const ldapGroupMap = await server.services.ldap.createLdapGroupMap({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
ldapConfigId: req.params.configId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
return ldapGroupMap;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "DELETE",
|
||||||
|
url: "/config/:configId/group-maps/:groupMapId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
configId: z.string().trim(),
|
||||||
|
groupMapId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: LdapGroupMapsSchema
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const ldapGroupMap = await server.services.ldap.deleteLdapGroupMap({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
ldapConfigId: req.params.configId,
|
||||||
|
ldapGroupMapId: req.params.groupMapId
|
||||||
|
});
|
||||||
|
return ldapGroupMap;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
method: "POST",
|
||||||
|
url: "/config/:configId/test-connection",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
configId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.boolean()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handler: async (req) => {
|
||||||
|
const result = await server.services.ldap.testLDAPConnection({
|
||||||
|
actor: req.permission.type,
|
||||||
|
actorId: req.permission.id,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
actorAuthMethod: req.permission.authMethod,
|
||||||
|
actorOrgId: req.permission.orgId,
|
||||||
|
ldapConfigId: req.params.configId
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
@ -3,13 +3,17 @@
|
|||||||
// TODO(akhilmhdh): Fix this when licence service gets it type
|
// TODO(akhilmhdh): Fix this when licence service gets it type
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/plans/table",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/plans/table",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({ billingCycle: z.enum(["monthly", "yearly"]) }),
|
querystring: z.object({ billingCycle: z.enum(["monthly", "yearly"]) }),
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
@ -32,8 +36,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/plan",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/plan",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -54,8 +61,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/plans",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/plans",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
querystring: z.object({ workspaceId: z.string().trim().optional() }),
|
querystring: z.object({ workspaceId: z.string().trim().optional() }),
|
||||||
@ -77,8 +87,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/session/trial",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:organizationId/session/trial",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
body: z.object({ success_url: z.string().trim() }),
|
body: z.object({ success_url: z.string().trim() }),
|
||||||
@ -103,6 +116,9 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/customer-portal-session",
|
url: "/:organizationId/customer-portal-session",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -123,8 +139,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/plan/billing",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/plan/billing",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -145,8 +164,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/plan/table",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/plan/table",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -167,8 +189,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/billing-details",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -189,8 +214,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/:organizationId/billing-details",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@ -217,8 +245,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/payment-methods",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/billing-details/payment-methods",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -239,8 +270,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/payment-methods",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:organizationId/billing-details/payment-methods",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@ -267,8 +301,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim(),
|
organizationId: z.string().trim(),
|
||||||
@ -293,8 +330,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/tax-ids",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/billing-details/tax-ids",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@ -317,8 +357,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/tax-ids",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:organizationId/billing-details/tax-ids",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@ -347,8 +390,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/billing-details/tax-ids/:taxId",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:organizationId/billing-details/tax-ids/:taxId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim(),
|
organizationId: z.string().trim(),
|
||||||
@ -373,8 +419,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/invoices",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/invoices",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@ -397,8 +446,11 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:organizationId/licenses",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:organizationId/licenses",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
|
@ -2,6 +2,7 @@ import slugify from "@sindresorhus/slugify";
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
import { OrgMembershipRole, OrgMembershipsSchema, OrgRolesSchema } from "@app/db/schemas";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -9,6 +10,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:organizationId/roles",
|
url: "/:organizationId/roles",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@ -51,6 +55,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:organizationId/roles/:roleId",
|
url: "/:organizationId/roles/:roleId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim(),
|
organizationId: z.string().trim(),
|
||||||
@ -95,6 +102,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:organizationId/roles/:roleId",
|
url: "/:organizationId/roles/:roleId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim(),
|
organizationId: z.string().trim(),
|
||||||
@ -122,6 +132,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:organizationId/roles",
|
url: "/:organizationId/roles",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
@ -151,6 +164,9 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:organizationId/permissions",
|
url: "/:organizationId/permissions",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
organizationId: z.string().trim()
|
organizationId: z.string().trim()
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -8,6 +9,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:projectId/roles",
|
url: "/:projectId/roles",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim()
|
projectId: z.string().trim()
|
||||||
@ -41,6 +45,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
url: "/:projectId/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim(),
|
projectId: z.string().trim(),
|
||||||
@ -76,6 +83,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: "/:projectId/roles/:roleId",
|
url: "/:projectId/roles/:roleId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim(),
|
projectId: z.string().trim(),
|
||||||
@ -104,6 +114,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:projectId/roles",
|
url: "/:projectId/roles",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim()
|
projectId: z.string().trim()
|
||||||
@ -134,6 +147,9 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:projectId/permissions",
|
url: "/:projectId/permissions",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
projectId: z.string().trim()
|
projectId: z.string().trim()
|
||||||
@ -141,7 +157,13 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
response: {
|
response: {
|
||||||
200: z.object({
|
200: z.object({
|
||||||
data: z.object({
|
data: z.object({
|
||||||
membership: ProjectMembershipsSchema,
|
membership: ProjectMembershipsSchema.extend({
|
||||||
|
roles: z
|
||||||
|
.object({
|
||||||
|
role: z.string()
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
}),
|
||||||
permissions: z.any().array()
|
permissions: z.any().array()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -155,6 +177,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
|
|||||||
req.permission.authMethod,
|
req.permission.authMethod,
|
||||||
req.permission.orgId
|
req.permission.orgId
|
||||||
);
|
);
|
||||||
|
|
||||||
return { data: { permissions, membership } };
|
return { data: { permissions, membership } };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,8 @@ import { z } from "zod";
|
|||||||
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
|
||||||
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
|
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { getLastMidnightDateISO, removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -11,11 +12,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/secret-snapshots",
|
url: "/:workspaceId/secret-snapshots",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Return project secret snapshots ids",
|
description: "Return project secret snapshots ids",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
apiKeyAuth: [],
|
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -51,6 +54,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/secret-snapshots/count",
|
url: "/:workspaceId/secret-snapshots/count",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -83,12 +89,14 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/audit-logs",
|
url: "/:workspaceId/audit-logs",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Return audit logs",
|
description: "Return audit logs",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
bearerAuth: [],
|
bearerAuth: []
|
||||||
apiKeyAuth: []
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
params: z.object({
|
params: z.object({
|
||||||
@ -135,6 +143,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
projectId: req.params.workspaceId,
|
projectId: req.params.workspaceId,
|
||||||
...req.query,
|
...req.query,
|
||||||
|
startDate: req.query.endDate || getLastMidnightDateISO(),
|
||||||
auditLogActor: req.query.actor,
|
auditLogActor: req.query.actor,
|
||||||
actor: req.permission.type
|
actor: req.permission.type
|
||||||
});
|
});
|
||||||
@ -145,6 +154,9 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:workspaceId/audit-logs/filters/actors",
|
url: "/:workspaceId/audit-logs/filters/actors",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
|
@ -17,6 +17,7 @@ import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml
|
|||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { BadRequestError } from "@app/lib/errors";
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
import { logger } from "@app/lib/logger";
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -101,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
if (!profile) throw new BadRequestError({ message: "Missing profile" });
|
||||||
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
|
||||||
|
|
||||||
if (!profile.email || !profile.firstName) {
|
if (!email || !profile.firstName) {
|
||||||
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
|
||||||
username: profile.nameID ?? email,
|
externalId: profile.nameID,
|
||||||
email,
|
email,
|
||||||
firstName: profile.firstName as string,
|
firstName: profile.firstName as string,
|
||||||
lastName: profile.lastName as string,
|
lastName: profile.lastName as string,
|
||||||
@ -203,8 +204,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
@ -240,8 +244,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@ -270,8 +277,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/config",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/config",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z
|
body: z
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { ScimTokensSchema } from "@app/db/schemas";
|
import { ScimTokensSchema } from "@app/db/schemas";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -20,6 +21,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/scim-tokens",
|
url: "/scim-tokens",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
@ -51,6 +55,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/scim-tokens",
|
url: "/scim-tokens",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
@ -78,6 +85,9 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/scim-tokens/:scimTokenId",
|
url: "/scim-tokens/:scimTokenId",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
onRequest: verifyAuth([AuthMode.JWT]),
|
onRequest: verifyAuth([AuthMode.JWT]),
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
@ -143,7 +153,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const users = await req.server.services.scim.listScimUsers({
|
const users = await req.server.services.scim.listScimUsers({
|
||||||
offset: req.query.startIndex,
|
startIndex: req.query.startIndex,
|
||||||
limit: req.query.count,
|
limit: req.query.count,
|
||||||
filter: req.query.filter,
|
filter: req.query.filter,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
@ -153,11 +163,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:userId",
|
url: "/Users/:orgMembershipId",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
userId: z.string().trim()
|
orgMembershipId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
201: z.object({
|
201: z.object({
|
||||||
@ -183,7 +193,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.getScimUser({
|
const user = await req.server.services.scim.getScimUser({
|
||||||
userId: req.params.userId,
|
orgMembershipId: req.params.orgMembershipId,
|
||||||
orgId: req.permission.orgId
|
orgId: req.permission.orgId
|
||||||
});
|
});
|
||||||
return user;
|
return user;
|
||||||
@ -196,7 +206,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
userName: z.string().trim().email(),
|
userName: z.string().trim(),
|
||||||
name: z.object({
|
name: z.object({
|
||||||
familyName: z.string().trim(),
|
familyName: z.string().trim(),
|
||||||
givenName: z.string().trim()
|
givenName: z.string().trim()
|
||||||
@ -217,7 +227,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
200: z.object({
|
200: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
id: z.string().trim(),
|
id: z.string().trim(),
|
||||||
userName: z.string().trim().email(),
|
userName: z.string().trim(),
|
||||||
name: z.object({
|
name: z.object({
|
||||||
familyName: z.string().trim(),
|
familyName: z.string().trim(),
|
||||||
givenName: z.string().trim()
|
givenName: z.string().trim()
|
||||||
@ -239,7 +249,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
|
||||||
|
|
||||||
const user = await req.server.services.scim.createScimUser({
|
const user = await req.server.services.scim.createScimUser({
|
||||||
username: req.body.userName,
|
externalId: req.body.userName,
|
||||||
email: primaryEmail,
|
email: primaryEmail,
|
||||||
firstName: req.body.name.givenName,
|
firstName: req.body.name.givenName,
|
||||||
lastName: req.body.name.familyName,
|
lastName: req.body.name.familyName,
|
||||||
@ -251,26 +261,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:userId",
|
url: "/Users/:orgMembershipId",
|
||||||
method: "PATCH",
|
method: "DELETE",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
userId: z.string().trim()
|
orgMembershipId: z.string().trim()
|
||||||
}),
|
|
||||||
body: z.object({
|
|
||||||
schemas: z.array(z.string()),
|
|
||||||
Operations: z.array(
|
|
||||||
z.object({
|
|
||||||
op: z.string().trim(),
|
|
||||||
path: z.string().trim().optional(),
|
|
||||||
value: z.union([
|
|
||||||
z.object({
|
|
||||||
active: z.boolean()
|
|
||||||
}),
|
|
||||||
z.string().trim()
|
|
||||||
])
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}),
|
}),
|
||||||
response: {
|
response: {
|
||||||
200: z.object({})
|
200: z.object({})
|
||||||
@ -278,21 +273,272 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
},
|
},
|
||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.updateScimUser({
|
const user = await req.server.services.scim.deleteScimUser({
|
||||||
userId: req.params.userId,
|
orgMembershipId: req.params.orgMembershipId,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId
|
||||||
operations: req.body.Operations
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/Users/:userId",
|
url: "/Groups",
|
||||||
|
method: "POST",
|
||||||
|
schema: {
|
||||||
|
body: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional() // okta-specific
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z
|
||||||
|
.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional(),
|
||||||
|
meta: z.object({
|
||||||
|
resourceType: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await req.server.services.scim.createScimGroup({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Groups",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
querystring: z.object({
|
||||||
|
startIndex: z.coerce.number().default(1),
|
||||||
|
count: z.coerce.number().default(20),
|
||||||
|
filter: z.string().trim().optional()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
Resources: z.array(
|
||||||
|
z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z.array(z.any()).length(0),
|
||||||
|
meta: z.object({
|
||||||
|
resourceType: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
),
|
||||||
|
itemsPerPage: z.number(),
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
startIndex: z.number(),
|
||||||
|
totalResults: z.number()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const groups = await req.server.services.scim.listScimGroups({
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
startIndex: req.query.startIndex,
|
||||||
|
limit: req.query.count
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Groups/:groupId",
|
||||||
|
method: "GET",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
groupId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
meta: z.object({
|
||||||
|
resourceType: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await req.server.services.scim.getScimGroup({
|
||||||
|
groupId: req.params.groupId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Groups/:groupId",
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
userId: z.string().trim()
|
groupId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(), // infisical orgMembershipId
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
meta: z.object({
|
||||||
|
resourceType: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await req.server.services.scim.updateScimGroupNamePut({
|
||||||
|
groupId: req.params.groupId,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
...req.body
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Groups/:groupId",
|
||||||
|
method: "PATCH",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
groupId: z.string().trim()
|
||||||
|
}),
|
||||||
|
body: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
Operations: z.array(
|
||||||
|
z.union([
|
||||||
|
z.object({
|
||||||
|
op: z.literal("replace"),
|
||||||
|
value: z.object({
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim()
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
op: z.literal("remove"),
|
||||||
|
path: z.string().trim()
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
op: z.literal("add"),
|
||||||
|
value: z.object({
|
||||||
|
value: z.string().trim(),
|
||||||
|
display: z.string().trim().optional()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({
|
||||||
|
schemas: z.array(z.string()),
|
||||||
|
id: z.string().trim(),
|
||||||
|
displayName: z.string().trim(),
|
||||||
|
members: z.array(
|
||||||
|
z.object({
|
||||||
|
value: z.string(),
|
||||||
|
display: z.string()
|
||||||
|
})
|
||||||
|
),
|
||||||
|
meta: z.object({
|
||||||
|
resourceType: z.string().trim()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await req.server.services.scim.updateScimGroupNamePatch({
|
||||||
|
groupId: req.params.groupId,
|
||||||
|
orgId: req.permission.orgId,
|
||||||
|
operations: req.body.Operations
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Groups/:groupId",
|
||||||
|
method: "DELETE",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
groupId: z.string().trim()
|
||||||
|
}),
|
||||||
|
response: {
|
||||||
|
200: z.object({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
|
handler: async (req) => {
|
||||||
|
const group = await req.server.services.scim.deleteScimGroup({
|
||||||
|
groupId: req.params.groupId,
|
||||||
|
orgId: req.permission.orgId
|
||||||
|
});
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
server.route({
|
||||||
|
url: "/Users/:orgMembershipId",
|
||||||
|
method: "PUT",
|
||||||
|
schema: {
|
||||||
|
params: z.object({
|
||||||
|
orgMembershipId: z.string().trim()
|
||||||
}),
|
}),
|
||||||
body: z.object({
|
body: z.object({
|
||||||
schemas: z.array(z.string()),
|
schemas: z.array(z.string()),
|
||||||
@ -329,7 +575,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
|
|||||||
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
|
||||||
handler: async (req) => {
|
handler: async (req) => {
|
||||||
const user = await req.server.services.scim.replaceScimUser({
|
const user = await req.server.services.scim.replaceScimUser({
|
||||||
userId: req.params.userId,
|
orgMembershipId: req.params.orgMembershipId,
|
||||||
orgId: req.permission.orgId,
|
orgId: req.permission.orgId,
|
||||||
active: req.body.active
|
active: req.body.active
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
@ -9,6 +10,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
url: "/",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z
|
body: z
|
||||||
.object({
|
.object({
|
||||||
@ -47,6 +51,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:sapId",
|
url: "/:sapId",
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
sapId: z.string()
|
sapId: z.string()
|
||||||
@ -85,6 +92,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:sapId",
|
url: "/:sapId",
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
sapId: z.string()
|
sapId: z.string()
|
||||||
@ -111,6 +121,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
url: "/",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -137,6 +150,9 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/board",
|
url: "/board",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim(),
|
workspaceId: z.string().trim(),
|
||||||
|
@ -10,13 +10,17 @@ import {
|
|||||||
} from "@app/db/schemas";
|
} from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretApprovalRequestRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim(),
|
workspaceId: z.string().trim(),
|
||||||
@ -62,8 +66,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/count",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/count",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -93,6 +100,9 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/:id/merge",
|
url: "/:id/merge",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.string()
|
id: z.string()
|
||||||
@ -117,8 +127,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:id/review",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:id/review",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.string()
|
id: z.string()
|
||||||
@ -147,8 +160,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:id/status",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:id/status",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.string()
|
id: z.string()
|
||||||
@ -203,8 +219,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
|
|||||||
.array()
|
.array()
|
||||||
.optional();
|
.optional();
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:id",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:id",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.string()
|
id: z.string()
|
||||||
|
@ -1,12 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSecretRotationProviderRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretRotationProviderRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:workspaceId",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:workspaceId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
|
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
|
||||||
import { removeTrailingSlash } from "@app/lib/fn";
|
import { removeTrailingSlash } from "@app/lib/fn";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSecretRotationRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretRotationRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
workspaceId: z.string().trim(),
|
workspaceId: z.string().trim(),
|
||||||
@ -52,6 +56,9 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/restart",
|
url: "/restart",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
id: z.string().trim()
|
id: z.string().trim()
|
||||||
@ -86,6 +93,9 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
url: "/",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -136,8 +146,11 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:id",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:id",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
id: z.string().trim()
|
id: z.string().trim()
|
||||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
import { GitAppOrgSchema, SecretScanningGitRisksSchema } from "@app/db/schemas";
|
||||||
import { SecretScanningRiskStatus } from "@app/ee/services/secret-scanning/secret-scanning-types";
|
import { SecretScanningRiskStatus } from "@app/ee/services/secret-scanning/secret-scanning-types";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSecretScanningRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretScanningRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/create-installation-session/organization",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/create-installation-session/organization",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({ organizationId: z.string().trim() }),
|
body: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -31,8 +35,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/link-installation",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/link-installation",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
installationId: z.string(),
|
installationId: z.string(),
|
||||||
@ -56,8 +63,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/installation-status/organization/:organizationId",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/installation-status/organization/:organizationId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -80,6 +90,9 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/organization/:organizationId/risks",
|
url: "/organization/:organizationId/risks",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim() }),
|
||||||
response: {
|
response: {
|
||||||
@ -100,8 +113,11 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/organization/:organizationId/risks/:riskId/status",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/organization/:organizationId/risks/:riskId/status",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({ organizationId: z.string().trim(), riskId: z.string().trim() }),
|
params: z.object({ organizationId: z.string().trim(), riskId: z.string().trim() }),
|
||||||
body: z.object({ status: z.nativeEnum(SecretScanningRiskStatus) }),
|
body: z.object({ status: z.nativeEnum(SecretScanningRiskStatus) }),
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
import { SecretVersionsSchema } from "@app/db/schemas";
|
import { SecretVersionsSchema } from "@app/db/schemas";
|
||||||
|
import { readLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerSecretVersionRouter = async (server: FastifyZodProvider) => {
|
export const registerSecretVersionRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:secretId/secret-versions",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:secretId/secret-versions",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretId: z.string()
|
secretId: z.string()
|
||||||
|
@ -2,6 +2,7 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
|
||||||
import { PROJECTS } from "@app/lib/api-docs";
|
import { PROJECTS } from "@app/lib/api-docs";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -9,6 +10,9 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: "/:secretSnapshotId",
|
url: "/:secretSnapshotId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
secretSnapshotId: z.string().trim()
|
secretSnapshotId: z.string().trim()
|
||||||
@ -58,11 +62,13 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
|
|||||||
server.route({
|
server.route({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: "/:secretSnapshotId/rollback",
|
url: "/:secretSnapshotId/rollback",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
description: "Roll back project secrets to those captured in a secret snapshot version.",
|
description: "Roll back project secrets to those captured in a secret snapshot version.",
|
||||||
security: [
|
security: [
|
||||||
{
|
{
|
||||||
apiKeyAuth: [],
|
|
||||||
bearerAuth: []
|
bearerAuth: []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -2,13 +2,17 @@ import { z } from "zod";
|
|||||||
|
|
||||||
import { TrustedIpsSchema } from "@app/db/schemas";
|
import { TrustedIpsSchema } from "@app/db/schemas";
|
||||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:workspaceId/trusted-ips",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:workspaceId/trusted-ips",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -33,8 +37,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:workspaceId/trusted-ips",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/:workspaceId/trusted-ips",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim()
|
workspaceId: z.string().trim()
|
||||||
@ -78,8 +85,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim(),
|
workspaceId: z.string().trim(),
|
||||||
@ -124,8 +134,11 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:workspaceId/trusted-ips/:trustedIpId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
workspaceId: z.string().trim(),
|
workspaceId: z.string().trim(),
|
||||||
|
@ -6,6 +6,7 @@ import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas";
|
|||||||
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types";
|
||||||
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
import { PROJECT_USER_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||||
import { AuthMode } from "@app/services/auth/auth-type";
|
import { AuthMode } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
@ -13,6 +14,9 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
server.route({
|
server.route({
|
||||||
url: "/permanent",
|
url: "/permanent",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
||||||
@ -21,11 +25,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(60)
|
.max(60)
|
||||||
.trim()
|
.trim()
|
||||||
.default(slugify(alphaNumericNanoId(12)))
|
|
||||||
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
|
.optional()
|
||||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions)
|
||||||
}),
|
}),
|
||||||
@ -43,6 +47,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
...req.body,
|
...req.body,
|
||||||
|
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
|
||||||
isTemporary: false,
|
isTemporary: false,
|
||||||
permissions: JSON.stringify(req.body.permissions)
|
permissions: JSON.stringify(req.body.permissions)
|
||||||
});
|
});
|
||||||
@ -51,8 +56,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/temporary",
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
url: "/temporary",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
body: z.object({
|
body: z.object({
|
||||||
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
projectMembershipId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.projectMembershipId),
|
||||||
@ -61,11 +69,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
.min(1)
|
.min(1)
|
||||||
.max(60)
|
.max(60)
|
||||||
.trim()
|
.trim()
|
||||||
.default(`privilege-${slugify(alphaNumericNanoId(12))}`)
|
|
||||||
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
.refine((v) => v.toLowerCase() === v, "Slug must be lowercase")
|
||||||
.refine((v) => slugify(v) === v, {
|
.refine((v) => slugify(v) === v, {
|
||||||
message: "Slug must be a valid slug"
|
message: "Slug must be a valid slug"
|
||||||
})
|
})
|
||||||
|
.optional()
|
||||||
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
.describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.slug),
|
||||||
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
permissions: z.any().array().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.CREATE.permissions),
|
||||||
temporaryMode: z
|
temporaryMode: z
|
||||||
@ -94,6 +102,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
actorOrgId: req.permission.orgId,
|
actorOrgId: req.permission.orgId,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
...req.body,
|
...req.body,
|
||||||
|
slug: req.body.slug ? slugify(req.body.slug) : `privilege-${slugify(alphaNumericNanoId(12))}`,
|
||||||
isTemporary: true,
|
isTemporary: true,
|
||||||
permissions: JSON.stringify(req.body.permissions)
|
permissions: JSON.stringify(req.body.permissions)
|
||||||
});
|
});
|
||||||
@ -102,8 +111,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:privilegeId",
|
|
||||||
method: "PATCH",
|
method: "PATCH",
|
||||||
|
url: "/:privilegeId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
privilegeId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.privilegeId)
|
privilegeId: z.string().min(1).describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.UPDATE.privilegeId)
|
||||||
@ -156,8 +168,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:privilegeId",
|
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
|
url: "/:privilegeId",
|
||||||
|
config: {
|
||||||
|
rateLimit: writeLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.DELETE.privilegeId)
|
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.DELETE.privilegeId)
|
||||||
@ -182,8 +197,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
projectMembershipId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.LIST.projectMembershipId)
|
projectMembershipId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.LIST.projectMembershipId)
|
||||||
@ -208,8 +226,11 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr
|
|||||||
});
|
});
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
url: "/:privilegeId",
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
url: "/:privilegeId",
|
||||||
|
config: {
|
||||||
|
rateLimit: readLimit
|
||||||
|
},
|
||||||
schema: {
|
schema: {
|
||||||
params: z.object({
|
params: z.object({
|
||||||
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.GET_BY_PRIVILEGEID.privilegeId)
|
privilegeId: z.string().describe(PROJECT_USER_ADDITIONAL_PRIVILEGE.GET_BY_PRIVILEGEID.privilegeId)
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName } from "@app/db/schemas";
|
||||||
|
import { ormify } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TAccessApprovalPolicyApproverDALFactory = ReturnType<typeof accessApprovalPolicyApproverDALFactory>;
|
||||||
|
|
||||||
|
export const accessApprovalPolicyApproverDALFactory = (db: TDbClient) => {
|
||||||
|
const accessApprovalPolicyApproverOrm = ormify(db, TableName.AccessApprovalPolicyApprover);
|
||||||
|
return { ...accessApprovalPolicyApproverOrm };
|
||||||
|
};
|
@ -0,0 +1,76 @@
|
|||||||
|
import { Knex } from "knex";
|
||||||
|
|
||||||
|
import { TDbClient } from "@app/db";
|
||||||
|
import { TableName, TAccessApprovalPolicies } from "@app/db/schemas";
|
||||||
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
|
import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex";
|
||||||
|
|
||||||
|
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
|
||||||
|
|
||||||
|
export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
|
||||||
|
const accessApprovalPolicyOrm = ormify(db, TableName.AccessApprovalPolicy);
|
||||||
|
|
||||||
|
const accessApprovalPolicyFindQuery = async (tx: Knex, filter: TFindFilter<TAccessApprovalPolicies>) => {
|
||||||
|
const result = await tx(TableName.AccessApprovalPolicy)
|
||||||
|
// eslint-disable-next-line
|
||||||
|
.where(buildFindFilter(filter))
|
||||||
|
.join(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
|
||||||
|
.join(
|
||||||
|
TableName.AccessApprovalPolicyApprover,
|
||||||
|
`${TableName.AccessApprovalPolicy}.id`,
|
||||||
|
`${TableName.AccessApprovalPolicyApprover}.policyId`
|
||||||
|
)
|
||||||
|
.select(tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
|
||||||
|
.select(tx.ref("name").withSchema(TableName.Environment).as("envName"))
|
||||||
|
.select(tx.ref("slug").withSchema(TableName.Environment).as("envSlug"))
|
||||||
|
.select(tx.ref("id").withSchema(TableName.Environment).as("envId"))
|
||||||
|
.select(tx.ref("projectId").withSchema(TableName.Environment))
|
||||||
|
.select(selectAllTableCols(TableName.AccessApprovalPolicy));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const findById = async (id: string, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const doc = await accessApprovalPolicyFindQuery(tx || db, {
|
||||||
|
[`${TableName.AccessApprovalPolicy}.id` as "id"]: id
|
||||||
|
});
|
||||||
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
|
doc,
|
||||||
|
"id",
|
||||||
|
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
envId,
|
||||||
|
environment: { id: envId, name, slug }
|
||||||
|
}),
|
||||||
|
({ approverId }) => approverId,
|
||||||
|
"approvers"
|
||||||
|
);
|
||||||
|
return formatedDoc?.[0];
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "FindById" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const find = async (filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>, tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = await accessApprovalPolicyFindQuery(tx || db, filter);
|
||||||
|
const formatedDoc = mergeOneToManyRelation(
|
||||||
|
docs,
|
||||||
|
"id",
|
||||||
|
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
|
||||||
|
...el,
|
||||||
|
envId,
|
||||||
|
environment: { id: envId, name, slug }
|
||||||
|
}),
|
||||||
|
({ approverId }) => approverId,
|
||||||
|
"approvers"
|
||||||
|
);
|
||||||
|
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "Find" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...accessApprovalPolicyOrm, find, findById };
|
||||||
|
};
|
@ -0,0 +1,36 @@
|
|||||||
|
import { ForbiddenError, subject } from "@casl/ability";
|
||||||
|
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
import { ActorType } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
|
||||||
|
import { TVerifyApprovers } from "./access-approval-policy-types";
|
||||||
|
|
||||||
|
export const verifyApprovers = async ({
|
||||||
|
userIds,
|
||||||
|
projectId,
|
||||||
|
orgId,
|
||||||
|
envSlug,
|
||||||
|
actorAuthMethod,
|
||||||
|
secretPath,
|
||||||
|
permissionService
|
||||||
|
}: TVerifyApprovers) => {
|
||||||
|
for await (const userId of userIds) {
|
||||||
|
try {
|
||||||
|
const { permission: approverPermission } = await permissionService.getProjectPermission(
|
||||||
|
ActorType.USER,
|
||||||
|
userId,
|
||||||
|
projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
orgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(approverPermission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
subject(ProjectPermissionSub.Secrets, { environment: envSlug, secretPath })
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
throw new BadRequestError({ message: "One or more approvers doesn't have access to be specified secret path" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,273 @@
|
|||||||
|
import { ForbiddenError } from "@casl/ability";
|
||||||
|
|
||||||
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
|
||||||
|
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||||
|
import { BadRequestError } from "@app/lib/errors";
|
||||||
|
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 { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
|
||||||
|
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
|
||||||
|
import { verifyApprovers } from "./access-approval-policy-fns";
|
||||||
|
import {
|
||||||
|
TCreateAccessApprovalPolicy,
|
||||||
|
TDeleteAccessApprovalPolicy,
|
||||||
|
TGetAccessPolicyCountByEnvironmentDTO,
|
||||||
|
TListAccessApprovalPoliciesDTO,
|
||||||
|
TUpdateAccessApprovalPolicy
|
||||||
|
} from "./access-approval-policy-types";
|
||||||
|
|
||||||
|
type TSecretApprovalPolicyServiceFactoryDep = {
|
||||||
|
projectDAL: TProjectDALFactory;
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
|
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
|
||||||
|
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
|
||||||
|
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
|
||||||
|
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
|
||||||
|
|
||||||
|
export const accessApprovalPolicyServiceFactory = ({
|
||||||
|
accessApprovalPolicyDAL,
|
||||||
|
accessApprovalPolicyApproverDAL,
|
||||||
|
permissionService,
|
||||||
|
projectEnvDAL,
|
||||||
|
projectDAL,
|
||||||
|
projectMembershipDAL
|
||||||
|
}: TSecretApprovalPolicyServiceFactoryDep) => {
|
||||||
|
const createAccessApprovalPolicy = async ({
|
||||||
|
name,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorOrgId,
|
||||||
|
secretPath,
|
||||||
|
actorAuthMethod,
|
||||||
|
approvals,
|
||||||
|
approvers,
|
||||||
|
projectSlug,
|
||||||
|
environment
|
||||||
|
}: TCreateAccessApprovalPolicy) => {
|
||||||
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
|
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||||
|
|
||||||
|
if (approvals > approvers.length)
|
||||||
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Create,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
|
||||||
|
if (!env) throw new BadRequestError({ message: "Environment not found" });
|
||||||
|
|
||||||
|
const secretApprovers = await projectMembershipDAL.find({
|
||||||
|
projectId: project.id,
|
||||||
|
$in: { id: approvers }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (secretApprovers.length !== approvers.length) {
|
||||||
|
throw new BadRequestError({ message: "Approver not found in project" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await verifyApprovers({
|
||||||
|
projectId: project.id,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
envSlug: environment,
|
||||||
|
secretPath,
|
||||||
|
actorAuthMethod,
|
||||||
|
permissionService,
|
||||||
|
userIds: secretApprovers.map((approver) => approver.userId)
|
||||||
|
});
|
||||||
|
|
||||||
|
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
|
const doc = await accessApprovalPolicyDAL.create(
|
||||||
|
{
|
||||||
|
envId: env.id,
|
||||||
|
approvals,
|
||||||
|
secretPath,
|
||||||
|
name
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
|
secretApprovers.map(({ id }) => ({
|
||||||
|
approverId: id,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
return doc;
|
||||||
|
});
|
||||||
|
return { ...accessApproval, environment: env, projectId: project.id };
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccessApprovalPolicyByProjectSlug = async ({
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
projectSlug
|
||||||
|
}: TListAccessApprovalPoliciesDTO) => {
|
||||||
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
|
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||||
|
|
||||||
|
// Anyone in the project should be able to get the policies.
|
||||||
|
/* const { permission } = */ await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
// ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
|
const accessApprovalPolicies = await accessApprovalPolicyDAL.find({ projectId: project.id });
|
||||||
|
return accessApprovalPolicies;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateAccessApprovalPolicy = async ({
|
||||||
|
policyId,
|
||||||
|
approvers,
|
||||||
|
secretPath,
|
||||||
|
name,
|
||||||
|
actorId,
|
||||||
|
actor,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
approvals
|
||||||
|
}: TUpdateAccessApprovalPolicy) => {
|
||||||
|
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
|
||||||
|
if (!accessApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
accessApprovalPolicy.projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
|
||||||
|
|
||||||
|
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
|
||||||
|
const doc = await accessApprovalPolicyDAL.updateById(
|
||||||
|
accessApprovalPolicy.id,
|
||||||
|
{
|
||||||
|
approvals,
|
||||||
|
secretPath,
|
||||||
|
name
|
||||||
|
},
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
if (approvers) {
|
||||||
|
// Find the workspace project memberships of the users passed in the approvers array
|
||||||
|
const secretApprovers = await projectMembershipDAL.find(
|
||||||
|
{
|
||||||
|
projectId: accessApprovalPolicy.projectId,
|
||||||
|
$in: { id: approvers }
|
||||||
|
},
|
||||||
|
{ tx }
|
||||||
|
);
|
||||||
|
|
||||||
|
await verifyApprovers({
|
||||||
|
projectId: accessApprovalPolicy.projectId,
|
||||||
|
orgId: actorOrgId,
|
||||||
|
envSlug: accessApprovalPolicy.environment.slug,
|
||||||
|
secretPath: doc.secretPath!,
|
||||||
|
actorAuthMethod,
|
||||||
|
permissionService,
|
||||||
|
userIds: secretApprovers.map((approver) => approver.userId)
|
||||||
|
});
|
||||||
|
|
||||||
|
if (secretApprovers.length !== approvers.length)
|
||||||
|
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
|
||||||
|
await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
|
||||||
|
await accessApprovalPolicyApproverDAL.insertMany(
|
||||||
|
secretApprovers.map(({ id }) => ({
|
||||||
|
approverId: id,
|
||||||
|
policyId: doc.id
|
||||||
|
})),
|
||||||
|
tx
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return doc;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
...updatedPolicy,
|
||||||
|
environment: accessApprovalPolicy.environment,
|
||||||
|
projectId: accessApprovalPolicy.projectId
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteAccessApprovalPolicy = async ({
|
||||||
|
policyId,
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
}: TDeleteAccessApprovalPolicy) => {
|
||||||
|
const policy = await accessApprovalPolicyDAL.findById(policyId);
|
||||||
|
if (!policy) throw new BadRequestError({ message: "Secret approval policy not found" });
|
||||||
|
|
||||||
|
const { permission } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
policy.projectId,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
|
ProjectPermissionActions.Delete,
|
||||||
|
ProjectPermissionSub.SecretApproval
|
||||||
|
);
|
||||||
|
|
||||||
|
await accessApprovalPolicyDAL.deleteById(policyId);
|
||||||
|
return policy;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccessPolicyCountByEnvSlug = async ({
|
||||||
|
actor,
|
||||||
|
actorOrgId,
|
||||||
|
actorAuthMethod,
|
||||||
|
projectSlug,
|
||||||
|
actorId,
|
||||||
|
envSlug
|
||||||
|
}: TGetAccessPolicyCountByEnvironmentDTO) => {
|
||||||
|
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
|
||||||
|
|
||||||
|
if (!project) throw new BadRequestError({ message: "Project not found" });
|
||||||
|
|
||||||
|
const { membership } = await permissionService.getProjectPermission(
|
||||||
|
actor,
|
||||||
|
actorId,
|
||||||
|
project.id,
|
||||||
|
actorAuthMethod,
|
||||||
|
actorOrgId
|
||||||
|
);
|
||||||
|
if (!membership) throw new BadRequestError({ message: "User not found in project" });
|
||||||
|
|
||||||
|
const environment = await projectEnvDAL.findOne({ projectId: project.id, slug: envSlug });
|
||||||
|
if (!environment) throw new BadRequestError({ message: "Environment not found" });
|
||||||
|
|
||||||
|
const policies = await accessApprovalPolicyDAL.find({ envId: environment.id, projectId: project.id });
|
||||||
|
if (!policies) throw new BadRequestError({ message: "No policies found" });
|
||||||
|
|
||||||
|
return { count: policies.length };
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
getAccessPolicyCountByEnvSlug,
|
||||||
|
createAccessApprovalPolicy,
|
||||||
|
deleteAccessApprovalPolicy,
|
||||||
|
updateAccessApprovalPolicy,
|
||||||
|
getAccessApprovalPolicyByProjectSlug
|
||||||
|
};
|
||||||
|
};
|
@ -0,0 +1,44 @@
|
|||||||
|
import { TProjectPermission } from "@app/lib/types";
|
||||||
|
import { ActorAuthMethod } from "@app/services/auth/auth-type";
|
||||||
|
|
||||||
|
import { TPermissionServiceFactory } from "../permission/permission-service";
|
||||||
|
|
||||||
|
export type TVerifyApprovers = {
|
||||||
|
userIds: string[];
|
||||||
|
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
|
||||||
|
envSlug: string;
|
||||||
|
actorAuthMethod: ActorAuthMethod;
|
||||||
|
secretPath: string;
|
||||||
|
projectId: string;
|
||||||
|
orgId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TCreateAccessApprovalPolicy = {
|
||||||
|
approvals: number;
|
||||||
|
secretPath: string;
|
||||||
|
environment: string;
|
||||||
|
approvers: string[];
|
||||||
|
projectSlug: string;
|
||||||
|
name: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TUpdateAccessApprovalPolicy = {
|
||||||
|
policyId: string;
|
||||||
|
approvals?: number;
|
||||||
|
approvers?: string[];
|
||||||
|
secretPath?: string;
|
||||||
|
name?: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TDeleteAccessApprovalPolicy = {
|
||||||
|
policyId: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TGetAccessPolicyCountByEnvironmentDTO = {
|
||||||
|
envSlug: string;
|
||||||
|
projectSlug: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
||||||
|
|
||||||
|
export type TListAccessApprovalPoliciesDTO = {
|
||||||
|
projectSlug: string;
|
||||||
|
} & Omit<TProjectPermission, "projectId">;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user