1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-21 17:02:49 +00:00

Compare commits

..

561 Commits

Author SHA1 Message Date
ba45e83880 Clean 2024-08-22 23:37:36 -07:00
288f47f4bd Update API reference CRL docs 2024-08-22 12:30:48 -07:00
b090ebfd41 Update API reference CRL docs 2024-08-22 12:26:13 -07:00
67773bff5e Update wording on external parent ca 2024-08-22 12:18:00 -07:00
8ef1cfda04 Update docs for CRL 2024-08-22 12:16:37 -07:00
2a79d5ba36 Fix merge conflicts 2024-08-22 12:01:43 -07:00
0cb95f36ff Finish updating CRL impl 2024-08-22 11:55:19 -07:00
4a1dfda41f Merge pull request from Infisical/maidul-udfysfgj32
Remove service token depreciation notice
2024-08-22 14:29:55 -04:00
c238b7b6ae remove service token notice 2024-08-22 13:57:40 -04:00
83d314ba32 Merge pull request from Infisical/install-external-ca
Install Intermediate CA with External Parent CA
2024-08-22 09:41:52 -07:00
b94a0ffa6c Merge pull request from akhilmhdh/fix/build-mismatch-lines
feat: added backend build sourcemap for line matching
2024-08-22 09:34:12 -04:00
=
b60e404243 feat: added backend build sourcemap for line matching 2024-08-22 15:18:33 +05:30
10120e1825 Merge pull request from akhilmhdh/feat/debounce-last-used
feat: added identity and service token postgres update debounced
2024-08-22 00:50:54 -04:00
31e66c18e7 Merge pull request from Infisical/maidul-deuyfgwyu
Set default to host sts endpoint for aws auth
2024-08-21 22:38:49 -04:00
fb06f5a3bc default to host sts for aws auth 2024-08-21 22:29:30 -04:00
1515dd8a71 Merge pull request from akhilmhdh/feat/ui-patch-v1
feat: resolved ui issues related to permission based hiding
2024-08-21 13:50:24 -04:00
=
da18a12648 fix: resovled create failing in bulk create too 2024-08-21 23:13:42 +05:30
=
49a0d3cec6 feat: resolved ui issues related to permission based hiding 2024-08-21 23:01:23 +05:30
=
e821a11271 feat: added identity and service token postgres update debounced 2024-08-21 22:21:31 +05:30
af4428acec Add external parent ca support to docs 2024-08-20 22:43:29 -07:00
61370cc6b2 Finish allow installing intermediate CA with external parent CA 2024-08-20 21:44:41 -07:00
cf3b2ebbca Merge pull request from Infisical/daniel/read-secrets-notification-removal
Fix: Remove notification when unable to read secrets from environment
2024-08-20 21:56:41 +04:00
e970cc0f47 Fix: Error notification when user does not have access to read from certain environments 2024-08-20 21:41:02 +04:00
bd5cd03aeb Merge pull request from Infisical/daniel/access-requests-group-support
feat(core): Group support for access requests
2024-08-20 21:20:01 +04:00
c46e4d7fc1 fix: scim cleanup 2024-08-20 19:50:42 +04:00
1f3896231a fix: remove privileges when user loses access to project/org 2024-08-20 19:50:42 +04:00
4323f6fa8f Update 20240724101056_access-request-groups.ts 2024-08-20 19:50:42 +04:00
65db91d491 Update 20240724101056_access-request-groups.ts 2024-08-20 19:50:42 +04:00
ae5b57f69f Update 20240724101056_access-request-groups.ts 2024-08-20 19:50:42 +04:00
b717de4f78 Update 20240724101056_access-request-groups.ts 2024-08-20 19:50:42 +04:00
1216d218c1 fix: rollback access approval requests requestedBy 2024-08-20 19:50:42 +04:00
209004ec6d fix: rollback access approval requests requestedBy 2024-08-20 19:50:42 +04:00
c865d12849 Update 20240724101056_access-request-groups.ts 2024-08-20 19:50:42 +04:00
c921c28185 Update AccessPolicyModal.tsx 2024-08-20 19:50:42 +04:00
3647943c80 Feat: Access requests group support 2024-08-20 19:50:42 +04:00
4bf5381060 Feat: Access requests group support 2024-08-20 19:50:42 +04:00
a10c358f83 Feat: Access requests group support 2024-08-20 19:50:42 +04:00
d3c63b5699 Access approval request 2024-08-20 19:50:42 +04:00
c64334462f Access approval policy 2024-08-20 19:50:42 +04:00
c497e19b99 Routers 2024-08-20 19:50:42 +04:00
2aeae616de Migration 2024-08-20 19:50:42 +04:00
e0e21530e2 Schemas 2024-08-20 19:50:41 +04:00
7b4b802a9b Merge pull request from Infisical/daniel/sdk-docs-updates
Fix: Include imports SDK docs
2024-08-20 01:21:28 +04:00
95cf3cf6cc Docs: Add expand secret references to single secret sdk docs 2024-08-20 01:12:42 +04:00
d021b414cf Fix: Include imports SDK docs 2024-08-20 01:09:53 +04:00
bed75c36dd Merge pull request from Infisical/feature/certificate-template
feat: certificate templates
2024-08-19 11:48:03 -07:00
04cb499f0f doc: finalized sample request response values 2024-08-19 16:53:17 +08:00
189a610f52 doc: add cert template api usage 2024-08-19 16:40:18 +08:00
00039ba0e4 misc: addressed PR feedback regarding audit logs and endpoint structure 2024-08-19 16:15:43 +08:00
abdcb95a8f Merge remote-tracking branch 'origin/main' into feature/certificate-template 2024-08-19 14:51:42 +08:00
47ea4ae9a6 Fix merge conflicts 2024-08-18 12:05:15 -07:00
903b2c3dc6 Merge pull request from Infisical/handbook-update
added talking-to-customers.mdx to handbook
2024-08-18 12:55:20 -04:00
c795b3b3a0 added talking-to-customers.mdx to handbook 2024-08-17 22:49:53 -07:00
0d8ff1828e Merge pull request from Infisical/certificate-alerting
Alerting System for expiring CA + Certificates
2024-08-17 22:45:24 -07:00
30d6af7760 Make PR review adjustments 2024-08-17 21:23:25 -07:00
44b42359da Merge remote-tracking branch 'origin' into certificate-alerting 2024-08-17 19:47:37 -07:00
38373722e3 Merge remote-tracking branch 'origin' into certificate-alerting 2024-08-17 19:46:23 -07:00
7ec68ca9a1 Update expiry badge display for certs 2024-08-17 19:44:47 -07:00
a49d5b121b Merge pull request from akhilmhdh/fix/#2288
fix: resolved add all members to project failing when there is pending users in organization
2024-08-16 20:52:43 -04:00
901ff7a605 Merge pull request from akhilmhdh/feat/license-check-approval-api
feat: license check in secret approval api level
2024-08-16 14:07:03 -04:00
ba4aa15c92 doc: added platform docs for certificate template 2024-08-17 01:04:29 +08:00
=
a00103aa1e feat: license check in secret approval api level 2024-08-16 22:34:10 +05:30
0c17cc3577 doc: API references 2024-08-17 00:15:11 +08:00
51d84a47b9 misc: added certificate templates to permissions 2024-08-16 23:30:29 +08:00
d529670a52 misc: addressed failing github actions 2024-08-16 23:14:45 +08:00
ed0463e3e4 misc: added audit logs for certificate template 2024-08-16 23:08:01 +08:00
20db0a255c Merge pull request from Infisical/ca-renewal
CA Renewal (Same Key Pair)
2024-08-16 10:37:44 -04:00
6fe1d77375 misc: added descriptive tooltips 2024-08-16 21:09:35 +08:00
f90855e7a5 misc: add tracking of certificate template ID 2024-08-16 20:17:52 +08:00
97f5c33aea feat: added collection selection for cert template 2024-08-16 19:40:23 +08:00
=
34c2200269 fix: resolved add all members to project failing when there is pending users 2024-08-16 16:20:38 +05:30
69925721cc Merge branch 'certificate-alerting' into feature/certificate-template 2024-08-16 18:42:12 +08:00
0961d2f1c6 feat: subject alternative name policy enforcement 2024-08-16 17:24:33 +08:00
b9bd518aa6 feat: initial enforcement of template policy 2024-08-16 16:32:47 +08:00
692c9b5d9c Merge pull request from dthree/patch-1
fix: added missing word
2024-08-16 00:24:05 -04:00
DC
32046ca880 fix: added missing word 2024-08-15 20:17:40 -07:00
590dbbcb04 Merge pull request from Infisical/maidul-iqdgqwuygd
Add DISABLE_AUDIT_LOG_GENERATION
2024-08-15 22:44:08 -04:00
27d2af4979 Add DISABLE_AUDIT_LOG_GENERATION
Added `DISABLE_AUDIT_LOG_GENERATION` which when set to true will prevent the creation of audit logs in Infisical.

This will be used for load testing purposes and help verify if audit logs are a bottle neck for performance
2024-08-15 22:39:49 -04:00
a1e6c6f7d5 Merge pull request from akhilmhdh/feat/replication-test
fix: switched sync integration to have redis lock
2024-08-15 15:18:42 -04:00
=
cc94a3366a feat: made requested changes for integration sync 2024-08-15 23:38:16 +05:30
6a6c084b8a Add description to PKI collection 2024-08-15 11:03:58 -07:00
7baa3b4cbe Add PKI collection to issue cert modal 2024-08-15 10:41:37 -07:00
=
6cab7504fc fix: switched sync integration to have redis lock 2024-08-15 22:04:32 +05:30
ca3d8c5594 Bring PR up to speed with ca renewal changes 2024-08-15 09:31:21 -07:00
28a2a6c41a feat: initial integration of cert template management 2024-08-15 21:06:37 +08:00
05efd95472 feat: completed certificate template schema endpoints 2024-08-15 15:46:35 +08:00
fa31f87479 Merge pull request from Infisical/doc/add-dynamic-secrets-to-api-reference
doc: add dynamic secrets to api references
2024-08-15 15:37:42 +08:00
b176f13392 doc: add dynamic secrets to api references 2024-08-15 15:21:49 +08:00
f4384bb01e feat: initial structure 2024-08-15 14:49:30 +08:00
856c2423be Update cert structure to ref correct ca cert in cert chain retrieval 2024-08-14 15:43:49 -07:00
4570de09ae Merge pull request from akhilmhdh/feat/replication-test
feat: resolved getSecretByName empty value from imported in kms arch
2024-08-14 16:05:01 -04:00
=
4feff5b4ca feat: resolved getSecretByName empty value from imported in kms arch 2024-08-15 01:24:54 +05:30
6081e2927e Merge pull request from rhythmbhiwani/fix-pagination-disappear
Fixed Pagination Disappearing on Secret Sharing Page
2024-08-14 14:54:37 -04:00
0b42f29916 Merge pull request from akhilmhdh/feat/replication-test
feat: added log point for aws tag and check for delete secret in bridge
2024-08-14 12:25:28 -04:00
=
b60d0992f4 feat: added log point for aws tag and check for delete secret in bridge 2024-08-14 21:42:07 +05:30
a8a68f600c Merge pull request from akhilmhdh/feat/replication-test
feat(ui): resolved a race condition in ui
2024-08-13 14:48:08 -04:00
=
742f5f6621 feat(ui): resolved a race condition in ui 2024-08-14 00:13:55 +05:30
f993e4aa5c Clear type check issue 2024-08-13 11:31:57 -07:00
bb6416acb7 Update CA certificate tracking impl to use foreign ref instead of number 2024-08-13 11:22:45 -07:00
f3cd7efe0e Merge pull request from akhilmhdh/feat/replication-test
feat: added more endpoints for delete
2024-08-13 12:41:54 -04:00
2b16c19b70 improve logs for aws ssm debug 2024-08-13 12:40:02 -04:00
=
943b540383 feat: added more endpoints for delete 2024-08-13 21:48:03 +05:30
e180021aa6 Merge pull request from akhilmhdh/feat/replication-test
feat: added debug points to test ssm integration in replication
2024-08-13 11:23:25 -04:00
=
8e08c443ad feat: added log to print operation based keys 2024-08-13 20:50:19 +05:30
=
dae26daeeb feat: added debug points to test ssm integration in replication 2024-08-13 20:40:53 +05:30
170f8d9add Merge pull request from Infisical/misc/addressed-reported-cli-behaviors
misc: addressed reported flaws with CLI usage
2024-08-13 12:49:20 +08:00
8d41ef198a Merge pull request from akhilmhdh/feat/client-secret-cleanup
fix: resolved secret approval broken due to tag name removal
2024-08-12 16:51:55 -04:00
=
69d60a227a fix: resolved secret approval broken due to tag name removal 2024-08-13 02:16:57 +05:30
c8eefcfbf9 Merge pull request from akhilmhdh/feat/client-secret-cleanup
feat: switched to ssm update as overwrite with tag as seperate operation
2024-08-12 16:38:57 -04:00
=
53cec754cc feat: switched to ssm update as overwrite with tag as seperate operation 2024-08-13 02:04:55 +05:30
5db3e177eb Fixed Pagination Disappearing on Secret Sharing Page 2024-08-13 02:01:25 +05:30
3fcc3ccff4 fix spending money tpyo 2024-08-12 12:41:15 -04:00
df07d7b6d7 update spending docs 2024-08-12 11:34:32 -04:00
28a655bef1 Merge pull request from akhilmhdh/feat/client-secret-cleanup
Client secret cleanup on resource cleanup queue
2024-08-12 11:01:46 -04:00
=
5f2cd04f46 feat: removed not needed condition 2024-08-12 20:29:05 +05:30
=
897ce1f267 chore: new reviewable command in root make file to check all the entities lint and type error 2024-08-12 13:19:55 +05:30
=
6afc17b84b feat: implemented universal auth client secret cleanup in resource cleanup queue 2024-08-12 13:19:25 +05:30
9017a5e838 Update spending-money.mdx 2024-08-12 01:29:45 -04:00
cb8e4d884e add equipment details to handbook 2024-08-11 23:17:58 -04:00
16807c3dd6 update k8s helm chart image tag 2024-08-11 13:09:22 -04:00
61791e385c update chart version of k8 2024-08-11 10:34:23 -04:00
bbd7bfb0f5 Merge pull request from MohamadTahir/fix-operator-bugs
Bug Fixes
2024-08-11 10:33:26 -04:00
4de8c48b2c Merge pull request from Ayush-Dutt-Sharma/ayush/minor-bug-#2269
replaced "creditnals" to "credentials"
2024-08-11 19:18:43 +05:30
a4bbe2c612 fix the client site url & the creation of new variable instead of updating the previous initiated variable 2024-08-11 16:46:48 +03:00
541a2e7d05 replaced "creditnals" to "credentials" 2024-08-11 14:10:49 +05:30
ea4e51d826 Merge pull request from Ayush-Dutt-Sharma/ayush/bug-2267-backend
better logging and while loop for ask propmt again
2024-08-10 19:48:05 +05:30
3bc920c593 better logging and while loop for ask propmt again 2024-08-10 15:36:43 +05:30
f4244c6d4d Finish docs for pki alerting + expose endpoints 2024-08-09 19:22:47 -07:00
e1b9965f01 Add frontend audit log ui for pki alerting / collection 2024-08-09 09:28:15 -07:00
705b4f7513 Add audit logging for pki alerts / collections 2024-08-09 09:07:02 -07:00
df38c761ad Merge pull request from akhilmhdh/fix/migration-switch-batch-insert
Secret migration switched to chunking based batch insert
2024-08-09 11:46:19 -04:00
=
32a84471f2 feat: added a new batch insert operation to convert inserts into chunks and updated secret migration 2024-08-09 21:02:26 +05:30
fc4a20caf2 Rename pki alerting structures 2024-08-09 08:27:53 -07:00
ea14df2cbd Merge pull request from akhilmhdh/fix/tag-filter-secret-api
Tag based filtering for secret endpoint
2024-08-09 20:33:43 +05:30
6bd6cac366 Merge pull request from Infisical/misc/addressed-misleading-google-saml-setup
misc: addressed misleading docs and placeholder values for Google SAML
2024-08-09 07:43:56 -07:00
45294253aa Merge pull request from GLEF1X/bugfix/yaml-exporting
fix(cli): make yaml exporting reliable and standardized
2024-08-09 10:01:26 -04:00
635fbdc80b misc: addressedm misleading docs and placeholder values for google saml 2024-08-09 21:29:33 +08:00
d20c48b7cf Merge pull request from Ayush-Dutt-Sharma/ayush/document-fixes
kubernetes operators integration doc fix
2024-08-09 14:59:50 +05:30
=
1fc18fe23b feat: added name in attach tag 2024-08-09 14:38:15 +05:30
99403e122b kubernetes operators integration doc fix 2024-08-09 14:33:29 +05:30
5176e70437 rephrase error messages 2024-08-08 18:15:13 -04:00
82b2b0af97 Merge pull request from akhilmhdh/feat/secret-get-personal
fix: resolved cli failign to get overriden secret in get command
2024-08-08 15:08:39 -04:00
e313c866a2 remove backup test for temp 2024-08-08 14:25:12 -04:00
2d81606049 update test with typo fix 2024-08-08 14:03:52 -04:00
718f4ef129 Merge pull request from Infisical/maidu-2321e
remove INFISICAL_VAULT_FILE_PASSPHRASE because it is being auto generated now
2024-08-08 13:52:05 -04:00
a42f3b3763 remove INFISICAL_VAULT_FILE_PASSPHRASE because it is being auto generated now 2024-08-08 13:50:34 -04:00
f7d882a6fc Merge pull request from akhilmhdh/fix/backup
Resolved keyring dataset too big by keeping only the encryption key
2024-08-08 13:19:50 -04:00
385afdfcf8 generate random string fn 2024-08-08 13:03:45 -04:00
281d703cc3 removeed vault use command and auto generated passphrase 2024-08-08 13:02:08 -04:00
6f56ed5474 add missing error logs on secrets backup 2024-08-08 13:01:14 -04:00
=
809e4eeba1 fix: resolved cli failign to get overriden secret in get command 2024-08-08 21:23:04 +05:30
=
254446c895 fix: resolved keyring dataset too big by keeping only the encryption key 2024-08-08 13:04:33 +05:30
bb52e2beb4 Update secret-tag-router.ts 2024-08-08 00:31:41 -04:00
2739b08e59 revert bb934ef7b1c47195b2ff65a335712add791cb59c 2024-08-07 22:15:06 -04:00
ba5e877a3b Revert "add base64 package"
This reverts commit 4892eea009ee1ed73c27d783d2dc4e7adc735d11.
2024-08-07 22:14:08 -04:00
d2752216f6 Merge pull request from Infisical/revert-2252-maidul-dhusduqwdhj
Revert "Patch CLI auto select file vault "
2024-08-07 22:13:00 -04:00
d91fb0db02 Revert "Patch CLI auto select file vault " 2024-08-07 22:12:50 -04:00
556e4d62c4 Added pki collection table, pki alert modal 2024-08-07 17:09:07 -07:00
4892eea009 add base64 package 2024-08-07 19:06:25 -04:00
09c6fcb73b Merge pull request from Infisical/maidul-dhusduqwdhj
Patch CLI auto select file vault
2024-08-07 19:03:38 -04:00
79181a1e3d remove os 2024-08-07 23:03:14 +00:00
bb934ef7b1 set vault type when auto selection enabled 2024-08-07 23:02:35 +00:00
cd9316537d prevent auto saving passphrase to disk 2024-08-07 18:56:15 -04:00
942e5f2f65 update phrase 2024-08-07 18:35:57 -04:00
353d231a4e Patch CLI auto select file vault
# Description 📣

When we auto select file vault, we also need to set it's type. When we set the type, we don't need to fall back to file vault in the `GetValueInKeyring` and `DeleteValueInKeyring` because `currentVaultBackend` will be `file`.

