Compare commits

..

634 Commits

Author SHA1 Message Date
Daniel Hougaard
a61e92c49c Update go.mdx 2024-06-11 04:26:35 +02:00
Daniel Hougaard
fa05639592 Docs: Go SDK 2024-06-10 05:18:39 +02:00
Maidul Islam
6c596092b0 Merge pull request #1927 from Infisical/shubham/eng-983-optimise-secretinput-usage-to-mask-secret-when-not-in-focus
fix: share secret input now masks value onBlur
2024-06-09 17:43:30 -04:00
Maidul Islam
fcd13eac8a update saml org slug environment 2024-06-09 14:41:23 -04:00
Maidul Islam
1fb653754c update saml slug env 2024-06-09 14:37:13 -04:00
Akhil Mohan
bb1d73b0f5 Merge pull request #1935 from Infisical/fix-saml-auto-redirect
patch saml auto redirect
2024-06-09 23:09:29 +05:30
Maidul Islam
59e9226d85 patch saml auto redirect 2024-06-09 13:30:44 -04:00
Maidul Islam
e6f42e1231 Merge pull request #1933 from Infisical/add-folder-sorting
added sorting for folders in overview
2024-06-08 22:31:07 -04:00
Vladyslav Matsiiako
06e7a90a44 added sorting for folders in overview 2024-06-08 22:26:49 -04:00
Maidul Islam
f075ff23a9 patch encoding type for kms 2024-06-08 18:38:25 -04:00
ShubhamPalriwala
e5b7ebbabf revert: change in core component 2024-06-08 05:47:47 +05:30
Maidul Islam
faa1572faf update docs grammar 2024-06-07 12:04:21 -04:00
Maidul Islam
d288bcbd74 explain how auto reload works in docs 2024-06-07 12:01:59 -04:00
BlackMagiq
af1d30a49a Merge pull request #1926 from Infisical/misc/added-auto-redeploy-cf-pages-option
misc: added auto-redeploy option for cf pages integration
2024-06-07 09:58:06 -04:00
ShubhamPalriwala
9d46c269d4 fix: secret input on tab moves to next field and masks value 2024-06-07 13:05:04 +05:30
Sheen Capadngan
cd92ce627c misc: added autoredeploy option for cf pages integration 2024-06-07 13:23:46 +08:00
Sheen Capadngan
c1ca2a6f8c Merge pull request #1918 from Infisical/feat/added-rundeck-api-integration
feat: added rundeck integration
2024-06-06 12:34:59 +08:00
BlackMagiq
9b6602a8e9 Merge pull request #1917 from akhilmhdh/feat/internal-kms
Internal functions for KMS
2024-06-06 00:01:27 -04:00
=
22db286dda fix: resolved failing github action for api breaking change 2024-06-06 01:25:38 +05:30
Sheen Capadngan
9fd0373e39 misc: improved error handling 2024-06-06 03:15:57 +08:00
=
62f92b0bfa feat: resolved root key failing for base64 and also added projectid and orgid field 2024-06-05 23:58:45 +05:30
=
abbef4fc44 feat: changed encrypt function input to buffer 2024-06-05 22:44:21 +05:30
=
34ca942f9d fix: resolved failing test setup 2024-06-05 22:44:21 +05:30
=
1acf25dd53 feat: implmented first internal functions for KMS service 2024-06-05 22:44:20 +05:30
=
a0653883b6 feat: added pg bytea support to generate schema function 2024-06-05 22:44:20 +05:30
Maidul Islam
f3a4c32e3a Merge pull request #1921 from akhilmhdh/fix/select-list-overflow
fix: ui issue resolved for list overflow
2024-06-04 20:38:40 -04:00
Sheen Capadngan
6a6fe3e202 misc: finalized doc reference 2024-06-04 22:37:45 +08:00
Sheen Capadngan
8f4963839e misc: final updates 2024-06-04 22:34:26 +08:00
Akhil Mohan
4c06f134fb Merge pull request #1884 from Infisical/fix/resolved-trailing-slash-issue-additional-privileges
fix: resolved trailing slash issue with additional privileges
2024-06-04 19:59:40 +05:30
Sheen Capadngan
12d3632a03 misc: added documentation for rundeck integration 2024-06-04 22:04:12 +08:00
=
c34c13887a fix: ui issue resolved for list overflow 2024-06-04 19:27:25 +05:30
Sheen Capadngan
378d6c259b feat: finished integration sync for rundeck 2024-06-04 21:10:24 +08:00
Sheen Capadngan
2a538d9560 feat: added frontend pages for rundeck integration 2024-06-04 13:30:50 +08:00
Maidul Islam
eafa50747b increase rate limits 2024-06-03 13:55:59 -04:00
Maidul Islam
77f794e1d0 Merge pull request #1891 from Infisical/snyk-fix-d845d74692da524b59866cf430937d1d
[Snyk] Security upgrade mysql2 from 3.9.7 to 3.9.8
2024-06-03 00:23:25 -04:00
Maidul Islam
3b9afb8b5b Merge pull request #1914 from Infisical/shubham/optimise-loading-gif
chore: compress loader gifs
2024-06-03 00:10:03 -04:00
Sheen Capadngan
8bf763dd5a Merge pull request #1903 from Infisical/feat/add-tags-to-existing-aws-params
feat: added support for tagging existing aws params
2024-06-03 01:39:17 +08:00
ShubhamPalriwala
e93b465004 chore: compress loader gifs 2024-06-01 21:46:03 +05:30
Maidul Islam
000d87075b Merge pull request #1913 from srevinsaju/patch-1 2024-06-01 09:45:21 -04:00
Srevin Saju
2291bdc036 docs: fix typo 2024-06-01 16:22:59 +03:00
Maidul Islam
791361d2c3 update migration name 2024-05-31 18:12:47 -04:00
Maidul Islam
2a18844ef8 Merge pull request #1775 from akhilmhdh/feat/secret-replication
Secret replication
2024-05-31 17:59:43 -04:00
=
1dfad876cf fix: replicated import not expanding inside folder 2024-06-01 00:39:09 +05:30
Maidul Islam
7ddf4492a7 Merge pull request #1907 from Infisical/daniel/k8-secret-expand
Feat: Expand secret references with Machine Identity
2024-05-31 14:16:14 -04:00
Maidul Islam
3c92a2a256 make imports and refs default for all users (this is non breaking so okay) 2024-05-31 14:15:34 -04:00
Maidul Islam
45683dc4c6 Merge pull request #1908 from Infisical/daniel/k8-mi-include-imports
Feat: Include Imports for Machine Identity auth
2024-05-31 14:10:32 -04:00
=
c6043568cf feat: removed isReplicated fields from secret as no longer needed corresponding its changes in ui 2024-05-31 23:10:04 +05:30
=
cf690e2e16 feat: switched to pull all and selectively replicated strategy and simplified logic 2024-05-31 23:10:04 +05:30
Maidul Islam
c67642786f make replicated secrets more intuitive 2024-05-31 23:10:04 +05:30
=
41914e0027 feat: switched to secretapproval check for license 2024-05-31 23:10:04 +05:30
=
a13d4a4970 feat: implemented replication to a folder strategy ui 2024-05-31 23:10:03 +05:30
=
5db6ac711c feat: implemented replication to a folder strategy 2024-05-31 23:10:03 +05:30
=
f426025fd5 fix: resolved approval failing when secret was missing in board 2024-05-31 23:10:03 +05:30
Maidul Islam
d6fcba9169 update texts for secret replication 2024-05-31 23:10:03 +05:30
=
51d4fcf9ee feat: moved secret replication to ee folder 2024-05-31 23:10:03 +05:30
=
316259f218 feat: added subscription plan to secret replication 2024-05-31 23:10:03 +05:30
=
7311cf8f6c feat: updated enable replication text 2024-05-31 23:10:03 +05:30
=
5560c18a09 feat: made migration script idempotent 2024-05-31 23:10:02 +05:30
=
b0c472b5e1 feat: added user status signs for replication failure etc 2024-05-31 23:10:02 +05:30
=
25a615cbb3 feat: made sure secret updates make replicated into normal ones 2024-05-31 23:10:02 +05:30
=
4502d394a3 feat: added back dedupe queue for both replication and syncing ops 2024-05-31 23:10:02 +05:30
=
531d3751a8 feat: resolved personal secret breaking secret replication 2024-05-31 23:10:02 +05:30
=
2d0d90785f feat: added icon for replicated secret 2024-05-31 23:10:02 +05:30
=
cec884ce34 fix: merge conflicts 2024-05-31 23:10:02 +05:30
=
346dbee96a feat: switched to actor and actorId for replication 2024-05-31 23:10:02 +05:30
Akhil Mohan
d5229a27b2 feat: added resync replication feature 2024-05-31 23:10:01 +05:30
Akhil Mohan
a11f120a83 feat: updated ui replication text and approval replication flag 2024-05-31 23:10:01 +05:30
Akhil Mohan
51c1487ed1 feat: updated ui to reflect secret import and approval 2024-05-31 23:10:01 +05:30
Akhil Mohan
c9d6c5c5f7 feat: update prefix key and secret queue fixed for new secretPathFind 2024-05-31 23:10:01 +05:30
Akhil Mohan
3541ddf8ac feat: fixed folder dal wrong folder details in findBySecretPath issue and replication dal 2024-05-31 23:10:01 +05:30
Akhil Mohan
0ae286a80e feat: added secret approval policy in replication 2024-05-31 23:10:01 +05:30
Akhil Mohan
36b7911bcc feat: poc for secret replication completed 2024-05-31 23:10:01 +05:30
Maidul Islam
520167a8ff Merge pull request #1905 from Infisical/shubham/eng-970-single-text-area-instead-of-key-and-value
feat: secret sharing supports expiry on view count
2024-05-31 11:34:04 -04:00
Daniel Hougaard
8c2f709f2a Bump version 2024-05-31 16:25:43 +02:00
Daniel Hougaard
804314cc18 Feat: Include imports for MI Auth 2024-05-31 16:22:32 +02:00
Daniel Hougaard
0c9557b8b5 Feat: Expand secret references with Machine Identity 2024-05-31 15:41:34 +02:00
ShubhamPalriwala
fb4f12fa37 fix: validation message + limit default input unit options 2024-05-31 16:59:54 +05:30
ShubhamPalriwala
29b106c5bd feat: secrets expire either with time or views 2024-05-31 12:47:19 +05:30
ShubhamPalriwala
e7d32b5f2d feat: hide expiry condition to end user & fix decrement condition in knex 2024-05-31 10:58:49 +05:30
ShubhamPalriwala
862e0437e7 fix: use dropdown instead of switch 2024-05-31 09:24:39 +05:30
ShubhamPalriwala
89eff65124 feat: public page shows left views or time on shared secret 2024-05-31 00:29:40 +05:30
ShubhamPalriwala
2347242776 docs: secret sharing 2024-05-31 00:22:31 +05:30
ShubhamPalriwala
3438dbc70d feat: secret sharing supports expiry on view count + multi-line secret value 2024-05-31 00:00:48 +05:30
Sheen Capadngan
d79d7ca5e8 feat: added support for tagging existing aws params 2024-05-30 21:40:09 +08:00
ShubhamPalriwala
c097c918ed fix: docs to open in new tab 2024-05-30 10:06:14 +05:30
Maidul Islam
b801c1e48f Merge pull request #1860 from Infisical/doc/role-desc
docs: updated the cause of option1 and option2 in role update commit
2024-05-29 19:20:16 -04:00
vmatsiiako
cd2b81cb9f Update navbar 2024-05-29 16:14:45 -07:00
Maidul Islam
bdd65784a1 Merge pull request #1898 from Infisical/shubham/eng-632-fix-ghost-users-are-added-to-seats-in-stripe
fix: remove ghost users being added as seats in stripe
2024-05-29 17:21:52 -04:00
Maidul Islam
73195b07a4 update secret share text 2024-05-29 17:16:04 -04:00
vmatsiiako
bdff2cd33d Update secret-reference.mdx 2024-05-29 14:13:38 -07:00
Maidul Islam
1990ce8c7d update secret sharing texts 2024-05-29 17:06:24 -04:00
Maidul Islam
285c4a93c6 update secret sharing time stamp 2024-05-29 20:34:08 +00:00
Maidul Islam
bbb21c95f6 Merge pull request #1886 from Infisical/shubham/feat-secret-sharing 2024-05-29 14:46:13 -04:00
Sheen Capadngan
394340c599 Merge pull request #1899 from Infisical/feat/added-support-for-configuring-custom-ssl-cert-trust
feat: added support for configuring trust of custom SSL certs
2024-05-30 01:36:10 +08:00
ShubhamPalriwala
30039b97b5 fix: remove unnecessary useEffect 2024-05-29 23:01:23 +05:30
Sheen Capadngan
71d4935c0f feat: added support for configuring trust of custom SSL certs 2024-05-29 23:50:36 +08:00
Akhil Mohan
40e7ab33cb fix: resolved lint issue 2024-05-29 13:35:32 +00:00
Sheen Capadngan
aa193adf48 Merge pull request #1896 from Infisical/feat/removed-the-need-to-pass-api-for-cli-domain
feat: removed the need to pass /api for cli domain
2024-05-29 21:34:00 +08:00
Sheen Capadngan
dbac4b4567 Merge pull request #1887 from Infisical/feat/added-support-for-personal-secrets-creation-via-CLI
feat: added support for personal secrets creation via CLI
2024-05-29 21:30:15 +08:00
Sheen Capadngan
df38e79590 fix: addressed type issue 2024-05-29 21:11:03 +08:00
ShubhamPalriwala
8f778403b4 cleanup: secret sharing perms 2024-05-29 18:15:22 +05:30
ShubhamPalriwala
686a28cc09 fix: remove ghost users being added as seats in stripe 2024-05-29 18:05:51 +05:30
ShubhamPalriwala
1068e6024d fix: page title 2024-05-29 14:20:40 +05:30
ShubhamPalriwala
286426b240 feat: use hash as pw & move to symmetric encrpytion 2024-05-29 14:10:47 +05:30
ShubhamPalriwala
b5b778e241 fix: minor ui changes + delete expired secrets + address other feedback 2024-05-29 14:10:47 +05:30
ShubhamPalriwala
f85a35fde8 feat: move feature to org level 2024-05-29 14:10:47 +05:30
ShubhamPalriwala
3b40f37f50 cleanup: console logs 2024-05-29 14:10:46 +05:30
ShubhamPalriwala
4e51a3b784 fix: input type & docs link 2024-05-29 14:10:46 +05:30
ShubhamPalriwala
387981ea87 feat: secret sharing 2024-05-29 14:10:46 +05:30
Maidul Islam
81b0c8bc12 Merge pull request #1897 from Infisical/create-pull-request/patch-1716962873 2024-05-29 02:08:42 -04:00
github-actions
06dca77be2 chore: renamed new migration files to latest timestamp (gh-action) 2024-05-29 06:07:52 +00:00
Maidul Islam
b79ed28bb8 Merge pull request #1895 from Infisical/maidul-12djiqd 2024-05-29 02:07:26 -04:00
Sheen Capadngan
7c6b6653f5 feat: removed the need to pass /api for cli domain 2024-05-29 13:51:20 +08:00
Maidul Islam
6055661515 add secret version secrets index 2024-05-29 00:54:29 -04:00
Daniel Hougaard
f3eda1fd13 Merge pull request #1893 from Infisical/daniel/fix-query-invalidation-bug
Fix: Select organization query invalidation
2024-05-29 03:08:41 +02:00
Daniel Hougaard
60178a6ba6 Update queries.tsx 2024-05-29 02:59:53 +02:00
Daniel Hougaard
3e6d43e4df Update queries.tsx 2024-05-29 02:59:29 +02:00
snyk-bot
f11c2d6b3e fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-MYSQL2-6861580
2024-05-29 00:10:15 +00:00
Maidul Islam
be68ecc25d update api fields 2024-05-28 17:31:09 -04:00
Maidul Islam
b2ad7cc7c0 small rephrase 2024-05-28 17:20:22 -04:00
Maidul Islam
6c6c436cc6 Merge pull request #1874 from akhilmhdh/feat/tf-role-sp-changes
Updates api endpoints for project role and identity specfic privilege
2024-05-28 16:59:44 -04:00
Sheen Capadngan
01ea41611b Merge pull request #1890 from Infisical/misc/minor-mfa-lock-prompt-adjustments
misc: minor ui adjustments
2024-05-29 02:06:05 +08:00
Sheen Capadngan
dc7bf9674a misc: minor ui adjustments 2024-05-29 02:00:49 +08:00
Sheen Capadngan
b6814b67b0 Merge pull request #1885 from Infisical/fix/gitlab-integration-creation-with-groups
fix: resolved gitlab integration creation issue with groups selection
2024-05-29 01:08:45 +08:00
Sheen Capadngan
5234a89612 Merge pull request #1888 from Infisical/create-pull-request/patch-1716910746
GH Action: rename new migration file timestamp
2024-05-29 01:01:35 +08:00
Sheen Capadngan
45bb2f0fcc Merge pull request #1889 from Infisical/fix/added-lock-prompt-to-sso-signin
fix: added account locked prompt to sso signin flow
2024-05-29 01:00:48 +08:00
Sheen Capadngan
4c7e218d0d misc: removed unnecessary set state 2024-05-29 00:58:29 +08:00
Sheen Capadngan
0371a57548 fix: added account locked prompt to sso signin flow 2024-05-29 00:43:14 +08:00
github-actions
7d0eb9a0fd chore: renamed new migration files to latest timestamp (gh-action) 2024-05-28 15:39:05 +00:00
Sheen Capadngan
44b14756b1 Merge pull request #1861 from Infisical/feat/secure-mfa-endpoints-with-improved-rate-limiting
feat: secure mfa endpoints with improved rate limiting and account locking
2024-05-28 23:38:35 +08:00
Sheen Capadngan
1a4f8b23ff feat: added support for personal secrets creation via CLI 2024-05-28 21:13:59 +08:00
Sheen Capadngan
51f4047207 Merge pull request #1883 from Infisical/misc/improve-rbac-integration-creation-and-update-1
misc: improved integration rbac control
2024-05-28 19:49:27 +08:00
Sheen Capadngan
a618e0ebf2 fix: resolved gitlab integration creation issue regarding groups 2024-05-28 19:43:00 +08:00
Maidul Islam
4567e505ec pause project delete 2024-05-28 05:40:32 -04:00
Vladyslav Matsiiako
c638caede5 changed sidebar color 2024-05-27 22:32:54 -07:00
Vladyslav Matsiiako
300deb5607 added time off page to company wiki 2024-05-27 22:26:21 -07:00
Sheen Capadngan
3e9ce79398 fix: resolved trailing slash issue with additional privileges 2024-05-28 12:38:57 +08:00
Sheen Capadngan
0fc4fb8858 misc: added backend validation for secrets read during integration create/update 2024-05-28 12:13:35 +08:00
Vladyslav Matsiiako
1e63604f1e added more styling to docs 2024-05-27 16:03:42 -07:00
Vladyslav Matsiiako
6ce86c4240 added more styling to docs 2024-05-27 16:00:39 -07:00
Vladyslav Matsiiako
fd65936ae7 Merge branch 'main' of https://github.com/Infisical/infisical 2024-05-27 15:59:14 -07:00
Daniel Hougaard
c894a18797 Merge pull request #1718 from Infisical/daniel/mi-ux-fix-2
Fix: Machine Identities user experience improvement
2024-05-28 00:34:17 +02:00
Vladyslav Matsiiako
c170ba6249 changed docs design 2024-05-27 15:32:56 -07:00
Daniel Hougaard
c344330c93 Merge pull request #1882 from Infisical/daniel/azure-auth-sdk-docs
Docs: Azure & Kubernetes auth SDK documentation
2024-05-28 00:18:16 +02:00
Daniel Hougaard
a6dd36f684 Docs: Azure documentation 2024-05-28 00:11:57 +02:00
Daniel Hougaard
eb8acba037 Docs: Azure documentation 2024-05-28 00:11:55 +02:00
Daniel Hougaard
c7a8e1102e Docs: Azure documentation 2024-05-28 00:11:52 +02:00
Daniel Hougaard
aca71a7b6f Docs: Azure documentation 2024-05-28 00:11:49 +02:00
Daniel Hougaard
ae075df0ec Fix: Old docs 2024-05-28 00:11:33 +02:00
Daniel Hougaard
75927f711c Merge pull request #1863 from Infisical/daniel/auth-sdk-docs
Docs: Updated docs to reflect new SDK structure
2024-05-27 23:29:03 +02:00
BlackMagiq
b1b1ce07a3 Merge pull request #1878 from Infisical/create-pull-request/patch-1716795461
GH Action: rename new migration file timestamp
2024-05-27 11:10:48 -07:00
Sheen Capadngan
fe4cc950d3 misc: updated temporary lock error message 2024-05-28 01:41:16 +08:00
Sheen Capadngan
81f7884d03 Merge pull request #1881 from Infisical/fix/resolved-identity-deletion-issue-across-projects
fix: resolved identity deletion issue across projects
2024-05-28 00:28:18 +08:00
Sheen Capadngan
b8c35fbf15 fix: resolved identity deletion issue across projects 2024-05-28 00:13:47 +08:00
Sheen Capadngan
42e73d66fc Merge pull request #1873 from Infisical/feat/add-personal-overrides-and-secret-reference-to-download-envs
feat: added personal overrides and support for secret ref during download
2024-05-27 23:09:59 +08:00
Sheen Capadngan
a0f678a295 misc: moved to using router push instead of reload 2024-05-27 22:59:24 +08:00
github-actions
fe40e4f475 chore: renamed new migration files to latest timestamp (gh-action) 2024-05-27 07:37:39 +00:00
BlackMagiq
b9782c1a85 Merge pull request #1833 from Infisical/azure-auth
Azure Native Authentication Method
2024-05-27 00:37:09 -07:00
Vladyslav Matsiiako
a0be2985dd Added money page 2024-05-26 22:19:42 -07:00
vmatsiiako
86d16c5b9f Merge pull request #1877 from Infisical/sheensantoscapadngan-patch-1
Update onboarding.mdx
2024-05-26 22:05:12 -07:00
Sheen Capadngan
c1c1471439 Update onboarding.mdx 2024-05-27 12:28:14 +08:00
Sheen Capadngan
3639a7fc18 misc: migrated to native DAL method 2024-05-27 11:01:22 +08:00
Sheen Capadngan
59c8dc3cda Merge branch 'main' into feat/secure-mfa-endpoints-with-improved-rate-limiting 2024-05-27 10:55:06 +08:00
vmatsiiako
527e1d6b79 Merge pull request #1876 from Infisical/aws-integration-patch
added company handbook
2024-05-26 16:16:18 -07:00
Vladyslav Matsiiako
3e32915a82 added company handbook 2024-05-26 16:14:37 -07:00
=
7a955e3fae docs: api docs for identity specific privilege 2024-05-26 22:49:36 +05:30
=
ee5130f56c feat: privilege api better permission inputs and required changes in ui for role and privilege 2024-05-26 22:49:35 +05:30
=
719f3beab0 feat: api changes for update role api for identity based use 2024-05-26 22:49:35 +05:30
Maidul Islam
4faa9ced04 Merge pull request #1837 from akhilmhdh/feat/resource-daily-prune
Daily cron for cleaning up expired tokens from db
2024-05-24 12:53:26 -04:00
Maidul Islam
b6ff07b605 revert repete cron 2024-05-24 12:45:19 -04:00
Maidul Islam
1753cd76be update delete access token logic 2024-05-24 12:43:14 -04:00
Sheen Capadngan
f75fc54e10 Merge pull request #1870 from Infisical/doc/updated-gcp-secrets-manager-doc-reminder
doc: added reminder for GCP oauth user permissions
2024-05-25 00:00:15 +08:00
Sheen Capadngan
b9a6f94eea misc: moved user lock reset after backup success 2024-05-24 23:56:24 +08:00
Maidul Islam
966bd77234 Update gcp-secret-manager.mdx 2024-05-24 11:55:29 -04:00
Sheen Capadngan
c782df1176 Merge pull request #1872 from Infisical/fix/resolve-cloudflare-pages-integration
fix: resolved cloudflare pages integration
2024-05-24 23:50:57 +08:00
Sheen Capadngan
c0daa11aeb misc: addressed PR comments 2024-05-24 23:45:16 +08:00
Sheen Capadngan
9b2b6d61be Merge branch 'main' into feat/secure-mfa-endpoints-with-improved-rate-limiting 2024-05-24 22:18:05 +08:00
Sheen Capadngan
efe10e361f feat: added personal overrides and support for secret ref to download envs 2024-05-24 22:14:32 +08:00
Sheen Capadngan
e9c5b7f846 Merge pull request #1871 from Infisical/fix/address-json-drop-behavior
fix: address json drag behavior
2024-05-24 21:46:33 +08:00
Sheen Capadngan
008b37c0f4 fix: resolved cloudflare pages integration 2024-05-24 19:45:20 +08:00
Sheen Capadngan
c9b234dbea fix: address json drag behavior 2024-05-24 17:42:38 +08:00
Sheen Capadngan
049df6abec Merge pull request #1869 from Infisical/misc/made-aws-sm-mapping-plaintext-one-to-one
misc: made aws sm mapping one to one plaintext
2024-05-24 02:15:04 +08:00
Sheen Capadngan
8497182a7b misc: finalized addition 2024-05-24 02:11:03 +08:00
Sheen Capadngan
133841c322 doc: added reminder for oauth user permissions 2024-05-24 01:55:59 +08:00
Sheen Capadngan
e7c5645aa9 misc: made aws sm mapping one to one plaintext 2024-05-24 00:35:55 +08:00
Sheen Capadngan
0bc778b9bf Merge pull request #1865 from Infisical/feat/add-one-to-one-support-for-aws-sm
feat: added one to one support for aws secret manager integration
2024-05-23 23:48:47 +08:00
Sheen Capadngan
b0bc41da14 misc: finalized schema type 2024-05-23 22:18:24 +08:00
Daniel Hougaard
a234b686c2 Merge pull request #1867 from Infisical/daniel/better-no-project-found-error
Fix: Better error message on project not found during bot lookup
2024-05-23 15:54:14 +02:00
Daniel Hougaard
6230167794 Update project-bot-fns.ts 2024-05-23 15:48:54 +02:00
Daniel Hougaard
68d1849ba0 Fix: Better error message on project not found during bot lookup 2024-05-23 15:47:08 +02:00
Daniel Hougaard
5c10427eaf Merge pull request #1866 from Infisical/daniel/fix-no-orgs-selectable
Fix: No orgs selectable if a user has been removed from an organization
2024-05-23 15:19:13 +02:00
Daniel Hougaard
290d99e02c Fix: No orgs selectable if a user has been removed from an organization 2024-05-23 15:11:20 +02:00
Sheen Capadngan
b75d601754 misc: documentation changes and minor UI adjustments 2024-05-23 21:03:48 +08:00
Sheen Capadngan
de2a5b4255 feat: added one to one support for aws SM integration 2024-05-23 20:30:55 +08:00
Daniel Hougaard
3d65d121c0 docs: updated docs to reflect new SDK structure 2024-05-23 04:45:33 +02:00
Maidul Islam
663f8abc51 bring back last updated at for service token 2024-05-22 20:44:07 -04:00
Maidul Islam
941a71efaf add index for expire at needed for pruning 2024-05-22 20:38:04 -04:00
Maidul Islam
19bbc2ab26 add secrets index 2024-05-22 19:04:44 -04:00
Maidul Islam
f4de52e714 add index on secret snapshot folders 2024-05-22 18:15:04 -04:00
Maidul Islam
0b87121b67 add index to secret-snapshot-secret for field snapshotId 2024-05-22 17:46:16 -04:00
Maidul Islam
e649667da8 add indexs to secret versions and secret snapshot secrets 2024-05-22 16:52:18 -04:00
Maidul Islam
6af4b3f64c add index for audit logs 2024-05-22 15:48:24 -04:00
Maidul Islam
efcc248486 add tx to find ghost user 2024-05-22 14:54:20 -04:00
Maidul Islam
82eeae6030 track identity 2024-05-22 13:34:44 -04:00
Sheen Capadngan
a0d9331e67 misc: removed comment 2024-05-23 00:21:19 +08:00
Sheen Capadngan
8ec8b1ce2f feat: add custom rate limiting for mfa 2024-05-22 23:51:58 +08:00
Sheen Capadngan
e3dae9d498 feat: integration user lock flow to frontend 2024-05-22 22:51:20 +08:00
Sheen Capadngan
41d72d5dc6 feat: added user-locking on mfa failure 2024-05-22 21:55:56 +08:00
Maidul Islam
440c77965c add logs to track permission inject 2024-05-21 22:35:13 -04:00
Maidul Islam
880289217e revert identity based rate limit 2024-05-21 22:24:53 -04:00
Maidul Islam
d0947f1040 update service tokens 2024-05-21 22:02:01 -04:00
Akhil Mohan
75bb651b1d docs: updated the cause of option1 and option2 in role update commit 2024-05-22 00:19:04 +05:30
Sheen Capadngan
303edadb1e Merge pull request #1848 from Infisical/feat/add-integration-sync-status
feat: added integration sync status
2024-05-22 01:19:36 +08:00
Maidul Islam
50155a610d Merge pull request #1858 from Infisical/misc/address-digital-ocean-env-encryption
misc: made digital ocean envs encrypted by default
2024-05-21 13:15:09 -04:00
Sheen Capadngan
c2830a56b6 misc: made digital ocean envs encrypted by default 2024-05-22 01:12:28 +08:00
Sheen Capadngan
b9a9b6b4d9 misc: applied ui/ux changes 2024-05-22 00:06:06 +08:00
Maidul Islam
e7f7f271c8 Merge pull request #1857 from Infisical/misc/added-pino-logger-redaction
misc: added logger redaction
2024-05-21 11:49:36 -04:00
Sheen Capadngan
b26e96c5a2 misc: added logger redaction 2024-05-21 23:04:11 +08:00
Sheen Capadngan
9b404c215b adjustment: ui changes to sync button 2024-05-21 16:04:36 +08:00
Sheen Capadngan
d6dae04959 misc: removed unnecessary notification 2024-05-21 14:01:15 +08:00
Sheen Capadngan
629bd9b7c6 added support for manual syncing of integrations 2024-05-21 13:56:44 +08:00
Tuan Dang
4e06fa3a0c Move azure auth migration file to front 2024-05-20 21:15:42 -07:00
Tuan Dang
0f827fc31a Merge remote-tracking branch 'origin' into azure-auth 2024-05-20 21:14:30 -07:00
vmatsiiako
3d4aa0fdc9 Merge pull request #1853 from Infisical/daniel/jenkins-docs
Docs: Jenkins Plugin
2024-05-20 20:13:42 -07:00
Daniel Hougaard
711e30a6be Docs: Plugin installation 2024-05-21 04:58:24 +02:00
Daniel Hougaard
7b1462fdee Docs: Updated Jenkins docs to reflect new plugin 2024-05-21 04:56:26 +02:00
Daniel Hougaard
50915833ff Images 2024-05-21 04:56:15 +02:00
Maidul Islam
44e37fd531 update distinct id for service tokens 2024-05-20 19:50:52 -04:00
Maidul Islam
fa3f957738 count for null actor 2024-05-20 19:26:03 -04:00
Maidul Islam
224b26ced6 Merge pull request #1852 from Infisical/rate-limit-based-on-identity
rate limit based on identity
2024-05-20 19:04:41 -04:00
Maidul Islam
e833d9e67c revert secret read limit 2024-05-20 19:01:01 -04:00
Maidul Islam
dc08edb7d2 rate limit based on identity 2024-05-20 18:52:23 -04:00
Maidul Islam
0b78e30848 Delete mongo infisical helm 2024-05-20 16:27:15 -04:00
Sheen Capadngan
9253c69325 misc: finalized ui design of integration sync status 2024-05-21 02:35:23 +08:00
Tuan Dang
7189544705 Merge branch 'azure-auth' of https://github.com/Infisical/infisical into azure-auth 2024-05-20 08:41:19 -07:00
Tuan Dang
a724ab101c Fix identities docs markings 2024-05-20 08:39:10 -07:00
Sheen Capadngan
7d3a62cc4c feat: added integration sync status 2024-05-20 20:56:29 +08:00
Tuan Dang
dea67e3cb0 Update azure auth based on review 2024-05-19 22:24:26 -07:00
Tuan Dang
ce66cccd8b Fix merge conflicts 2024-05-19 22:19:49 -07:00
Tuan Dang
7e2147f14e Adjust aws iam auth docs 2024-05-19 22:05:38 -07:00
Daniel Hougaard
91eda2419a Update machine-identities.mdx 2024-05-20 00:32:10 +02:00
Akhil Mohan
32f39c98a7 Merge pull request #1842 from akhilmhdh/feat/membership-by-id
Endpoints for retreiving membership details
2024-05-19 23:51:30 +05:30
Maidul Islam
ddf6db5a7e small rephrase 2024-05-19 14:19:42 -04:00
BlackMagiq
554dbf6c23 Merge pull request #1846 from Infisical/create-pull-request/patch-1716042374
GH Action: rename new migration file timestamp
2024-05-18 07:33:38 -07:00
github-actions
d1997f04c0 chore: renamed new migration files to latest timestamp (gh-action) 2024-05-18 14:26:13 +00:00
BlackMagiq
deefaa0961 Merge pull request #1827 from Infisical/k8s-auth
Kubernetes Native Authentication Method
2024-05-18 07:25:52 -07:00
Tuan Dang
a392c9f022 Move k8s migration to front 2024-05-17 22:41:33 -07:00
Maidul Islam
34222b83ee review fixes for k8s auth 2024-05-17 21:44:02 -04:00
Tuan Dang
b350eef2b9 Add access token trusted ip support for azure auth 2024-05-17 15:43:12 -07:00
Tuan Dang
85725215f2 Merge remote-tracking branch 'origin' into azure-auth 2024-05-17 15:41:58 -07:00
Tuan Dang
ef36852a47 Add access token trusted ip support to k8s auth 2024-05-17 15:41:32 -07:00
Tuan Dang
d79fd826a4 Merge remote-tracking branch 'origin' into k8s-auth 2024-05-17 15:39:52 -07:00
Maidul Islam
18aaa423a9 Merge pull request #1845 from Infisical/patch-gcp-id-token-auth
Patch Identity Access Token Trusted IPs validation for AWS/GCP Auth
2024-05-17 18:38:15 -04:00
Tuan Dang
32c33eaf6e Patch identity token trusted ips validation for aws/gcp auths 2024-05-17 11:58:08 -07:00
Maidul Islam
702699b4f0 Update faq.mdx 2024-05-17 12:13:11 -04:00
Maidul Islam
35ee03d347 Merge pull request #1843 from akhilmhdh/fix/validation-permission
feat: added validation for project permission body in identity specific privilege
2024-05-17 11:50:35 -04:00
=
9c5deee688 feat: added validation for project permission body in identity specific privilege 2024-05-17 21:09:50 +05:30
=
ce4cb39a2d docs: added doc for new endpoints of getting membership and some title change 2024-05-17 20:49:58 +05:30
=
84724e5f65 feat: added endpoints to fetch a particule project user membership and identity 2024-05-17 20:45:31 +05:30
=
56c2e12760 feat: added create identity project membership to api reference and support for roles 2024-05-17 17:09:35 +05:30
=
21656a7ab6 docs: seperate project user and identities api into seperate 2024-05-17 16:15:52 +05:30
=
2ccc77ef40 feat: split project api description for identities and users into seperate 2024-05-17 16:15:05 +05:30
Akhil Mohan
1438415d0c Merge pull request #1450 from Cristobal-M/feat-support-imports-in-cli-export
feat(cli): support of include-imports in export command
2024-05-17 14:21:34 +05:30
Akhil Mohan
eca0e62764 Merge pull request #1829 from akhilmhdh/feat/revoke-access-token
Revoke access token endpoint
2024-05-16 23:41:38 +05:30
Maidul Islam
e4186f0317 Merge pull request #1838 from akhilmhdh/fix/aws-parameter-stoer
fix: get all secrets from aws ssm
2024-05-16 12:27:20 -04:00
=
704c630797 feat: added rate limit for sync secrets 2024-05-16 21:34:31 +05:30
Maidul Islam
f398fee2b8 make var readable 2024-05-16 11:43:32 -04:00
=
7fce51e8c1 fix: get all secrets from aws ssm 2024-05-16 20:51:07 +05:30
=
76c9d642a9 fix: resolved identity check failing due to comma seperated header in ip 2024-05-16 15:46:19 +05:30
=
3ed5dd6109 feat: removed audit log queue and switched to resource clean up queue 2024-05-16 15:46:19 +05:30
=
08e7815ec1 feat: added increment and decrement ops in update knex orm 2024-05-16 15:46:19 +05:30
=
04d961b832 feat: added dal to remove expired token for queue and fixed token validation check missing num uses increment and maxTTL failed check 2024-05-16 15:46:18 +05:30
Cristobal
a6fe233122 Feat: missing documentation for include-imports in export and run command 2024-05-16 11:44:29 +02:00
Tuan Dang
9c0a1b7089 Merge remote-tracking branch 'origin' into azure-auth 2024-05-15 23:23:50 -07:00
Tuan Dang
9352e8bca0 Add docs for Azure auth 2024-05-15 23:13:19 -07:00
Maidul Islam
5e678b1ad2 Merge pull request #1836 from akhilmhdh/fix/create-secret-fail-reference
fix: resolved create secret failing for reference
2024-05-15 22:34:37 -04:00
Akhil Mohan
cf453e87d8 Merge pull request #1835 from Infisical/daniel/fix-expansion
Fix: Fix secret expansion II
2024-05-16 08:02:41 +05:30
=
4af703df5b fix: resolved create secret failing for reference 2024-05-16 07:35:05 +05:30
Daniel Hougaard
75b8b521b3 Update secret-service.ts 2024-05-16 03:31:01 +02:00
Daniel Hougaard
58c1d3b0ac Merge pull request #1832 from Infisical/daniel/fix-secret-expand-with-recursive
Fix: Secret expansion with recursive mode enabled
2024-05-16 02:33:28 +02:00
Maidul Islam
6b5cafa631 Merge pull request #1834 from Infisical/patch-update-project-identity
patch project identity update
2024-05-15 20:23:09 -04:00
Maidul Islam
4a35623956 remove for of with for await 2024-05-15 20:19:10 -04:00
Maidul Islam
74fe673724 patch project identity update 2024-05-15 20:12:45 -04:00
Tuan Dang
265932df20 Finish preliminary azure auth method 2024-05-15 16:30:42 -07:00
Daniel Hougaard
2f92719771 Fix: Secret expansion with recursive mode 2024-05-16 00:29:07 +02:00
Akhil Mohan
399ca7a221 Merge pull request #1826 from justin1121/patch-1
Update secret-versioning.mdx
2024-05-15 15:34:03 +05:30
=
29f37295e1 docs: added revoke token api to api-reference 2024-05-15 15:27:26 +05:30
=
e3184a5f40 feat(api): added revoke access token endpoint 2024-05-15 15:26:38 +05:30
Tuan Dang
ace008f44e Make rejectUnauthorized true if ca cert is passed for k8s auth method 2024-05-14 22:49:37 -07:00
Maidul Islam
4afd95fe1a Merge pull request #1825 from akhilmhdh/feat/sync-integration-inline
Secret reference and integration sync support
2024-05-15 01:36:19 -04:00
Maidul Islam
3cd719f6b0 update index secret references button 2024-05-15 09:57:24 +05:30
Maidul Islam
c6352cc970 updated texts and comments 2024-05-15 09:57:24 +05:30
=
d4555f9698 feat: ui for reindex secret reference 2024-05-15 09:57:24 +05:30
=
393964c4ae feat: implemented inline secret reference integration sync 2024-05-15 09:57:23 +05:30
Tuan Dang
e4afbe8662 Update k8s auth docs 2024-05-14 20:44:09 -07:00
Tuan Dang
0d89aa8607 Add docs for K8s auth method 2024-05-14 18:02:05 -07:00
Tuan Dang
2b91ec5ae9 Fix merge conflicts 2024-05-14 13:37:39 -07:00
Maidul Islam
c438479246 update prod pipeline names 2024-05-14 16:14:42 -04:00
Justin Patriquin
9828cbbfbe Update secret-versioning.mdx 2024-05-14 16:28:43 -03:00
Tuan Dang
cd910a2fac Update k8s auth impl to be able to test ca, tokenReviewerjwt locally 2024-05-14 11:42:26 -07:00
Maidul Islam
fc1dffd7e2 Merge pull request #1823 from Infisical/snyk-fix-a2a4b055e42c14d5cbdb505e7670d300
[Snyk] Security upgrade bullmq from 5.3.3 to 5.4.2
2024-05-14 12:02:13 -04:00
Maidul Islam
55f8198a2d Merge pull request #1821 from matthewaerose/patch-1
Fix: Correct typo from 'Halm' to 'Helm'
2024-05-14 11:46:49 -04:00
Maidul Islam
4d166402df Merge pull request #1824 from Infisical/create-pull-request/patch-1715660210
GH Action: rename new migration file timestamp
2024-05-14 00:17:34 -04:00
github-actions
19edf83dbc chore: renamed new migration files to latest timestamp (gh-action) 2024-05-14 04:16:49 +00:00
snyk-bot
13f6b238e7 fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-BRACES-6838727
- https://snyk.io/vuln/SNYK-JS-MICROMATCH-6838728
2024-05-14 04:16:40 +00:00
Maidul Islam
8dee1f8fc7 Merge pull request #1800 from Infisical/gcp-iam-auth
GCP Native Authentication Method
2024-05-14 00:16:28 -04:00
Maidul Islam
3b23035dfb disable secret scanning 2024-05-13 23:12:36 -04:00
Matthew
0c8ef13d8d Fix: Correct typo from 'Halm' to 'Helm' 2024-05-13 13:38:09 -05:00
Maidul Islam
389d51fa5c Merge pull request #1819 from akhilmhdh/feat/hide-secret-scanner
feat: added secret-scanning disable option
2024-05-13 13:53:35 -04:00
Maidul Islam
638208e9fa update secret scanning text 2024-05-13 13:48:23 -04:00
Maidul Islam
c176d1e4f7 Merge pull request #1818 from akhilmhdh/fix/patches-v2
Improvised secret input component and fontawesome performance improvment
2024-05-13 13:42:30 -04:00
=
91a23a608e feat: added secret-scanning disable option 2024-05-13 21:55:37 +05:30
=
c6a25271dd fix: changed cross key to check for submission for save secret changes 2024-05-13 19:50:38 +05:30
=
0f5c1340d3 feat: dashboard optimized on font awesome levels using symbols technique 2024-05-13 13:40:59 +05:30
=
ecbdae110d feat: simplified secret input with auto completion 2024-05-13 13:40:59 +05:30
=
8ef727b4ec fix: resolved typo in dashboard nav header redirection 2024-05-13 13:40:59 +05:30
=
c6f24dbb5e fix: resolved unique key error secret input rendering 2024-05-13 13:40:59 +05:30
Tuan Dang
c45dae4137 Merge remote-tracking branch 'origin' into k8s-auth 2024-05-12 16:16:44 -07:00
vmatsiiako
18c0d2fd6f Merge pull request #1814 from Infisical/aws-integration-patch
Allow updating tags in AWS Secret Manager integration
2024-05-12 15:03:19 -07:00
Tuan Dang
c1fb8f47bf Add UntagResource IAM policy requirement for AWS SM integration docs 2024-05-12 08:57:41 -07:00
Tuan Dang
bd57a068d1 Fix merge conflicts 2024-05-12 08:43:29 -07:00
Maidul Islam
990eddeb32 Merge pull request #1816 from akhilmhdh/fix/remove-migration-notice
fix: removed migration notice
2024-05-11 13:43:04 -04:00
=
ce01f8d099 fix: removed migration notice 2024-05-11 23:04:43 +05:30
Maidul Islam
faf6708b00 Merge pull request #1815 from akhilmhdh/fix/migration-mode-patch-v1
feat: maintaince mode enable machine identity login and renew
2024-05-11 11:26:21 -04:00
=
a58d6ebdac feat: maintaince mode enable machine identity login and renew 2024-05-11 20:54:00 +05:30
Tuan Dang
818b136836 Make app and appId optional in update integration endpoint 2024-05-10 19:17:40 -07:00
Tuan Dang
0cdade6a2d Update AWS SM integration to allow updating tags 2024-05-10 19:07:44 -07:00
Tuan Dang
bcf9b68e2b Update GCP auth method description 2024-05-10 10:28:29 -07:00
Tuan Dang
6aa9fb6ecd Updated docs 2024-05-10 10:24:29 -07:00
Tuan Dang
38e7382d85 Remove GCP audit log space 2024-05-10 10:15:43 -07:00
Tuan Dang
95e12287c2 Minor edits to renaming GCE -> ID Token 2024-05-10 10:14:13 -07:00
Tuan Dang
c6d14a4bea Update 2024-05-10 10:10:51 -07:00
Tuan Dang
0a91586904 Remove service account JSON requirement from GCP Auth 2024-05-10 09:56:35 -07:00
Sheen Capadngan
6561a9c7be Merge pull request #1804 from Infisical/feat/add-support-for-secret-folder-rename-overview
Feature: add support for secret folder rename in the overview page
2024-05-10 23:07:14 +08:00
Daniel Hougaard
86aaa486b4 Update secret-folder-service.ts 2024-05-10 17:00:30 +02:00
Sheen Capadngan
9880977098 misc: addressed naming suggestion 2024-05-10 22:52:08 +08:00
Sheen Capadngan
b93aaffe77 adjustment: updated to use project slug 2024-05-10 22:34:16 +08:00
Maidul Islam
1ea0d55dd1 Merge pull request #1813 from Infisical/misc/update-documentation-for-github-integration
misc: updated documentation for github integration to include official action
2024-05-10 09:14:14 -04:00
Sheen Capadngan
0866a90c8e misc: updated documentation for github integration 2024-05-10 16:29:12 +08:00
Sheen Capadngan
3fff272cb3 feat: added snapshot for batch 2024-05-10 15:46:31 +08:00
Sheen Capadngan
2559809eac misc: addressed formatting issues 2024-05-10 14:41:35 +08:00
Sheen Capadngan
f32abbdc25 feat: integrate overview folder rename with new batch endpoint 2024-05-10 14:00:49 +08:00
Sheen Capadngan
a6f750fafb feat: added batch update endpoint for folders 2024-05-10 13:57:00 +08:00
Tuan Dang
610f474ecc Rename migration file 2024-05-09 16:58:39 -07:00
Tuan Dang
03f4a699e6 Improve GCP docs 2024-05-09 16:53:08 -07:00
Tuan Dang
533d49304a Update GCP documentation 2024-05-09 15:35:50 -07:00
Tuan Dang
184b59ad1d Resolve merge conflicts 2024-05-09 12:51:24 -07:00
Maidul Islam
b4a2123fa3 Merge pull request #1812 from Infisical/delete-pg-migrator
Delete PG migrator folder
2024-05-09 15:19:04 -04:00
Tuan Dang
79cacfa89c Delete PG migrator folder 2024-05-09 12:16:13 -07:00
Maidul Islam
44531487d6 Merge pull request #1811 from Infisical/maidul-pacth233
revert schema name for memberships-unique-constraint
2024-05-09 13:46:32 -04:00
Maidul Islam
7c77a4f049 revert schema name 2024-05-09 13:42:23 -04:00
Maidul Islam
9dfb587032 Merge pull request #1810 from Infisical/check-saml-email-verification
Update isEmailVerified field upon invite signups
2024-05-09 13:03:52 -04:00
Tuan Dang
3952ad9a2e Update isEmailVerified field upon invite signups 2024-05-09 09:51:53 -07:00
Akhil Mohan
9c15cb407d Merge pull request #1806 from Infisical/aws-non-delete
Add option to not delete secrets in parameter store
2024-05-09 21:56:48 +05:30
Maidul Islam
cb17efa10b Merge pull request #1809 from akhilmhdh/fix/patches-v2
Workspace slug support in secret v3 Get Key
2024-05-09 12:17:14 -04:00
Maidul Islam
4adc2c4927 update api descriptions 2024-05-09 12:11:46 -04:00
Maidul Islam
1a26b34ad8 Merge pull request #1805 from Infisical/revise-aws-auth
Reframe AWS IAM auth to AWS Auth with IAM type
2024-05-09 12:06:31 -04:00
=
21c339d27a fix: better error message on ua based login error 2024-05-09 21:32:09 +05:30
Maidul Islam
1da4cf85f8 rename schema file 2024-05-09 11:59:47 -04:00
=
20f29c752d fix: added workspaceSlug support get secret by key 2024-05-09 21:23:57 +05:30
BlackMagiq
29ea12f8b1 Merge pull request #1807 from Infisical/mermaid-universal-auth
Add mermaid diagram for Universal Auth
2024-05-08 22:05:12 -07:00
Tuan Dang
b4f1cce587 Add mermaid diagram for universal auth 2024-05-08 22:03:57 -07:00
Maidul Islam
5a92520ca3 Update build-staging-and-deploy-aws.yml 2024-05-09 00:53:42 -04:00
Tuan Dang
42471b22bb Finish AWS Auth mermaid diagram 2024-05-08 21:52:56 -07:00
Vladyslav Matsiiako
79704e9c98 add option to not delete secrets in parameter store 2024-05-08 21:49:09 -07:00
Maidul Islam
1165d11816 Update build-staging-and-deploy-aws.yml 2024-05-09 00:27:21 -04:00
Tuan Dang
15ea96815c Rename AWS IAM auth to AWS Auth with IAM type 2024-05-08 21:22:23 -07:00
Maidul Islam
86d4d88b58 package json lock 2024-05-09 00:19:44 -04:00
Maidul Islam
a12ad91e59 Update build-staging-and-deploy-aws.yml 2024-05-09 00:15:42 -04:00
Tuan Dang
3113e40d0b Add mermaid diagrams to gcp auth docs 2024-05-08 20:09:08 -07:00
Tuan Dang
2406d3d904 Update GCP auth docs 2024-05-08 17:03:26 -07:00
Tuan Dang
e99182c141 Complete adding GCP GCE auth 2024-05-08 15:51:09 -07:00
vmatsiiako
f23056bcbc Update IdentityTable.tsx 2024-05-08 09:20:30 -07:00
Sheen Capadngan
522dd0836e feat: added validation for folder name duplicates 2024-05-08 23:25:33 +08:00
Sheen Capadngan
e461787c78 feat: added support for renaming folders in the overview page 2024-05-08 23:24:33 +08:00
Sheen Capadngan
f74993e850 Merge pull request #1803 from Infisical/misc/improved-select-path-component-ux-1
misc: added handling of input focus to select path component
2024-05-08 22:00:02 +08:00
Sheen Capadngan
d0036a5656 Merge remote-tracking branch 'origin/main' into misc/improved-select-path-component-ux-1 2024-05-08 17:28:31 +08:00
Sheen Capadngan
e7f19421ef misc: resolved auto-popup of suggestions 2024-05-08 17:24:06 +08:00
Daniel Hougaard
e18d830fe8 Merge pull request #1801 from Infisical/daniel/k8-recursive
Feat: Recursive support for K8 operaetor
2024-05-08 00:44:07 +02:00
Daniel Hougaard
be2fc4fec4 Update Chart.yaml 2024-05-08 00:42:38 +02:00
Daniel Hougaard
829dbb9970 Update values.yaml 2024-05-08 00:41:53 +02:00
Daniel Hougaard
0b012c5dfb Chore: Helm 2024-05-08 00:23:50 +02:00
Daniel Hougaard
b0421ccad0 Docs: Add recursive to example 2024-05-08 00:21:08 +02:00
Daniel Hougaard
6b83326d00 Feat: Recursive mode support 2024-05-08 00:18:53 +02:00
Daniel Hougaard
1f6abc7f27 Feat: Recursive mode and fix error formatting 2024-05-08 00:18:40 +02:00
Daniel Hougaard
4a02520147 Update sample 2024-05-08 00:18:26 +02:00
Daniel Hougaard
14f38eb961 Feat: Recursive mode types 2024-05-08 00:16:51 +02:00
Tuan Dang
ac469dbe4f Update GCP auth docs 2024-05-07 14:58:14 -07:00
Tuan Dang
d98430fe07 Merge remote-tracking branch 'origin' into gcp-iam-auth 2024-05-07 14:29:08 -07:00
Tuan Dang
82bafd02bb Fix merge conflicts 2024-05-07 14:28:41 -07:00
BlackMagiq
37a59b2576 Merge pull request #1799 from Infisical/create-pull-request/patch-1715116016
GH Action: rename new migration file timestamp
2024-05-07 14:27:45 -07:00
github-actions
cebd22da8e chore: renamed new migration files to latest timestamp (gh-action) 2024-05-07 21:06:55 +00:00
BlackMagiq
d200405c6e Merge pull request #1778 from Infisical/aws-iam-auth
AWS IAM Authentication Method
2024-05-07 14:06:30 -07:00
Maidul Islam
3a1cdc4f44 Delete backend/src/db/migrations/20240507162149_test.ts 2024-05-07 15:41:09 -04:00
Tuan Dang
1d40d9e448 Begin frontend for GCP IAM Auth 2024-05-07 12:40:19 -07:00
Tuan Dang
e96ca8d355 Draft GCP IAM Auth docs 2024-05-07 12:15:18 -07:00
Maidul Islam
2929d94f0a Merge pull request #1797 from Infisical/maidul98-patch-10
test
2024-05-07 14:28:03 -04:00
Maidul Islam
0383ae9e8b Create 20240507162149_test.ts 2024-05-07 14:27:44 -04:00
Maidul Islam
00faa6257f Delete backend/src/db/migrations/20240507162149_test.ts 2024-05-07 14:27:33 -04:00
Maidul Islam
183bde55ca correctly fetch merged by user login 2024-05-07 14:26:56 -04:00
Maidul Islam
c96fc1f798 Merge pull request #1795 from Infisical/maidul98-patch-9
test
2024-05-07 14:09:49 -04:00
Maidul Islam
80f7ff1ea8 Create 20240507162149_test.ts 2024-05-07 14:09:38 -04:00
Maidul Islam
c87620109b Rename 20240507162141_access to 20240507162141_access.ts 2024-05-07 13:58:10 -04:00
Maidul Islam
02c158b4ed Delete backend/src/db/migrations/20240507162180_test 2024-05-07 13:47:25 -04:00
Tuan Dang
588f4bdb09 Fix merge conflict 2024-05-07 10:45:07 -07:00
Tuan Dang
4d74d264dd Finish preliminary backend endpoints for GCP IAM Auth method 2024-05-07 10:42:39 -07:00
Maidul Islam
ddfa64eb33 Merge pull request #1793 from Infisical/maidul98-patch-8
testing-ignore
2024-05-07 13:27:19 -04:00
Maidul Islam
7fdaa1543a Create 20240507162180_test 2024-05-07 13:26:52 -04:00
Maidul Islam
c8433f39ed Delete backend/src/db/migrations/20240507162180_test 2024-05-07 13:26:42 -04:00
Maidul Islam
ba238a8f3b get pr details by pr number 2024-05-07 13:25:35 -04:00
Sheen Capadngan
dd89a80449 Merge pull request #1788 from Infisical/feature/add-multi-select-deletion-overview
Feature: Add support for deleting secrets and folders in the Overview page
2024-05-08 01:25:21 +08:00
Maidul Islam
a1585db76a Merge pull request #1791 from Infisical/maidul98-patch-7
Create 20240507162180_test
2024-05-07 13:16:59 -04:00
Maidul Islam
f5f0bf3c83 Create 20240507162180_test 2024-05-07 13:16:42 -04:00
Maidul Islam
3638645b8a get closed by user 2024-05-07 13:15:15 -04:00
Sheen Capadngan
f957b9d970 misc: migrated to react-state 2024-05-08 01:03:41 +08:00
Maidul Islam
b461697fbf Merge pull request #1790 from Infisical/fix/api-doc-typo
doc: fixed typo in api privilege documentation
2024-05-07 12:56:34 -04:00
Akhil Mohan
3ce91b8a20 doc: fixed typo in api privilege documentation 2024-05-07 22:25:36 +05:30
Sheen Capadngan
8bab14a672 misc: added handling of input focus 2024-05-08 00:43:14 +08:00
Maidul Islam
78922a80e2 Merge pull request #1716 from Infisical/snyk-fix-0eecde4245cc6ed2d19ec9aa18a14703
[Snyk] Security upgrade mysql2 from 3.9.4 to 3.9.7
2024-05-07 12:23:13 -04:00
Maidul Islam
0181007c66 Merge pull request #1789 from Infisical/create-pull-request/patch-1715098901
GH Action: rename new migration file timestamp
2024-05-07 12:22:42 -04:00
github-actions
306cf8733e chore: renamed new migration files to latest timestamp (gh-action) 2024-05-07 16:21:40 +00:00
Maidul Islam
6e829516db Merge pull request #1652 from Infisical/daniel/request-access
Feat: Request Access
2024-05-07 12:21:17 -04:00
Sheen Capadngan
c08fcc6f5e adjustment: finalized notification text 2024-05-08 00:12:55 +08:00
Daniel Hougaard
9a585ad930 Fix: Rebase error 2024-05-07 17:30:36 +02:00
Daniel Hougaard
95c1fff7d3 Chore: Remove unused files 2024-05-07 17:30:36 +02:00
Daniel Hougaard
9c2591f3a6 Fix: Moved Divider to v2 2024-05-07 17:30:36 +02:00
Daniel Hougaard
a579598b6d Chore: Moved verifyApprovers 2024-05-07 17:30:36 +02:00
Daniel Hougaard
af0d31db2c Fix: Improved migrations 2024-05-07 17:30:36 +02:00
Daniel Hougaard
fb6c4acf31 Delete access-approval-request-secret-dal.ts 2024-05-07 17:30:36 +02:00
Daniel Hougaard
551ca0fa8c Migration improvements 2024-05-07 17:30:36 +02:00
Daniel Hougaard
4a0ccbe69e Fixed bugs 2024-05-07 17:30:36 +02:00
Daniel Hougaard
f5a463ddea Update SecretApprovalPage.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
ce1ad6f32e Fix: Rebase errors 2024-05-07 17:30:36 +02:00
Daniel Hougaard
56c8b4f5e5 Removed unnessecary types 2024-05-07 17:30:36 +02:00
Daniel Hougaard
29b26e3158 Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
6e209bf099 Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
949d210263 Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Vladyslav Matsiiako
1a2d8e96f3 style changes 2024-05-07 17:30:36 +02:00
Daniel Hougaard
9198eb5fba Update licence-fns.ts 2024-05-07 17:30:36 +02:00
Daniel Hougaard
0580f37c5e Update generate-schema-types.ts 2024-05-07 17:30:36 +02:00
Daniel Hougaard
e53d40f0e5 Update SecretApprovalPage.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
801c0c5ada Fix: Remove redundant code 2024-05-07 17:30:36 +02:00
Daniel Hougaard
7b8af89bee Fix: Validate approvers access 2024-05-07 17:30:36 +02:00
Daniel Hougaard
ef7f5c9eac Feat: Request access (new routes) 2024-05-07 17:30:36 +02:00
Daniel Hougaard
db0b4a5ad1 Feat: Request access 2024-05-07 17:30:36 +02:00
Daniel Hougaard
cb505d1525 Draft 2024-05-07 17:30:36 +02:00
Daniel Hougaard
c66476e2b4 Fix: Multiple approvers acceptance bug 2024-05-07 17:30:36 +02:00
Daniel Hougaard
60a06edd9b Style: Fix styling 2024-05-07 17:30:36 +02:00
Daniel Hougaard
e8e1d46f0e Capitalization 2024-05-07 17:30:36 +02:00
Daniel Hougaard
038fe3508c Removed unnessecary types 2024-05-07 17:30:36 +02:00
Daniel Hougaard
7d1dff9e5a Fix: Security vulnurbility making it possible to spoof env & secret path requested. 2024-05-07 17:30:36 +02:00
Daniel Hougaard
5117f5d3c1 Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
350dd97b98 Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
121902e51f Update AccessApprovalRequest.tsx 2024-05-07 17:30:36 +02:00
Vladyslav Matsiiako
923bf02046 style changes 2024-05-07 17:30:36 +02:00
Daniel Hougaard
27447ddc88 Update licence-fns.ts 2024-05-07 17:30:36 +02:00
Daniel Hougaard
a3b4b650d1 Removed unused parameter 2024-05-07 17:30:36 +02:00
Daniel Hougaard
3f0f45e853 Update SpecificPrivilegeSection.tsx 2024-05-07 17:30:36 +02:00
Daniel Hougaard
3bb50b235d Update generate-schema-types.ts 2024-05-07 17:30:36 +02:00
Daniel Hougaard
1afd120e8e Feat: Request access 2024-05-07 17:30:36 +02:00
Daniel Hougaard
ab3593af37 Feat: Request access 2024-05-07 17:30:36 +02:00
Daniel Hougaard
2c2afbea7a Fix: Move to project slug 2024-05-07 17:30:36 +02:00
Daniel Hougaard
4eabbb3ac5 Fix: Added support for request access 2024-05-07 17:30:36 +02:00
Daniel Hougaard
1ccd74e1a5 Fix: Remove redundant code 2024-05-07 17:30:35 +02:00
Daniel Hougaard
812cced9d5 Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
cd6be68461 Fix: Validate approvers access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
5c69bbf515 Feat: Request access (new routes) 2024-05-07 17:30:35 +02:00
Daniel Hougaard
448f89fd1c Feat: Request Access (migrations) 2024-05-07 17:30:35 +02:00
Daniel Hougaard
3331699f56 Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
810f670e64 Feat: Request Access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
5894df4370 Draft 2024-05-07 17:30:35 +02:00
Daniel Hougaard
2aacd54116 Update SpecificPrivilegeSection.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
73d9fcc0de Draft 2024-05-07 17:30:35 +02:00
Tuan Dang
7ac3bb20df Update instance recognition of offline license 2024-05-07 17:30:35 +02:00
Daniel Hougaard
d659b5a624 Fix: Duplicate access request check 2024-05-07 17:30:35 +02:00
Daniel Hougaard
0bbdf2a8f4 Update SecretApprovalPage.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
a8eba9cfbf Fix: Moved from email to username 2024-05-07 17:30:35 +02:00
Daniel Hougaard
a3d7c5f599 Cleanup 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c325674da0 Fix: Move standalone components to individual files 2024-05-07 17:30:35 +02:00
Daniel Hougaard
3637152a6b Chore: Remove unused files 2024-05-07 17:30:35 +02:00
Daniel Hougaard
8ed3c0cd68 Fix: Use username instead of email 2024-05-07 17:30:35 +02:00
Daniel Hougaard
cdd836d58f Fix: Columns 2024-05-07 17:30:35 +02:00
Daniel Hougaard
3d3b1eb21a Fix: Use username instead of email 2024-05-07 17:30:35 +02:00
Daniel Hougaard
6aab28c4c7 Feat: Badge component 2024-05-07 17:30:35 +02:00
Daniel Hougaard
f038b28c1c Fix: Moved Divider to v2 2024-05-07 17:30:35 +02:00
Daniel Hougaard
24a286e898 Update index.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
0c1103e778 Fix: Pick 2024-05-07 17:30:35 +02:00
Daniel Hougaard
2c1eecaf85 Chore: Moved verifyApprovers 2024-05-07 17:30:35 +02:00
Daniel Hougaard
5884565de7 Fix: Make verifyApprovers independent on memberships 2024-05-07 17:30:35 +02:00
Daniel Hougaard
dd43268506 Fix: Made API endpoints more REST compliant 2024-05-07 17:30:35 +02:00
Daniel Hougaard
9d362b8597 Chore: Cleaned up models 2024-05-07 17:30:35 +02:00
Daniel Hougaard
972ecc3e92 Fix: Improved migrations 2024-05-07 17:30:35 +02:00
Daniel Hougaard
dc3014409f Delete access-approval-request-secret-dal.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
4e449f62c0 Fix: Don't display requested by when user has no access to read workspace members 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c911a7cd81 Fix: Don't display requested by when user has no access to read workspace members 2024-05-07 17:30:35 +02:00
Daniel Hougaard
44370d49e3 Fix: Add tooltip for clarity and fix wording 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c7d2dfd351 Fix: Requesting approvals on previously rejected resources 2024-05-07 17:30:35 +02:00
Daniel Hougaard
1785548a40 Fix: Sort by createdAt 2024-05-07 17:30:35 +02:00
Daniel Hougaard
2baf9e0739 Migration improvements 2024-05-07 17:30:35 +02:00
Daniel Hougaard
01e7ed23ba Fixed bugs 2024-05-07 17:30:35 +02:00
Daniel Hougaard
1f789110e3 Update SecretApprovalPage.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c874c943c1 Fix: Rebase errors 2024-05-07 17:30:35 +02:00
Daniel Hougaard
dab69dcb51 Removed unnessecary types 2024-05-07 17:30:35 +02:00
Daniel Hougaard
8e82bfae86 Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
bc810ea567 Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
22470376d9 Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Vladyslav Matsiiako
bb9503471f style changes 2024-05-07 17:30:35 +02:00
Daniel Hougaard
a687b1d0db Update licence-fns.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
0aa77f90c8 Update SpecificPrivilegeSection.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
5a04371fb0 Update generate-schema-types.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
70c06c91c8 Update SecretApprovalPage.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
926d324ae3 Fix: Added support for request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
e48377dea9 Fix: Remove redundant code 2024-05-07 17:30:35 +02:00
Daniel Hougaard
5e1484bd05 Fix: Validate approvers access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
6d9de752d7 Feat: Request access (new routes) 2024-05-07 17:30:35 +02:00
Daniel Hougaard
f9a9b1222e Feat: Request Access (migrations) 2024-05-07 17:30:35 +02:00
Daniel Hougaard
4326ce970a Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
7a3a9ca9ea Draft 2024-05-07 17:30:35 +02:00
Daniel Hougaard
32a110e0ca Fix: Multiple approvers acceptance bug 2024-05-07 17:30:35 +02:00
Daniel Hougaard
da5278f6bf Fix: Rename change -> secret 2024-05-07 17:30:35 +02:00
Daniel Hougaard
7e765681cb Style: Fix styling 2024-05-07 17:30:35 +02:00
Daniel Hougaard
0990ce1f92 Capitalization 2024-05-07 17:30:35 +02:00
Daniel Hougaard
2369ff6813 Removed unnessecary types 2024-05-07 17:30:35 +02:00
Daniel Hougaard
478520f090 Remove unnessecary types and projectMembershipid 2024-05-07 17:30:35 +02:00
Daniel Hougaard
54313f9c08 Renaming 2024-05-07 17:30:35 +02:00
Daniel Hougaard
cb8763bc9c Update smtp-service.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c5d11eee7f Feat: Find users by project membership ID's 2024-05-07 17:30:35 +02:00
Daniel Hougaard
8e1d19c041 Feat: access request emails 2024-05-07 17:30:35 +02:00
Daniel Hougaard
608c7a4dee Update index.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
c7b60bcf0e Update access-approval-request-types.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
6ae62675be Feat: Send emails for access requests 2024-05-07 17:30:35 +02:00
Daniel Hougaard
fb2ab200b9 Feat: Request access, extract permission details 2024-05-07 17:30:35 +02:00
Daniel Hougaard
f1428d72c2 Fix: Security vulnurbility making it possible to spoof env & secret path requested. 2024-05-07 17:30:35 +02:00
Daniel Hougaard
4cb51805f0 Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
8c40918cef Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
3a002b921a Update AccessApprovalRequest.tsx 2024-05-07 17:30:35 +02:00
Vladyslav Matsiiako
299653528c style changes 2024-05-07 17:30:35 +02:00
Daniel Hougaard
8c256bd9c8 Fix: Status filtering & query invalidation 2024-05-07 17:30:35 +02:00
Daniel Hougaard
f8e0e01bb8 Fix: Access request query invalidation 2024-05-07 17:30:35 +02:00
Vladyslav Matsiiako
b59413ded0 fix privilegeId issue 2024-05-07 17:30:35 +02:00
Daniel Hougaard
15c747e8e8 Fix: Request access permissions 2024-05-07 17:30:35 +02:00
Daniel Hougaard
073a9ee6a4 Update licence-fns.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
d371c568f1 Add count 2024-05-07 17:30:35 +02:00
Daniel Hougaard
e6c086ab09 Fix: Don't allow users to request access to the same resource with same permissions multiple times 2024-05-07 17:30:35 +02:00
Daniel Hougaard
890c8b89be Removed unused parameter 2024-05-07 17:30:35 +02:00
Daniel Hougaard
6f4b62cfbb Removed logs 2024-05-07 17:30:35 +02:00
Daniel Hougaard
076c70f6ff Removed logs 2024-05-07 17:30:35 +02:00
Daniel Hougaard
aedc1f2441 Update SpecificPrivilegeSection.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
352d363bd4 Update generate-schema-types.ts 2024-05-07 17:30:35 +02:00
Daniel Hougaard
ac92a916b4 Update SecretApprovalPage.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
17587ff1b8 Fix: Minor fixes 2024-05-07 17:30:35 +02:00
Daniel Hougaard
7f1c8d9ff6 Create index.tsx 2024-05-07 17:30:35 +02:00
Daniel Hougaard
ac24c0f760 Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
0e95c1bcee Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
447630135b Feat: Request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
ddd6adf804 Fix: Move to project slug 2024-05-07 17:30:35 +02:00
Daniel Hougaard
a4b6d2650a Fix: Move to project slug 2024-05-07 17:30:35 +02:00
Daniel Hougaard
2f5d6b11da Fix: Move to project slug 2024-05-07 17:30:35 +02:00
Daniel Hougaard
d380b7f788 Fix: Added support for request access 2024-05-07 17:30:35 +02:00
Daniel Hougaard
7aee4fdfcd Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
83bd3a0bf4 Update index.tsx 2024-05-07 17:30:27 +02:00
Daniel Hougaard
1f68730aa3 Fix: Improve disabled Select 2024-05-07 17:30:27 +02:00
Daniel Hougaard
7fd1d72985 Fix: Access Request setup 2024-05-07 17:30:27 +02:00
Daniel Hougaard
b298eec9db Fix: Danger color not working on disabled buttons 2024-05-07 17:30:27 +02:00
Daniel Hougaard
696479a2ef Fix: Remove redundant code 2024-05-07 17:30:27 +02:00
Daniel Hougaard
ad6e2aeb9e Feat: Request Access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
ad405109a0 Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
992a82015a Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
317956a038 Fix: Types mismatch 2024-05-07 17:30:27 +02:00
Daniel Hougaard
5255c4075a Fix: Validate approvers access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
eca36f1993 Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
7e29a6a656 Fix: Access Approval Policy DAL bugs 2024-05-07 17:30:27 +02:00
Daniel Hougaard
f458e34c37 Feat: Request access (new routes) 2024-05-07 17:30:27 +02:00
Daniel Hougaard
99f5ed1f4b Fix: Move to project slug 2024-05-07 17:30:27 +02:00
Daniel Hougaard
f981c59b5c Feat: Request access (models) 2024-05-07 17:30:27 +02:00
Daniel Hougaard
a528d011c0 Feat: Request Access (migrations) 2024-05-07 17:30:27 +02:00
Daniel Hougaard
d337118803 Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
68a11db1c6 Feat: Request access 2024-05-07 17:30:27 +02:00
Daniel Hougaard
91bf6a6dad Fix: Remove logs 2024-05-07 17:30:13 +02:00
Daniel Hougaard
12c655a152 Feat: Request Access 2024-05-07 17:30:13 +02:00
Daniel Hougaard
1d2f10178f Draft 2024-05-07 17:30:13 +02:00
Tuan Dang
c5cd5047d7 Update trusted email migration file with backfill 2024-05-07 07:59:37 -07:00
Sheen Capadngan
06c103c10a misc: added handling for no changes made 2024-05-07 22:19:20 +08:00
Sheen Capadngan
b6a73459a8 misc: addressed rbac for bulk delete in overview 2024-05-07 16:37:10 +08:00
Sheen Capadngan
536f51f6ba misc: added descriptive error message 2024-05-07 15:21:17 +08:00
Sheen Capadngan
a9b72b2da3 feat: added handling of folder/secret deletion 2024-05-07 15:16:37 +08:00
Tuan Dang
e3c80309c3 Move aws auth migration file to front 2024-05-06 23:03:45 -07:00
Tuan Dang
ec3d6c20e8 Merge remote-tracking branch 'origin' into aws-iam-auth 2024-05-06 22:58:47 -07:00
Tuan Dang
5d7c0f30c8 Fix typo universal auth 2024-05-06 22:58:35 -07:00
Sheen Capadngan
a3552d00d1 feat: add multi-select in secret overview 2024-05-07 13:52:42 +08:00
Maidul Islam
c9f0ba08e1 Merge pull request #1787 from Infisical/create-pull-request/patch-1715052491
GH Action: rename new migration file timestamp
2024-05-07 01:17:35 -04:00
github-actions
308e605b6c chore: renamed new migration files to latest timestamp (gh-action) 2024-05-07 03:28:10 +00:00
Maidul Islam
4d8965eb82 Merge pull request #1762 from Infisical/groups-phase-2c
Groups Phase 2B (Trust external SAML/LDAP email option, email verification, SCIM user ID ref update)
2024-05-06 23:27:50 -04:00
Tuan Dang
0357e7c80e Put email-confirmation migration into trusted-saml-ldap-emails file 2024-05-06 19:58:58 -07:00
Tuan Dang
ba1b223655 Patch migration file hasTable ref 2024-05-06 19:44:43 -07:00
Tuan Dang
0b089e6fa6 Update aws iam auth fns filename 2024-05-06 18:35:34 -07:00
Tuan Dang
3b88a2759b Patch unsynchronized username/email for saml/scim 2024-05-06 18:27:36 -07:00
Maidul Islam
42383d5643 Merge pull request #1782 from akhilmhdh/feat/privilege-identity-api-change
Privilege identity api change
2024-05-06 15:01:02 -04:00
Akhil Mohan
d198ba1a79 feat: refactored the map unpack to a function 2024-05-06 23:27:51 +05:30
Maidul Islam
b3579cb271 rephrase text for permission schema zod 2024-05-06 13:44:39 -04:00
Tuan Dang
30ccb78c81 Merge remote-tracking branch 'origin' into groups-phase-2c 2024-05-06 09:33:36 -07:00
Maidul Islam
fdd67c89b3 Merge pull request #1783 from akhilmhdh:feat/dashboard-slug-fix
feat: debounced main page search and rolled back to old input component
2024-05-06 12:31:57 -04:00
Akhil Mohan
79e9b1b2ae feat: debounced main page search and rolled back to old input component 2024-05-06 20:43:23 +05:30
Akhil Mohan
86fd4d5fba feat: added a fixed sorted order to avoid jumps 2024-05-06 14:26:46 +05:30
Akhil Mohan
4692aa12bd feat: updated identity additional privilege permission object in api to have a proper body and explanation 2024-05-06 14:01:30 +05:30
Akhil Mohan
61a0997adc fix(ui): secret path input showing / for a valid value that comes delayed 2024-05-06 14:00:32 +05:30
Tuan Dang
c276c44c08 Finish preliminary backend endpoints / db structure for k8s auth 2024-05-05 19:14:49 -07:00
Maidul Islam
b4f1bec1a9 Merge pull request #1781 from Infisical/feature/added-secret-expand-in-raw-secret-get
feat: added secret expand option in secrets get API
2024-05-04 22:09:12 -04:00
Maidul Islam
ab79342743 rename to expandSecretReferences 2024-05-04 22:05:57 -04:00
Maidul Islam
1957531ac4 Update docker-compose.mdx 2024-05-04 21:01:19 -04:00
Sheen Capadngan
61ae0e2fc7 feat: added secret expand option in secrets get API 2024-05-04 14:42:22 +08:00
Tuan Dang
cbf8e041e9 Finish docs for AWS IAM Auth, update ARN regex 2024-05-03 17:20:44 -07:00
Tuan Dang
5c4d35e30a Merge remote-tracking branch 'origin' into aws-iam-auth 2024-05-02 22:53:14 -07:00
Tuan Dang
d5c74d558a Start docs for AWS IAM auth 2024-05-02 22:52:37 -07:00
Tuan Dang
9c002ad645 Finish preliminary AWS IAM Auth method 2024-05-02 22:42:02 -07:00
Tuan Dang
ea4ef7f7ef Merge remote-tracking branch 'origin' into groups-phase-2c 2024-04-30 21:37:48 -07:00
Tuan Dang
0482424a1c Make merge user step automatic after email verification 2024-04-30 21:33:27 -07:00
Tuan Dang
ce2a9c8640 Rename migration file 2024-04-29 11:57:30 -07:00
Tuan Dang
ac97f273e3 Merge remote-tracking branch 'origin' into groups-phase-2c 2024-04-29 11:55:53 -07:00
Tuan Dang
69c50af14e Move trust saml/ldap emails to server config 2024-04-29 11:53:28 -07:00
Tuan Dang
519403023a Pick 2024-04-28 22:04:22 -07:00
Tuan Dang
b2a976f3d4 Update groups CRUD SCIM to use orgMembershipId 2024-04-28 21:58:24 -07:00
Tuan Dang
a7af3a48d9 Continue moving SCIM userId refs to orgMembershipId 2024-04-28 19:09:12 -07:00
Tuan Dang
80da2a19aa Add TRUST_SAML_EMAILS and TRUST_LDAP_EMAILS opts 2024-04-26 22:30:07 -07:00
Tuan Dang
858a35812a Finish preliminary email validation, merge user flow w saml/ldap 2024-04-26 20:19:43 -07:00
Tuan Dang
d0cb06d875 Merge remote-tracking branch 'origin' into groups-phase-2c 2024-04-26 09:08:30 -07:00
Tuan Dang
d42f620e1b Continue user aliases 2024-04-26 09:02:10 -07:00
Tuan Dang
71e309bbcb Merge remote-tracking branch 'origin' into groups-phase-2c 2024-04-25 17:03:23 -07:00
Tuan Dang
8ff407927c Continue merge user 2024-04-25 17:02:55 -07:00
Tuan Dang
d9005e8665 Merge remote-tracking branch 'origin' into groups-phase-2c 2024-04-25 06:50:02 -07:00
Daniel Hougaard
fdf5fcad0a Update IdentityTable.tsx 2024-04-22 23:09:46 +02:00
Daniel Hougaard
a85c59e3e2 Fix: Improve user experience for machine identities 2024-04-22 22:00:37 +02:00
snyk-bot
c88923e0c6 fix: backend/package.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-MYSQL2-6670046
2024-04-22 17:59:21 +00:00
Tuan Dang
54fcc23a6c Begin groups phase 2b 2024-04-19 16:16:16 -07:00
655 changed files with 21385 additions and 19103 deletions