Also rephrased the text asking the user to eneter a passphrase.
2024-08-07 18:35:07 -04:00
68e05b7198 add debug log to print keyring error 2024-08-07 14:51:55 -04:00
4f998e3940 Merge pull request from akhilmhdh/fix/replication
fix: resolved replication secret not getting deleted
2024-08-07 11:57:14 -04:00
=
1248840dc8 fix: resolved replication secret not getting deleted 2024-08-07 21:23:22 +05:30
64c8125e4b add external secrets operator mention in k8s docs 2024-08-07 11:13:02 -04:00
1690a9429c Begin cert alerting 2024-08-07 07:08:47 -07:00
=
c109fbab3e feat: removed tag name used in queries 2024-08-07 13:24:22 +05:30
=
15fb01089b feat: name removal in tag respective changes in frontend 2024-08-07 13:15:53 +05:30
=
6f4be3e25a feat: removed name from tag and stricter slugification for tag endpoint 2024-08-07 13:14:39 +05:30
8d33647739 Merge pull request from Infisical/maidul-sqhdqwdgvqwjf
patch findProjectUserWorkspaceKey
2024-08-06 22:12:03 +05:30
d1c142e5b1 patch findProjectUserWorkspaceKey 2024-08-06 12:39:06 -04:00
bb1cad0c5b Merge pull request from Infisical/misc/add-org-level-rate-limit
misc: moved to license-plan-based rate limits
2024-08-06 10:42:57 -04:00
2a1cfe15b4 update text when secrets deleted after integ delete 2024-08-06 10:07:41 -04:00
881d70bc64 Merge pull request from Infisical/feat/enabled-secrets-deletion-on-integ-removal
feat: added secrets deletion feature on integration removal
2024-08-06 09:54:15 -04:00
14c1b4f07b misc: hide not found text when flag plain is enabled 2024-08-06 21:21:45 +08:00
3028bdd424 misc: made local workspace file not required if using auth token 2024-08-06 21:06:14 +08:00
902a0b0ed4 Merge pull request from akhilmhdh/fix/missing-coment-field 2024-08-06 08:18:18 -04:00
ba92192537 misc: removed creation limits completely 2024-08-06 19:41:09 +08:00
26ed8df73c misc: finalized list of license rate limits 2024-08-06 19:14:49 +08:00
c1decab912 misc: addressed comments 2024-08-06 18:58:07 +08:00
=
216c073290 fix: missing comment key in updated project 2024-08-06 16:14:25 +05:30
=
8626bce632 feat: added tag support for secret operation in cli 2024-08-06 15:36:03 +05:30
=
c5a2b0321f feat: completed secret v3 raw to support tag based filtering 2024-08-06 15:35:00 +05:30
5af53d3398 Update/remove comments 2024-08-05 20:19:14 -07:00
8da8c6a66c Block expired CAs from issuing certs 2024-08-05 20:06:17 -07:00
88a4390ea0 Add docs for CA renewal 2024-08-05 19:49:35 -07:00
c70d0a577c Finish preliminary CA renewal with same key pair 2024-08-05 15:43:40 -07:00
1070954bdd misc: used destructuring 2024-08-06 02:05:13 +08:00
587a4a1120 Make progress on ca cert versioning 2024-08-05 11:00:22 -07:00
cc689d3178 feat: added secrets deletion feature on integration removal 2024-08-06 01:52:58 +08:00
e6848828f2 Merge pull request from Infisical/daniel/keyring-cli-improvements
feat(cli): persistant `file` vault passphrase
2024-08-05 13:13:29 -04:00
c8b93e4467 Update doc to show correct command 2024-08-05 13:11:40 -04:00
0bca24bb00 Merge pull request from Infisical/handbook-update
add meetings article to handbook
2024-08-05 12:42:07 -04:00
c563ada50f Merge pull request from akhilmhdh/fix/bot-creation-failing
fix: resolved auto bot create failing on update
2024-08-05 11:15:25 -04:00
=
26d1616e22 fix: resolved auto bot create failing on update 2024-08-05 20:41:19 +05:30
5fd071d1de Merge pull request from akhilmhdh/feat/org-project-management
Feat/org project management
2024-08-05 10:21:09 -04:00
a6ac78356b rename org admin console subject name 2024-08-05 10:03:33 -04:00
e4a2137991 update permission action name for org admin console 2024-08-05 10:01:15 -04:00
9721d7a15e add meetings article to handbook 2024-08-04 14:04:09 -07:00
93db5c4555 Merge pull request from Infisical/maidul-mdjhquwqjhd
update broken image in ksm docs
2024-08-04 11:48:16 -04:00
ad4393fdef update broken image in ksm docs 2024-08-04 11:46:58 -04:00
cd06e4e7f3 hot patch 2024-08-03 19:05:34 -04:00
711a4179ce rename admin panel 2024-08-03 07:52:35 -04:00
=
b4a2a477d3 feat: brought back workspace permission and made requested changes 2024-08-03 14:55:30 +05:30
8e53a1b171 Merge pull request from Infisical/daniel/fix-lint
Fix: Linting
2024-08-02 22:00:28 -04:00
71af463ad8 fix format 2024-08-03 03:49:47 +02:00
7abd18b11c Merge pull request from LemmyMwaura/parse-secret-on-paste
feat: parse secrets (key,value) on paste
2024-08-03 03:33:17 +02:00
1aee50a751 Fix: Parser improvements and lint fixes 2024-08-03 03:29:45 +02:00
0f23b7e1d3 misc: added check for undefined orgId 2024-08-03 02:10:47 +08:00
e9b37a1f98 Merge pull request from Vishvsalvi/deleteActionModal-Placeholder
Placeholder value is same as it's label
2024-08-02 14:04:40 -04:00
33193a47ae misc: updated default onprem rate limits 2024-08-03 01:52:04 +08:00
43fded2350 refactor: take into account other delimiters 2024-08-02 20:41:47 +03:00
7b6f4d810d Placeholder value is same as it's label 2024-08-02 20:51:08 +05:30
1ad286ca87 misc: name updates and more comments 2024-08-02 22:58:53 +08:00
be7c11a3f5 Merge remote-tracking branch 'origin/main' into misc/add-org-level-rate-limit 2024-08-02 22:42:23 +08:00
=
b97bbe5beb feat: text change in sidebar 2024-08-02 19:54:43 +05:30
=
cf5260b383 feat: minor bug fix on access operation 2024-08-02 19:54:42 +05:30
=
13e0dd8e0f feat: completed org admin based project access feature 2024-08-02 19:54:42 +05:30
7f9150e60e Merge pull request from Infisical/maidul-wdqwdwf
Update docker-compose to docker compose in GHA
2024-08-02 19:54:17 +05:30
995f0360fb update docker-compsoe to docker compose 2024-08-02 10:22:21 -04:00
ecab69a7ab Merge pull request from Infisical/issue-cert-csr
Add Sign Certificate Endpoint for Certificate Issuance
2024-08-02 07:16:17 -07:00
cca36ab106 Merge remote-tracking branch 'origin' into issue-cert-csr 2024-08-02 07:06:58 -07:00
76311a1b5f Update DN parsing fn 2024-08-02 07:00:36 -07:00
55a6740714 misc: moved to plan-based rate limit 2024-08-02 21:37:48 +08:00
a0490d0fde Merge pull request from Infisical/feat/added-secret-folder-rbac
feat: added secret folder permissions
2024-08-02 19:05:12 +08:00
78e41a51c0 update workspace to project 2024-08-01 17:29:33 -04:00
8414f04e94 Merge pull request from akhilmhdh/feat/remove-migration-webhooks
feat: resolved invite failing and removed all unused things from frontend for previous upgrade
2024-08-01 11:18:50 -04:00
=
79e414ea9f feat: resolved invite failing and removed all unused things from frontend on previous upgrade 2024-08-01 20:12:23 +05:30
83772c1770 Merge pull request from GLEF1X/refactor/required-key-secret-input
refactor(secret-key-input): pass `isRequired` prop to secret key input
2024-08-01 10:35:23 -04:00
09928efba3 feat: added secret folder rbac' 2024-08-01 22:24:35 +08:00
48eb4e772f Merge pull request from akhilmhdh/feat/remove-migration-webhooks
feat: removed all the migration done for webhook and dynamic secret to KMS
2024-08-01 09:26:49 -04:00
7467a05fc4 fix(lint): fix triple equal strict check 2024-08-01 14:42:15 +03:00
afba636850 feat: parse full env secrets (key,value) when pasted from clipboard 2024-08-01 14:22:22 +03:00
96cc315762 refactor(secret-key-input): pass isRequired prop to secret key input 2024-08-01 06:22:49 -04:00
=
e95d7e55c1 feat: removed all the migration done for webhook and dynamic secret towards kms encryption 2024-08-01 13:39:41 +05:30
520c068ac4 Merge pull request from Infisical/doc/add-documentation-for-kms-with-aws-hsm
doc: added documentation for using AWS HSM
2024-07-31 21:37:23 -04:00
cf330777ed kms and hsm doc updates 2024-07-31 21:36:52 -04:00
c1eae42b26 update aws kms docs 2024-07-31 20:40:54 -04:00
9f0d7c6d11 Correct sign-certificate endpoint ref in docs 2024-07-31 14:04:52 -07:00
683e3dd7be Add sign certificate endpoint 2024-07-31 13:57:47 -07:00
46ca3856b3 change upgrade btn based on admin 2024-07-31 10:59:36 -04:00
891cb06de0 Update keyringwrapper.go 2024-07-31 16:55:53 +02:00
aff7481fbc doc: added documentation for using AWS HSM 2024-07-31 20:30:40 +08:00
e7c1a4d4a0 Merge pull request from Infisical/misc/added-error-prompt-for-fetch-secrets-kms
misc: added error prompt for fetch secrets issue with kms
2024-07-31 14:34:35 +05:30
27f9628dc5 misc: updated refetch interval 2024-07-31 17:02:28 +08:00
1866ce4240 misc: moved get project secrets error handling to hook 2024-07-31 16:48:48 +08:00
e6b6de5e8e misc: added error prompt for fetch secrets issue with kms 2024-07-31 16:31:37 +08:00
02e8f20cbf remove extra : 2024-07-31 03:14:06 +00:00
9184ec0765 Merge pull request from GLEF1X/refactor/hashicorp-vault-integration
refactor(hashicorp-integration): make hashicorp vault integration easier to use
2024-07-30 23:05:28 -04:00
1d55c7bcb0 refactor(integration): add aria-required to Input component 2024-07-30 22:07:50 -04:00
96cffd6196 refactor(integration): make hashicorp vault integration easier to use
* Makes `namespace` optional allowing to use self-hosted OSS hashicorp vault
2024-07-30 22:06:54 -04:00
5bb2866b28 Merge pull request from Infisical/secret-engine-v2-bridge
Secret engine v2 bridge
2024-07-30 21:41:21 -04:00
7a7841e487 Merge pull request from Infisical/daniel/tls-docs
docs(sdks): Custom TLS certificate support
2024-07-30 20:52:52 -04:00
b0819ee592 update agent functions docs 2024-07-30 20:50:35 -04:00
b4689bed17 fix docs typo 2024-07-30 20:11:30 -04:00
bfd24ea938 Merge pull request from Infisical/maidul-dig2urdy3
add single secret fetch for agent
2024-07-30 20:09:32 -04:00
cea1a5e7ea add docs for single and list secrets functions for agent 2024-07-30 20:01:52 -04:00
8d32ca2fb6 Merge pull request from Infisical/vmatsiiako-docs-patch-1
Update migrating-from-envkey.mdx
2024-07-30 16:25:56 -07:00
d468067d43 Update migrating-from-envkey.mdx 2024-07-30 16:24:47 -07:00
3a640d6cf8 add single secret fetch for agent 2024-07-30 19:23:24 -04:00
8fc85105a9 Merge pull request from Infisical/secret-sharing-fix-padding
Add More Padding to Secret Sharing Banner
2024-07-30 13:49:29 -07:00
48bd354bae Add more padding for secret sharing promo banner 2024-07-30 13:46:40 -07:00
6e1dc7375c Update csharp.mdx 2024-07-30 22:24:43 +02:00
164627139e TLS docs 2024-07-30 22:24:23 +02:00
=
f7c962425c feat: renamed migrations to be latest 2024-07-30 23:54:07 +05:30
=
d92979d50e feat: resolved rebase ts errors 2024-07-30 23:50:04 +05:30
021dbf3558 Merge pull request from Infisical/secret-sharing-fix
Minor UI Improvements
2024-07-30 11:17:53 -07:00
=
29060ffc9e feat: added a success message on upgrade success 2024-07-30 23:19:33 +05:30
=
d9c7724857 feat: removed replica node from delete db query 2024-07-30 23:19:33 +05:30
=
9063787772 feat: changed webhook and dynamic secret change to migration mode, resolved snapshot deletion issue in update 2024-07-30 23:19:33 +05:30
c821bc0e14 misc: address project set kms issue 2024-07-30 23:19:33 +05:30
83eed831da text rephrase 2024-07-30 23:19:32 +05:30
=
5c8d6157d7 feat: added logic for webhook and dynamic secret to use the kms encryption 2024-07-30 23:19:32 +05:30
=
5d78b6941d feat: made encryption tab hidden for project v2 and v1 2024-07-30 23:19:32 +05:30
=
1d09d4cdfd feat: resolved a edge case on snapshot based secret version insertion due to missing snapshots in some parts 2024-07-30 23:19:32 +05:30
=
9877444117 feat: updated operator version title for migration 2024-07-30 23:19:32 +05:30
=
6f2ae344a7 feat: correction in test command 2024-07-30 23:19:32 +05:30
=
549d388f59 feat: improved migration wizard to info user prerequisite check list 2024-07-30 23:19:32 +05:30
=
e2caa98c74 feat: finished migrator logic 2024-07-30 23:19:32 +05:30
=
6bb41913bf feat: completed migration backend logic 2024-07-30 23:19:31 +05:30
=
844a4ebc02 feat: sanitized project schema on routes to avoid exposing encrypted keys 2024-07-30 23:19:31 +05:30
=
b37f780c4c feat: added auto bot creator when bot is missing by taking the user old server encrypted private key 2024-07-30 23:19:31 +05:30
=
6e7997b1bd feat: added kms deletion on project deletion and removed e2ee blind index upgrade banner 2024-07-30 23:19:08 +05:30
=
e210a6a24f feat: added missing index in secret v2 2024-07-30 23:19:08 +05:30
=
b950bf0cf7 feat: added back secret referencing expansion support 2024-07-30 23:19:07 +05:30
=
a53d0b2334 feat: added first version of migrator to secret v2 2024-07-30 23:19:07 +05:30
=
ab88e6c414 checkpoint 2024-07-30 23:19:07 +05:30
49eb6d6474 misc: removed minimum requirements for kms description 2024-07-30 23:19:07 +05:30
05d7e26f8b misc: addressed minor kms issues 2024-07-30 23:19:07 +05:30
=
6a156371c0 feat: resolved bug reported on search and integration failing due to typo in integration field 2024-07-30 23:19:07 +05:30
=
8435b20178 feat: added test case for secret v2 with raw endpoints 2024-07-30 23:19:07 +05:30
=
7d7fcd0db6 feat: resolved failing testcases 2024-07-30 23:19:06 +05:30
=
b5182550da feat: lint fix 2024-07-30 23:19:06 +05:30
=
3e0ae5765f feat: updated kms service to return only kms details and some more minor changes 2024-07-30 23:19:06 +05:30
=
f7ef86eb11 feat: fixed secret approval for architecture v2 2024-07-30 23:19:06 +05:30
=
acf9a488ac feat: secret v2 architecture for secret rotation 2024-07-30 23:19:06 +05:30
=
4a06e3e712 feat: testing v2 architecture changes and corrections as needed 2024-07-30 23:19:06 +05:30
=
b7b0e60b1d feat: ui removed all private key except secret rotation to raw endpoints version 2024-07-30 23:19:05 +05:30
=
d4747abba8 feat: resolved concurrent bug with kms management 2024-07-30 23:19:05 +05:30
=
641860cdb8 feat: resolved all ts issues on router schema and other functions 2024-07-30 23:19:05 +05:30
=
36ac1f47ca feat: all the services are now working with secrets v2 architecture 2024-07-30 23:17:41 +05:30
=
643d13b0ec checkpoint 2024-07-30 23:11:04 +05:30
=
ef2816b2ee feat: added bridge logic in secret replication, snapshot and approval for raw endpoint 2024-07-30 23:11:04 +05:30
=
9e314d7a09 feat: migration updated for secret v2 snapshot and secret approval 2024-07-30 23:03:56 +05:30
=
8eab27d752 feat: added kms encryption and decryption secret bridge 2024-07-30 23:03:56 +05:30
=
b563c4030b feat: created base for secret v2 bridge and plugged it to secret-router 2024-07-30 23:03:56 +05:30
=
761a0f121c feat: added new secret v2 data structures 2024-07-30 23:03:56 +05:30
70400ef369 misc: made kms description optional 2024-07-30 23:03:56 +05:30
9aecfe77ad doc: added aws permission setup doc for kms 2024-07-30 23:03:56 +05:30
cedeb1ce27 doc: initial docs for kms 2024-07-30 23:03:55 +05:30
0e75a8f6d7 misc: made kms hook generic 2024-07-30 23:03:55 +05:30
a5b030c4a7 misc: renamed project method 2024-07-30 23:03:55 +05:30
4009580cf2 misc: removed kms from service 2024-07-30 23:03:55 +05:30
64869ea8e0 misc: created abstraction for get kms by id 2024-07-30 23:03:55 +05:30
ffc1b1ec1c misc: modified design of advanced settings 2024-07-30 23:03:55 +05:30
880a689376 misc: finalized project backup prompts 2024-07-30 23:03:54 +05:30
3709f31b5a misc: added empty metadata 2024-07-30 23:03:54 +05:30
6b6fd9735c misc: added ability for users to select KMS during project creation 2024-07-30 23:03:54 +05:30
a57d1f1c9a misc: modified modal text 2024-07-30 23:03:54 +05:30
6c06de6da4 misc: addressed type issue with audit log 2024-07-30 23:03:54 +05:30
0c9e979fb8 feat: load project kms backup 2024-07-30 23:03:54 +05:30
32fc254ae1 misc: added UI for load backup 2024-07-30 23:03:53 +05:30
69d813887b misc: added audit logs for kms backup and other minor edits 2024-07-30 23:03:53 +05:30
80be054425 misc: developed create kms backup feature 2024-07-30 23:03:53 +05:30
4d032cfbfa misc: made project key and data key creation concurrency safe 2024-07-30 23:03:53 +05:30
d41011e056 misc: made org key and data key concurrency safe 2024-07-30 23:03:53 +05:30
d918f3ecdf misc: finalized switching of project KMS 2024-07-30 23:03:53 +05:30
7e5c3e8163 misc: partial project kms switch 2024-07-30 23:03:52 +05:30
cb347aa16a misc: changed order of aws validate connection and creation 2024-07-30 23:03:14 +05:30
88a7cc3068 misc: added audit logs for external kms 2024-07-30 23:03:14 +05:30
4ddfb05134 misc: added license checks for external kms management 2024-07-30 23:03:14 +05:30
7bb0ec0111 misc: migrated to dedicated org permissions for kms management 2024-07-30 23:03:13 +05:30
31af4a4602 misc: minor UI updates 2024-07-30 23:03:13 +05:30
dd46a21035 feat: finalized kms settings in org-level 2024-07-30 23:03:13 +05:30
26a5d74b14 misc: modified encryption/decryption of external kms config 2024-07-30 23:03:13 +05:30
7e9389cb26 Made with love 2024-07-30 10:32:58 -07:00
eda57881ec Minor UI adjustments 2024-07-30 10:31:30 -07:00
5eafdba6c8 Merge remote-tracking branch 'akhilmhdh/feat/aws-kms-sm' into feat/integrate-external-kms 2024-07-30 23:01:13 +05:30
9c4bb79472 misc: connected aws add kms 2024-07-30 23:01:13 +05:30
937b0c0a7c feat: added initial aws form 2024-07-30 23:01:12 +05:30
=
cb132f4c65 fix: resolving undefined secret key 2024-07-30 23:01:12 +05:30
=
4caa77e28a refactor(ui): migrated secret endpoints of e2ee to raw 2024-07-30 23:01:12 +05:30
=
547be80dcf feat: made raw secret endpoints and normal e2ee ones to be same functionality 2024-07-30 23:01:12 +05:30
2cbae96c9a feat: added project data key 2024-07-30 23:01:12 +05:30
553d51e5b3 Merge pull request from Infisical/maidul-dwdqwdfwef
Lint fixes to unblock prod pipeline
2024-07-30 11:06:01 -04:00
16e0a441ae unblock prod pipeline 2024-07-30 11:00:27 -04:00
d6c0941fa9 Merge pull request from Infisical/secret-sharing-update
Secret Sharing Update
2024-07-30 07:27:56 -07:00
7cbd254f06 Add back hashed hex for secret sharing 2024-07-30 07:16:03 -07:00
4b83b92725 Merge pull request from Infisical/handbook-update
add envkey migration page
2024-07-30 08:54:40 -04:00
fe72f034c1 Update migrating-from-envkey.mdx 2024-07-30 08:54:22 -04:00
dbe771dba0 refactor: remove unnecessary comment 2024-07-30 05:30:13 -04:00
273fd6c98f refactor: remove deprecated errors package
- Replace errors.Wrap with fmt.Errorf and %w verb
2024-07-30 05:23:43 -04:00
d5f4ce4376 Update vault.go 2024-07-30 10:22:15 +02:00
6803553b21 add envkey migration page 2024-07-29 23:23:05 -07:00
18aac6508b fix(cli): make yaml exporting reliable and standardized 2024-07-29 22:38:10 -04:00
1c8299054a Merge pull request from GLEF1X/perf/optimize-group-delete
perf(group-fns): optimize sequential delete to be concurrent
2024-07-29 22:13:00 -04:00
85653a90d5 update phrasing 2024-07-29 22:06:03 -04:00
98b6373d6a perf(group-fns): optimize sequential delete to be concurrent 2024-07-29 21:40:48 -04:00
1d97921c7c Merge pull request from LemmyMwaura/delete-secret-modal
feat: add confirm step (modal) before deleting a secret
2024-07-29 19:52:51 -04:00
0d4164ea81 Merge remote-tracking branch 'origin' into secret-sharing-update 2024-07-29 15:22:13 -07:00
79bd8613d3 Fix padding 2024-07-29 15:16:11 -07:00
8deea21a83 Bring back logo, promo text in secret sharing 2024-07-29 15:05:38 -07:00
3b3c2be933 Merge pull request from LemmyMwaura/persist-tab-state
feat: persist tab state on route change.
2024-07-29 17:35:07 -04:00
c041e44399 Continue secret sharing 2024-07-29 14:32:11 -07:00
c1aeb04174 Merge pull request from Infisical/vmatsiiako-changelog-patch-1
Update changelog
2024-07-29 17:26:28 -04:00
3f3c0aab0f refactor: revert the org level enum to only types that existed before 2024-07-29 20:04:58 +03:00
b740e8c900 Rename types to Types with correct case 2024-07-29 20:02:42 +03:00
4416b11094 refactor: change folder name to uppercase for consistency 2024-07-29 19:48:49 +03:00
d8169a866d refactor: update types import path 2024-07-29 19:41:02 +03:00
7239158e7f refactor: localize tabs at both the org and project level 2024-07-29 19:37:19 +03:00
879ef2c178 Update keyringwrapper.go 2024-07-29 12:37:58 +02:00
8777cfe680 Update keyringwrapper.go 2024-07-29 12:34:35 +02:00
2b630f75aa Update keyringwrapper.go 2024-07-29 12:31:02 +02:00
91cee20cc8 Minor improvemnets 2024-07-29 12:21:38 +02:00
4249ec6030 Update login.go 2024-07-29 12:21:31 +02:00
e7a95e6af2 Update login.go 2024-07-29 12:15:53 +02:00
a9f04a3c1f Update keyringwrapper.go 2024-07-29 12:13:40 +02:00
3d380710ee Update keyringwrapper.go 2024-07-29 12:10:42 +02:00
2177ec6bcc Update vault.go 2024-07-29 12:04:34 +02:00
fefe2d1de1 Update changelog 2024-07-28 10:53:44 -07:00
3f3e41282d fix: remove unnecessary selectedTab div 2024-07-28 20:33:17 +03:00
c14f94177a Merge pull request from Infisical/vmatsiiako-changelog-update-july2024
Update changelog
2024-07-28 10:14:59 -07:00
ceb741955d Update changelog 2024-07-28 10:08:58 -07:00
f5bc4e1b5f refactor: return value as Tabsection from isTabSection fn (avoids assertion at setState level) 2024-07-28 07:50:27 +03:00
06900b9c99 refactor: create helper fn to check if string is in TabSections 2024-07-28 07:14:57 +03:00
d71cb96adf fix(lint): resolve type error 2024-07-27 23:33:09 +03:00
61ebec25b3 refactor: update envs to environments 2024-07-27 23:24:10 +03:00
57320c51fb fix: add selectedtab when moving back from roles page 2024-07-27 23:10:12 +03:00
4aa9cd0f72 feat: also persist the state on delete 2024-07-27 22:58:36 +03:00
ea39ef9269 feat: persist state at the org level when tab switching 2024-07-27 22:45:53 +03:00
15749a1f52 feat: update url onvalue change 2024-07-27 22:18:56 +03:00
9e9aff129e feat: use shared enum for consistent values 2024-07-27 22:12:19 +03:00
4ac487c974 feat: selectTab state from url 2024-07-27 22:04:43 +03:00
2e50072caa feat: move shared enum to separate file 2024-07-27 22:04:11 +03:00
2bd170df7d feat: add queryparam when switching tabs 2024-07-27 22:03:44 +03:00
938a7b7e72 Merge pull request from Infisical/secret-sharing
Secret Sharing UI/UX Adjustment
2024-07-27 10:09:03 -07:00
af864b456b Adjust secret sharing screen form padding 2024-07-27 07:32:56 -07:00
a30e3874cd Adjustments to secret sharing styling 2024-07-27 07:31:30 -07:00
de886f8dd0 feat: make title dynamic when deleting folders and secrets 2024-07-27 12:27:06 +03:00
b3db29ac37 refactor: update modal message to match other delete modals in the dashboard 2024-07-27 11:42:30 +03:00
070eb2aacd Update keyringwrapper.go 2024-07-26 22:47:46 +02:00
e619cfa313 feat(cli): set persistent file vault password 2024-07-26 22:47:37 +02:00
c3038e3ca1 docs: passphrase command 2024-07-26 22:47:07 +02:00
ce1db38afd refactor: re-use existing modal for deletion 2024-07-26 22:05:44 +03:00
0fa6b7a08a Merge pull request from Infisical/project-role-concept
Project Role Page
2024-07-26 11:27:25 -07:00
29c5bf5491 Remove top margin from RolePermissionSecretsRow 2024-07-26 11:22:15 -07:00
4d711ae149 Finish project role page 2024-07-26 11:00:47 -07:00
ff0e7feeee feat(cli): CLI Keyring improvements 2024-07-26 19:14:21 +02:00
9dd675ff98 refactor: move delete statement into body tag 2024-07-26 19:56:31 +03:00
8fd3e50d04 feat: implement delete secret via modal logic 2024-07-26 19:48:30 +03:00
391ed0723e feat: add delete secret modal 2024-07-26 19:47:35 +03:00
84af8e708e Merge remote-tracking branch 'origin' into project-role-concept 2024-07-26 07:28:17 -07:00
b39b5bd1a1 Merge pull request from Infisical/patch-org-role-update
Fix updating org role details should not send empty array of permissions
2024-07-26 07:27:51 -07:00
b3d9d91b52 Fix updating org role details should not send empty array of permissions 2024-07-26 06:52:21 -07:00
5ad4061881 Continue project role page 2024-07-26 06:43:09 -07:00
f29862eaf2 Merge pull request from Infisical/list-ca-endpoint-descriptions
Add descriptions for parameters for LIST (GET) CAs / certificates endpoints
2024-07-25 17:59:57 -04:00
7cb174b644 Add descriptions for list cas/certs endpoints 2024-07-25 14:53:41 -07:00
bf00d16c80 Continue progress on project role page 2024-07-25 14:45:02 -07:00
e30a0fe8be Merge pull request from Infisical/cert-search-filtering
Add List CAs / Certificates to Documentation + Filter Options
2024-07-25 09:40:44 -07:00
6e6f0252ae Adjust default offsets for cas/certs query 2024-07-25 08:09:21 -07:00
2348df7a4d Add list cert, ca + logical filters to docs 2024-07-25 08:06:18 -07:00
962cf67dfb Merge pull request from felixtrav/patch-1
Update envars.mdx - Added PORT
2024-07-25 10:21:06 -04:00
32627c20c4 Merge pull request from Infisical/org-role-cleanup
Cleanup frontend unused org role logic (moved)
2024-07-25 07:17:56 -07:00
c50f8fd78c Merge pull request from akhilmhdh/feat/cli-login-fallback-missing
Missing paste token option in CLI brower login flow
2024-07-25 10:08:57 -04:00
1cb4dc9e84 Start project role concept 2024-07-25 06:47:18 -07:00
977ce09245 Cleanup frontend unused org role logic (moved) 2024-07-25 05:43:57 -07:00
=
08d7dead8c fix(cli): resolved not printing the url on api override 2024-07-25 15:28:54 +05:30
=
a30e06e392 feat: added back missing token paste option in cli login from browser 2024-07-25 15:28:29 +05:30
23f3f09cb6 temporarily remove linux deployment 2024-07-24 23:42:36 -04:00
5cd0f665fa Update envars.mdx - Added PORT
Added the PORT configuration option to the documentation which controls the port the application listens on.
2024-07-24 19:17:33 -04:00
443e76c1df Merge pull request from Infisical/daniel/aarch64-binary-fix
fix(binary): aarch64 binary native bindings fix
2024-07-24 16:33:15 +02:00
4ea22b6761 Updated ubuntu version 2024-07-24 14:17:19 +00:00
ae7e0d0963 Merge pull request from Infisical/misc/added-email-self-host-conditionals
misc: added checks for formatting email templates for self-hosted or cloud
2024-07-24 09:22:49 -04:00
ed6c6d54c0 Update build-binaries.yml 2024-07-24 11:16:58 +02:00
428ff5186f Removed compression for testing 2024-07-24 10:47:20 +02:00
d07b0d20d6 Update build-binaries.yml 2024-07-24 10:46:55 +02:00
8e373fe9bf misc: added email formatting for remaining templates 2024-07-24 16:33:41 +08:00
28087cdcc4 misc: added email self-host conditionals 2024-07-24 00:55:02 +08:00
dcef49950d Merge pull request from Infisical/daniel/ruby-docs
feat(docs): Ruby sdk
2024-07-23 08:36:32 -07:00
1e5d567ef7 Update ruby.mdx 2024-07-23 15:30:13 +02:00
d09c320150 fix: bad documentation link 2024-07-23 15:27:23 +02:00
229599b8de docs: ruby sdk documentation 2024-07-23 15:27:11 +02:00
02eea4d886 Merge pull request from Infisical/misc/updated-cf-worker-integration-doc
misc: updated cf worker integration doc
2024-07-23 21:16:56 +08:00
d12144a7e7 misc: added highligting 2024-07-23 21:03:46 +08:00
5fa69235d1 misc: updated cf worker integration doc 2024-07-23 20:40:07 +08:00
7dd9337b1c Merge pull request from Infisical/daniel/deployment-doc-fix
chore(docs): typo in url
2024-07-23 10:19:59 +02:00
f9eaee4dbc Merge pull request from Infisical/daniel/deployment-doc-fix
chore(docs): remove redundant doc
2024-07-23 09:55:10 +02:00
cd3a64f3e7 Update standalone-binary.mdx 2024-07-23 09:52:42 +02:00
121254f98d Update standalone-binary.mdx 2024-07-23 09:52:14 +02:00
1591c1dbac Merge pull request from Infisical/misc/add-endpoint-for-terraform-environment
misc: add endpoint for environment terraform resource
2024-07-23 15:39:20 +08:00
3c59d288c4 misc: readded auth 2024-07-23 15:33:09 +08:00
632b775d7f misc: removed api key auth from get env by id 2024-07-23 15:28:58 +08:00
d66da3d770 misc: removed deprecated auth method 2024-07-23 15:23:50 +08:00
da43f405c4 Merge pull request from Infisical/role-concept
Organization Role Page
2024-07-23 12:26:43 +07:00
d5c0abbc3b Opt for bulk save role permissions instead of save on each form change 2024-07-23 11:58:55 +07:00
7a642e7634 Merge pull request from Infisical/daniel/cli-security-warning
fix(cli): dependency security warning
2024-07-22 21:36:48 +02:00
de686acc23 misc: add endpoint for environment terraform resource 2024-07-23 02:35:37 +08:00
b359f4278e Fix type issues 2024-07-22 19:15:18 +07:00
29d76c1deb Adjust OrgRoleTable 2024-07-22 19:02:03 +07:00
6ba1012f5b Add default role support for RolePage 2024-07-22 18:55:34 +07:00
4abb3ef348 Fix 2024-07-22 13:42:59 +02:00
73e764474d Update go.sum 2024-07-22 13:42:27 +02:00
7eb5689b4c Update go.mod 2024-07-22 13:42:24 +02:00
5d945f432d Merge pull request from Infisical/daniel/minor-ui-change
chore(ui): Grammar fix
2024-07-22 13:39:07 +02:00
1066710c4f Fix: Rename tips to tip 2024-07-22 13:14:25 +02:00
b64d4e57c4 Clean org roles concept refactor 2024-07-22 16:56:27 +07:00
bd860e6c5a Continue progress on role ui update 2024-07-22 12:41:26 +07:00
37137b8c68 Merge pull request from akhilmhdh/feat/login-cli-token-paste
Feat/login cli token paste
2024-07-21 23:37:41 -04:00
8b10cf863d add verify jwt, update text phrasing and fix double render input field 2024-07-21 23:35:09 -04:00
=
eb45bed7d9 feat: updated text over cli 2024-07-21 22:34:23 +05:30
=
1ee65205a0 feat: ui option to paste token on sending token to cli fails 2024-07-21 19:25:41 +05:30
=
f41272d4df feat: added option for manually adding token in browser login failure 2024-07-21 19:24:54 +05:30
8bf4df9f27 Merge pull request from akhilmhdh/cli-operation-to-raw
Switched CLI and K8s operator to secret raw endpoint
2024-07-20 13:19:13 -04:00
037a8f2ebb Merge branch 'main' into cli-operation-to-raw 2024-07-20 13:15:56 -04:00
14bc436283 Merge pull request from Infisical/fix/import-failing 2024-07-20 09:19:10 -04:00
a108c7dde1 fix: resolved get secret failing when import is invalid 2024-07-20 14:42:26 +05:30
54ccd73d2a Merge pull request from aheruz/feat/secret-request-bypass-reason
feat: add bypass reason on bypassed secret requests
2024-07-19 17:22:18 -04:00
729ca7b6d6 small nits for bypass pr 2024-07-19 17:19:06 -04:00
754db67f11 update chart version 2024-07-19 16:47:21 -04:00
f97756a07b doc: approval workflows revamp 2024-07-19 22:14:40 +02:00
22df51ab8e feat(frontend): send bypass reason on bypassed merges 2024-07-19 21:33:41 +02:00
bff8f55ea2 feat(backend): save bypass reason on secret_approval_requests 2024-07-19 21:31:48 +02:00
2f17f5e7df update bot not found message 2024-07-19 14:50:16 -04:00
72d2247bf2 add support for --projectId when user is logged in on secrets set 2024-07-19 12:55:52 -04:00
4ecd4c0337 Merge pull request from aheruz/feat/ENG-985-secret-share-organization
feat(ENG-985): secret share within organization
2024-07-19 10:30:32 -04:00
=
538613dd40 feat: added bot error message for old instance 2024-07-19 19:59:16 +05:30
4c5c24f689 feat: remove accessType hardcap in migration + style 2024-07-19 16:19:45 +02:00
dead16a98a Merge pull request from Infisical/daniel/deployment-documentation
docs(deployment): Standalone and high availability deployment
2024-07-19 08:28:26 -04:00
224368b172 fix: general access accessible only from private creation 2024-07-19 12:20:46 +02:00
3731459e99 Make progress on org role detail modal 2024-07-19 16:53:04 +07:00
dc055c11ab Merge remote-tracking branch 'origin' into role-concept 2024-07-19 15:27:25 +07:00
22878a035b Continue RolePermissionsTable 2024-07-19 15:24:21 +07:00
2f2c9d4508 style: message structure on SecretTable 2024-07-19 03:36:24 +02:00
774017adbe style: remove logs 2024-07-19 03:32:46 +02:00
f9d1d9c89f doc: Adding accessType on secret sharing 2024-07-19 03:26:26 +02:00
eb82fc0d9a feat(frontend): Adding accessType on secret sharing 2024-07-19 03:17:47 +02:00
e45585a909 feat(backend): Adding accessType on secret sharing 2024-07-19 03:15:38 +02:00
6f0484f074 update bot key message 2024-07-18 18:29:39 -04:00
4ba529f22d print error message when projectId flag is not passed for set secret 2024-07-18 18:05:01 -04:00
5360fb033a fix set secret 2024-07-18 17:53:25 -04:00
27e14bcafe Merge pull request from aheruz/style/typo-bypass-is-one-word 2024-07-18 13:09:22 -04:00
bc5003ae4c style(frontend): type bypass 2024-07-18 18:53:45 +02:00
f544b39597 Merge pull request from aheruz/feature/enhance-approval-policies
feat: enhance approval policies
2024-07-18 12:33:47 -04:00
8381f52f1e text rephrase update 2024-07-18 12:29:47 -04:00
aa96a833d7 Merge pull request from kasyap1234/main
Add SMTP2GO credentials for email configuration
2024-07-18 11:50:21 -04:00
53c64b759c feat(frontend): adding tooltip for labels 2024-07-18 17:37:12 +02:00
74f2224c6b Merge pull request from Infisical/user-page-fix
Minor Patches for User Page
2024-07-18 10:27:39 -04:00
ecb5342a55 fix(backend): ensure idempotent migration 2024-07-18 16:23:56 +02:00
bcb657b81e style: change actions to elipses dropdown 2024-07-18 16:23:09 +02:00
ebe6b08cab Public key to not invited state change for project provisioning ui 2024-07-18 20:46:36 +07:00
43b14d0091 Patch for update org membership call, hide edit user on self user page 2024-07-18 20:40:12 +07:00
7127f6d1e1 Begin role page 2024-07-18 20:12:52 +07:00
20387cff35 Merge pull request from Infisical/user-page
Add Manual User Deactivation/Activation + User Page
2024-07-18 09:01:05 -04:00
997d7f22fc fix(backend): secretPath default / + default enforcementLevel 2024-07-18 13:13:39 +02:00
e1ecad2331 fix(frontend): types 2024-07-18 12:59:10 +02:00
ce26a06129 role table restyle 2024-07-18 17:12:02 +07:00
7622cac07e Update high-availability.mdx 2024-07-18 09:58:48 +02:00
a101602e0a Update overview.mdx 2024-07-18 08:25:37 +02:00
ca63a7baa7 Standalone binary docs 2024-07-18 08:25:31 +02:00
ff4f15c437 HA deployment docs 2024-07-18 08:25:26 +02:00
d6c2715852 Update mint.json 2024-07-18 08:25:14 +02:00
fc386c0cbc Create haproxy-stats.png 2024-07-18 08:25:11 +02:00
263a88379f Create ha-stack.png 2024-07-18 08:25:07 +02:00
4b718b679a Change deactivate button messaging, add scim check 2024-07-18 10:44:54 +07:00
498b1109c9 resolve pr review issues 2024-07-18 10:32:39 +07:00
b70bf4cadb fix SMTP2GO configuration 2024-07-18 06:23:24 +05:30
d301f74feb fix(frontend): interactions SecretApprovalRequestAction 2024-07-18 02:46:50 +02:00
454826fbb6 feat(frontend): accept soft approvals on access requests 2024-07-18 02:25:53 +02:00
f464d7a096 feat(backend): accept soft approvals on access requests 2024-07-18 02:25:12 +02:00
cae9ace1ca Merge branch 'Infisical:main' into main 2024-07-18 05:54:30 +05:30
8a5a295a01 feat(frontend): accept soft approvals on secret requests 2024-07-18 01:30:40 +02:00
95a4661787 Merge pull request from Infisical/maidul-dqwdfffwr312
add ips for whitelisting
2024-07-17 19:14:49 -04:00
7e9c846ba3 add ips for whitelisting 2024-07-17 19:11:55 -04:00
aed310b9ee feat(backemd): accept soft approvals on secret requests 2024-07-18 01:02:57 +02:00
c331af5345 feat(frontend): add enforcementLevel into AccessPolicy 2024-07-17 22:32:30 +02:00
d4dd684f32 feat(backend): add enforcementLevel into access_approval_policies 2024-07-17 22:30:55 +02:00
=
1f6c33bdb8 feat: updated bot not found error message 2024-07-18 01:46:28 +05:30
a538e37a62 chore(backend): schema secret_approval_policies 2024-07-17 21:48:57 +02:00
f3f87cfd84 feat(frontend): add enforcementLevel into SecretPolicy 2024-07-17 21:43:53 +02:00
2c57bd94fb feat(backend): add enforcementLevel into secret_approval_policies 2024-07-17 21:39:33 +02:00
869fcd6541 feat(frontend): remove SecretApprovalPolicyList 2024-07-17 21:30:55 +02:00
7b3e116bf8 feat(frontend): remove AccessApprovalPolicyList 2024-07-17 20:26:27 +02:00
0a95f6dc1d feat(frontend): welcome ApprovalPolicyList 2024-07-17 20:22:43 +02:00
d19c856e9b chore(frontend): rename approverUserIds to approvers in registerSecretApprovalPolicy 2024-07-17 20:05:34 +02:00
ada0033bd0 Fix type issue frontend 2024-07-17 23:13:04 +07:00
6818c8730f chore: rename approverUserIds to approvers in registerSecretApprovalPolicy 2024-07-17 16:49:54 +02:00
8542ec8c3e Complete preliminary user page 2024-07-17 19:48:04 +07:00
c141b916d3 Merge pull request from Infisical/further-scim-smoothening
Further SCIM Smoothening
2024-07-17 18:04:25 +07:00
b09dddec1c Add SMTP2GO credentials for email configuration 2024-07-17 15:41:36 +05:30
1ae375188b Correct database error message 2024-07-17 11:27:09 +07:00
22b954b657 Further smoothen scim 2024-07-17 11:24:57 +07:00
9efeb8926f Merge pull request from Infisical/maidul-dewfewfqwef
Address vanta postcss update
2024-07-16 21:46:14 -04:00
=
1d6d424c91 fix: removed print not used 2024-07-16 14:08:09 +05:30
=
c39ea130b1 feat: changed backup secret to keyring and resolved backup not working in previous versions 2024-07-16 14:05:38 +05:30
=
5514508482 refactor(cli): removed unused secret logic for e2ee used 2024-07-15 01:23:47 +05:30
=
5921dcaa51 feat: switched operator to raw endpoints for secret management 2024-07-15 01:23:03 +05:30
=
b2c62c4193 feat(cli): changed all secret endpoint to raw endpoint 2024-07-14 15:05:53 +05:30
700 changed files with 36877 additions and 14034 deletions
.github/workflows
Makefile
backend
e2e-test/routes/v3
package-lock.jsonpackage.json
scripts
src
@types
db
ee
routes/v1
services
keystore
lib
queue
server
services
access-token-queue
certificate-authority
certificate-template
certificate
group-project
identity-access-token
identity-aws-auth
identity-ua
integration-auth
integration
kms
org-admin
org-membership
org
pki-alert
pki-collection
project-bot
project-env
project-membership
project-role
project
resource-cleanup
secret-folder
secret-import
secret-sharing
secret-tag
secret-v2-bridge
secret
service-token
smtp
webhook
cli
company
docs
api-reference/endpoints
changelog
cli/commands
documentation
images
integrations
mint.json
sdks
self-hosting
frontend
package-lock.jsonpackage.json
src
components
tags/CreateTagModal
utilities/secrets
v2
const.ts
context
OrgPermissionContext
ProjectPermissionContext
helpers
hooks
layouts/AppLayout
pages
cli-redirect.tsx
integrations
aws-parameter-store
aws-secret-manager
circleci
flyio
github
gitlab
hashicorp-vault
heroku
qovery
render
vercel
login
org/[id]
admin
memberships/[membershipId]
overview
roles/[roleId]
project/[id]
ca/[caId]
pki-collections/[collectionId]
roles/[roleSlug]
share-secret
shared/secret/[id]
views
IntegrationsPage
IntegrationsPage.tsx
components/IntegrationsSection
Login/components
MFAStep
PasswordStep
Org
OrgAdminPage
Project
AuditLogsPage/components
CaPage
CertificatesPage
MembersPage
PkiCollectionPage
RolePage
Types
SecretApprovalPage
SecretMainPage
SecretOverviewPage
SecretRotationPage
SecretRotationPage.tsx
components/CreateRotationForm/steps
Settings
ShareSecretPage/components
ShareSecretPublicPage
ViewSecretPublicPage
admin/DashboardPage
helm-charts/secrets-operator
k8-operator

@ -14,7 +14,6 @@ defaults:
jobs:
build-and-deploy:
runs-on: ubuntu-20.04
strategy:
matrix:
arch: [x64, arm64]
@ -24,6 +23,7 @@ jobs:
target: node20-linux
- os: win
target: node20-win
runs-on: ${{ (matrix.arch == 'arm64' && matrix.os == 'linux') && 'ubuntu24-arm64' || 'ubuntu-latest' }}
steps:
- name: Checkout code
@ -49,9 +49,9 @@ jobs:
- name: Package into node binary
run: |
if [ "${{ matrix.os }}" != "linux" ]; then
pkg --no-bytecode --public-packages "*" --public --compress Brotli --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-core-${{ matrix.os }}-${{ matrix.arch }} .
pkg --no-bytecode --public-packages "*" --public --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-core-${{ matrix.os }}-${{ matrix.arch }} .
else
pkg --no-bytecode --public-packages "*" --public --compress Brotli --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-core .
pkg --no-bytecode --public-packages "*" --public --target ${{ matrix.target }}-${{ matrix.arch }} --output ./binary/infisical-core .
fi
# Set up .deb package structure (Debian/Ubuntu only)
@ -84,7 +84,12 @@ jobs:
mv infisical-core.deb ./binary/infisical-core-${{matrix.arch}}.deb
- uses: actions/setup-python@v4
- run: pip install --upgrade cloudsmith-cli
with:
python-version: "3.x" # Specify the Python version you need
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
pip install --upgrade cloudsmith-cli
# Publish .deb file to Cloudsmith (Debian/Ubuntu only)
- name: Publish to Cloudsmith (Debian/Ubuntu)

@ -22,14 +22,14 @@ jobs:
# uncomment this when testing locally using nektos/act
- uses: KengoTODA/actions-setup-docker-compose@v1
if: ${{ env.ACT }}
name: Install `docker-compose` for local simulations
name: Install `docker compose` for local simulations
with:
version: "2.14.2"
- name: 📦Build the latest image
run: docker build --tag infisical-api .
working-directory: backend
- name: Start postgres and redis
run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis
run: touch .env && docker compose -f docker-compose.dev.yml up -d db redis
- name: Start the server
run: |
echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env
@ -72,6 +72,6 @@ jobs:
run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR
- name: cleanup
run: |
docker-compose -f "docker-compose.dev.yml" down
docker compose -f "docker-compose.dev.yml" down
docker stop infisical-api
docker remove infisical-api

@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@v3
- uses: KengoTODA/actions-setup-docker-compose@v1
if: ${{ env.ACT }}
name: Install `docker-compose` for local simulations
name: Install `docker compose` for local simulations
with:
version: "2.14.2"
- name: 🔧 Setup Node 20
@ -33,7 +33,7 @@ jobs:
run: npm install
working-directory: backend
- name: Start postgres and redis
run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis
run: touch .env && docker compose -f docker-compose.dev.yml up -d db redis
- name: Start integration test
run: npm run test:e2e
working-directory: backend
@ -44,4 +44,4 @@ jobs:
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
- name: cleanup
run: |
docker-compose -f "docker-compose.dev.yml" down
docker compose -f "docker-compose.dev.yml" down

@ -50,6 +50,6 @@ jobs:
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }}
CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }}
INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
# INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
run: go test -v -count=1 ./test

@ -15,3 +15,16 @@ up-prod:
down:
docker compose -f docker-compose.dev.yml down
reviewable-ui:
cd frontend && \
npm run lint:fix && \
npm run type:check
reviewable-api:
cd backend && \
npm run lint:fix && \
npm run type:check
reviewable: reviewable-ui reviewable-api