View File

@@ -74,21 +74,21 @@ jobs:
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
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
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-prod-platform
container-name: infisical-core-platform
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
service: infisical-core-platform
cluster: infisical-core-platform
wait-for-service-stability: true
production-postgres-deployment:
@@ -122,19 +122,19 @@ jobs:
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
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
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-prod-platform
container-name: infisical-core-platform
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-prod-platform
cluster: infisical-prod-platform
service: infisical-core-platform
cluster: infisical-core-platform
wait-for-service-stability: true

View File

@@ -40,13 +40,14 @@ jobs:
REDIS_URL: redis://172.17.0.1:6379
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
JWT_AUTH_SECRET: something-random
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
- uses: actions/setup-go@v5
with:
go-version: '1.21.5'
- name: Wait for container to be stable and check logs
run: |
SECONDS=0
HEALTHY=0
r HEALTHY=0
while [ $SECONDS -lt 60 ]; do
if docker ps | grep infisical-api | grep -q healthy; then
echo "Container is healthy."
@@ -73,4 +74,4 @@ jobs:
run: |
docker-compose -f "docker-compose.dev.yml" down
docker stop infisical-api
docker remove infisical-api
docker remove infisical-api

View File

@@ -38,6 +38,16 @@ jobs:
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
@@ -46,3 +56,4 @@ jobs:
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 }}