@ -0,0 +1,576 @@
import { SecretType } from "@app/db/schemas";
import { seedData1 } from "@app/db/seed-data";
import { AuthMode } from "@app/services/auth/auth-type";
type TRawSecret = {
secretKey: string;
secretValue: string;
secretComment?: string;
version: number;
};
const createSecret = async (dto: { path: string; key: string; value: string; comment: string; type?: SecretType }) => {
const createSecretReqBody = {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
type: dto.type || SecretType.Shared,
secretPath: dto.path,
secretKey: dto.key,
secretValue: dto.value,
secretComment: dto.comment
};
const createSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/raw/${dto.key}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: createSecretReqBody
});
expect(createSecRes.statusCode).toBe(200);
const createdSecretPayload = JSON.parse(createSecRes.payload);
expect(createdSecretPayload).toHaveProperty("secret");
return createdSecretPayload.secret as TRawSecret;
};
const deleteSecret = async (dto: { path: string; key: string }) => {
const deleteSecRes = await testServer.inject({
method: "DELETE",
url: `/api/v3/secrets/raw/${dto.key}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
secretPath: dto.path
}
});
expect(deleteSecRes.statusCode).toBe(200);
const updatedSecretPayload = JSON.parse(deleteSecRes.payload);
expect(updatedSecretPayload).toHaveProperty("secret");
return updatedSecretPayload.secret as TRawSecret;
};
describe.each([{ auth: AuthMode.JWT }, { auth: AuthMode.IDENTITY_ACCESS_TOKEN }])(
"Secret V2 Architecture - $auth mode",
async ({ auth }) => {
let folderId = "";
let authToken = "";
const secretTestCases = [
{
path: "/",
secret: {
key: "SEC1",
value: "something-secret",
comment: "some comment"
}
},
{
path: "/nested1/nested2/folder",
secret: {
key: "NESTED-SEC1",
value: "something-secret",
comment: "some comment"
}
},
{
path: "/",
secret: {
key: "secret-key-2",
value: `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCa6eeFk+cMVqFn
hoVQDYgn2Ptp5Azysr2UPq6P73pCL9BzUtOXKZROqDyGehzzfg3wE2KdYU1Jk5Uq
fP0ZOWDIlM2SaVCSI3FW32o5+ZiggjpqcVdLFc/PS0S/ZdSmpPd8h11iO2brtIAI
ugTW8fcKlGSNUwx9aFmE7A6JnTRliTxB1l6QaC+YAwTK39VgeVH2gDSWC407aS15
QobAkaBKKmFkzB5D7i2ZJwt+uXJV/rbLmyDmtnw0lubciGn7NX9wbYef180fisqT
aPNAz0nPKk0fFH2Wd5MZixNGbrrpDA+FCYvI5doThZyT2hpj08qWP07oXXCAqw46
IEupNSILAgMBAAECggEBAIJb5KzeaiZS3B3O8G4OBQ5rJB3WfyLYUHnoSWLsBbie
nc392/ovThLmtZAAQE6SO85Tsb93+t64Z2TKqv1H8G658UeMgfWIB78v4CcLJ2mi
TN/3opqXrzjkQOTDHzBgT7al/mpETHZ6fOdbCemK0fVALGFUioUZg4M8VXtuI4Jw
q28jAyoRKrCrzda4BeQ553NZ4G5RvwhX3O2I8B8upTbt5hLcisBKy8MPLYY5LUFj
YKAP+raf6QLliP6KYHuVxUlgzxjLTxVG41etcyqqZF+foyiKBO3PU3n8oh++tgQP
ExOxiR0JSkBG5b+oOBD0zxcvo3/SjBHn0dJOZCSU2SkCgYEAyCe676XnNyBZMRD7
6trsaoiCWBpA6M8H44+x3w4cQFtqV38RyLy60D+iMKjIaLqeBbnay61VMzo24Bz3
EuF2n4+9k/MetLJ0NCw8HmN5k0WSMD2BFsJWG8glVbzaqzehP4tIclwDTYc1jQVt
IoV2/iL7HGT+x2daUwbU5kN5hK0CgYEAxiLB+fmjxJW7VY4SHDLqPdpIW0q/kv4K
d/yZBrCX799vjmFb9vLh7PkQUfJhMJ/ttJOd7EtT3xh4mfkBeLfHwVU0d/ahbmSH
UJu/E9ZGxAW3PP0kxHZtPrLKQwBnfq8AxBauIhR3rPSorQTIOKtwz1jMlHFSUpuL
3KeK2YfDYJcCgYEAkQnJOlNcAuRb/WQzSHIvktssqK8NjiZHryy3Vc0hx7j2jES2
HGI2dSVHYD9OSiXA0KFm3OTTsnViwm/60iGzFdjRJV6tR39xGUVcoyCuPnvRfUd0
PYvBXgxgkYpyYlPDcwp5CvWGJy3tLi1acgOIwIuUr3S38sL//t4adGk8q1kCgYB8
Jbs1Tl53BvrimKpwUNbE+sjrquJu0A7vL68SqgQJoQ7dP9PH4Ff/i+/V6PFM7mib
BQOm02wyFbs7fvKVGVJoqWK+6CIucX732x7W5yRgHtS5ukQXdbzt1Ek3wkEW98Cb
HTruz7RNAt/NyXlLSODeit1lBbx3Vk9EaxZtRsv88QKBgGn7JwXgez9NOyobsNIo
QVO80rpUeenSjuFi+R0VmbLKe/wgAQbYJ0xTAsQ0btqViMzB27D6mJyC+KUIwWNX
MN8a+m46v4kqvZkKL2c4gmDibyURNe/vCtCHFuanJS/1mo2tr4XDyEeiuK52eTd9
omQDpP86RX/hIIQ+JyLSaWYa
-----END PRIVATE KEY-----`,
comment:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation"
}
},
{
path: "/nested1/nested2/folder",
secret: {
key: "secret-key-3",
value: `-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCa6eeFk+cMVqFn
hoVQDYgn2Ptp5Azysr2UPq6P73pCL9BzUtOXKZROqDyGehzzfg3wE2KdYU1Jk5Uq
fP0ZOWDIlM2SaVCSI3FW32o5+ZiggjpqcVdLFc/PS0S/ZdSmpPd8h11iO2brtIAI
ugTW8fcKlGSNUwx9aFmE7A6JnTRliTxB1l6QaC+YAwTK39VgeVH2gDSWC407aS15
QobAkaBKKmFkzB5D7i2ZJwt+uXJV/rbLmyDmtnw0lubciGn7NX9wbYef180fisqT
aPNAz0nPKk0fFH2Wd5MZixNGbrrpDA+FCYvI5doThZyT2hpj08qWP07oXXCAqw46
IEupNSILAgMBAAECggEBAIJb5KzeaiZS3B3O8G4OBQ5rJB3WfyLYUHnoSWLsBbie
nc392/ovThLmtZAAQE6SO85Tsb93+t64Z2TKqv1H8G658UeMgfWIB78v4CcLJ2mi
TN/3opqXrzjkQOTDHzBgT7al/mpETHZ6fOdbCemK0fVALGFUioUZg4M8VXtuI4Jw
q28jAyoRKrCrzda4BeQ553NZ4G5RvwhX3O2I8B8upTbt5hLcisBKy8MPLYY5LUFj
YKAP+raf6QLliP6KYHuVxUlgzxjLTxVG41etcyqqZF+foyiKBO3PU3n8oh++tgQP
ExOxiR0JSkBG5b+oOBD0zxcvo3/SjBHn0dJOZCSU2SkCgYEAyCe676XnNyBZMRD7
6trsaoiCWBpA6M8H44+x3w4cQFtqV38RyLy60D+iMKjIaLqeBbnay61VMzo24Bz3
EuF2n4+9k/MetLJ0NCw8HmN5k0WSMD2BFsJWG8glVbzaqzehP4tIclwDTYc1jQVt
IoV2/iL7HGT+x2daUwbU5kN5hK0CgYEAxiLB+fmjxJW7VY4SHDLqPdpIW0q/kv4K
d/yZBrCX799vjmFb9vLh7PkQUfJhMJ/ttJOd7EtT3xh4mfkBeLfHwVU0d/ahbmSH
UJu/E9ZGxAW3PP0kxHZtPrLKQwBnfq8AxBauIhR3rPSorQTIOKtwz1jMlHFSUpuL
3KeK2YfDYJcCgYEAkQnJOlNcAuRb/WQzSHIvktssqK8NjiZHryy3Vc0hx7j2jES2
HGI2dSVHYD9OSiXA0KFm3OTTsnViwm/60iGzFdjRJV6tR39xGUVcoyCuPnvRfUd0
PYvBXgxgkYpyYlPDcwp5CvWGJy3tLi1acgOIwIuUr3S38sL//t4adGk8q1kCgYB8
Jbs1Tl53BvrimKpwUNbE+sjrquJu0A7vL68SqgQJoQ7dP9PH4Ff/i+/V6PFM7mib
BQOm02wyFbs7fvKVGVJoqWK+6CIucX732x7W5yRgHtS5ukQXdbzt1Ek3wkEW98Cb
HTruz7RNAt/NyXlLSODeit1lBbx3Vk9EaxZtRsv88QKBgGn7JwXgez9NOyobsNIo
QVO80rpUeenSjuFi+R0VmbLKe/wgAQbYJ0xTAsQ0btqViMzB27D6mJyC+KUIwWNX
MN8a+m46v4kqvZkKL2c4gmDibyURNe/vCtCHFuanJS/1mo2tr4XDyEeiuK52eTd9
omQDpP86RX/hIIQ+JyLSaWYa
-----END PRIVATE KEY-----`,
comment:
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation"
}
},
{
path: "/nested1/nested2/folder",
secret: {
key: "secret-key-3",
value:
"TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gU2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uCg==",
comment: ""
}
}
];
beforeAll(async () => {
if (auth === AuthMode.JWT) {
authToken = jwtAuthToken;
} else if (auth === AuthMode.IDENTITY_ACCESS_TOKEN) {
const identityLogin = await testServer.inject({
method: "POST",
url: "/api/v1/auth/universal-auth/login",
body: {
clientSecret: seedData1.machineIdentity.clientCredentials.secret,
clientId: seedData1.machineIdentity.clientCredentials.id
}
});
expect(identityLogin.statusCode).toBe(200);
authToken = identityLogin.json().accessToken;
}
// create a deep folder
const folderCreate = await testServer.inject({
method: "POST",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
name: "folder",
path: "/nested1/nested2"
}
});
expect(folderCreate.statusCode).toBe(200);
folderId = folderCreate.json().folder.id;
});
afterAll(async () => {
const deleteFolder = await testServer.inject({
method: "DELETE",
url: `/api/v1/folders/${folderId}`,
headers: {
authorization: `Bearer ${authToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
path: "/nested1/nested2"
}
});
expect(deleteFolder.statusCode).toBe(200);
});
const getSecrets = async (environment: string, secretPath = "/") => {
const res = await testServer.inject({
method: "GET",
url: `/api/v3/secrets/raw`,
headers: {
authorization: `Bearer ${authToken}`
},
query: {
secretPath,
environment,
workspaceId: seedData1.projectV3.id
}
});
const secrets: TRawSecret[] = JSON.parse(res.payload).secrets || [];
return secrets;
};
test.each(secretTestCases)("Create secret in path $path", async ({ secret, path }) => {
const createdSecret = await createSecret({ path, ...secret });
expect(createdSecret.secretKey).toEqual(secret.key);
expect(createdSecret.secretValue).toEqual(secret.value);
expect(createdSecret.secretComment || "").toEqual(secret.comment);
expect(createdSecret.version).toEqual(1);
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: secret.key,
secretValue: secret.value,
type: SecretType.Shared
})
])
);
await deleteSecret({ path, key: secret.key });
});
test.each(secretTestCases)("Get secret by name in path $path", async ({ secret, path }) => {
await createSecret({ path, ...secret });
const getSecByNameRes = await testServer.inject({
method: "GET",
url: `/api/v3/secrets/raw/${secret.key}`,
headers: {
authorization: `Bearer ${authToken}`
},
query: {
secretPath: path,
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug
}
});
expect(getSecByNameRes.statusCode).toBe(200);
const getSecretByNamePayload = JSON.parse(getSecByNameRes.payload);
expect(getSecretByNamePayload).toHaveProperty("secret");
const decryptedSecret = getSecretByNamePayload.secret as TRawSecret;
expect(decryptedSecret.secretKey).toEqual(secret.key);
expect(decryptedSecret.secretValue).toEqual(secret.value);
expect(decryptedSecret.secretComment || "").toEqual(secret.comment);
await deleteSecret({ path, key: secret.key });
});
if (auth === AuthMode.JWT) {
test.each(secretTestCases)(
"Creating personal secret without shared throw error in path $path",
async ({ secret }) => {
const createSecretReqBody = {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
type: SecretType.Personal,
secretKey: secret.key,
secretValue: secret.value,
secretComment: secret.comment
};
const createSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/raw/SEC2`,
headers: {
authorization: `Bearer ${authToken}`
},
body: createSecretReqBody
});
const payload = JSON.parse(createSecRes.payload);
expect(createSecRes.statusCode).toBe(400);
expect(payload.error).toEqual("BadRequest");
}
);
test.each(secretTestCases)("Creating personal secret in path $path", async ({ secret, path }) => {
await createSecret({ path, ...secret });
const createSecretReqBody = {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
type: SecretType.Personal,
secretPath: path,
secretKey: secret.key,
secretValue: "personal-value",
secretComment: secret.comment
};
const createSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/raw/${secret.key}`,
headers: {
authorization: `Bearer ${authToken}`
},
body: createSecretReqBody
});
expect(createSecRes.statusCode).toBe(200);
// list secrets should contain personal one and shared one
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: secret.key,
secretValue: secret.value,
type: SecretType.Shared
}),
expect.objectContaining({
secretKey: secret.key,
secretValue: "personal-value",
type: SecretType.Personal
})
])
);
await deleteSecret({ path, key: secret.key });
});
test.each(secretTestCases)(
"Deleting personal one should not delete shared secret in path $path",
async ({ secret, path }) => {
await createSecret({ path, ...secret }); // shared one
await createSecret({ path, ...secret, type: SecretType.Personal });
// shared secret deletion should delete personal ones also
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: secret.key,
type: SecretType.Shared
}),
expect.not.objectContaining({
secretKey: secret.key,
type: SecretType.Personal
})
])
);
await deleteSecret({ path, key: secret.key });
}
);
}
test.each(secretTestCases)("Update secret in path $path", async ({ path, secret }) => {
await createSecret({ path, ...secret });
const updateSecretReqBody = {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
type: SecretType.Shared,
secretPath: path,
secretKey: secret.key,
secretValue: "new-value",
secretComment: secret.comment
};
const updateSecRes = await testServer.inject({
method: "PATCH",
url: `/api/v3/secrets/raw/${secret.key}`,
headers: {
authorization: `Bearer ${authToken}`
},
body: updateSecretReqBody
});
expect(updateSecRes.statusCode).toBe(200);
const updatedSecretPayload = JSON.parse(updateSecRes.payload);
expect(updatedSecretPayload).toHaveProperty("secret");
const decryptedSecret = updatedSecretPayload.secret;
expect(decryptedSecret.secretKey).toEqual(secret.key);
expect(decryptedSecret.secretValue).toEqual("new-value");
expect(decryptedSecret.secretComment || "").toEqual(secret.comment);
// list secret should have updated value
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
secretKey: secret.key,
secretValue: "new-value",
type: SecretType.Shared
})
])
);
await deleteSecret({ path, key: secret.key });
});
test.each(secretTestCases)("Delete secret in path $path", async ({ secret, path }) => {
await createSecret({ path, ...secret });
const deletedSecret = await deleteSecret({ path, key: secret.key });
expect(deletedSecret.secretKey).toEqual(secret.key);
// shared secret deletion should delete personal ones also
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.not.arrayContaining([
expect.objectContaining({
secretKey: secret.key,
type: SecretType.Shared
}),
expect.objectContaining({
secretKey: secret.key,
type: SecretType.Personal
})
])
);
});
test.each(secretTestCases)("Bulk create secrets in path $path", async ({ secret, path }) => {
const createSharedSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/batch/raw`,
headers: {
authorization: `Bearer ${authToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretKey: `BULK-${secret.key}-${i + 1}`,
secretValue: secret.value,
secretComment: secret.comment
}))
}
});
expect(createSharedSecRes.statusCode).toBe(200);
const createSharedSecPayload = JSON.parse(createSharedSecRes.payload);
expect(createSharedSecPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
secretKey: `BULK-${secret.key}-${i + 1}`,
secretValue: secret.value,
type: SecretType.Shared
})
)
)
);
await Promise.all(
Array.from(Array(5)).map((_e, i) => deleteSecret({ path, key: `BULK-${secret.key}-${i + 1}` }))
);
});
test.each(secretTestCases)("Bulk create fail on existing secret in path $path", async ({ secret, path }) => {
await createSecret({ ...secret, key: `BULK-${secret.key}-1`, path });
const createSharedSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/batch/raw`,
headers: {
authorization: `Bearer ${authToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretKey: `BULK-${secret.key}-${i + 1}`,
secretValue: secret.value,
secretComment: secret.comment
}))
}
});
expect(createSharedSecRes.statusCode).toBe(400);
await deleteSecret({ path, key: `BULK-${secret.key}-1` });
});
test.each(secretTestCases)("Bulk update secrets in path $path", async ({ secret, path }) => {
await Promise.all(
Array.from(Array(5)).map((_e, i) => createSecret({ ...secret, key: `BULK-${secret.key}-${i + 1}`, path }))
);
const updateSharedSecRes = await testServer.inject({
method: "PATCH",
url: `/api/v3/secrets/batch/raw`,
headers: {
authorization: `Bearer ${authToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretKey: `BULK-${secret.key}-${i + 1}`,
secretValue: "update-value",
secretComment: secret.comment
}))
}
});
expect(updateSharedSecRes.statusCode).toBe(200);
const updateSharedSecPayload = JSON.parse(updateSharedSecRes.payload);
expect(updateSharedSecPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
secretKey: `BULK-${secret.key}-${i + 1}`,
secretValue: "update-value",
type: SecretType.Shared
})
)
)
);
await Promise.all(
Array.from(Array(5)).map((_e, i) => deleteSecret({ path, key: `BULK-${secret.key}-${i + 1}` }))
);
});
test.each(secretTestCases)("Bulk delete secrets in path $path", async ({ secret, path }) => {
await Promise.all(
Array.from(Array(5)).map((_e, i) => createSecret({ ...secret, key: `BULK-${secret.key}-${i + 1}`, path }))
);
const deletedSharedSecRes = await testServer.inject({
method: "DELETE",
url: `/api/v3/secrets/batch/raw`,
headers: {
authorization: `Bearer ${authToken}`
},
body: {
workspaceId: seedData1.projectV3.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretKey: `BULK-${secret.key}-${i + 1}`
}))
}
});
expect(deletedSharedSecRes.statusCode).toBe(200);
const deletedSecretPayload = JSON.parse(deletedSharedSecRes.payload);
expect(deletedSecretPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.not.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
secretKey: `BULK-${secret.value}-${i + 1}`,
type: SecretType.Shared
})
)
)
);
});
}
);

4617
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -34,14 +34,14 @@
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
"dev:docker": "nodemon",
"build": "tsup",
"build": "tsup --sourcemap",
"build:frontend": "npm run build --prefix ../frontend",
"start": "node dist/main.mjs",
"start": "node --enable-source-maps dist/main.mjs",
"type:check": "tsc --noEmit",
"lint:fix": "eslint --fix --ext js,ts ./src",
"lint": "eslint 'src/**/*.ts'",
"test:e2e": "vitest run -c vitest.e2e.config.ts",
"test:e2e-watch": "vitest -c vitest.e2e.config.ts",
"test:e2e": "vitest run -c vitest.e2e.config.ts --bail=1",
"test:e2e-watch": "vitest -c vitest.e2e.config.ts --bail=1",
"test:e2e-coverage": "vitest run --coverage -c vitest.e2e.config.ts",
"generate:component": "tsx ./scripts/create-backend-file.ts",
"generate:schema": "tsx ./scripts/generate-schema-types.ts",
@ -78,6 +78,7 @@
"@types/picomatch": "^2.3.3",
"@types/prompt-sync": "^4.2.3",
"@types/resolve": "^1.20.6",
"@types/safe-regex": "^1.1.6",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
@ -121,10 +122,11 @@
"@fastify/swagger": "^8.14.0",
"@fastify/swagger-ui": "^2.1.0",
"@node-saml/passport-saml": "^4.0.4",
"@octokit/plugin-retry": "^5.0.5",
"@octokit/rest": "^20.0.2",
"@octokit/webhooks-types": "^7.3.1",
"@peculiar/asn1-schema": "^2.3.8",
"@peculiar/x509": "^1.10.0",
"@peculiar/x509": "^1.12.1",
"@serdnam/pino-cloudwatch-transport": "^1.0.4",
"@sindresorhus/slugify": "1.1.0",
"@team-plain/typescript-sdk": "^4.6.1",
@ -171,6 +173,7 @@
"pino": "^8.16.2",
"posthog-node": "^3.6.2",
"probot": "^13.0.0",
"safe-regex": "^2.1.1",
"smee-client": "^2.0.0",
"tedious": "^18.2.1",
"tweetnacl": "^1.0.3",

@ -7,14 +7,33 @@ const prompt = promptSync({
sigint: true
});
type ComponentType = 1 | 2 | 3;
console.log(`
Component List
--------------
0. Exit
1. Service component
2. DAL component
3. Router component
`);
const componentType = parseInt(prompt("Select a component: "), 10);
function getComponentType(): ComponentType {
while (true) {
const input = prompt("Select a component (0-3): ");
const componentType = parseInt(input, 10);
if (componentType === 0) {
console.log("Exiting the program. Goodbye!");
process.exit(0);
} else if (componentType === 1 || componentType === 2 || componentType === 3) {
return componentType;
} else {
console.log("Invalid input. Please enter 0, 1, 2, or 3.");
}
}
}
const componentType = getComponentType();
if (componentType === 1) {
const componentName = prompt("Enter service name: ");

@ -18,6 +18,7 @@ import { TOidcConfigServiceFactory } from "@app/ee/services/oidc/oidc-config-ser
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service";
import { RateLimitConfiguration } from "@app/ee/services/rate-limit/rate-limit-types";
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
@ -35,6 +36,7 @@ import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
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";
@ -50,6 +52,9 @@ import { TIntegrationServiceFactory } from "@app/services/integration/integratio
import { TIntegrationAuthServiceFactory } from "@app/services/integration-auth/integration-auth-service";
import { TOrgRoleServiceFactory } from "@app/services/org/org-role-service";
import { TOrgServiceFactory } from "@app/services/org/org-service";
import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-service";
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
import { TProjectServiceFactory } from "@app/services/project/project-service";
import { TProjectBotServiceFactory } from "@app/services/project-bot/project-bot-service";
import { TProjectEnvServiceFactory } from "@app/services/project-env/project-env-service";
@ -88,6 +93,7 @@ declare module "fastify" {
id: string;
orgId: string;
};
rateLimits: RateLimitConfiguration;
// passport data
passportUser: {
isUserCompleted: string;
@ -113,6 +119,7 @@ declare module "fastify" {
group: TGroupServiceFactory;
groupProject: TGroupProjectServiceFactory;
apiKey: TApiKeyServiceFactory;
pkiAlert: TPkiAlertServiceFactory;
project: TProjectServiceFactory;
projectMembership: TProjectMembershipServiceFactory;
projectEnv: TProjectEnvServiceFactory;
@ -150,8 +157,10 @@ declare module "fastify" {
auditLog: TAuditLogServiceFactory;
auditLogStream: TAuditLogStreamServiceFactory;
certificate: TCertificateServiceFactory;
certificateTemplate: TCertificateTemplateServiceFactory;
certificateAuthority: TCertificateAuthorityServiceFactory;
certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory;
pkiCollection: TPkiCollectionServiceFactory;
secretScanning: TSecretScanningServiceFactory;
license: TLicenseServiceFactory;
trustedIp: TTrustedIpServiceFactory;
@ -165,6 +174,7 @@ declare module "fastify" {
rateLimit: TRateLimitServiceFactory;
userEngagement: TUserEngagementServiceFactory;
externalKms: TExternalKmsServiceFactory;
orgAdmin: TOrgAdminServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

@ -53,6 +53,9 @@ import {
TCertificateSecretsUpdate,
TCertificatesInsert,
TCertificatesUpdate,
TCertificateTemplates,
TCertificateTemplatesInsert,
TCertificateTemplatesUpdate,
TDynamicSecretLeases,
TDynamicSecretLeasesInsert,
TDynamicSecretLeasesUpdate,
@ -161,6 +164,15 @@ import {
TOrgRoles,
TOrgRolesInsert,
TOrgRolesUpdate,
TPkiAlerts,
TPkiAlertsInsert,
TPkiAlertsUpdate,
TPkiCollectionItems,
TPkiCollectionItemsInsert,
TPkiCollectionItemsUpdate,
TPkiCollections,
TPkiCollectionsInsert,
TPkiCollectionsUpdate,
TProjectBots,
TProjectBotsInsert,
TProjectBotsUpdate,
@ -204,6 +216,9 @@ import {
TSecretApprovalRequestSecretTags,
TSecretApprovalRequestSecretTagsInsert,
TSecretApprovalRequestSecretTagsUpdate,
TSecretApprovalRequestSecretTagsV2,
TSecretApprovalRequestSecretTagsV2Insert,
TSecretApprovalRequestSecretTagsV2Update,
TSecretApprovalRequestsInsert,
TSecretApprovalRequestsReviewers,
TSecretApprovalRequestsReviewersInsert,
@ -211,6 +226,9 @@ import {
TSecretApprovalRequestsSecrets,
TSecretApprovalRequestsSecretsInsert,
TSecretApprovalRequestsSecretsUpdate,
TSecretApprovalRequestsSecretsV2,
TSecretApprovalRequestsSecretsV2Insert,
TSecretApprovalRequestsSecretsV2Update,
TSecretApprovalRequestsUpdate,
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
@ -227,9 +245,15 @@ import {
TSecretReferences,
TSecretReferencesInsert,
TSecretReferencesUpdate,
TSecretReferencesV2,
TSecretReferencesV2Insert,
TSecretReferencesV2Update,
TSecretRotationOutputs,
TSecretRotationOutputsInsert,
TSecretRotationOutputsUpdate,
TSecretRotationOutputV2,
TSecretRotationOutputV2Insert,
TSecretRotationOutputV2Update,
TSecretRotations,
TSecretRotationsInsert,
TSecretRotationsUpdate,
@ -248,6 +272,9 @@ import {
TSecretSnapshotSecrets,
TSecretSnapshotSecretsInsert,
TSecretSnapshotSecretsUpdate,
TSecretSnapshotSecretsV2,
TSecretSnapshotSecretsV2Insert,
TSecretSnapshotSecretsV2Update,
TSecretSnapshotsInsert,
TSecretSnapshotsUpdate,
TSecretsUpdate,
@ -263,6 +290,9 @@ import {
TSecretVersionTagJunction,
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate,
TSecretVersionV2TagJunction,
TSecretVersionV2TagJunctionInsert,
TSecretVersionV2TagJunctionUpdate,
TServiceTokens,
TServiceTokensInsert,
TServiceTokensUpdate,
@ -291,6 +321,17 @@ import {
TWebhooksInsert,
TWebhooksUpdate
} from "@app/db/schemas";
import {
TSecretV2TagJunction,
TSecretV2TagJunctionInsert,
TSecretV2TagJunctionUpdate
} from "@app/db/schemas/secret-v2-tag-junction";
import {
TSecretVersionsV2,
TSecretVersionsV2Insert,
TSecretVersionsV2Update
} from "@app/db/schemas/secret-versions-v2";
import { TSecretsV2, TSecretsV2Insert, TSecretsV2Update } from "@app/db/schemas/secrets-v2";
declare module "knex" {
namespace Knex {
@ -326,6 +367,11 @@ declare module "knex/types/tables" {
TCertificateAuthorityCrlUpdate
>;
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
TCertificateTemplates,
TCertificateTemplatesInsert,
TCertificateTemplatesUpdate
>;
[TableName.CertificateBody]: KnexOriginal.CompositeTableType<
TCertificateBodies,
TCertificateBodiesInsert,
@ -336,6 +382,17 @@ declare module "knex/types/tables" {
TCertificateSecretsInsert,
TCertificateSecretsUpdate
>;
[TableName.PkiAlert]: KnexOriginal.CompositeTableType<TPkiAlerts, TPkiAlertsInsert, TPkiAlertsUpdate>;
[TableName.PkiCollection]: KnexOriginal.CompositeTableType<
TPkiCollections,
TPkiCollectionsInsert,
TPkiCollectionsUpdate
>;
[TableName.PkiCollectionItem]: KnexOriginal.CompositeTableType<
TPkiCollectionItems,
TPkiCollectionItemsInsert,
TPkiCollectionItemsUpdate
>;
[TableName.UserGroupMembership]: KnexOriginal.CompositeTableType<
TUserGroupMembership,
TUserGroupMembershipInsert,
@ -645,7 +702,23 @@ declare module "knex/types/tables" {
TSecretScanningGitRisksUpdate
>;
[TableName.TrustedIps]: KnexOriginal.CompositeTableType<TTrustedIps, TTrustedIpsInsert, TTrustedIpsUpdate>;
[TableName.SecretV2]: KnexOriginal.CompositeTableType<TSecretsV2, TSecretsV2Insert, TSecretsV2Update>;
[TableName.SecretVersionV2]: KnexOriginal.CompositeTableType<
TSecretVersionsV2,
TSecretVersionsV2Insert,
TSecretVersionsV2Update
>;
[TableName.SecretReferenceV2]: KnexOriginal.CompositeTableType<
TSecretReferencesV2,
TSecretReferencesV2Insert,
TSecretReferencesV2Update
>;
// Junction tables
[TableName.SecretV2JnTag]: KnexOriginal.CompositeTableType<
TSecretV2TagJunction,
TSecretV2TagJunctionInsert,
TSecretV2TagJunctionUpdate
>;
[TableName.JnSecretTag]: KnexOriginal.CompositeTableType<
TSecretTagJunction,
TSecretTagJunctionInsert,
@ -656,6 +729,31 @@ declare module "knex/types/tables" {
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate
>;
[TableName.SecretVersionV2Tag]: KnexOriginal.CompositeTableType<
TSecretVersionV2TagJunction,
TSecretVersionV2TagJunctionInsert,
TSecretVersionV2TagJunctionUpdate
>;
[TableName.SnapshotSecretV2]: KnexOriginal.CompositeTableType<
TSecretSnapshotSecretsV2,
TSecretSnapshotSecretsV2Insert,
TSecretSnapshotSecretsV2Update
>;
[TableName.SecretApprovalRequestSecretV2]: KnexOriginal.CompositeTableType<
TSecretApprovalRequestsSecretsV2,
TSecretApprovalRequestsSecretsV2Insert,
TSecretApprovalRequestsSecretsV2Update
>;
[TableName.SecretApprovalRequestSecretTagV2]: KnexOriginal.CompositeTableType<
TSecretApprovalRequestSecretTagsV2,
TSecretApprovalRequestSecretTagsV2Insert,
TSecretApprovalRequestSecretTagsV2Update
>;
[TableName.SecretRotationOutputV2]: KnexOriginal.CompositeTableType<
TSecretRotationOutputV2,
TSecretRotationOutputV2Insert,
TSecretRotationOutputV2Update
>;
// KMS service
[TableName.KmsServerRootConfig]: KnexOriginal.CompositeTableType<
TKmsRootConfig,

@ -0,0 +1,23 @@
import { Knex } from "knex";
import { EnforcementLevel } from "@app/lib/types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "enforcementLevel");
if (!hasColumn) {
await knex.schema.table(TableName.SecretApprovalPolicy, (table) => {
table.string("enforcementLevel", 10).notNullable().defaultTo(EnforcementLevel.Hard);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretApprovalPolicy, "enforcementLevel");
if (hasColumn) {
await knex.schema.table(TableName.SecretApprovalPolicy, (table) => {
table.dropColumn("enforcementLevel");
});
}
}

@ -0,0 +1,23 @@
import { Knex } from "knex";
import { EnforcementLevel } from "@app/lib/types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "enforcementLevel");
if (!hasColumn) {
await knex.schema.table(TableName.AccessApprovalPolicy, (table) => {
table.string("enforcementLevel", 10).notNullable().defaultTo(EnforcementLevel.Hard);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.AccessApprovalPolicy, "enforcementLevel");
if (hasColumn) {
await knex.schema.table(TableName.AccessApprovalPolicy, (table) => {
table.dropColumn("enforcementLevel");
});
}
}

@ -0,0 +1,23 @@
import { Knex } from "knex";
import { SecretSharingAccessType } from "@app/lib/types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretSharing, "accessType");
if (!hasColumn) {
await knex.schema.table(TableName.SecretSharing, (table) => {
table.string("accessType").notNullable().defaultTo(SecretSharingAccessType.Anyone);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretSharing, "accessType");
if (hasColumn) {
await knex.schema.table(TableName.SecretSharing, (table) => {
table.dropColumn("accessType");
});
}
}

@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "bypassReason");
if (!hasColumn) {
await knex.schema.table(TableName.SecretApprovalRequest, (table) => {
table.string("bypassReason").nullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.SecretApprovalRequest, "bypassReason");
if (hasColumn) {
await knex.schema.table(TableName.SecretApprovalRequest, (table) => {
table.dropColumn("bypassReason");
});
}
}

@ -0,0 +1,294 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
// ---------- ACCESS APPROVAL POLICY APPROVER ------------
const hasApproverUserId = await knex.schema.hasColumn(TableName.AccessApprovalPolicyApprover, "approverUserId");
const hasApproverId = await knex.schema.hasColumn(TableName.AccessApprovalPolicyApprover, "approverId");
if (!hasApproverUserId) {
// add the new fields
await knex.schema.alterTable(TableName.AccessApprovalPolicyApprover, (tb) => {
// if (hasApproverId) tb.setNullable("approverId");
tb.uuid("approverUserId");
tb.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
});
// convert project membership id => user id
await knex(TableName.AccessApprovalPolicyApprover).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
approverUserId: knex(TableName.ProjectMembership)
.select("userId")
.where("id", knex.raw("??", [`${TableName.AccessApprovalPolicyApprover}.approverId`]))
});
// drop the old field
await knex.schema.alterTable(TableName.AccessApprovalPolicyApprover, (tb) => {
if (hasApproverId) tb.dropColumn("approverId");
tb.uuid("approverUserId").notNullable().alter();
});
}
// ---------- ACCESS APPROVAL REQUEST ------------
const hasAccessApprovalRequestTable = await knex.schema.hasTable(TableName.AccessApprovalRequest);
const hasRequestedByUserId = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedByUserId");
const hasRequestedBy = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedBy");
if (hasAccessApprovalRequestTable) {
// new fields
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
if (!hasRequestedByUserId) {
tb.uuid("requestedByUserId");
tb.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
}
});
// copy the assigned project membership => user id to new fields
await knex(TableName.AccessApprovalRequest).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
requestedByUserId: knex(TableName.ProjectMembership)
.select("userId")
.where("id", knex.raw("??", [`${TableName.AccessApprovalRequest}.requestedBy`]))
});
// drop old fields
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
if (hasRequestedBy) {
// DROP AT A LATER TIME
// tb.dropColumn("requestedBy");
// ADD ALLOW NULLABLE FOR NOW
tb.uuid("requestedBy").nullable().alter();
}
tb.uuid("requestedByUserId").notNullable().alter();
});
}
// ---------- ACCESS APPROVAL REQUEST REVIEWER ------------
const hasMemberId = await knex.schema.hasColumn(TableName.AccessApprovalRequestReviewer, "member");
const hasReviewerUserId = await knex.schema.hasColumn(TableName.AccessApprovalRequestReviewer, "reviewerUserId");
if (!hasReviewerUserId) {
// new fields
await knex.schema.alterTable(TableName.AccessApprovalRequestReviewer, (tb) => {
// if (hasMemberId) tb.setNullable("member");
tb.uuid("reviewerUserId");
tb.foreign("reviewerUserId").references("id").inTable(TableName.Users).onDelete("SET NULL");
});
// copy project membership => user id to new fields
await knex(TableName.AccessApprovalRequestReviewer).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
reviewerUserId: knex(TableName.ProjectMembership)
.select("userId")
.where("id", knex.raw("??", [`${TableName.AccessApprovalRequestReviewer}.member`]))
});
// drop table
await knex.schema.alterTable(TableName.AccessApprovalRequestReviewer, (tb) => {
if (hasMemberId) {
// DROP AT A LATER TIME
// tb.dropColumn("member");
// ADD ALLOW NULLABLE FOR NOW
tb.uuid("member").nullable().alter();
}
tb.uuid("reviewerUserId").notNullable().alter();
});
}
// ---------- PROJECT USER ADDITIONAL PRIVILEGE ------------
const projectUserAdditionalPrivilegeHasProjectMembershipId = await knex.schema.hasColumn(
TableName.ProjectUserAdditionalPrivilege,
"projectMembershipId"
);
const projectUserAdditionalPrivilegeHasUserId = await knex.schema.hasColumn(
TableName.ProjectUserAdditionalPrivilege,
"userId"
);
if (!projectUserAdditionalPrivilegeHasUserId) {
await knex.schema.alterTable(TableName.ProjectUserAdditionalPrivilege, (tb) => {
tb.uuid("userId");
tb.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
tb.string("projectId");
tb.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
await knex(TableName.ProjectUserAdditionalPrivilege)
.update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
userId: knex(TableName.ProjectMembership)
.select("userId")
.where("id", knex.raw("??", [`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`])),
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
projectId: knex(TableName.ProjectMembership)
.select("projectId")
.where("id", knex.raw("??", [`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`]))
})
.whereNotNull("projectMembershipId");
await knex.schema.alterTable(TableName.ProjectUserAdditionalPrivilege, (tb) => {
tb.uuid("userId").notNullable().alter();
tb.string("projectId").notNullable().alter();
});
}
if (projectUserAdditionalPrivilegeHasProjectMembershipId) {
await knex.schema.alterTable(TableName.ProjectUserAdditionalPrivilege, (tb) => {
// DROP AT A LATER TIME
// tb.dropColumn("projectMembershipId");
// ADD ALLOW NULLABLE FOR NOW
tb.uuid("projectMembershipId").nullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
// We remove project user additional privileges first, because it may delete records in the database where the project membership is not found.
// The project membership won't be found on records created by group members. In those cades we just delete the record and continue.
// When the additionl privilege record is deleted, it will cascade delete the access request created by the group member.
// ---------- PROJECT USER ADDITIONAL PRIVILEGE ------------
const hasUserId = await knex.schema.hasColumn(TableName.ProjectUserAdditionalPrivilege, "userId");
const hasProjectMembershipId = await knex.schema.hasColumn(
TableName.ProjectUserAdditionalPrivilege,
"projectMembershipId"
);
// If it doesn't have the userId field, then the up migration has not run
if (!hasUserId) {
return;
}
await knex.schema.alterTable(TableName.ProjectUserAdditionalPrivilege, (tb) => {
if (!hasProjectMembershipId) {
tb.uuid("projectMembershipId");
tb.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
}
});
if (!hasProjectMembershipId) {
// First, update records where a matching project membership exists
await knex(TableName.ProjectUserAdditionalPrivilege).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
projectMembershipId: knex(TableName.ProjectMembership)
.select("id")
.where("userId", knex.raw("??", [`${TableName.ProjectUserAdditionalPrivilege}.userId`]))
});
await knex(TableName.AccessApprovalRequest).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
projectMembershipId: knex(TableName.ProjectMembership)
.select("id")
.where("userId", knex.raw("??", [`${TableName.SecretApprovalRequest}.userId`]))
});
await knex.schema.alterTable(TableName.ProjectUserAdditionalPrivilege, (tb) => {
tb.dropColumn("userId");
tb.dropColumn("projectId");
tb.uuid("projectMembershipId").notNullable().alter();
});
}
// Then, delete records where no matching project membership was found
await knex(TableName.ProjectUserAdditionalPrivilege).whereNull("projectMembershipId").delete();
await knex(TableName.AccessApprovalRequest).whereNull("requestedBy").delete();
// ---------- ACCESS APPROVAL POLICY APPROVER ------------
const hasApproverUserId = await knex.schema.hasColumn(TableName.AccessApprovalPolicyApprover, "approverUserId");
const hasApproverId = await knex.schema.hasColumn(TableName.AccessApprovalPolicyApprover, "approverId");
if (hasApproverUserId) {
await knex.schema.alterTable(TableName.AccessApprovalPolicyApprover, (tb) => {
if (!hasApproverId) {
tb.uuid("approverId");
tb.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
}
});
if (!hasApproverId) {
await knex(TableName.AccessApprovalPolicyApprover).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
approverId: knex(TableName.ProjectMembership)
.select("id")
.where("userId", knex.raw("??", [`${TableName.AccessApprovalPolicyApprover}.approverUserId`]))
});
await knex.schema.alterTable(TableName.AccessApprovalPolicyApprover, (tb) => {
tb.dropColumn("approverUserId");
tb.uuid("approverId").notNullable().alter();
});
}
// ---------- ACCESS APPROVAL REQUEST ------------
const hasAccessApprovalRequestTable = await knex.schema.hasTable(TableName.AccessApprovalRequest);
const hasRequestedByUserId = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedByUserId");
const hasRequestedBy = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "requestedBy");
if (hasAccessApprovalRequestTable) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
if (!hasRequestedBy) {
tb.uuid("requestedBy");
tb.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
}
});
// Try to find a project membership based on the AccessApprovalRequest.requestedByUserId and AccessApprovalRequest.policyId(reference to AccessApprovalRequestPolicy).envId(reference to Environment).projectId(reference to Project)
// If a project membership is found, set the AccessApprovalRequest.requestedBy to the project membership id
// If a project membership is not found, remove the AccessApprovalRequest record
await knex(TableName.AccessApprovalRequest).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
requestedBy: knex(TableName.ProjectMembership)
.select("id")
.where("userId", knex.raw("??", [`${TableName.AccessApprovalRequest}.requestedByUserId`]))
});
// Then, delete records where no matching project membership was found
await knex(TableName.AccessApprovalRequest).whereNull("requestedBy").delete();
await knex.schema.alterTable(TableName.AccessApprovalRequest, (tb) => {
if (hasRequestedByUserId) {
tb.dropColumn("requestedByUserId");
}
if (hasRequestedBy) tb.uuid("requestedBy").notNullable().alter();
});
}
// ---------- ACCESS APPROVAL REQUEST REVIEWER ------------
const hasMemberId = await knex.schema.hasColumn(TableName.AccessApprovalRequestReviewer, "member");
const hasReviewerUserId = await knex.schema.hasColumn(TableName.AccessApprovalRequestReviewer, "reviewerUserId");
if (hasReviewerUserId) {
if (!hasMemberId) {
await knex.schema.alterTable(TableName.AccessApprovalRequestReviewer, (tb) => {
tb.uuid("member");
tb.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
});
}
await knex(TableName.AccessApprovalRequestReviewer).update({
// eslint-disable-next-line
// @ts-ignore because generate schema happens after this
member: knex(TableName.ProjectMembership)
.select("id")
.where("userId", knex.raw("??", [`${TableName.AccessApprovalRequestReviewer}.reviewerUserId`]))
});
await knex.schema.alterTable(TableName.AccessApprovalRequestReviewer, (tb) => {
tb.dropColumn("reviewerUserId");
tb.uuid("member").notNullable().alter();
});
}
}
}

@ -0,0 +1,39 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const doesNameExist = await knex.schema.hasColumn(TableName.SecretSharing, "name");
if (!doesNameExist) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.string("name").nullable();
});
}
const doesLastViewedAtExist = await knex.schema.hasColumn(TableName.SecretSharing, "lastViewedAt");
if (!doesLastViewedAtExist) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.timestamp("lastViewedAt").nullable();
});
}
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const doesNameExist = await knex.schema.hasColumn(TableName.SecretSharing, "name");
if (doesNameExist) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.dropColumn("name");
});
}
const doesLastViewedAtExist = await knex.schema.hasColumn(TableName.SecretSharing, "lastViewedAt");
if (doesLastViewedAtExist) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.dropColumn("lastViewedAt");
});
}
}
}

@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasKmsDataKeyCol = await knex.schema.hasColumn(TableName.Organization, "kmsEncryptedDataKey");
await knex.schema.alterTable(TableName.Organization, (tb) => {
if (!hasKmsDataKeyCol) {
tb.binary("kmsEncryptedDataKey");
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasKmsDataKeyCol = await knex.schema.hasColumn(TableName.Organization, "kmsEncryptedDataKey");
await knex.schema.alterTable(TableName.Organization, (t) => {
if (hasKmsDataKeyCol) {
t.dropColumn("kmsEncryptedDataKey");
}
});
}

@ -0,0 +1,29 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasKmsSecretManagerEncryptedDataKey = await knex.schema.hasColumn(
TableName.Project,
"kmsSecretManagerEncryptedDataKey"
);
await knex.schema.alterTable(TableName.Project, (tb) => {
if (!hasKmsSecretManagerEncryptedDataKey) {
tb.binary("kmsSecretManagerEncryptedDataKey");
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasKmsSecretManagerEncryptedDataKey = await knex.schema.hasColumn(
TableName.Project,
"kmsSecretManagerEncryptedDataKey"
);
await knex.schema.alterTable(TableName.Project, (t) => {
if (hasKmsSecretManagerEncryptedDataKey) {
t.dropColumn("kmsSecretManagerEncryptedDataKey");
}
});
}

@ -0,0 +1,181 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Knex } from "knex";
import { SecretType, TableName } from "../schemas";
import { createJunctionTable, createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const doesSecretV2TableExist = await knex.schema.hasTable(TableName.SecretV2);
if (!doesSecretV2TableExist) {
await knex.schema.createTable(TableName.SecretV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1).notNullable();
t.string("type").notNullable().defaultTo(SecretType.Shared);
t.string("key", 500).notNullable();
t.binary("encryptedValue");
t.binary("encryptedComment");
t.string("reminderNote");
t.integer("reminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.jsonb("metadata");
t.uuid("userId");
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("folderId").notNullable();
t.foreign("folderId").references("id").inTable(TableName.SecretFolder).onDelete("CASCADE");
t.timestamps(true, true, true);
t.index(["folderId", "userId"]);
});
}
await createOnUpdateTrigger(knex, TableName.SecretV2);
// many to many relation between tags
await createJunctionTable(knex, TableName.SecretV2JnTag, TableName.SecretV2, TableName.SecretTag);
const doesSecretV2VersionTableExist = await knex.schema.hasTable(TableName.SecretVersionV2);
if (!doesSecretV2VersionTableExist) {
await knex.schema.createTable(TableName.SecretVersionV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1).notNullable();
t.string("type").notNullable().defaultTo(SecretType.Shared);
t.string("key", 500).notNullable();
t.binary("encryptedValue");
t.binary("encryptedComment");
t.string("reminderNote");
t.integer("reminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.jsonb("metadata");
// to avoid orphan rows
t.uuid("envId");
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.uuid("secretId").notNullable();
t.uuid("folderId").notNullable();
t.uuid("userId");
t.foreign("userId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.SecretVersionV2);
if (!(await knex.schema.hasTable(TableName.SecretReferenceV2))) {
await knex.schema.createTable(TableName.SecretReferenceV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("environment").notNullable();
t.string("secretPath").notNullable();
t.string("secretKey", 500).notNullable();
t.uuid("secretId").notNullable();
t.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
});
}
await createJunctionTable(knex, TableName.SecretVersionV2Tag, TableName.SecretVersionV2, TableName.SecretTag);
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequestSecretV2))) {
await knex.schema.createTable(TableName.SecretApprovalRequestSecretV2, (t) => {
// everything related to secret
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("version").defaultTo(1);
t.string("key", 500).notNullable();
t.binary("encryptedValue");
t.binary("encryptedComment");
t.string("reminderNote");
t.integer("reminderRepeatDays");
t.boolean("skipMultilineEncoding").defaultTo(false);
t.jsonb("metadata");
t.timestamps(true, true, true);
// commit details
t.uuid("requestId").notNullable();
t.foreign("requestId").references("id").inTable(TableName.SecretApprovalRequest).onDelete("CASCADE");
t.string("op").notNullable();
t.uuid("secretId");
t.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("SET NULL");
t.uuid("secretVersion");
t.foreign("secretVersion").references("id").inTable(TableName.SecretVersionV2).onDelete("SET NULL");
});
}
if (!(await knex.schema.hasTable(TableName.SecretApprovalRequestSecretTagV2))) {
await knex.schema.createTable(TableName.SecretApprovalRequestSecretTagV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("secretId").notNullable();
t.foreign("secretId").references("id").inTable(TableName.SecretApprovalRequestSecretV2).onDelete("CASCADE");
t.uuid("tagId").notNullable();
t.foreign("tagId").references("id").inTable(TableName.SecretTag).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
if (!(await knex.schema.hasTable(TableName.SnapshotSecretV2))) {
await knex.schema.createTable(TableName.SnapshotSecretV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("envId").index().notNullable();
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
// not a relation kept like that to keep it when rolled back
t.uuid("secretVersionId").index().notNullable();
t.foreign("secretVersionId").references("id").inTable(TableName.SecretVersionV2).onDelete("CASCADE");
t.uuid("snapshotId").index().notNullable();
t.foreign("snapshotId").references("id").inTable(TableName.Snapshot).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
const hasEncryptedAccess = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccess");
const hasEncryptedAccessId = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccessId");
const hasEncryptedRefresh = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedRefresh");
const hasEncryptedAwsIamAssumRole = await knex.schema.hasColumn(
TableName.IntegrationAuth,
"encryptedAwsAssumeIamRoleArn"
);
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
if (!hasEncryptedAccess) t.binary("encryptedAccess");
if (!hasEncryptedAccessId) t.binary("encryptedAccessId");
if (!hasEncryptedRefresh) t.binary("encryptedRefresh");
if (!hasEncryptedAwsIamAssumRole) t.binary("encryptedAwsAssumeIamRoleArn");
});
}
if (!(await knex.schema.hasTable(TableName.SecretRotationOutputV2))) {
await knex.schema.createTable(TableName.SecretRotationOutputV2, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("key").notNullable();
t.uuid("secretId").notNullable();
t.foreign("secretId").references("id").inTable(TableName.SecretV2).onDelete("CASCADE");
t.uuid("rotationId").notNullable();
t.foreign("rotationId").references("id").inTable(TableName.SecretRotation).onDelete("CASCADE");
});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SnapshotSecretV2);
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequestSecretTagV2);
await knex.schema.dropTableIfExists(TableName.SecretApprovalRequestSecretV2);
await knex.schema.dropTableIfExists(TableName.SecretV2JnTag);
await knex.schema.dropTableIfExists(TableName.SecretReferenceV2);
await knex.schema.dropTableIfExists(TableName.SecretRotationOutputV2);
await dropOnUpdateTrigger(knex, TableName.SecretVersionV2);
await knex.schema.dropTableIfExists(TableName.SecretVersionV2Tag);
await knex.schema.dropTableIfExists(TableName.SecretVersionV2);
await dropOnUpdateTrigger(knex, TableName.SecretV2);
await knex.schema.dropTableIfExists(TableName.SecretV2);
if (await knex.schema.hasTable(TableName.IntegrationAuth)) {
const hasEncryptedAccess = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccess");
const hasEncryptedAccessId = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedAccessId");
const hasEncryptedRefresh = await knex.schema.hasColumn(TableName.IntegrationAuth, "encryptedRefresh");
const hasEncryptedAwsIamAssumRole = await knex.schema.hasColumn(
TableName.IntegrationAuth,
"encryptedAwsAssumeIamRoleArn"
);
await knex.schema.alterTable(TableName.IntegrationAuth, (t) => {
if (hasEncryptedAccess) t.dropColumn("encryptedAccess");
if (hasEncryptedAccessId) t.dropColumn("encryptedAccessId");
if (hasEncryptedRefresh) t.dropColumn("encryptedRefresh");
if (hasEncryptedAwsIamAssumRole) t.dropColumn("encryptedAwsAssumeIamRoleArn");
});
}
}

@ -0,0 +1,117 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
const hasActiveCaCertIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId");
if (!hasActiveCaCertIdColumn) {
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.uuid("activeCaCertId").nullable();
t.foreign("activeCaCertId").references("id").inTable(TableName.CertificateAuthorityCert);
});
await knex.raw(`
UPDATE "${TableName.CertificateAuthority}" ca
SET "activeCaCertId" = cac.id
FROM "${TableName.CertificateAuthorityCert}" cac
WHERE ca.id = cac."caId"
`);
}
}
if (await knex.schema.hasTable(TableName.CertificateAuthorityCert)) {
const hasVersionColumn = await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "version");
if (!hasVersionColumn) {
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.integer("version").nullable();
t.dropUnique(["caId"]);
});
await knex(TableName.CertificateAuthorityCert).update({ version: 1 }).whereNull("version");
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.integer("version").notNullable().alter();
});
}
const hasCaSecretIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "caSecretId");
if (!hasCaSecretIdColumn) {
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.uuid("caSecretId").nullable();
t.foreign("caSecretId").references("id").inTable(TableName.CertificateAuthoritySecret).onDelete("CASCADE");
});
await knex.raw(`
UPDATE "${TableName.CertificateAuthorityCert}" cert
SET "caSecretId" = (
SELECT sec.id
FROM "${TableName.CertificateAuthoritySecret}" sec
WHERE sec."caId" = cert."caId"
)
`);
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.uuid("caSecretId").notNullable().alter();
});
}
}
if (await knex.schema.hasTable(TableName.CertificateAuthoritySecret)) {
await knex.schema.alterTable(TableName.CertificateAuthoritySecret, (t) => {
t.dropUnique(["caId"]);
});
}
if (await knex.schema.hasTable(TableName.Certificate)) {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.uuid("caCertId").nullable();
t.foreign("caCertId").references("id").inTable(TableName.CertificateAuthorityCert);
});
await knex.raw(`
UPDATE "${TableName.Certificate}" cert
SET "caCertId" = (
SELECT caCert.id
FROM "${TableName.CertificateAuthorityCert}" caCert
WHERE caCert."caId" = cert."caId"
)
`);
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.uuid("caCertId").notNullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.CertificateAuthority)) {
if (await knex.schema.hasColumn(TableName.CertificateAuthority, "activeCaCertId")) {
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.dropColumn("activeCaCertId");
});
}
}
if (await knex.schema.hasTable(TableName.CertificateAuthorityCert)) {
if (await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "version")) {
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.dropColumn("version");
});
}
if (await knex.schema.hasColumn(TableName.CertificateAuthorityCert, "caSecretId")) {
await knex.schema.alterTable(TableName.CertificateAuthorityCert, (t) => {
t.dropColumn("caSecretId");
});
}
}
if (await knex.schema.hasTable(TableName.Certificate)) {
if (await knex.schema.hasColumn(TableName.Certificate, "caCertId")) {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.dropColumn("caCertId");
});
}
}
}

@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCreationLimitCol = await knex.schema.hasColumn(TableName.RateLimit, "creationLimit");
await knex.schema.alterTable(TableName.RateLimit, (t) => {
if (hasCreationLimitCol) {
t.dropColumn("creationLimit");
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasCreationLimitCol = await knex.schema.hasColumn(TableName.RateLimit, "creationLimit");
await knex.schema.alterTable(TableName.RateLimit, (t) => {
if (!hasCreationLimitCol) {
t.integer("creationLimit").defaultTo(30).notNullable();
}
});
}

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

@ -0,0 +1,62 @@
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.PkiCollection))) {
await knex.schema.createTable(TableName.PkiCollection, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.string("name").notNullable();
t.string("description").notNullable();
});
}
await createOnUpdateTrigger(knex, TableName.PkiCollection);
if (!(await knex.schema.hasTable(TableName.PkiCollectionItem))) {
await knex.schema.createTable(TableName.PkiCollectionItem, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.uuid("pkiCollectionId").notNullable();
t.foreign("pkiCollectionId").references("id").inTable(TableName.PkiCollection).onDelete("CASCADE");
t.uuid("caId").nullable();
t.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
t.uuid("certId").nullable();
t.foreign("certId").references("id").inTable(TableName.Certificate).onDelete("CASCADE");
});
}
await createOnUpdateTrigger(knex, TableName.PkiCollectionItem);
if (!(await knex.schema.hasTable(TableName.PkiAlert))) {
await knex.schema.createTable(TableName.PkiAlert, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("pkiCollectionId").notNullable();
t.foreign("pkiCollectionId").references("id").inTable(TableName.PkiCollection).onDelete("CASCADE");
t.string("name").notNullable();
t.integer("alertBeforeDays").notNullable();
t.string("recipientEmails").notNullable();
t.unique(["name", "projectId"]);
});
}
await createOnUpdateTrigger(knex, TableName.PkiAlert);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.PkiAlert);
await dropOnUpdateTrigger(knex, TableName.PkiAlert);
await knex.schema.dropTableIfExists(TableName.PkiCollectionItem);
await dropOnUpdateTrigger(knex, TableName.PkiCollectionItem);
await knex.schema.dropTableIfExists(TableName.PkiCollection);
await dropOnUpdateTrigger(knex, TableName.PkiCollection);
}

@ -0,0 +1,55 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
const hasCertificateTemplateTable = await knex.schema.hasTable(TableName.CertificateTemplate);
if (!hasCertificateTemplateTable) {
await knex.schema.createTable(TableName.CertificateTemplate, (tb) => {
tb.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
tb.uuid("caId").notNullable();
tb.foreign("caId").references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
tb.uuid("pkiCollectionId");
tb.foreign("pkiCollectionId").references("id").inTable(TableName.PkiCollection).onDelete("SET NULL");
tb.string("name").notNullable();
tb.string("commonName").notNullable();
tb.string("subjectAlternativeName").notNullable();
tb.string("ttl").notNullable();
tb.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.CertificateTemplate);
}
const doesCertificateTableHaveTemplateId = await knex.schema.hasColumn(
TableName.Certificate,
"certificateTemplateId"
);
if (!doesCertificateTableHaveTemplateId) {
await knex.schema.alterTable(TableName.Certificate, (tb) => {
tb.uuid("certificateTemplateId");
tb.foreign("certificateTemplateId").references("id").inTable(TableName.CertificateTemplate).onDelete("SET NULL");
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesCertificateTableHaveTemplateId = await knex.schema.hasColumn(
TableName.Certificate,
"certificateTemplateId"
);
if (doesCertificateTableHaveTemplateId) {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.dropColumn("certificateTemplateId");
});
}
const hasCertificateTemplateTable = await knex.schema.hasTable(TableName.CertificateTemplate);
if (hasCertificateTemplateTable) {
await knex.schema.dropTable(TableName.CertificateTemplate);
await dropOnUpdateTrigger(knex, TableName.CertificateTemplate);
}
}

@ -0,0 +1,36 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.CertificateAuthorityCrl)) {
const hasCaSecretIdColumn = await knex.schema.hasColumn(TableName.CertificateAuthorityCrl, "caSecretId");
if (!hasCaSecretIdColumn) {
await knex.schema.alterTable(TableName.CertificateAuthorityCrl, (t) => {
t.uuid("caSecretId").nullable();
t.foreign("caSecretId").references("id").inTable(TableName.CertificateAuthoritySecret).onDelete("CASCADE");
});
await knex.raw(`
UPDATE "${TableName.CertificateAuthorityCrl}" crl
SET "caSecretId" = (
SELECT sec.id
FROM "${TableName.CertificateAuthoritySecret}" sec
WHERE sec."caId" = crl."caId"
)
`);
await knex.schema.alterTable(TableName.CertificateAuthorityCrl, (t) => {
t.uuid("caSecretId").notNullable().alter();
});
}
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.CertificateAuthorityCrl)) {
await knex.schema.alterTable(TableName.CertificateAuthorityCrl, (t) => {
t.dropColumn("caSecretId");
});
}
}

@ -0,0 +1,105 @@
import slugify from "@sindresorhus/slugify";
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
import { randomSecureBytes } from "@app/lib/crypto";
import { symmetricCipherService, SymmetricEncryption } from "@app/lib/crypto/cipher";
import { alphaNumericNanoId } from "@app/lib/nanoid";
const getInstanceRootKey = async (knex: Knex) => {
const encryptionKey = process.env.ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
// if root key its base64 encoded
const isBase64 = !process.env.ENCRYPTION_KEY;
if (!encryptionKey) throw new Error("ENCRYPTION_KEY variable needed for migration");
const encryptionKeyBuffer = Buffer.from(encryptionKey, isBase64 ? "base64" : "utf8");
const KMS_ROOT_CONFIG_UUID = "00000000-0000-0000-0000-000000000000";
const kmsRootConfig = await knex(TableName.KmsServerRootConfig).where({ id: KMS_ROOT_CONFIG_UUID }).first();
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
if (kmsRootConfig) {
const decryptedRootKey = cipher.decrypt(kmsRootConfig.encryptedRootKey, encryptionKeyBuffer);
// set the flag so that other instancen nodes can start
return decryptedRootKey;
}
const newRootKey = randomSecureBytes(32);
const encryptedRootKey = cipher.encrypt(newRootKey, encryptionKeyBuffer);
await knex(TableName.KmsServerRootConfig).insert({
encryptedRootKey,
// eslint-disable-next-line
// @ts-ignore id is kept as fixed for idempotence and to avoid race condition
id: KMS_ROOT_CONFIG_UUID
});
return encryptedRootKey;
};
export const getSecretManagerDataKey = async (knex: Knex, projectId: string) => {
const KMS_VERSION = "v01";
const KMS_VERSION_BLOB_LENGTH = 3;
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const project = await knex(TableName.Project).where({ id: projectId }).first();
if (!project) throw new Error("Missing project id");
const ROOT_ENCRYPTION_KEY = await getInstanceRootKey(knex);
let secretManagerKmsKey;
const projectSecretManagerKmsId = project?.kmsSecretManagerKeyId;
if (projectSecretManagerKmsId) {
const kmsDoc = await knex(TableName.KmsKey)
.leftJoin(TableName.InternalKms, `${TableName.KmsKey}.id`, `${TableName.InternalKms}.kmsKeyId`)
.where({ [`${TableName.KmsKey}.id` as "id"]: projectSecretManagerKmsId })
.first();
if (!kmsDoc) throw new Error("missing kms");
secretManagerKmsKey = cipher.decrypt(kmsDoc.encryptedKey, ROOT_ENCRYPTION_KEY);
} else {
const [kmsDoc] = await knex(TableName.KmsKey)
.insert({
slug: slugify(alphaNumericNanoId(8).toLowerCase()),
orgId: project.orgId,
isReserved: false
})
.returning("*");
secretManagerKmsKey = randomSecureBytes(32);
const encryptedKeyMaterial = cipher.encrypt(secretManagerKmsKey, ROOT_ENCRYPTION_KEY);
await knex(TableName.InternalKms).insert({
version: 1,
encryptedKey: encryptedKeyMaterial,
encryptionAlgorithm: SymmetricEncryption.AES_GCM_256,
kmsKeyId: kmsDoc.id
});
}
const encryptedSecretManagerDataKey = project?.kmsSecretManagerEncryptedDataKey;
let dataKey: Buffer;
if (!encryptedSecretManagerDataKey) {
dataKey = randomSecureBytes();
// the below versioning we do it automatically in kms service
const unversionedDataKey = cipher.encrypt(dataKey, secretManagerKmsKey);
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3
await knex(TableName.Project)
.where({ id: projectId })
.update({
kmsSecretManagerEncryptedDataKey: Buffer.concat([unversionedDataKey, versionBlob])
});
} else {
const cipherTextBlob = encryptedSecretManagerDataKey.subarray(0, -KMS_VERSION_BLOB_LENGTH);
dataKey = cipher.decrypt(cipherTextBlob, secretManagerKmsKey);
}
return {
encryptor: ({ plainText }: { plainText: Buffer }) => {
const encryptedPlainTextBlob = cipher.encrypt(plainText, dataKey);
// Buffer#1 encrypted text + Buffer#2 version number
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3
const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]);
return { cipherTextBlob };
},
decryptor: ({ cipherTextBlob: versionedCipherTextBlob }: { cipherTextBlob: Buffer }) => {
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH);
const decryptedBlob = cipher.decrypt(cipherTextBlob, dataKey);
return decryptedBlob;
}
};
};

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

@ -14,7 +14,8 @@ export const AccessApprovalPoliciesSchema = z.object({
secretPath: z.string().nullable().optional(),
envId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
enforcementLevel: z.string().default("hard")
});
export type TAccessApprovalPolicies = z.infer<typeof AccessApprovalPoliciesSchema>;

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

@ -11,12 +11,13 @@ export const AccessApprovalRequestsSchema = z.object({
id: z.string().uuid(),
policyId: z.string().uuid(),
privilegeId: z.string().uuid().nullable().optional(),
requestedBy: z.string().uuid(),
requestedBy: z.string().uuid().nullable().optional(),
isTemporary: z.boolean(),
temporaryRange: z.string().nullable().optional(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
requestedByUserId: z.string().uuid()
});
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;

@ -27,7 +27,8 @@ export const CertificateAuthoritiesSchema = z.object({
maxPathLength: z.number().nullable().optional(),
keyAlgorithm: z.string(),
notBefore: z.date().nullable().optional(),
notAfter: z.date().nullable().optional()
notAfter: z.date().nullable().optional(),
activeCaCertId: z.string().uuid().nullable().optional()
});
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;

@ -15,7 +15,9 @@ export const CertificateAuthorityCertsSchema = z.object({
updatedAt: z.date(),
caId: z.string().uuid(),
encryptedCertificate: zodBuffer,
encryptedCertificateChain: zodBuffer
encryptedCertificateChain: zodBuffer,
version: z.number(),
caSecretId: z.string().uuid()
});
export type TCertificateAuthorityCerts = z.infer<typeof CertificateAuthorityCertsSchema>;

@ -14,7 +14,8 @@ export const CertificateAuthorityCrlSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
caId: z.string().uuid(),
encryptedCrl: zodBuffer
encryptedCrl: zodBuffer,
caSecretId: z.string().uuid()
});
export type TCertificateAuthorityCrl = z.infer<typeof CertificateAuthorityCrlSchema>;

@ -0,0 +1,24 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const CertificateTemplatesSchema = z.object({
id: z.string().uuid(),
caId: z.string().uuid(),
pkiCollectionId: z.string().uuid().nullable().optional(),
name: z.string(),
commonName: z.string(),
subjectAlternativeName: z.string(),
ttl: z.string(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TCertificateTemplates = z.infer<typeof CertificateTemplatesSchema>;
export type TCertificateTemplatesInsert = Omit<z.input<typeof CertificateTemplatesSchema>, TImmutableDBKeys>;
export type TCertificateTemplatesUpdate = Partial<Omit<z.input<typeof CertificateTemplatesSchema>, TImmutableDBKeys>>;

@ -20,7 +20,9 @@ export const CertificatesSchema = z.object({
notAfter: z.date(),
revokedAt: z.date().nullable().optional(),
revocationReason: z.number().nullable().optional(),
altNames: z.string().default("").nullable().optional()
altNames: z.string().default("").nullable().optional(),
caCertId: z.string().uuid(),
certificateTemplateId: z.string().uuid().nullable().optional()
});
export type TCertificates = z.infer<typeof CertificatesSchema>;

@ -14,6 +14,7 @@ export * from "./certificate-authority-crl";
export * from "./certificate-authority-secret";
export * from "./certificate-bodies";
export * from "./certificate-secrets";
export * from "./certificate-templates";
export * from "./certificates";
export * from "./dynamic-secret-leases";
export * from "./dynamic-secrets";
@ -52,6 +53,9 @@ export * from "./org-bots";
export * from "./org-memberships";
export * from "./org-roles";
export * from "./organizations";
export * from "./pki-alerts";
export * from "./pki-collection-items";
export * from "./pki-collections";
export * from "./project-bots";
export * from "./project-environments";
export * from "./project-keys";
@ -66,26 +70,35 @@ export * from "./scim-tokens";
export * from "./secret-approval-policies";
export * from "./secret-approval-policies-approvers";
export * from "./secret-approval-request-secret-tags";
export * from "./secret-approval-request-secret-tags-v2";
export * from "./secret-approval-requests";
export * from "./secret-approval-requests-reviewers";
export * from "./secret-approval-requests-secrets";
export * from "./secret-approval-requests-secrets-v2";
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-references-v2";
export * from "./secret-rotation-output-v2";
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-snapshot-secrets-v2";
export * from "./secret-snapshots";
export * from "./secret-tag-junction";
export * from "./secret-tags";
export * from "./secret-v2-tag-junction";
export * from "./secret-version-tag-junction";
export * from "./secret-version-v2-tag-junction";
export * from "./secret-versions";
export * from "./secret-versions-v2";
export * from "./secrets";
export * from "./secrets-v2";
export * from "./service-tokens";
export * from "./super-admin";
export * from "./trusted-ips";

@ -5,6 +5,8 @@
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const IntegrationAuthsSchema = z.object({
@ -32,7 +34,11 @@ export const IntegrationAuthsSchema = z.object({
updatedAt: z.date(),
awsAssumeIamRoleArnCipherText: z.string().nullable().optional(),
awsAssumeIamRoleArnIV: z.string().nullable().optional(),
awsAssumeIamRoleArnTag: z.string().nullable().optional()
awsAssumeIamRoleArnTag: z.string().nullable().optional(),
encryptedAccess: zodBuffer.nullable().optional(),
encryptedAccessId: zodBuffer.nullable().optional(),
encryptedRefresh: zodBuffer.nullable().optional(),
encryptedAwsAssumeIamRoleArn: zodBuffer.nullable().optional()
});
export type TIntegrationAuths = z.infer<typeof IntegrationAuthsSchema>;

@ -13,9 +13,9 @@ export const KmsKeysSchema = z.object({
isDisabled: z.boolean().default(false).nullable().optional(),
isReserved: z.boolean().default(true).nullable().optional(),
orgId: z.string().uuid(),
slug: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
slug: z.string()
updatedAt: z.date()
});
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;

@ -9,6 +9,10 @@ export enum TableName {
Certificate = "certificates",
CertificateBody = "certificate_bodies",
CertificateSecret = "certificate_secrets",
CertificateTemplate = "certificate_templates",
PkiAlert = "pki_alerts",
PkiCollection = "pki_collections",
PkiCollectionItem = "pki_collection_items",
Groups = "groups",
GroupProjectMembership = "group_project_memberships",
GroupProjectMembershipRole = "group_project_membership_roles",
@ -90,9 +94,18 @@ export enum TableName {
TrustedIps = "trusted_ips",
DynamicSecret = "dynamic_secrets",
DynamicSecretLease = "dynamic_secret_leases",
SecretV2 = "secrets_v2",
SecretReferenceV2 = "secret_references_v2",
SecretVersionV2 = "secret_versions_v2",
SecretApprovalRequestSecretV2 = "secret_approval_requests_secrets_v2",
SecretApprovalRequestSecretTagV2 = "secret_approval_request_secret_tags_v2",
SnapshotSecretV2 = "secret_snapshot_secrets_v2",
// junction tables with tags
SecretV2JnTag = "secret_v2_tag_junction",
JnSecretTag = "secret_tag_junction",
SecretVersionTag = "secret_version_tag_junction",
SecretVersionV2Tag = "secret_version_v2_tag_junction",
SecretRotationOutputV2 = "secret_rotation_output_v2",
// KMS Service
KmsServerRootConfig = "kms_root_config",
KmsKey = "kms_keys",
@ -157,7 +170,8 @@ export enum SecretType {
export enum ProjectVersion {
V1 = 1,
V2 = 2
V2 = 2,
V3 = 3
}
export enum ProjectUpgradeStatus {

@ -18,7 +18,7 @@ export const OrgMembershipsSchema = z.object({
orgId: z.string().uuid(),
roleId: z.string().uuid().nullable().optional(),
projectFavorites: z.string().array().nullable().optional(),
isActive: z.boolean()
isActive: z.boolean().default(true)
});
export type TOrgMemberships = z.infer<typeof OrgMembershipsSchema>;

@ -5,6 +5,8 @@
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const OrganizationsSchema = z.object({
@ -16,7 +18,8 @@ export const OrganizationsSchema = z.object({
updatedAt: z.date(),
authEnforced: z.boolean().default(false).nullable().optional(),
scimEnabled: z.boolean().default(false).nullable().optional(),
kmsDefaultKeyId: z.string().uuid().nullable().optional()
kmsDefaultKeyId: z.string().uuid().nullable().optional(),
kmsEncryptedDataKey: zodBuffer.nullable().optional()
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;

@ -0,0 +1,23 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const PkiAlertsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string(),
pkiCollectionId: z.string().uuid(),
name: z.string(),
alertBeforeDays: z.number(),
recipientEmails: z.string()
});
export type TPkiAlerts = z.infer<typeof PkiAlertsSchema>;
export type TPkiAlertsInsert = Omit<z.input<typeof PkiAlertsSchema>, TImmutableDBKeys>;
export type TPkiAlertsUpdate = Partial<Omit<z.input<typeof PkiAlertsSchema>, TImmutableDBKeys>>;

@ -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 PkiCollectionItemsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
pkiCollectionId: z.string().uuid(),
caId: z.string().uuid().nullable().optional(),
certId: z.string().uuid().nullable().optional()
});
export type TPkiCollectionItems = z.infer<typeof PkiCollectionItemsSchema>;
export type TPkiCollectionItemsInsert = Omit<z.input<typeof PkiCollectionItemsSchema>, TImmutableDBKeys>;
export type TPkiCollectionItemsUpdate = Partial<Omit<z.input<typeof PkiCollectionItemsSchema>, TImmutableDBKeys>>;

@ -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 PkiCollectionsSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
projectId: z.string(),
name: z.string(),
description: z.string()
});
export type TPkiCollections = z.infer<typeof PkiCollectionsSchema>;
export type TPkiCollectionsInsert = Omit<z.input<typeof PkiCollectionsSchema>, TImmutableDBKeys>;
export type TPkiCollectionsUpdate = Partial<Omit<z.input<typeof PkiCollectionsSchema>, TImmutableDBKeys>>;

@ -10,7 +10,7 @@ import { TImmutableDBKeys } from "./models";
export const ProjectUserAdditionalPrivilegeSchema = z.object({
id: z.string().uuid(),
slug: z.string(),
projectMembershipId: z.string().uuid(),
projectMembershipId: z.string().uuid().nullable().optional(),
isTemporary: z.boolean().default(false),
temporaryMode: z.string().nullable().optional(),
temporaryRange: z.string().nullable().optional(),
@ -18,7 +18,9 @@ export const ProjectUserAdditionalPrivilegeSchema = z.object({
temporaryAccessEndTime: z.date().nullable().optional(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
userId: z.string().uuid(),
projectId: z.string()
});
export type TProjectUserAdditionalPrivilege = z.infer<typeof ProjectUserAdditionalPrivilegeSchema>;

@ -5,6 +5,8 @@
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const ProjectsSchema = z.object({
@ -20,7 +22,8 @@ export const ProjectsSchema = z.object({
pitVersionLimit: z.number().default(10),
kmsCertificateKeyId: z.string().uuid().nullable().optional(),
auditLogsRetentionDays: z.number().nullable().optional(),
kmsSecretManagerKeyId: z.string().uuid().nullable().optional()
kmsSecretManagerKeyId: z.string().uuid().nullable().optional(),
kmsSecretManagerEncryptedDataKey: zodBuffer.nullable().optional()
});
export type TProjects = z.infer<typeof ProjectsSchema>;

@ -15,7 +15,6 @@ export const RateLimitSchema = z.object({
authRateLimit: z.number().default(60),
inviteUserRateLimit: z.number().default(30),
mfaRateLimit: z.number().default(20),
creationLimit: z.number().default(30),
publicEndpointLimit: z.number().default(30),
createdAt: z.date(),
updatedAt: z.date()

@ -14,7 +14,8 @@ export const SecretApprovalPoliciesSchema = z.object({
approvals: z.number().default(1),
envId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
enforcementLevel: z.string().default("hard")
});
export type TSecretApprovalPolicies = z.infer<typeof SecretApprovalPoliciesSchema>;

@ -0,0 +1,25 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretApprovalRequestSecretTagsV2Schema = z.object({
id: z.string().uuid(),
secretId: z.string().uuid(),
tagId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretApprovalRequestSecretTagsV2 = z.infer<typeof SecretApprovalRequestSecretTagsV2Schema>;
export type TSecretApprovalRequestSecretTagsV2Insert = Omit<
z.input<typeof SecretApprovalRequestSecretTagsV2Schema>,
TImmutableDBKeys
>;
export type TSecretApprovalRequestSecretTagsV2Update = Partial<
Omit<z.input<typeof SecretApprovalRequestSecretTagsV2Schema>, TImmutableDBKeys>
>;

@ -0,0 +1,37 @@
// 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 SecretApprovalRequestsSecretsV2Schema = z.object({
id: z.string().uuid(),
version: z.number().default(1).nullable().optional(),
key: z.string(),
encryptedValue: zodBuffer.nullable().optional(),
encryptedComment: zodBuffer.nullable().optional(),
reminderNote: z.string().nullable().optional(),
reminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
metadata: z.unknown().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date(),
requestId: z.string().uuid(),
op: z.string(),
secretId: z.string().uuid().nullable().optional(),
secretVersion: z.string().uuid().nullable().optional()
});
export type TSecretApprovalRequestsSecretsV2 = z.infer<typeof SecretApprovalRequestsSecretsV2Schema>;
export type TSecretApprovalRequestsSecretsV2Insert = Omit<
z.input<typeof SecretApprovalRequestsSecretsV2Schema>,
TImmutableDBKeys
>;
export type TSecretApprovalRequestsSecretsV2Update = Partial<
Omit<z.input<typeof SecretApprovalRequestsSecretsV2Schema>, TImmutableDBKeys>
>;

@ -19,7 +19,8 @@ export const SecretApprovalRequestsSchema = z.object({
updatedAt: z.date(),
isReplicated: z.boolean().nullable().optional(),
committerUserId: z.string().uuid(),
statusChangedByUserId: z.string().uuid().nullable().optional()
statusChangedByUserId: z.string().uuid().nullable().optional(),
bypassReason: z.string().nullable().optional()
});
export type TSecretApprovalRequests = z.infer<typeof SecretApprovalRequestsSchema>;

@ -0,0 +1,20 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretReferencesV2Schema = z.object({
id: z.string().uuid(),
environment: z.string(),
secretPath: z.string(),
secretKey: z.string(),
secretId: z.string().uuid()
});
export type TSecretReferencesV2 = z.infer<typeof SecretReferencesV2Schema>;
export type TSecretReferencesV2Insert = Omit<z.input<typeof SecretReferencesV2Schema>, TImmutableDBKeys>;
export type TSecretReferencesV2Update = Partial<Omit<z.input<typeof SecretReferencesV2Schema>, TImmutableDBKeys>>;

@ -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 SecretRotationOutputV2Schema = z.object({
id: z.string().uuid(),
key: z.string(),
secretId: z.string().uuid(),
rotationId: z.string().uuid()
});
export type TSecretRotationOutputV2 = z.infer<typeof SecretRotationOutputV2Schema>;
export type TSecretRotationOutputV2Insert = Omit<z.input<typeof SecretRotationOutputV2Schema>, TImmutableDBKeys>;
export type TSecretRotationOutputV2Update = Partial<
Omit<z.input<typeof SecretRotationOutputV2Schema>, TImmutableDBKeys>
>;

@ -18,7 +18,10 @@ export const SecretSharingSchema = z.object({
orgId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date(),
expiresAfterViews: z.number().nullable().optional()
expiresAfterViews: z.number().nullable().optional(),
accessType: z.string().default("anyone"),
name: z.string().nullable().optional(),
lastViewedAt: z.date().nullable().optional()
});
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;

@ -0,0 +1,23 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretSnapshotSecretsV2Schema = z.object({
id: z.string().uuid(),
envId: z.string().uuid(),
secretVersionId: z.string().uuid(),
snapshotId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretSnapshotSecretsV2 = z.infer<typeof SecretSnapshotSecretsV2Schema>;
export type TSecretSnapshotSecretsV2Insert = Omit<z.input<typeof SecretSnapshotSecretsV2Schema>, TImmutableDBKeys>;
export type TSecretSnapshotSecretsV2Update = Partial<
Omit<z.input<typeof SecretSnapshotSecretsV2Schema>, TImmutableDBKeys>
>;

@ -9,7 +9,6 @@ import { TImmutableDBKeys } from "./models";
export const SecretTagsSchema = z.object({
id: z.string().uuid(),
name: z.string(),
slug: z.string(),
color: z.string().nullable().optional(),
createdAt: z.date(),

@ -0,0 +1,18 @@
// 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 SecretV2TagJunctionSchema = z.object({
id: z.string().uuid(),
secrets_v2Id: z.string().uuid(),
secret_tagsId: z.string().uuid()
});
export type TSecretV2TagJunction = z.infer<typeof SecretV2TagJunctionSchema>;
export type TSecretV2TagJunctionInsert = Omit<z.input<typeof SecretV2TagJunctionSchema>, TImmutableDBKeys>;
export type TSecretV2TagJunctionUpdate = Partial<Omit<z.input<typeof SecretV2TagJunctionSchema>, TImmutableDBKeys>>;

@ -0,0 +1,23 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretVersionV2TagJunctionSchema = z.object({
id: z.string().uuid(),
secret_versions_v2Id: z.string().uuid(),
secret_tagsId: z.string().uuid()
});
export type TSecretVersionV2TagJunction = z.infer<typeof SecretVersionV2TagJunctionSchema>;
export type TSecretVersionV2TagJunctionInsert = Omit<
z.input<typeof SecretVersionV2TagJunctionSchema>,
TImmutableDBKeys
>;
export type TSecretVersionV2TagJunctionUpdate = Partial<
Omit<z.input<typeof SecretVersionV2TagJunctionSchema>, TImmutableDBKeys>
>;

@ -0,0 +1,33 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const SecretVersionsV2Schema = z.object({
id: z.string().uuid(),
version: z.number().default(1),
type: z.string().default("shared"),
key: z.string(),
encryptedValue: zodBuffer.nullable().optional(),
encryptedComment: zodBuffer.nullable().optional(),
reminderNote: z.string().nullable().optional(),
reminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
metadata: z.unknown().nullable().optional(),
envId: z.string().uuid().nullable().optional(),
secretId: z.string().uuid(),
folderId: z.string().uuid(),
userId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretVersionsV2 = z.infer<typeof SecretVersionsV2Schema>;
export type TSecretVersionsV2Insert = Omit<z.input<typeof SecretVersionsV2Schema>, TImmutableDBKeys>;
export type TSecretVersionsV2Update = Partial<Omit<z.input<typeof SecretVersionsV2Schema>, TImmutableDBKeys>>;

@ -0,0 +1,31 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const SecretsV2Schema = z.object({
id: z.string().uuid(),
version: z.number().default(1),
type: z.string().default("shared"),
key: z.string(),
encryptedValue: zodBuffer.nullable().optional(),
encryptedComment: zodBuffer.nullable().optional(),
reminderNote: z.string().nullable().optional(),
reminderRepeatDays: z.number().nullable().optional(),
skipMultilineEncoding: z.boolean().default(false).nullable().optional(),
metadata: z.unknown().nullable().optional(),
userId: z.string().uuid().nullable().optional(),
folderId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretsV2 = z.infer<typeof SecretsV2Schema>;
export type TSecretsV2Insert = Omit<z.input<typeof SecretsV2Schema>, TImmutableDBKeys>;
export type TSecretsV2Update = Partial<Omit<z.input<typeof SecretsV2Schema>, TImmutableDBKeys>>;

@ -33,6 +33,11 @@ export const seedData1 = {
name: "first project",
slug: "first-project"
},
projectV3: {
id: "77fa7aed-9288-401e-a4c9-3a9430be62a4",
name: "first project v2",
slug: "first-project-v2"
},
environment: {
name: "Development",
slug: "dev"

@ -0,0 +1,50 @@
import { Knex } from "knex";
import { ProjectMembershipRole, ProjectVersion, TableName } from "../schemas";
import { seedData1 } from "../seed-data";
export const DEFAULT_PROJECT_ENVS = [
{ name: "Development", slug: "dev" },
{ name: "Staging", slug: "staging" },
{ name: "Production", slug: "prod" }
];
export async function seed(knex: Knex): Promise<void> {
const [projectV2] = await knex(TableName.Project)
.insert({
name: seedData1.projectV3.name,
orgId: seedData1.organization.id,
slug: seedData1.projectV3.slug,
version: ProjectVersion.V3,
// eslint-disable-next-line
// @ts-ignore
id: seedData1.projectV3.id
})
.returning("*");
const projectMembershipV3 = await knex(TableName.ProjectMembership)
.insert({
projectId: projectV2.id,
userId: seedData1.id
})
.returning("*");
await knex(TableName.ProjectUserMembershipRole).insert({
role: ProjectMembershipRole.Admin,
projectMembershipId: projectMembershipV3[0].id
});
// create default environments and default folders
const projectV3Envs = await knex(TableName.Environment)
.insert(
DEFAULT_PROJECT_ENVS.map(({ name, slug }, index) => ({
name,
slug,
projectId: seedData1.projectV3.id,
position: index + 1
}))
)
.returning("*");
await knex(TableName.SecretFolder).insert(
projectV3Envs.map(({ id }) => ({ name: "root", envId: id, parentId: null }))
);
}

@ -86,4 +86,15 @@ export async function seed(knex: Knex): Promise<void> {
role: ProjectMembershipRole.Admin,
projectMembershipId: identityProjectMembership[0].id
});
const identityProjectMembershipV3 = await knex(TableName.IdentityProjectMembership)
.insert({
identityId: seedData1.machineIdentity.id,
projectId: seedData1.projectV3.id
})
.returning("*");
await knex(TableName.IdentityProjectMembershipRole).insert({
role: ProjectMembershipRole.Admin,
projectMembershipId: identityProjectMembershipV3[0].id
});
}

@ -1,6 +1,7 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { EnforcementLevel } from "@app/lib/types";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
@ -16,10 +17,11 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
name: z.string().optional(),
secretPath: z.string().trim().default("/"),
environment: z.string(),
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1)
approverUserIds: z.string().array().min(1),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
})
.refine((data) => data.approvals <= data.approvers.length, {
.refine((data) => data.approvals <= data.approverUserIds.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
}),
@ -38,7 +40,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
actorOrgId: req.permission.orgId,
...req.body,
projectSlug: req.body.projectSlug,
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`,
enforcementLevel: req.body.enforcementLevel
});
return { approval };
}
@ -53,7 +56,16 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
}),
response: {
200: z.object({
approvals: sapPubSchema.extend({ approvers: z.string().array(), secretPath: z.string().optional() }).array()
approvals: sapPubSchema
.extend({
userApprovers: z
.object({
userId: z.string()
})
.array(),
secretPath: z.string().optional().nullable()
})
.array()
})
}
},
@ -66,6 +78,7 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
actorOrgId: req.permission.orgId,
projectSlug: req.query.projectSlug
});
return { approvals };
}
});
@ -114,10 +127,11 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
.trim()
.optional()
.transform((val) => (val === "" ? "/" : val)),
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1)
approverUserIds: z.string().array().min(1),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
})
.refine((data) => data.approvals <= data.approvers.length, {
.refine((data) => data.approvals <= data.approverUserIds.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
}),

@ -1,10 +1,19 @@
import { z } from "zod";
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema } from "@app/db/schemas";
import { AccessApprovalRequestsReviewersSchema, AccessApprovalRequestsSchema, UsersSchema } from "@app/db/schemas";
import { ApprovalStatus } from "@app/ee/services/access-approval-request/access-approval-request-types";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const approvalRequestUser = z.object({ userId: z.string() }).merge(
UsersSchema.pick({
email: true,
firstName: true,
lastName: true,
username: true
})
);
export const registerAccessApprovalRequestRouter = async (server: FastifyZodProvider) => {
server.route({
url: "/",
@ -99,14 +108,16 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().nullish(),
envId: z.string()
envId: z.string(),
enforcementLevel: z.string()
}),
reviewers: z
.object({
member: z.string(),
userId: z.string(),
status: z.string()
})
.array()
.array(),
requestedByUser: approvalRequestUser
}).array()
})
}

@ -1,86 +1,31 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import { z } from "zod";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { CERTIFICATE_AUTHORITIES } from "@app/lib/api-docs";
import { CA_CRLS } from "@app/lib/api-docs";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerCaCrlRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/:caId/crl",
url: "/:crlId",
config: {
rateLimit: readLimit
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: {
description: "Get CRL of the CA",
description: "Get CRL in DER format",
params: z.object({
caId: z.string().trim().describe(CERTIFICATE_AUTHORITIES.GET_CRL.caId)
crlId: z.string().trim().describe(CA_CRLS.GET.crlId)
}),
response: {
200: z.object({
crl: z.string().describe(CERTIFICATE_AUTHORITIES.GET_CRL.crl)
})
200: z.instanceof(Buffer)
}
},
handler: async (req) => {
const { crl, ca } = await server.services.certificateAuthorityCrl.getCaCrl({
caId: req.params.caId,
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
handler: async (req, res) => {
const { crl } = await server.services.certificateAuthorityCrl.getCrlById(req.params.crlId);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: ca.projectId,
event: {
type: EventType.GET_CA_CRL,
metadata: {
caId: ca.id,
dn: ca.dn
}
}
});
res.header("Content-Type", "application/pkix-crl");
return {
crl
};
return Buffer.from(crl);
}
});
// server.route({
// method: "GET",
// url: "/:caId/crl/rotate",
// config: {
// rateLimit: writeLimit
// },
// onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
// schema: {
// description: "Rotate CRL of the CA",
// params: z.object({
// caId: z.string().trim()
// }),
// response: {
// 200: z.object({
// message: z.string()
// })
// }
// },
// handler: async (req) => {
// await server.services.certificateAuthority.rotateCaCrl({
// caId: req.params.caId,
// actor: req.permission.type,
// actorId: req.permission.id,
// actorAuthMethod: req.permission.authMethod,
// actorOrgId: req.permission.orgId
// });
// return {
// message: "Successfully rotated CA CRL"
// };
// }
// });
};