View File

@@ -3,4 +3,5 @@ frontend/src/views/Project/MembersPage/components/IdentityTab/components/Identit
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

View File

@@ -1,7 +1,6 @@
ARG POSTHOG_HOST=https://app.posthog.com
ARG POSTHOG_API_KEY=posthog-api-key
ARG INTERCOM_ID=intercom-id
ARG SAML_ORG_SLUG=saml-org-slug-default
FROM node:20-alpine AS base
@@ -35,9 +34,7 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY $POSTHOG_API_KEY
ARG INTERCOM_ID
ENV NEXT_PUBLIC_INTERCOM_ID $INTERCOM_ID
ARG 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
ENV NEXT_PUBLIC_INFISICAL_PLATFORM_VERSION $INFISICAL_PLATFORM_VERSION
# Build
RUN npm run build
@@ -55,6 +52,7 @@ VOLUME /app/.next/cache/images
COPY --chown=non-root-user:nodejs --chmod=555 frontend/scripts ./scripts
COPY --from=frontend-builder /app/public ./public
RUN chown non-root-user:nodejs ./public/data
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/standalone ./
COPY --from=frontend-builder --chown=non-root-user:nodejs /app/.next/static ./.next/static
@@ -93,9 +91,18 @@ RUN mkdir frontend-build
# Production stage
FROM base AS production
RUN apk add --upgrade --no-cache ca-certificates
RUN addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 non-root-user
# Give non-root-user permission to update SSL certs
RUN chown -R non-root-user /etc/ssl/certs
RUN chown non-root-user /etc/ssl/certs/ca-certificates.crt
RUN chmod -R u+rwx /etc/ssl/certs
RUN chmod u+rw /etc/ssl/certs/ca-certificates.crt
RUN chown non-root-user /usr/sbin/update-ca-certificates
RUN chmod u+rx /usr/sbin/update-ca-certificates
## set pre baked keys
ARG POSTHOG_API_KEY
ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
@@ -103,9 +110,6 @@ ENV NEXT_PUBLIC_POSTHOG_API_KEY=$POSTHOG_API_KEY \
ARG INTERCOM_ID=intercom-id
ENV 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 /

View File

@@ -1,4 +1,5 @@
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { Lock } from "@app/lib/red-lock";
export const mockKeyStore = (): TKeyStoreFactory => {
const store: Record<string, string | number | Buffer> = {};
@@ -25,6 +26,12 @@ export const mockKeyStore = (): TKeyStoreFactory => {
},
incrementBy: async () => {
return 1;
}
},
acquireLock: () => {
return Promise.resolve({
release: () => {}
}) as Promise<Lock>;
},
waitTillReady: async () => {}
};
};

File diff suppressed because it is too large Load Diff

View File

@@ -95,11 +95,13 @@
"axios": "^1.6.7",
"axios-retry": "^4.0.0",
"bcrypt": "^5.1.1",
"bullmq": "^5.3.3",
"bullmq": "^5.4.2",
"cassandra-driver": "^4.7.2",
"dotenv": "^16.4.1",
"fastify": "^4.26.0",
"fastify-plugin": "^4.5.1",
"google-auth-library": "^9.9.0",
"googleapis": "^137.1.0",
"handlebars": "^4.7.8",
"ioredis": "^5.3.2",
"jmespath": "^0.16.0",
@@ -110,7 +112,7 @@
"libsodium-wrappers": "^0.7.13",
"lodash.isequal": "^4.5.0",
"ms": "^2.1.3",
"mysql2": "^3.9.4",
"mysql2": "^3.9.8",
"nanoid": "^5.0.4",
"nodemailer": "^6.9.9",
"ora": "^7.0.1",

View File

@@ -35,6 +35,8 @@ const getZodPrimitiveType = (type: string) => {
return "z.coerce.number()";
case "text":
return "z.string()";
case "bytea":
return "zodBuffer";
default:
throw new Error(`Invalid type: ${type}`);
}
@@ -96,10 +98,15 @@ const main = async () => {
const columnNames = Object.keys(columns);
let schema = "";
const zodImportSet = new Set<string>();
for (let colNum = 0; colNum < columnNames.length; colNum++) {
const columnName = columnNames[colNum];
const colInfo = columns[columnName];
let ztype = getZodPrimitiveType(colInfo.type);
if (["zodBuffer"].includes(ztype)) {
zodImportSet.add(ztype);
}
// don't put optional on id
if (colInfo.defaultValue && columnName !== "id") {
const { defaultValue } = colInfo;
@@ -121,6 +128,8 @@ const main = async () => {
.split("_")
.reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
const zodImports = Array.from(zodImportSet);
// the insert and update are changed to zod input type to use default cases
writeFileSync(
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
@@ -131,6 +140,8 @@ const main = async () => {
import { z } from "zod";
${zodImports.length ? `import { ${zodImports.join(",")} } from \"@app/lib/zod\";` : ""}
import { TImmutableDBKeys } from "./models";
export const ${pascalCase}Schema = z.object({${schema}});

View File

@@ -32,6 +32,10 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-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 { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
@@ -48,6 +52,8 @@ import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
@@ -103,6 +109,7 @@ declare module "fastify" {
projectKey: TProjectKeyServiceFactory;
projectRole: TProjectRoleServiceFactory;
secret: TSecretServiceFactory;
secretReplication: TSecretReplicationServiceFactory;
secretTag: TSecretTagServiceFactory;
secretImport: TSecretImportServiceFactory;
projectBot: TProjectBotServiceFactory;
@@ -115,6 +122,10 @@ declare module "fastify" {
identityAccessToken: TIdentityAccessTokenServiceFactory;
identityProject: TIdentityProjectServiceFactory;
identityUa: TIdentityUaServiceFactory;
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
identityGcpAuth: TIdentityGcpAuthServiceFactory;
identityAwsAuth: TIdentityAwsAuthServiceFactory;
identityAzureAuth: TIdentityAzureAuthServiceFactory;
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
@@ -135,6 +146,7 @@ declare module "fastify" {
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
secretSharing: TSecretSharingServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@@ -50,9 +50,6 @@ import {
TGroupProjectMemberships,
TGroupProjectMembershipsInsert,
TGroupProjectMembershipsUpdate,
TGroupProjectUserAdditionalPrivilege,
TGroupProjectUserAdditionalPrivilegeInsert,
TGroupProjectUserAdditionalPrivilegeUpdate,
TGroups,
TGroupsInsert,
TGroupsUpdate,
@@ -62,6 +59,18 @@ import {
TIdentityAccessTokens,
TIdentityAccessTokensInsert,
TIdentityAccessTokensUpdate,
TIdentityAwsAuths,
TIdentityAwsAuthsInsert,
TIdentityAwsAuthsUpdate,
TIdentityAzureAuths,
TIdentityAzureAuthsInsert,
TIdentityAzureAuthsUpdate,
TIdentityGcpAuths,
TIdentityGcpAuthsInsert,
TIdentityGcpAuthsUpdate,
TIdentityKubernetesAuths,
TIdentityKubernetesAuthsInsert,
TIdentityKubernetesAuthsUpdate,
TIdentityOrgMemberships,
TIdentityOrgMembershipsInsert,
TIdentityOrgMembershipsUpdate,
@@ -89,6 +98,15 @@ import {
TIntegrations,
TIntegrationsInsert,
TIntegrationsUpdate,
TKmsKeys,
TKmsKeysInsert,
TKmsKeysUpdate,
TKmsKeyVersions,
TKmsKeyVersionsInsert,
TKmsKeyVersionsUpdate,
TKmsRootConfig,
TKmsRootConfigInsert,
TKmsRootConfigUpdate,
TLdapConfigs,
TLdapConfigsInsert,
TLdapConfigsUpdate,
@@ -167,6 +185,9 @@ import {
TSecretImports,
TSecretImportsInsert,
TSecretImportsUpdate,
TSecretReferences,
TSecretReferencesInsert,
TSecretReferencesUpdate,
TSecretRotationOutputs,
TSecretRotationOutputsInsert,
TSecretRotationOutputsUpdate,
@@ -177,6 +198,9 @@ import {
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
TSecretScanningGitRisksUpdate,
TSecretSharing,
TSecretSharingInsert,
TSecretSharingUpdate,
TSecretsInsert,
TSecretSnapshotFolders,
TSecretSnapshotFoldersInsert,
@@ -293,11 +317,6 @@ declare module "knex/types/tables" {
TProjectUserMembershipRolesInsert,
TProjectUserMembershipRolesUpdate
>;
[TableName.GroupProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
TGroupProjectUserAdditionalPrivilege,
TGroupProjectUserAdditionalPrivilegeInsert,
TGroupProjectUserAdditionalPrivilegeUpdate
>;
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
TProjectUserAdditionalPrivilege,
@@ -306,6 +325,11 @@ declare module "knex/types/tables" {
>;
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
[TableName.SecretReference]: Knex.CompositeTableType<
TSecretReferences,
TSecretReferencesInsert,
TSecretReferencesUpdate
>;
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
@@ -318,6 +342,7 @@ declare module "knex/types/tables" {
TSecretFolderVersionsInsert,
TSecretFolderVersionsUpdate
>;
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
@@ -334,6 +359,26 @@ declare module "knex/types/tables" {
TIdentityUniversalAuthsInsert,
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<
TIdentityUaClientSecrets,
TIdentityUaClientSecretsInsert,
@@ -480,5 +525,13 @@ declare module "knex/types/tables" {
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate
>;
// KMS service
[TableName.KmsServerRootConfig]: Knex.CompositeTableType<
TKmsRootConfig,
TKmsRootConfigInsert,
TKmsRootConfigUpdate
>;
[TableName.KmsKey]: Knex.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
[TableName.KmsKeyVersion]: Knex.CompositeTableType<TKmsKeyVersions, TKmsKeyVersionsInsert, TKmsKeyVersionsUpdate>;
}
}

View File

@@ -1,37 +0,0 @@
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.GroupProjectUserAdditionalPrivilege))) {
await knex.schema.createTable(TableName.GroupProjectUserAdditionalPrivilege, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("slug", 60).notNullable();
t.uuid("groupProjectMembershipId").notNullable();
t.foreign("groupProjectMembershipId")
.references("id")
.inTable(TableName.GroupProjectMembership)
.onDelete("CASCADE");
t.uuid("requestedByUserId").notNullable();
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
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.jsonb("permissions").notNullable();
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
await knex.schema.dropTableIfExists(TableName.GroupProjectUserAdditionalPrivilege);
}

View File

@@ -1,71 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
// SecretApprovalPolicyApprover, approverUserId
if (!(await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId"))) {
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (t) => {
t.uuid("approverId").nullable().alter();
t.uuid("approverUserId").nullable();
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
// SecretApprovalRequest, statusChangeByUserId
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeByUserId"))) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
t.uuid("statusChangeBy").nullable().alter();
t.uuid("statusChangeByUserId").nullable();
t.foreign("statusChangeByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
});
}
// SecretApprovalRequest, committerUserId
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId"))) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
t.uuid("committerId").nullable().alter();
t.uuid("committerUserId").nullable();
t.foreign("committerUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
// SecretApprovalRequestReviewer, memberUserId
if (!(await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "memberUserId"))) {
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (t) => {
t.uuid("member").nullable().alter();
t.uuid("memberUserId").nullable();
t.foreign("memberUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.SecretApprovalPolicyApprover, "approverUserId")) {
await knex.schema.alterTable(TableName.SecretApprovalPolicyApprover, (t) => {
t.dropColumn("approverUserId");
});
}
if (await knex.schema.hasColumn(TableName.SecretApprovalRequest, "statusChangeByUserId")) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
t.dropColumn("statusChangeByUserId");
});
}
if (await knex.schema.hasColumn(TableName.SecretApprovalRequest, "committerUserId")) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
t.dropColumn("committerUserId");
});
}
if (await knex.schema.hasColumn(TableName.SecretApprovalRequestReviewer, "memberUserId")) {
await knex.schema.alterTable(TableName.SecretApprovalRequestReviewer, (t) => {
t.dropColumn("memberUserId");
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -9,8 +9,9 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.integer("approvals").defaultTo(1).notNullable();
t.uuid("envId").notNullable();
t.string("secretPath");
t.uuid("envId").notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.timestamps(true, true, true);
});
@@ -20,9 +21,8 @@ export async function up(knex: Knex): Promise<void> {
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("approverUserId").nullable();
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
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");

View File

@@ -11,26 +11,11 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("policyId").notNullable();
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
t.uuid("projectUserPrivilegeId").nullable();
t.foreign("projectUserPrivilegeId")
.references("id")
.inTable(TableName.ProjectUserAdditionalPrivilege)
.onDelete("CASCADE");
t.uuid("privilegeId").nullable();
t.foreign("privilegeId").references("id").inTable(TableName.ProjectUserAdditionalPrivilege).onDelete("CASCADE");
t.uuid("groupProjectUserPrivilegeId").nullable();
t.foreign("groupProjectUserPrivilegeId")
.references("id")
.inTable(TableName.GroupProjectUserAdditionalPrivilege)
.onDelete("CASCADE");
t.uuid("requestedByUserId").notNullable();
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("projectMembershipId").nullable();
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.uuid("groupMembershipId").nullable();
t.foreign("groupMembershipId").references("id").inTable(TableName.GroupProjectMembership).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();
@@ -46,17 +31,14 @@ export async function up(knex: Knex): Promise<void> {
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("memberUserId").notNullable();
t.foreign("memberUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View 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);
}

View File

@@ -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");
}
});
}

View File

@@ -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");
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -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"]);
});
}
}

View File

@@ -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");
});
}
}

View File

@@ -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);
}

View File

@@ -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");
}
});
}

View File

@@ -0,0 +1,29 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretSharing))) {
await knex.schema.createTable(TableName.SecretSharing, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.text("encryptedValue").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.text("hashedHex").notNullable();
t.timestamp("expiresAt").notNullable();
t.uuid("userId").notNullable();
t.uuid("orgId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.SecretSharing);
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretSharing);
}

View File

@@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesSecretVersionIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "secretVersionId");
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
if (doesSecretVersionIdExist) t.index("secretVersionId");
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesSecretVersionIdExist = await knex.schema.hasColumn(TableName.SnapshotSecret, "secretVersionId");
if (await knex.schema.hasTable(TableName.SnapshotSecret)) {
await knex.schema.alterTable(TableName.SnapshotSecret, (t) => {
if (doesSecretVersionIdExist) t.dropIndex("secretVersionId");
});
}
}

View File

@@ -0,0 +1,29 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretSharing))) {
await knex.schema.createTable(TableName.SecretSharing, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.text("encryptedValue").notNullable();
t.text("iv").notNullable();
t.text("tag").notNullable();
t.text("hashedHex").notNullable();
t.timestamp("expiresAt").notNullable();
t.uuid("userId").notNullable();
t.uuid("orgId").notNullable();
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.SecretSharing);
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretSharing);
}

View File

@@ -0,0 +1,33 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasExpiresAfterViewsColumn = await knex.schema.hasColumn(TableName.SecretSharing, "expiresAfterViews");
const hasSecretNameColumn = await knex.schema.hasColumn(TableName.SecretSharing, "name");
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
if (!hasExpiresAfterViewsColumn) {
t.integer("expiresAfterViews");
}
if (hasSecretNameColumn) {
t.dropColumn("name");
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasExpiresAfterViewsColumn = await knex.schema.hasColumn(TableName.SecretSharing, "expiresAfterViews");
const hasSecretNameColumn = await knex.schema.hasColumn(TableName.SecretSharing, "name");
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
if (hasExpiresAfterViewsColumn) {
t.dropColumn("expiresAfterViews");
}
if (!hasSecretNameColumn) {
t.string("name").notNullable();
}
});
}

View File

@@ -0,0 +1,85 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesSecretImportIsReplicationExist = await knex.schema.hasColumn(TableName.SecretImport, "isReplication");
const doesSecretImportIsReplicationSuccessExist = await knex.schema.hasColumn(
TableName.SecretImport,
"isReplicationSuccess"
);
const doesSecretImportReplicationStatusExist = await knex.schema.hasColumn(
TableName.SecretImport,
"replicationStatus"
);
const doesSecretImportLastReplicatedExist = await knex.schema.hasColumn(TableName.SecretImport, "lastReplicated");
const doesSecretImportIsReservedExist = await knex.schema.hasColumn(TableName.SecretImport, "isReserved");
if (await knex.schema.hasTable(TableName.SecretImport)) {
await knex.schema.alterTable(TableName.SecretImport, (t) => {
if (!doesSecretImportIsReplicationExist) t.boolean("isReplication").defaultTo(false);
if (!doesSecretImportIsReplicationSuccessExist) t.boolean("isReplicationSuccess").nullable();
if (!doesSecretImportReplicationStatusExist) t.text("replicationStatus").nullable();
if (!doesSecretImportLastReplicatedExist) t.datetime("lastReplicated").nullable();
if (!doesSecretImportIsReservedExist) t.boolean("isReserved").defaultTo(false);
});
}
const doesSecretFolderReservedExist = await knex.schema.hasColumn(TableName.SecretFolder, "isReserved");
if (await knex.schema.hasTable(TableName.SecretFolder)) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
if (!doesSecretFolderReservedExist) t.boolean("isReserved").defaultTo(false);
});
}
const doesSecretApprovalRequestIsReplicatedExist = await knex.schema.hasColumn(
TableName.SecretApprovalRequest,
"isReplicated"
);
if (await knex.schema.hasTable(TableName.SecretApprovalRequest)) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
if (!doesSecretApprovalRequestIsReplicatedExist) t.boolean("isReplicated");
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesSecretImportIsReplicationExist = await knex.schema.hasColumn(TableName.SecretImport, "isReplication");
const doesSecretImportIsReplicationSuccessExist = await knex.schema.hasColumn(
TableName.SecretImport,
"isReplicationSuccess"
);
const doesSecretImportReplicationStatusExist = await knex.schema.hasColumn(
TableName.SecretImport,
"replicationStatus"
);
const doesSecretImportLastReplicatedExist = await knex.schema.hasColumn(TableName.SecretImport, "lastReplicated");
const doesSecretImportIsReservedExist = await knex.schema.hasColumn(TableName.SecretImport, "isReserved");
if (await knex.schema.hasTable(TableName.SecretImport)) {
await knex.schema.alterTable(TableName.SecretImport, (t) => {
if (doesSecretImportIsReplicationExist) t.dropColumn("isReplication");
if (doesSecretImportIsReplicationSuccessExist) t.dropColumn("isReplicationSuccess");
if (doesSecretImportReplicationStatusExist) t.dropColumn("replicationStatus");
if (doesSecretImportLastReplicatedExist) t.dropColumn("lastReplicated");
if (doesSecretImportIsReservedExist) t.dropColumn("isReserved");
});
}
const doesSecretFolderReservedExist = await knex.schema.hasColumn(TableName.SecretFolder, "isReserved");
if (await knex.schema.hasTable(TableName.SecretFolder)) {
await knex.schema.alterTable(TableName.SecretFolder, (t) => {
if (doesSecretFolderReservedExist) t.dropColumn("isReserved");
});
}
const doesSecretApprovalRequestIsReplicatedExist = await knex.schema.hasColumn(
TableName.SecretApprovalRequest,
"isReplicated"
);
if (await knex.schema.hasTable(TableName.SecretApprovalRequest)) {
await knex.schema.alterTable(TableName.SecretApprovalRequest, (t) => {
if (doesSecretApprovalRequestIsReplicatedExist) t.dropColumn("isReplicated");
});
}
}

View File

@@ -0,0 +1,56 @@
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.KmsServerRootConfig))) {
await knex.schema.createTable(TableName.KmsServerRootConfig, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.binary("encryptedRootKey").notNullable();
});
}
await createOnUpdateTrigger(knex, TableName.KmsServerRootConfig);
if (!(await knex.schema.hasTable(TableName.KmsKey))) {
await knex.schema.createTable(TableName.KmsKey, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.binary("encryptedKey").notNullable();
t.string("encryptionAlgorithm").notNullable();
t.integer("version").defaultTo(1).notNullable();
t.string("description");
t.boolean("isDisabled").defaultTo(false);
t.boolean("isReserved").defaultTo(true);
t.string("projectId");
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("orgId");
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.KmsKey);
if (!(await knex.schema.hasTable(TableName.KmsKeyVersion))) {
await knex.schema.createTable(TableName.KmsKeyVersion, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.binary("encryptedKey").notNullable();
t.integer("version").notNullable();
t.uuid("kmsKeyId").notNullable();
t.foreign("kmsKeyId").references("id").inTable(TableName.KmsKey).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.KmsKeyVersion);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.KmsServerRootConfig);
await dropOnUpdateTrigger(knex, TableName.KmsServerRootConfig);
await knex.schema.dropTableIfExists(TableName.KmsKeyVersion);
await dropOnUpdateTrigger(knex, TableName.KmsKeyVersion);
await knex.schema.dropTableIfExists(TableName.KmsKey);
await dropOnUpdateTrigger(knex, TableName.KmsKey);
}

View File

@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
export const AccessApprovalPoliciesApproversSchema = z.object({
id: z.string().uuid(),
approverUserId: z.string().uuid().nullable().optional(),
approverId: z.string().uuid(),
policyId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()

View File

@@ -11,8 +11,8 @@ export const AccessApprovalPoliciesSchema = z.object({
id: z.string().uuid(),
name: z.string(),
approvals: z.number().default(1),
envId: z.string().uuid(),
secretPath: z.string().nullable().optional(),
envId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});

View File

@@ -9,7 +9,7 @@ import { TImmutableDBKeys } from "./models";
export const AccessApprovalRequestsReviewersSchema = z.object({
id: z.string().uuid(),
memberUserId: z.string().uuid(),
member: z.string().uuid(),
status: z.string(),
requestId: z.string().uuid(),
createdAt: z.date(),

View File

@@ -10,11 +10,8 @@ import { TImmutableDBKeys } from "./models";
export const AccessApprovalRequestsSchema = z.object({
id: z.string().uuid(),
policyId: z.string().uuid(),
projectUserPrivilegeId: z.string().uuid().nullable().optional(),
groupProjectUserPrivilegeId: z.string().uuid().nullable().optional(),
requestedByUserId: z.string().uuid(),
projectMembershipId: z.string().uuid().nullable().optional(),
groupMembershipId: z.string().uuid().nullable().optional(),
privilegeId: z.string().uuid().nullable().optional(),
requestedBy: z.string().uuid(),
isTemporary: z.boolean(),
temporaryRange: z.string().nullable().optional(),
permissions: z.unknown(),

View File

@@ -1,32 +0,0 @@
// 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 GroupProjectUserAdditionalPrivilegeSchema = z.object({
id: z.string().uuid(),
slug: z.string(),
groupProjectMembershipId: z.string().uuid(),
requestedByUserId: z.string().uuid(),
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(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TGroupProjectUserAdditionalPrivilege = z.infer<typeof GroupProjectUserAdditionalPrivilegeSchema>;
export type TGroupProjectUserAdditionalPrivilegeInsert = Omit<
z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>,
TImmutableDBKeys
>;
export type TGroupProjectUserAdditionalPrivilegeUpdate = Partial<
Omit<z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>, TImmutableDBKeys>
>;

View 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>>;

View 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>>;

View 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>>;

View 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>
>;

View File

@@ -14,10 +14,13 @@ export * from "./git-app-install-sessions";
export * from "./git-app-org";
export * from "./group-project-membership-roles";
export * from "./group-project-memberships";
export * from "./group-project-user-additional-privilege";
export * from "./groups";
export * from "./identities";
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-project-additional-privilege";
export * from "./identity-project-membership-role";
@@ -27,6 +30,9 @@ export * from "./identity-universal-auths";
export * from "./incident-contacts";
export * from "./integration-auths";
export * from "./integrations";
export * from "./kms-key-versions";
export * from "./kms-keys";
export * from "./kms-root-config";
export * from "./ldap-configs";
export * from "./ldap-group-maps";
export * from "./models";
@@ -54,9 +60,11 @@ export * from "./secret-blind-indexes";
export * from "./secret-folder-versions";
export * from "./secret-folders";
export * from "./secret-imports";
export * from "./secret-references";
export * from "./secret-rotation-outputs";
export * from "./secret-rotations";
export * from "./secret-scanning-git-risks";
export * from "./secret-sharing";
export * from "./secret-snapshot-folders";
export * from "./secret-snapshot-secrets";
export * from "./secret-snapshots";

View File

@@ -28,7 +28,10 @@ export const IntegrationsSchema = z.object({
secretPath: z.string().default("/"),
createdAt: 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>;

View 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 { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsKeyVersionsSchema = z.object({
id: z.string().uuid(),
encryptedKey: zodBuffer,
version: z.number(),
kmsKeyId: z.string().uuid()
});
export type TKmsKeyVersions = z.infer<typeof KmsKeyVersionsSchema>;
export type TKmsKeyVersionsInsert = Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>;
export type TKmsKeyVersionsUpdate = Partial<Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>>;

View 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 { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsKeysSchema = z.object({
id: z.string().uuid(),
encryptedKey: zodBuffer,
encryptionAlgorithm: z.string(),
version: z.number().default(1),
description: z.string().nullable().optional(),
isDisabled: z.boolean().default(false).nullable().optional(),
isReserved: z.boolean().default(true).nullable().optional(),
projectId: z.string().nullable().optional(),
orgId: z.string().uuid().nullable().optional()
});
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
export type TKmsKeysInsert = Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>;
export type TKmsKeysUpdate = Partial<Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>>;

View 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 { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsRootConfigSchema = z.object({
id: z.string().uuid(),
encryptedRootKey: zodBuffer
});
export type TKmsRootConfig = z.infer<typeof KmsRootConfigSchema>;
export type TKmsRootConfigInsert = Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>;
export type TKmsRootConfigUpdate = Partial<Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>>;

View File

@@ -25,10 +25,11 @@ export enum TableName {
ProjectMembership = "project_memberships",
ProjectRoles = "project_roles",
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
GroupProjectUserAdditionalPrivilege = "group_project_user_additional_privilege",
ProjectUserMembershipRole = "project_user_membership_roles",
ProjectKeys = "project_keys",
Secret = "secrets",
SecretReference = "secret_references",
SecretSharing = "secret_sharing",
SecretBlindIndex = "secret_blind_indexes",
SecretVersion = "secret_versions",
SecretFolder = "secret_folders",
@@ -45,7 +46,11 @@ export enum TableName {
Identity = "identities",
IdentityAccessToken = "identity_access_tokens",
IdentityUniversalAuth = "identity_universal_auths",
IdentityKubernetesAuth = "identity_kubernetes_auths",
IdentityGcpAuth = "identity_gcp_auths",
IdentityAzureAuth = "identity_azure_auths",
IdentityUaClientSecret = "identity_ua_client_secrets",
IdentityAwsAuth = "identity_aws_auths",
IdentityOrgMembership = "identity_org_memberships",
IdentityProjectMembership = "identity_project_memberships",
IdentityProjectMembershipRole = "identity_project_membership_role",
@@ -76,7 +81,11 @@ export enum TableName {
DynamicSecretLease = "dynamic_secret_leases",
// junction tables with tags
JnSecretTag = "secret_tag_junction",
SecretVersionTag = "secret_version_tag_junction"
SecretVersionTag = "secret_version_tag_junction",
// KMS Service
KmsServerRootConfig = "kms_root_config",
KmsKey = "kms_keys",
KmsKeyVersion = "kms_key_versions"
}
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
@@ -143,5 +152,9 @@ export enum ProjectUpgradeStatus {
}
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"
}

View File

@@ -9,11 +9,10 @@ import { TImmutableDBKeys } from "./models";
export const SecretApprovalPoliciesApproversSchema = z.object({
id: z.string().uuid(),
approverId: z.string().uuid().nullable().optional(),
approverId: z.string().uuid(),
policyId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
approverUserId: z.string().uuid().nullable().optional()
updatedAt: z.date()
});
export type TSecretApprovalPoliciesApprovers = z.infer<typeof SecretApprovalPoliciesApproversSchema>;

View File

@@ -9,12 +9,11 @@ import { TImmutableDBKeys } from "./models";
export const SecretApprovalRequestsReviewersSchema = z.object({
id: z.string().uuid(),
member: z.string().uuid().nullable().optional(),
member: z.string().uuid(),
status: z.string(),
requestId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
memberUserId: z.string().uuid().nullable().optional()
updatedAt: z.date()
});
export type TSecretApprovalRequestsReviewers = z.infer<typeof SecretApprovalRequestsReviewersSchema>;

View File

@@ -16,11 +16,10 @@ export const SecretApprovalRequestsSchema = z.object({
slug: z.string(),
folderId: z.string().uuid(),
statusChangeBy: z.string().uuid().nullable().optional(),
committerId: z.string().uuid().nullable().optional(),
committerId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
statusChangeByUserId: z.string().uuid().nullable().optional(),
committerUserId: z.string().uuid().nullable().optional()
isReplicated: z.boolean().nullable().optional()
});
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;

View File

@@ -14,7 +14,8 @@ export const SecretFoldersSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
envId: z.string().uuid(),
parentId: z.string().uuid().nullable().optional()
parentId: z.string().uuid().nullable().optional(),
isReserved: z.boolean().default(false).nullable().optional()
});
export type TSecretFolders = z.infer<typeof SecretFoldersSchema>;

View File

@@ -15,7 +15,12 @@ export const SecretImportsSchema = z.object({
position: z.number(),
createdAt: z.date(),
updatedAt: z.date(),
folderId: z.string().uuid()
folderId: z.string().uuid(),
isReplication: z.boolean().default(false).nullable().optional(),
isReplicationSuccess: z.boolean().nullable().optional(),
replicationStatus: z.string().nullable().optional(),
lastReplicated: z.date().nullable().optional(),
isReserved: z.boolean().default(false).nullable().optional()
});
export type TSecretImports = z.infer<typeof SecretImportsSchema>;

View 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>>;

View 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 SecretSharingSchema = z.object({
id: z.string().uuid(),
encryptedValue: z.string(),
iv: z.string(),
tag: z.string(),
hashedHex: z.string(),
expiresAt: z.date(),
userId: z.string().uuid(),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
expiresAfterViews: z.number().nullable().optional()
});
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
export type TSecretSharingInsert = Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>;
export type TSecretSharingUpdate = Partial<Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>>;

View File

@@ -14,7 +14,9 @@ export const SuperAdminSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
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>;

View File

@@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
export const UserAliasesSchema = z.object({
id: z.string().uuid(),
userId: z.string().uuid(),
username: z.string(),
username: z.string().nullable().optional(),
aliasType: z.string(),
externalId: z.string(),
emails: z.string().array().nullable().optional(),

View File

@@ -21,7 +21,11 @@ export const UsersSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
isGhost: z.boolean().default(false),
username: z.string()
username: z.string(),
isEmailVerified: z.boolean().default(false).nullable().optional(),
consecutiveFailedMfaAttempts: z.number().default(0).nullable().optional(),
isLocked: z.boolean().default(false).nullable().optional(),
temporaryLockDateEnd: z.date().nullable().optional()
});
export type TUsers = z.infer<typeof UsersSchema>;

View File

@@ -53,9 +53,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
}),
response: {
200: z.object({
approvals: sapPubSchema
.extend({ approvers: z.string().nullish().array(), secretPath: z.string().optional() })
.array()
approvals: sapPubSchema.extend({ approvers: z.string().array(), secretPath: z.string().optional() }).array()
})
}
},

View File

@@ -74,7 +74,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
schema: {
querystring: z.object({
projectSlug: z.string().trim(),
authorUserId: z.string().trim().optional(),
authorProjectMembershipId: z.string().trim().optional(),
envSlug: z.string().trim().optional()
}),
response: {
@@ -84,8 +84,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
isApproved: z.boolean(),
privilege: z
.object({
projectMembershipId: z.string().nullish(),
groupMembershipId: z.string().nullish(),
membershipId: z.string(),
isTemporary: z.boolean(),
temporaryMode: z.string().nullish(),
temporaryRange: z.string().nullish(),
@@ -116,8 +115,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
handler: async (req) => {
const { requests } = await server.services.accessApprovalRequest.listApprovalRequests({
projectSlug: req.query.projectSlug,
authorProjectMembershipId: req.query.authorProjectMembershipId,
envSlug: req.query.envSlug,
authorUserId: req.query.authorUserId,
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
@@ -128,37 +127,6 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
}
});
server.route({
url: "/:requestId",
method: "DELETE",
schema: {
params: z.object({
requestId: z.string().trim()
}),
querystring: z.object({
projectSlug: z.string().trim()
}),
response: {
200: z.object({
request: AccessApprovalRequestsSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { request } = await server.services.accessApprovalRequest.deleteAccessApprovalRequest({
actor: req.permission.type,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
requestId: req.params.requestId,
projectSlug: req.query.projectSlug
});
return { request };
}
});
server.route({
url: "/:requestId/review",
method: "POST",

View File

@@ -1,16 +1,19 @@
import { MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import { packRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify";
import ms from "ms";
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 { ProjectPermissionSet } from "@app/ee/services/permission/project-permission";
import { IDENTITY_ADDITIONAL_PRIVILEGE } from "@app/lib/api-docs";
import { BadRequestError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import {
ProjectPermissionSchema,
ProjectSpecificPrivilegePermissionSchema,
SanitizedIdentityPrivilegeSchema
} from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: FastifyZodProvider) => {
@@ -41,16 +44,33 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
})
.optional()
.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)
.optional(),
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
).optional()
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
privilege: SanitizedIdentityPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { permissions, privilegePermission } = req.body;
if (!permissions && !privilegePermission) {
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
}
const permission = privilegePermission
? privilegePermission.actions.map((action) => ({
action,
subject: privilegePermission.subject,
conditions: privilegePermission.conditions
}))
: permissions!;
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
@@ -59,7 +79,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: false,
permissions: JSON.stringify(packRules(req.body.permissions))
permissions: JSON.stringify(packRules(permission))
});
return { privilege };
}
@@ -92,7 +112,12 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
})
.optional()
.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)
.optional(),
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.privilegePermission
).optional(),
temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.CREATE.temporaryMode),
@@ -107,12 +132,25 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
privilege: SanitizedIdentityPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { permissions, privilegePermission } = req.body;
if (!permissions && !privilegePermission) {
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
}
const permission = privilegePermission
? privilegePermission.actions.map((action) => ({
action,
subject: privilegePermission.subject,
conditions: privilegePermission.conditions
}))
: permissions!;
const privilege = await server.services.identityProjectAdditionalPrivilege.create({
actorId: req.permission.id,
actor: req.permission.type,
@@ -121,7 +159,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
...req.body,
slug: req.body.slug ? slugify(req.body.slug) : slugify(alphaNumericNanoId(12)),
isTemporary: true,
permissions: JSON.stringify(packRules(req.body.permissions))
permissions: JSON.stringify(packRules(permission))
});
return { privilege };
}
@@ -157,14 +195,17 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
message: "Slug must be a valid slug"
})
.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),
privilegePermission: ProjectSpecificPrivilegePermissionSchema.describe(
IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.privilegePermission
).optional(),
isTemporary: z.boolean().describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.isTemporary),
temporaryMode: z
.nativeEnum(IdentityProjectAdditionalPrivilegeTemporaryMode)
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryMode),
temporaryRange: z
.string()
.refine((val) => ms(val) > 0, "Temporary range must be a positive number")
.refine((val) => typeof val === "undefined" || ms(val) > 0, "Temporary range must be a positive number")
.describe(IDENTITY_ADDITIONAL_PRIVILEGE.UPDATE.temporaryRange),
temporaryAccessStartTime: z
.string()
@@ -175,13 +216,24 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
privilege: SanitizedIdentityPrivilegeSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const updatedInfo = req.body.privilegeDetails;
const { permissions, privilegePermission, ...updatedInfo } = req.body.privilegeDetails;
if (!permissions && !privilegePermission) {
throw new BadRequestError({ message: "Permission or privilegePermission must be provided" });
}
const permission = privilegePermission
? privilegePermission.actions.map((action) => ({
action,
subject: privilegePermission.subject,
conditions: privilegePermission.conditions
}))
: permissions!;
const privilege = await server.services.identityProjectAdditionalPrivilege.updateBySlug({
actorId: req.permission.id,
actor: req.permission.type,
@@ -192,7 +244,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
projectSlug: req.body.projectSlug,
data: {
...updatedInfo,
permissions: updatedInfo?.permissions ? JSON.stringify(packRules(updatedInfo.permissions)) : undefined
permissions: permission ? JSON.stringify(packRules(permission)) : undefined
}
});
return { privilege };
@@ -219,7 +271,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
privilege: SanitizedIdentityPrivilegeSchema
})
}
},
@@ -260,7 +312,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
}),
response: {
200: z.object({
privilege: IdentityProjectAdditionalPrivilegeSchema
privilege: SanitizedIdentityPrivilegeSchema
})
}
},
@@ -293,16 +345,11 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
],
querystring: z.object({
identityId: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.identityId),
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)
projectSlug: z.string().min(1).describe(IDENTITY_ADDITIONAL_PRIVILEGE.LIST.projectSlug)
}),
response: {
200: z.object({
privileges: IdentityProjectAdditionalPrivilegeSchema.array()
privileges: SanitizedIdentityPrivilegeSchema.array()
})
}
},
@@ -315,15 +362,9 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F
actorOrgId: req.permission.orgId,
...req.query
});
if (req.query.unpacked) {
return {
privileges: privileges.map(({ permissions, ...el }) => ({
...el,
permissions: unpackRules(permissions as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
}))
};
}
return { privileges };
return {
privileges
};
}
});
};