@ -131,7 +131,7 @@ export const registerDynamicSecretLeaseRouter = async (server: FastifyZodProvide
.default("/")
.transform(removeTrailingSlash)
.describe(DYNAMIC_SECRET_LEASES.RENEW.path),
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.ttl)
environmentSlug: z.string().min(1).describe(DYNAMIC_SECRET_LEASES.RENEW.environmentSlug)
}),
response: {
200: z.object({

@ -1,6 +1,7 @@
import { z } from "zod";
import { ExternalKmsSchema, KmsKeysSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import {
ExternalKmsAwsSchema,
ExternalKmsInputSchema,
@ -19,6 +20,23 @@ const sanitizedExternalSchema = KmsKeysSchema.extend({
})
});
const sanitizedExternalSchemaForGetAll = KmsKeysSchema.pick({
id: true,
description: true,
isDisabled: true,
createdAt: true,
updatedAt: true,
slug: true
})
.extend({
externalKms: ExternalKmsSchema.pick({
provider: true,
status: true,
statusDetails: true
})
})
.array();
const sanitizedExternalSchemaForGetById = KmsKeysSchema.extend({
external: ExternalKmsSchema.pick({
id: true,
@ -39,8 +57,8 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
},
schema: {
body: z.object({
slug: z.string().min(1).trim().toLowerCase().optional(),
description: z.string().min(1).trim().optional(),
slug: z.string().min(1).trim().toLowerCase(),
description: z.string().trim().optional(),
provider: ExternalKmsInputSchema
}),
response: {
@ -60,6 +78,21 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
provider: req.body.provider,
description: req.body.description
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.CREATE_KMS,
metadata: {
kmsId: externalKms.id,
provider: req.body.provider.type,
slug: req.body.slug,
description: req.body.description
}
}
});
return { externalKms };
}
});
@ -76,7 +109,7 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
}),
body: z.object({
slug: z.string().min(1).trim().toLowerCase().optional(),
description: z.string().min(1).trim().optional(),
description: z.string().trim().optional(),
provider: ExternalKmsInputUpdateSchema
}),
response: {
@ -97,6 +130,21 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
description: req.body.description,
id: req.params.id
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.UPDATE_KMS,
metadata: {
kmsId: externalKms.id,
provider: req.body.provider.type,
slug: req.body.slug,
description: req.body.description
}
}
});
return { externalKms };
}
});
@ -126,6 +174,19 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId,
id: req.params.id
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.DELETE_KMS,
metadata: {
kmsId: externalKms.id,
slug: externalKms.slug
}
}
});
return { externalKms };
}
});
@ -155,10 +216,48 @@ export const registerExternalKmsRouter = async (server: FastifyZodProvider) => {
actorOrgId: req.permission.orgId,
id: req.params.id
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
orgId: req.permission.orgId,
event: {
type: EventType.GET_KMS,
metadata: {
kmsId: externalKms.id,
slug: externalKms.slug
}
}
});
return { externalKms };
}
});
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
response: {
200: z.object({
externalKmsList: sanitizedExternalSchemaForGetAll
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const externalKmsList = await server.services.externalKms.list({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
});
return { externalKmsList };
}
});
server.route({
method: "GET",
url: "/slug/:slug",

@ -4,6 +4,7 @@ import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
import { registerCaCrlRouter } from "./certificate-authority-crl-router";
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerExternalKmsRouter } from "./external-kms-router";
import { registerGroupRouter } from "./group-router";
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
import { registerLdapRouter } from "./ldap-router";
@ -60,7 +61,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(
async (pkiRouter) => {
await pkiRouter.register(registerCaCrlRouter, { prefix: "/ca" });
await pkiRouter.register(registerCaCrlRouter, { prefix: "/crl" });
},
{ prefix: "/pki" }
);
@ -87,4 +88,8 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
},
{ prefix: "/additional-privilege" }
);
await server.register(registerExternalKmsRouter, {
prefix: "/external-kms"
});
};

@ -52,6 +52,36 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}
});
server.route({
method: "GET",
url: "/:organizationId/roles/:roleId",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim(),
roleId: z.string().trim()
}),
response: {
200: z.object({
role: OrgRolesSchema
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const role = await server.services.orgRole.getRole(
req.permission.id,
req.params.organizationId,
req.params.roleId,
req.permission.authMethod,
req.permission.orgId
);
return { role };
}
});
server.route({
method: "PATCH",
url: "/:organizationId/roles/:roleId",
@ -69,7 +99,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
.trim()
.optional()
.refine(
(val) => typeof val === "undefined" || Object.keys(OrgMembershipRole).includes(val),
(val) => typeof val !== "undefined" && !Object.keys(OrgMembershipRole).includes(val),
"Please choose a different slug, the slug you have entered is reserved."
)
.refine((val) => typeof val === "undefined" || slugify(val) === val, {
@ -77,7 +107,7 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}),
name: z.string().trim().optional(),
description: z.string().trim().optional(),
permissions: z.any().array()
permissions: z.any().array().optional()
}),
response: {
200: z.object({

@ -101,7 +101,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
message: "Slug must be a valid"
}),
name: z.string().trim().optional().describe(PROJECT_ROLE.UPDATE.name),
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions)
permissions: ProjectPermissionSchema.array().describe(PROJECT_ROLE.UPDATE.permissions).optional()
}),
response: {
200: z.object({
@ -120,7 +120,7 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
roleId: req.params.roleId,
data: {
...req.body,
permissions: JSON.stringify(packRules(req.body.permissions))
permissions: req.body.permissions ? JSON.stringify(packRules(req.body.permissions)) : undefined
}
});
return { role };

@ -4,9 +4,10 @@ import { AuditLogsSchema, SecretSnapshotsSchema } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { AUDIT_LOGS, PROJECTS } from "@app/lib/api-docs";
import { getLastMidnightDateISO, removeTrailingSlash } from "@app/lib/fn";
import { readLimit } from "@app/server/config/rateLimiter";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
import { KmsType } from "@app/services/kms/kms-types";
export const registerProjectRouter = async (server: FastifyZodProvider) => {
server.route({
@ -171,4 +172,212 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
onRequest: verifyAuth([AuthMode.JWT]),
handler: async () => ({ actors: [] })
});
server.route({
method: "GET",
url: "/:workspaceId/kms",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
}),
response: {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
isExternal: z.boolean()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const kmsKey = await server.services.project.getProjectKmsKeys({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId
});
return kmsKey;
}
});
server.route({
method: "PATCH",
url: "/:workspaceId/kms",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
}),
body: z.object({
kms: z.discriminatedUnion("type", [
z.object({ type: z.literal(KmsType.Internal) }),
z.object({ type: z.literal(KmsType.External), kmsId: z.string() })
])
}),
response: {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
isExternal: z.boolean()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { secretManagerKmsKey } = await server.services.project.updateProjectKmsKey({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId,
...req.body
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.params.workspaceId,
event: {
type: EventType.UPDATE_PROJECT_KMS,
metadata: {
secretManagerKmsKey: {
id: secretManagerKmsKey.id,
slug: secretManagerKmsKey.slug
}
}
}
});
return {
secretManagerKmsKey
};
}
});
server.route({
method: "GET",
url: "/:workspaceId/kms/backup",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
}),
response: {
200: z.object({
secretManager: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const backup = await server.services.project.getProjectKmsBackup({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.params.workspaceId,
event: {
type: EventType.GET_PROJECT_KMS_BACKUP,
metadata: {}
}
});
return backup;
}
});
server.route({
method: "POST",
url: "/:workspaceId/kms/backup",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
}),
body: z.object({
backup: z.string().min(1)
}),
response: {
200: z.object({
secretManagerKmsKey: z.object({
id: z.string(),
slug: z.string(),
isExternal: z.boolean()
})
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const backup = await server.services.project.loadProjectKmsBackup({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId,
backup: req.body.backup
});
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: req.params.workspaceId,
event: {
type: EventType.LOAD_PROJECT_KMS_BACKUP,
metadata: {}
}
});
return backup;
}
});
server.route({
method: "POST",
url: "/:workspaceId/migrate-v3",
config: {
rateLimit: writeLimit
},
schema: {
params: z.object({
workspaceId: z.string().trim()
}),
response: {
200: z.object({
message: z.string()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const migration = await server.services.secret.startSecretV2Migration({
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId
});
return migration;
}
});
};

@ -58,7 +58,6 @@ export const registerRateLimitRouter = async (server: FastifyZodProvider) => {
authRateLimit: z.number(),
inviteUserRateLimit: z.number(),
mfaRateLimit: z.number(),
creationLimit: z.number(),
publicEndpointLimit: z.number()
}),
response: {

@ -350,7 +350,12 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
schemas: z.array(z.string()),
id: z.string().trim(),
displayName: z.string().trim(),
members: z.array(z.any()).length(0),
members: z.array(
z.object({
value: z.string(),
display: z.string()
})
),
meta: z.object({
resourceType: z.string().trim()
})
@ -423,7 +428,7 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
displayName: z.string().trim(),
members: z.array(
z.object({
value: z.string(), // infisical orgMembershipId
value: z.string(),
display: z.string()
})
)

@ -2,6 +2,7 @@ import { nanoid } from "nanoid";
import { z } from "zod";
import { removeTrailingSlash } from "@app/lib/fn";
import { EnforcementLevel } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
@ -24,11 +25,13 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.string()
.optional()
.nullable()
.default("/")
.transform((val) => (val ? removeTrailingSlash(val) : val)),
approverUserIds: z.string().array().min(1),
approvals: z.number().min(1).default(1)
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard)
})
.refine((data) => data.approvals <= data.approverUserIds.length, {
.refine((data) => data.approvals <= data.approvers.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
}),
@ -47,7 +50,8 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId,
...req.body,
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`,
enforcementLevel: req.body.enforcementLevel
});
return { approval };
}
@ -66,15 +70,17 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
body: z
.object({
name: z.string().optional(),
approverUserIds: z.string().array().min(1),
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1),
secretPath: z
.string()
.optional()
.nullable()
.transform((val) => (val ? removeTrailingSlash(val) : val))
.transform((val) => (val === "" ? "/" : val)),
enforcementLevel: z.nativeEnum(EnforcementLevel).optional()
})
.refine((data) => data.approvals <= data.approverUserIds.length, {
.refine((data) => data.approvals <= data.approvers.length, {
path: ["approvals"],
message: "The number of approvals should be lower than the number of approvers."
}),

@ -3,16 +3,14 @@ import { z } from "zod";
import {
SecretApprovalRequestsReviewersSchema,
SecretApprovalRequestsSchema,
SecretApprovalRequestsSecretsSchema,
SecretsSchema,
SecretTagsSchema,
SecretVersionsSchema,
UsersSchema
} from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApprovalStatus, RequestState } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
const approvalRequestUser = z.object({ userId: z.string() }).merge(
@ -49,7 +47,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
secretPath: z.string().optional().nullable()
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string()
}),
committerUser: approvalRequestUser,
commits: z.object({ op: z.string(), secretId: z.string().nullable().optional() }).array(),
@ -116,6 +115,9 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
params: z.object({
id: z.string()
}),
body: z.object({
bypassReason: z.string().optional()
}),
response: {
200: z.object({
approval: SecretApprovalRequestsSchema
@ -129,7 +131,8 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
actor: req.permission.type,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId,
approvalId: req.params.id
approvalId: req.params.id,
bypassReason: req.body.bypassReason
});
return { approval };
}
@ -248,53 +251,40 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
name: z.string(),
approvals: z.number(),
approvers: approvalRequestUser.array(),
secretPath: z.string().optional().nullable()
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string()
}),
environment: z.string(),
statusChangedByUser: approvalRequestUser.optional(),
committerUser: approvalRequestUser,
reviewers: approvalRequestUser.extend({ status: z.string() }).array(),
secretPath: z.string(),
commits: SecretApprovalRequestsSecretsSchema.omit({ secretBlindIndex: true })
.merge(
z.object({
tags: tagSchema,
secret: SecretsSchema.pick({
id: true,
version: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
commits: secretRawSchema
.omit({ _id: true, environment: true, workspace: true, type: true, version: true })
.extend({
op: z.string(),
tags: tagSchema,
secret: z
.object({
id: z.string(),
version: z.number(),
secretKey: z.string(),
secretValue: z.string().optional(),
secretComment: z.string().optional()
})
.optional()
.nullable(),
secretVersion: SecretVersionsSchema.pick({
id: true,
version: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
.optional()
.nullable(),
secretVersion: z
.object({
id: z.string(),
version: z.number(),
secretKey: z.string(),
secretValue: z.string().optional(),
secretComment: z.string().optional(),
tags: tagSchema
})
.merge(
z.object({
tags: tagSchema
})
)
.optional()
})
)
.optional()
})
.array()
})
)

@ -1,6 +1,6 @@
import { z } from "zod";
import { SecretRotationOutputsSchema, SecretRotationsSchema, SecretsSchema } from "@app/db/schemas";
import { SecretRotationOutputsSchema, SecretRotationsSchema } from "@app/db/schemas";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -112,18 +112,10 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
outputs: z
.object({
key: z.string(),
secret: SecretsSchema.pick({
id: true,
version: true,
secretKeyIV: true,
secretKeyTag: true,
secretKeyCiphertext: true,
secretValueIV: true,
secretValueTag: true,
secretValueCiphertext: true,
secretCommentIV: true,
secretCommentTag: true,
secretCommentCiphertext: true
secret: z.object({
secretKey: z.string(),
id: z.string(),
version: z.number()
})
})
.array()

@ -1,8 +1,8 @@
import { z } from "zod";
import { SecretVersionsSchema } from "@app/db/schemas";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretVersionRouter = async (server: FastifyZodProvider) => {
@ -22,7 +22,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
}),
response: {
200: z.object({
secretVersions: SecretVersionsSchema.omit({ secretBlindIndex: true }).array()
secretVersions: secretRawSchema.array()
})
}
},

@ -1,9 +1,10 @@
import { z } from "zod";
import { SecretSnapshotsSchema, SecretTagsSchema, SecretVersionsSchema } from "@app/db/schemas";
import { SecretSnapshotsSchema, SecretTagsSchema } from "@app/db/schemas";
import { PROJECTS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { secretRawSchema } from "@app/server/routes/sanitizedSchemas";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
@ -27,17 +28,17 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
slug: z.string(),
name: z.string()
}),
secretVersions: SecretVersionsSchema.omit({ secretBlindIndex: true })
.merge(
z.object({
tags: SecretTagsSchema.pick({
id: true,
slug: true,
name: true,
color: true
}).array()
})
)
secretVersions: secretRawSchema
.omit({ _id: true, environment: true, workspace: true, type: true })
.extend({
secretId: z.string(),
tags: SecretTagsSchema.pick({
id: true,
slug: true,
name: true,
color: true
}).array()
})
.array(),
folderVersion: z.object({ id: z.string(), name: z.string() }).array(),
createdAt: z.date(),

@ -1,9 +1,9 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName, TAccessApprovalPolicies } from "@app/db/schemas";
import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { buildFindFilter, mergeOneToManyRelation, ormify, selectAllTableCols, TFindFilter } from "@app/lib/knex";
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
@ -15,12 +15,12 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
// eslint-disable-next-line
.where(buildFindFilter(filter))
.join(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.join(
.leftJoin(
TableName.AccessApprovalPolicyApprover,
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.select(tx.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(tx.ref("approverUserId").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"))
@ -35,18 +35,30 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
const doc = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), {
[`${TableName.AccessApprovalPolicy}.id` as "id"]: id
});
const formatedDoc = mergeOneToManyRelation(
doc,
"id",
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
const formattedDoc = sqlNestRelationships({
data: doc,
key: "id",
parentMapper: (data) => ({
environment: {
id: data.envId,
name: data.envName,
slug: data.envSlug
},
projectId: data.projectId,
...AccessApprovalPoliciesSchema.parse(data)
}),
({ approverId }) => approverId,
"approvers"
);
return formatedDoc?.[0];
childrenMapper: [
{
key: "approverUserId",
label: "userApprovers" as const,
mapper: ({ approverUserId }) => ({
userId: approverUserId
})
}
]
});
return formattedDoc?.[0];
} catch (error) {
throw new DatabaseError({ error, name: "FindById" });
}
@ -55,18 +67,32 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
const find = async (filter: TFindFilter<TAccessApprovalPolicies & { projectId: string }>, tx?: Knex) => {
try {
const docs = await accessApprovalPolicyFindQuery(tx || db.replicaNode(), filter);
const formatedDoc = mergeOneToManyRelation(
docs,
"id",
({ approverId, envId, envName: name, envSlug: slug, ...el }) => ({
...el,
envId,
environment: { id: envId, name, slug }
const formattedDocs = sqlNestRelationships({
data: docs,
key: "id",
parentMapper: (data) => ({
environment: {
id: data.envId,
name: data.envName,
slug: data.envSlug
},
projectId: data.projectId,
...AccessApprovalPoliciesSchema.parse(data)
// secretPath: data.secretPath || undefined,
}),
({ approverId }) => approverId,
"approvers"
);
return formatedDoc.map((policy) => ({ ...policy, secretPath: policy.secretPath || undefined }));
childrenMapper: [
{
key: "approverUserId",
label: "userApprovers" as const,
mapper: ({ approverUserId }) => ({
userId: approverUserId
})
}
]
});
return formattedDocs;
} catch (error) {
throw new DatabaseError({ error, name: "Find" });
}

@ -34,8 +34,7 @@ export const accessApprovalPolicyServiceFactory = ({
accessApprovalPolicyApproverDAL,
permissionService,
projectEnvDAL,
projectDAL,
projectMembershipDAL
projectDAL
}: TSecretApprovalPolicyServiceFactoryDep) => {
const createAccessApprovalPolicy = async ({
name,
@ -45,14 +44,15 @@ export const accessApprovalPolicyServiceFactory = ({
secretPath,
actorAuthMethod,
approvals,
approvers,
approverUserIds,
projectSlug,
environment
environment,
enforcementLevel
}: TCreateAccessApprovalPolicy) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new BadRequestError({ message: "Project not found" });
if (approvals > approvers.length)
if (approvals > approverUserIds.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
const { permission } = await permissionService.getProjectPermission(
@ -69,15 +69,6 @@ export const accessApprovalPolicyServiceFactory = ({
const env = await projectEnvDAL.findOne({ slug: environment, projectId: project.id });
if (!env) throw new BadRequestError({ message: "Environment not found" });
const secretApprovers = await projectMembershipDAL.find({
projectId: project.id,
$in: { id: approvers }
});
if (secretApprovers.length !== approvers.length) {
throw new BadRequestError({ message: "Approver not found in project" });
}
await verifyApprovers({
projectId: project.id,
orgId: actorOrgId,
@ -85,7 +76,7 @@ export const accessApprovalPolicyServiceFactory = ({
secretPath,
actorAuthMethod,
permissionService,
userIds: secretApprovers.map((approver) => approver.userId)
userIds: approverUserIds
});
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
@ -94,13 +85,14 @@ export const accessApprovalPolicyServiceFactory = ({
envId: env.id,
approvals,
secretPath,
name
name,
enforcementLevel
},
tx
);
await accessApprovalPolicyApproverDAL.insertMany(
secretApprovers.map(({ id }) => ({
approverId: id,
approverUserIds.map((userId) => ({
approverUserId: userId,
policyId: doc.id
})),
tx
@ -136,14 +128,15 @@ export const accessApprovalPolicyServiceFactory = ({
const updateAccessApprovalPolicy = async ({
policyId,
approvers,
approverUserIds,
secretPath,
name,
actorId,
actor,
actorOrgId,
actorAuthMethod,
approvals
approvals,
enforcementLevel
}: TUpdateAccessApprovalPolicy) => {
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
if (!accessApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
@ -163,20 +156,12 @@ export const accessApprovalPolicyServiceFactory = ({
{
approvals,
secretPath,
name
name,
enforcementLevel
},
tx
);
if (approvers) {
// Find the workspace project memberships of the users passed in the approvers array
const secretApprovers = await projectMembershipDAL.find(
{
projectId: accessApprovalPolicy.projectId,
$in: { id: approvers }
},
{ tx }
);
if (approverUserIds) {
await verifyApprovers({
projectId: accessApprovalPolicy.projectId,
orgId: actorOrgId,
@ -184,15 +169,13 @@ export const accessApprovalPolicyServiceFactory = ({
secretPath: doc.secretPath!,
actorAuthMethod,
permissionService,
userIds: secretApprovers.map((approver) => approver.userId)
userIds: approverUserIds
});
if (secretApprovers.length !== approvers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
await accessApprovalPolicyApproverDAL.delete({ policyId: doc.id }, tx);
await accessApprovalPolicyApproverDAL.insertMany(
secretApprovers.map(({ id }) => ({
approverId: id,
approverUserIds.map((userId) => ({
approverUserId: userId,
policyId: doc.id
})),
tx

@ -1,4 +1,4 @@
import { TProjectPermission } from "@app/lib/types";
import { EnforcementLevel, TProjectPermission } from "@app/lib/types";
import { ActorAuthMethod } from "@app/services/auth/auth-type";
import { TPermissionServiceFactory } from "../permission/permission-service";
@ -17,17 +17,19 @@ export type TCreateAccessApprovalPolicy = {
approvals: number;
secretPath: string;
environment: string;
approvers: string[];
approverUserIds: string[];
projectSlug: string;
name: string;
enforcementLevel: EnforcementLevel;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateAccessApprovalPolicy = {
policyId: string;
approvals?: number;
approvers?: string[];
approverUserIds?: string[];
secretPath?: string;
name?: string;
enforcementLevel?: EnforcementLevel;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteAccessApprovalPolicy = {

@ -1,7 +1,7 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests } from "@app/db/schemas";
import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests, TUsers } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
@ -40,6 +40,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.join<TUsers>(
db(TableName.Users).as("requestedByUser"),
`${TableName.AccessApprovalRequest}.requestedByUserId`,
`requestedByUser.id`
)
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.select(selectAllTableCols(TableName.AccessApprovalRequest))
@ -48,10 +54,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
db.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
db.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals"),
db.ref("secretPath").withSchema(TableName.AccessApprovalPolicy).as("policySecretPath"),
db.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
db.ref("envId").withSchema(TableName.AccessApprovalPolicy).as("policyEnvId")
)
.select(db.ref("approverId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(
db.ref("projectId").withSchema(TableName.Environment),
@ -60,15 +67,20 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
)
.select(
db.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
db.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"),
db.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus")
)
// TODO: ADD SUPPORT FOR GROUPS!!!!
.select(
db
.ref("projectMembershipId")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("privilegeMembershipId"),
db.ref("email").withSchema("requestedByUser").as("requestedByUserEmail"),
db.ref("username").withSchema("requestedByUser").as("requestedByUserUsername"),
db.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
db.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("privilegeUserId"),
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).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"),
@ -98,11 +110,21 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
name: doc.policyName,
approvals: doc.policyApprovals,
secretPath: doc.policySecretPath,
enforcementLevel: doc.policyEnforcementLevel,
envId: doc.policyEnvId
},
requestedByUser: {
userId: doc.requestedByUserId,
email: doc.requestedByUserEmail,
firstName: doc.requestedByUserFirstName,
lastName: doc.requestedByUserLastName,
username: doc.requestedByUserUsername
},
privilege: doc.privilegeId
? {
membershipId: doc.privilegeMembershipId,
userId: doc.privilegeUserId,
projectId: doc.projectId,
isTemporary: doc.privilegeIsTemporary,
temporaryMode: doc.privilegeTemporaryMode,
temporaryRange: doc.privilegeTemporaryRange,
@ -116,11 +138,11 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}),
childrenMapper: [
{
key: "reviewerMemberId",
key: "reviewerUserId",
label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
mapper: ({ reviewerUserId: userId, reviewerStatus: status }) => (userId ? { userId, status } : undefined)
},
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
{ key: "approverUserId", label: "approvers" as const, mapper: ({ approverUserId }) => approverUserId }
]
});
@ -144,29 +166,65 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicy}.id`
)
.join<TUsers>(
db(TableName.Users).as("requestedByUser"),
`${TableName.AccessApprovalRequest}.requestedByUserId`,
`requestedByUser.id`
)
.join(
TableName.AccessApprovalPolicyApprover,
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.join<TUsers>(
db(TableName.Users).as("accessApprovalPolicyApproverUser"),
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
"accessApprovalPolicyApproverUser.id"
)
.leftJoin(
TableName.AccessApprovalRequestReviewer,
`${TableName.AccessApprovalRequest}.id`,
`${TableName.AccessApprovalRequestReviewer}.requestId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("accessApprovalReviewerUser"),
`${TableName.AccessApprovalRequestReviewer}.reviewerUserId`,
`accessApprovalReviewerUser.id`
)
.leftJoin(TableName.Environment, `${TableName.AccessApprovalPolicy}.envId`, `${TableName.Environment}.id`)
.select(selectAllTableCols(TableName.AccessApprovalRequest))
.select(
tx.ref("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"),
tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover),
tx.ref("email").withSchema("accessApprovalPolicyApproverUser").as("approverEmail"),
tx.ref("username").withSchema("accessApprovalPolicyApproverUser").as("approverUsername"),
tx.ref("firstName").withSchema("accessApprovalPolicyApproverUser").as("approverFirstName"),
tx.ref("lastName").withSchema("accessApprovalPolicyApproverUser").as("approverLastName"),
tx.ref("email").withSchema("requestedByUser").as("requestedByUserEmail"),
tx.ref("username").withSchema("requestedByUser").as("requestedByUserUsername"),
tx.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
tx.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
tx.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer),
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
tx.ref("email").withSchema("accessApprovalReviewerUser").as("reviewerEmail"),
tx.ref("username").withSchema("accessApprovalReviewerUser").as("reviewerUsername"),
tx.ref("firstName").withSchema("accessApprovalReviewerUser").as("reviewerFirstName"),
tx.ref("lastName").withSchema("accessApprovalReviewerUser").as("reviewerLastName"),
tx.ref("id").withSchema(TableName.AccessApprovalPolicy).as("policyId"),
tx.ref("name").withSchema(TableName.AccessApprovalPolicy).as("policyName"),
tx.ref("projectId").withSchema(TableName.Environment),
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("approverId").withSchema(TableName.AccessApprovalPolicyApprover)
tx.ref("enforcementLevel").withSchema(TableName.AccessApprovalPolicy).as("policyEnforcementLevel"),
tx.ref("approvals").withSchema(TableName.AccessApprovalPolicy).as("policyApprovals")
);
const findById = async (id: string, tx?: Knex) => {
@ -184,16 +242,47 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
id: el.policyId,
name: el.policyName,
approvals: el.policyApprovals,
secretPath: el.policySecretPath
secretPath: el.policySecretPath,
enforcementLevel: el.policyEnforcementLevel
},
requestedByUser: {
userId: el.requestedByUserId,
email: el.requestedByUserEmail,
firstName: el.requestedByUserFirstName,
lastName: el.requestedByUserLastName,
username: el.requestedByUserUsername
}
}),
childrenMapper: [
{
key: "reviewerMemberId",
key: "reviewerUserId",
label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
mapper: ({
reviewerUserId: userId,
reviewerStatus: status,
reviewerEmail: email,
reviewerLastName: lastName,
reviewerUsername: username,
reviewerFirstName: firstName
}) => (userId ? { userId, status, email, firstName, lastName, username } : undefined)
},
{ key: "approverId", label: "approvers" as const, mapper: ({ approverId }) => approverId }
{
key: "approverUserId",
label: "approvers" as const,
mapper: ({
approverUserId,
approverEmail: email,
approverUsername: username,
approverLastName: lastName,
approverFirstName: firstName
}) => ({
userId: approverUserId,
email,
firstName,
lastName,
username
})
}
]
});
if (!formatedDoc?.[0]) return;
@ -231,7 +320,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("member").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerMemberId"));
.select(db.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerUserId"));
const formattedRequests = sqlNestRelationships({
data: accessRequests,
@ -241,9 +330,10 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}),
childrenMapper: [
{
key: "reviewerMemberId",
key: "reviewerUserId",
label: "reviewers" as const,
mapper: ({ reviewerMemberId: member, reviewerStatus: status }) => (member ? { member, status } : undefined)
mapper: ({ reviewerUserId: reviewer, reviewerStatus: status }) =>
reviewer ? { reviewer, status } : undefined
}
]
});

@ -52,7 +52,10 @@ type TSecretApprovalRequestServiceFactoryDep = {
>;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findById">;
smtpService: Pick<TSmtpService, "sendMail">;
userDAL: Pick<TUserDALFactory, "findUserByProjectMembershipId" | "findUsersByProjectMembershipIds">;
userDAL: Pick<
TUserDALFactory,
"findUserByProjectMembershipId" | "findUsersByProjectMembershipIds" | "find" | "findById"
>;
};
export type TAccessApprovalRequestServiceFactory = ReturnType<typeof accessApprovalRequestServiceFactory>;
@ -94,7 +97,7 @@ export const accessApprovalRequestServiceFactory = ({
);
if (!membership) throw new UnauthorizedError({ message: "You are not a member of this project" });
const requestedByUser = await userDAL.findUserByProjectMembershipId(membership.id);
const requestedByUser = await userDAL.findById(actorId);
if (!requestedByUser) throw new UnauthorizedError({ message: "User not found" });
await projectDAL.checkProjectUpgradeStatus(project.id);
@ -114,13 +117,15 @@ export const accessApprovalRequestServiceFactory = ({
policyId: policy.id
});
const approverUsers = await userDAL.findUsersByProjectMembershipIds(
approvers.map((approver) => approver.approverId)
);
const approverUsers = await userDAL.find({
$in: {
id: approvers.map((approver) => approver.approverUserId)
}
});
const duplicateRequests = await accessApprovalRequestDAL.find({
policyId: policy.id,
requestedBy: membership.id,
requestedByUserId: actorId,
permissions: JSON.stringify(requestedPermissions),
isTemporary
});
@ -153,7 +158,7 @@ export const accessApprovalRequestServiceFactory = ({
const approvalRequest = await accessApprovalRequestDAL.create(
{
policyId: policy.id,
requestedBy: membership.id,
requestedByUserId: actorId,
temporaryRange: temporaryRange || null,
permissions: JSON.stringify(requestedPermissions),
isTemporary
@ -212,7 +217,7 @@ export const accessApprovalRequestServiceFactory = ({
let requests = await accessApprovalRequestDAL.findRequestsWithPrivilegeByPolicyIds(policies.map((p) => p.id));
if (authorProjectMembershipId) {
requests = requests.filter((request) => request.requestedBy === authorProjectMembershipId);
requests = requests.filter((request) => request.requestedByUserId === actorId);
}
if (envSlug) {
@ -246,8 +251,8 @@ export const accessApprovalRequestServiceFactory = ({
if (
!hasRole(ProjectMembershipRole.Admin) &&
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
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
!policy.approvers.find((approver) => approver.userId === actorId) // The request isn't performed by an assigned approver
) {
throw new UnauthorizedError({ message: "You are not authorized to approve this request" });
}
@ -273,7 +278,7 @@ export const accessApprovalRequestServiceFactory = ({
const review = await accessApprovalRequestReviewerDAL.findOne(
{
requestId: accessApprovalRequest.id,
member: membership.id
reviewerUserId: actorId
},
tx
);
@ -282,7 +287,7 @@ export const accessApprovalRequestServiceFactory = ({
{
status,
requestId: accessApprovalRequest.id,
member: membership.id
reviewerUserId: actorId
},
tx
);
@ -303,7 +308,8 @@ export const accessApprovalRequestServiceFactory = ({
// Permanent access
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.requestedBy,
userId: accessApprovalRequest.requestedByUserId,
projectId: accessApprovalRequest.projectId,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions)
},
@ -317,7 +323,8 @@ export const accessApprovalRequestServiceFactory = ({
const privilege = await additionalPrivilegeDAL.create(
{
projectMembershipId: accessApprovalRequest.requestedBy,
userId: accessApprovalRequest.requestedByUserId,
projectId: accessApprovalRequest.projectId,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions),
isTemporary: true,

@ -75,15 +75,16 @@ export const auditLogDALFactory = (db: TDbClient) => {
.del()
.returning("id");
numberOfRetryOnFailure = 0; // reset
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 100); // time to breathe for db
});
} catch (error) {
numberOfRetryOnFailure += 1;
logger.error(error, "Failed to delete audit log on pruning");
} finally {
// eslint-disable-next-line no-await-in-loop
await new Promise((resolve) => {
setTimeout(resolve, 10); // time to breathe for db
});
}
} while (deletedAuditLogIds.length > 0 && numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE);
} while (deletedAuditLogIds.length > 0 || numberOfRetryOnFailure < MAX_RETRY_ON_FAILURE);
};
return { ...auditLogOrm, pruneAuditLog, find };

@ -1,5 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { TPermissionServiceFactory } from "../permission/permission-service";
@ -61,6 +62,10 @@ export const auditLogServiceFactory = ({
};
const createAuditLog = async (data: TCreateAuditLogDTO) => {
const appCfg = getConfig();
if (appCfg.DISABLE_AUDIT_LOG_GENERATION) {
return;
}
// add all cases in which project id or org id cannot be added
if (data.event.type !== EventType.LOGIN_IDENTITY_UNIVERSAL_AUTH) {
if (!data.projectId && !data.orgId) throw new BadRequestError({ message: "Must either project id or org id" });

@ -2,6 +2,7 @@ import { TProjectPermission } from "@app/lib/types";
import { ActorType } from "@app/services/auth/auth-type";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
export type TListProjectAuditLogDTO = {
auditLogActor?: string;
@ -106,6 +107,7 @@ export enum EventType {
CREATE_ENVIRONMENT = "create-environment",
UPDATE_ENVIRONMENT = "update-environment",
DELETE_ENVIRONMENT = "delete-environment",
GET_ENVIRONMENT = "get-environment",
ADD_WORKSPACE_MEMBER = "add-workspace-member",
ADD_BATCH_WORKSPACE_MEMBER = "add-workspace-members",
REMOVE_WORKSPACE_MEMBER = "remove-workspace-member",
@ -129,16 +131,42 @@ export enum EventType {
GET_CA = "get-certificate-authority",
UPDATE_CA = "update-certificate-authority",
DELETE_CA = "delete-certificate-authority",
RENEW_CA = "renew-certificate-authority",
GET_CA_CSR = "get-certificate-authority-csr",
GET_CA_CERTS = "get-certificate-authority-certs",
GET_CA_CERT = "get-certificate-authority-cert",
SIGN_INTERMEDIATE = "sign-intermediate",
IMPORT_CA_CERT = "import-certificate-authority-cert",
GET_CA_CRL = "get-certificate-authority-crl",
GET_CA_CRLS = "get-certificate-authority-crls",
ISSUE_CERT = "issue-cert",
SIGN_CERT = "sign-cert",
GET_CERT = "get-cert",
DELETE_CERT = "delete-cert",
REVOKE_CERT = "revoke-cert",
GET_CERT_BODY = "get-cert-body"
GET_CERT_BODY = "get-cert-body",
CREATE_PKI_ALERT = "create-pki-alert",
GET_PKI_ALERT = "get-pki-alert",
UPDATE_PKI_ALERT = "update-pki-alert",
DELETE_PKI_ALERT = "delete-pki-alert",
CREATE_PKI_COLLECTION = "create-pki-collection",
GET_PKI_COLLECTION = "get-pki-collection",
UPDATE_PKI_COLLECTION = "update-pki-collection",
DELETE_PKI_COLLECTION = "delete-pki-collection",
GET_PKI_COLLECTION_ITEMS = "get-pki-collection-items",
ADD_PKI_COLLECTION_ITEM = "add-pki-collection-item",
DELETE_PKI_COLLECTION_ITEM = "delete-pki-collection-item",
CREATE_KMS = "create-kms",
UPDATE_KMS = "update-kms",
DELETE_KMS = "delete-kms",
GET_KMS = "get-kms",
UPDATE_PROJECT_KMS = "update-project-kms",
GET_PROJECT_KMS_BACKUP = "get-project-kms-backup",
LOAD_PROJECT_KMS_BACKUP = "load-project-kms-backup",
ORG_ADMIN_ACCESS_PROJECT = "org-admin-accessed-project",
CREATE_CERTIFICATE_TEMPLATE = "create-certificate-template",
UPDATE_CERTIFICATE_TEMPLATE = "update-certificate-template",
DELETE_CERTIFICATE_TEMPLATE = "delete-certificate-template",
GET_CERTIFICATE_TEMPLATE = "get-certificate-template"
}
interface UserActorMetadata {
@ -328,6 +356,7 @@ interface DeleteIntegrationEvent {
targetServiceId?: string;
path?: string;
region?: string;
shouldDeleteIntegrationSecrets?: boolean;
};
}
@ -831,6 +860,13 @@ interface CreateEnvironmentEvent {
};
}
interface GetEnvironmentEvent {
type: EventType.GET_ENVIRONMENT;
metadata: {
id: string;
};
}
interface UpdateEnvironmentEvent {
type: EventType.UPDATE_ENVIRONMENT;
metadata: {
@ -1078,6 +1114,14 @@ interface DeleteCa {
};
}
interface RenewCa {
type: EventType.RENEW_CA;
metadata: {
caId: string;
dn: string;
};
}
interface GetCaCsr {
type: EventType.GET_CA_CSR;
metadata: {
@ -1086,6 +1130,14 @@ interface GetCaCsr {
};
}
interface GetCaCerts {
type: EventType.GET_CA_CERTS;
metadata: {
caId: string;
dn: string;
};
}
interface GetCaCert {
type: EventType.GET_CA_CERT;
metadata: {
@ -1111,8 +1163,8 @@ interface ImportCaCert {
};
}
interface GetCaCrl {
type: EventType.GET_CA_CRL;
interface GetCaCrls {
type: EventType.GET_CA_CRLS;
metadata: {
caId: string;
dn: string;
@ -1128,6 +1180,15 @@ interface IssueCert {
};
}
interface SignCert {
type: EventType.SIGN_CERT;
metadata: {
caId: string;
dn: string;
serialNumber: string;
};
}
interface GetCert {
type: EventType.GET_CERT;
metadata: {
@ -1164,6 +1225,201 @@ interface GetCertBody {
};
}
interface CreatePkiAlert {
type: EventType.CREATE_PKI_ALERT;
metadata: {
pkiAlertId: string;
pkiCollectionId: string;
name: string;
alertBeforeDays: number;
recipientEmails: string;
};
}
interface GetPkiAlert {
type: EventType.GET_PKI_ALERT;
metadata: {
pkiAlertId: string;
};
}
interface UpdatePkiAlert {
type: EventType.UPDATE_PKI_ALERT;
metadata: {
pkiAlertId: string;
pkiCollectionId?: string;
name?: string;
alertBeforeDays?: number;
recipientEmails?: string;
};
}
interface DeletePkiAlert {
type: EventType.DELETE_PKI_ALERT;
metadata: {
pkiAlertId: string;
};
}
interface CreatePkiCollection {
type: EventType.CREATE_PKI_COLLECTION;
metadata: {
pkiCollectionId: string;
name: string;
};
}
interface GetPkiCollection {
type: EventType.GET_PKI_COLLECTION;
metadata: {
pkiCollectionId: string;
};
}
interface UpdatePkiCollection {
type: EventType.UPDATE_PKI_COLLECTION;
metadata: {
pkiCollectionId: string;
name?: string;
};
}
interface DeletePkiCollection {
type: EventType.DELETE_PKI_COLLECTION;
metadata: {
pkiCollectionId: string;
};
}
interface GetPkiCollectionItems {
type: EventType.GET_PKI_COLLECTION_ITEMS;
metadata: {
pkiCollectionId: string;
};
}
interface AddPkiCollectionItem {
type: EventType.ADD_PKI_COLLECTION_ITEM;
metadata: {
pkiCollectionItemId: string;
pkiCollectionId: string;
type: PkiItemType;
itemId: string;
};
}
interface DeletePkiCollectionItem {
type: EventType.DELETE_PKI_COLLECTION_ITEM;
metadata: {
pkiCollectionItemId: string;
pkiCollectionId: string;
};
}
interface CreateKmsEvent {
type: EventType.CREATE_KMS;
metadata: {
kmsId: string;
provider: string;
slug: string;
description?: string;
};
}
interface DeleteKmsEvent {
type: EventType.DELETE_KMS;
metadata: {
kmsId: string;
slug: string;
};
}
interface UpdateKmsEvent {
type: EventType.UPDATE_KMS;
metadata: {
kmsId: string;
provider: string;
slug?: string;
description?: string;
};
}
interface GetKmsEvent {
type: EventType.GET_KMS;
metadata: {
kmsId: string;
slug: string;
};
}
interface UpdateProjectKmsEvent {
type: EventType.UPDATE_PROJECT_KMS;
metadata: {
secretManagerKmsKey: {
id: string;
slug: string;
};
};
}
interface GetProjectKmsBackupEvent {
type: EventType.GET_PROJECT_KMS_BACKUP;
metadata: Record<string, string>; // no metadata yet
}
interface LoadProjectKmsBackupEvent {
type: EventType.LOAD_PROJECT_KMS_BACKUP;
metadata: Record<string, string>; // no metadata yet
}
interface CreateCertificateTemplate {
type: EventType.CREATE_CERTIFICATE_TEMPLATE;
metadata: {
certificateTemplateId: string;
caId: string;
pkiCollectionId?: string;
name: string;
commonName: string;
subjectAlternativeName: string;
ttl: string;
};
}
interface GetCertificateTemplate {
type: EventType.GET_CERTIFICATE_TEMPLATE;
metadata: {
certificateTemplateId: string;
};
}
interface UpdateCertificateTemplate {
type: EventType.UPDATE_CERTIFICATE_TEMPLATE;
metadata: {
certificateTemplateId: string;
caId: string;
pkiCollectionId?: string;
name: string;
commonName: string;
subjectAlternativeName: string;
ttl: string;
};
}
interface DeleteCertificateTemplate {
type: EventType.DELETE_CERTIFICATE_TEMPLATE;
metadata: {
certificateTemplateId: string;
};
}
interface OrgAdminAccessProjectEvent {
type: EventType.ORG_ADMIN_ACCESS_PROJECT;
metadata: {
userId: string;
username: string;
email: string;
projectId: string;
}; // no metadata yet
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@ -1230,6 +1486,7 @@ export type Event =
| UpdateIdentityOidcAuthEvent
| GetIdentityOidcAuthEvent
| CreateEnvironmentEvent
| GetEnvironmentEvent
| UpdateEnvironmentEvent
| DeleteEnvironmentEvent
| AddWorkspaceMemberEvent
@ -1255,13 +1512,39 @@ export type Event =
| GetCa
| UpdateCa
| DeleteCa
| RenewCa
| GetCaCsr
| GetCaCerts
| GetCaCert
| SignIntermediate
| ImportCaCert
| GetCaCrl
| GetCaCrls
| IssueCert
| SignCert
| GetCert
| DeleteCert
| RevokeCert
| GetCertBody;
| GetCertBody
| CreatePkiAlert
| GetPkiAlert
| UpdatePkiAlert
| DeletePkiAlert
| CreatePkiCollection
| GetPkiCollection
| UpdatePkiCollection
| DeletePkiCollection
| GetPkiCollectionItems
| AddPkiCollectionItem
| DeletePkiCollectionItem
| CreateKmsEvent
| UpdateKmsEvent
| DeleteKmsEvent
| GetKmsEvent
| UpdateProjectKmsEvent
| GetProjectKmsBackupEvent
| LoadProjectKmsBackupEvent
| OrgAdminAccessProjectEvent
| CreateCertificateTemplate
| UpdateCertificateTemplate
| GetCertificateTemplate
| DeleteCertificateTemplate;

@ -2,24 +2,24 @@ import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
// import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
import { TGetCrl } from "./certificate-authority-crl-types";
import { TGetCaCrlsDTO, TGetCrlById } from "./certificate-authority-crl-types";
type TCertificateAuthorityCrlServiceFactoryDep = {
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "findOne">;
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "find" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
// licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
export type TCertificateAuthorityCrlServiceFactory = ReturnType<typeof certificateAuthorityCrlServiceFactory>;
@ -29,13 +29,42 @@ export const certificateAuthorityCrlServiceFactory = ({
certificateAuthorityCrlDAL,
projectDAL,
kmsService,
permissionService,
licenseService
permissionService // licenseService
}: TCertificateAuthorityCrlServiceFactoryDep) => {
/**
* Return the Certificate Revocation List (CRL) for CA with id [caId]
* Return CRL with id [crlId]
*/
const getCaCrl = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCrl) => {
const getCrlById = async (crlId: TGetCrlById) => {
const caCrl = await certificateAuthorityCrlDAL.findById(crlId);
if (!caCrl) throw new NotFoundError({ message: "CRL not found" });
const ca = await certificateAuthorityDAL.findById(caCrl.caId);
const keyId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
projectDAL,
kmsService
});
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: keyId
});
const decryptedCrl = await kmsDecryptor({ cipherTextBlob: caCrl.encryptedCrl });
const crl = new x509.X509Crl(decryptedCrl);
return {
ca,
caCrl,
crl: crl.rawData
};
};
/**
* Returns a list of CRL ids for CA with id [caId]
*/
const getCaCrls = async ({ caId, actorId, actorAuthMethod, actor, actorOrgId }: TGetCaCrlsDTO) => {
const ca = await certificateAuthorityDAL.findById(caId);
if (!ca) throw new BadRequestError({ message: "CA not found" });
@ -52,15 +81,14 @@ export const certificateAuthorityCrlServiceFactory = ({
ProjectPermissionSub.CertificateAuthorities
);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan.caCrl)
throw new BadRequestError({
message:
"Failed to get CA certificate revocation list (CRL) due to plan restriction. Upgrade plan to get the CA CRL."
});
// const plan = await licenseService.getPlan(actorOrgId);
// if (!plan.caCrl)
// throw new BadRequestError({
// message:
// "Failed to get CA certificate revocation lists (CRLs) due to plan restriction. Upgrade plan to get the CA CRL."
// });
const caCrl = await certificateAuthorityCrlDAL.findOne({ caId: ca.id });
if (!caCrl) throw new BadRequestError({ message: "CRL not found" });
const caCrls = await certificateAuthorityCrlDAL.find({ caId: ca.id }, { sort: [["createdAt", "desc"]] });
const keyId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
@ -72,15 +100,23 @@ export const certificateAuthorityCrlServiceFactory = ({
kmsId: keyId
});
const decryptedCrl = kmsDecryptor({ cipherTextBlob: caCrl.encryptedCrl });
const crl = new x509.X509Crl(decryptedCrl);
const decryptedCrls = await Promise.all(
caCrls.map(async (caCrl) => {
const decryptedCrl = await kmsDecryptor({ cipherTextBlob: caCrl.encryptedCrl });
const crl = new x509.X509Crl(decryptedCrl);
const base64crl = crl.toString("base64");
const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`;
const base64crl = crl.toString("base64");
const crlPem = `-----BEGIN X509 CRL-----\n${base64crl.match(/.{1,64}/g)?.join("\n")}\n-----END X509 CRL-----`;
return {
id: caCrl.id,
crl: crlPem
};
})
);
return {
crl: crlPem,
ca
ca,
crls: decryptedCrls
};
};
@ -166,7 +202,8 @@ export const certificateAuthorityCrlServiceFactory = ({
// };
return {
getCaCrl
getCrlById,
getCaCrls
// rotateCaCrl
};
};

@ -1,5 +1,7 @@
import { TProjectPermission } from "@app/lib/types";
export type TGetCrl = {
export type TGetCrlById = string;
export type TGetCaCrlsDTO = {
caId: string;
} & Omit<TProjectPermission, "projectId">;

@ -12,10 +12,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
const countLeasesForDynamicSecret = async (dynamicSecretId: string, tx?: Knex) => {
try {
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
.count("*")
.where({ dynamicSecretId })
.first();
const doc = await (tx || db)(TableName.DynamicSecretLease).count("*").where({ dynamicSecretId }).first();
return parseInt(doc || "0", 10);
} catch (error) {
throw new DatabaseError({ error, name: "DynamicSecretCountLeases" });
@ -24,7 +21,7 @@ export const dynamicSecretLeaseDALFactory = (db: TDbClient) => {
const findById = async (id: string, tx?: Knex) => {
try {
const doc = await (tx || db.replicaNode())(TableName.DynamicSecretLease)
const doc = await (tx || db)(TableName.DynamicSecretLease)
.where({ [`${TableName.DynamicSecretLease}.id` as "id"]: id })
.first()
.join(

@ -31,6 +31,8 @@ export const externalKmsDALFactory = (db: TDbClient) => {
isReserved: el.isReserved,
orgId: el.orgId,
slug: el.slug,
createdAt: el.createdAt,
updatedAt: el.updatedAt,
externalKms: {
id: el.externalKmsId,
provider: el.externalKmsProvider,

@ -5,7 +5,9 @@ import { BadRequestError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service";
import { TExternalKmsDALFactory } from "./external-kms-dal";
@ -22,9 +24,10 @@ import { ExternalKmsAwsSchema, KmsProviders } from "./providers/model";
type TExternalKmsServiceFactoryDep = {
externalKmsDAL: TExternalKmsDALFactory;
kmsService: Pick<TKmsServiceFactory, "getOrgKmsKeyId" | "encryptWithKmsKey" | "decryptWithKmsKey">;
kmsService: Pick<TKmsServiceFactory, "getOrgKmsKeyId" | "createCipherPairWithDataKey">;
kmsDAL: Pick<TKmsKeyDALFactory, "create" | "updateById" | "findById" | "deleteById" | "findOne">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
};
export type TExternalKmsServiceFactory = ReturnType<typeof externalKmsServiceFactory>;
@ -32,6 +35,7 @@ export type TExternalKmsServiceFactory = ReturnType<typeof externalKmsServiceFac
export const externalKmsServiceFactory = ({
externalKmsDAL,
permissionService,
licenseService,
kmsService,
kmsDAL
}: TExternalKmsServiceFactoryDep) => {
@ -51,7 +55,15 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
const plan = await licenseService.getPlan(actorOrgId);
if (!plan.externalKms) {
throw new BadRequestError({
message: "Failed to create external KMS due to plan restriction. Upgrade to the Enterprise plan."
});
}
const kmsSlug = slug ? slugify(slug) : slugify(alphaNumericNanoId(8).toLowerCase());
let sanitizedProviderInput = "";
@ -59,21 +71,23 @@ export const externalKmsServiceFactory = ({
case KmsProviders.Aws:
{
const externalKms = await AwsKmsProviderFactory({ inputs: provider.inputs });
await externalKms.validateConnection();
// if missing kms key this generate a new kms key id and returns new provider input
const newProviderInput = await externalKms.generateInputKmsKey();
sanitizedProviderInput = JSON.stringify(newProviderInput);
await externalKms.validateConnection();
}
break;
default:
throw new BadRequestError({ message: "external kms provided is invalid" });
}
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId);
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: orgKmsKeyId
const { encryptor: orgDataKeyEncryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: actorOrgId
});
const { cipherTextBlob: encryptedProviderInputs } = kmsEncryptor({
const { cipherTextBlob: encryptedProviderInputs } = orgDataKeyEncryptor({
plainText: Buffer.from(sanitizedProviderInput, "utf8")
});
@ -119,19 +133,28 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms);
const plan = await licenseService.getPlan(kmsDoc.orgId);
if (!plan.externalKms) {
throw new BadRequestError({
message: "Failed to update external KMS due to plan restriction. Upgrade to the Enterprise plan."
});
}
const kmsSlug = slug ? slugify(slug) : undefined;
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId);
let sanitizedProviderInput = "";
if (provider) {
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: orgDefaultKmsId
const { encryptor: orgDataKeyEncryptor, decryptor: orgDataKeyDecryptor } =
await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: actorOrgId
});
const decryptedProviderInputBlob = kmsDecryptor({
if (provider) {
const decryptedProviderInputBlob = orgDataKeyDecryptor({
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
});
@ -154,10 +177,7 @@ export const externalKmsServiceFactory = ({
let encryptedProviderInputs: Buffer | undefined;
if (sanitizedProviderInput) {
const kmsEncryptor = await kmsService.encryptWithKmsKey({
kmsId: orgDefaultKmsId
});
const { cipherTextBlob } = kmsEncryptor({
const { cipherTextBlob } = orgDataKeyEncryptor({
plainText: Buffer.from(sanitizedProviderInput, "utf8")
});
encryptedProviderInputs = cipherTextBlob;
@ -197,7 +217,7 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms);
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
@ -218,7 +238,7 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
const externalKmsDocs = await externalKmsDAL.find({ orgId: actorOrgId });
@ -234,16 +254,18 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId);
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: orgDefaultKmsId
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: actorOrgId
});
const decryptedProviderInputBlob = kmsDecryptor({
const decryptedProviderInputBlob = orgDataKeyDecryptor({
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
});
switch (externalKmsDoc.provider) {
@ -273,16 +295,17 @@ export const externalKmsServiceFactory = ({
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Settings);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
const externalKmsDoc = await externalKmsDAL.findOne({ kmsKeyId: kmsDoc.id });
if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" });
const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId);
const kmsDecryptor = await kmsService.decryptWithKmsKey({
kmsId: orgDefaultKmsId
const { decryptor: orgDataKeyDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.Organization,
orgId: actorOrgId
});
const decryptedProviderInputBlob = kmsDecryptor({
const decryptedProviderInputBlob = orgDataKeyDecryptor({
cipherTextBlob: externalKmsDoc.encryptedProviderInputs
});

@ -50,17 +50,26 @@ type TAwsKmsProviderFactoryReturn = TExternalKmsProviderFns & {
};
export const AwsKmsProviderFactory = async ({ inputs }: AwsKmsProviderArgs): Promise<TAwsKmsProviderFactoryReturn> => {
const providerInputs = await ExternalKmsAwsSchema.parseAsync(inputs);
const awsClient = await getAwsKmsClient(providerInputs);
let providerInputs = await ExternalKmsAwsSchema.parseAsync(inputs);
let awsClient = await getAwsKmsClient(providerInputs);
const generateInputKmsKey = async () => {
if (providerInputs.kmsKeyId) return providerInputs;
const command = new CreateKeyCommand({ Tags: [{ TagKey: "author", TagValue: "infisical" }] });
const kmsKey = await awsClient.send(command);
if (!kmsKey.KeyMetadata?.KeyId) throw new Error("Failed to generate kms key");
return { ...providerInputs, kmsKeyId: kmsKey.KeyMetadata?.KeyId };
const updatedProviderInputs = await ExternalKmsAwsSchema.parseAsync({
...providerInputs,
kmsKeyId: kmsKey.KeyMetadata?.KeyId
});
providerInputs = updatedProviderInputs;
awsClient = await getAwsKmsClient(providerInputs);
return updatedProviderInputs;
};
const validateConnection = async () => {

@ -336,31 +336,36 @@ export const removeUsersFromGroupByUserIds = async ({
)
);
// TODO: this part can be optimized
for await (const userId of userIds) {
const t = await userGroupMembershipDAL.filterProjectsByUserMembership(userId, group.id, projectIds, tx);
const projectsToDeleteKeyFor = projectIds.filter((p) => !t.has(p));
const promises: Array<Promise<void>> = [];
for (const userId of userIds) {
promises.push(
(async () => {
const t = await userGroupMembershipDAL.filterProjectsByUserMembership(userId, group.id, projectIds, tx);
const projectsToDeleteKeyFor = projectIds.filter((p) => !t.has(p));
if (projectsToDeleteKeyFor.length) {
await projectKeyDAL.delete(
{
receiverId: userId,
$in: {
projectId: projectsToDeleteKeyFor
}
},
tx
);
}
if (projectsToDeleteKeyFor.length) {
await projectKeyDAL.delete(
{
receiverId: userId,
$in: {
projectId: projectsToDeleteKeyFor
}
},
tx
);
}
await userGroupMembershipDAL.delete(
{
groupId: group.id,
userId
},
tx
await userGroupMembershipDAL.delete(
{
groupId: group.id,
userId
},
tx
);
})()
);
}
await Promise.all(promises);
}
if (membersToRemoveFromGroupPending.length) {

@ -162,17 +162,50 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
}
};
const findUserGroupMembershipsInOrg = async (userId: string, orgId: string) => {
const findGroupMembershipsByUserIdInOrg = async (userId: string, orgId: string) => {
try {
const docs = await db
.replicaNode()(TableName.UserGroupMembership)
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.UserGroupMembership}.userId`, userId)
.where(`${TableName.Groups}.orgId`, orgId);
.where(`${TableName.Groups}.orgId`, orgId)
.select(
db.ref("id").withSchema(TableName.UserGroupMembership),
db.ref("groupId").withSchema(TableName.UserGroupMembership),
db.ref("name").withSchema(TableName.Groups).as("groupName"),
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"),
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
db.ref("lastName").withSchema(TableName.Users).as("lastName")
);
return docs;
} catch (error) {
throw new DatabaseError({ error, name: "findTest" });
throw new DatabaseError({ error, name: "Find group memberships by user id in org" });
}
};
const findGroupMembershipsByGroupIdInOrg = async (groupId: string, orgId: string) => {
try {
const docs = await db
.replicaNode()(TableName.UserGroupMembership)
.join(TableName.Groups, `${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`)
.join(TableName.OrgMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.OrgMembership}.userId`)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.where(`${TableName.Groups}.id`, groupId)
.where(`${TableName.Groups}.orgId`, orgId)
.select(
db.ref("id").withSchema(TableName.UserGroupMembership),
db.ref("groupId").withSchema(TableName.UserGroupMembership),
db.ref("name").withSchema(TableName.Groups).as("groupName"),
db.ref("id").withSchema(TableName.OrgMembership).as("orgMembershipId"),
db.ref("firstName").withSchema(TableName.Users).as("firstName"),
db.ref("lastName").withSchema(TableName.Users).as("lastName")
);
return docs;
} catch (error) {
throw new DatabaseError({ error, name: "Find group memberships by group id in org" });
}
};
@ -182,6 +215,7 @@ export const userGroupMembershipDALFactory = (db: TDbClient) => {
findUserGroupMembershipsInProject,
findGroupMembersNotInProject,
deletePendingUserGroupMembershipsByUserIds,
findUserGroupMembershipsInOrg
findGroupMembershipsByUserIdInOrg,
findGroupMembershipsByGroupIdInOrg
};
};

@ -39,7 +39,13 @@ export const getDefaultOnPremFeatures = (): TFeatureSet => ({
secretApproval: false,
secretRotation: true,
caCrl: false,
instanceUserManagement: false
instanceUserManagement: false,
externalKms: false,
rateLimits: {
readLimit: 60,
writeLimit: 200,
secretsLimit: 40
}
});
export const setupLicenceRequestWithStore = (baseURL: string, refreshUrl: string, licenseKey: string) => {

@ -57,6 +57,12 @@ export type TFeatureSet = {
secretRotation: true;
caCrl: false;
instanceUserManagement: false;
externalKms: false;
rateLimits: {
readLimit: number;
writeLimit: number;
secretsLimit: number;
};
};
export type TOrgPlansTableDTO = {

@ -9,6 +9,10 @@ export enum OrgPermissionActions {
Delete = "delete"
}
export enum OrgPermissionAdminConsoleAction {
AccessAllProjects = "access-all-projects"
}
export enum OrgPermissionSubjects {
Workspace = "workspace",
Role = "role",
@ -21,7 +25,9 @@ export enum OrgPermissionSubjects {
Groups = "groups",
Billing = "billing",
SecretScanning = "secret-scanning",
Identity = "identity"
Identity = "identity",
Kms = "kms",
AdminConsole = "organization-admin-console"
}
export type OrgPermissionSet =
@ -37,7 +43,9 @@ export type OrgPermissionSet =
| [OrgPermissionActions, OrgPermissionSubjects.Groups]
| [OrgPermissionActions, OrgPermissionSubjects.SecretScanning]
| [OrgPermissionActions, OrgPermissionSubjects.Billing]
| [OrgPermissionActions, OrgPermissionSubjects.Identity];
| [OrgPermissionActions, OrgPermissionSubjects.Identity]
| [OrgPermissionActions, OrgPermissionSubjects.Kms]
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
const buildAdminPermission = () => {
const { can, build } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
@ -100,6 +108,13 @@ const buildAdminPermission = () => {
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity);
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity);
can(OrgPermissionActions.Read, OrgPermissionSubjects.Kms);
can(OrgPermissionActions.Create, OrgPermissionSubjects.Kms);
can(OrgPermissionActions.Edit, OrgPermissionSubjects.Kms);
can(OrgPermissionActions.Delete, OrgPermissionSubjects.Kms);
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
return build({ conditionsMatcher });
};

@ -66,6 +66,7 @@ export const permissionDALFactory = (db: TDbClient) => {
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
`${TableName.GroupProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.GroupProjectMembershipRole}.customRoleId`,
@ -73,6 +74,12 @@ export const permissionDALFactory = (db: TDbClient) => {
)
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.GroupProjectMembership}.projectId`,
`${TableName.Project}.id`
)
.select(selectAllTableCols(TableName.GroupProjectMembershipRole))
.select(
db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"),
@ -81,9 +88,30 @@ 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")
)
.select("permissions");
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
db.ref("permissions").withSchema(TableName.ProjectRoles).as("permissions"),
// db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("apPermissions")
// Additional Privileges
db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"),
db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"),
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"),
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessStartTime"),
db
.ref("temporaryAccessEndTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
.as("userApTemporaryAccessEndTime")
);
// .select(`${TableName.ProjectRoles}.permissions`);
const docs = await db(TableName.ProjectMembership)
.join(
@ -98,12 +126,13 @@ export const permissionDALFactory = (db: TDbClient) => {
)
.leftJoin(
TableName.ProjectUserAdditionalPrivilege,
`${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId`,
`${TableName.ProjectMembership}.id`
`${TableName.ProjectUserAdditionalPrivilege}.projectId`,
`${TableName.ProjectMembership}.projectId`
)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.where("userId", userId)
.where(`${TableName.ProjectMembership}.userId`, userId)
.where(`${TableName.ProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.ProjectUserMembershipRole))
.select(
@ -120,6 +149,10 @@ export const permissionDALFactory = (db: TDbClient) => {
db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"),
db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"),
db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"),
db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"),
db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"),
db
.ref("temporaryAccessStartTime")
.withSchema(TableName.ProjectUserAdditionalPrivilege)
@ -198,6 +231,31 @@ export const permissionDALFactory = (db: TDbClient) => {
permissions: z.unknown(),
customRoleSlug: z.string().optional().nullable()
}).parse(data)
},
{
key: "userApId",
label: "additionalPrivileges" as const,
mapper: ({
userApId,
userApProjectId,
userApUserId,
userApPermissions,
userApIsTemporary,
userApTemporaryMode,
userApTemporaryRange,
userApTemporaryAccessEndTime,
userApTemporaryAccessStartTime
}) => ({
id: userApId,
userId: userApUserId,
projectId: userApProjectId,
permissions: userApPermissions,
temporaryRange: userApTemporaryRange,
temporaryMode: userApTemporaryMode,
temporaryAccessEndTime: userApTemporaryAccessEndTime,
temporaryAccessStartTime: userApTemporaryAccessStartTime,
isTemporary: userApIsTemporary
})
}
]
})
@ -218,15 +276,24 @@ 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 activeAdditionalPrivileges =
permission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime }) =>
!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)
) ?? [];
const activeGroupAdditionalPrivileges =
groupPermission?.[0]?.additionalPrivileges?.filter(
({ isTemporary, temporaryAccessEndTime, userId: apUserId, projectId: apProjectId }) =>
apProjectId === projectId &&
apUserId === userId &&
(!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime))
) ?? [];
return {
...(permission[0] || groupPermission[0]),
roles: [...activeRoles, ...activeGroupRoles],
additionalPrivileges: activeAdditionalPrivileges
additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges]
};
} catch (error) {
throw new DatabaseError({ error, name: "GetProjectPermission" });

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