View File

@@ -1,6 +1,6 @@
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
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 { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerGroupRouter } from "./group-router";

View File

@@ -18,6 +18,7 @@ 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 { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -52,6 +53,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line
async (req: IncomingMessage, user, cb) => {
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;
@@ -74,7 +76,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
username: user.uid,
firstName: user.givenName ?? user.cn ?? "",
lastName: user.sn ?? "",
emails: user.mail ? [user.mail] : [],
email: user.mail,
groups,
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
orgId: (req as unknown as FastifyRequest).ldapConfig.organization

View File

@@ -23,7 +23,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.min(1)
.trim()
.refine(
(val) => !Object.keys(OrgMembershipRole).includes(val),
(val) => !Object.values(OrgMembershipRole).includes(val as OrgMembershipRole),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {

View File

@@ -1,146 +1,232 @@
import { packRules } from "@casl/ability/extra";
import slugify from "@sindresorhus/slugify";
import { z } from "zod";
import { ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
import { ProjectMembershipRole, ProjectMembershipsSchema, ProjectRolesSchema } from "@app/db/schemas";
import { PROJECT_ROLE } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { ProjectPermissionSchema, SanitizedRoleSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
url: "/:projectId/roles",
url: "/:projectSlug/roles",
config: {
rateLimit: writeLimit
},
schema: {
description: "Create a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim()
projectSlug: z.string().trim().describe(PROJECT_ROLE.CREATE.projectSlug)
}),
body: z.object({
slug: z.string().trim(),
name: z.string().trim(),
description: z.string().trim().optional(),
permissions: z.any().array()
slug: z
.string()
.toLowerCase()
.trim()
.min(1)
.refine(
(val) => !Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((v) => slugify(v) === v, {
message: "Slug must be a valid"
})
.describe(PROJECT_ROLE.CREATE.slug),
name: z.string().min(1).trim().describe(PROJECT_ROLE.CREATE.name),
description: z.string().trim().optional().describe(PROJECT_ROLE.CREATE.description),
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.CREATE.permissions)
}),
response: {
200: z.object({
role: ProjectRolesSchema
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.createRole(
req.permission.type,
req.permission.id,
req.params.projectId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
const role = await server.services.projectRole.createRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
data: {
...req.body,
permissions: JSON.stringify(packRules(req.body.permissions))
}
});
return { role };
}
});
server.route({
method: "PATCH",
url: "/:projectId/roles/:roleId",
url: "/:projectSlug/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Update a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim(),
roleId: z.string().trim()
projectSlug: z.string().trim().describe(PROJECT_ROLE.UPDATE.projectSlug),
roleId: z.string().trim().describe(PROJECT_ROLE.UPDATE.roleId)
}),
body: z.object({
slug: z.string().trim().optional(),
name: z.string().trim().optional(),
description: z.string().trim().optional(),
permissions: z.any().array()
slug: z
.string()
.toLowerCase()
.trim()
.optional()
.describe(PROJECT_ROLE.UPDATE.slug)
.refine(
(val) =>
typeof val === "undefined" ||
!Object.values(ProjectMembershipRole).includes(val as ProjectMembershipRole),
"Please choose a different slug, the slug you have entered is reserved"
)
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
message: "Slug must be a valid"
}),
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions)
}),
response: {
200: z.object({
role: ProjectRolesSchema
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.updateRole(
req.permission.type,
req.permission.id,
req.params.projectId,
req.params.roleId,
req.body,
req.permission.authMethod,
req.permission.orgId
);
const role = await server.services.projectRole.updateRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId,
data: {
...req.body,
permissions: JSON.stringify(packRules(req.body.permissions))
}
});
return { role };
}
});
server.route({
method: "DELETE",
url: "/:projectId/roles/:roleId",
url: "/:projectSlug/roles/:roleId",
config: {
rateLimit: writeLimit
},
schema: {
description: "Delete a project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectId: z.string().trim(),
roleId: z.string().trim()
projectSlug: z.string().trim().describe(PROJECT_ROLE.DELETE.projectSlug),
roleId: z.string().trim().describe(PROJECT_ROLE.DELETE.roleId)
}),
response: {
200: z.object({
role: ProjectRolesSchema
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const role = await server.services.projectRole.deleteRole(
req.permission.type,
req.permission.id,
req.params.projectId,
req.params.roleId,
req.permission.authMethod,
req.permission.orgId
);
const role = await server.services.projectRole.deleteRole({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleId: req.params.roleId
});
return { role };
}
});
server.route({
method: "GET",
url: "/:projectId/roles",
url: "/:projectSlug/roles",
config: {
rateLimit: readLimit
},
schema: {
description: "List project role",
security: [
{
bearerAuth: []
}
],
params: z.object({
projectSlug: z.string().trim().describe(PROJECT_ROLE.LIST.projectSlug)
}),
response: {
200: z.object({
roles: ProjectRolesSchema.omit({ permissions: true }).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const roles = await server.services.projectRole.listRoles({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug
});
return { roles };
}
});
server.route({
method: "GET",
url: "/:projectSlug/roles/slug/:slug",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim()
projectSlug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.projectSlug),
slug: z.string().trim().describe(PROJECT_ROLE.GET_ROLE_BY_SLUG.roleSlug)
}),
response: {
200: z.object({
data: z.object({
roles: ProjectRolesSchema.omit({ permissions: true })
.merge(z.object({ permissions: z.unknown() }))
.array()
})
role: SanitizedRoleSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const roles = await server.services.projectRole.listRoles(
req.permission.type,
req.permission.id,
req.params.projectId,
req.permission.authMethod,
req.permission.orgId
);
return { data: { roles } };
const role = await server.services.projectRole.getRoleBySlug({
actorAuthMethod: req.permission.authMethod,
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actor: req.permission.type,
projectSlug: req.params.projectSlug,
roleSlug: req.params.slug
});
return { role };
}
});

View File

@@ -102,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
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
if (!profile.email || !profile.firstName) {
if (!email || !profile.firstName) {
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
}
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
username: profile.nameID ?? email,
externalId: profile.nameID,
email,
firstName: profile.firstName as string,
lastName: profile.lastName as string,

View File

@@ -153,7 +153,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const users = await req.server.services.scim.listScimUsers({
offset: req.query.startIndex,
startIndex: req.query.startIndex,
limit: req.query.count,
filter: req.query.filter,
orgId: req.permission.orgId
@@ -163,11 +163,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
});
server.route({
url: "/Users/:userId",
url: "/Users/:orgMembershipId",
method: "GET",
schema: {
params: z.object({
userId: z.string().trim()
orgMembershipId: z.string().trim()
}),
response: {
201: z.object({
@@ -193,7 +193,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const user = await req.server.services.scim.getScimUser({
userId: req.params.userId,
orgMembershipId: req.params.orgMembershipId,
orgId: req.permission.orgId
});
return user;
@@ -249,7 +249,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
const primaryEmail = req.body.emails?.find((email) => email.primary)?.value;
const user = await req.server.services.scim.createScimUser({
username: req.body.userName,
externalId: req.body.userName,
email: primaryEmail,
firstName: req.body.name.givenName,
lastName: req.body.name.familyName,
@@ -261,11 +261,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
});
server.route({
url: "/Users/:userId",
url: "/Users/:orgMembershipId",
method: "DELETE",
schema: {
params: z.object({
userId: z.string().trim()
orgMembershipId: z.string().trim()
}),
response: {
200: z.object({})
@@ -274,7 +274,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const user = await req.server.services.scim.deleteScimUser({
userId: req.params.userId,
orgMembershipId: req.params.orgMembershipId,
orgId: req.permission.orgId
});
@@ -361,7 +361,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
handler: async (req) => {
const groups = await req.server.services.scim.listScimGroups({
orgId: req.permission.orgId,
offset: req.query.startIndex,
startIndex: req.query.startIndex,
limit: req.query.count
});
@@ -416,10 +416,10 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
displayName: z.string().trim(),
members: z.array(
z.object({
value: z.string(), // infisical userId
value: z.string(), // infisical orgMembershipId
display: z.string()
})
) // note: is this where members are added to group?
)
}),
response: {
200: z.object({
@@ -534,11 +534,11 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
});
server.route({
url: "/Users/:userId",
url: "/Users/:orgMembershipId",
method: "PUT",
schema: {
params: z.object({
userId: z.string().trim()
orgMembershipId: z.string().trim()
}),
body: z.object({
schemas: z.array(z.string()),
@@ -575,7 +575,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const user = await req.server.services.scim.replaceScimUser({
userId: req.params.userId,
orgMembershipId: req.params.orgMembershipId,
orgId: req.permission.orgId,
active: req.body.active
});

View File

@@ -130,7 +130,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
}),
response: {
200: z.object({
approvals: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).array()
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
})
}
},
@@ -161,7 +161,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
}),
response: {
200: z.object({
policy: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).optional()
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
})
}
},

View File

@@ -32,22 +32,20 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
}),
response: {
200: z.object({
approvals: SecretApprovalRequestsSchema.merge(
z.object({
// secretPath: z.string(),
policy: z.object({
id: z.string(),
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().optional().nullable()
}),
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
environment: z.string(),
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
approvers: z.string().array()
})
).array()
approvals: SecretApprovalRequestsSchema.extend({
// secretPath: z.string(),
policy: z.object({
id: z.string(),
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().optional().nullable()
}),
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
environment: z.string(),
reviewers: z.object({ member: z.string(), status: z.string() }).array(),
approvers: z.string().array()
}).array()
})
}
},
@@ -197,7 +195,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
type: isClosing ? EventType.SECRET_APPROVAL_CLOSED : EventType.SECRET_APPROVAL_REOPENED,
// eslint-disable-next-line
metadata: {
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeByUserId as string,
[isClosing ? ("closedBy" as const) : ("reopenedBy" as const)]: approval.statusChangeBy as string,
secretApprovalRequestId: approval.id,
secretApprovalRequestSlug: approval.slug
// eslint-disable-next-line

View File

@@ -20,7 +20,7 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
.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"))
@@ -38,12 +38,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
const formatedDoc = mergeOneToManyRelation(
doc,
"id",
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
}),
({ approverUserId }) => approverUserId,
({ approverId }) => approverId,
"approvers"
);
return formatedDoc?.[0];
@@ -58,12 +58,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
const formatedDoc = mergeOneToManyRelation(
docs,
"id",
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
}),
({ approverUserId }) => approverUserId,
({ approverId }) => approverId,
"approvers"
);
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));

View File

@@ -5,7 +5,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
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 { TUserDALFactory } from "@app/services/user/user-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";
@@ -24,7 +24,7 @@ type TSecretApprovalPolicyServiceFactoryDep = {
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
userDAL: Pick<TUserDALFactory, "findUsersByProjectId">;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
};
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
@@ -34,8 +34,8 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalPolicyApproverDAL,
permissionService,
projectEnvDAL,
userDAL,
projectDAL
projectDAL,
projectMembershipDAL
}: TSecretApprovalPolicyServiceFactoryDep) => {
const createAccessApprovalPolicy = async ({
name,
@@ -69,13 +69,12 @@ export const accessApprovalPolicyServiceFactory = ({
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
if (!env) throw new BadRequestError({ message: "Environment not found" });
// We need to get the users by project ID to ensure they are part of the project.
const accessApproverUsers = await userDAL.findUsersByProjectId(
project.id,
approvers.map((approverUserId) => approverUserId)
);
const secretApprovers = await projectMembershipDAL.find({
projectId: project.id,
$in: { id: approvers }
});
if (accessApproverUsers.length !== approvers.length) {
if (secretApprovers.length !== approvers.length) {
throw new BadRequestError({ message: "Approver not found in project" });
}
@@ -86,7 +85,7 @@ export const accessApprovalPolicyServiceFactory = ({
secretPath,
actorAuthMethod,
permissionService,
userIds: accessApproverUsers.map((user) => user.id)
userIds: secretApprovers.map((approver) => approver.userId)
});
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
@@ -100,8 +99,8 @@ export const accessApprovalPolicyServiceFactory = ({
tx
);
await accessApprovalPolicyApproverDAL.insertMany(
accessApproverUsers.map((user) => ({
approverUserId: user.id,
secretApprovers.map(({ id }) => ({
approverId: id,
policyId: doc.id
})),
tx
@@ -170,9 +169,12 @@ export const accessApprovalPolicyServiceFactory = ({
);
if (approvers) {
// Find the workspace project memberships of the users passed in the approvers array
const secretApproverUsers = await userDAL.findUsersByProjectId(
accessApprovalPolicy.projectId,
approvers.map((approverUserId) => approverUserId)
const secretApprovers = await projectMembershipDAL.find(
{
projectId: accessApprovalPolicy.projectId,
$in: { id: approvers }
},
{ tx }
);
await verifyApprovers({
@@ -182,15 +184,15 @@ export const accessApprovalPolicyServiceFactory = ({
secretPath: doc.secretPath!,
actorAuthMethod,
permissionService,
userIds: secretApproverUsers.map((user) => user.id)
userIds: secretApprovers.map((approver) => approver.userId)
});
if (secretApproverUsers.length !== approvers.length)
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(
secretApproverUsers.map((user) => ({
approverUserId: user.id,
secretApprovers.map(({ id }) => ({
approverId: id,
policyId: doc.id
})),
tx

View File

@@ -11,42 +11,6 @@ export type TAccessApprovalRequestDALFactory = ReturnType<typeof accessApprovalR
export const accessApprovalRequestDALFactory = (db: TDbClient) => {
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
const projectUserAdditionalPrivilegeOrm = ormify(db, TableName.ProjectUserAdditionalPrivilege);
const groupProjectUserAdditionalPrivilegeOrm = ormify(db, TableName.GroupProjectUserAdditionalPrivilege);
const deleteMany = async (filter: TFindFilter<TAccessApprovalRequests>, tx?: Knex) => {
const transaction = tx || (await db.transaction());
try {
const accessApprovalRequests = await accessApprovalRequestOrm.find(filter, { tx: transaction });
await projectUserAdditionalPrivilegeOrm.delete(
{
$in: {
id: accessApprovalRequests
.filter((req) => Boolean(req.projectUserPrivilegeId))
.map((req) => req.projectUserPrivilegeId!)
}
},
transaction
);
await groupProjectUserAdditionalPrivilegeOrm.delete(
{
$in: {
id: accessApprovalRequests
.filter((req) => Boolean(req.groupProjectUserPrivilegeId))
.map((req) => req.groupProjectUserPrivilegeId!)
}
},
transaction
);
return await accessApprovalRequestOrm.delete(filter, transaction);
} catch (error) {
throw new DatabaseError({ error, name: "DeleteManyAccessApprovalRequest" });
}
};
const findRequestsWithPrivilegeByPolicyIds = async (policyIds: string[]) => {
try {
@@ -55,14 +19,9 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.AccessApprovalRequest}.projectUserPrivilegeId`,
`${TableName.AccessApprovalRequest}.privilegeId`,
`${TableName.ProjectUserAdditionalPrivilege}.id`
)
.leftJoin(
TableName.GroupProjectUserAdditionalPrivilege,
`${TableName.AccessApprovalRequest}.groupProjectUserPrivilegeId`,
`${TableName.GroupProjectUserAdditionalPrivilege}.id`
)
.leftJoin(
TableName.AccessApprovalPolicy,
`${TableName.AccessApprovalRequest}.policyId`,
@@ -91,7 +50,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
)
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(
db.ref("projectId").withSchema(TableName.Environment),
@@ -100,85 +59,32 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
)
.select(
db.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"),
db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")
)
// Project user additional privilege
.select(
db
.ref("projectMembershipId")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("projectPrivilegeProjectMembershipId"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("projectPrivilegeIsTemporary"),
db
.ref("temporaryMode")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("projectPrivilegeTemporaryMode"),
db
.ref("temporaryRange")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("projectPrivilegeTemporaryRange"),
.as("privilegeMembershipId"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeIsTemporary"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryMode"),
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeTemporaryRange"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("projectPrivilegeTemporaryAccessStartTime"),
.as("privilegeTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("projectPrivilegeTemporaryAccessEndTime"),
.as("privilegeTemporaryAccessEndTime"),
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("projectPrivilegePermissions")
)
// Group project user additional privilege
.select(
db
.ref("groupProjectMembershipId")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeGroupProjectMembershipId"),
db
.ref("requestedByUserId")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeRequestedByUserId"),
db
.ref("isTemporary")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeIsTemporary"),
db
.ref("temporaryMode")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeTemporaryMode"),
db
.ref("temporaryRange")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeTemporaryRange"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegeTemporaryAccessEndTime"),
db
.ref("permissions")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("groupPrivilegePermissions")
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegePermissions")
)
.orderBy(`${TableName.AccessApprovalRequest}.createdAt`, "desc");
const projectUserFormattedDocs = sqlNestRelationships({
const formattedDocs = sqlNestRelationships({
data: docs,
key: "id",
parentMapper: (doc) => ({
@@ -193,49 +99,33 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
secretPath: doc.policySecretPath,
envId: doc.policyEnvId
},
// eslint-disable-next-line no-nested-ternary
privilege: doc.projectUserPrivilegeId
privilege: doc.privilegeId
? {
projectMembershipId: doc.projectMembershipId,
groupMembershipId: null,
requestedByUserId: null,
isTemporary: doc.projectPrivilegeIsTemporary,
temporaryMode: doc.projectPrivilegeTemporaryMode,
temporaryRange: doc.projectPrivilegeTemporaryRange,
temporaryAccessStartTime: doc.projectPrivilegeTemporaryAccessStartTime,
temporaryAccessEndTime: doc.projectPrivilegeTemporaryAccessEndTime,
permissions: doc.projectPrivilegePermissions
membershipId: doc.privilegeMembershipId,
isTemporary: doc.privilegeIsTemporary,
temporaryMode: doc.privilegeTemporaryMode,
temporaryRange: doc.privilegeTemporaryRange,
temporaryAccessStartTime: doc.privilegeTemporaryAccessStartTime,
temporaryAccessEndTime: doc.privilegeTemporaryAccessEndTime,
permissions: doc.privilegePermissions
}
: doc.groupProjectUserPrivilegeId
? {
groupMembershipId: doc.groupPrivilegeGroupProjectMembershipId,
requestedByUserId: doc.groupPrivilegeRequestedByUserId,
projectMembershipId: null,
isTemporary: doc.groupPrivilegeIsTemporary,
temporaryMode: doc.groupPrivilegeTemporaryMode,
temporaryRange: doc.groupPrivilegeTemporaryRange,
temporaryAccessStartTime: doc.groupPrivilegeTemporaryAccessStartTime,
temporaryAccessEndTime: doc.groupPrivilegeTemporaryAccessEndTime,
permissions: doc.groupPrivilegePermissions
}
: null,
: null,
isApproved: Boolean(doc.projectUserPrivilegeId || doc.groupProjectUserPrivilegeId)
isApproved: !!doc.privilegeId
}),
childrenMapper: [
{
key: "reviewerUserId",
key: "reviewerMemberId",
label: "reviewers" as const,
mapper: ({ reviewerUserId, reviewerStatus: status }) =>
reviewerUserId ? { member: reviewerUserId, status } : undefined
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
},
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
]
});
if (!projectUserFormattedDocs) return [];
if (!formattedDocs) return [];
return projectUserFormattedDocs.map((doc) => ({
return formattedDocs.map((doc) => ({
...doc,
policy: { ...doc.policy, approvers: doc.approvers }
}));
@@ -267,7 +157,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.select(selectAllTableCols(TableName.AccessApprovalRequest))
.select(
tx.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"),
tx.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
@@ -275,7 +165,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
tx.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover)
tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover)
);
const findById = async (id: string, tx?: Knex) => {
@@ -298,12 +188,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}),
childrenMapper: [
{
key: "reviewerUserId",
key: "reviewerMemberId",
label: "reviewers" as const,
mapper: ({ reviewerUserId, reviewerStatus: status }) =>
reviewerUserId ? { member: reviewerUserId, status } : undefined
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
},
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
]
});
if (!formatedDoc?.[0]) return;
@@ -325,6 +214,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicy}.id`
)
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.AccessApprovalRequest}.privilegeId`,
`${TableName.ProjectUserAdditionalPrivilege}.id`
)
.leftJoin(
TableName.AccessApprovalRequestReviewer,
`${TableName.AccessApprovalRequest}.id`,
@@ -334,7 +229,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
.where(`${TableName.Environment}.projectId`, projectId)
.select(selectAllTableCols(TableName.AccessApprovalRequest))
.select(db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"))
.select(db.ref("memberUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("memberUserId"));
.select(db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"));
const formattedRequests = sqlNestRelationships({
data: accessRequests,
@@ -344,28 +239,21 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}),
childrenMapper: [
{
key: "memberUserId",
key: "reviewerMemberId",
label: "reviewers" as const,
mapper: ({ memberUserId, reviewerStatus: status }) =>
memberUserId ? { member: memberUserId, status } : undefined
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
}
]
});
// an approval is pending if there is no reviewer rejections and no privilege ID is set
const pendingApprovals = formattedRequests.filter(
(req) =>
!req.projectUserPrivilegeId &&
!req.groupProjectUserPrivilegeId &&
!req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
(req) => !req.privilegeId && !req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
);
// an approval is finalized if there are any rejections or a privilege ID is set
const finalizedApprovals = formattedRequests.filter(
(req) =>
req.projectUserPrivilegeId ||
req.groupProjectUserPrivilegeId ||
req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
(req) => req.privilegeId || req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
);
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };
@@ -374,5 +262,5 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}
};
return { ...accessApprovalRequestOrm, findById, findRequestsWithPrivilegeByPolicyIds, getCount, delete: deleteMany };
return { ...accessApprovalRequestOrm, findById, findRequestsWithPrivilegeByPolicyIds, getCount };
};

View File

@@ -1,8 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import ms from "ms";
import { ProjectMembershipRole, TProjectUserAdditionalPrivilege } from "@app/db/schemas";
import { ProjectMembershipRole } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -15,9 +14,7 @@ import { TUserDALFactory } from "@app/services/user/user-dal";
import { TAccessApprovalPolicyApproverDALFactory } from "../access-approval-policy/access-approval-policy-approver-dal";
import { TAccessApprovalPolicyDALFactory } from "../access-approval-policy/access-approval-policy-dal";
import { verifyApprovers } from "../access-approval-policy/access-approval-policy-fns";
import { TGroupProjectUserAdditionalPrivilegeDALFactory } from "../group-project-user-additional-privilege/group-project-user-additional-privilege-dal";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
import { ProjectUserAdditionalPrivilegeTemporaryMode } from "../project-user-additional-privilege/project-user-additional-privilege-types";
import { TAccessApprovalRequestDALFactory } from "./access-approval-request-dal";
@@ -26,15 +23,13 @@ import { TAccessApprovalRequestReviewerDALFactory } from "./access-approval-requ
import {
ApprovalStatus,
TCreateAccessApprovalRequestDTO,
TDeleteApprovalRequestDTO,
TGetAccessRequestCountDTO,
TListApprovalRequestsDTO,
TReviewAccessRequestDTO
} from "./access-approval-request-types";
type TAccessApprovalRequestServiceFactoryDep = {
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById" | "deleteById">;
groupAdditionalPrivilegeDAL: TGroupProjectUserAdditionalPrivilegeDALFactory;
type TSecretApprovalRequestServiceFactoryDep = {
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "create" | "findById">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
accessApprovalPolicyApproverDAL: Pick<TAccessApprovalPolicyApproverDALFactory, "find">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
@@ -49,7 +44,6 @@ type TAccessApprovalRequestServiceFactoryDep = {
| "updateById"
| "findOne"
| "getCount"
| "deleteById"
>;
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
accessApprovalRequestReviewerDAL: Pick<
@@ -58,10 +52,7 @@ type TAccessApprovalRequestServiceFactoryDep = {
>;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
smtpService: Pick<TSmtpService, "sendMail">;
userDAL: Pick<
TUserDALFactory,
"findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "findUsersByProjectId" | "findUserByProjectId"
>;
userDAL: Pick<TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds">;
};
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
@@ -71,7 +62,6 @@ export const accessApprovalRequestServiceFactory = ({
projectEnvDAL,
permissionService,
accessApprovalRequestDAL,
groupAdditionalPrivilegeDAL,
accessApprovalRequestReviewerDAL,
projectMembershipDAL,
accessApprovalPolicyDAL,
@@ -79,7 +69,7 @@ export const accessApprovalRequestServiceFactory = ({
additionalPrivilegeDAL,
smtpService,
userDAL
}: TAccessApprovalRequestServiceFactoryDep) => {
}: TSecretApprovalRequestServiceFactoryDep) => {
const createAccessApprovalRequest = async ({
isTemporary,
temporaryRange,
@@ -104,6 +94,9 @@ export const accessApprovalRequestServiceFactory = ({
);
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
const requestedByUser = await userDAL.findUserByProjectMembershipId(membership.id);
if (!requestedByUser) throw new UnauthorizedError({ message: "User not found" });
await projectDAL.checkProjectUpgradeStatus(project.id);
const { envSlug, secretPath, accessTypes } = verifyRequestedPermissions({ permissions: requestedPermissions });
@@ -121,43 +114,25 @@ export const accessApprovalRequestServiceFactory = ({
policyId: policy.id
});
if (approvers.some((approver) => !approver.approverUserId)) {
throw new BadRequestError({ message: "Policy approvers must be assigned to users" });
}
const approverUsers = await userDAL.findUsersByProjectId(
project.id,
approvers.map((approver) => approver.approverUserId!)
const approverUsers = await userDAL.findUsersByProjectMembershipIds(
approvers.map((approver) => approver.approverId)
);
const requestedByUser = await userDAL.findUserByProjectId(project.id, actorId);
if (!requestedByUser) throw new BadRequestError({ message: "User not found in project" });
const duplicateRequests = await accessApprovalRequestDAL.find({
policyId: policy.id,
requestedByUserId: actorId,
requestedBy: membership.id,
permissions: JSON.stringify(requestedPermissions),
isTemporary
});
if (duplicateRequests?.length > 0) {
for await (const duplicateRequest of duplicateRequests) {
let foundPrivilege: Pick<
TProjectUserAdditionalPrivilege,
"temporaryAccessEndTime" | "isTemporary" | "id"
> | null = null;
if (duplicateRequest.privilegeId) {
const privilege = await additionalPrivilegeDAL.findById(duplicateRequest.privilegeId);
if (duplicateRequest.projectUserPrivilegeId) {
foundPrivilege = await additionalPrivilegeDAL.findById(duplicateRequest.projectUserPrivilegeId);
} else if (duplicateRequest.groupProjectUserPrivilegeId) {
foundPrivilege = await groupAdditionalPrivilegeDAL.findById(duplicateRequest.groupProjectUserPrivilegeId);
}
const isExpired = new Date() > new Date(privilege.temporaryAccessEndTime || ("" as string));
if (foundPrivilege) {
const isExpired = new Date() > new Date(foundPrivilege.temporaryAccessEndTime || ("" as string));
if (!isExpired || !foundPrivilege.isTemporary) {
if (!isExpired || !privilege.isTemporary) {
throw new BadRequestError({ message: "You already have an active privilege with the same criteria" });
}
} else {
@@ -175,18 +150,10 @@ export const accessApprovalRequestServiceFactory = ({
}
const approval = await accessApprovalRequestDAL.transaction(async (tx) => {
const requesterUser = await userDAL.findUserByProjectId(project.id, actorId);
if (!requesterUser?.projectMembershipId && !requesterUser?.groupProjectMembershipId) {
throw new BadRequestError({ message: "You don't have a membership for this project" });
}
const approvalRequest = await accessApprovalRequestDAL.create(
{
projectMembershipId: requesterUser.projectMembershipId || null,
groupMembershipId: requesterUser.groupProjectMembershipId || null,
policyId: policy.id,
requestedByUserId: actorId, // This is the user ID of the person who made the request
requestedBy: membership.id,
temporaryRange: temporaryRange || null,
permissions: JSON.stringify(requestedPermissions),
isTemporary
@@ -220,62 +187,9 @@ export const accessApprovalRequestServiceFactory = ({
return { request: approval };
};
const deleteAccessApprovalRequest = async ({
projectSlug,
actor,
requestId,
actorOrgId,
actorId,
actorAuthMethod
}: TDeleteApprovalRequestDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new UnauthorizedError({ message: "Project not found" });
const { membership, permission } = await permissionService.getProjectPermission(
actor,
actorId,
project.id,
actorAuthMethod,
actorOrgId
);
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretApproval
);
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
if (!accessApprovalRequest?.projectUserPrivilegeId && !accessApprovalRequest?.groupProjectUserPrivilegeId) {
throw new BadRequestError({ message: "Access request must be approved to be deleted" });
}
if (accessApprovalRequest?.projectId !== project.id) {
throw new UnauthorizedError({ message: "Request not found in project" });
}
const approvers = await accessApprovalPolicyApproverDAL.find({
policyId: accessApprovalRequest.policyId
});
// make sure the actor (actorId) is an approver
if (!approvers.some((approver) => approver.approverUserId === actorId)) {
throw new UnauthorizedError({ message: "Only policy approvers can delete access requests" });
}
if (accessApprovalRequest.projectUserPrivilegeId) {
await additionalPrivilegeDAL.deleteById(accessApprovalRequest.projectUserPrivilegeId);
} else if (accessApprovalRequest.groupProjectUserPrivilegeId) {
await groupAdditionalPrivilegeDAL.deleteById(accessApprovalRequest.groupProjectUserPrivilegeId);
}
return { request: accessApprovalRequest };
};
const listApprovalRequests = async ({
projectSlug,
authorUserId,
authorProjectMembershipId,
envSlug,
actor,
actorOrgId,
@@ -297,8 +211,13 @@ export const accessApprovalRequestServiceFactory = ({
const policies = await accessApprovalPolicyDAL.find({ projectId: project.id });
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
if (authorUserId) requests = requests.filter((request) => request.requestedByUserId === authorUserId);
if (envSlug) requests = requests.filter((request) => request.environment === envSlug);
if (authorProjectMembershipId) {
requests = requests.filter((request) => request.requestedBy === authorProjectMembershipId);
}
if (envSlug) {
requests = requests.filter((request) => request.environment === envSlug);
}
return { requests };
};
@@ -327,8 +246,8 @@ export const accessApprovalRequestServiceFactory = ({
if (
!hasRole(ProjectMembershipRole.Admin) &&
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
!policy.approvers.find((approverUserId) => approverUserId === membership.id) // The request isn't performed by an assigned approver
accessApprovalRequest.requestedBy !== membership.id && // The request wasn't made by the current user
!policy.approvers.find((approverId) => approverId === membership.id) // The request isn't performed by an assigned approver
) {
throw new UnauthorizedError({ message: "You are not authorized to approve this request" });
}
@@ -354,7 +273,7 @@ export const accessApprovalRequestServiceFactory = ({
const review = await accessApprovalRequestReviewerDAL.findOne(
{
requestId: accessApprovalRequest.id,
memberUserId: actorId
member: membership.id
},
tx
);
@@ -363,7 +282,7 @@ export const accessApprovalRequestServiceFactory = ({
{
status,
requestId: accessApprovalRequest.id,
memberUserId: actorId
member: membership.id
},
tx
);
@@ -378,92 +297,41 @@ export const accessApprovalRequestServiceFactory = ({
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
}
let projectUserPrivilegeId: string | null = null;
let groupProjectMembershipId: string | null = null;
let privilegeId: string | null = null;
if (!accessApprovalRequest.groupMembershipId && !accessApprovalRequest.projectMembershipId) {
throw new BadRequestError({ message: "Project membership or group membership is required" });
}
// Permanent access
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
if (accessApprovalRequest.groupMembershipId) {
// Group user privilege
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
{
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
requestedByUserId: accessApprovalRequest.requestedByUserId,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions)
},
tx
);
groupProjectMembershipId = groupProjectUserAdditionalPrivilege.id;
} else {
// Project user privilege
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.projectMembershipId!,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions)
},
tx
);
projectUserPrivilegeId = privilege.id;
}
// Permanent access
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.requestedBy,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions)
},
tx
);
privilegeId = privilege.id;
} else {
// Temporary access
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
const startTime = new Date();
if (accessApprovalRequest.groupMembershipId) {
// Group user privilege
const groupProjectUserAdditionalPrivilege = await groupAdditionalPrivilegeDAL.create(
{
groupProjectMembershipId: accessApprovalRequest.groupMembershipId,
requestedByUserId: accessApprovalRequest.requestedByUserId,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions),
isTemporary: true,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: accessApprovalRequest.temporaryRange!,
temporaryAccessStartTime: startTime,
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
},
tx
);
groupProjectMembershipId = groupProjectUserAdditionalPrivilege.id;
} else {
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.projectMembershipId!,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions),
isTemporary: true,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: accessApprovalRequest.temporaryRange!,
temporaryAccessStartTime: startTime,
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
},
tx
);
projectUserPrivilegeId = privilege.id;
}
}
if (projectUserPrivilegeId) {
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { projectUserPrivilegeId }, tx);
} else if (groupProjectMembershipId) {
await accessApprovalRequestDAL.updateById(
accessApprovalRequest.id,
{ groupProjectUserPrivilegeId: groupProjectMembershipId },
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.requestedBy,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions),
isTemporary: true,
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: accessApprovalRequest.temporaryRange!,
temporaryAccessStartTime: startTime,
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
},
tx
);
} else {
throw new BadRequestError({ message: "No privilege was created" });
privilegeId = privilege.id;
}
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
}
return newReview;
@@ -496,7 +364,6 @@ export const accessApprovalRequestServiceFactory = ({
createAccessApprovalRequest,
listApprovalRequests,
reviewAccessRequest,
deleteAccessApprovalRequest,
getCount
};
};

View File

@@ -28,11 +28,6 @@ export type TCreateAccessApprovalRequestDTO = {
export type TListApprovalRequestsDTO = {
projectSlug: string;
authorUserId?: string;
authorProjectMembershipId?: string;
envSlug?: string;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteApprovalRequestDTO = {
requestId: string;
projectSlug: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -3,7 +3,6 @@ import { RawAxiosRequestHeaders } from "axios";
import { SecretKeyEncoding } from "@app/db/schemas";
import { request } from "@app/lib/config/request";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -113,35 +112,7 @@ export const auditLogQueueServiceFactory = ({
);
});
queueService.start(QueueName.AuditLogPrune, async () => {
logger.info(`${QueueName.AuditLogPrune}: queue task started`);
await auditLogDAL.pruneAuditLog();
logger.info(`${QueueName.AuditLogPrune}: queue task completed`);
});
// we do a repeat cron job in utc timezone at 12 Midnight each day
const startAuditLogPruneJob = async () => {
// clear previous job
await queueService.stopRepeatableJob(
QueueName.AuditLogPrune,
QueueJobs.AuditLogPrune,
{ pattern: "0 0 * * *", utc: true },
QueueName.AuditLogPrune // just a job id
);
await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, {
delay: 5000,
jobId: QueueName.AuditLogPrune,
repeat: { pattern: "0 0 * * *", utc: true }
});
};
queueService.listen(QueueName.AuditLogPrune, "failed", (err) => {
logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`);
});
return {
pushToLog,
startAuditLogPruneJob
pushToLog
};
};

View File

@@ -51,6 +51,7 @@ export enum EventType {
UNAUTHORIZE_INTEGRATION = "unauthorize-integration",
CREATE_INTEGRATION = "create-integration",
DELETE_INTEGRATION = "delete-integration",
MANUAL_SYNC_INTEGRATION = "manual-sync-integration",
ADD_TRUSTED_IP = "add-trusted-ip",
UPDATE_TRUSTED_IP = "update-trusted-ip",
DELETE_TRUSTED_IP = "delete-trusted-ip",
@@ -63,9 +64,25 @@ export enum EventType {
ADD_IDENTITY_UNIVERSAL_AUTH = "add-identity-universal-auth",
UPDATE_IDENTITY_UNIVERSAL_AUTH = "update-identity-universal-auth",
GET_IDENTITY_UNIVERSAL_AUTH = "get-identity-universal-auth",
LOGIN_IDENTITY_KUBERNETES_AUTH = "login-identity-kubernetes-auth",
ADD_IDENTITY_KUBERNETES_AUTH = "add-identity-kubernetes-auth",
UPDATE_IDENTITY_KUBENETES_AUTH = "update-identity-kubernetes-auth",
GET_IDENTITY_KUBERNETES_AUTH = "get-identity-kubernetes-auth",
CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "create-identity-universal-auth-client-secret",
REVOKE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET = "revoke-identity-universal-auth-client-secret",
GET_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRETS = "get-identity-universal-auth-client-secret",
LOGIN_IDENTITY_GCP_AUTH = "login-identity-gcp-auth",
ADD_IDENTITY_GCP_AUTH = "add-identity-gcp-auth",
UPDATE_IDENTITY_GCP_AUTH = "update-identity-gcp-auth",
GET_IDENTITY_GCP_AUTH = "get-identity-gcp-auth",
LOGIN_IDENTITY_AWS_AUTH = "login-identity-aws-auth",
ADD_IDENTITY_AWS_AUTH = "add-identity-aws-auth",
UPDATE_IDENTITY_AWS_AUTH = "update-identity-aws-auth",
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
GET_IDENTITY_AZURE_AUTH = "get-identity-azure-auth",
CREATE_ENVIRONMENT = "create-environment",
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
@@ -269,6 +286,25 @@ interface DeleteIntegrationEvent {
};
}
interface ManualSyncIntegrationEvent {
type: EventType.MANUAL_SYNC_INTEGRATION;
metadata: {
integrationId: string;
integration: string;
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
};
}
interface AddTrustedIPEvent {
type: EventType.ADD_TRUSTED_IP;
metadata: {
@@ -383,6 +419,50 @@ interface GetIdentityUniversalAuthEvent {
};
}
interface LoginIdentityKubernetesAuthEvent {
type: EventType.LOGIN_IDENTITY_KUBERNETES_AUTH;
metadata: {
identityId: string;
identityKubernetesAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityKubernetesAuthEvent {
type: EventType.ADD_IDENTITY_KUBERNETES_AUTH;
metadata: {
identityId: string;
kubernetesHost: string;
allowedNamespaces: string;
allowedNames: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
};
}
interface UpdateIdentityKubernetesAuthEvent {
type: EventType.UPDATE_IDENTITY_KUBENETES_AUTH;
metadata: {
identityId: string;
kubernetesHost?: string;
allowedNamespaces?: string;
allowedNames?: string;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
};
}
interface GetIdentityKubernetesAuthEvent {
type: EventType.GET_IDENTITY_KUBERNETES_AUTH;
metadata: {
identityId: string;
};
}
interface CreateIdentityUniversalAuthClientSecretEvent {
type: EventType.CREATE_IDENTITY_UNIVERSAL_AUTH_CLIENT_SECRET;
metadata: {
@@ -406,6 +486,138 @@ interface RevokeIdentityUniversalAuthClientSecretEvent {
};
}
interface LoginIdentityGcpAuthEvent {
type: EventType.LOGIN_IDENTITY_GCP_AUTH;
metadata: {
identityId: string;
identityGcpAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityGcpAuthEvent {
type: EventType.ADD_IDENTITY_GCP_AUTH;
metadata: {
identityId: string;
type: string;
allowedServiceAccounts: string;
allowedProjects: string;
allowedZones: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
};
}
interface UpdateIdentityGcpAuthEvent {
type: EventType.UPDATE_IDENTITY_GCP_AUTH;
metadata: {
identityId: string;
type?: string;
allowedServiceAccounts?: string;
allowedProjects?: string;
allowedZones?: string;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
};
}
interface GetIdentityGcpAuthEvent {
type: EventType.GET_IDENTITY_GCP_AUTH;
metadata: {
identityId: string;
};
}
interface LoginIdentityAwsAuthEvent {
type: EventType.LOGIN_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
identityAwsAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityAwsAuthEvent {
type: EventType.ADD_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
stsEndpoint: string;
allowedPrincipalArns: string;
allowedAccountIds: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
};
}
interface UpdateIdentityAwsAuthEvent {
type: EventType.UPDATE_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
stsEndpoint?: string;
allowedPrincipalArns?: string;
allowedAccountIds?: string;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
};
}
interface GetIdentityAwsAuthEvent {
type: EventType.GET_IDENTITY_AWS_AUTH;
metadata: {
identityId: string;
};
}
interface LoginIdentityAzureAuthEvent {
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
metadata: {
identityId: string;
identityAzureAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityAzureAuthEvent {
type: EventType.ADD_IDENTITY_AZURE_AUTH;
metadata: {
identityId: string;
tenantId: string;
resource: string;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
};
}
interface UpdateIdentityAzureAuthEvent {
type: EventType.UPDATE_IDENTITY_AZURE_AUTH;
metadata: {
identityId: string;
tenantId?: string;
resource?: string;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
};
}
interface GetIdentityAzureAuthEvent {
type: EventType.GET_IDENTITY_AZURE_AUTH;
metadata: {
identityId: string;
};
}
interface CreateEnvironmentEvent {
type: EventType.CREATE_ENVIRONMENT;
metadata: {
@@ -625,9 +837,9 @@ interface SecretApprovalReopened {
interface SecretApprovalRequest {
type: EventType.SECRET_APPROVAL_REQUEST;
metadata: {
committedBy: string;
secretApprovalRequestSlug: string;
secretApprovalRequestId: string;
committedByUser?: string | null; // Needs to be nullable for backward compatibility
};
}
@@ -645,6 +857,7 @@ export type Event =
| UnauthorizeIntegrationEvent
| CreateIntegrationEvent
| DeleteIntegrationEvent
| ManualSyncIntegrationEvent
| AddTrustedIPEvent
| UpdateTrustedIPEvent
| DeleteTrustedIPEvent
@@ -657,9 +870,25 @@ export type Event =
| AddIdentityUniversalAuthEvent
| UpdateIdentityUniversalAuthEvent
| GetIdentityUniversalAuthEvent
| LoginIdentityKubernetesAuthEvent
| AddIdentityKubernetesAuthEvent
| UpdateIdentityKubernetesAuthEvent
| GetIdentityKubernetesAuthEvent
| CreateIdentityUniversalAuthClientSecretEvent
| GetIdentityUniversalAuthClientSecretsEvent
| RevokeIdentityUniversalAuthClientSecretEvent
| LoginIdentityGcpAuthEvent
| AddIdentityGcpAuthEvent
| UpdateIdentityGcpAuthEvent
| GetIdentityGcpAuthEvent
| LoginIdentityAwsAuthEvent
| AddIdentityAwsAuthEvent
| UpdateIdentityAwsAuthEvent
| GetIdentityAwsAuthEvent
| LoginIdentityAzureAuthEvent
| AddIdentityAzureAuthEvent
| UpdateIdentityAzureAuthEvent
| GetIdentityAzureAuthEvent
| CreateEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent

View File

@@ -1,12 +0,0 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TGroupProjectUserAdditionalPrivilegeDALFactory = ReturnType<
typeof groupProjectUserAdditionalPrivilegeDALFactory
>;
export const groupProjectUserAdditionalPrivilegeDALFactory = (db: TDbClient) => {
const orm = ormify(db, TableName.GroupProjectUserAdditionalPrivilege);
return orm;
};

View File

@@ -5,78 +5,10 @@ import { TableName, TGroups } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { buildFindFilter, ormify, selectAllTableCols, TFindFilter, TFindOpt } from "@app/lib/knex";
import { TUserGroupMembershipDALFactory } from "./user-group-membership-dal";
export type TGroupDALFactory = ReturnType<typeof groupDALFactory>;
export const groupDALFactory = (db: TDbClient, userGroupMembershipDAL: TUserGroupMembershipDALFactory) => {
export const groupDALFactory = (db: TDbClient) => {
const groupOrm = ormify(db, TableName.Groups);
const groupMembershipOrm = ormify(db, TableName.GroupProjectMembership);
const accessApprovalRequestOrm = ormify(db, TableName.AccessApprovalRequest);
const secretApprovalRequestOrm = ormify(db, TableName.SecretApprovalRequest);
const deleteMany = async (filterQuery: TFindFilter<TGroups>, tx?: Knex) => {
const transaction = tx || (await db.transaction());
// Find all memberships
const groups = await groupOrm.find(filterQuery, { tx: transaction });
for await (const group of groups) {
// Find all the group memberships of the groups (a group membership is which projects the group is a part of)
const groupProjectMemberships = await groupMembershipOrm.find(
{ groupId: group.id },
{
tx: transaction
}
);
// For each of those group memberships, we need to find all the members of the group that don't have a regular membership in the project
for await (const groupMembership of groupProjectMemberships) {
const members = await userGroupMembershipDAL.findGroupMembersNotInProject(
group.id,
groupMembership.projectId,
transaction
);
// We then delete all the access approval requests and secret approval requests associated with these members
await accessApprovalRequestOrm.delete(
{
groupMembershipId: groupMembership.id,
$in: {
requestedByUserId: members.map(({ user }) => user.id)
}
},
transaction
);
const policies = await (tx || db)(TableName.SecretApprovalPolicy)
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.where(`${TableName.Environment}.projectId`, groupMembership.projectId)
.select(selectAllTableCols(TableName.SecretApprovalPolicy));
await secretApprovalRequestOrm.delete(
{
$in: {
policyId: policies.map(({ id }) => id),
committerUserId: members.map(({ user }) => user.id)
}
},
transaction
);
}
}
await groupOrm.delete(
{
$in: {
id: groups.map((group) => group.id)
}
},
transaction
);
return groups;
};
const findGroups = async (filter: TFindFilter<TGroups>, { offset, limit, sort, tx }: TFindOpt<TGroups> = {}) => {
try {
@@ -190,10 +122,9 @@ export const groupDALFactory = (db: TDbClient, userGroupMembershipDAL: TUserGrou
};
return {
...groupOrm,
findGroups,
findByOrgId,
findAllGroupMembers,
delete: deleteMany
...groupOrm
};
};

View File

@@ -1,6 +1,6 @@
import { Knex } from "knex";
import { SecretKeyEncoding, TUsers } from "@app/db/schemas";
import { SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { BadRequestError, ScimRequestError } from "@app/lib/errors";
@@ -188,9 +188,9 @@ export const addUsersToGroupByUserIds = async ({
// check if all user(s) are part of the organization
const existingUserOrgMemberships = await orgDAL.findMembership(
{
orgId: group.orgId,
[`${TableName.OrgMembership}.orgId` as "orgId"]: group.orgId,
$in: {
userId: userIds
[`${TableName.OrgMembership}.userId` as "userId"]: userIds
}
},
{ tx }
@@ -266,9 +266,6 @@ export const removeUsersFromGroupByUserIds = async ({
userIds,
userDAL,
userGroupMembershipDAL,
accessApprovalRequestDAL,
secretApprovalRequestDAL,
secretApprovalPolicyDAL,
groupProjectDAL,
projectKeyDAL,
tx: outerTx
@@ -325,15 +322,19 @@ export const removeUsersFromGroupByUserIds = async ({
});
if (membersToRemoveFromGroupNonPending.length) {
const groupProjectMemberships = await groupProjectDAL.find(
{
groupId: group.id
},
{ tx }
);
// check which projects the group is part of
const projectIds = Array.from(new Set(groupProjectMemberships.map((gp) => gp.projectId)));
const projectIds = Array.from(
new Set(
(
await groupProjectDAL.find(
{
groupId: group.id
},
{ tx }
)
).map((gp) => gp.projectId)
)
);
// TODO: this part can be optimized
for await (const userId of userIds) {
@@ -352,35 +353,10 @@ export const removeUsersFromGroupByUserIds = async ({
);
}
await accessApprovalRequestDAL.delete(
{
$in: {
groupMembershipId: groupProjectMemberships
.filter((gp) => projectsToDeleteKeyFor.includes(gp.projectId))
.map((gp) => gp.id)
},
requestedByUserId: userId
},
tx
);
const projectSecretApprovalPolicies = await secretApprovalPolicyDAL.findByProjectIds(projectIds);
await secretApprovalRequestDAL.delete(
{
committerUserId: userId,
$in: {
policyId: projectSecretApprovalPolicies.map((p) => p.id)
}
},
tx
);
await userGroupMembershipDAL.delete(
{
groupId: group.id,
$in: {
userId: membersToRemoveFromGroupNonPending.map((member) => member.id)
}
userId
},
tx
);
@@ -388,15 +364,12 @@ export const removeUsersFromGroupByUserIds = async ({
}
if (membersToRemoveFromGroupPending.length) {
await userGroupMembershipDAL.delete(
{
groupId: group.id,
$in: {
userId: membersToRemoveFromGroupPending.map((member) => member.id)
}
},
tx
);
await userGroupMembershipDAL.delete({
groupId: group.id,
$in: {
userId: membersToRemoveFromGroupPending.map((member) => member.id)
}
});
}
return membersToRemoveFromGroupNonPending.concat(membersToRemoveFromGroupPending);

View File

@@ -12,12 +12,9 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
import { TGroupDALFactory } from "./group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "./group-fns";
import {
@@ -44,9 +41,6 @@ type TGroupServiceFactoryDep = {
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "findLatestProjectKey" | "insertMany">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission" | "getOrgPermissionByRole">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
};
export type TGroupServiceFactory = ReturnType<typeof groupServiceFactory>;
@@ -56,9 +50,6 @@ export const groupServiceFactory = ({
groupDAL,
groupProjectDAL,
orgDAL,
secretApprovalRequestDAL,
secretApprovalPolicyDAL,
accessApprovalRequestDAL,
userGroupMembershipDAL,
projectDAL,
projectBotDAL,
@@ -337,9 +328,6 @@ export const groupServiceFactory = ({
group,
userIds: [user.id],
userDAL,
accessApprovalRequestDAL,
secretApprovalPolicyDAL,
secretApprovalRequestDAL,
userGroupMembershipDAL,
groupProjectDAL,
projectKeyDAL

View File

@@ -10,10 +10,6 @@ import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
export type TCreateGroupDTO = {
name: string;
slug?: string;
@@ -81,9 +77,6 @@ export type TRemoveUsersFromGroupByUserIds = {
group: TGroups;
userIds: string[];
userDAL: Pick<TUserDALFactory, "find" | "transaction">;
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
userGroupMembershipDAL: Pick<TUserGroupMembershipDALFactory, "find" | "filterProjectsByUserMembership" | "delete">;
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
projectKeyDAL: Pick<TProjectKeyDALFactory, "delete">;

View File

@@ -1,5 +1,7 @@
import { ForbiddenError } from "@casl/ability";
import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, unpackRules } from "@casl/ability/extra";
import ms from "ms";
import { z } from "zod";
import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
@@ -8,7 +10,7 @@ import { TIdentityProjectDALFactory } from "@app/services/identity-project/ident
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/project-permission";
import { ProjectPermissionActions, ProjectPermissionSet, ProjectPermissionSub } from "../permission/project-permission";
import { TIdentityProjectAdditionalPrivilegeDALFactory } from "./identity-project-additional-privilege-dal";
import {
IdentityProjectAdditionalPrivilegeTemporaryMode,
@@ -30,6 +32,27 @@ export type TIdentityProjectAdditionalPrivilegeServiceFactory = ReturnType<
typeof identityProjectAdditionalPrivilegeServiceFactory
>;
// TODO(akhilmhdh): move this to more centralized
export const UnpackedPermissionSchema = z.object({
subject: z.union([z.string().min(1), z.string().array()]).optional(),
action: z.union([z.string().min(1), z.string().array()]),
conditions: z
.object({
environment: z.string().optional(),
secretPath: z
.object({
$glob: z.string().min(1)
})
.optional()
})
.optional()
});
const unpackPermissions = (permissions: unknown) =>
UnpackedPermissionSchema.array().parse(
unpackRules((permissions || []) as PackRule<RawRuleOf<MongoAbility<ProjectPermissionSet>>>[])
);
export const identityProjectAdditionalPrivilegeServiceFactory = ({
identityProjectAdditionalPrivilegeDAL,
identityProjectDAL,
@@ -86,7 +109,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
slug,
permissions: customPermission
});
return additionalPrivilege;
return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
}
const relativeTempAllocatedTimeInMs = ms(dto.temporaryRange);
@@ -100,7 +126,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryAccessStartTime: new Date(dto.temporaryAccessStartTime),
temporaryAccessEndTime: new Date(new Date(dto.temporaryAccessStartTime).getTime() + relativeTempAllocatedTimeInMs)
});
return additionalPrivilege;
return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
};
const updateBySlug = async ({
@@ -163,7 +192,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryAccessStartTime: new Date(temporaryAccessStartTime || ""),
temporaryAccessEndTime: new Date(new Date(temporaryAccessStartTime || "").getTime() + ms(temporaryRange || ""))
});
return additionalPrivilege;
return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
}
const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.updateById(identityPrivilege.id, {
@@ -174,7 +207,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
temporaryRange: null,
temporaryMode: null
});
return additionalPrivilege;
return {
...additionalPrivilege,
permissions: unpackPermissions(additionalPrivilege.permissions)
};
};
const deleteBySlug = async ({
@@ -220,7 +257,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
const deletedPrivilege = await identityProjectAdditionalPrivilegeDAL.deleteById(identityPrivilege.id);
return deletedPrivilege;
return {
...deletedPrivilege,
permissions: unpackPermissions(deletedPrivilege.permissions)
};
};
const getPrivilegeDetailsBySlug = async ({
@@ -254,7 +295,10 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
});
if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" });
return identityPrivilege;
return {
...identityPrivilege,
permissions: unpackPermissions(identityPrivilege.permissions)
};
};
const listIdentityProjectPrivileges = async ({
@@ -284,7 +328,11 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({
const identityPrivileges = await identityProjectAdditionalPrivilegeDAL.find({
projectMembershipId: identityProjectMembership.id
});
return identityPrivileges;
return identityPrivileges.map((el) => ({
...el,
permissions: unpackPermissions(el.permissions)
}));
};
return {

View File

@@ -1,7 +1,14 @@
import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken";
import { OrgMembershipRole, OrgMembershipStatus, SecretKeyEncoding, TLdapConfigsUpdate } from "@app/db/schemas";
import {
OrgMembershipRole,
OrgMembershipStatus,
SecretKeyEncoding,
TableName,
TLdapConfigsUpdate,
TUsers
} from "@app/db/schemas";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
@@ -19,19 +26,19 @@ import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
import { TLdapConfigDALFactory } from "./ldap-config-dal";
import {
TCreateLdapCfgDTO,
@@ -49,6 +56,7 @@ import { TLdapGroupMapDALFactory } from "./ldap-group-map-dal";
type TLdapConfigServiceFactoryDep = {
ldapConfigDAL: Pick<TLdapConfigDALFactory, "create" | "update" | "findOne">;
ldapGroupMapDAL: Pick<TLdapGroupMapDALFactory, "find" | "create" | "delete" | "findLdapGroupMapsByLdapConfigId">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
orgDAL: Pick<
TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
@@ -70,9 +78,6 @@ type TLdapConfigServiceFactoryDep = {
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
};
export type TLdapConfigServiceFactory = ReturnType<typeof ldapConfigServiceFactory>;
@@ -81,12 +86,10 @@ export const ldapConfigServiceFactory = ({
ldapConfigDAL,
ldapGroupMapDAL,
orgDAL,
orgMembershipDAL,
orgBotDAL,
groupDAL,
groupProjectDAL,
accessApprovalRequestDAL,
secretApprovalPolicyDAL,
secretApprovalRequestDAL,
projectKeyDAL,
projectDAL,
projectBotDAL,
@@ -388,16 +391,17 @@ export const ldapConfigServiceFactory = ({
username,
firstName,
lastName,
emails,
email,
groups,
orgId,
relayState
}: TLdapLoginDTO) => {
const appCfg = getConfig();
const serverCfg = await getServerCfg();
let userAlias = await userAliasDAL.findOne({
externalId,
orgId,
aliasType: AuthMethod.LDAP
aliasType: UserAliasType.LDAP
});
const organization = await orgDAL.findOrgById(orgId);
@@ -405,7 +409,13 @@ export const ldapConfigServiceFactory = ({
if (userAlias) {
await userDAL.transaction(async (tx) => {
const [orgMembership] = await orgDAL.findMembership({ userId: userAlias.userId }, { tx });
const [orgMembership] = await orgDAL.findMembership(
{
[`${TableName.OrgMembership}.userId` as "userId"]: userAlias.userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
{ tx }
);
if (!orgMembership) {
await orgDAL.createMembership(
{
@@ -428,40 +438,75 @@ export const ldapConfigServiceFactory = ({
});
} else {
userAlias = await userDAL.transaction(async (tx) => {
const uniqueUsername = await normalizeUsername(username, userDAL);
const newUser = await userDAL.create(
{
username: uniqueUsername,
email: emails[0],
firstName,
lastName,
authMethods: [AuthMethod.LDAP],
isGhost: false
},
tx
);
let newUser: TUsers | undefined;
if (serverCfg.trustSamlEmails) {
newUser = await userDAL.findOne(
{
email,
isEmailVerified: true
},
tx
);
}
if (!newUser) {
const uniqueUsername = await normalizeUsername(username, userDAL);
newUser = await userDAL.create(
{
username: serverCfg.trustLdapEmails ? email : uniqueUsername,
email,
isEmailVerified: serverCfg.trustLdapEmails,
firstName,
lastName,
authMethods: [],
isGhost: false
},
tx
);
}
const newUserAlias = await userAliasDAL.create(
{
userId: newUser.id,
username,
aliasType: AuthMethod.LDAP,
aliasType: UserAliasType.LDAP,
externalId,
emails,
emails: [email],
orgId
},
tx
);
await orgDAL.createMembership(
const [orgMembership] = await orgDAL.findMembership(
{
userId: newUser.id,
orgId,
role: OrgMembershipRole.Member,
status: OrgMembershipStatus.Invited
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
tx
{ tx }
);
if (!orgMembership) {
await orgMembershipDAL.create(
{
userId: userAlias.userId,
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
},
tx
);
// Only update the membership to Accepted if the user account is already completed.
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
await orgDAL.updateMembershipById(
orgMembership.id,
{
status: OrgMembershipStatus.Accepted
},
tx
);
}
return newUserAlias;
});
}
@@ -533,10 +578,7 @@ export const ldapConfigServiceFactory = ({
group,
userIds: [newUser.id],
userDAL,
secretApprovalRequestDAL,
accessApprovalRequestDAL,
userGroupMembershipDAL,
secretApprovalPolicyDAL,
groupProjectDAL,
projectKeyDAL,
tx
@@ -555,11 +597,14 @@ export const ldapConfigServiceFactory = ({
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user.id,
username: user.username,
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
firstName,
lastName,
organizationName: organization.name,
organizationId: organization.id,
organizationSlug: organization.slug,
authMethod: AuthMethod.LDAP,
authType: UserAliasType.LDAP,
isUserCompleted,
...(relayState
? {

View File

@@ -51,7 +51,7 @@ export type TLdapLoginDTO = {
username: string;
firstName: string;
lastName: string;
emails: string[];
email: string;
orgId: string;
groups?: {
dn: string;

View File

@@ -16,6 +16,8 @@ export const licenseDALFactory = (db: TDbClient) => {
void bd.where({ orgId });
}
})
.join(TableName.Users, `${TableName.OrgMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Users}.isGhost`, false)
.count();
return doc?.[0].count;
} catch (error) {

View File

@@ -62,11 +62,6 @@ export const permissionDALFactory = (db: TDbClient) => {
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
`${TableName.GroupProjectMembership}.id`
)
.leftJoin(
TableName.GroupProjectUserAdditionalPrivilege,
`${TableName.GroupProjectUserAdditionalPrivilege}.groupProjectMembershipId`,
`${TableName.GroupProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.GroupProjectMembershipRole}.customRoleId`,
@@ -82,34 +77,11 @@ export const permissionDALFactory = (db: TDbClient) => {
db.ref("projectId").withSchema(TableName.GroupProjectMembership),
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("orgId").withSchema(TableName.Project),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
db.ref("permissions").withSchema(TableName.ProjectRoles)
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug")
)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
.select(
db.ref("projectId").withSchema(TableName.GroupProjectMembership).as("groupMembershipProjectId"),
db.ref("id").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApId"),
db.ref("permissions").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApPermissions"),
db.ref("temporaryMode").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApIsTemporary"),
db.ref("temporaryRange").withSchema(TableName.GroupProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
db.ref("groupProjectMembershipId").withSchema(TableName.GroupProjectUserAdditionalPrivilege),
db
.ref("requestedByUserId")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("userApRequestedByUserId"),
.select("permissions");
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.GroupProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessEndTime")
);
const projectMemberDocs = await db(TableName.ProjectMembership)
const docs = await db(TableName.ProjectMembership)
.join(
TableName.ProjectUserMembershipRole,
`${TableName.ProjectUserMembershipRole}.projectMembershipId`,
@@ -155,7 +127,7 @@ export const permissionDALFactory = (db: TDbClient) => {
);
const permission = sqlNestRelationships({
data: projectMemberDocs,
data: docs,
key: "projectId",
parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({
orgId,
@@ -222,33 +194,6 @@ export const permissionDALFactory = (db: TDbClient) => {
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
},
{
key: "userApId",
label: "additionalPrivileges" as const,
mapper: ({
groupMembershipProjectId,
groupProjectMembershipId,
userApId,
userApPermissions,
userApRequestedByUserId,
userApIsTemporary,
userApTemporaryMode,
userApTemporaryRange,
userApTemporaryAccessEndTime,
userApTemporaryAccessStartTime
}) => ({
groupProjectMembershipId,
groupMembershipProjectId,
id: userApId,
userId: userApRequestedByUserId,
permissions: userApPermissions,
temporaryRange: userApTemporaryRange,
temporaryMode: userApTemporaryMode,
temporaryAccessEndTime: userApTemporaryAccessEndTime,
temporaryAccessStartTime: userApTemporaryAccessStartTime,
isTemporary: userApIsTemporary
})
}
]
})
@@ -269,24 +214,15 @@ export const permissionDALFactory = (db: TDbClient) => {
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
) ?? [];
const activeAdditionalPrivileges =
permission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
) ?? [];
const activeGroupAdditionalPrivileges =
groupPermission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime, groupProjectMembershipId, groupMembershipProjectId, userId: user }) =>
groupMembershipProjectId === projectId &&
!!groupProjectMembershipId &&
user === userId &&
(!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime))
) ?? [];
const activeAdditionalPrivileges = permission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
);
return {
...(permission[0] || groupPermission[0]),
roles: [...activeRoles, ...activeGroupRoles],
additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges]
additionalPrivileges: activeAdditionalPrivileges
};
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectPermission" });

View File

@@ -90,10 +90,6 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
// This is fine. This service is only used for direct user privileges, not group-based privileges
if (!userPrivilege.projectMembershipId)
throw new BadRequestError({ message: "Operation not supported for groups" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
@@ -142,10 +138,6 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
// This is fine. This service is only used for direct user privileges, not group-based privileges
if (!userPrivilege.projectMembershipId)
throw new BadRequestError({ message: "Operation not supported for groups" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });
@@ -172,10 +164,6 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({
const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId);
if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" });
// This is fine. This service is only used for direct user privileges, not group-based privileges
if (!userPrivilege.projectMembershipId)
throw new BadRequestError({ message: "Operation not supported for groups" });
const projectMembership = await projectMembershipDAL.findById(userPrivilege.projectMembershipId);
if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" });

View File

@@ -7,7 +7,8 @@ import {
SecretKeyEncoding,
TableName,
TSamlConfigs,
TSamlConfigsUpdate
TSamlConfigsUpdate,
TUsers
} from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import {
@@ -19,10 +20,18 @@ import {
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { AuthTokenType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TokenType } from "@app/services/auth-token/auth-token-types";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
@@ -31,15 +40,19 @@ import { TSamlConfigDALFactory } from "./saml-config-dal";
import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
type TSamlConfigServiceFactoryDep = {
samlConfigDAL: TSamlConfigDALFactory;
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById">;
samlConfigDAL: Pick<TSamlConfigDALFactory, "create" | "findOne" | "update" | "findById">;
userDAL: Pick<TUserDALFactory, "create" | "findOne" | "transaction" | "updateById" | "findById">;
userAliasDAL: Pick<TUserAliasDALFactory, "create" | "findOne">;
orgDAL: Pick<
TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "create">;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
tokenService: Pick<TAuthTokenServiceFactory, "createTokenForUser">;
smtpService: Pick<TSmtpService, "sendMail">;
};
export type TSamlConfigServiceFactory = ReturnType<typeof samlConfigServiceFactory>;
@@ -48,9 +61,13 @@ export const samlConfigServiceFactory = ({
samlConfigDAL,
orgBotDAL,
orgDAL,
orgMembershipDAL,
userDAL,
userAliasDAL,
permissionService,
licenseService
licenseService,
tokenService,
smtpService
}: TSamlConfigServiceFactoryDep) => {
const createSamlCfg = async ({
cert,
@@ -305,7 +322,7 @@ export const samlConfigServiceFactory = ({
};
const samlLogin = async ({
username,
externalId,
email,
firstName,
lastName,
@@ -314,38 +331,40 @@ export const samlConfigServiceFactory = ({
relayState
}: TSamlLoginDTO) => {
const appCfg = getConfig();
let user = await userDAL.findOne({ username });
const serverCfg = await getServerCfg();
const userAlias = await userAliasDAL.findOne({
externalId,
orgId,
aliasType: UserAliasType.SAML
});
const organization = await orgDAL.findOrgById(orgId);
if (!organization) throw new BadRequestError({ message: "Org not found" });
// TODO(dangtony98): remove this after aliases update
if (authProvider === AuthMethod.KEYCLOAK_SAML && appCfg.LICENSE_SERVER_KEY) {
throw new BadRequestError({ message: "Keycloak SAML is not yet available on Infisical Cloud" });
}
if (user) {
await userDAL.transaction(async (tx) => {
let user: TUsers;
if (userAlias) {
user = await userDAL.transaction(async (tx) => {
const foundUser = await userDAL.findById(userAlias.userId, tx);
const [orgMembership] = await orgDAL.findMembership(
{
userId: user.id,
[`${TableName.OrgMembership}.userId` as "userId"]: foundUser.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
{ tx }
);
if (!orgMembership) {
await orgDAL.createMembership(
await orgMembershipDAL.create(
{
userId: user.id,
orgId,
userId: userAlias.userId,
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
status: foundUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
},
tx
);
// Only update the membership to Accepted if the user account is already completed.
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
} else if (orgMembership.status === OrgMembershipStatus.Invited && foundUser.isAccepted) {
await orgDAL.updateMembershipById(
orgMembership.id,
{
@@ -354,40 +373,97 @@ export const samlConfigServiceFactory = ({
tx
);
}
return foundUser;
});
} else {
user = await userDAL.transaction(async (tx) => {
const newUser = await userDAL.create(
let newUser: TUsers | undefined;
if (serverCfg.trustSamlEmails) {
newUser = await userDAL.findOne(
{
email,
isEmailVerified: true
},
tx
);
}
if (!newUser) {
const uniqueUsername = await normalizeUsername(`${firstName ?? ""}-${lastName ?? ""}`, userDAL);
newUser = await userDAL.create(
{
username: serverCfg.trustSamlEmails ? email : uniqueUsername,
email,
isEmailVerified: serverCfg.trustSamlEmails,
firstName,
lastName,
authMethods: [],
isGhost: false
},
tx
);
}
await userAliasDAL.create(
{
username,
email,
firstName,
lastName,
authMethods: [AuthMethod.EMAIL],
isGhost: false
userId: newUser.id,
aliasType: UserAliasType.SAML,
externalId,
emails: email ? [email] : [],
orgId
},
tx
);
await orgDAL.createMembership({
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: OrgMembershipStatus.Invited
});
const [orgMembership] = await orgDAL.findMembership(
{
[`${TableName.OrgMembership}.userId` as "userId"]: newUser.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
{ tx }
);
if (!orgMembership) {
await orgMembershipDAL.create(
{
userId: newUser.id,
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: newUser.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
},
tx
);
// Only update the membership to Accepted if the user account is already completed.
} else if (orgMembership.status === OrgMembershipStatus.Invited && newUser.isAccepted) {
await orgDAL.updateMembershipById(
orgMembership.id,
{
status: OrgMembershipStatus.Accepted
},
tx
);
}
return newUser;
});
}
const isUserCompleted = Boolean(user.isAccepted);
const providerAuthToken = jwt.sign(
{
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user.id,
username: user.username,
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
firstName,
lastName,
organizationName: organization.name,
organizationId: organization.id,
organizationSlug: organization.slug,
authMethod: authProvider,
authType: UserAliasType.SAML,
isUserCompleted,
...(relayState
? {
@@ -403,6 +479,22 @@ export const samlConfigServiceFactory = ({
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
if (user.email && !user.isEmailVerified) {
const token = await tokenService.createTokenForUser({
type: TokenType.TOKEN_EMAIL_VERIFICATION,
userId: user.id
});
await smtpService.sendMail({
template: SmtpTemplates.EmailVerification,
subjectLine: "Infisical confirmation code",
recipients: [user.email],
substitutions: {
code: token
}
});
}
return { isUserCompleted, providerAuthToken };
};

View File

@@ -45,8 +45,8 @@ export type TGetSamlCfgDTO =
};
export type TSamlLoginDTO = {
username: string;
email?: string;
externalId: string;
email: string;
firstName: string;
lastName?: string;
authProvider: string;

View File

@@ -2,31 +2,31 @@ import { TListScimGroups, TListScimUsers, TScimGroup, TScimUser } from "./scim-t
export const buildScimUserList = ({
scimUsers,
offset,
startIndex,
limit
}: {
scimUsers: TScimUser[];
offset: number;
startIndex: number;
limit: number;
}): TListScimUsers => {
return {
Resources: scimUsers,
itemsPerPage: limit,
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
startIndex: offset,
startIndex,
totalResults: scimUsers.length
};
};
export const buildScimUser = ({
userId,
orgMembershipId,
username,
email,
firstName,
lastName,
active
}: {
userId: string;
orgMembershipId: string;
username: string;
email?: string | null;
firstName: string;
@@ -35,7 +35,7 @@ export const buildScimUser = ({
}): TScimUser => {
const scimUser = {
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
id: userId,
id: orgMembershipId,
userName: username,
displayName: `${firstName} ${lastName}`,
name: {
@@ -65,18 +65,18 @@ export const buildScimUser = ({
export const buildScimGroupList = ({
scimGroups,
offset,
startIndex,
limit
}: {
scimGroups: TScimGroup[];
offset: number;
startIndex: number;
limit: number;
}): TListScimGroups => {
return {
Resources: scimGroups,
itemsPerPage: limit,
schemas: ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
startIndex: offset,
startIndex,
totalResults: scimGroups.length
};
};

View File

@@ -2,7 +2,7 @@ import { ForbiddenError } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import jwt from "jsonwebtoken";
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups } from "@app/db/schemas";
import { OrgMembershipRole, OrgMembershipStatus, TableName, TGroups, TOrgMemberships, TUsers } from "@app/db/schemas";
import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { addUsersToGroupByUserIds, removeUsersFromGroupByUserIds } from "@app/ee/services/group/group-fns";
import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-group-membership-dal";
@@ -11,23 +11,25 @@ import { getConfig } from "@app/lib/config/env";
import { BadRequestError, ScimRequestError, UnauthorizedError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TOrgPermission } from "@app/lib/types";
import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { AuthTokenType } from "@app/services/auth/auth-type";
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { deleteOrgMembership } from "@app/services/org/org-fns";
import { deleteOrgMembershipFn } from "@app/services/org/org-fns";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { normalizeUsername } from "@app/services/user/user-fns";
import { TUserAliasDALFactory } from "@app/services/user-alias/user-alias-dal";
import { UserAliasType } from "@app/services/user-alias/user-alias-types";
import { TAccessApprovalRequestDALFactory } from "../access-approval-request/access-approval-request-dal";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { TSecretApprovalPolicyDALFactory } from "../secret-approval-policy/secret-approval-policy-dal";
import { TSecretApprovalRequestDALFactory } from "../secret-approval-request/secret-approval-request-dal";
import { buildScimGroup, buildScimGroupList, buildScimUser, buildScimUserList } from "./scim-fns";
import {
TCreateScimGroupDTO,
@@ -50,27 +52,32 @@ import {
type TScimServiceFactoryDep = {
scimDAL: Pick<TScimDALFactory, "create" | "find" | "findById" | "deleteById">;
userDAL: Pick<TUserDALFactory, "find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch">;
userDAL: Pick<
TUserDALFactory,
"find" | "findOne" | "create" | "transaction" | "findUserEncKeyByUserIdsBatch" | "findById"
>;
userAliasDAL: Pick<TUserAliasDALFactory, "findOne" | "create" | "delete">;
orgDAL: Pick<
TOrgDALFactory,
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction"
"createMembership" | "findById" | "findMembership" | "deleteMembershipById" | "transaction" | "updateMembershipById"
>;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find" | "findOne" | "create" | "updateById">;
projectDAL: Pick<TProjectDALFactory, "find" | "findProjectGhostUser">;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete">;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete" | "findProjectMembershipsByUserId">;
groupDAL: Pick<
TGroupDALFactory,
"create" | "findOne" | "findAllGroupMembers" | "update" | "delete" | "findGroups" | "transaction"
>;
groupProjectDAL: Pick<TGroupProjectDALFactory, "find">;
userGroupMembershipDAL: TUserGroupMembershipDALFactory; // TODO: Pick
userGroupMembershipDAL: Pick<
TUserGroupMembershipDALFactory,
"find" | "transaction" | "insertMany" | "filterProjectsByUserMembership" | "delete"
>;
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "findLatestProjectKey" | "insertMany" | "delete">;
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
licenseService: Pick<TLicenseServiceFactory, "getPlan" | "updateSubscriptionOrgMemberCount">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
secretApprovalRequestDAL: Pick<TSecretApprovalRequestDALFactory, "delete">;
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "delete">;
secretApprovalPolicyDAL: Pick<TSecretApprovalPolicyDALFactory, "findByProjectIds">;
smtpService: TSmtpService;
smtpService: Pick<TSmtpService, "sendMail">;
};
export type TScimServiceFactory = ReturnType<typeof scimServiceFactory>;
@@ -79,7 +86,9 @@ export const scimServiceFactory = ({
licenseService,
scimDAL,
userDAL,
userAliasDAL,
orgDAL,
orgMembershipDAL,
projectDAL,
projectMembershipDAL,
groupDAL,
@@ -87,9 +96,6 @@ export const scimServiceFactory = ({
userGroupMembershipDAL,
projectKeyDAL,
projectBotDAL,
accessApprovalRequestDAL,
secretApprovalRequestDAL,
secretApprovalPolicyDAL,
permissionService,
smtpService
}: TScimServiceFactoryDep) => {
@@ -169,7 +175,7 @@ export const scimServiceFactory = ({
};
// SCIM server endpoints
const listScimUsers = async ({ offset, limit, filter, orgId }: TListScimUsersDTO): Promise<TListScimUsers> => {
const listScimUsers = async ({ startIndex, limit, filter, orgId }: TListScimUsersDTO): Promise<TListScimUsers> => {
const org = await orgDAL.findById(orgId);
if (!org.scimEnabled)
@@ -187,11 +193,11 @@ export const scimServiceFactory = ({
attributeName = "email";
}
return { [attributeName]: parsedValue };
return { [attributeName]: parsedValue.replace(/"/g, "") };
};
const findOpts = {
...(offset && { offset }),
...(startIndex && { offset: startIndex - 1 }),
...(limit && { limit })
};
@@ -203,10 +209,10 @@ export const scimServiceFactory = ({
findOpts
);
const scimUsers = users.map(({ userId, username, firstName, lastName, email }) =>
const scimUsers = users.map(({ id, externalId, username, firstName, lastName, email }) =>
buildScimUser({
userId: userId ?? "",
username,
orgMembershipId: id ?? "",
username: externalId ?? username,
firstName: firstName ?? "",
lastName: lastName ?? "",
email,
@@ -216,16 +222,16 @@ export const scimServiceFactory = ({
return buildScimUserList({
scimUsers,
offset,
startIndex,
limit
});
};
const getScimUser = async ({ userId, orgId }: TGetScimUserDTO) => {
const getScimUser = async ({ orgMembershipId, orgId }: TGetScimUserDTO) => {
const [membership] = await orgDAL
.findMembership({
userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
})
.catch(() => {
throw new ScimRequestError({
@@ -247,8 +253,8 @@ export const scimServiceFactory = ({
});
return buildScimUser({
userId: membership.userId as string,
username: membership.username,
orgMembershipId: membership.id,
username: membership.externalId ?? membership.username,
email: membership.email ?? "",
firstName: membership.firstName as string,
lastName: membership.lastName as string,
@@ -256,7 +262,9 @@ export const scimServiceFactory = ({
});
};
const createScimUser = async ({ username, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
const createScimUser = async ({ externalId, email, firstName, lastName, orgId }: TCreateScimUserDTO) => {
if (!email) throw new ScimRequestError({ detail: "Invalid request. Missing email.", status: 400 });
const org = await orgDAL.findById(orgId);
if (!org)
@@ -271,67 +279,121 @@ export const scimServiceFactory = ({
status: 403
});
let user = await userDAL.findOne({
username
const appCfg = getConfig();
const serverCfg = await getServerCfg();
const userAlias = await userAliasDAL.findOne({
externalId,
orgId,
aliasType: UserAliasType.SAML
});
if (user) {
await userDAL.transaction(async (tx) => {
const [orgMembership] = await orgDAL.findMembership(
const { user: createdUser, orgMembership: createdOrgMembership } = await userDAL.transaction(async (tx) => {
let user: TUsers | undefined;
let orgMembership: TOrgMemberships;
if (userAlias) {
user = await userDAL.findById(userAlias.userId, tx);
orgMembership = await orgMembershipDAL.findOne(
{
userId: user.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
orgId
},
{ tx }
tx
);
if (orgMembership)
throw new ScimRequestError({
detail: "User already exists in the database",
status: 409
});
if (!orgMembership) {
await orgDAL.createMembership(
orgMembership = await orgMembershipDAL.create(
{
userId: user.id,
orgId,
userId: userAlias.userId,
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: OrgMembershipStatus.Invited
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
},
tx
);
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
orgMembership = await orgMembershipDAL.updateById(
orgMembership.id,
{
status: OrgMembershipStatus.Accepted
},
tx
);
}
});
} else {
user = await userDAL.transaction(async (tx) => {
const newUser = await userDAL.create(
} else {
if (serverCfg.trustSamlEmails) {
user = await userDAL.findOne(
{
email,
isEmailVerified: true
},
tx
);
}
if (!user) {
const uniqueUsername = await normalizeUsername(`${firstName}-${lastName}`, userDAL);
user = await userDAL.create(
{
username: serverCfg.trustSamlEmails ? email : uniqueUsername,
email,
isEmailVerified: serverCfg.trustSamlEmails,
firstName,
lastName,
authMethods: [],
isGhost: false
},
tx
);
}
await userAliasDAL.create(
{
username,
email,
firstName,
lastName,
authMethods: [AuthMethod.EMAIL],
isGhost: false
userId: user.id,
aliasType: UserAliasType.SAML,
externalId,
emails: email ? [email] : [],
orgId
},
tx
);
await orgDAL.createMembership(
const [foundOrgMembership] = await orgDAL.findMembership(
{
inviteEmail: email,
orgId,
userId: newUser.id,
role: OrgMembershipRole.Member,
status: OrgMembershipStatus.Invited
[`${TableName.OrgMembership}.userId` as "userId"]: user.id,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
},
tx
{ tx }
);
return newUser;
});
}
const appCfg = getConfig();
orgMembership = foundOrgMembership;
if (!orgMembership) {
orgMembership = await orgMembershipDAL.create(
{
userId: user.id,
inviteEmail: email,
orgId,
role: OrgMembershipRole.Member,
status: user.isAccepted ? OrgMembershipStatus.Accepted : OrgMembershipStatus.Invited // if user is fully completed, then set status to accepted, otherwise set it to invited so we can update it later
},
tx
);
// Only update the membership to Accepted if the user account is already completed.
} else if (orgMembership.status === OrgMembershipStatus.Invited && user.isAccepted) {
orgMembership = await orgDAL.updateMembershipById(
orgMembership.id,
{
status: OrgMembershipStatus.Accepted
},
tx
);
}
}
return { user, orgMembership };
});
if (email) {
await smtpService.sendMail({
@@ -346,20 +408,20 @@ export const scimServiceFactory = ({
}
return buildScimUser({
userId: user.id,
username: user.username,
firstName: user.firstName as string,
lastName: user.lastName as string,
email: user.email ?? "",
orgMembershipId: createdOrgMembership.id,
username: externalId,
firstName: createdUser.firstName as string,
lastName: createdUser.lastName as string,
email: createdUser.email ?? "",
active: true
});
};
const updateScimUser = async ({ userId, orgId, operations }: TUpdateScimUserDTO) => {
const updateScimUser = async ({ orgMembershipId, orgId, operations }: TUpdateScimUserDTO) => {
const [membership] = await orgDAL
.findMembership({
userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
})
.catch(() => {
throw new ScimRequestError({
@@ -395,18 +457,20 @@ export const scimServiceFactory = ({
});
if (!active) {
await deleteOrgMembership({
await deleteOrgMembershipFn({
orgMembershipId: membership.id,
orgId: membership.orgId,
orgDAL,
projectDAL,
projectMembershipDAL
projectMembershipDAL,
projectKeyDAL,
userAliasDAL,
licenseService
});
}
return buildScimUser({
userId: membership.userId as string,
username: membership.username,
orgMembershipId: membership.id,
username: membership.externalId ?? membership.username,
email: membership.email,
firstName: membership.firstName as string,
lastName: membership.lastName as string,
@@ -414,11 +478,11 @@ export const scimServiceFactory = ({
});
};
const replaceScimUser = async ({ userId, active, orgId }: TReplaceScimUserDTO) => {
const replaceScimUser = async ({ orgMembershipId, active, orgId }: TReplaceScimUserDTO) => {
const [membership] = await orgDAL
.findMembership({
userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
})
.catch(() => {
throw new ScimRequestError({
@@ -440,19 +504,20 @@ export const scimServiceFactory = ({
});
if (!active) {
// tx
await deleteOrgMembership({
await deleteOrgMembershipFn({
orgMembershipId: membership.id,
orgId: membership.orgId,
orgDAL,
projectDAL,
projectMembershipDAL
projectMembershipDAL,
projectKeyDAL,
userAliasDAL,
licenseService
});
}
return buildScimUser({
userId: membership.userId as string,
username: membership.username,
orgMembershipId: membership.id,
username: membership.externalId ?? membership.username,
email: membership.email,
firstName: membership.firstName as string,
lastName: membership.lastName as string,
@@ -460,18 +525,11 @@ export const scimServiceFactory = ({
});
};
const deleteScimUser = async ({ userId, orgId }: TDeleteScimUserDTO) => {
const [membership] = await orgDAL
.findMembership({
userId,
[`${TableName.OrgMembership}.orgId` as "id"]: orgId
})
.catch(() => {
throw new ScimRequestError({
detail: "User not found",
status: 404
});
});
const deleteScimUser = async ({ orgMembershipId, orgId }: TDeleteScimUserDTO) => {
const [membership] = await orgDAL.findMembership({
[`${TableName.OrgMembership}.id` as "id"]: orgMembershipId,
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId
});
if (!membership)
throw new ScimRequestError({
@@ -486,18 +544,20 @@ export const scimServiceFactory = ({
});
}
await deleteOrgMembership({
await deleteOrgMembershipFn({
orgMembershipId: membership.id,
orgId: membership.orgId,
orgDAL,
projectDAL,
projectMembershipDAL
projectMembershipDAL,
projectKeyDAL,
userAliasDAL,
licenseService
});
return {}; // intentionally return empty object upon success
};
const listScimGroups = async ({ orgId, offset, limit }: TListScimGroupsDTO) => {
const listScimGroups = async ({ orgId, startIndex, limit }: TListScimGroupsDTO) => {
const plan = await licenseService.getPlan(orgId);
if (!plan.groups)
throw new BadRequestError({
@@ -518,21 +578,27 @@ export const scimServiceFactory = ({
status: 403
});
const groups = await groupDAL.findGroups({
orgId
});
const groups = await groupDAL.findGroups(
{
orgId
},
{
offset: startIndex - 1,
limit
}
);
const scimGroups = groups.map((group) =>
buildScimGroup({
groupId: group.id,
name: group.name,
members: []
members: [] // does this need to be populated?
})
);
return buildScimGroupList({
scimGroups,
offset,
startIndex,
limit
});
};
@@ -571,9 +637,15 @@ export const scimServiceFactory = ({
);
if (members && members.length) {
const orgMemberships = await orgMembershipDAL.find({
$in: {
id: members.map((member) => member.value)
}
});
const newMembers = await addUsersToGroupByUserIds({
group,
userIds: members.map((member) => member.value),
userIds: orgMemberships.map((membership) => membership.userId as string),
userDAL,
userGroupMembershipDAL,
orgDAL,
@@ -590,12 +662,19 @@ export const scimServiceFactory = ({
return { group, newMembers: [] };
});
const orgMemberships = await orgDAL.findMembership({
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
$in: {
[`${TableName.OrgMembership}.userId` as "userId"]: newGroup.newMembers.map((member) => member.id)
}
});
return buildScimGroup({
groupId: newGroup.group.id,
name: newGroup.group.name,
members: newGroup.newMembers.map((member) => ({
value: member.id,
display: `${member.firstName} ${member.lastName}`
members: orgMemberships.map(({ id, firstName, lastName }) => ({
value: id,
display: `${firstName} ${lastName}`
}))
});
};
@@ -624,15 +703,22 @@ export const scimServiceFactory = ({
groupId: group.id
});
const orgMemberships = await orgDAL.findMembership({
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
$in: {
[`${TableName.OrgMembership}.userId` as "userId"]: users
.filter((user) => user.isPartOfGroup)
.map((user) => user.id)
}
});
return buildScimGroup({
groupId: group.id,
name: group.name,
members: users
.filter((user) => user.isPartOfGroup)
.map((user) => ({
value: user.id,
display: `${user.firstName} ${user.lastName}`
}))
members: orgMemberships.map(({ id, firstName, lastName }) => ({
value: id,
display: `${firstName} ${lastName}`
}))
});
};
@@ -676,7 +762,13 @@ export const scimServiceFactory = ({
}
if (members) {
const membersIdsSet = new Set(members.map((member) => member.value));
const orgMemberships = await orgMembershipDAL.find({
$in: {
id: members.map((member) => member.value)
}
});
const membersIdsSet = new Set(orgMemberships.map((orgMembership) => orgMembership.userId));
const directMemberUserIds = (
await userGroupMembershipDAL.find({
@@ -695,13 +787,13 @@ export const scimServiceFactory = ({
const allMembersUserIds = directMemberUserIds.concat(pendingGroupAdditionsUserIds);
const allMembersUserIdsSet = new Set(allMembersUserIds);
const toAddUserIds = members.filter((member) => !allMembersUserIdsSet.has(member.value));
const toAddUserIds = orgMemberships.filter((member) => !allMembersUserIdsSet.has(member.userId as string));
const toRemoveUserIds = allMembersUserIds.filter((userId) => !membersIdsSet.has(userId));
if (toAddUserIds.length) {
await addUsersToGroupByUserIds({
group,
userIds: toAddUserIds.map((member) => member.value),
userIds: toAddUserIds.map((member) => member.userId as string),
userDAL,
userGroupMembershipDAL,
orgDAL,
@@ -719,9 +811,6 @@ export const scimServiceFactory = ({
userIds: toRemoveUserIds,
userDAL,
userGroupMembershipDAL,
secretApprovalPolicyDAL,
accessApprovalRequestDAL,
secretApprovalRequestDAL,
groupProjectDAL,
projectKeyDAL,
tx

View File

@@ -12,7 +12,7 @@ export type TDeleteScimTokenDTO = {
// SCIM server endpoint types
export type TListScimUsersDTO = {
offset: number;
startIndex: number;
limit: number;
filter?: string;
orgId: string;
@@ -27,12 +27,12 @@ export type TListScimUsers = {
};
export type TGetScimUserDTO = {
userId: string;
orgMembershipId: string;
orgId: string;
};
export type TCreateScimUserDTO = {
username: string;
externalId: string;
email?: string;
firstName: string;
lastName: string;
@@ -40,7 +40,7 @@ export type TCreateScimUserDTO = {
};
export type TUpdateScimUserDTO = {
userId: string;
orgMembershipId: string;
orgId: string;
operations: {
op: string;
@@ -54,18 +54,18 @@ export type TUpdateScimUserDTO = {
};
export type TReplaceScimUserDTO = {
userId: string;
orgMembershipId: string;
active: boolean;
orgId: string;
};
export type TDeleteScimUserDTO = {
userId: string;
orgMembershipId: string;
orgId: string;
};
export type TListScimGroupsDTO = {
offset: number;
startIndex: number;
limit: number;
orgId: string;
};

View File

@@ -20,7 +20,7 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalPolicy}.id`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.select(tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover))
.select(tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover))
.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"))
@@ -33,18 +33,18 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
const doc = await sapFindQuery(tx || db, {
[`${TableName.SecretApprovalPolicy}.id` as "id"]: id
});
const formattedDoc = mergeOneToManyRelation(
const formatedDoc = mergeOneToManyRelation(
doc,
"id",
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
}),
({ approverUserId }) => approverUserId,
({ approverId }) => approverId,
"approvers"
);
return formattedDoc?.[0];
return formatedDoc?.[0];
} catch (error) {
throw new DatabaseError({ error, name: "FindById" });
}
@@ -53,31 +53,22 @@ export const secretApprovalPolicyDALFactory = (db: TDbClient) => {
const find = async (filter: TFindFilter<TSecretApprovalPolicies & { projectId: string }>, tx?: Knex) => {
try {
const docs = await sapFindQuery(tx || db, filter);
const formattedDoc = mergeOneToManyRelation(
const formatedDoc = mergeOneToManyRelation(
docs,
"id",
({ approverUserId, envId, envName: name, envSlug: slug, ...el }) => ({
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
}),
({ approverUserId }) => approverUserId,
({ approverId }) => approverId,
"approvers"
);
return formattedDoc;
return formatedDoc;
} catch (error) {
throw new DatabaseError({ error, name: "Find" });
}
};
const findByProjectIds = async (projectIds: string[], tx?: Knex) => {
const policies = await (tx || db)(TableName.SecretApprovalPolicy)
.join(TableName.Environment, `${TableName.SecretApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.whereIn(`${TableName.Environment}.projectId`, projectIds)
.select(selectAllTableCols(TableName.SecretApprovalPolicy));
return policies;
};
return { ...secretApprovalPolicyOrm, findById, find, findByProjectIds };
return { ...secretApprovalPolicyOrm, findById, find };
};

View File

@@ -7,7 +7,6 @@ import { BadRequestError } from "@app/lib/errors";
import { containsGlobPatterns } from "@app/lib/picomatch";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
import { TUserDALFactory } from "@app/services/user/user-dal";
import { TSecretApprovalPolicyApproverDALFactory } from "./secret-approval-policy-approver-dal";
import { TSecretApprovalPolicyDALFactory } from "./secret-approval-policy-dal";
@@ -30,7 +29,6 @@ type TSecretApprovalPolicyServiceFactoryDep = {
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne">;
secretApprovalPolicyApproverDAL: TSecretApprovalPolicyApproverDALFactory;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
userDAL: Pick<TUserDALFactory, "findUsersByProjectId" | "findUserByProjectId">;
};
export type TSecretApprovalPolicyServiceFactory = ReturnType<typeof secretApprovalPolicyServiceFactory>;
@@ -40,7 +38,7 @@ export const secretApprovalPolicyServiceFactory = ({
permissionService,
secretApprovalPolicyApproverDAL,
projectEnvDAL,
userDAL
projectMembershipDAL
}: TSecretApprovalPolicyServiceFactoryDep) => {
const createSecretApprovalPolicy = async ({
name,
@@ -71,12 +69,11 @@ export const secretApprovalPolicyServiceFactory = ({
const env = await projectEnvDAL.findOne({ slug: environment, projectId });
if (!env) throw new BadRequestError({ message: "Environment not found" });
const secretApproverUsers = await userDAL.findUsersByProjectId(
const secretApprovers = await projectMembershipDAL.find({
projectId,
approvers.map((approverUserId) => approverUserId)
);
if (secretApproverUsers.length !== approvers.length)
$in: { id: approvers }
});
if (secretApprovers.length !== approvers.length)
throw new BadRequestError({ message: "Approver not found in project" });
const secretApproval = await secretApprovalPolicyDAL.transaction(async (tx) => {
@@ -90,8 +87,8 @@ export const secretApprovalPolicyServiceFactory = ({
tx
);
await secretApprovalPolicyApproverDAL.insertMany(
secretApproverUsers.map(({ id }) => ({
approverUserId: id,
secretApprovers.map(({ id }) => ({
approverId: id,
policyId: doc.id
})),
tx
@@ -135,19 +132,21 @@ export const secretApprovalPolicyServiceFactory = ({
tx
);
if (approvers) {
const secretApproverUsers = await userDAL.findUsersByProjectId(
secretApprovalPolicy.projectId,
approvers.map((approverUserId) => approverUserId)
const secretApprovers = await projectMembershipDAL.find(
{
projectId: secretApprovalPolicy.projectId,
$in: { id: approvers }
},
{ tx }
);
if (secretApproverUsers.length !== approvers.length)
if (secretApprovers.length !== approvers.length)
throw new BadRequestError({ message: "Approver not found in project" });
if (doc.approvals > secretApproverUsers.length)
if (doc.approvals > secretApprovers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
await secretApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
await secretApprovalPolicyApproverDAL.insertMany(
secretApproverUsers.map((user) => ({
approverUserId: user.id,
secretApprovers.map(({ id }) => ({
approverId: id,
policyId: doc.id
})),
tx

View File

@@ -16,7 +16,7 @@ export type TSecretApprovalRequestDALFactory = ReturnType<typeof secretApprovalR
type TFindQueryFilter = {
projectId: string;
actorId: string;
membershipId: string;
status?: RequestState;
environment?: string;
committer?: string;
@@ -49,7 +49,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
)
.select(selectAllTableCols(TableName.SecretApprovalRequest))
.select(
tx.ref("memberUserId").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
tx.ref("member").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
tx.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
tx.ref("id").withSchema(TableName.SecretApprovalPolicy).as("policyId"),
tx.ref("name").withSchema(TableName.SecretApprovalPolicy).as("policyName"),
@@ -57,14 +57,14 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
tx.ref("slug").withSchema(TableName.Environment).as("environment"),
tx.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
tx.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
tx.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover)
tx.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
);
const findById = async (id: string, tx?: Knex) => {
try {
const sql = findQuery({ [`${TableName.SecretApprovalRequest}.id` as "id"]: id }, tx || db);
const docs = await sql;
const formattedDoc = sqlNestRelationships({
const formatedDoc = sqlNestRelationships({
data: docs,
key: "id",
parentMapper: (el) => ({
@@ -84,20 +84,20 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
},
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
]
});
if (!formattedDoc?.[0]) return;
if (!formatedDoc?.[0]) return;
return {
...formattedDoc[0],
policy: { ...formattedDoc[0].policy, approvers: formattedDoc[0].approvers }
...formatedDoc[0],
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
};
} catch (error) {
throw new DatabaseError({ error, name: "FindByIdSAR" });
}
};
const findProjectRequestCount = async (projectId: string, approverUserId: string, tx?: Knex) => {
const findProjectRequestCount = async (projectId: string, membershipId: string, tx?: Knex) => {
try {
const docs = await (tx || db)
.with(
@@ -110,12 +110,12 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.SecretApprovalRequest}.policyId`,
`${TableName.SecretApprovalPolicyApprover}.policyId`
)
.where({ [`${TableName.Environment}.projectId` as "projectId"]: projectId })
.where({ projectId })
.andWhere(
(bd) =>
void bd
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, approverUserId)
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, approverUserId)
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
)
.select("status", `${TableName.SecretApprovalRequest}.id`)
.groupBy(`${TableName.SecretApprovalRequest}.id`, "status")
@@ -142,7 +142,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
};
const findByProjectId = async (
{ status, limit = 20, offset = 0, projectId, committer, environment, actorId }: TFindQueryFilter,
{ status, limit = 20, offset = 0, projectId, committer, environment, membershipId }: TFindQueryFilter,
tx?: Knex
) => {
try {
@@ -173,21 +173,21 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
)
.where(
stripUndefinedInWhere({
[`${TableName.Environment}.projectId`]: projectId,
projectId,
[`${TableName.Environment}.slug` as "slug"]: environment,
[`${TableName.SecretApprovalRequest}.status`]: status,
committerUserId: committer
committerId: committer
})
)
.andWhere(
(bd) =>
void bd
.where(`${TableName.SecretApprovalPolicyApprover}.approverUserId`, actorId)
.orWhere(`${TableName.SecretApprovalRequest}.committerUserId`, actorId)
.where(`${TableName.SecretApprovalPolicyApprover}.approverId`, membershipId)
.orWhere(`${TableName.SecretApprovalRequest}.committerId`, membershipId)
)
.select(selectAllTableCols(TableName.SecretApprovalRequest))
.select(
db.ref("projectId").withSchema(TableName.Environment).as("envProjectId"),
db.ref("projectId").withSchema(TableName.Environment),
db.ref("slug").withSchema(TableName.Environment).as("environment"),
db.ref("id").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerMemberId"),
db.ref("status").withSchema(TableName.SecretApprovalRequestReviewer).as("reviewerStatus"),
@@ -201,7 +201,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
),
db.ref("secretPath").withSchema(TableName.SecretApprovalPolicy).as("policySecretPath"),
db.ref("approvals").withSchema(TableName.SecretApprovalPolicy).as("policyApprovals"),
db.ref("approverUserId").withSchema(TableName.SecretApprovalPolicyApprover)
db.ref("approverId").withSchema(TableName.SecretApprovalPolicyApprover)
)
.orderBy("createdAt", "desc");
@@ -217,7 +217,7 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
parentMapper: (el) => ({
...SecretApprovalRequestsSchema.parse(el),
environment: el.environment,
projectId: el.envProjectId,
projectId: el.projectId,
policy: {
id: el.policyId,
name: el.policyName,
@@ -232,9 +232,9 @@ export const secretApprovalRequestDALFactory = (db: TDbClient) => {
mapper: ({ reviewerMemberId: member, reviewerStatus: s }) => (member ? { member, status: s } : undefined)
},
{
key: "approverUserId",
key: "approverId",
label: "approvers" as const,
mapper: ({ approverUserId }) => approverUserId
mapper: ({ approverId }) => approverId
},
{
key: "commitId",

View File

@@ -113,7 +113,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
db.ref("secretCommentTag").withSchema(TableName.SecretVersion).as("secVerCommentTag"),
db.ref("secretCommentCiphertext").withSchema(TableName.SecretVersion).as("secVerCommentCiphertext")
);
const formattedDoc = sqlNestRelationships({
const formatedDoc = sqlNestRelationships({
data: doc,
key: "id",
parentMapper: (data) => SecretApprovalRequestsSecretsSchema.omit({ secretVersion: true }).parse(data),
@@ -212,7 +212,7 @@ export const secretApprovalRequestSecretDALFactory = (db: TDbClient) => {
}
]
});
return formattedDoc?.map(({ secret, secretVersion, ...el }) => ({
return formatedDoc?.map(({ secret, secretVersion, ...el }) => ({
...el,
secret: secret?.[0],
secretVersion: secretVersion?.[0]

View File

@@ -7,14 +7,24 @@ import {
SecretType,
TSecretApprovalRequestsSecretsInsert
} from "@app/db/schemas";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { groupBy, pick, unique } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { ActorType } from "@app/services/auth/auth-type";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TSecretDALFactory } from "@app/services/secret/secret-dal";
import {
fnSecretBlindIndexCheck,
fnSecretBlindIndexCheckV2,
fnSecretBulkDelete,
fnSecretBulkInsert,
fnSecretBulkUpdate,
getAllNestedSecretReferences
} from "@app/services/secret/secret-fns";
import { TSecretQueueFactory } from "@app/services/secret/secret-queue";
import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { SecretOperations } from "@app/services/secret/secret-types";
import { TSecretVersionDALFactory } from "@app/services/secret/secret-version-dal";
import { TSecretVersionTagDALFactory } from "@app/services/secret/secret-version-tag-dal";
import { TSecretBlindIndexDALFactory } from "@app/services/secret-blind-index/secret-blind-index-dal";
@@ -29,7 +39,6 @@ import { TSecretApprovalRequestReviewerDALFactory } from "./secret-approval-requ
import { TSecretApprovalRequestSecretDALFactory } from "./secret-approval-request-secret-dal";
import {
ApprovalStatus,
CommitType,
RequestState,
TApprovalRequestCountDTO,
TGenerateSecretApprovalRequestDTO,
@@ -42,10 +51,11 @@ import {
type TSecretApprovalRequestServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
secretApprovalRequestDAL: TSecretApprovalRequestDALFactory;
secretApprovalRequestSecretDAL: TSecretApprovalRequestSecretDALFactory;
secretApprovalRequestReviewerDAL: TSecretApprovalRequestReviewerDALFactory;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findById" | "findSecretPathByFolderIds">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findSecretPathByFolderIds">;
secretDAL: TSecretDALFactory;
secretTagDAL: Pick<TSecretTagDALFactory, "findManyTagsById" | "saveTagsToSecret" | "deleteTagsManySecret">;
secretBlindIndexDAL: Pick<TSecretBlindIndexDALFactory, "findOne">;
@@ -53,15 +63,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
secretVersionDAL: Pick<TSecretVersionDALFactory, "findLatestVersionMany" | "insertMany">;
secretVersionTagDAL: Pick<TSecretVersionTagDALFactory, "insertMany">;
projectDAL: Pick<TProjectDALFactory, "checkProjectUpgradeStatus">;
secretService: Pick<
TSecretServiceFactory,
| "fnSecretBulkInsert"
| "fnSecretBulkUpdate"
| "fnSecretBlindIndexCheck"
| "fnSecretBulkDelete"
| "fnSecretBlindIndexCheckV2"
>;
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets">;
secretQueueService: Pick<TSecretQueueFactory, "syncSecrets" | "removeSecretReminder">;
};
export type TSecretApprovalRequestServiceFactory = ReturnType<typeof secretApprovalRequestServiceFactory>;
@@ -78,14 +80,14 @@ export const secretApprovalRequestServiceFactory = ({
projectDAL,
permissionService,
snapshotService,
secretService,
secretVersionDAL,
secretQueueService
secretQueueService,
projectBotService
}: TSecretApprovalRequestServiceFactoryDep) => {
const requestCount = async ({ projectId, actor, actorId, actorOrgId, actorAuthMethod }: TApprovalRequestCountDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
await permissionService.getProjectPermission(
const { membership } = await permissionService.getProjectPermission(
actor as ActorType.USER,
actorId,
projectId,
@@ -93,7 +95,7 @@ export const secretApprovalRequestServiceFactory = ({
actorOrgId
);
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, actorId);
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
return count;
};
@@ -111,14 +113,19 @@ export const secretApprovalRequestServiceFactory = ({
}: TListApprovalsDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
await permissionService.getProjectPermission(actor, actorId, projectId, actorAuthMethod, actorOrgId);
const { membership } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
const approvals = await secretApprovalRequestDAL.findByProjectId({
projectId,
committer,
environment,
status,
actorId,
membershipId: membership.id,
limit,
offset
});
@@ -138,7 +145,7 @@ export const secretApprovalRequestServiceFactory = ({
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
const { policy } = secretApprovalRequest;
const { hasRole } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
actor,
actorId,
secretApprovalRequest.projectId,
@@ -147,8 +154,8 @@ export const secretApprovalRequestServiceFactory = ({
);
if (
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerUserId !== actorId &&
!policy.approvers.find((approverUserId) => approverUserId === actorId)
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
throw new UnauthorizedError({ message: "User has no access" });
}
@@ -173,7 +180,7 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy } = secretApprovalRequest;
const { hasRole } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
@@ -182,8 +189,8 @@ export const secretApprovalRequestServiceFactory = ({
);
if (
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerUserId !== actorId &&
!policy.approvers.find((approverUserId) => approverUserId === actorId)
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
throw new UnauthorizedError({ message: "User has no access" });
}
@@ -191,7 +198,7 @@ export const secretApprovalRequestServiceFactory = ({
const review = await secretApprovalRequestReviewerDAL.findOne(
{
requestId: secretApprovalRequest.id,
memberUserId: actorId
member: membership.id
},
tx
);
@@ -200,7 +207,7 @@ export const secretApprovalRequestServiceFactory = ({
{
status,
requestId: secretApprovalRequest.id,
memberUserId: actorId
member: membership.id
},
tx
);
@@ -223,7 +230,7 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy } = secretApprovalRequest;
const { hasRole } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
secretApprovalRequest.projectId,
@@ -232,8 +239,8 @@ export const secretApprovalRequestServiceFactory = ({
);
if (
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerUserId !== actorId &&
!policy.approvers.find((approverUserId) => approverUserId === actorId)
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
throw new UnauthorizedError({ message: "User has no access" });
}
@@ -246,9 +253,8 @@ export const secretApprovalRequestServiceFactory = ({
const updatedRequest = await secretApprovalRequestDAL.updateById(secretApprovalRequest.id, {
status,
statusChangeByUserId: actorId
statusChangeBy: membership.id
});
return { ...secretApprovalRequest, ...updatedRequest };
};
@@ -264,7 +270,7 @@ export const secretApprovalRequestServiceFactory = ({
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy, folderId, projectId } = secretApprovalRequest;
const { hasRole } = await permissionService.getProjectPermission(
const { membership, hasRole } = await permissionService.getProjectPermission(
ActorType.USER,
actorId,
projectId,
@@ -274,8 +280,8 @@ export const secretApprovalRequestServiceFactory = ({
if (
!hasRole(ProjectMembershipRole.Admin) &&
secretApprovalRequest.committerUserId !== actorId &&
!policy.approvers.find((approverUserId) => approverUserId === actorId)
secretApprovalRequest.committerId !== membership.id &&
!policy.approvers.find((approverId) => approverId === membership.id)
) {
throw new UnauthorizedError({ message: "User has no access" });
}
@@ -286,18 +292,19 @@ export const secretApprovalRequestServiceFactory = ({
const hasMinApproval =
secretApprovalRequest.policy.approvals <=
secretApprovalRequest.policy.approvers.filter(
(approverUserId) => reviewers[approverUserId.toString()] === ApprovalStatus.APPROVED
(approverId) => reviewers[approverId.toString()] === ApprovalStatus.APPROVED
).length;
if (!hasMinApproval) throw new BadRequestError({ message: "Doesn't have minimum approvals needed" });
const secretApprovalSecrets = await secretApprovalRequestSecretDAL.findByRequestId(secretApprovalRequest.id);
if (!secretApprovalSecrets) throw new BadRequestError({ message: "No secrets found" });
const conflicts: Array<{ secretId: string; op: CommitType }> = [];
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Create);
const conflicts: Array<{ secretId: string; op: SecretOperations }> = [];
let secretCreationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Create);
if (secretCreationCommits.length) {
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
folderId,
secretDAL,
inputSecrets: secretCreationCommits.map(({ secretBlindIndex }) => {
if (!secretBlindIndex) {
throw new BadRequestError({
@@ -310,17 +317,19 @@ export const secretApprovalRequestServiceFactory = ({
secretCreationCommits
.filter(({ secretBlindIndex }) => conflictGroupByBlindIndex[secretBlindIndex || ""])
.forEach((el) => {
conflicts.push({ op: CommitType.Create, secretId: el.id });
conflicts.push({ op: SecretOperations.Create, secretId: el.id });
});
secretCreationCommits = secretCreationCommits.filter(
({ secretBlindIndex }) => !conflictGroupByBlindIndex[secretBlindIndex || ""]
);
}
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Update);
let secretUpdationCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Update);
if (secretUpdationCommits.length) {
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await secretService.fnSecretBlindIndexCheckV2({
const { secsGroupedByBlindIndex: conflictGroupByBlindIndex } = await fnSecretBlindIndexCheckV2({
folderId,
secretDAL,
userId: "",
inputSecrets: secretUpdationCommits
.filter(({ secretBlindIndex, secret }) => secret && secret.secretBlindIndex !== secretBlindIndex)
.map(({ secretBlindIndex }) => {
@@ -338,7 +347,7 @@ export const secretApprovalRequestServiceFactory = ({
(secretBlindIndex && conflictGroupByBlindIndex[secretBlindIndex]) || !secretId
)
.forEach((el) => {
conflicts.push({ op: CommitType.Update, secretId: el.id });
conflicts.push({ op: SecretOperations.Update, secretId: el.id });
});
secretUpdationCommits = secretUpdationCommits.filter(
@@ -347,11 +356,11 @@ export const secretApprovalRequestServiceFactory = ({
);
}
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === CommitType.Delete);
const secretDeletionCommits = secretApprovalSecrets.filter(({ op }) => op === SecretOperations.Delete);
const botKey = await projectBotService.getBotKey(projectId).catch(() => null);
const mergeStatus = await secretApprovalRequestDAL.transaction(async (tx) => {
const newSecrets = secretCreationCommits.length
? await secretService.fnSecretBulkInsert({
? await fnSecretBulkInsert({
tx,
folderId,
inputSecrets: secretCreationCommits.map((el) => ({
@@ -375,7 +384,17 @@ export const secretApprovalRequestServiceFactory = ({
]),
tags: el?.tags.map(({ id }) => id),
version: 1,
type: SecretType.Shared
type: SecretType.Shared,
references: botKey
? getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
})
)
: undefined
})),
secretDAL,
secretVersionDAL,
@@ -384,7 +403,7 @@ export const secretApprovalRequestServiceFactory = ({
})
: [];
const updatedSecrets = secretUpdationCommits.length
? await secretService.fnSecretBulkUpdate({
? await fnSecretBulkUpdate({
folderId,
projectId,
tx,
@@ -410,7 +429,17 @@ export const secretApprovalRequestServiceFactory = ({
"secretReminderNote",
"secretReminderRepeatDays",
"secretBlindIndex"
])
]),
references: botKey
? getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
})
)
: undefined
}
})),
secretDAL,
@@ -420,11 +449,13 @@ export const secretApprovalRequestServiceFactory = ({
})
: [];
const deletedSecret = secretDeletionCommits.length
? await secretService.fnSecretBulkDelete({
? await fnSecretBulkDelete({
projectId,
folderId,
tx,
actorId: "",
secretDAL,
secretQueueService,
inputSecrets: secretDeletionCommits.map(({ secretBlindIndex }) => {
if (!secretBlindIndex) {
throw new BadRequestError({
@@ -441,7 +472,7 @@ export const secretApprovalRequestServiceFactory = ({
conflicts: JSON.stringify(conflicts),
hasMerged: true,
status: RequestState.Closed,
statusChangeByUserId: actorId
statusChangeBy: membership.id
},
tx
);
@@ -451,12 +482,14 @@ export const secretApprovalRequestServiceFactory = ({
};
});
await snapshotService.performSnapshot(folderId);
const folder = await folderDAL.findById(folderId);
// TODO(akhilmhdh-pg): change query to do secret path from folder
const [folder] = await folderDAL.findSecretPathByFolderIds(projectId, [folderId]);
if (!folder) throw new BadRequestError({ message: "Folder not found" });
await secretQueueService.syncSecrets({
projectId,
secretPath: "/",
environment: folder?.environment.envSlug as string
secretPath: folder.path,
environmentSlug: folder.environmentSlug,
actorId,
actor
});
return mergeStatus;
};
@@ -476,7 +509,7 @@ export const secretApprovalRequestServiceFactory = ({
}: TGenerateSecretApprovalRequestDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const { permission } = await permissionService.getProjectPermission(
const { permission, membership } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
@@ -504,9 +537,9 @@ export const secretApprovalRequestServiceFactory = ({
const commits: Omit<TSecretApprovalRequestsSecretsInsert, "requestId">[] = [];
const commitTagIds: Record<string, string[]> = {};
// for created secret approval change
const createdSecrets = data[CommitType.Create];
const createdSecrets = data[SecretOperations.Create];
if (createdSecrets && createdSecrets?.length) {
const { keyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
const { keyName2BlindIndex } = await fnSecretBlindIndexCheck({
inputSecrets: createdSecrets,
folderId,
isNew: true,
@@ -517,7 +550,7 @@ export const secretApprovalRequestServiceFactory = ({
commits.push(
...createdSecrets.map(({ secretName, ...el }) => ({
...el,
op: CommitType.Create as const,
op: SecretOperations.Create as const,
version: 1,
secretBlindIndex: keyName2BlindIndex[secretName],
algorithm: SecretEncryptionAlgo.AES_256_GCM,
@@ -529,12 +562,12 @@ export const secretApprovalRequestServiceFactory = ({
});
}
// not secret approval for update operations
const updatedSecrets = data[CommitType.Update];
const updatedSecrets = data[SecretOperations.Update];
if (updatedSecrets && updatedSecrets?.length) {
// get all blind index
// Find all those secrets
// if not throw not found
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await secretService.fnSecretBlindIndexCheck({
const { keyName2BlindIndex, secrets: secretsToBeUpdated } = await fnSecretBlindIndexCheck({
inputSecrets: updatedSecrets,
folderId,
isNew: false,
@@ -545,8 +578,8 @@ export const secretApprovalRequestServiceFactory = ({
// now find any secret that needs to update its name
// same process as above
const nameUpdatedSecrets = updatedSecrets.filter(({ newSecretName }) => Boolean(newSecretName));
const { keyName2BlindIndex: newKeyName2BlindIndex } = await secretService.fnSecretBlindIndexCheck({
inputSecrets: nameUpdatedSecrets,
const { keyName2BlindIndex: newKeyName2BlindIndex } = await fnSecretBlindIndexCheck({
inputSecrets: nameUpdatedSecrets.map(({ newSecretName }) => ({ secretName: newSecretName as string })),
folderId,
isNew: true,
blindIndexCfg,
@@ -563,14 +596,14 @@ export const secretApprovalRequestServiceFactory = ({
const secretId = secsGroupedByBlindIndex[keyName2BlindIndex[secretName]][0].id;
const secretBlindIndex =
newSecretName && newKeyName2BlindIndex[newSecretName]
? newKeyName2BlindIndex?.[secretName]
? newKeyName2BlindIndex?.[newSecretName]
: keyName2BlindIndex[secretName];
// add tags
if (tagIds?.length) commitTagIds[keyName2BlindIndex[secretName]] = tagIds;
return {
...latestSecretVersions[secretId],
...el,
op: CommitType.Update as const,
op: SecretOperations.Update as const,
secret: secretId,
secretVersion: latestSecretVersions[secretId].id,
secretBlindIndex,
@@ -580,12 +613,12 @@ export const secretApprovalRequestServiceFactory = ({
);
}
// deleted secrets
const deletedSecrets = data[CommitType.Delete];
const deletedSecrets = data[SecretOperations.Delete];
if (deletedSecrets && deletedSecrets.length) {
// get all blind index
// Find all those secrets
// if not throw not found
const { keyName2BlindIndex, secrets } = await secretService.fnSecretBlindIndexCheck({
const { keyName2BlindIndex, secrets } = await fnSecretBlindIndexCheck({
inputSecrets: deletedSecrets,
folderId,
isNew: false,
@@ -606,7 +639,7 @@ export const secretApprovalRequestServiceFactory = ({
if (!latestSecretVersions[secretId].secretBlindIndex)
throw new BadRequestError({ message: "Failed to find secret blind index" });
return {
op: CommitType.Delete as const,
op: SecretOperations.Delete as const,
...latestSecretVersions[secretId],
secretBlindIndex: latestSecretVersions[secretId].secretBlindIndex as string,
secret: secretId,
@@ -630,7 +663,7 @@ export const secretApprovalRequestServiceFactory = ({
policyId: policy.id,
status: "open",
hasMerged: false,
committerUserId: actorId
committerId: membership.id
},
tx
);

Some files were not shown because too many files have changed in this diff Show More