Compare commits

...

599 Commits

Author SHA1 Message Date
4c8bf9bd92 Update values.yaml 2025-06-06 20:16:50 +04:00
a6554deb80 Update connection.go 2025-06-06 20:14:03 +04:00
4bd1eb6f70 Update helm-charts/infisical-gateway/CHANGELOG.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-06-06 04:12:04 +04:00
022ecf75e1 fix(gateway): handle malformed URL's 2025-06-06 04:02:24 +04:00
ce170a6a47 Merge pull request #3740 from Infisical/daniel/gateway-helm-bump
helm(infisical-gateway): bump CLI image version to latest
2025-06-05 16:43:54 -04:00
cb8e36ae15 helm(infisical-gateway): bump CLI image version to latest 2025-06-06 00:41:35 +04:00
16ce1f441e Merge pull request #3731 from Infisical/daniel/gateway-auth-methods
feat(identities/kubernetes-auth): gateway as token reviewer
2025-06-05 16:33:24 -04:00
8043b61c9f Merge pull request #3730 from Infisical/org-access-control-no-access-display
improvement(org-access-control): Add org access control no access display
2025-06-05 13:27:38 -07:00
d374ff2093 Merge pull request #3732 from Infisical/ENG-2809
Add {{environment}} support for key schemas
2025-06-05 16:27:22 -04:00
eb7c533261 Update identity-kubernetes-auth-service.ts 2025-06-06 00:26:01 +04:00
9a935c9177 Lint 2025-06-05 16:07:00 -04:00
9d24eb15dc Feedback 2025-06-05 16:01:56 -04:00
ed4882dfac fix: simplify gateway http copy logic 2025-06-05 23:50:46 +04:00
7acd7fd522 Merge pull request #3737 from akhilmhdh/feat/limit-project-create
feat: added lock for project create
2025-06-06 00:53:13 +05:30
2148b636f5 Merge branch 'main' into ENG-2809 2025-06-05 15:10:22 -04:00
=
e40b4a0a4b feat: added lock for project create 2025-06-06 00:31:21 +05:30
311bf8b515 Merge pull request #3734 from Infisical/gateway-netowkr
Added networking docs to cover gateway
2025-06-05 10:47:01 -04:00
78c4c3e847 Update overview.mdx 2025-06-05 18:43:46 +04:00
b8aa36be99 cleanup and minor requested changes 2025-06-05 18:40:54 +04:00
594445814a docs(identity/kubernetes-auth): added docs for gateway as reviewer 2025-06-05 18:40:34 +04:00
a467b13069 Merge pull request #3728 from Infisical/condition-eq-comma-check
improvement(permissions): Prevent comma separated values with eq and neq checks
2025-06-05 19:48:38 +05:30
c425c03939 cleanup 2025-06-05 17:44:41 +04:00
9cc17452fa address greptile 2025-06-05 01:23:28 -04:00
93ba6f7b58 add netowkring docs 2025-06-05 01:18:21 -04:00
0fcb66e9ab Merge pull request #3733 from Infisical/improve-smtp-rate-limits
improvement(smtp-rate-limit): trim and substring keys and default to realIp
2025-06-04 23:11:41 -04:00
135f425fcf improvement: trim and substring keys and default to realIp 2025-06-04 20:00:53 -07:00
9c149cb4bf Merge pull request #3726 from Infisical/email-rate-limit
Improvement: add more aggresive rate limiting on smtp endpoints
2025-06-04 19:14:09 -07:00
ce45c1a43d improvements: address feedback 2025-06-04 19:05:22 -07:00
1a14c71564 Greptile review fixes 2025-06-04 21:41:21 -04:00
e7fe2ea51e Fix lint issues 2025-06-04 21:35:17 -04:00
caa129b565 requested changes 2025-06-05 05:23:30 +04:00
30d7e63a67 Add {{environment}} support for key schemas 2025-06-04 21:20:16 -04:00
a4c21d85ac Update identity-kubernetes-auth-router.ts 2025-06-05 05:07:58 +04:00
c34a139b19 cleanup 2025-06-05 05:02:58 +04:00
f2a55da9b6 Update .infisicalignore 2025-06-05 04:49:50 +04:00
a3584d6a8a Merge branch 'heads/main' into daniel/gateway-auth-methods 2025-06-05 04:49:35 +04:00
36f1559e5e cleanup 2025-06-05 04:45:57 +04:00
07902f7db9 feat(identities/kubernetes-auth): use gateway as token reviewer 2025-06-05 04:42:15 +04:00
6fddecdf82 Merge pull request #3729 from akhilmhdh/feat/ui-change-for-approval-replication
feat: updated ui for replication approval
2025-06-04 19:05:13 -04:00
99e2c85f8f Merge pull request #3718 from Infisical/filter-org-members-by-role
improvement(org-users-table): Add filter by roles to org users table
2025-06-04 16:01:43 -07:00
6e1504dc73 Merge pull request #3727 from Infisical/update-github-radar-image
improvement(github-radar-app): update image
2025-06-04 18:29:41 -04:00
=
07d930f608 feat: small text changes 2025-06-05 03:54:09 +05:30
1101707d8b improvement: add org access control no access display 2025-06-04 15:15:12 -07:00
=
696bbcb072 feat: updated ui for replication approval 2025-06-05 03:44:54 +05:30
54435d0ad9 improvements: prevent comma separated value usage with eq and neq checks 2025-06-04 14:21:36 -07:00
6c52847dec improvement: update image 2025-06-04 13:48:33 -07:00
698260cba6 improvement: add more aggresive rate limiting on smtp endpoints 2025-06-04 13:27:08 -07:00
caeda09b21 Merge pull request #3725 from Infisical/doc/spire
doc: add oidc auth doc for spire
2025-06-04 12:59:49 -04:00
1201baf35c doc: add oidc auth doc for spire 2025-06-04 15:42:43 +00:00
5d5f843a9f Merge pull request #3724 from Infisical/fix/secretRequestUIOverflows
Fix broken UI for secret requests due to long secret values
2025-06-04 21:08:03 +05:30
caca23b56c Fix broken UI for secret requests due to long secret values 2025-06-04 12:33:37 -03:00
01ea22f167 move bounty progam to invite only - low quality reports 2025-06-04 10:58:03 -04:00
83c53b9d5a Merge pull request #3677 from Infisical/secret-scanning-v2-pt-1
feature(secret-scanning-v2): secret scanning architecture and github data source
2025-06-03 16:34:29 -07:00
8cc457d49a Merge pull request #3710 from Infisical/feat/verticaDynamicSecret
feat(dynamic-secret): add vertica dynamic secret option
2025-06-03 20:27:47 -03:00
540374f543 Merge pull request #3720 from Infisical/add-email-body-padding
improvement(email-templates): Add y-padding to email body
2025-06-03 16:06:34 -07:00
4edb90d644 improvement: add y padding to email body 2025-06-03 15:58:00 -07:00
1a7151aba7 Merge pull request #3716 from Infisical/adjustable-max-view-limit-secret-sharing
Improvement(secret-sharing): Allow free number entry for max views in secret sharing
2025-06-03 15:48:42 -07:00
80d2d9d2cf improvement: handle singular 2025-06-03 15:38:39 -07:00
4268fdea44 improvement: address feedback 2025-06-03 15:36:24 -07:00
781965767d Merge pull request #3719 from Infisical/fix/ui-button-fix
Fix/UI button fix
2025-06-03 18:33:15 -04:00
fef7e43869 revert license 2025-06-03 18:10:20 -04:00
9e651a58e3 fix margin and make text click-through 2025-06-03 18:09:45 -04:00
0fbf8efd3a improvement: add filter by roles to org users table 2025-06-03 14:36:47 -07:00
dcb77bbdd4 Merge pull request #3717 from akhilmhdh/feat/sort-access-control
feat: resolved cert issue with localhost
2025-06-03 16:30:54 -04:00
=
36f7e7d81b feat: resolved cert issue with localhost 2025-06-04 01:34:38 +05:30
8f97b3ad87 improvement: allow free number entry for max views in secret sharing 2025-06-03 12:50:22 -07:00
be80444ec2 Merge pull request #3712 from Infisical/misc/update-dynamic-secret-validation-error-handling
misc: update dynamic secret validation error handling
2025-06-04 02:45:52 +08:00
6f2043dc26 Merge pull request #3714 from akhilmhdh/feat/sort-access-control
feat: added back the describeReadValue permission to default roles
2025-06-03 23:55:19 +05:30
6ae7b5e996 cleanup 2025-06-03 22:24:27 +04:00
=
95fcf560a5 feat: added back the describeReadValue permission to default roles 2025-06-03 23:46:59 +05:30
d8ee05bfba improvements: address feedback 2025-06-03 10:41:46 -07:00
400157a468 feat(cli): gateway auth methods 2025-06-03 21:35:54 +04:00
274952544f Merge pull request #3711 from akhilmhdh/feat/sort-access-control
feat: added sort for roles in both user and identity details view
2025-06-03 12:59:21 -04:00
d23beaedf1 Merge pull request #3707 from Infisical/misc/workspace-file-now-only-needed-when-project-id-omitted
misc: workspace file now only needed when project id is omitted (CLI)
2025-06-04 00:24:40 +08:00
73e89fc4db misc: update dynamic secret validation error handling 2025-06-04 00:12:40 +08:00
=
817e762e6b feat: added sort for roles in both user and identity details view 2025-06-03 21:04:02 +05:30
ce5712606f feat(dynamic-secret): Vertica option improvements 2025-06-03 10:45:58 -03:00
ce67e5f137 feat(dynamic-secret): add vertica dynamic secret option 2025-06-03 10:04:11 -03:00
440c45fd42 Merge pull request #3695 from Infisical/daniel/identity-get-projects
fix: allow identities to list projects they are apart of
2025-06-03 16:52:03 +04:00
893a042c25 Merge pull request #3698 from Infisical/daniel/cli-api-errors
fix(cli): improve error handling
2025-06-03 16:49:37 +04:00
f3fb65fcc3 misc: update error message being displayed 2025-06-03 20:06:42 +08:00
c0add863be misc: workspace file now only needed when project id is omitted (CLI) 2025-06-03 19:41:37 +08:00
5878904f77 Merge pull request #3704 from Infisical/feat/add-auto-login-for-bad-sessions-1
feat: add auto-login support for CLI for bad user session
2025-06-03 17:25:02 +08:00
98ab969356 improvements: address greppy 2025-06-02 20:24:50 -07:00
d4523b0ca4 improvements: additional feedback 2025-06-02 18:19:51 -07:00
2be8c47ae8 chore: add route tree 2025-06-02 16:29:24 -07:00
8730d14104 merge main 2025-06-02 16:24:55 -07:00
d924580599 improvements: address feedback and setup queue worker profiles 2025-06-02 14:40:06 -07:00
6e3fe0fe24 misc: addressed comments 2025-06-03 03:26:27 +08:00
9d11babc4d misc: add error message 2025-06-03 03:02:09 +08:00
ce97179b49 feat: spawn new session for login 2025-06-03 02:59:07 +08:00
f9ebb919e5 feat: add auto login for bad user sessions 2025-06-03 02:47:09 +08:00
739ef8e05a Merge pull request #3701 from Infisical/daniel/cli-auto-open-login
feat(cli): automatically open browser on login
2025-06-02 21:57:18 +04:00
644659bc10 Merge pull request #3688 from Infisical/daniel/super-admin-view-orgs
feat(instance-management): organizations overview and control
2025-06-02 21:26:15 +04:00
21e4fa83ef Update Sidebar.tsx 2025-06-02 20:48:01 +04:00
a6a6c72397 requested changes 2025-06-02 20:43:58 +04:00
4061feba21 Update login.go 2025-06-02 20:38:07 +04:00
90a415722c Merge pull request #3697 from Infisical/approvals-redesign
revamp UI for access requests
2025-06-02 13:15:38 -03:00
f3d5790e2c Fix lint issues 2025-06-02 13:10:50 -03:00
0d0fddb53a feat(cli): automatically open browser on login 2025-06-02 18:52:55 +04:00
9f2e379d4d Merge pull request #3700 from akhilmhdh/fix/gateway-dns-resolve
feat: resolved gateway verify issue and validation check
2025-06-02 10:15:38 -04:00
14e898351f Merge pull request #3673 from Infisical/check-for-recipients-on-project-access
Fix(org-admin-project-access): Check for recipients prior to sending project access email
2025-06-02 07:05:53 -07:00
=
16e0aa13c8 feat: fixed type error 2025-06-02 19:18:04 +05:30
dc130ecd7f Update routes.ts 2025-06-02 17:45:47 +04:00
b70c6b6260 fix: refactored admin panel layout 2025-06-02 17:45:27 +04:00
=
a701635f08 feat: remove gateway condition 2025-06-02 16:23:10 +05:30
=
9eb98dd276 feat: resolved gateway verify issue and validation check 2025-06-02 15:40:32 +05:30
96e9bc3b2f Merge pull request #3667 from akhilmhdh/feat/dynamic-secret-username-template
Feat/dynamic secret username template
2025-06-01 21:59:56 -04:00
22f32e060b filter out random request ID value 2025-06-01 21:31:26 +04:00
b4f26aac25 fix: tests failing 2025-06-01 21:26:16 +04:00
b634a6c371 requested changes 2025-06-01 21:10:05 +04:00
080ae5ce6f fix(cli): improve error handling 2025-06-01 20:22:15 +04:00
90d213a8ab Merge pull request #3696 from Infisical/daniel/remove-fips-section
docs: remove fips section
2025-06-01 17:46:46 +04:00
52a26b51af revamp UI for access requests 2025-05-31 17:46:01 -07:00
3b28e946cf Update hsm-integration.mdx 2025-06-01 00:23:27 +04:00
dfcf613023 fix: allow identities to list projects they are apart of 2025-06-01 00:12:56 +04:00
3ae2ec1f51 chore: revert license and fix type error 2025-05-30 20:26:55 -07:00
ce4e35e908 feature: secret scanning pt 3 2025-05-30 20:19:44 -07:00
4db82e37c1 Merge pull request #3657 from Infisical/ENG-2608
feat(secret-rotation): MySQL Secret Rotation v2
2025-05-30 19:12:57 -04:00
3a8789af76 Merge pull request #3692 from Infisical/fix/secret-sync-regex
fix(secret-sync): RE2 for regex + input limits
2025-05-30 18:10:30 -04:00
79ebfc92e9 RE2 for regex + input limits 2025-05-30 18:01:49 -04:00
ffca4aa054 lint 2025-05-30 16:52:37 -04:00
52b3f7e8c8 ui fix 2025-05-30 16:36:09 -04:00
9de33d8c23 Merge pull request #3689 from Infisical/add-gloo-docs
Gloo mesh docs
2025-05-30 15:55:05 -04:00
97aed61c54 Merge pull request #3691 from Infisical/fix/accessApprovalIssueOnDeletedPrivileges
feat(access-request): fix issue for deleted custom privileges reopening old closed access requests
2025-05-30 19:19:32 +01:00
972dbac7db Merge pull request #3686 from akhilmhdh/feat/template-k8-issuer
Feat/template k8 issuer
2025-05-30 14:16:49 -04:00
5c0e265703 fix: resolved merge conflict 2025-05-30 18:03:04 +00:00
4efbb8dca6 fix: resolved merge conflict 2025-05-30 17:54:57 +00:00
=
09db9e340b feat: review comments addressed 2025-05-30 17:53:22 +00:00
=
5e3d4edec9 feat: added new lottie 2025-05-30 17:53:22 +00:00
=
86348eb434 feat: completed reptile reviews 2025-05-30 17:53:22 +00:00
=
d31d28666a feat: added slugification to old routes 2025-05-30 17:53:22 +00:00
=
3362ec29cd feat: updated doc for k8s issuer 2025-05-30 17:53:21 +00:00
=
3a0e2bf88b feat: completed frontend changes for new pki templates 2025-05-30 17:53:21 +00:00
=
86862b932c feat: completed backend changes for new pki template 2025-05-30 17:53:21 +00:00
85fefb2a82 feat(access-request): code improvements 2025-05-30 14:53:12 -03:00
858ec2095e feat(access-request): fix issue for deleted custom privileges reopening old closed access requests 2025-05-30 14:17:52 -03:00
a5bb80d2cf Merge pull request #3690 from Infisical/policy-ui-tweak
New policy warning UI
2025-05-30 13:09:28 -04:00
3156057278 New policy warning UI 2025-05-30 13:08:10 -04:00
b5da1d7a6c Merge pull request #3662 from Infisical/ENG-2800
feat(policies): Bypass Approval Rework
2025-05-30 12:00:11 -04:00
8fa8161602 lint 2025-05-30 11:51:15 -04:00
b12aca62ff Update docs/documentation/platform/pki/pki-issuer.mdx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-30 11:44:23 -04:00
c9cd843184 Update docs/documentation/platform/pki/integration-guides/gloo-mesh.mdx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-30 11:44:05 -04:00
47442b16f5 Update docs/documentation/platform/pki/integration-guides/gloo-mesh.mdx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-30 11:43:47 -04:00
0bdb5d3f19 Merge branch 'main' into ENG-2800 2025-05-30 11:42:24 -04:00
cd9ab0024e Gloo mesh docs
Added docs for Gloo Mesh. To be merged after infisical-core PKI updates are made and Issuer is released
2025-05-30 11:41:19 -04:00
f4bed26781 Rename user to username 2025-05-30 11:39:50 -04:00
abedb4b53c feat(instance-management): organizations overview and control 2025-05-30 19:28:16 +04:00
29561d37e9 feat(instance-management): organizations overview and control 2025-05-30 19:28:05 +04:00
75e9ea9c5d reworded docs 2025-05-30 02:11:44 -04:00
d0c10838e1 Added docs 2025-05-30 02:02:14 -04:00
4773336a04 feature: secret scanning pt2 and address initial feedback 2025-05-29 20:40:48 -07:00
4dc587576b Merge pull request #3683 from Infisical/offline-lottie
Add support for offline lottie
2025-05-29 22:22:16 -04:00
7097731539 downgrade dolottie-web to match dotlottie-react 2025-05-29 22:05:19 -04:00
4261281b0f address lint 2025-05-29 21:55:44 -04:00
ff7ff06a6a add dotlottie-web as direct import 2025-05-29 21:55:12 -04:00
6cbeb4ddf9 Add support for offline lottie
In air gapped, lotties won't load because the WASM player is fetched from CDN. This PR bundles the player so we can fetch it directly from file system
2025-05-29 21:46:45 -04:00
5a07c3d1d4 Merge pull request #3682 from Infisical/add-managed-permission
add manage permission for billing
2025-05-29 18:51:35 -04:00
d96e880015 updates billing types else where 2025-05-29 18:26:34 -04:00
4df6c8c2cc Merge pull request #3681 from Infisical/fix/secretPoliciesDeletedBehavior
feat(access-request): fix deleted policy interfering with the newest and valid policy and fix for default values on the creation form
2025-05-29 17:50:52 -04:00
70860e0d26 fix backend lint 2025-05-29 17:48:50 -04:00
3f3b81f9bf fix frontend lint 2025-05-29 17:34:05 -04:00
5181cac9c8 add manage permission for billing 2025-05-29 17:29:06 -04:00
5af39b1a40 feat(access-request): fix deleted policy interfering with the newest and valid policy and fix for default values on the creation form 2025-05-29 17:43:47 -03:00
a9723134f9 Review fixes 2025-05-29 14:43:54 -04:00
fe237fbf4a update program 2025-05-29 14:32:14 -04:00
98e79207cc Merge pull request #3680 from Infisical/misc/pki-improvements
misc: general improvements
2025-05-30 01:48:36 +08:00
26375715e4 Remove log from oidc 2025-05-29 13:12:39 -04:00
5c435f7645 misc: removed updating configuration for internal CAs 2025-05-30 00:09:47 +08:00
f7a9e13209 misc: general improvements 2025-05-29 23:36:31 +08:00
04908edb5b update 2025-05-29 10:28:35 -04:00
e8753a3ce8 Update 2025-05-29 10:16:59 -04:00
1947989ca5 Merge pull request #3668 from Infisical/feat/add-kubernetes-dynamic-secret
feat: add kubernetes dynamic secret
2025-05-29 21:45:22 +08:00
c22e616771 misc: addressed k8 doc changes 2025-05-29 13:34:41 +00:00
40711ac707 misc: addressed comments 2025-05-29 21:15:53 +08:00
a47e6910b1 Merge pull request #3678 from Infisical/daniel/fix-k8s-https-protocol
fix: allow https on gateway k8s hosts
2025-05-29 17:06:20 +04:00
78c4a591a9 requested changes 2025-05-29 16:57:22 +04:00
f6b7717517 fix: allow https on gateway k8s hosts 2025-05-29 16:39:47 +04:00
=
0885620981 feat: removed all tooltip text as it's doc 2025-05-29 17:54:45 +05:30
=
f67511fa19 feat: added max to validation of dynamic secret username template 2025-05-29 17:51:18 +05:30
e6c97510ca feature: secret scanning architecture and github data source (wip) 2025-05-28 22:21:03 -07:00
476671e6ef Merge branch 'main' into ENG-2800 2025-05-28 23:39:57 -04:00
b21a5b6425 Merge pull request #3672 from Infisical/ENG-2843
Improved Key Schema docs + tooltip
2025-05-28 23:39:01 -04:00
66a5691ffd Merge pull request #3675 from Infisical/revert-3546-feat/point-in-time-revamp
Revert "feat(PIT): Point In Time Revamp"
2025-05-28 20:56:38 -04:00
6bdf62d453 Revert "feat(PIT): Point In Time Revamp" 2025-05-28 20:56:04 -04:00
652a48b520 Merge pull request #3674 from Infisical/revert-3671-fix/pitCheckpointCreationBatch
Revert "PIT: fix checkpoint creation to do it in batches to avoid insert fails"
2025-05-28 20:55:56 -04:00
3148c54e18 Revert "PIT: fix checkpoint creation to do it in batches to avoid insert fails" 2025-05-28 20:55:46 -04:00
44367f9149 add boolean filter 2025-05-28 17:06:08 -07:00
286dc39ed2 fix: check for recipients to send project access email 2025-05-28 16:45:43 -07:00
bd4cf64fc6 Merge pull request #3670 from Infisical/ENG-2827
feat(secret-sharing): Require Login for Secrets Shared to Specific Emails
2025-05-28 19:23:26 -04:00
f4e3d7d576 Review fix 2025-05-28 19:22:46 -04:00
8298f9974f Improved Key Schema docs + tooltip 2025-05-28 19:18:09 -04:00
da347e96e1 Merge pull request #3671 from Infisical/fix/pitCheckpointCreationBatch
PIT: fix checkpoint creation to do it in batches to avoid insert fails
2025-05-29 00:17:33 +01:00
5df96234a0 PIT: fix checkpoint creation to do it in batches to avoid insert fails 2025-05-28 20:10:12 -03:00
e78682560c Merge pull request #3546 from Infisical/feat/point-in-time-revamp
feat(PIT): Point In Time Revamp
2025-05-28 18:24:37 -04:00
1602fac5ca PIT: decrese PIT_CHECKPOINT_WINDOW to 1 for deployment 2025-05-28 19:16:19 -03:00
0100bf7032 PIT: decrese PIT_CHECKPOINT_WINDOW to 5 for deployment 2025-05-28 19:13:28 -03:00
e2c49878c6 Merge pull request #3666 from Infisical/feat/add-token-period-support
feat: add token period support for ua
2025-05-28 17:38:59 -04:00
e74117b7fd add link to secret zero section 2025-05-28 17:32:03 -04:00
335aada941 Doc and review tweaks 2025-05-28 17:28:34 -04:00
b949fe06c3 Doc update 2025-05-28 17:25:21 -04:00
28e539c481 PIT: improve wording on the revert button 2025-05-28 17:37:44 -03:00
5c4c881b60 Docs update 2025-05-28 15:50:46 -04:00
8ffb92bfb3 Docs revamp 2025-05-28 15:39:44 -04:00
db9a1726c2 misc: doc improvments 2025-05-29 03:32:19 +08:00
15986633c7 PIT: omit commit version check on rollbacks and reverts 2025-05-28 16:07:42 -03:00
c4809bbb54 PIT: remove reminders from commit history 2025-05-28 15:51:51 -03:00
6305aab0d1 Merge branch 'main' into ENG-2827 2025-05-28 14:44:51 -04:00
456493ff5a feat(secret-sharing): Require Login for Email Sharing 2025-05-28 14:44:27 -04:00
8cfaefcec5 misc: added missing types 2025-05-29 02:43:36 +08:00
e39e80a0e7 misc: added proper propagation of error to logs 2025-05-29 02:38:14 +08:00
8cae92f29e misc: make it work with gateway 2025-05-29 02:01:17 +08:00
918911f2e4 misc: addressed greptile 2025-05-29 01:40:12 +08:00
a1aee45eb2 doc: added docs 2025-05-28 17:36:47 +00:00
5fe93dc35a Merge pull request #3669 from Infisical/update-oidc-logs
Update OIDC logs
2025-05-28 12:34:36 -04:00
5e0e7763a3 Merge pull request #3664 from Infisical/aws-secret-manager-fix
Fix: Update aws secret manager sync to handle constrained iam policies
2025-05-28 09:31:41 -07:00
f663d1d4a6 update log 2025-05-28 12:28:33 -04:00
650f6d9585 feat: add kubernetes dynamic secret 2025-05-29 00:16:01 +08:00
=
90c36eeded feat: reptile requested changes 2025-05-28 19:37:08 +05:30
7994034639 Merge pull request #3660 from Infisical/misc/add-proper-notice-for-non-admin-privilege-upgrade-1
misc: added proper notice for non-admins doing privilege upgrade
2025-05-28 09:59:09 -04:00
48619ed24c Fix lint issue 2025-05-28 08:50:40 -03:00
21fb8df39b Merge branch 'feat/point-in-time-revamp' of https://github.com/Infisical/infisical into feat/point-in-time-revamp 2025-05-28 08:44:16 -03:00
f03a7cc249 PIT: add description to folder versioning 2025-05-28 08:43:32 -03:00
=
b5c3f17ec1 feat: resolved reptile changes 2025-05-28 17:04:43 +05:30
=
99d88f7687 doc: updated doc for dynamic secret to have user template input 2025-05-28 16:09:35 +05:30
=
8e3559828f feat: ui changes for input template 2025-05-28 16:09:12 +05:30
=
93d7c812e7 feat: backend changes for dynamic secret 2025-05-28 16:08:26 +05:30
f2dcbfa91c misc: moved prompt to tooltip 2025-05-28 16:33:14 +08:00
d08510ebe4 misc: add proper grace period for max ttl and descriptive comment 2025-05-28 16:24:23 +08:00
767159bf8f doc: added mention of periodic token to ua section 2025-05-28 08:10:27 +00:00
98457cdb34 misc: addressed frontend lint 2025-05-28 15:40:09 +08:00
8ed8f1200d feat: add token period support for ua 2025-05-28 15:35:10 +08:00
30252c2bcb minor text updates 2025-05-28 00:06:50 -04:00
9687f33122 Merge pull request #3665 from Infisical/allow-machine-to-read-billing
Allow machine identity to read billing
2025-05-27 22:36:29 -04:00
a5282a56c9 allow machine identity to read billing 2025-05-27 22:26:32 -04:00
cc3551c417 fix: update aws secret manager sync to handle constrained iam policies 2025-05-27 18:25:20 -07:00
9e6fe39609 Merge pull request #3663 from Infisical/add-logs-for-oidc-claims
add oidc logs
2025-05-27 21:24:38 -04:00
2bc91c42a7 add oidc logs 2025-05-27 21:18:22 -04:00
accb21f7ed Greptile review fixes 2025-05-27 21:11:19 -04:00
8f010e740f Docs update 2025-05-27 20:50:19 -04:00
f3768c90c7 Merge branch 'main' into ENG-2800 2025-05-27 20:47:13 -04:00
3190ff2eb1 feat(policies): Bypass Approval Rework 2025-05-27 20:46:46 -04:00
c7ec825830 Improve restore buttons on the UI and reconstruct folder children on revert by default 2025-05-27 19:42:31 -03:00
5b7f445e33 PIT: fix for folder commit order on cascade deletion 2025-05-27 18:28:00 -03:00
7fe53ab00e PIT: add batch logic to initializeFolder migration 2025-05-27 11:58:17 -03:00
90c17820fc misc: added proper notice for non-admins doing privilege upgrade 2025-05-27 22:54:50 +08:00
e739b29b3c Merge pull request #3659 from akhilmhdh/feat/cloud-region-flag
feat: added region flag
2025-05-27 10:49:55 -04:00
=
1a89f2a479 feat: added missing validation 2025-05-27 19:17:06 +05:30
78568bffe2 Merge pull request #3655 from Infisical/fix/cliCustomHeadersDoc
Fix CLI custom headers doc tip
2025-05-27 13:08:46 +01:00
=
1407a122b9 feat: added region flag 2025-05-27 15:50:48 +05:30
8168b5faf8 PIT: fix resourceChangeSchema schema 2025-05-26 23:25:05 -03:00
8b9e035bf6 PIT: fix folder update issue 2025-05-26 23:08:01 -03:00
d36d0784ca PIT: Add delete commit for cascade deletion 2025-05-26 21:51:43 -03:00
e69354b546 Merge pull request #3640 from akhilmhdh/feat/redis-sentinel-support
Feat/redis sentinel support
2025-05-26 18:47:15 -04:00
64bd5ddcc8 Merge branch 'main' into feat/redis-sentinel-support 2025-05-26 18:42:12 -04:00
72088634d8 update config file 2025-05-26 18:40:31 -04:00
f3a84f6001 Merge branch 'main' into feat/point-in-time-revamp 2025-05-26 17:28:38 -03:00
13672481a8 Merge branch 'main' into feat/point-in-time-revamp 2025-05-26 17:14:30 -03:00
058394f892 Merge pull request #3583 from Infisical/feat/acme-and-external-ca
feat: acme and external CA for PKI
2025-05-27 03:47:36 +08:00
4f26b43789 License revert 2025-05-26 14:59:01 -04:00
4817eb2fc6 Docs 2025-05-26 14:58:39 -04:00
c623c615a1 Fix lint issue 2025-05-26 14:52:04 -03:00
034a8112b7 Merge branch 'main' into feat/point-in-time-revamp 2025-05-26 14:42:55 -03:00
5fc6fd71ce Fix tag and metadata insert/update logic on revert/rollback and fix tree checkpoint logic to exclude reserved folders 2025-05-26 14:31:05 -03:00
f45c917922 Merge 2025-05-26 12:56:15 -04:00
debef510e4 Merge 2025-05-26 12:54:36 -04:00
be37e27dbf Merge pull request #3647 from Infisical/ENG-2814
feat(secret-sync): 1Password Secret Sync + App Connection
2025-05-26 11:56:56 -04:00
3b62f956e9 Merge pull request #3656 from akhilmhdh/feat/org-id-logger
feat: added missing memberused, identityused in getplan for cloud
2025-05-26 11:32:52 -04:00
=
f49e3788cc feat: added missing memberused, identityused in getplan 2025-05-26 20:59:57 +05:30
1147f87eed lint fixes 2025-05-26 10:56:53 -04:00
995e3254ba comment fix 2025-05-26 10:41:21 -04:00
67d0c53912 Merge 2025-05-26 10:39:51 -04:00
a6fbcb3e01 Merge pull request #3654 from Infisical/approvals-redesign
improve change requests design
2025-05-26 10:35:47 -04:00
db1ca2b89f Merge pull request #3643 from Infisical/ENG-2801
feat(policies): Approval Request Break-Glass Bypass
2025-05-26 10:29:21 -04:00
f91bbe1f31 Merge remote-tracking branch 'origin/main' into feat/acme-and-external-ca 2025-05-26 21:33:23 +08:00
e5f475e8d6 Fix type and lint issues 2025-05-26 09:16:10 -03:00
1e4ca2f48f Fix CLI custom headers doc tip 2025-05-26 08:50:28 -03:00
8d5e7406c3 improve change requests design 2025-05-25 15:53:30 -07:00
3b230dad9a Merge pull request #3653 from akhilmhdh/feat/org-id-logger
feat: small patch on license
2025-05-25 13:38:39 -04:00
=
782bf2cdc9 feat: resolved count fallback 2025-05-25 22:35:16 +05:30
=
982b506eb8 feat: small patch on license 2025-05-25 22:29:12 +05:30
e5bc609a2a PIT: add last commit indicator and remove unnecessary empty folder commit 2025-05-25 12:07:00 -03:00
b812761bdd PIT: hide restore button for last commit 2025-05-25 11:52:28 -03:00
14362dbe6a PIT: general improvements and fixes 2025-05-25 11:00:06 -03:00
b7b90aea33 PIT: general improvements and fixes 2025-05-25 00:12:31 -03:00
14cc21787d checkpoint 2025-05-24 03:50:24 -04:00
8d147867ed Merge pull request #3652 from Infisical/ENG-2817
Update docs and some UI to make Admin SSO bypass more clear
2025-05-24 01:30:07 -04:00
eb4e727922 Update overview.mdx 2025-05-24 01:29:38 -04:00
bb276a0dba review fixes 2025-05-24 01:25:49 -04:00
7cdb015b81 Merge pull request #3633 from Infisical/ENG-2807
feat(secret-sync): Move OCI Vault Sync + OCI App Connection to enterprise
2025-05-23 20:38:53 -04:00
ce446fa723 Small out-of-scope greptile fixes 2025-05-23 20:29:34 -04:00
82f6c9fb58 UI tweaks 2025-05-23 20:18:05 -04:00
6369d13862 Update docs and some UI to make Admin SSO bypass more clear 2025-05-23 18:47:33 -04:00
9f91970be2 Merge pull request #3651 from Infisical/debug-verify-email-log
debug: Add log to help debug verify loop
2025-05-23 15:04:08 -07:00
c7398d924a improvement: make log more cloudwatch friendly 2025-05-23 15:01:09 -07:00
df57364985 ui fix 2025-05-23 17:59:29 -04:00
84322f4f68 temp: add log to help debug verify loop 2025-05-23 14:10:04 -07:00
f551806737 checkpoint 2025-05-23 17:04:16 -04:00
5518df116f Merge pull request #3617 from Infisical/ENG-2797
feat(audit-logs): Audit org updates, project create / update / delete
2025-05-23 13:41:54 -04:00
73c6c076e8 Review fixes 2025-05-23 13:18:56 -04:00
ba2a772247 Merge branch 'main' into ENG-2797 2025-05-23 13:13:43 -04:00
8fbe46256b Merge pull request #3649 from Infisical/ENG-2820
feat(smtp-service): Custom CA Certs
2025-05-23 13:10:03 -04:00
b75bb93d83 Describe fix 2025-05-23 13:08:15 -04:00
db4db04ba6 Doc updates 2025-05-23 13:02:04 -04:00
db44d958d3 Base64 example for docs 2025-05-23 12:41:58 -04:00
12beb06682 Swap to using base64 2025-05-23 12:33:31 -04:00
804f8be07d Review fixes:
- Review envName from endpoint params and derive it
- Use variables in logic blocks
- New function on frontend + memoization
2025-05-23 12:05:38 -04:00
e81991c545 Merge branch 'main' into ENG-2801 2025-05-23 11:18:45 -04:00
28a3bf0b94 Improvement on createCommit function to add changes in batches 2025-05-23 10:59:05 -03:00
5712c24370 Fix migration to initialize pit projects 2025-05-23 10:45:39 -03:00
65bc522ae9 feat(smtp-service): Custom CA Certs 2025-05-23 03:19:45 -04:00
b950e07ad6 fixed firefox bug 2025-05-23 02:06:05 -04:00
498bf8244c Merge branch 'main' into ENG-2807 2025-05-23 01:51:06 -04:00
4a391c7ac2 PIT: add commits to snapshots and improve old role hidding 2025-05-23 01:46:13 -03:00
d49c1e4b72 greptile review fixes 2025-05-22 20:41:35 -04:00
424e4670e5 Merge pull request #3646 from akhilmhdh/feat/org-id-logger
feat: org id logger
2025-05-22 17:11:21 -07:00
5e803e76d7 lint 2025-05-22 20:00:02 -04:00
6648397a64 docs 2025-05-22 19:57:15 -04:00
85edbbcdc3 add org id to missing auth modes 2025-05-22 16:29:40 -07:00
a64f8ac776 feat(secret-sync): 1Password Secret Sync 2025-05-22 17:51:09 -04:00
=
b46a0dfc21 feat: org id logger 2025-05-23 02:03:14 +05:30
95ef113aea doc: updated subscriber and external ca 2025-05-22 19:45:34 +00:00
07bf65b1c3 doc: add external CA doc with reference to Acme CA 2025-05-22 19:28:21 +00:00
12071e4816 misc: updated renewal unit UI 2025-05-23 02:51:09 +08:00
a40d4efa39 misc: updated repeat schedule for auto renewal 2025-05-23 01:28:53 +08:00
6d509d85f4 feat(app-connections): 1Password App Connection 2025-05-22 13:13:47 -04:00
5b200f42a3 misc: update audit logs 2025-05-23 01:01:14 +08:00
64f724ed95 feat: added subscriber cert auto-renewal 2025-05-23 00:53:50 +08:00
b0d5be6221 Merge pull request #3637 from Infisical/ENG-2803
feat(frontend): Persist "perPage" for tables
2025-05-22 12:38:52 -04:00
2b21c9d348 Fix for secret-sync import secrets creating a new version for secrets that did not change 2025-05-22 13:02:38 -03:00
f0a45fb7d8 Review fixes 2025-05-22 11:32:49 -04:00
40398efb06 Merge branch 'main' into ENG-2803 2025-05-22 11:19:29 -04:00
a16c1336fc Merge pull request #3645 from Infisical/fix/secretInputSelectAllFix
Only select all secret value on edit but no view permissions, and keep the select until user starts writting
2025-05-22 12:01:20 -03:00
ef4df9691d Fix license-fns test changes 2025-05-22 11:46:43 -03:00
6a23583391 Only select all secret value on edit but no view permissions, and keep the select until user starts writting 2025-05-22 11:41:35 -03:00
e8d00161eb misc: addressed lint 2025-05-22 21:48:03 +08:00
0a5a073db1 Merge remote-tracking branch 'origin/main' into feat/acme-and-external-ca 2025-05-22 21:35:20 +08:00
0f14685d54 misc: updated doc title 2025-05-22 13:33:15 +00:00
d5888d5bbb misc: updated docs based on review 2025-05-22 13:31:00 +00:00
8ff95aedd5 misc: addressed CA status issue 2025-05-22 20:04:21 +08:00
2b948a18f3 Type fixes and PIT history pagination 2025-05-21 23:43:41 -03:00
4d173ad163 ui and backend improvements 2025-05-21 19:46:47 -04:00
7041b88b9d license revert 2025-05-21 18:44:08 -04:00
f06004370d PIT: address PR suggestions 2025-05-21 19:42:09 -03:00
c1fa344f02 Greptile review fixes 2025-05-21 18:17:01 -04:00
df75b3b8d3 misc: migrated internal CA to use new CA endpoint 2025-05-22 04:21:54 +08:00
e0322c8a7f Merge pull request #3642 from Infisical/misc/add-proper-error-for-bypass-failure
misc: add proper error message for bypass failure
2025-05-21 13:06:21 -07:00
e3725dd3ab merge + final tweaks 2025-05-21 15:46:36 -04:00
dc6a94ccda Merge branch 'main' into ENG-2801 2025-05-21 15:02:21 -04:00
e5229a5377 access request bypass 2025-05-21 15:01:54 -04:00
2e8003ca95 Merge pull request #3628 from Infisical/ENG-2800
feat(policies): Specific permission for bypassing policy
2025-05-21 14:48:36 -04:00
=
04989372b1 feat: resolved ts issue 2025-05-21 22:55:15 +05:30
d185dbb7ff misc: add proper error message for bypass failure 2025-05-22 01:00:13 +08:00
77de085ffc misc: addressed first set of review comments 2025-05-22 00:22:49 +08:00
afcae17e91 Merge pull request #3639 from Infisical/increase-slug-schema
increase name sizes
2025-05-21 08:13:32 -07:00
=
c985690e9a feat: reptile review changes 2025-05-21 20:11:59 +05:30
=
bb2a70b986 feat: updated doc 2025-05-21 20:01:13 +05:30
=
3ac3710273 feat: added sentinel suppor for backend 2025-05-21 20:01:04 +05:30
=
92cb034155 feat: added sentinel sink 2025-05-21 20:00:38 +05:30
2493bbbc97 PIT: fix blocker for deep rollbacks 2025-05-21 09:08:12 -03:00
77b42836e7 Merge remote-tracking branch 'origin/main' into feat/acme-and-external-ca 2025-05-21 19:21:12 +08:00
949615606f misc: moved external pki migration to latest along with column changes 2025-05-21 19:07:20 +08:00
6cd7657e41 lint 2025-05-21 02:44:16 -04:00
38bf5e8b1d increase name sizes 2025-05-21 02:36:10 -04:00
4292cb2a04 Merge pull request #3518 from akhilmhdh/fix/email-ambigious
fix: email casing conflicts
2025-05-20 21:16:16 -07:00
051f53c66e Update bug-bounty.mdx 2025-05-20 18:15:36 -07:00
a6bafb8adc feat(frontend): Persisnt "perPage" for tables 2025-05-20 19:42:32 -04:00
99daa43fc6 delete duplicate accounts 2025-05-20 16:40:21 -07:00
e9e1f4ff5d final touches 2025-05-20 16:53:58 -04:00
13afc9c996 Merge branch 'main' into ENG-2797 2025-05-20 16:48:28 -04:00
67d4da40ec review fixes 2025-05-20 16:48:24 -04:00
27badad3d7 Merge pull request #3614 from Infisical/ldap-target-principal-rotation
feature(secret-rotation): Add support for LDAP target principal self-rotation and UPN
2025-05-20 12:56:52 -07:00
b5e3af6e7d Merge pull request #3636 from Infisical/helm-update-v0.9.3
Update Helm chart to version v0.9.3
2025-05-20 23:55:21 +04:00
280fbdfbb9 Update Helm chart to version v0.9.3 2025-05-20 19:54:55 +00:00
18fc10aaec Merge pull request #3635 from Infisical/daniel/k8s-generator-fix
fix(k8s): disable clustergenerator watching in namespace scoped installations
2025-05-20 23:52:43 +04:00
b20e04bdeb improvements: address feedback 2025-05-20 12:41:37 -07:00
10d14edc20 Update infisicalpushsecret_controller.go 2025-05-20 23:35:43 +04:00
4abdd4216b Merge pull request #3634 from akhilmhdh/feat/license-server-changes
Feat: license server changes
2025-05-20 12:14:43 -07:00
=
332ed68c13 feat: updated message based on feedback 2025-05-21 00:42:06 +05:30
52feabd786 fix(k8s): disable clustergenerator watching in namespace scoped installation 2025-05-20 23:03:58 +04:00
=
d7a99db66a feat: corrected to small subset of error status code 2025-05-21 00:29:36 +05:30
=
fc0bdc25af feat: corrected text 2025-05-21 00:26:02 +05:30
ec633c3e3d greptile review fixes 2025-05-20 14:52:52 -04:00
=
5ffe45eaf5 feat: fixed license server changes in cloud 2025-05-21 00:21:27 +05:30
=
8f795100ea feat: updated cloud functions for quantity change made 2025-05-21 00:21:27 +05:30
1efdb31037 app connection + finishing touches 2025-05-20 13:25:15 -04:00
8d8a3efd77 Merge pull request #3631 from Infisical/daniel/password-resets-fix
fix(password-resets): allow password resets when users don't have a password set
2025-05-20 18:14:07 +04:00
44aa743d56 Type fixes 2025-05-20 11:09:25 -03:00
fefb71dd86 Merge branch 'main' into feat/point-in-time-revamp 2025-05-20 10:52:20 -03:00
677180548b Update auth-password-service.ts 2025-05-20 17:47:47 +04:00
1748052cb0 Merge branch 'main' into feat/point-in-time-revamp 2025-05-20 10:37:41 -03:00
293bea474e Merge pull request #3626 from Infisical/daniel/agent-injector-docs
docs: k8s agent injector
2025-05-20 17:33:15 +04:00
bc4fc9a1ca docs: injector diagram 2025-05-20 17:20:54 +04:00
483850441d Update kubernetes-injector.mdx 2025-05-20 16:58:19 +04:00
4355fd09cc requested changes 2025-05-20 16:57:11 +04:00
1f85d9c486 Merge pull request #3629 from Infisical/misc/add-fortanix-hsm
misc: add docs for Fortanix HSM
2025-05-20 20:51:13 +08:00
c01a98ccf1 Merge pull request #3555 from Infisical/feat/point-in-time-revamp-2710
Feat/point in time revamp 2710
2025-05-20 09:46:08 -03:00
9ea9f90928 PIT: add envID to rollback endpoint 2025-05-20 09:34:43 -03:00
6319f53802 PIT: UI views 2025-05-20 08:22:14 -03:00
75d33820b3 Merge pull request #3630 from Infisical/daniel/agent-exit-code
fix(agent): exit code 1 on fetch secrets error
2025-05-20 14:39:34 +04:00
074446df1f Update agent.go 2025-05-20 14:32:07 +04:00
7ffa0ef8f5 Update deployment.yaml 2025-05-20 12:36:14 +04:00
5250e7c3d5 Update docs/documentation/platform/kms/hsm-integration.mdx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-20 12:34:57 +04:00
2deaa4eff3 misc: final revisions 2025-05-20 06:14:15 +00:00
0b6bc4c1f0 update spend 2025-05-19 21:58:19 -07:00
966294bd0e move OCI Vault Secret Sync to EE 2025-05-19 23:33:58 -04:00
e1dee0678e lint fix 2025-05-19 21:42:25 -04:00
8b25f202fe feat(policies): Specific permission for bypassing policy 2025-05-19 21:28:18 -04:00
abbe7bbd0c Merge pull request #3627 from Infisical/fix-breaking-schema-changes--for-k8s
Allow Hyphens in k8s
2025-05-19 18:26:09 -07:00
565340dc50 fix lint 2025-05-19 18:13:45 -07:00
36c428f152 allow hyphens in host name 2025-05-19 17:45:12 -07:00
f97826ea82 allow hyphens in host name 2025-05-19 17:42:42 -07:00
0f5cbf055c remove limit 2025-05-19 17:27:47 -07:00
1345ff02e3 docs: k8s agent injector 2025-05-20 01:54:17 +04:00
b960ee61d7 Merge pull request #3624 from Infisical/product-select-docs
add product select to docs + change the heading
2025-05-19 17:16:38 -04:00
0b98a214a7 ui tweaks 2025-05-19 17:15:42 -04:00
599c2226e4 Merge pull request #3615 from Infisical/ENG-2787
feat(org): Shared Secret limits for org
2025-05-19 16:26:10 -04:00
8e24a4d3f8 misc: added docs 2025-05-19 20:19:39 +00:00
27486e7600 Merge pull request #3625 from Infisical/ENG-2795
fix secret rollback not tainting form
2025-05-19 16:17:26 -04:00
979e9efbcb fix lint issue 2025-05-19 15:52:50 -04:00
e06b5ecd1b misc: add error handling for already initialized error 2025-05-20 03:44:21 +08:00
1097ec64b2 ui improvements 2025-05-19 15:40:07 -04:00
93fe9929b7 fix secret rollback not tainting form 2025-05-19 15:22:24 -04:00
aca654a993 Update docs/documentation/platform/organization.mdx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-19 13:38:34 -04:00
b5cf237a4a add product select to docs + change the heading 2025-05-19 13:35:35 -04:00
6efb630200 Moved secret share limits to secret share settings 2025-05-19 12:32:22 -04:00
151ede6cbf Merge 2025-05-19 12:20:02 -04:00
931ee1e8da Merge pull request #3616 from Infisical/ENG-2783
feat(secret-sharing): Specify Emails
2025-05-19 12:12:07 -04:00
0401793d38 Changed "token" param to "hash" and used hex encoding for URL 2025-05-19 10:48:58 -04:00
eb31318d39 misc: corrected direct issuance checks for CAs 2025-05-19 21:06:13 +08:00
7f6dcd3afa Merge remote-tracking branch 'origin/main' into feat/acme-and-external-ca 2025-05-19 20:11:48 +08:00
2b4a6ad907 misc: addressed review comments 2025-05-19 20:08:43 +08:00
0613c12508 Merge pull request #3618 from Infisical/fix-bundle-for-old-certs 2025-05-18 13:29:31 -04:00
ba8fcb6891 Merge branch 'feat/acme-and-external-ca' of https://github.com/Infisical/infisical into feat/acme-and-external-ca 2025-05-18 23:57:38 +08:00
c2df8cf869 misc: allow wildcard support for SAN 2025-05-18 23:57:17 +08:00
e383872486 Merge branch 'feat/acme-and-external-ca' of https://github.com/Infisical/infisical into feat/acme-and-external-ca 2025-05-18 15:41:07 +00:00
490c589a44 misc: updated doc reference urls 2025-05-18 15:40:20 +00:00
b358f2dbb7 feat: added subscriber endpoint for fetching active cert 2025-05-18 23:37:23 +08:00
10ed6f6b52 misc: finalized descriptions and api reference 2025-05-18 22:22:00 +08:00
e0f1311f6d doc: added docs for external CA 2025-05-18 13:31:36 +00:00
60d3ffac5d Merge pull request #3620 from Infisical/daniel/k8s-auth-fix
fix(identities-auth): fixed kubernetes auth login
2025-05-17 22:18:52 +04:00
5e192539a1 Update identity-kubernetes-auth-service.ts 2025-05-17 22:13:49 +04:00
021a8ddace Update identity-kubernetes-auth-service.ts 2025-05-17 22:06:51 +04:00
f92aba14cd Merge pull request #3619 from Infisical/fix-padding
Org Products Padding Fix
2025-05-17 13:11:56 -04:00
fdeefcdfcf padding to match similar container 2025-05-17 13:10:15 -04:00
645f70f770 tweaks 2025-05-17 13:05:09 -04:00
923feb81f3 fix bundle endpoint for old certs 2025-05-17 12:44:05 -04:00
1cff92d000 misc: added type assertion 2025-05-18 00:41:27 +08:00
db8f43385d misc: addressed undefined issue 2025-05-18 00:27:52 +08:00
41b45c212d misc: addressed lint issue 2025-05-18 00:17:38 +08:00
ef9269fe10 misc: addressed type issue with date fields 2025-05-18 00:07:03 +08:00
4d95052896 misc: add indicators for errors 2025-05-17 23:52:20 +08:00
260679b01d misc: addressed type 2025-05-17 22:39:43 +08:00
a77cc77be8 explicitly pass values 2025-05-17 03:15:22 -04:00
9bc5c55cd0 revert license 2025-05-17 03:03:44 -04:00
2cbad206b5 feat(audit-logs): Audit org updates, project create / update / delete 2025-05-17 03:02:33 -04:00
16c51af340 review fixes 2025-05-17 02:17:41 -04:00
9fd37ca456 greptile review fixes 2025-05-17 01:51:05 -04:00
56b7328231 misc: addressed type issue and ux improvements 2025-05-17 13:00:04 +08:00
92bebf7d84 feat(secret-sharing): Specify Emails 2025-05-17 00:54:40 -04:00
df053bbae9 Merge pull request #3611 from Infisical/ENG-2782
feat(project): Enable / Disable Secret Sharing
2025-05-16 18:58:39 -04:00
42319f01a7 greptile review fixes 2025-05-16 18:54:57 -04:00
0ea9f9b60d feat(org): Shared Secret limits for org 2025-05-16 18:36:02 -04:00
33ce783fda improvements: address feedback 2025-05-16 15:16:36 -07:00
63c48dc095 feature: add suport for target principal self rotation 2025-05-16 13:15:33 -07:00
edefa7698c misc: addressed comments 2025-05-17 03:42:49 +08:00
16eefe5bac Merge pull request #3610 from Infisical/sso-empty-state
improvement(sso-page): Add empty display for SSO general tab if no SSO is enabled
2025-05-16 10:10:16 -07:00
b984111a73 Merge pull request #3612 from Infisical/daniel/cli-auth-fix
fix(auth): cli auth bug
2025-05-16 17:29:21 +04:00
677ff62b5c fix(auth): cli auth bug 2025-05-16 17:22:18 +04:00
60ea4bb579 Merge branch 'ENG-2661' into feat/acme-and-external-ca 2025-05-16 21:01:32 +08:00
8cc2e08f24 fix(auth): cli auth bug 2025-05-16 16:58:01 +04:00
04d553f052 misc: moved cert issuance to job 2025-05-16 20:38:08 +08:00
d90178f49a Merge pull request #3590 from Infisical/daniel/k8s-auth-gateway
feat(gateway): gateway support for identities
2025-05-16 00:10:16 -07:00
ad50cff184 Update frontend/src/pages/secret-manager/SettingsPage/components/SecretSharingSection/SecretSharingSection.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-16 00:21:30 -04:00
8e43d2a994 feat(project): Enable / Disable Secret Sharing 2025-05-16 00:08:55 -04:00
7074fdbac3 Merge pull request #3609 from Infisical/ENG-2736
feat(org-settings): Option to hide certain products from the sidebar
2025-05-15 23:24:14 -04:00
ef70de1e0b fix: add noopenner to doc link 2025-05-15 20:05:56 -07:00
7e9ee7b5e3 fix: add empty display for sso general tab if no sso is enabled 2025-05-15 20:01:08 -07:00
517c613d05 migration fix 2025-05-15 22:50:09 -04:00
ae8cf06ec6 greptile review fixes 2025-05-15 21:05:39 -04:00
818778ddc5 Update frontend/src/pages/organization/SettingsPage/components/OrgProductSelectSection/OrgProductSelectSection.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-15 21:01:46 -04:00
2e12d9a13c Update frontend/src/pages/organization/SettingsPage/components/OrgGeneralTab/OrgGeneralTab.tsx
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2025-05-15 21:01:30 -04:00
e678c9d1cf remove comments 2025-05-15 20:49:01 -04:00
da0b07ce2a added the other two products and small UI tweaks 2025-05-15 20:45:32 -04:00
3306a9ca69 Merge pull request #3608 from Infisical/key-schema-tweak
allow underscores in key schema
2025-05-15 18:55:45 -04:00
e9af34a6ba Merge pull request #3607 from Infisical/key-schema-doc-tweaks
feat(docs): Key Schema Tweaks
2025-05-15 15:51:23 -07:00
3de8ed169f allow underscores in key schema 2025-05-15 18:49:30 -04:00
d1eb350bdd Merge pull request #3606 from Infisical/oidc-groups-claim-handle-string
improvement(oidc-group-membership-mapping): Update OIDC group claims to handle single group string
2025-05-15 14:47:46 -07:00
0c1ccf7c2e fix: update oidc group claims to handle single group string 2025-05-15 14:39:07 -07:00
d268f52a1c small ui tweak 2025-05-15 16:50:37 -04:00
c519cee5d1 frontend 2025-05-15 16:32:57 -04:00
6d10afc9d2 feat: POC for ACME done 2025-05-16 02:58:05 +08:00
b55a39dd24 Merge pull request #3604 from Infisical/misc/add-identity-support-for-audit-log-retention
misc: add identity support for audit log retention
2025-05-15 09:25:49 -07:00
7b880f85cc misc: add identity support for audit log retention 2025-05-15 16:19:47 +00:00
c7dc595e1a doc overview update 2025-05-15 12:05:06 -04:00
6e494f198b Merge pull request #3603 from Infisical/fix-oci-machine-identity
fix oci machine identity
2025-05-15 11:42:58 -04:00
e1f3eaf1a0 Comment for regex 2025-05-15 11:41:00 -04:00
be26dc9872 requested changes 2025-05-15 16:55:36 +04:00
aaeb6e73fe requested changes 2025-05-15 16:06:20 +04:00
1e11702c58 remove unused import 2025-05-15 01:17:38 -04:00
3b81cdb16e fix oci machine identity 2025-05-15 01:12:33 -04:00
6584166815 Merge pull request #3598 from Infisical/ENG-2755
feat(secret-sync): Secret Key Schema
2025-05-14 23:57:18 -04:00
827cb35194 review fixes 2025-05-14 23:52:05 -04:00
89a6a0ba13 Merge pull request #3602 from Infisical/general-oidc-group-mapping-docs
docs(oidc-group-membership-mapping): Add general OIDC group membership mapping documentation
2025-05-14 16:25:26 -07:00
3b9a50d65d improvements: address feedback 2025-05-14 16:20:50 -07:00
beb7200233 fix: correct overview image links 2025-05-14 14:29:46 -07:00
18e3d132a2 documentation: add general oidc group membership mapping documentation 2025-05-14 14:22:35 -07:00
c2949964b3 misc: added route for acme 2025-05-15 04:18:01 +08:00
=
52f8c6adba feat: updated ui 2025-05-15 00:56:53 +05:30
=
3d2b2cbbab feat: updated logic to have login sso 2025-05-15 00:56:53 +05:30
=
1a82809bd5 fix: resolved lint issue 2025-05-15 00:56:53 +05:30
=
c4f994750d feat: removed merge logic as we now have duplicate fix logic 2025-05-15 00:56:53 +05:30
=
fa7020949c feat: resolve alignment issue and fixed sanitization to top level 2025-05-15 00:56:53 +05:30
=
eca2b3ccde feat: rabbit and reptile feedback changes 2025-05-15 00:56:53 +05:30
=
67fc16ecd3 feat: updated frontend for casing deletion process fix 2025-05-15 00:56:53 +05:30
=
f85add7cca feat: implemented backend updates for email casing issue 2025-05-15 00:56:52 +05:30
3f74d3a80d update import 2025-05-14 13:49:25 -04:00
4a44dc6119 format a frontend file 2025-05-14 13:45:45 -04:00
dd4bc4bc73 more doc tweaks 2025-05-14 13:43:23 -04:00
6188de43e4 Merge pull request #3574 from Infisical/ENG-2706
feat(machine-identities): oracle cloud machine identity auth
2025-05-14 12:56:16 -04:00
36310387e0 Update oci-auth.mdx 2025-05-14 20:44:41 +04:00
43f3960225 Merge branch 'main' into ENG-2706 2025-05-14 12:35:17 -04:00
2f0a442866 Merge pull request #3573 from Infisical/duplicate-project-roles
feature(project/org-roles): Add ability to duplicate org and project roles
2025-05-14 09:23:02 -07:00
7e05bc86a9 improvement: address feedback 2025-05-14 08:58:29 -07:00
b0c4fddf86 review fixes 2025-05-14 11:23:12 -04:00
6faad102e2 misc: added internal CA route 2025-05-14 23:10:10 +08:00
f5578d39a6 Merge pull request #3597 from Infisical/linux-upgrade-docs
add linux upgrade docs
2025-05-14 07:45:01 -07:00
8bfd3913da PIT: add backend logic for deep PIT and rollback 2025-05-14 10:26:41 -03:00
cd028ae133 Update 20250212191958_create-gateway.ts 2025-05-14 16:01:07 +04:00
63c71fabcd fix: migrate project gateway 2025-05-14 16:00:27 +04:00
e90166f1f0 Merge branch 'heads/main' into daniel/k8s-auth-gateway 2025-05-14 14:26:05 +04:00
d1e5ae2d85 misc: updated pki collection lst 2025-05-14 14:45:24 +08:00
5a3fbc0401 Merge pull request #3599 from Infisical/misc/updated-custom-cert-to-be-crt-formawt
misc: update custom cert to be crt format for docs
2025-05-14 14:24:29 +08:00
7c52e000cd misc: update custom cert to be crt format for docs 2025-05-14 14:12:08 +08:00
cccd4ba9e5 doc changes and other tweaks 2025-05-14 01:32:09 -04:00
63f0f8e299 final release 2025-05-14 01:16:42 -04:00
2dd407b136 Merge pull request #3596 from Infisical/pulumi-documentation-update
Adding Pulumi documentation
2025-05-13 22:21:33 -06:00
bae62421ae with stripSchema and filterForSchema 2025-05-13 23:08:54 -04:00
d397002704 Update pulumi.mdx 2025-05-13 20:29:06 -06:00
f5b1f671e3 Update pulumi.mdx 2025-05-13 20:17:23 -06:00
0597c5f0c0 Adding Pulumi documentation 2025-05-13 20:14:08 -06:00
eb3afc8034 Merge pull request #3595 from Infisical/remove-legacy-native-integrations-notice
improvement(native-integrations): Remove legacy badge/banner from native integrations UI
2025-05-13 18:51:03 -07:00
b67457fe93 chore: remove unused imports 2025-05-13 18:46:53 -07:00
75abdbe938 remove legacy badge/banner from native integrations UI 2025-05-13 18:41:14 -07:00
e5555ffd3f misc: addressed cert issuance restriction update 2025-05-14 04:20:00 +08:00
6b95bb0ceb misc: continued migration to new ca structure 2025-05-14 04:08:57 +08:00
7ed96164e5 improvement: address feedback 2025-05-13 12:25:24 -07:00
b0e25a8bd1 Merge remote-tracking branch 'origin/main' into feat/acme-and-external-ca 2025-05-14 00:06:40 +08:00
d483e70748 review fixes 2025-05-13 10:44:28 -04:00
8adf4787b9 Update 20250513081738_remove-gateway-project-link.ts 2025-05-13 15:31:13 +04:00
a12522db55 requested changes 2025-05-13 15:18:23 +04:00
49ab487dc2 Update organization-permissions.mdx 2025-05-13 15:04:21 +04:00
daf0731580 feat(gateways): decouple gateways from projects 2025-05-13 14:59:58 +04:00
4b94848a79 Merge remote-tracking branch 'origin/main' into ENG-2661 2025-05-13 16:35:42 +08:00
879b12002c Merge remote-tracking branch 'origin/main' into ENG-2661 2025-05-13 16:24:55 +08:00
bc93db8603 misc: initial setup 2025-05-13 05:02:15 +08:00
091e521180 review fixes 2025-05-12 14:49:45 -04:00
d5dbc7d7e0 erge branch 'daniel/unblock-dev' into ENG-2706 2025-05-12 10:52:40 -04:00
c43a87947f merge fixes 2025-05-12 10:29:52 -04:00
0af9415aa6 Merge branch 'main' into ENG-2706 2025-05-12 10:18:33 -04:00
fb2b64cb19 feat(identities/k8s): gateway support 2025-05-12 15:19:42 +04:00
ce612877b8 docs 2025-05-09 22:47:20 -04:00
4ad8b468d5 Merge branch 'main' into ENG-2706 2025-05-09 22:37:22 -04:00
5742fc648b add tenancy OCID requirement 2025-05-09 22:33:02 -04:00
aa68a3ef58 feature: add org role duplication 2025-05-09 14:29:18 -07:00
9e1d38a27b Add PIT rollback 2025-05-09 16:03:50 -03:00
78d5bc823d PIT: Add folder reconstruction functions 2025-05-09 09:20:17 -03:00
578a0d7d93 review fixes 2025-05-09 02:54:49 -04:00
a6ee6fc4ea docs, grammar fixes, frontend tweak 2025-05-09 01:29:11 -04:00
b21c17572d block local and private IPs on host header 2025-05-09 00:08:02 -04:00
44c7be54cf improvement: address feedback 2025-05-08 20:22:42 -07:00
45c08b3f09 improvement: improve role not found error display 2025-05-08 20:15:47 -07:00
57a29577fe feature: duplicate project role 2025-05-08 20:10:25 -07:00
2700a96df4 Remove unused package 2025-05-08 21:30:40 -04:00
7457ef3b66 bug fix 2025-05-08 21:24:03 -04:00
806df70dd7 tweaks 2025-05-08 21:03:58 -04:00
8eda358c17 schema gen 2025-05-08 20:59:05 -04:00
b34aabe72b merges 2025-05-08 20:56:04 -04:00
dfaed3c513 oci machine identity auth option 2025-05-08 20:42:58 -04:00
e8d424bbb0 PIT: Add initialization and checkpoint logic 2025-05-08 09:41:01 -03:00
f0c52cc8da Add comments to provide context on this change 2025-05-07 08:43:56 -03:00
e58dbe853e Minor improvements on commits code quality 2025-05-07 08:38:19 -03:00
f493a617b1 Add new commit logic on every folder/secret operation 2025-05-06 18:57:25 -03:00
32a3e1d200 commit 2025-05-06 08:11:50 -03:00
7447d17e94 bug fix, migration fix, frontend tweak 2025-05-05 17:21:59 -04:00
4efa4ad8df merging PKI PRs 2025-05-05 17:06:32 -04:00
c6e56f0380 Stop removing secret/folder versions on projects with version >= 3 2025-05-05 16:43:58 -03:00
d61216ed62 Merge branch 'main' into ENG-2661 2025-05-05 13:33:19 -04:00
x
580de0565b review fixes 2025-04-30 22:24:26 -04:00
x
bbfd4a44c3 small comment changes 2025-04-30 21:41:28 -04:00
x
01e13ca7bd small tweaks 2025-04-30 21:36:11 -04:00
x
f5fdd1a266 Merge branch 'main' into ENG-2661 2025-04-30 21:20:17 -04:00
x
bda74ce13e logging, finalizing some functions, and other tweaks 2025-04-30 20:20:31 -04:00
x
6a973be6f3 cert chain tweaks 2025-04-30 16:26:31 -04:00
x
7f836ed9bc update a few endpoints to not rely on CA 2025-04-30 13:39:50 -04:00
x
4d847ab2cb ca relation removal migration 2025-04-30 12:16:40 -04:00
x
80cecbb937 Merge branch 'main' into ENG-2661 2025-04-30 10:49:36 -04:00
x
8b6c97d5bc checkpoint frontend 2025-04-29 19:26:07 -04:00
x
5641d334cd checkpoint 2025-04-29 19:24:00 -04:00
1190 changed files with 53131 additions and 9415 deletions

View File

@ -107,6 +107,14 @@ INF_APP_CONNECTION_GITHUB_APP_PRIVATE_KEY=
INF_APP_CONNECTION_GITHUB_APP_SLUG=
INF_APP_CONNECTION_GITHUB_APP_ID=
#github radar app connection
INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_ID=
INF_APP_CONNECTION_GITHUB_RADAR_APP_CLIENT_SECRET=
INF_APP_CONNECTION_GITHUB_RADAR_APP_PRIVATE_KEY=
INF_APP_CONNECTION_GITHUB_RADAR_APP_SLUG=
INF_APP_CONNECTION_GITHUB_RADAR_APP_ID=
INF_APP_CONNECTION_GITHUB_RADAR_APP_WEBHOOK_SECRET=
#gcp app connection
INF_APP_CONNECTION_GCP_SERVICE_ACCOUNT_CREDENTIAL=

View File

@ -40,3 +40,4 @@ cli/detect/config/gitleaks.toml:gcp-api-key:578
cli/detect/config/gitleaks.toml:gcp-api-key:579
cli/detect/config/gitleaks.toml:gcp-api-key:581
cli/detect/config/gitleaks.toml:gcp-api-key:582
backend/src/services/smtp/smtp-service.ts:generic-api-key:79

View File

@ -15,8 +15,8 @@ import { mockSmtpServer } from "./mocks/smtp";
import { initDbConnection } from "@app/db";
import { queueServiceFactory } from "@app/queue";
import { keyStoreFactory } from "@app/keystore/keystore";
import { Redis } from "ioredis";
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
import { buildRedisFromConfig } from "@app/lib/config/redis";
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
export default {
@ -30,7 +30,7 @@ export default {
dbRootCert: envConfig.DB_ROOT_CERT
});
const redis = new Redis(envConfig.REDIS_URL);
const redis = buildRedisFromConfig(envConfig);
await redis.flushdb("SYNC");
try {
@ -55,8 +55,8 @@ export default {
});
const smtp = mockSmtpServer();
const queue = queueServiceFactory(envConfig.REDIS_URL, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
const keyStore = keyStoreFactory(envConfig.REDIS_URL);
const queue = queueServiceFactory(envConfig, { dbConnectionUrl: envConfig.DB_CONNECTION_URI });
const keyStore = keyStoreFactory(envConfig);
const hsmModule = initializeHsmModule(envConfig);
hsmModule.initialize();

1877
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -131,6 +131,7 @@
"@aws-sdk/client-elasticache": "^3.637.0",
"@aws-sdk/client-iam": "^3.525.0",
"@aws-sdk/client-kms": "^3.609.0",
"@aws-sdk/client-route-53": "^3.810.0",
"@aws-sdk/client-secrets-manager": "^3.504.0",
"@aws-sdk/client-sts": "^3.600.0",
"@casl/ability": "^6.5.0",
@ -174,6 +175,7 @@
"@slack/oauth": "^3.0.2",
"@slack/web-api": "^7.8.0",
"@ucast/mongo2js": "^1.3.4",
"acme-client": "^5.4.0",
"ajv": "^8.12.0",
"argon2": "^0.31.2",
"aws-sdk": "^2.1553.0",

View File

@ -37,6 +37,7 @@ import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-ap
import { TSecretRotationServiceFactory } from "@app/ee/services/secret-rotation/secret-rotation-service";
import { TSecretRotationV2ServiceFactory } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-service";
import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/secret-scanning-service";
import { TSecretScanningV2ServiceFactory } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
@ -53,6 +54,7 @@ import { ActorAuthMethod, ActorType } from "@app/services/auth/auth-type";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TCertificateServiceFactory } from "@app/services/certificate/certificate-service";
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
import { TCmekServiceFactory } from "@app/services/cmek/cmek-service";
import { TExternalGroupOrgRoleMappingServiceFactory } from "@app/services/external-group-org-role-mapping/external-group-org-role-mapping-service";
@ -68,6 +70,7 @@ import { TIdentityJwtAuthServiceFactory } from "@app/services/identity-jwt-auth/
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
import { TIdentityLdapAuthServiceFactory } from "@app/services/identity-ldap-auth/identity-ldap-auth-service";
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
import { TIdentityOciAuthServiceFactory } from "@app/services/identity-oci-auth/identity-oci-auth-service";
import { TIdentityOidcAuthServiceFactory } from "@app/services/identity-oidc-auth/identity-oidc-auth-service";
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
import { TIdentityTokenAuthServiceFactory } from "@app/services/identity-token-auth/identity-token-auth-service";
@ -81,6 +84,7 @@ import { TOrgAdminServiceFactory } from "@app/services/org-admin/org-admin-servi
import { TPkiAlertServiceFactory } from "@app/services/pki-alert/pki-alert-service";
import { TPkiCollectionServiceFactory } from "@app/services/pki-collection/pki-collection-service";
import { TPkiSubscriberServiceFactory } from "@app/services/pki-subscriber/pki-subscriber-service";
import { TPkiTemplatesServiceFactory } from "@app/services/pki-templates/pki-templates-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";
@ -109,6 +113,7 @@ import { TWorkflowIntegrationServiceFactory } from "@app/services/workflow-integ
declare module "@fastify/request-context" {
interface RequestContextData {
reqId: string;
orgId?: string;
identityAuthInfo?: {
identityId: string;
oidc?: {
@ -209,6 +214,7 @@ declare module "fastify" {
identityGcpAuth: TIdentityGcpAuthServiceFactory;
identityAwsAuth: TIdentityAwsAuthServiceFactory;
identityAzureAuth: TIdentityAzureAuthServiceFactory;
identityOciAuth: TIdentityOciAuthServiceFactory;
identityOidcAuth: TIdentityOidcAuthServiceFactory;
identityJwtAuth: TIdentityJwtAuthServiceFactory;
identityLdapAuth: TIdentityLdapAuthServiceFactory;
@ -266,6 +272,9 @@ declare module "fastify" {
microsoftTeams: TMicrosoftTeamsServiceFactory;
assumePrivileges: TAssumePrivilegeServiceFactory;
githubOrgSync: TGithubOrgSyncServiceFactory;
secretScanningV2: TSecretScanningV2ServiceFactory;
internalCertificateAuthority: TInternalCertificateAuthorityServiceFactory;
pkiTemplate: TPkiTemplatesServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@ -6,6 +6,9 @@ import {
TAccessApprovalPoliciesApprovers,
TAccessApprovalPoliciesApproversInsert,
TAccessApprovalPoliciesApproversUpdate,
TAccessApprovalPoliciesBypassers,
TAccessApprovalPoliciesBypassersInsert,
TAccessApprovalPoliciesBypassersUpdate,
TAccessApprovalPoliciesInsert,
TAccessApprovalPoliciesUpdate,
TAccessApprovalRequests,
@ -68,6 +71,9 @@ import {
TDynamicSecrets,
TDynamicSecretsInsert,
TDynamicSecretsUpdate,
TExternalCertificateAuthorities,
TExternalCertificateAuthoritiesInsert,
TExternalCertificateAuthoritiesUpdate,
TExternalGroupOrgRoleMappings,
TExternalGroupOrgRoleMappingsInsert,
TExternalGroupOrgRoleMappingsUpdate,
@ -119,6 +125,9 @@ import {
TIdentityMetadata,
TIdentityMetadataInsert,
TIdentityMetadataUpdate,
TIdentityOciAuths,
TIdentityOciAuthsInsert,
TIdentityOciAuthsUpdate,
TIdentityOidcAuths,
TIdentityOidcAuthsInsert,
TIdentityOidcAuthsUpdate,
@ -152,6 +161,9 @@ import {
TIntegrations,
TIntegrationsInsert,
TIntegrationsUpdate,
TInternalCertificateAuthorities,
TInternalCertificateAuthoritiesInsert,
TInternalCertificateAuthoritiesUpdate,
TInternalKms,
TInternalKmsInsert,
TInternalKmsUpdate,
@ -267,6 +279,9 @@ import {
TSecretApprovalPoliciesApprovers,
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate,
TSecretApprovalPoliciesBypassers,
TSecretApprovalPoliciesBypassersInsert,
TSecretApprovalPoliciesBypassersUpdate,
TSecretApprovalPoliciesInsert,
TSecretApprovalPoliciesUpdate,
TSecretApprovalRequests,
@ -321,9 +336,24 @@ import {
TSecretRotationV2SecretMappingsInsert,
TSecretRotationV2SecretMappingsUpdate,
TSecrets,
TSecretScanningConfigs,
TSecretScanningConfigsInsert,
TSecretScanningConfigsUpdate,
TSecretScanningDataSources,
TSecretScanningDataSourcesInsert,
TSecretScanningDataSourcesUpdate,
TSecretScanningFindings,
TSecretScanningFindingsInsert,
TSecretScanningFindingsUpdate,
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
TSecretScanningGitRisksUpdate,
TSecretScanningResources,
TSecretScanningResourcesInsert,
TSecretScanningResourcesUpdate,
TSecretScanningScans,
TSecretScanningScansInsert,
TSecretScanningScansUpdate,
TSecretSharing,
TSecretSharingInsert,
TSecretSharingUpdate,
@ -535,6 +565,16 @@ declare module "knex/types/tables" {
TCertificateAuthorityCrlInsert,
TCertificateAuthorityCrlUpdate
>;
[TableName.InternalCertificateAuthority]: KnexOriginal.CompositeTableType<
TInternalCertificateAuthorities,
TInternalCertificateAuthoritiesInsert,
TInternalCertificateAuthoritiesUpdate
>;
[TableName.ExternalCertificateAuthority]: KnexOriginal.CompositeTableType<
TExternalCertificateAuthorities,
TExternalCertificateAuthoritiesInsert,
TExternalCertificateAuthoritiesUpdate
>;
[TableName.Certificate]: KnexOriginal.CompositeTableType<TCertificates, TCertificatesInsert, TCertificatesUpdate>;
[TableName.CertificateTemplate]: KnexOriginal.CompositeTableType<
TCertificateTemplates,
@ -738,6 +778,11 @@ declare module "knex/types/tables" {
TIdentityAzureAuthsInsert,
TIdentityAzureAuthsUpdate
>;
[TableName.IdentityOciAuth]: KnexOriginal.CompositeTableType<
TIdentityOciAuths,
TIdentityOciAuthsInsert,
TIdentityOciAuthsUpdate
>;
[TableName.IdentityOidcAuth]: KnexOriginal.CompositeTableType<
TIdentityOidcAuths,
TIdentityOidcAuthsInsert,
@ -796,6 +841,12 @@ declare module "knex/types/tables" {
TAccessApprovalPoliciesApproversUpdate
>;
[TableName.AccessApprovalPolicyBypasser]: KnexOriginal.CompositeTableType<
TAccessApprovalPoliciesBypassers,
TAccessApprovalPoliciesBypassersInsert,
TAccessApprovalPoliciesBypassersUpdate
>;
[TableName.AccessApprovalRequest]: KnexOriginal.CompositeTableType<
TAccessApprovalRequests,
TAccessApprovalRequestsInsert,
@ -819,6 +870,11 @@ declare module "knex/types/tables" {
TSecretApprovalPoliciesApproversInsert,
TSecretApprovalPoliciesApproversUpdate
>;
[TableName.SecretApprovalPolicyBypasser]: KnexOriginal.CompositeTableType<
TSecretApprovalPoliciesBypassers,
TSecretApprovalPoliciesBypassersInsert,
TSecretApprovalPoliciesBypassersUpdate
>;
[TableName.SecretApprovalRequest]: KnexOriginal.CompositeTableType<
TSecretApprovalRequests,
TSecretApprovalRequestsInsert,
@ -1066,5 +1122,30 @@ declare module "knex/types/tables" {
TGithubOrgSyncConfigsInsert,
TGithubOrgSyncConfigsUpdate
>;
[TableName.SecretScanningDataSource]: KnexOriginal.CompositeTableType<
TSecretScanningDataSources,
TSecretScanningDataSourcesInsert,
TSecretScanningDataSourcesUpdate
>;
[TableName.SecretScanningResource]: KnexOriginal.CompositeTableType<
TSecretScanningResources,
TSecretScanningResourcesInsert,
TSecretScanningResourcesUpdate
>;
[TableName.SecretScanningScan]: KnexOriginal.CompositeTableType<
TSecretScanningScans,
TSecretScanningScansInsert,
TSecretScanningScansUpdate
>;
[TableName.SecretScanningFinding]: KnexOriginal.CompositeTableType<
TSecretScanningFindings,
TSecretScanningFindingsInsert,
TSecretScanningFindingsUpdate
>;
[TableName.SecretScanningConfig]: KnexOriginal.CompositeTableType<
TSecretScanningConfigs,
TSecretScanningConfigsInsert,
TSecretScanningConfigsUpdate
>;
}
}

View File

@ -0,0 +1,44 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.Certificate)) {
const hasProjectIdColumn = await knex.schema.hasColumn(TableName.Certificate, "projectId");
if (!hasProjectIdColumn) {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.string("projectId", 36).nullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
});
await knex.raw(`
UPDATE "${TableName.Certificate}" cert
SET "projectId" = ca."projectId"
FROM "${TableName.CertificateAuthority}" ca
WHERE cert."caId" = ca.id
`);
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.string("projectId").notNullable().alter();
});
}
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.uuid("caId").nullable().alter();
t.uuid("caCertId").nullable().alter();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.Certificate)) {
if (await knex.schema.hasColumn(TableName.Certificate, "projectId")) {
await knex.schema.alterTable(TableName.Certificate, (t) => {
t.dropForeign("projectId");
t.dropColumn("projectId");
});
}
}
// Altering back to notNullable for caId and caCertId will fail
}

View File

@ -0,0 +1,47 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasEmail = await knex.schema.hasColumn(TableName.Users, "email");
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
if (hasEmail) {
await knex(TableName.Users)
.where({ isGhost: false })
.update({
// @ts-expect-error email assume string this is expected
email: knex.raw("lower(email)")
});
}
if (hasUsername) {
await knex.schema.raw(`
CREATE INDEX IF NOT EXISTS ${TableName.Users}_lower_username_idx
ON ${TableName.Users} (LOWER(username))
`);
const duplicatesSubquery = knex(TableName.Users)
.select(knex.raw("lower(username) as lowercase_username"))
.groupBy("lowercase_username")
.having(knex.raw("count(*)"), ">", 1);
// Update usernames to lowercase where they won't create duplicates
await knex(TableName.Users)
.where({ isGhost: false })
.whereRaw("username <> lower(username)") // Only update if not already lowercase
// @ts-expect-error username assume string this is expected
.whereNotIn(knex.raw("lower(username)"), duplicatesSubquery)
.update({
// @ts-expect-error username assume string this is expected
username: knex.raw("lower(username)")
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasUsername = await knex.schema.hasColumn(TableName.Users, "username");
if (hasUsername) {
await knex.schema.raw(`
DROP INDEX IF EXISTS ${TableName.Users}_lower_username_idx
`);
}
}

View File

@ -0,0 +1,30 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityOciAuth))) {
await knex.schema.createTable(TableName.IdentityOciAuth, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
t.jsonb("accessTokenTrustedIps").notNullable();
t.timestamps(true, true, true);
t.uuid("identityId").notNullable().unique();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.string("type").notNullable();
t.string("tenancyOcid").notNullable();
t.string("allowedUsernames").nullable();
});
}
await createOnUpdateTrigger(knex, TableName.IdentityOciAuth);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityOciAuth);
await dropOnUpdateTrigger(knex, TableName.IdentityOciAuth);
}

View File

@ -0,0 +1,25 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
if (!hasGatewayIdColumn) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
table.uuid("gatewayId").nullable();
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasGatewayIdColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "gatewayId");
if (hasGatewayIdColumn) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
table.dropForeign("gatewayId");
table.dropColumn("gatewayId");
});
}
}

View File

@ -0,0 +1,110 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { TableName } from "../schemas";
import { getMigrationEnvConfig } from "./utils/env-config";
import { getMigrationEncryptionServices } from "./utils/services";
// Note(daniel): We aren't dropping tables or columns in this migrations so we can easily rollback if needed.
// In the future we need to drop the projectGatewayId on the dynamic secrets table, and drop the project_gateways table entirely.
const BATCH_SIZE = 500;
export async function up(knex: Knex): Promise<void> {
// eslint-disable-next-line no-param-reassign
knex.replicaNode = () => {
return knex;
};
if (!(await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId"))) {
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
table.uuid("gatewayId").nullable();
table.foreign("gatewayId").references("id").inTable(TableName.Gateway).onDelete("SET NULL");
table.index("gatewayId");
});
const existingDynamicSecretsWithProjectGatewayId = await knex(TableName.DynamicSecret)
.select(selectAllTableCols(TableName.DynamicSecret))
.whereNotNull(`${TableName.DynamicSecret}.projectGatewayId`)
.join(TableName.ProjectGateway, `${TableName.ProjectGateway}.id`, `${TableName.DynamicSecret}.projectGatewayId`)
.whereNotNull(`${TableName.ProjectGateway}.gatewayId`)
.select(
knex.ref("projectId").withSchema(TableName.ProjectGateway).as("projectId"),
knex.ref("gatewayId").withSchema(TableName.ProjectGateway).as("projectGatewayGatewayId")
);
initLogger();
const envConfig = getMigrationEnvConfig();
const keyStore = inMemoryKeyStore();
const { kmsService } = await getMigrationEncryptionServices({ envConfig, keyStore, db: knex });
const updatedDynamicSecrets = await Promise.all(
existingDynamicSecretsWithProjectGatewayId.map(async (existingDynamicSecret) => {
if (!existingDynamicSecret.projectGatewayGatewayId) {
const result = {
...existingDynamicSecret,
gatewayId: null
};
const { projectId, projectGatewayGatewayId, ...rest } = result;
return rest;
}
const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId: existingDynamicSecret.projectId
});
const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
type: KmsDataKey.SecretManager,
projectId: existingDynamicSecret.projectId
});
let decryptedStoredInput = JSON.parse(
secretManagerDecryptor({ cipherTextBlob: Buffer.from(existingDynamicSecret.encryptedInput) }).toString()
) as object;
// We're not removing the existing projectGatewayId from the input so we can easily rollback without having to re-encrypt the input
decryptedStoredInput = {
...decryptedStoredInput,
gatewayId: existingDynamicSecret.projectGatewayGatewayId
};
const encryptedInput = secretManagerEncryptor({
plainText: Buffer.from(JSON.stringify(decryptedStoredInput))
}).cipherTextBlob;
const result = {
...existingDynamicSecret,
encryptedInput,
gatewayId: existingDynamicSecret.projectGatewayGatewayId
};
const { projectId, projectGatewayGatewayId, ...rest } = result;
return rest;
})
);
for (let i = 0; i < updatedDynamicSecrets.length; i += BATCH_SIZE) {
// eslint-disable-next-line no-await-in-loop
await knex(TableName.DynamicSecret)
.insert(updatedDynamicSecrets.slice(i, i + BATCH_SIZE))
.onConflict("id")
.merge();
}
}
}
export async function down(knex: Knex): Promise<void> {
// no re-encryption needed as we keep the old projectGatewayId in the input
if (await knex.schema.hasColumn(TableName.DynamicSecret, "gatewayId")) {
await knex.schema.alterTable(TableName.DynamicSecret, (table) => {
table.dropForeign("gatewayId");
table.dropColumn("gatewayId");
});
}
}

View File

@ -0,0 +1,53 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const columns = await knex.table(TableName.Organization).columnInfo();
await knex.schema.alterTable(TableName.Organization, (t) => {
if (!columns.secretsProductEnabled) {
t.boolean("secretsProductEnabled").defaultTo(true);
}
if (!columns.pkiProductEnabled) {
t.boolean("pkiProductEnabled").defaultTo(true);
}
if (!columns.kmsProductEnabled) {
t.boolean("kmsProductEnabled").defaultTo(true);
}
if (!columns.sshProductEnabled) {
t.boolean("sshProductEnabled").defaultTo(true);
}
if (!columns.scannerProductEnabled) {
t.boolean("scannerProductEnabled").defaultTo(true);
}
if (!columns.shareSecretsProductEnabled) {
t.boolean("shareSecretsProductEnabled").defaultTo(true);
}
});
}
export async function down(knex: Knex): Promise<void> {
const columns = await knex.table(TableName.Organization).columnInfo();
await knex.schema.alterTable(TableName.Organization, (t) => {
if (columns.secretsProductEnabled) {
t.dropColumn("secretsProductEnabled");
}
if (columns.pkiProductEnabled) {
t.dropColumn("pkiProductEnabled");
}
if (columns.kmsProductEnabled) {
t.dropColumn("kmsProductEnabled");
}
if (columns.sshProductEnabled) {
t.dropColumn("sshProductEnabled");
}
if (columns.scannerProductEnabled) {
t.dropColumn("scannerProductEnabled");
}
if (columns.shareSecretsProductEnabled) {
t.dropColumn("shareSecretsProductEnabled");
}
});
}

View File

@ -0,0 +1,21 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
if (!hasSecretSharingColumn) {
await knex.schema.table(TableName.Project, (table) => {
table.boolean("secretSharing").notNullable().defaultTo(true);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasSecretSharingColumn = await knex.schema.hasColumn(TableName.Project, "secretSharing");
if (hasSecretSharingColumn) {
await knex.schema.table(TableName.Project, (table) => {
table.dropColumn("secretSharing");
});
}
}

View File

@ -0,0 +1,35 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
if (!hasLifetimeColumn || !hasViewLimitColumn) {
await knex.schema.alterTable(TableName.Organization, (t) => {
if (!hasLifetimeColumn) {
t.integer("maxSharedSecretLifetime").nullable().defaultTo(2592000); // 30 days in seconds
}
if (!hasViewLimitColumn) {
t.integer("maxSharedSecretViewLimit").nullable();
}
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasLifetimeColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretLifetime");
const hasViewLimitColumn = await knex.schema.hasColumn(TableName.Organization, "maxSharedSecretViewLimit");
if (hasLifetimeColumn || hasViewLimitColumn) {
await knex.schema.alterTable(TableName.Organization, (t) => {
if (hasLifetimeColumn) {
t.dropColumn("maxSharedSecretLifetime");
}
if (hasViewLimitColumn) {
t.dropColumn("maxSharedSecretViewLimit");
}
});
}
}

View File

@ -0,0 +1,43 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
if (!hasEncryptedSalt || !hasAuthorizedEmails) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
// These two columns are only needed when secrets are shared with a specific list of emails
if (!hasEncryptedSalt) {
t.binary("encryptedSalt").nullable();
}
if (!hasAuthorizedEmails) {
t.json("authorizedEmails").nullable();
}
});
}
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
const hasAuthorizedEmails = await knex.schema.hasColumn(TableName.SecretSharing, "authorizedEmails");
if (hasEncryptedSalt || hasAuthorizedEmails) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
if (hasEncryptedSalt) {
t.dropColumn("encryptedSalt");
}
if (hasAuthorizedEmails) {
t.dropColumn("authorizedEmails");
}
});
}
}
}

View File

@ -0,0 +1,107 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "@app/db/utils";
import {
SecretScanningFindingStatus,
SecretScanningScanStatus
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretScanningDataSource))) {
await knex.schema.createTable(TableName.SecretScanningDataSource, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("externalId").index(); // if we need a unique way of identifying this data source from an external resource
t.string("name", 48).notNullable();
t.string("description");
t.string("type").notNullable();
t.jsonb("config").notNullable();
t.binary("encryptedCredentials"); // webhook credentials, etc.
t.uuid("connectionId");
t.boolean("isAutoScanEnabled").defaultTo(true);
t.foreign("connectionId").references("id").inTable(TableName.AppConnection);
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.timestamps(true, true, true);
t.boolean("isDisconnected").notNullable().defaultTo(false);
t.unique(["projectId", "name"]);
});
await createOnUpdateTrigger(knex, TableName.SecretScanningDataSource);
}
if (!(await knex.schema.hasTable(TableName.SecretScanningResource))) {
await knex.schema.createTable(TableName.SecretScanningResource, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("externalId").notNullable();
t.string("name").notNullable();
t.string("type").notNullable();
t.uuid("dataSourceId").notNullable();
t.foreign("dataSourceId").references("id").inTable(TableName.SecretScanningDataSource).onDelete("CASCADE");
t.timestamps(true, true, true);
t.unique(["dataSourceId", "externalId"]);
});
await createOnUpdateTrigger(knex, TableName.SecretScanningResource);
}
if (!(await knex.schema.hasTable(TableName.SecretScanningScan))) {
await knex.schema.createTable(TableName.SecretScanningScan, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("status").notNullable().defaultTo(SecretScanningScanStatus.Queued);
t.string("statusMessage", 1024);
t.string("type").notNullable();
t.uuid("resourceId").notNullable();
t.foreign("resourceId").references("id").inTable(TableName.SecretScanningResource).onDelete("CASCADE");
t.timestamp("createdAt").defaultTo(knex.fn.now());
});
}
if (!(await knex.schema.hasTable(TableName.SecretScanningFinding))) {
await knex.schema.createTable(TableName.SecretScanningFinding, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("dataSourceName").notNullable();
t.string("dataSourceType").notNullable();
t.string("resourceName").notNullable();
t.string("resourceType").notNullable();
t.string("rule").notNullable();
t.string("severity").notNullable();
t.string("status").notNullable().defaultTo(SecretScanningFindingStatus.Unresolved);
t.string("remarks");
t.string("fingerprint").notNullable();
t.jsonb("details").notNullable();
t.string("projectId").notNullable();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.uuid("scanId");
t.foreign("scanId").references("id").inTable(TableName.SecretScanningScan).onDelete("SET NULL");
t.timestamps(true, true, true);
t.unique(["projectId", "fingerprint"]);
});
await createOnUpdateTrigger(knex, TableName.SecretScanningFinding);
}
if (!(await knex.schema.hasTable(TableName.SecretScanningConfig))) {
await knex.schema.createTable(TableName.SecretScanningConfig, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("projectId").notNullable().unique();
t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.string("content", 5000);
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.SecretScanningConfig);
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretScanningFinding);
await dropOnUpdateTrigger(knex, TableName.SecretScanningFinding);
await knex.schema.dropTableIfExists(TableName.SecretScanningScan);
await knex.schema.dropTableIfExists(TableName.SecretScanningResource);
await dropOnUpdateTrigger(knex, TableName.SecretScanningResource);
await knex.schema.dropTableIfExists(TableName.SecretScanningDataSource);
await dropOnUpdateTrigger(knex, TableName.SecretScanningDataSource);
await knex.schema.dropTableIfExists(TableName.SecretScanningConfig);
await dropOnUpdateTrigger(knex, TableName.SecretScanningConfig);
}

View File

@ -0,0 +1,22 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.SecretSync, (t) => {
t.string("name", 64).notNullable().alter();
});
await knex.schema.alterTable(TableName.ProjectTemplates, (t) => {
t.string("name", 64).notNullable().alter();
});
await knex.schema.alterTable(TableName.AppConnection, (t) => {
t.string("name", 64).notNullable().alter();
});
await knex.schema.alterTable(TableName.SecretRotationV2, (t) => {
t.string("name", 64).notNullable().alter();
});
}
export async function down(): Promise<void> {
// No down migration or it will error
}

View File

@ -0,0 +1,205 @@
import slugify from "@sindresorhus/slugify";
import { Knex } from "knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
if (hasCATable && !hasInternalCATable) {
await knex.schema.createTableLike(TableName.InternalCertificateAuthority, TableName.CertificateAuthority, (t) => {
t.uuid("caId").nullable();
});
// @ts-expect-error intentional: migration
await knex(TableName.InternalCertificateAuthority).insert(knex(TableName.CertificateAuthority).select("*"));
await knex(TableName.InternalCertificateAuthority).update("caId", knex.ref("id"));
await knex.schema.alterTable(TableName.InternalCertificateAuthority, (t) => {
t.dropColumn("projectId");
t.dropColumn("requireTemplateForIssuance");
t.dropColumn("createdAt");
t.dropColumn("updatedAt");
t.dropColumn("status");
t.uuid("parentCaId")
.nullable()
.references("id")
.inTable(TableName.CertificateAuthority)
.onDelete("CASCADE")
.alter();
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert).alter();
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE").alter();
});
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.renameColumn("requireTemplateForIssuance", "enableDirectIssuance");
t.string("name").nullable();
});
// prefill name for existing internal CAs and flip enableDirectIssuance
const cas = await knex(TableName.CertificateAuthority).select("id", "friendlyName", "enableDirectIssuance");
await Promise.all(
cas.map((ca) => {
const slugifiedName = ca.friendlyName
? slugify(`${ca.friendlyName.slice(0, 16)}-${alphaNumericNanoId(8)}`)
: slugify(alphaNumericNanoId(12));
return knex(TableName.CertificateAuthority)
.where({ id: ca.id })
.update({ name: slugifiedName, enableDirectIssuance: !ca.enableDirectIssuance });
})
);
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.dropColumn("parentCaId");
t.dropColumn("type");
t.dropColumn("friendlyName");
t.dropColumn("organization");
t.dropColumn("ou");
t.dropColumn("country");
t.dropColumn("province");
t.dropColumn("locality");
t.dropColumn("commonName");
t.dropColumn("dn");
t.dropColumn("serialNumber");
t.dropColumn("maxPathLength");
t.dropColumn("keyAlgorithm");
t.dropColumn("notBefore");
t.dropColumn("notAfter");
t.dropColumn("activeCaCertId");
t.boolean("enableDirectIssuance").notNullable().defaultTo(true).alter();
t.string("name").notNullable().alter();
t.unique(["name", "projectId"]);
});
}
if (!hasExternalCATable) {
await knex.schema.createTable(TableName.ExternalCertificateAuthority, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("type").notNullable();
t.uuid("appConnectionId").nullable();
t.foreign("appConnectionId").references("id").inTable(TableName.AppConnection);
t.uuid("dnsAppConnectionId").nullable();
t.foreign("dnsAppConnectionId").references("id").inTable(TableName.AppConnection);
t.uuid("caId").notNullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
t.binary("credentials");
t.json("configuration");
});
}
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
t.string("ttl").nullable().alter();
t.boolean("enableAutoRenewal").notNullable().defaultTo(false);
t.integer("autoRenewalPeriodInDays");
t.datetime("lastAutoRenewAt");
t.string("lastOperationStatus");
t.text("lastOperationMessage");
t.dateTime("lastOperationAt");
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasCATable = await knex.schema.hasTable(TableName.CertificateAuthority);
const hasExternalCATable = await knex.schema.hasTable(TableName.ExternalCertificateAuthority);
const hasInternalCATable = await knex.schema.hasTable(TableName.InternalCertificateAuthority);
if (hasCATable && hasInternalCATable) {
// First add all columns as nullable
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.uuid("parentCaId").nullable().references("id").inTable(TableName.CertificateAuthority).onDelete("CASCADE");
t.string("type").nullable();
t.string("friendlyName").nullable();
t.string("organization").nullable();
t.string("ou").nullable();
t.string("country").nullable();
t.string("province").nullable();
t.string("locality").nullable();
t.string("commonName").nullable();
t.string("dn").nullable();
t.string("serialNumber").nullable().unique();
t.integer("maxPathLength").nullable();
t.string("keyAlgorithm").nullable();
t.timestamp("notBefore").nullable();
t.timestamp("notAfter").nullable();
t.uuid("activeCaCertId").nullable().references("id").inTable(TableName.CertificateAuthorityCert);
t.renameColumn("enableDirectIssuance", "requireTemplateForIssuance");
t.dropColumn("name");
});
// flip requireTemplateForIssuance for existing internal CAs
const cas = await knex(TableName.CertificateAuthority).select("id", "requireTemplateForIssuance");
await Promise.all(
cas.map((ca) => {
return (
knex(TableName.CertificateAuthority)
.where({ id: ca.id })
// @ts-expect-error intentional: migration
.update({ requireTemplateForIssuance: !ca.requireTemplateForIssuance })
);
})
);
await knex.raw(`
UPDATE ${TableName.CertificateAuthority} ca
SET
type = ica.type,
"friendlyName" = ica."friendlyName",
organization = ica.organization,
ou = ica.ou,
country = ica.country,
province = ica.province,
locality = ica.locality,
"commonName" = ica."commonName",
dn = ica.dn,
"parentCaId" = ica."parentCaId",
"serialNumber" = ica."serialNumber",
"maxPathLength" = ica."maxPathLength",
"keyAlgorithm" = ica."keyAlgorithm",
"notBefore" = ica."notBefore",
"notAfter" = ica."notAfter",
"activeCaCertId" = ica."activeCaCertId"
FROM ${TableName.InternalCertificateAuthority} ica
WHERE ca.id = ica."caId"
`);
await knex.schema.alterTable(TableName.CertificateAuthority, (t) => {
t.string("type").notNullable().alter();
t.string("friendlyName").notNullable().alter();
t.string("organization").notNullable().alter();
t.string("ou").notNullable().alter();
t.string("country").notNullable().alter();
t.string("province").notNullable().alter();
t.string("locality").notNullable().alter();
t.string("commonName").notNullable().alter();
t.string("dn").notNullable().alter();
t.string("keyAlgorithm").notNullable().alter();
t.boolean("requireTemplateForIssuance").notNullable().defaultTo(false).alter();
});
await knex.schema.dropTable(TableName.InternalCertificateAuthority);
}
if (hasExternalCATable) {
await knex.schema.dropTable(TableName.ExternalCertificateAuthority);
}
if (await knex.schema.hasTable(TableName.PkiSubscriber)) {
await knex.schema.alterTable(TableName.PkiSubscriber, (t) => {
t.dropColumn("enableAutoRenewal");
t.dropColumn("autoRenewalPeriodInDays");
t.dropColumn("lastAutoRenewAt");
t.dropColumn("lastOperationStatus");
t.dropColumn("lastOperationMessage");
t.dropColumn("lastOperationAt");
});
}
}

View File

@ -0,0 +1,48 @@
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.AccessApprovalPolicyBypasser))) {
await knex.schema.createTable(TableName.AccessApprovalPolicyBypasser, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("bypasserGroupId").nullable();
t.foreign("bypasserGroupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
t.uuid("bypasserUserId").nullable();
t.foreign("bypasserUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("policyId").notNullable();
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.AccessApprovalPolicyBypasser);
}
if (!(await knex.schema.hasTable(TableName.SecretApprovalPolicyBypasser))) {
await knex.schema.createTable(TableName.SecretApprovalPolicyBypasser, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("bypasserGroupId").nullable();
t.foreign("bypasserGroupId").references("id").inTable(TableName.Groups).onDelete("CASCADE");
t.uuid("bypasserUserId").nullable();
t.foreign("bypasserUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("policyId").notNullable();
t.foreign("policyId").references("id").inTable(TableName.SecretApprovalPolicy).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.SecretApprovalPolicyBypasser);
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretApprovalPolicyBypasser);
await knex.schema.dropTableIfExists(TableName.AccessApprovalPolicyBypasser);
await dropOnUpdateTrigger(knex, TableName.SecretApprovalPolicyBypasser);
await dropOnUpdateTrigger(knex, TableName.AccessApprovalPolicyBypasser);
}

View File

@ -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.DynamicSecret, "usernameTemplate");
if (!hasColumn) {
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
t.string("usernameTemplate").nullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasColumn = await knex.schema.hasColumn(TableName.DynamicSecret, "usernameTemplate");
if (hasColumn) {
await knex.schema.alterTable(TableName.DynamicSecret, (t) => {
t.dropColumn("usernameTemplate");
});
}
}

View File

@ -0,0 +1,139 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
if (!(await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod"))) {
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
t.bigInteger("accessTokenPeriod").defaultTo(0).notNullable();
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.IdentityAccessToken, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityAccessToken, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityUniversalAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityUniversalAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityAwsAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityAwsAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityOidcAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityOidcAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityAzureAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityAzureAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityGcpAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityGcpAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityJwtAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityJwtAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityLdapAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityLdapAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityOciAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityOciAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
if (await knex.schema.hasColumn(TableName.IdentityTokenAuth, "accessTokenPeriod")) {
await knex.schema.alterTable(TableName.IdentityTokenAuth, (t) => {
t.dropColumn("accessTokenPeriod");
});
}
}

View File

@ -0,0 +1,24 @@
import slugify from "@sindresorhus/slugify";
import { Knex } from "knex";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasNameCol = await knex.schema.hasColumn(TableName.CertificateTemplate, "name");
if (hasNameCol) {
const templates = await knex(TableName.CertificateTemplate).select("id", "name");
await Promise.all(
templates.map((el) => {
const slugifiedName = el.name
? slugify(`${el.name.slice(0, 16)}-${alphaNumericNanoId(8)}`)
: slugify(alphaNumericNanoId(12));
return knex(TableName.CertificateTemplate).where({ id: el.id }).update({ name: slugifiedName });
})
);
}
}
export async function down(): Promise<void> {}

View File

@ -0,0 +1,27 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
if (hasEncryptedSalt) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.dropColumn("encryptedSalt");
});
}
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasTable(TableName.SecretSharing)) {
const hasEncryptedSalt = await knex.schema.hasColumn(TableName.SecretSharing, "encryptedSalt");
if (!hasEncryptedSalt) {
await knex.schema.alterTable(TableName.SecretSharing, (t) => {
t.binary("encryptedSalt").nullable();
});
}
}
}

View File

@ -0,0 +1,63 @@
import { Knex } from "knex";
import { ApprovalStatus } from "@app/ee/services/secret-approval-request/secret-approval-request-types";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasPrivilegeDeletedAtColumn = await knex.schema.hasColumn(
TableName.AccessApprovalRequest,
"privilegeDeletedAt"
);
const hasStatusColumn = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "status");
if (!hasPrivilegeDeletedAtColumn) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.timestamp("privilegeDeletedAt").nullable();
});
}
if (!hasStatusColumn) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.string("status").defaultTo(ApprovalStatus.PENDING).notNullable();
});
// Update existing rows based on business logic
// If privilegeId is not null, set status to "approved"
await knex(TableName.AccessApprovalRequest).whereNotNull("privilegeId").update({ status: ApprovalStatus.APPROVED });
// If privilegeId is null and there's a rejected reviewer, set to "rejected"
const rejectedRequestIds = await knex(TableName.AccessApprovalRequestReviewer)
.select("requestId")
.where("status", "rejected")
.distinct()
.pluck("requestId");
if (rejectedRequestIds.length > 0) {
await knex(TableName.AccessApprovalRequest)
.whereNull("privilegeId")
.whereIn("id", rejectedRequestIds)
.update({ status: ApprovalStatus.REJECTED });
}
}
}
export async function down(knex: Knex): Promise<void> {
const hasPrivilegeDeletedAtColumn = await knex.schema.hasColumn(
TableName.AccessApprovalRequest,
"privilegeDeletedAt"
);
const hasStatusColumn = await knex.schema.hasColumn(TableName.AccessApprovalRequest, "status");
if (hasPrivilegeDeletedAtColumn) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.dropColumn("privilegeDeletedAt");
});
}
if (hasStatusColumn) {
await knex.schema.alterTable(TableName.AccessApprovalRequest, (t) => {
t.dropColumn("status");
});
}
}

View File

@ -0,0 +1,23 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasTokenReviewModeColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "tokenReviewMode");
if (!hasTokenReviewModeColumn) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
table.string("tokenReviewMode").notNullable().defaultTo("api");
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasTokenReviewModeColumn = await knex.schema.hasColumn(TableName.IdentityKubernetesAuth, "tokenReviewMode");
if (hasTokenReviewModeColumn) {
await knex.schema.alterTable(TableName.IdentityKubernetesAuth, (table) => {
table.dropColumn("tokenReviewMode");
});
}
}

View File

@ -0,0 +1,26 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const AccessApprovalPoliciesBypassersSchema = z.object({
id: z.string().uuid(),
bypasserGroupId: z.string().uuid().nullable().optional(),
bypasserUserId: z.string().uuid().nullable().optional(),
policyId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TAccessApprovalPoliciesBypassers = z.infer<typeof AccessApprovalPoliciesBypassersSchema>;
export type TAccessApprovalPoliciesBypassersInsert = Omit<
z.input<typeof AccessApprovalPoliciesBypassersSchema>,
TImmutableDBKeys
>;
export type TAccessApprovalPoliciesBypassersUpdate = Partial<
Omit<z.input<typeof AccessApprovalPoliciesBypassersSchema>, TImmutableDBKeys>
>;

View File

@ -18,7 +18,9 @@ export const AccessApprovalRequestsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
requestedByUserId: z.string().uuid(),
note: z.string().nullable().optional()
note: z.string().nullable().optional(),
privilegeDeletedAt: z.date().nullable().optional(),
status: z.string().default("pending")
});
export type TAccessApprovalRequests = z.infer<typeof AccessApprovalRequestsSchema>;

View File

@ -11,25 +11,10 @@ export const CertificateAuthoritiesSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
parentCaId: z.string().uuid().nullable().optional(),
projectId: z.string(),
type: z.string(),
enableDirectIssuance: z.boolean().default(true),
status: z.string(),
friendlyName: z.string(),
organization: z.string(),
ou: z.string(),
country: z.string(),
province: z.string(),
locality: z.string(),
commonName: z.string(),
dn: z.string(),
serialNumber: z.string().nullable().optional(),
maxPathLength: z.number().nullable().optional(),
keyAlgorithm: z.string(),
notBefore: z.date().nullable().optional(),
notAfter: z.date().nullable().optional(),
activeCaCertId: z.string().uuid().nullable().optional(),
requireTemplateForIssuance: z.boolean().default(false)
name: z.string()
});
export type TCertificateAuthorities = z.infer<typeof CertificateAuthoritiesSchema>;

View File

@ -11,7 +11,7 @@ export const CertificatesSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
caId: z.string().uuid(),
caId: z.string().uuid().nullable().optional(),
status: z.string(),
serialNumber: z.string(),
friendlyName: z.string(),
@ -21,11 +21,12 @@ export const CertificatesSchema = z.object({
revokedAt: z.date().nullable().optional(),
revocationReason: z.number().nullable().optional(),
altNames: z.string().nullable().optional(),
caCertId: z.string().uuid(),
caCertId: z.string().uuid().nullable().optional(),
certificateTemplateId: z.string().uuid().nullable().optional(),
keyUsages: z.string().array().nullable().optional(),
extendedKeyUsages: z.string().array().nullable().optional(),
pkiSubscriberId: z.string().uuid().nullable().optional()
pkiSubscriberId: z.string().uuid().nullable().optional(),
projectId: z.string()
});
export type TCertificates = z.infer<typeof CertificatesSchema>;

View File

@ -27,7 +27,9 @@ export const DynamicSecretsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
encryptedInput: zodBuffer,
projectGatewayId: z.string().uuid().nullable().optional()
projectGatewayId: z.string().uuid().nullable().optional(),
gatewayId: z.string().uuid().nullable().optional(),
usernameTemplate: z.string().nullable().optional()
});
export type TDynamicSecrets = z.infer<typeof DynamicSecretsSchema>;

View File

@ -0,0 +1,29 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const ExternalCertificateAuthoritiesSchema = z.object({
id: z.string().uuid(),
type: z.string(),
appConnectionId: z.string().uuid().nullable().optional(),
dnsAppConnectionId: z.string().uuid().nullable().optional(),
caId: z.string().uuid(),
credentials: zodBuffer.nullable().optional(),
configuration: z.unknown().nullable().optional()
});
export type TExternalCertificateAuthorities = z.infer<typeof ExternalCertificateAuthoritiesSchema>;
export type TExternalCertificateAuthoritiesInsert = Omit<
z.input<typeof ExternalCertificateAuthoritiesSchema>,
TImmutableDBKeys
>;
export type TExternalCertificateAuthoritiesUpdate = Partial<
Omit<z.input<typeof ExternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
>;

View File

@ -21,7 +21,8 @@ export const IdentityAccessTokensSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
name: z.string().nullable().optional(),
authMethod: z.string()
authMethod: z.string(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityAccessTokens = z.infer<typeof IdentityAccessTokensSchema>;

View File

@ -19,7 +19,8 @@ export const IdentityAwsAuthsSchema = z.object({
type: z.string(),
stsEndpoint: z.string(),
allowedPrincipalArns: z.string(),
allowedAccountIds: z.string()
allowedAccountIds: z.string(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityAwsAuths = z.infer<typeof IdentityAwsAuthsSchema>;

View File

@ -18,7 +18,8 @@ export const IdentityAzureAuthsSchema = z.object({
identityId: z.string().uuid(),
tenantId: z.string(),
resource: z.string(),
allowedServicePrincipalIds: z.string()
allowedServicePrincipalIds: z.string(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;

View File

@ -19,7 +19,8 @@ export const IdentityGcpAuthsSchema = z.object({
type: z.string(),
allowedServiceAccounts: z.string().nullable().optional(),
allowedProjects: z.string().nullable().optional(),
allowedZones: z.string().nullable().optional()
allowedZones: z.string().nullable().optional(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityGcpAuths = z.infer<typeof IdentityGcpAuthsSchema>;

View File

@ -25,7 +25,8 @@ export const IdentityJwtAuthsSchema = z.object({
boundClaims: z.unknown(),
boundSubject: z.string(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityJwtAuths = z.infer<typeof IdentityJwtAuthsSchema>;

View File

@ -29,7 +29,10 @@ export const IdentityKubernetesAuthsSchema = z.object({
allowedNames: z.string(),
allowedAudience: z.string(),
encryptedKubernetesTokenReviewerJwt: zodBuffer.nullable().optional(),
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional()
encryptedKubernetesCaCertificate: zodBuffer.nullable().optional(),
gatewayId: z.string().uuid().nullable().optional(),
accessTokenPeriod: z.coerce.number().default(0),
tokenReviewMode: z.string().default("api")
});
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;

View File

@ -24,7 +24,8 @@ export const IdentityLdapAuthsSchema = z.object({
searchFilter: z.string(),
allowedFields: z.unknown().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
updatedAt: z.date(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityLdapAuths = z.infer<typeof IdentityLdapAuthsSchema>;

View File

@ -0,0 +1,27 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const IdentityOciAuthsSchema = z.object({
id: z.string().uuid(),
accessTokenTTL: z.coerce.number().default(7200),
accessTokenMaxTTL: z.coerce.number().default(7200),
accessTokenNumUsesLimit: z.coerce.number().default(0),
accessTokenTrustedIps: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid(),
type: z.string(),
tenancyOcid: z.string(),
allowedUsernames: z.string().nullable().optional(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityOciAuths = z.infer<typeof IdentityOciAuthsSchema>;
export type TIdentityOciAuthsInsert = Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>;
export type TIdentityOciAuthsUpdate = Partial<Omit<z.input<typeof IdentityOciAuthsSchema>, TImmutableDBKeys>>;

View File

@ -27,7 +27,8 @@ export const IdentityOidcAuthsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
encryptedCaCertificate: zodBuffer.nullable().optional(),
claimMetadataMapping: z.unknown().nullable().optional()
claimMetadataMapping: z.unknown().nullable().optional(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityOidcAuths = z.infer<typeof IdentityOidcAuthsSchema>;

View File

@ -15,7 +15,8 @@ export const IdentityTokenAuthsSchema = z.object({
accessTokenTrustedIps: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid()
identityId: z.string().uuid(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityTokenAuths = z.infer<typeof IdentityTokenAuthsSchema>;

View File

@ -17,7 +17,8 @@ export const IdentityUniversalAuthsSchema = z.object({
accessTokenTrustedIps: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid()
identityId: z.string().uuid(),
accessTokenPeriod: z.coerce.number().default(0)
});
export type TIdentityUniversalAuths = z.infer<typeof IdentityUniversalAuthsSchema>;

View File

@ -1,5 +1,6 @@
export * from "./access-approval-policies";
export * from "./access-approval-policies-approvers";
export * from "./access-approval-policies-bypassers";
export * from "./access-approval-requests";
export * from "./access-approval-requests-reviewers";
export * from "./api-keys";
@ -20,6 +21,7 @@ export * from "./certificate-templates";
export * from "./certificates";
export * from "./dynamic-secret-leases";
export * from "./dynamic-secrets";
export * from "./external-certificate-authorities";
export * from "./external-group-org-role-mappings";
export * from "./external-kms";
export * from "./gateways";
@ -37,6 +39,7 @@ export * from "./identity-gcp-auths";
export * from "./identity-jwt-auths";
export * from "./identity-kubernetes-auths";
export * from "./identity-metadata";
export * from "./identity-oci-auths";
export * from "./identity-oidc-auths";
export * from "./identity-org-memberships";
export * from "./identity-project-additional-privilege";
@ -48,6 +51,7 @@ export * from "./identity-universal-auths";
export * from "./incident-contacts";
export * from "./integration-auths";
export * from "./integrations";
export * from "./internal-certificate-authorities";
export * from "./internal-kms";
export * from "./kmip-client-certificates";
export * from "./kmip-clients";
@ -89,6 +93,7 @@ export * from "./saml-configs";
export * from "./scim-tokens";
export * from "./secret-approval-policies";
export * from "./secret-approval-policies-approvers";
export * from "./secret-approval-policies-bypassers";
export * from "./secret-approval-request-secret-tags";
export * from "./secret-approval-request-secret-tags-v2";
export * from "./secret-approval-requests";
@ -106,7 +111,12 @@ export * from "./secret-rotation-outputs";
export * from "./secret-rotation-v2-secret-mappings";
export * from "./secret-rotations";
export * from "./secret-rotations-v2";
export * from "./secret-scanning-configs";
export * from "./secret-scanning-data-sources";
export * from "./secret-scanning-findings";
export * from "./secret-scanning-git-risks";
export * from "./secret-scanning-resources";
export * from "./secret-scanning-scans";
export * from "./secret-sharing";
export * from "./secret-snapshot-folders";
export * from "./secret-snapshot-secrets";

View File

@ -0,0 +1,38 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const InternalCertificateAuthoritiesSchema = z.object({
id: z.string().uuid(),
parentCaId: z.string().uuid().nullable().optional(),
type: z.string(),
friendlyName: z.string(),
organization: z.string(),
ou: z.string(),
country: z.string(),
province: z.string(),
locality: z.string(),
commonName: z.string(),
dn: z.string(),
serialNumber: z.string().nullable().optional(),
maxPathLength: z.number().nullable().optional(),
keyAlgorithm: z.string(),
notBefore: z.date().nullable().optional(),
notAfter: z.date().nullable().optional(),
activeCaCertId: z.string().uuid().nullable().optional(),
caId: z.string().uuid()
});
export type TInternalCertificateAuthorities = z.infer<typeof InternalCertificateAuthoritiesSchema>;
export type TInternalCertificateAuthoritiesInsert = Omit<
z.input<typeof InternalCertificateAuthoritiesSchema>,
TImmutableDBKeys
>;
export type TInternalCertificateAuthoritiesUpdate = Partial<
Omit<z.input<typeof InternalCertificateAuthoritiesSchema>, TImmutableDBKeys>
>;

View File

@ -13,6 +13,8 @@ export enum TableName {
SshCertificate = "ssh_certificates",
SshCertificateBody = "ssh_certificate_bodies",
CertificateAuthority = "certificate_authorities",
ExternalCertificateAuthority = "external_certificate_authorities",
InternalCertificateAuthority = "internal_certificate_authorities",
CertificateTemplateEstConfig = "certificate_template_est_configs",
CertificateAuthorityCert = "certificate_authority_certs",
CertificateAuthoritySecret = "certificate_authority_secret",
@ -79,6 +81,7 @@ export enum TableName {
IdentityAzureAuth = "identity_azure_auths",
IdentityUaClientSecret = "identity_ua_client_secrets",
IdentityAwsAuth = "identity_aws_auths",
IdentityOciAuth = "identity_oci_auths",
IdentityOidcAuth = "identity_oidc_auths",
IdentityJwtAuth = "identity_jwt_auths",
IdentityLdapAuth = "identity_ldap_auths",
@ -92,10 +95,12 @@ export enum TableName {
ScimToken = "scim_tokens",
AccessApprovalPolicy = "access_approval_policies",
AccessApprovalPolicyApprover = "access_approval_policies_approvers",
AccessApprovalPolicyBypasser = "access_approval_policies_bypassers",
AccessApprovalRequest = "access_approval_requests",
AccessApprovalRequestReviewer = "access_approval_requests_reviewers",
SecretApprovalPolicy = "secret_approval_policies",
SecretApprovalPolicyApprover = "secret_approval_policies_approvers",
SecretApprovalPolicyBypasser = "secret_approval_policies_bypassers",
SecretApprovalRequest = "secret_approval_requests",
SecretApprovalRequestReviewer = "secret_approval_requests_reviewers",
SecretApprovalRequestSecret = "secret_approval_requests_secrets",
@ -154,7 +159,12 @@ export enum TableName {
MicrosoftTeamsIntegrations = "microsoft_teams_integrations",
ProjectMicrosoftTeamsConfigs = "project_microsoft_teams_configs",
SecretReminderRecipients = "secret_reminder_recipients",
GithubOrgSyncConfig = "github_org_sync_configs"
GithubOrgSyncConfig = "github_org_sync_configs",
SecretScanningDataSource = "secret_scanning_data_sources",
SecretScanningResource = "secret_scanning_resources",
SecretScanningScan = "secret_scanning_scans",
SecretScanningFinding = "secret_scanning_findings",
SecretScanningConfig = "secret_scanning_configs"
}
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
@ -233,6 +243,7 @@ export enum IdentityAuthMethod {
GCP_AUTH = "gcp-auth",
AWS_AUTH = "aws-auth",
AZURE_AUTH = "azure-auth",
OCI_AUTH = "oci-auth",
OIDC_AUTH = "oidc-auth",
JWT_AUTH = "jwt-auth",
LDAP_AUTH = "ldap-auth"
@ -242,7 +253,8 @@ export enum ProjectType {
SecretManager = "secret-manager",
CertificateManager = "cert-manager",
KMS = "kms",
SSH = "ssh"
SSH = "ssh",
SecretScanning = "secret-scanning"
}
export enum ActionProjectType {
@ -250,6 +262,7 @@ export enum ActionProjectType {
CertificateManager = ProjectType.CertificateManager,
KMS = ProjectType.KMS,
SSH = ProjectType.SSH,
SecretScanning = ProjectType.SecretScanning,
// project operations that happen on all types
Any = "any"
}

View File

@ -28,7 +28,15 @@ export const OrganizationsSchema = z.object({
privilegeUpgradeInitiatedByUsername: z.string().nullable().optional(),
privilegeUpgradeInitiatedAt: z.date().nullable().optional(),
bypassOrgAuthEnabled: z.boolean().default(false),
userTokenExpiration: z.string().nullable().optional()
userTokenExpiration: z.string().nullable().optional(),
secretsProductEnabled: z.boolean().default(true).nullable().optional(),
pkiProductEnabled: z.boolean().default(true).nullable().optional(),
kmsProductEnabled: z.boolean().default(true).nullable().optional(),
sshProductEnabled: z.boolean().default(true).nullable().optional(),
scannerProductEnabled: z.boolean().default(true).nullable().optional(),
shareSecretsProductEnabled: z.boolean().default(true).nullable().optional(),
maxSharedSecretLifetime: z.number().default(2592000).nullable().optional(),
maxSharedSecretViewLimit: z.number().nullable().optional()
});
export type TOrganizations = z.infer<typeof OrganizationsSchema>;

View File

@ -16,10 +16,16 @@ export const PkiSubscribersSchema = z.object({
name: z.string(),
commonName: z.string(),
subjectAlternativeNames: z.string().array(),
ttl: z.string(),
ttl: z.string().nullable().optional(),
keyUsages: z.string().array(),
extendedKeyUsages: z.string().array(),
status: z.string()
status: z.string(),
enableAutoRenewal: z.boolean().default(false),
autoRenewalPeriodInDays: z.number().nullable().optional(),
lastAutoRenewAt: z.date().nullable().optional(),
lastOperationStatus: z.string().nullable().optional(),
lastOperationMessage: z.string().nullable().optional(),
lastOperationAt: z.date().nullable().optional()
});
export type TPkiSubscribers = z.infer<typeof PkiSubscribersSchema>;

View File

@ -27,7 +27,8 @@ export const ProjectsSchema = z.object({
description: z.string().nullable().optional(),
type: z.string(),
enforceCapitalization: z.boolean().default(false),
hasDeleteProtection: z.boolean().default(false).nullable().optional()
hasDeleteProtection: z.boolean().default(false).nullable().optional(),
secretSharing: z.boolean().default(true)
});
export type TProjects = z.infer<typeof ProjectsSchema>;

View File

@ -0,0 +1,26 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretApprovalPoliciesBypassersSchema = z.object({
id: z.string().uuid(),
bypasserGroupId: z.string().uuid().nullable().optional(),
bypasserUserId: z.string().uuid().nullable().optional(),
policyId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretApprovalPoliciesBypassers = z.infer<typeof SecretApprovalPoliciesBypassersSchema>;
export type TSecretApprovalPoliciesBypassersInsert = Omit<
z.input<typeof SecretApprovalPoliciesBypassersSchema>,
TImmutableDBKeys
>;
export type TSecretApprovalPoliciesBypassersUpdate = Partial<
Omit<z.input<typeof SecretApprovalPoliciesBypassersSchema>, TImmutableDBKeys>
>;

View File

@ -0,0 +1,20 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretScanningConfigsSchema = z.object({
id: z.string().uuid(),
projectId: z.string(),
content: z.string().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretScanningConfigs = z.infer<typeof SecretScanningConfigsSchema>;
export type TSecretScanningConfigsInsert = Omit<z.input<typeof SecretScanningConfigsSchema>, TImmutableDBKeys>;
export type TSecretScanningConfigsUpdate = Partial<Omit<z.input<typeof SecretScanningConfigsSchema>, TImmutableDBKeys>>;

View File

@ -0,0 +1,32 @@
// 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 SecretScanningDataSourcesSchema = z.object({
id: z.string().uuid(),
externalId: z.string().nullable().optional(),
name: z.string(),
description: z.string().nullable().optional(),
type: z.string(),
config: z.unknown(),
encryptedCredentials: zodBuffer.nullable().optional(),
connectionId: z.string().uuid().nullable().optional(),
isAutoScanEnabled: z.boolean().default(true).nullable().optional(),
projectId: z.string(),
createdAt: z.date(),
updatedAt: z.date(),
isDisconnected: z.boolean().default(false)
});
export type TSecretScanningDataSources = z.infer<typeof SecretScanningDataSourcesSchema>;
export type TSecretScanningDataSourcesInsert = Omit<z.input<typeof SecretScanningDataSourcesSchema>, TImmutableDBKeys>;
export type TSecretScanningDataSourcesUpdate = Partial<
Omit<z.input<typeof SecretScanningDataSourcesSchema>, TImmutableDBKeys>
>;

View File

@ -0,0 +1,32 @@
// 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 SecretScanningFindingsSchema = z.object({
id: z.string().uuid(),
dataSourceName: z.string(),
dataSourceType: z.string(),
resourceName: z.string(),
resourceType: z.string(),
rule: z.string(),
severity: z.string(),
status: z.string().default("unresolved"),
remarks: z.string().nullable().optional(),
fingerprint: z.string(),
details: z.unknown(),
projectId: z.string(),
scanId: z.string().uuid().nullable().optional(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TSecretScanningFindings = z.infer<typeof SecretScanningFindingsSchema>;
export type TSecretScanningFindingsInsert = Omit<z.input<typeof SecretScanningFindingsSchema>, TImmutableDBKeys>;
export type TSecretScanningFindingsUpdate = Partial<
Omit<z.input<typeof SecretScanningFindingsSchema>, TImmutableDBKeys>
>;

View File

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

View File

@ -0,0 +1,21 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretScanningScansSchema = z.object({
id: z.string().uuid(),
status: z.string().default("queued"),
statusMessage: z.string().nullable().optional(),
type: z.string(),
resourceId: z.string().uuid(),
createdAt: z.date().nullable().optional()
});
export type TSecretScanningScans = z.infer<typeof SecretScanningScansSchema>;
export type TSecretScanningScansInsert = Omit<z.input<typeof SecretScanningScansSchema>, TImmutableDBKeys>;
export type TSecretScanningScansUpdate = Partial<Omit<z.input<typeof SecretScanningScansSchema>, TImmutableDBKeys>>;

View File

@ -27,7 +27,8 @@ export const SecretSharingSchema = z.object({
password: z.string().nullable().optional(),
encryptedSecret: zodBuffer.nullable().optional(),
identifier: z.string().nullable().optional(),
type: z.string().default("share")
type: z.string().default("share"),
authorizedEmails: z.unknown().nullable().optional()
});
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;

View File

@ -1,7 +1,7 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { ApproverType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { EnforcementLevel } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -24,10 +24,19 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 approvers")
.min(1, { message: "At least one approver should be provided" }),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
@ -72,7 +81,8 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
.object({ type: z.nativeEnum(ApproverType), id: z.string().nullable().optional() })
.array()
.nullable()
.optional()
.optional(),
bypassers: z.object({ type: z.nativeEnum(BypasserType), id: z.string().nullable().optional() }).array()
})
.array()
.nullable()
@ -143,10 +153,19 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.min(1, { message: "At least one approver should be provided" }),
.min(1, { message: "At least one approver should be provided" })
.max(100, "Cannot have more than 100 approvers"),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).optional(),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
@ -220,6 +239,15 @@ export const registerAccessApprovalPolicyRouter = async (server: FastifyZodProvi
})
.array()
.nullable()
.optional(),
bypassers: z
.object({
type: z.nativeEnum(BypasserType),
id: z.string().nullable().optional(),
name: z.string().nullable().optional()
})
.array()
.nullable()
.optional()
})
})

View File

@ -113,6 +113,7 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
name: z.string(),
approvals: z.number(),
approvers: z.string().array(),
bypassers: z.string().array(),
secretPath: z.string().nullish(),
envId: z.string(),
enforcementLevel: z.string(),
@ -154,7 +155,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
requestId: z.string().trim()
}),
body: z.object({
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED])
status: z.enum([ApprovalStatus.APPROVED, ApprovalStatus.REJECTED]),
bypassReason: z.string().min(10).max(1000).optional()
}),
response: {
200: z.object({
@ -170,7 +172,8 @@ export const registerAccessApprovalRequestRouter = async (server: FastifyZodProv
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
requestId: req.params.requestId,
status: req.body.status
status: req.body.status,
bypassReason: req.body.bypassReason
});
return { review };

View File

@ -1,16 +1,16 @@
import z from "zod";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
CreateOCIConnectionSchema,
SanitizedOCIConnectionSchema,
UpdateOCIConnectionSchema
} from "@app/services/app-connection/oci";
} from "@app/ee/services/app-connections/oci";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { AuthMode } from "@app/services/auth/auth-type";
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
import { registerAppConnectionEndpoints } from "../../../../server/routes/v1/app-connection-routers/app-connection-endpoints";
export const registerOCIConnectionRouter = async (server: FastifyZodProvider) => {
registerAppConnectionEndpoints({

View File

@ -6,6 +6,8 @@ import { ApiDocsTags, DYNAMIC_SECRETS } from "@app/lib/api-docs";
import { daysToMillisecond } from "@app/lib/dates";
import { removeTrailingSlash } from "@app/lib/fn";
import { ms } from "@app/lib/ms";
import { isValidHandleBarTemplate } from "@app/lib/template/validate-handlebars";
import { CharacterType, characterValidator } from "@app/lib/validator/validate-string";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -13,6 +15,28 @@ import { SanitizedDynamicSecretSchema } from "@app/server/routes/sanitizedSchema
import { AuthMode } from "@app/services/auth/auth-type";
import { ResourceMetadataSchema } from "@app/services/resource-metadata/resource-metadata-schema";
const validateUsernameTemplateCharacters = characterValidator([
CharacterType.AlphaNumeric,
CharacterType.Underscore,
CharacterType.Hyphen,
CharacterType.OpenBrace,
CharacterType.CloseBrace,
CharacterType.CloseBracket,
CharacterType.OpenBracket,
CharacterType.Fullstop
]);
const userTemplateSchema = z
.string()
.trim()
.max(255)
.refine((el) => validateUsernameTemplateCharacters(el))
.refine((el) =>
isValidHandleBarTemplate(el, {
allowedExpressions: (val) => ["randomUsername", "unixTimestamp"].includes(val)
})
);
export const registerDynamicSecretRouter = async (server: FastifyZodProvider) => {
server.route({
method: "POST",
@ -52,7 +76,8 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
path: z.string().describe(DYNAMIC_SECRETS.CREATE.path).trim().default("/").transform(removeTrailingSlash),
environmentSlug: z.string().describe(DYNAMIC_SECRETS.CREATE.environmentSlug).min(1),
name: slugSchema({ min: 1, max: 64, field: "Name" }).describe(DYNAMIC_SECRETS.CREATE.name),
metadata: ResourceMetadataSchema.optional()
metadata: ResourceMetadataSchema.optional(),
usernameTemplate: userTemplateSchema.optional()
}),
response: {
200: z.object({
@ -73,39 +98,6 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
}
});
server.route({
method: "POST",
url: "/entra-id/users",
config: {
rateLimit: readLimit
},
schema: {
body: z.object({
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
}),
response: {
200: z
.object({
name: z.string().min(1).describe("The name of the user"),
id: z.string().min(1).describe("The ID of the user"),
email: z.string().min(1).describe("The email of the user")
})
.array()
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
tenantId: req.body.tenantId,
applicationId: req.body.applicationId,
clientSecret: req.body.clientSecret
});
return data;
}
});
server.route({
method: "PATCH",
url: "/:name",
@ -150,7 +142,8 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
})
.nullable(),
newName: z.string().describe(DYNAMIC_SECRETS.UPDATE.newName).optional(),
metadata: ResourceMetadataSchema.optional()
metadata: ResourceMetadataSchema.optional(),
usernameTemplate: userTemplateSchema.nullable().optional()
})
}),
response: {
@ -328,4 +321,37 @@ export const registerDynamicSecretRouter = async (server: FastifyZodProvider) =>
return { leases };
}
});
server.route({
method: "POST",
url: "/entra-id/users",
config: {
rateLimit: readLimit
},
schema: {
body: z.object({
tenantId: z.string().min(1).describe("The tenant ID of the Azure Entra ID"),
applicationId: z.string().min(1).describe("The application ID of the Azure Entra ID App Registration"),
clientSecret: z.string().min(1).describe("The client secret of the Azure Entra ID App Registration")
}),
response: {
200: z
.object({
name: z.string().min(1).describe("The name of the user"),
id: z.string().min(1).describe("The ID of the user"),
email: z.string().min(1).describe("The email of the user")
})
.array()
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const data = await server.services.dynamicSecret.fetchAzureEntraIdUsers({
tenantId: req.body.tenantId,
applicationId: req.body.applicationId,
clientSecret: req.body.clientSecret
});
return data;
}
});
};

View File

@ -121,14 +121,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
identity: z.object({
name: z.string(),
id: z.string()
}),
projects: z
.object({
name: z.string(),
id: z.string(),
slug: z.string()
})
.array()
})
}).array()
})
}
@ -158,17 +151,15 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
identity: z.object({
name: z.string(),
id: z.string()
}),
projectGatewayId: z.string()
})
}).array()
})
}
},
onRequest: verifyAuth([AuthMode.IDENTITY_ACCESS_TOKEN, AuthMode.JWT]),
handler: async (req) => {
const gateways = await server.services.gateway.getProjectGateways({
projectId: req.params.projectId,
projectPermission: req.permission
const gateways = await server.services.gateway.listGateways({
orgPermission: req.permission
});
return { gateways };
}
@ -216,8 +207,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
id: z.string()
}),
body: z.object({
name: slugSchema({ field: "name" }).optional(),
projectIds: z.string().array().optional()
name: slugSchema({ field: "name" }).optional()
}),
response: {
200: z.object({
@ -230,8 +220,7 @@ export const registerGatewayRouter = async (server: FastifyZodProvider) => {
const gateway = await server.services.gateway.updateGatewayById({
orgPermission: req.permission,
id: req.params.id,
name: req.body.name,
projectIds: req.body.projectIds
name: req.body.name
});
return { gateway };
}

View File

@ -47,7 +47,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
200: z.object({ plan: z.any() })
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const plan = await server.services.license.getOrgPlan({
actorId: req.permission.id,

View File

@ -145,7 +145,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
externalId: profile.nameID,
email,
email: email.toLowerCase(),
firstName,
lastName: lastName as string,
relayState: (req.body as { RelayState?: string }).RelayState,

View File

@ -1,7 +1,7 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { ApproverType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { ApproverType, BypasserType } from "@app/ee/services/access-approval-policy/access-approval-policy-types";
import { removeTrailingSlash } from "@app/lib/fn";
import { EnforcementLevel } from "@app/lib/types";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@ -30,10 +30,19 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.min(1, { message: "At least one approver should be provided" }),
.min(1, { message: "At least one approver should be provided" })
.max(100, "Cannot have more than 100 approvers"),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).default(1),
enforcementLevel: z.nativeEnum(EnforcementLevel).default(EnforcementLevel.Hard),
allowedSelfApprovals: z.boolean().default(true)
@ -75,10 +84,19 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
approvers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(ApproverType.Group), id: z.string() }),
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), name: z.string().optional() })
z.object({ type: z.literal(ApproverType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.min(1, { message: "At least one approver should be provided" }),
.min(1, { message: "At least one approver should be provided" })
.max(100, "Cannot have more than 100 approvers"),
bypassers: z
.discriminatedUnion("type", [
z.object({ type: z.literal(BypasserType.Group), id: z.string() }),
z.object({ type: z.literal(BypasserType.User), id: z.string().optional(), username: z.string().optional() })
])
.array()
.max(100, "Cannot have more than 100 bypassers")
.optional(),
approvals: z.number().min(1).default(1),
secretPath: z
.string()
@ -157,6 +175,12 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
id: z.string().nullable().optional(),
type: z.nativeEnum(ApproverType)
})
.array(),
bypassers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(BypasserType)
})
.array()
})
.array()
@ -193,7 +217,14 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(ApproverType),
name: z.string().nullable().optional()
username: z.string().nullable().optional()
})
.array(),
bypassers: z
.object({
id: z.string().nullable().optional(),
type: z.nativeEnum(BypasserType),
username: z.string().nullable().optional()
})
.array()
})

View File

@ -47,6 +47,11 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
userId: z.string().nullable().optional()
})
.array(),
bypassers: z
.object({
userId: z.string().nullable().optional()
})
.array(),
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string(),
deletedAt: z.date().nullish(),
@ -266,6 +271,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
name: z.string(),
approvals: z.number(),
approvers: approvalRequestUser.array(),
bypassers: approvalRequestUser.array(),
secretPath: z.string().optional().nullable(),
enforcementLevel: z.string(),
deletedAt: z.date().nullish(),

View File

@ -2,11 +2,10 @@ import {
CreateOCIVaultSyncSchema,
OCIVaultSyncSchema,
UpdateOCIVaultSyncSchema
} from "@app/services/secret-sync/oci-vault";
} from "@app/ee/services/secret-sync/oci-vault";
import { registerSyncSecretsEndpoints } from "@app/server/routes/v1/secret-sync-routers/secret-sync-endpoints";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { registerSyncSecretsEndpoints } from "./secret-sync-endpoints";
export const registerOCIVaultSyncRouter = async (server: FastifyZodProvider) =>
registerSyncSecretsEndpoints({
destination: SecretSync.OCIVault,

View File

@ -97,7 +97,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
allowCustomKeyIds: z.boolean().describe(SSH_CERTIFICATE_TEMPLATES.CREATE.allowCustomKeyIds)
})
.refine((data) => ms(data.maxTTL) >= ms(data.ttl), {
message: "Max TLL must be greater than or equal to TTL",
message: "Max TTL must be greater than or equal to TTL",
path: ["maxTTL"]
}),
response: {

View File

@ -2,6 +2,10 @@ import {
registerSecretRotationV2Router,
SECRET_ROTATION_REGISTER_ROUTER_MAP
} from "@app/ee/routes/v2/secret-rotation-v2-routers";
import {
registerSecretScanningV2Router,
SECRET_SCANNING_REGISTER_ROUTER_MAP
} from "@app/ee/routes/v2/secret-scanning-v2-routers";
import { registerIdentityProjectAdditionalPrivilegeRouter } from "./identity-project-additional-privilege-router";
import { registerProjectRoleRouter } from "./project-role-router";
@ -31,4 +35,17 @@ export const registerV2EERoutes = async (server: FastifyZodProvider) => {
},
{ prefix: "/secret-rotations" }
);
await server.register(
async (secretScanningV2Router) => {
// register generic secret scanning endpoints
await secretScanningV2Router.register(registerSecretScanningV2Router);
// register service-specific secret scanning endpoints (gitlab/github, etc.)
for await (const [type, router] of Object.entries(SECRET_SCANNING_REGISTER_ROUTER_MAP)) {
await secretScanningV2Router.register(router, { prefix: `data-sources/${type}` });
}
},
{ prefix: "/secret-scanning" }
);
};

View File

@ -5,6 +5,7 @@ import { registerAwsIamUserSecretRotationRouter } from "./aws-iam-user-secret-ro
import { registerAzureClientSecretRotationRouter } from "./azure-client-secret-rotation-router";
import { registerLdapPasswordRotationRouter } from "./ldap-password-rotation-router";
import { registerMsSqlCredentialsRotationRouter } from "./mssql-credentials-rotation-router";
import { registerMySqlCredentialsRotationRouter } from "./mysql-credentials-rotation-router";
import { registerPostgresCredentialsRotationRouter } from "./postgres-credentials-rotation-router";
export * from "./secret-rotation-v2-router";
@ -15,6 +16,7 @@ export const SECRET_ROTATION_REGISTER_ROUTER_MAP: Record<
> = {
[SecretRotation.PostgresCredentials]: registerPostgresCredentialsRotationRouter,
[SecretRotation.MsSqlCredentials]: registerMsSqlCredentialsRotationRouter,
[SecretRotation.MySqlCredentials]: registerMySqlCredentialsRotationRouter,
[SecretRotation.Auth0ClientSecret]: registerAuth0ClientSecretRotationRouter,
[SecretRotation.AzureClientSecret]: registerAzureClientSecretRotationRouter,
[SecretRotation.AwsIamUserSecret]: registerAwsIamUserSecretRotationRouter,

View File

@ -0,0 +1,19 @@
import {
CreateMySqlCredentialsRotationSchema,
MySqlCredentialsRotationSchema,
UpdateMySqlCredentialsRotationSchema
} from "@app/ee/services/secret-rotation-v2/mysql-credentials";
import { SecretRotation } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-enums";
import { SqlCredentialsRotationGeneratedCredentialsSchema } from "@app/ee/services/secret-rotation-v2/shared/sql-credentials";
import { registerSecretRotationEndpoints } from "./secret-rotation-v2-endpoints";
export const registerMySqlCredentialsRotationRouter = async (server: FastifyZodProvider) =>
registerSecretRotationEndpoints({
type: SecretRotation.MySqlCredentials,
server,
responseSchema: MySqlCredentialsRotationSchema,
createSchema: CreateMySqlCredentialsRotationSchema,
updateSchema: UpdateMySqlCredentialsRotationSchema,
generatedCredentialsSchema: SqlCredentialsRotationGeneratedCredentialsSchema
});

View File

@ -6,6 +6,7 @@ import { AwsIamUserSecretRotationListItemSchema } from "@app/ee/services/secret-
import { AzureClientSecretRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/azure-client-secret";
import { LdapPasswordRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/ldap-password";
import { MsSqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mssql-credentials";
import { MySqlCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/mysql-credentials";
import { PostgresCredentialsRotationListItemSchema } from "@app/ee/services/secret-rotation-v2/postgres-credentials";
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
import { ApiDocsTags, SecretRotations } from "@app/lib/api-docs";
@ -16,6 +17,7 @@ import { AuthMode } from "@app/services/auth/auth-type";
const SecretRotationV2OptionsSchema = z.discriminatedUnion("type", [
PostgresCredentialsRotationListItemSchema,
MsSqlCredentialsRotationListItemSchema,
MySqlCredentialsRotationListItemSchema,
Auth0ClientSecretRotationListItemSchema,
AzureClientSecretRotationListItemSchema,
AwsIamUserSecretRotationListItemSchema,

View File

@ -0,0 +1,16 @@
import { registerSecretScanningEndpoints } from "@app/ee/routes/v2/secret-scanning-v2-routers/secret-scanning-v2-endpoints";
import {
CreateGitHubDataSourceSchema,
GitHubDataSourceSchema,
UpdateGitHubDataSourceSchema
} from "@app/ee/services/secret-scanning-v2/github";
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
export const registerGitHubSecretScanningRouter = async (server: FastifyZodProvider) =>
registerSecretScanningEndpoints({
type: SecretScanningDataSource.GitHub,
server,
responseSchema: GitHubDataSourceSchema,
createSchema: CreateGitHubDataSourceSchema,
updateSchema: UpdateGitHubDataSourceSchema
});

View File

@ -0,0 +1,12 @@
import { SecretScanningDataSource } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
import { registerGitHubSecretScanningRouter } from "./github-secret-scanning-router";
export * from "./secret-scanning-v2-router";
export const SECRET_SCANNING_REGISTER_ROUTER_MAP: Record<
SecretScanningDataSource,
(server: FastifyZodProvider) => Promise<void>
> = {
[SecretScanningDataSource.GitHub]: registerGitHubSecretScanningRouter
};

View File

@ -0,0 +1,593 @@
import { z } from "zod";
import { SecretScanningResourcesSchema, SecretScanningScansSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import {
SecretScanningDataSource,
SecretScanningScanStatus
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
import { SECRET_SCANNING_DATA_SOURCE_NAME_MAP } from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-maps";
import {
TSecretScanningDataSource,
TSecretScanningDataSourceInput
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-types";
import { ApiDocsTags, SecretScanningDataSources } from "@app/lib/api-docs";
import { startsWithVowel } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerSecretScanningEndpoints = <
T extends TSecretScanningDataSource,
I extends TSecretScanningDataSourceInput
>({
server,
type,
createSchema,
updateSchema,
responseSchema
}: {
type: SecretScanningDataSource;
server: FastifyZodProvider;
createSchema: z.ZodType<{
name: string;
projectId: string;
connectionId?: string;
config: Partial<I["config"]>;
description?: string | null;
isAutoScanEnabled?: boolean;
}>;
updateSchema: z.ZodType<{
name?: string;
config?: Partial<I["config"]>;
description?: string | null;
isAutoScanEnabled?: boolean;
}>;
responseSchema: z.ZodTypeAny;
}) => {
const sourceType = SECRET_SCANNING_DATA_SOURCE_NAME_MAP[type];
server.route({
method: "GET",
url: `/`,
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `List the ${sourceType} Data Sources for the specified project.`,
querystring: z.object({
projectId: z
.string()
.trim()
.min(1, "Project ID required")
.describe(SecretScanningDataSources.LIST(type).projectId)
}),
response: {
200: z.object({ dataSources: responseSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId }
} = req;
const dataSources = (await server.services.secretScanningV2.listSecretScanningDataSourcesByProjectId(
{ projectId, type },
req.permission
)) as T[];
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_LIST,
metadata: {
type,
count: dataSources.length,
dataSourceIds: dataSources.map((source) => source.id)
}
}
});
return { dataSources };
}
});
server.route({
method: "GET",
url: "/:dataSourceId",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Get the specified ${sourceType} Data Source by ID.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.GET_BY_ID(type).dataSourceId)
}),
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const dataSource = (await server.services.secretScanningV2.findSecretScanningDataSourceById(
{ dataSourceId, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_GET,
metadata: {
dataSourceId,
type
}
}
});
return { dataSource };
}
});
server.route({
method: "GET",
url: `/data-source-name/:dataSourceName`,
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Get the specified ${sourceType} Data Source by name and project ID.`,
params: z.object({
sourceName: z
.string()
.trim()
.min(1, "Data Source name required")
.describe(SecretScanningDataSources.GET_BY_NAME(type).sourceName)
}),
querystring: z.object({
projectId: z
.string()
.trim()
.min(1, "Project ID required")
.describe(SecretScanningDataSources.GET_BY_NAME(type).projectId)
}),
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { sourceName } = req.params;
const { projectId } = req.query;
const dataSource = (await server.services.secretScanningV2.findSecretScanningDataSourceByName(
{ sourceName, projectId, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_GET,
metadata: {
dataSourceId: dataSource.id,
type
}
}
});
return { dataSource };
}
});
server.route({
method: "POST",
url: "/",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Create ${
startsWithVowel(sourceType) ? "an" : "a"
} ${sourceType} Data Source for the specified project.`,
body: createSchema,
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const dataSource = (await server.services.secretScanningV2.createSecretScanningDataSource(
{ ...req.body, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_CREATE,
metadata: {
dataSourceId: dataSource.id,
type,
...req.body
}
}
});
return { dataSource };
}
});
server.route({
method: "PATCH",
url: "/:dataSourceId",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Update the specified ${sourceType} Data Source.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.UPDATE(type).dataSourceId)
}),
body: updateSchema,
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const dataSource = (await server.services.secretScanningV2.updateSecretScanningDataSource(
{ ...req.body, dataSourceId, type },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_UPDATE,
metadata: {
dataSourceId,
type,
...req.body
}
}
});
return { dataSource };
}
});
server.route({
method: "DELETE",
url: `/:dataSourceId`,
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Delete the specified ${sourceType} Data Source.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.DELETE(type).dataSourceId)
}),
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const dataSource = (await server.services.secretScanningV2.deleteSecretScanningDataSource(
{ type, dataSourceId },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_DELETE,
metadata: {
type,
dataSourceId
}
}
});
return { dataSource };
}
});
server.route({
method: "POST",
url: `/:dataSourceId/scan`,
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Trigger a scan for the specified ${sourceType} Data Source.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.SCAN(type).dataSourceId)
}),
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const dataSource = (await server.services.secretScanningV2.triggerSecretScanningDataSourceScan(
{ type, dataSourceId },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_TRIGGER_SCAN,
metadata: {
type,
dataSourceId
}
}
});
return { dataSource };
}
});
server.route({
method: "POST",
url: `/:dataSourceId/resources/:resourceId/scan`,
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Trigger a scan for the specified ${sourceType} Data Source resource.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.SCAN(type).dataSourceId),
resourceId: z.string().uuid().describe(SecretScanningDataSources.SCAN(type).resourceId)
}),
response: {
200: z.object({ dataSource: responseSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId, resourceId } = req.params;
const dataSource = (await server.services.secretScanningV2.triggerSecretScanningDataSourceScan(
{ type, dataSourceId, resourceId },
req.permission
)) as T;
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: dataSource.projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_TRIGGER_SCAN,
metadata: {
type,
dataSourceId,
resourceId
}
}
});
return { dataSource };
}
});
server.route({
method: "GET",
url: "/:dataSourceId/resources",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Get the resources associated with the specified ${sourceType} Data Source by ID.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.LIST_RESOURCES(type).dataSourceId)
}),
response: {
200: z.object({ resources: SecretScanningResourcesSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const { resources, projectId } = await server.services.secretScanningV2.listSecretScanningResourcesByDataSourceId(
{ dataSourceId, type },
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_RESOURCE_LIST,
metadata: {
dataSourceId,
type,
resourceIds: resources.map((resource) => resource.id),
count: resources.length
}
}
});
return { resources };
}
});
server.route({
method: "GET",
url: "/:dataSourceId/scans",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: `Get the scans associated with the specified ${sourceType} Data Source by ID.`,
params: z.object({
dataSourceId: z.string().uuid().describe(SecretScanningDataSources.LIST_SCANS(type).dataSourceId)
}),
response: {
200: z.object({ scans: SecretScanningScansSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { dataSourceId } = req.params;
const { scans, projectId } = await server.services.secretScanningV2.listSecretScanningScansByDataSourceId(
{ dataSourceId, type },
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_SCAN_LIST,
metadata: {
dataSourceId,
type,
count: scans.length
}
}
});
return { scans };
}
});
// not exposed, for UI only
server.route({
method: "GET",
url: "/:dataSourceId/resources-dashboard",
config: {
rateLimit: readLimit
},
schema: {
tags: [ApiDocsTags.SecretScanning],
params: z.object({
dataSourceId: z.string().uuid()
}),
response: {
200: z.object({
resources: SecretScanningResourcesSchema.extend({
lastScannedAt: z.date().nullish(),
lastScanStatus: z.nativeEnum(SecretScanningScanStatus).nullish(),
lastScanStatusMessage: z.string().nullish(),
unresolvedFindings: z.number()
}).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { dataSourceId } = req.params;
const { resources, projectId } =
await server.services.secretScanningV2.listSecretScanningResourcesWithDetailsByDataSourceId(
{ dataSourceId, type },
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_RESOURCE_LIST,
metadata: {
dataSourceId,
type,
resourceIds: resources.map((resource) => resource.id),
count: resources.length
}
}
});
return { resources };
}
});
server.route({
method: "GET",
url: "/:dataSourceId/scans-dashboard",
config: {
rateLimit: readLimit
},
schema: {
tags: [ApiDocsTags.SecretScanning],
params: z.object({
dataSourceId: z.string().uuid()
}),
response: {
200: z.object({
scans: SecretScanningScansSchema.extend({
unresolvedFindings: z.number(),
resolvedFindings: z.number(),
resourceName: z.string()
}).array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const { dataSourceId } = req.params;
const { scans, projectId } =
await server.services.secretScanningV2.listSecretScanningScansWithDetailsByDataSourceId(
{ dataSourceId, type },
req.permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_SCAN_LIST,
metadata: {
dataSourceId,
type,
count: scans.length
}
}
});
return { scans };
}
});
};

View File

@ -0,0 +1,366 @@
import { z } from "zod";
import { SecretScanningConfigsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { GitHubDataSourceListItemSchema } from "@app/ee/services/secret-scanning-v2/github";
import {
SecretScanningFindingStatus,
SecretScanningScanStatus
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
import {
SecretScanningDataSourceSchema,
SecretScanningFindingSchema
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-union-schemas";
import {
ApiDocsTags,
SecretScanningConfigs,
SecretScanningDataSources,
SecretScanningFindings
} from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
const SecretScanningDataSourceOptionsSchema = z.discriminatedUnion("type", [GitHubDataSourceListItemSchema]);
export const registerSecretScanningV2Router = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/data-sources/options",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "List the available Secret Scanning Data Source Options.",
response: {
200: z.object({
dataSourceOptions: SecretScanningDataSourceOptionsSchema.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: () => {
const dataSourceOptions = server.services.secretScanningV2.listSecretScanningDataSourceOptions();
return { dataSourceOptions };
}
});
server.route({
method: "GET",
url: "/data-sources",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "List all the Secret Scanning Data Sources for the specified project.",
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretScanningDataSources.LIST().projectId)
}),
response: {
200: z.object({ dataSources: SecretScanningDataSourceSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const dataSources = await server.services.secretScanningV2.listSecretScanningDataSourcesByProjectId(
{ projectId },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_LIST,
metadata: {
dataSourceIds: dataSources.map((dataSource) => dataSource.id),
count: dataSources.length
}
}
});
return { dataSources };
}
});
server.route({
method: "GET",
url: "/findings",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "List all the Secret Scanning Findings for the specified project.",
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretScanningFindings.LIST.projectId)
}),
response: {
200: z.object({ findings: SecretScanningFindingSchema.array() })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const findings = await server.services.secretScanningV2.listSecretScanningFindingsByProjectId(
projectId,
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_FINDING_LIST,
metadata: {
findingIds: findings.map((finding) => finding.id),
count: findings.length
}
}
});
return { findings };
}
});
server.route({
method: "PATCH",
url: "/findings/:findingId",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "Update the specified Secret Scanning Finding.",
params: z.object({
findingId: z.string().trim().min(1, "Finding ID required").describe(SecretScanningFindings.UPDATE.findingId)
}),
body: z.object({
status: z.nativeEnum(SecretScanningFindingStatus).optional().describe(SecretScanningFindings.UPDATE.status),
remarks: z.string().nullish().describe(SecretScanningFindings.UPDATE.remarks)
}),
response: {
200: z.object({ finding: SecretScanningFindingSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
params: { findingId },
body,
permission
} = req;
const { finding, projectId } = await server.services.secretScanningV2.updateSecretScanningFindingById(
{ findingId, ...body },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_FINDING_UPDATE,
metadata: {
findingId,
...body
}
}
});
return { finding };
}
});
server.route({
method: "GET",
url: "/configs",
config: {
rateLimit: readLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "Get the Secret Scanning Config for the specified project.",
querystring: z.object({
projectId: z
.string()
.trim()
.min(1, "Project ID required")
.describe(SecretScanningConfigs.GET_BY_PROJECT_ID.projectId)
}),
response: {
200: z.object({
config: z.object({ content: z.string().nullish(), projectId: z.string(), updatedAt: z.date().nullish() })
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const config = await server.services.secretScanningV2.findSecretScanningConfigByProjectId(projectId, permission);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_CONFIG_GET
}
});
return { config };
}
});
server.route({
method: "PATCH",
url: "/configs",
config: {
rateLimit: writeLimit
},
schema: {
hide: false,
tags: [ApiDocsTags.SecretScanning],
description: "Update the specified Secret Scanning Configuration.",
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretScanningConfigs.UPDATE.projectId)
}),
body: z.object({
content: z.string().nullable().describe(SecretScanningConfigs.UPDATE.content)
}),
response: {
200: z.object({ config: SecretScanningConfigsSchema })
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId },
body,
permission
} = req;
const config = await server.services.secretScanningV2.upsertSecretScanningConfig(
{ projectId, ...body },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_CONFIG_UPDATE,
metadata: body
}
});
return { config };
}
});
// not exposed, for UI only
server.route({
method: "GET",
url: "/data-sources-dashboard",
config: {
rateLimit: readLimit
},
schema: {
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required")
}),
response: {
200: z.object({
dataSources: z
.intersection(
SecretScanningDataSourceSchema,
z.object({
lastScannedAt: z.date().nullish(),
lastScanStatus: z.nativeEnum(SecretScanningScanStatus).nullish(),
lastScanStatusMessage: z.string().nullish(),
unresolvedFindings: z.number().nullish()
})
)
.array()
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const dataSources = await server.services.secretScanningV2.listSecretScanningDataSourcesWithDetailsByProjectId(
{ projectId },
permission
);
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId,
event: {
type: EventType.SECRET_SCANNING_DATA_SOURCE_LIST,
metadata: {
dataSourceIds: dataSources.map((dataSource) => dataSource.id),
count: dataSources.length
}
}
});
return { dataSources };
}
});
server.route({
method: "GET",
url: "/unresolved-findings-count",
config: {
rateLimit: readLimit
},
schema: {
tags: [ApiDocsTags.SecretScanning],
querystring: z.object({
projectId: z.string().trim().min(1, "Project ID required").describe(SecretScanningFindings.LIST.projectId)
}),
response: {
200: z.object({ unresolvedFindings: z.number() })
}
},
onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => {
const {
query: { projectId },
permission
} = req;
const unresolvedFindings =
await server.services.secretScanningV2.getSecretScanningUnresolvedFindingsCountByProjectId(
projectId,
permission
);
return { unresolvedFindings };
}
});
};

View File

@ -8,3 +8,10 @@ export const accessApprovalPolicyApproverDALFactory = (db: TDbClient) => {
const accessApprovalPolicyApproverOrm = ormify(db, TableName.AccessApprovalPolicyApprover);
return { ...accessApprovalPolicyApproverOrm };
};
export type TAccessApprovalPolicyBypasserDALFactory = ReturnType<typeof accessApprovalPolicyBypasserDALFactory>;
export const accessApprovalPolicyBypasserDALFactory = (db: TDbClient) => {
const accessApprovalPolicyBypasserOrm = ormify(db, TableName.AccessApprovalPolicyBypasser);
return { ...accessApprovalPolicyBypasserOrm };
};

View File

@ -1,11 +1,11 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies } from "@app/db/schemas";
import { AccessApprovalPoliciesSchema, TableName, TAccessApprovalPolicies, TUsers } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { buildFindFilter, ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
import { ApproverType } from "./access-approval-policy-types";
import { ApproverType, BypasserType } from "./access-approval-policy-types";
export type TAccessApprovalPolicyDALFactory = ReturnType<typeof accessApprovalPolicyDALFactory>;
@ -34,9 +34,22 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.leftJoin(TableName.Users, `${TableName.AccessApprovalPolicyApprover}.approverUserId`, `${TableName.Users}.id`)
.leftJoin(
TableName.AccessApprovalPolicyBypasser,
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyBypasser}.policyId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("bypasserUsers"),
`${TableName.AccessApprovalPolicyBypasser}.bypasserUserId`,
`bypasserUsers.id`
)
.select(tx.ref("username").withSchema(TableName.Users).as("approverUsername"))
.select(tx.ref("username").withSchema("bypasserUsers").as("bypasserUsername"))
.select(tx.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(tx.ref("approverGroupId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(tx.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
.select(tx.ref("bypasserGroupId").withSchema(TableName.AccessApprovalPolicyBypasser))
.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"))
@ -129,6 +142,23 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
id,
type: ApproverType.Group
})
},
{
key: "bypasserUserId",
label: "bypassers" as const,
mapper: ({ bypasserUserId: id, bypasserUsername }) => ({
id,
type: BypasserType.User,
name: bypasserUsername
})
},
{
key: "bypasserGroupId",
label: "bypassers" as const,
mapper: ({ bypasserGroupId: id }) => ({
id,
type: BypasserType.Group
})
}
]
});
@ -144,5 +174,28 @@ export const accessApprovalPolicyDALFactory = (db: TDbClient) => {
return softDeletedPolicy;
};
return { ...accessApprovalPolicyOrm, find, findById, softDeleteById };
const findLastValidPolicy = async ({ envId, secretPath }: { envId: string; secretPath: string }, tx?: Knex) => {
try {
const result = await (tx || db.replicaNode())(TableName.AccessApprovalPolicy)
.where(
// eslint-disable-next-line @typescript-eslint/no-misused-promises
buildFindFilter(
{
envId,
secretPath
},
TableName.AccessApprovalPolicy
)
)
.orderBy("deletedAt", "desc")
.orderByRaw(`"deletedAt" IS NULL`)
.first();
return result;
} catch (error) {
throw new DatabaseError({ error, name: "FindLastValidPolicy" });
}
};
return { ...accessApprovalPolicyOrm, find, findById, softDeleteById, findLastValidPolicy };
};

View File

@ -4,6 +4,7 @@ import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { TProjectEnvDALFactory } from "@app/services/project-env/project-env-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
@ -14,10 +15,14 @@ import { TAccessApprovalRequestReviewerDALFactory } from "../access-approval-req
import { ApprovalStatus } from "../access-approval-request/access-approval-request-types";
import { TGroupDALFactory } from "../group/group-dal";
import { TProjectUserAdditionalPrivilegeDALFactory } from "../project-user-additional-privilege/project-user-additional-privilege-dal";
import { TAccessApprovalPolicyApproverDALFactory } from "./access-approval-policy-approver-dal";
import {
TAccessApprovalPolicyApproverDALFactory,
TAccessApprovalPolicyBypasserDALFactory
} from "./access-approval-policy-approver-dal";
import { TAccessApprovalPolicyDALFactory } from "./access-approval-policy-dal";
import {
ApproverType,
BypasserType,
TCreateAccessApprovalPolicy,
TDeleteAccessApprovalPolicy,
TGetAccessApprovalPolicyByIdDTO,
@ -32,12 +37,14 @@ type TAccessApprovalPolicyServiceFactoryDep = {
accessApprovalPolicyDAL: TAccessApprovalPolicyDALFactory;
projectEnvDAL: Pick<TProjectEnvDALFactory, "find" | "findOne">;
accessApprovalPolicyApproverDAL: TAccessApprovalPolicyApproverDALFactory;
accessApprovalPolicyBypasserDAL: TAccessApprovalPolicyBypasserDALFactory;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find">;
groupDAL: TGroupDALFactory;
userDAL: Pick<TUserDALFactory, "find">;
accessApprovalRequestDAL: Pick<TAccessApprovalRequestDALFactory, "update" | "find">;
additionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
accessApprovalRequestReviewerDAL: Pick<TAccessApprovalRequestReviewerDALFactory, "update">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "find">;
};
export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprovalPolicyServiceFactory>;
@ -45,6 +52,7 @@ export type TAccessApprovalPolicyServiceFactory = ReturnType<typeof accessApprov
export const accessApprovalPolicyServiceFactory = ({
accessApprovalPolicyDAL,
accessApprovalPolicyApproverDAL,
accessApprovalPolicyBypasserDAL,
groupDAL,
permissionService,
projectEnvDAL,
@ -52,7 +60,8 @@ export const accessApprovalPolicyServiceFactory = ({
userDAL,
accessApprovalRequestDAL,
additionalPrivilegeDAL,
accessApprovalRequestReviewerDAL
accessApprovalRequestReviewerDAL,
orgMembershipDAL
}: TAccessApprovalPolicyServiceFactoryDep) => {
const createAccessApprovalPolicy = async ({
name,
@ -63,6 +72,7 @@ export const accessApprovalPolicyServiceFactory = ({
actorAuthMethod,
approvals,
approvers,
bypassers,
projectSlug,
environment,
enforcementLevel,
@ -82,7 +92,7 @@ export const accessApprovalPolicyServiceFactory = ({
.filter(Boolean) as string[];
const userApproverNames = approvers
.map((approver) => (approver.type === ApproverType.User ? approver.name : undefined))
.map((approver) => (approver.type === ApproverType.User ? approver.username : undefined))
.filter(Boolean) as string[];
if (!groupApprovers && approvals > userApprovers.length + userApproverNames.length)
@ -147,6 +157,44 @@ export const accessApprovalPolicyServiceFactory = ({
.map((user) => user.id);
verifyAllApprovers.push(...verifyGroupApprovers);
let groupBypassers: string[] = [];
let bypasserUserIds: string[] = [];
if (bypassers && bypassers.length) {
groupBypassers = bypassers
.filter((bypasser) => bypasser.type === BypasserType.Group)
.map((bypasser) => bypasser.id) as string[];
const userBypassers = bypassers
.filter((bypasser) => bypasser.type === BypasserType.User)
.map((bypasser) => bypasser.id)
.filter(Boolean) as string[];
const userBypasserNames = bypassers
.map((bypasser) => (bypasser.type === BypasserType.User ? bypasser.username : undefined))
.filter(Boolean) as string[];
bypasserUserIds = userBypassers;
if (userBypasserNames.length) {
const bypasserUsers = await userDAL.find({
$in: {
username: userBypasserNames
}
});
const bypasserNamesFromDb = bypasserUsers.map((user) => user.username);
const invalidUsernames = userBypasserNames.filter((username) => !bypasserNamesFromDb.includes(username));
if (invalidUsernames.length) {
throw new BadRequestError({
message: `Invalid bypasser user: ${invalidUsernames.join(", ")}`
});
}
bypasserUserIds = bypasserUserIds.concat(bypasserUsers.map((user) => user.id));
}
}
const accessApproval = await accessApprovalPolicyDAL.transaction(async (tx) => {
const doc = await accessApprovalPolicyDAL.create(
{
@ -159,6 +207,7 @@ export const accessApprovalPolicyServiceFactory = ({
},
tx
);
if (approverUserIds.length) {
await accessApprovalPolicyApproverDAL.insertMany(
approverUserIds.map((userId) => ({
@ -179,8 +228,29 @@ export const accessApprovalPolicyServiceFactory = ({
);
}
if (bypasserUserIds.length) {
await accessApprovalPolicyBypasserDAL.insertMany(
bypasserUserIds.map((userId) => ({
bypasserUserId: userId,
policyId: doc.id
})),
tx
);
}
if (groupBypassers.length) {
await accessApprovalPolicyBypasserDAL.insertMany(
groupBypassers.map((groupId) => ({
bypasserGroupId: groupId,
policyId: doc.id
})),
tx
);
}
return doc;
});
return { ...accessApproval, environment: env, projectId: project.id };
};
@ -211,6 +281,7 @@ export const accessApprovalPolicyServiceFactory = ({
const updateAccessApprovalPolicy = async ({
policyId,
approvers,
bypassers,
secretPath,
name,
actorId,
@ -231,15 +302,15 @@ export const accessApprovalPolicyServiceFactory = ({
.filter(Boolean) as string[];
const userApproverNames = approvers
.map((approver) => (approver.type === ApproverType.User ? approver.name : undefined))
.map((approver) => (approver.type === ApproverType.User ? approver.username : undefined))
.filter(Boolean) as string[];
const accessApprovalPolicy = await accessApprovalPolicyDAL.findById(policyId);
const currentAppovals = approvals || accessApprovalPolicy.approvals;
const currentApprovals = approvals || accessApprovalPolicy.approvals;
if (
groupApprovers?.length === 0 &&
userApprovers &&
currentAppovals > userApprovers.length + userApproverNames.length
currentApprovals > userApprovers.length + userApproverNames.length
) {
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
}
@ -258,6 +329,78 @@ export const accessApprovalPolicyServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
let groupBypassers: string[] = [];
let bypasserUserIds: string[] = [];
if (bypassers && bypassers.length) {
groupBypassers = bypassers
.filter((bypasser) => bypasser.type === BypasserType.Group)
.map((bypasser) => bypasser.id) as string[];
groupBypassers = [...new Set(groupBypassers)];
const userBypassers = bypassers
.filter((bypasser) => bypasser.type === BypasserType.User)
.map((bypasser) => bypasser.id)
.filter(Boolean) as string[];
const userBypasserNames = bypassers
.map((bypasser) => (bypasser.type === BypasserType.User ? bypasser.username : undefined))
.filter(Boolean) as string[];
bypasserUserIds = userBypassers;
if (userBypasserNames.length) {
const bypasserUsers = await userDAL.find({
$in: {
username: userBypasserNames
}
});
const bypasserNamesFromDb = bypasserUsers.map((user) => user.username);
const invalidUsernames = userBypasserNames.filter((username) => !bypasserNamesFromDb.includes(username));
if (invalidUsernames.length) {
throw new BadRequestError({
message: `Invalid bypasser user: ${invalidUsernames.join(", ")}`
});
}
bypasserUserIds = [...new Set(bypasserUserIds.concat(bypasserUsers.map((user) => user.id)))];
}
// Validate user bypassers
if (bypasserUserIds.length > 0) {
const orgMemberships = await orgMembershipDAL.find({
$in: { userId: bypasserUserIds },
orgId: actorOrgId
});
if (orgMemberships.length !== bypasserUserIds.length) {
const foundUserIdsInOrg = new Set(orgMemberships.map((mem) => mem.userId));
const missingUserIds = bypasserUserIds.filter((id) => !foundUserIdsInOrg.has(id));
throw new BadRequestError({
message: `One or more specified bypasser users are not part of the organization or do not exist. Invalid or non-member user IDs: ${missingUserIds.join(", ")}`
});
}
}
// Validate group bypassers
if (groupBypassers.length > 0) {
const orgGroups = await groupDAL.find({
$in: { id: groupBypassers },
orgId: actorOrgId
});
if (orgGroups.length !== groupBypassers.length) {
const foundGroupIdsInOrg = new Set(orgGroups.map((group) => group.id));
const missingGroupIds = groupBypassers.filter((id) => !foundGroupIdsInOrg.has(id));
throw new BadRequestError({
message: `One or more specified bypasser groups are not part of the organization or do not exist. Invalid or non-member group IDs: ${missingGroupIds.join(", ")}`
});
}
}
}
const updatedPolicy = await accessApprovalPolicyDAL.transaction(async (tx) => {
const doc = await accessApprovalPolicyDAL.updateById(
accessApprovalPolicy.id,
@ -313,6 +456,28 @@ export const accessApprovalPolicyServiceFactory = ({
);
}
await accessApprovalPolicyBypasserDAL.delete({ policyId: doc.id }, tx);
if (bypasserUserIds.length) {
await accessApprovalPolicyBypasserDAL.insertMany(
bypasserUserIds.map((userId) => ({
bypasserUserId: userId,
policyId: doc.id
})),
tx
);
}
if (groupBypassers.length) {
await accessApprovalPolicyBypasserDAL.insertMany(
groupBypassers.map((groupId) => ({
bypasserGroupId: groupId,
policyId: doc.id
})),
tx
);
}
return doc;
});
return {

View File

@ -18,11 +18,20 @@ export enum ApproverType {
User = "user"
}
export enum BypasserType {
Group = "group",
User = "user"
}
export type TCreateAccessApprovalPolicy = {
approvals: number;
secretPath: string;
environment: string;
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; name?: string })[];
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
bypassers?: (
| { type: BypasserType.Group; id: string }
| { type: BypasserType.User; id?: string; username?: string }
)[];
projectSlug: string;
name: string;
enforcementLevel: EnforcementLevel;
@ -32,7 +41,11 @@ export type TCreateAccessApprovalPolicy = {
export type TUpdateAccessApprovalPolicy = {
policyId: string;
approvals?: number;
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; name?: string })[];
approvers: ({ type: ApproverType.Group; id: string } | { type: ApproverType.User; id?: string; username?: string })[];
bypassers?: (
| { type: BypasserType.Group; id: string }
| { type: BypasserType.User; id?: string; username?: string }
)[];
secretPath?: string;
name?: string;
enforcementLevel?: EnforcementLevel;

View File

@ -1,7 +1,13 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { AccessApprovalRequestsSchema, TableName, TAccessApprovalRequests, TUsers } from "@app/db/schemas";
import {
AccessApprovalRequestsSchema,
TableName,
TAccessApprovalRequests,
TUserGroupMembership,
TUsers
} from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, selectAllTableCols, sqlNestRelationships, TFindFilter } from "@app/lib/knex";
@ -28,12 +34,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalRequest}.policyId`,
`${TableName.AccessApprovalPolicy}.id`
)
.leftJoin(
TableName.AccessApprovalRequestReviewer,
`${TableName.AccessApprovalRequest}.id`,
`${TableName.AccessApprovalRequestReviewer}.requestId`
)
.leftJoin(
TableName.AccessApprovalPolicyApprover,
`${TableName.AccessApprovalPolicy}.id`,
@ -46,6 +52,17 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
)
.leftJoin(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.leftJoin(
TableName.AccessApprovalPolicyBypasser,
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyBypasser}.policyId`
)
.leftJoin<TUserGroupMembership>(
db(TableName.UserGroupMembership).as("bypasserUserGroupMembership"),
`${TableName.AccessApprovalPolicyBypasser}.bypasserGroupId`,
`bypasserUserGroupMembership.groupId`
)
.join<TUsers>(
db(TableName.Users).as("requestedByUser"),
`${TableName.AccessApprovalRequest}.requestedByUserId`,
@ -69,6 +86,9 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
.select(db.ref("approverUserId").withSchema(TableName.AccessApprovalPolicyApprover))
.select(db.ref("userId").withSchema(TableName.UserGroupMembership).as("approverGroupUserId"))
.select(db.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser))
.select(db.ref("userId").withSchema("bypasserUserGroupMembership").as("bypasserGroupUserId"))
.select(
db.ref("projectId").withSchema(TableName.Environment),
db.ref("slug").withSchema(TableName.Environment).as("envSlug"),
@ -145,7 +165,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
}
: null,
isApproved: !!doc.policyDeletedAt || !!doc.privilegeId
isApproved: !!doc.policyDeletedAt || !!doc.privilegeId || doc.status !== ApprovalStatus.PENDING
}),
childrenMapper: [
{
@ -158,6 +178,12 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
key: "approverGroupUserId",
label: "approvers" as const,
mapper: ({ approverGroupUserId }) => approverGroupUserId
},
{ key: "bypasserUserId", label: "bypassers" as const, mapper: ({ bypasserUserId }) => bypasserUserId },
{
key: "bypasserGroupUserId",
label: "bypassers" as const,
mapper: ({ bypasserGroupUserId }) => bypasserGroupUserId
}
]
});
@ -166,7 +192,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
return formattedDocs.map((doc) => ({
...doc,
policy: { ...doc.policy, approvers: doc.approvers }
policy: { ...doc.policy, approvers: doc.approvers, bypassers: doc.bypassers }
}));
} catch (error) {
throw new DatabaseError({ error, name: "FindRequestsWithPrivilege" });
@ -193,7 +219,6 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyApprover}.policyId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("accessApprovalPolicyApproverUser"),
`${TableName.AccessApprovalPolicyApprover}.approverUserId`,
@ -204,13 +229,33 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
`${TableName.AccessApprovalPolicyApprover}.approverGroupId`,
`${TableName.UserGroupMembership}.groupId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("accessApprovalPolicyGroupApproverUser"),
`${TableName.UserGroupMembership}.userId`,
"accessApprovalPolicyGroupApproverUser.id"
)
.leftJoin(
TableName.AccessApprovalPolicyBypasser,
`${TableName.AccessApprovalPolicy}.id`,
`${TableName.AccessApprovalPolicyBypasser}.policyId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("accessApprovalPolicyBypasserUser"),
`${TableName.AccessApprovalPolicyBypasser}.bypasserUserId`,
"accessApprovalPolicyBypasserUser.id"
)
.leftJoin<TUserGroupMembership>(
db(TableName.UserGroupMembership).as("bypasserUserGroupMembership"),
`${TableName.AccessApprovalPolicyBypasser}.bypasserGroupId`,
`bypasserUserGroupMembership.groupId`
)
.leftJoin<TUsers>(
db(TableName.Users).as("accessApprovalPolicyGroupBypasserUser"),
`bypasserUserGroupMembership.userId`,
"accessApprovalPolicyGroupBypasserUser.id"
)
.leftJoin(
TableName.AccessApprovalRequestReviewer,
`${TableName.AccessApprovalRequest}.id`,
@ -241,6 +286,18 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
tx.ref("firstName").withSchema("requestedByUser").as("requestedByUserFirstName"),
tx.ref("lastName").withSchema("requestedByUser").as("requestedByUserLastName"),
// Bypassers
tx.ref("bypasserUserId").withSchema(TableName.AccessApprovalPolicyBypasser),
tx.ref("userId").withSchema("bypasserUserGroupMembership").as("bypasserGroupUserId"),
tx.ref("email").withSchema("accessApprovalPolicyBypasserUser").as("bypasserEmail"),
tx.ref("email").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupEmail"),
tx.ref("username").withSchema("accessApprovalPolicyBypasserUser").as("bypasserUsername"),
tx.ref("username").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupUsername"),
tx.ref("firstName").withSchema("accessApprovalPolicyBypasserUser").as("bypasserFirstName"),
tx.ref("firstName").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupFirstName"),
tx.ref("lastName").withSchema("accessApprovalPolicyBypasserUser").as("bypasserLastName"),
tx.ref("lastName").withSchema("accessApprovalPolicyGroupBypasserUser").as("bypasserGroupLastName"),
tx.ref("reviewerUserId").withSchema(TableName.AccessApprovalRequestReviewer),
tx.ref("status").withSchema(TableName.AccessApprovalRequestReviewer).as("reviewerStatus"),
@ -265,7 +322,7 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
try {
const sql = findQuery({ [`${TableName.AccessApprovalRequest}.id` as "id"]: id }, tx || db.replicaNode());
const docs = await sql;
const formatedDoc = sqlNestRelationships({
const formattedDoc = sqlNestRelationships({
data: docs,
key: "id",
parentMapper: (el) => ({
@ -335,13 +392,51 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
lastName,
username
})
},
{
key: "bypasserUserId",
label: "bypassers" as const,
mapper: ({
bypasserUserId,
bypasserEmail: email,
bypasserUsername: username,
bypasserLastName: lastName,
bypasserFirstName: firstName
}) => ({
userId: bypasserUserId,
email,
firstName,
lastName,
username
})
},
{
key: "bypasserGroupUserId",
label: "bypassers" as const,
mapper: ({
userId,
bypasserGroupEmail: email,
bypasserGroupUsername: username,
bypasserGroupLastName: lastName,
bypasserFirstName: firstName
}) => ({
userId,
email,
firstName,
lastName,
username
})
}
]
});
if (!formatedDoc?.[0]) return;
if (!formattedDoc?.[0]) return;
return {
...formatedDoc[0],
policy: { ...formatedDoc[0].policy, approvers: formatedDoc[0].approvers }
...formattedDoc[0],
policy: {
...formattedDoc[0].policy,
approvers: formattedDoc[0].approvers,
bypassers: formattedDoc[0].bypassers
}
};
} catch (error) {
throw new DatabaseError({ error, name: "FindByIdAccessApprovalRequest" });
@ -392,14 +487,20 @@ export const accessApprovalRequestDALFactory = (db: TDbClient) => {
]
});
// an approval is pending if there is no reviewer rejections and no privilege ID is set
// an approval is pending if there is no reviewer rejections, no privilege ID is set and the status is pending
const pendingApprovals = formattedRequests.filter(
(req) => !req.privilegeId && !req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
(req) =>
!req.privilegeId &&
!req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED) &&
req.status === ApprovalStatus.PENDING
);
// an approval is finalized if there are any rejections or a privilege ID is set
// an approval is finalized if there are any rejections, a privilege ID is set or the number of approvals is equal to the number of approvals required
const finalizedApprovals = formattedRequests.filter(
(req) => req.privilegeId || req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED)
(req) =>
req.privilegeId ||
req.reviewers.some((r) => r.status === ApprovalStatus.REJECTED) ||
req.status !== ApprovalStatus.PENDING
);
return { pendingCount: pendingApprovals.length, finalizedCount: finalizedApprovals.length };

View File

@ -6,6 +6,7 @@ import { getConfig } from "@app/lib/config/env";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { EnforcementLevel } from "@app/lib/types";
import { triggerWorkflowIntegrationNotification } from "@app/lib/workflow-integrations/trigger-notification";
import { TriggerFeature } from "@app/lib/workflow-integrations/types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@ -55,7 +56,7 @@ type TSecretApprovalRequestServiceFactoryDep = {
| "findOne"
| "getCount"
>;
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find">;
accessApprovalPolicyDAL: Pick<TAccessApprovalPolicyDALFactory, "findOne" | "find" | "findLastValidPolicy">;
accessApprovalRequestReviewerDAL: Pick<
TAccessApprovalRequestReviewerDALFactory,
"create" | "find" | "findOne" | "transaction"
@ -130,7 +131,7 @@ export const accessApprovalRequestServiceFactory = ({
if (!environment) throw new NotFoundError({ message: `Environment with slug '${envSlug}' not found` });
const policy = await accessApprovalPolicyDAL.findOne({
const policy = await accessApprovalPolicyDAL.findLastValidPolicy({
envId: environment.id,
secretPath
});
@ -202,7 +203,7 @@ export const accessApprovalRequestServiceFactory = ({
const isRejected = reviewers.some((reviewer) => reviewer.status === ApprovalStatus.REJECTED);
if (!isRejected) {
if (!isRejected && duplicateRequest.status === ApprovalStatus.PENDING) {
throw new BadRequestError({ message: "You already have a pending access request with the same criteria" });
}
}
@ -323,24 +324,20 @@ export const accessApprovalRequestServiceFactory = ({
status,
actorId,
actorAuthMethod,
actorOrgId
actorOrgId,
bypassReason
}: TReviewAccessRequestDTO) => {
const accessApprovalRequest = await accessApprovalRequestDAL.findById(requestId);
if (!accessApprovalRequest) {
throw new NotFoundError({ message: `Secret approval request with ID '${requestId}' not found` });
}
const { policy } = accessApprovalRequest;
const { policy, environment } = accessApprovalRequest;
if (policy.deletedAt) {
throw new BadRequestError({
message: "The policy associated with this access request has been deleted."
});
}
if (!policy.allowedSelfApprovals && actorId === accessApprovalRequest.requestedByUserId) {
throw new BadRequestError({
message: "Failed to review access approval request. Users are not authorized to review their own request."
});
}
const { membership, hasRole } = await permissionService.getProjectPermission({
actor,
@ -355,29 +352,71 @@ export const accessApprovalRequestServiceFactory = ({
throw new ForbiddenRequestError({ message: "You are not a member of this project" });
}
const isSelfApproval = actorId === accessApprovalRequest.requestedByUserId;
const isSoftEnforcement = policy.enforcementLevel === EnforcementLevel.Soft;
const canBypass = !policy.bypassers.length || policy.bypassers.some((bypasser) => bypasser.userId === actorId);
const cannotBypassUnderSoftEnforcement = !(isSoftEnforcement && canBypass);
const isApprover = policy.approvers.find((approver) => approver.userId === actorId);
// If user is (not an approver OR cant self approve) AND can't bypass policy
if ((!isApprover || (!policy.allowedSelfApprovals && isSelfApproval)) && cannotBypassUnderSoftEnforcement) {
throw new BadRequestError({
message: "Failed to review access approval request. Users are not authorized to review their own request."
});
}
if (
!hasRole(ProjectMembershipRole.Admin) &&
accessApprovalRequest.requestedByUserId !== actorId && // The request wasn't made by the current user
!policy.approvers.find((approver) => approver.userId === actorId) // The request isn't performed by an assigned approver
!isApprover // The request isn't performed by an assigned approver
) {
throw new ForbiddenRequestError({ message: "You are not authorized to approve this request" });
}
const project = await projectDAL.findById(accessApprovalRequest.projectId);
if (!project) {
throw new NotFoundError({ message: "The project associated with this access request was not found." });
}
const existingReviews = await accessApprovalRequestReviewerDAL.find({ requestId: accessApprovalRequest.id });
if (existingReviews.some((review) => review.status === ApprovalStatus.REJECTED)) {
throw new BadRequestError({ message: "The request has already been rejected by another reviewer" });
}
const reviewStatus = await accessApprovalRequestReviewerDAL.transaction(async (tx) => {
const review = await accessApprovalRequestReviewerDAL.findOne(
const isBreakGlassApprovalAttempt =
policy.enforcementLevel === EnforcementLevel.Soft &&
actorId === accessApprovalRequest.requestedByUserId &&
status === ApprovalStatus.APPROVED;
let reviewForThisActorProcessing: {
id: string;
requestId: string;
reviewerUserId: string;
status: string;
createdAt: Date;
updatedAt: Date;
};
const existingReviewByActorInTx = await accessApprovalRequestReviewerDAL.findOne(
{
requestId: accessApprovalRequest.id,
reviewerUserId: actorId
},
tx
);
if (!review) {
const newReview = await accessApprovalRequestReviewerDAL.create(
// Check if review exists for actor
if (existingReviewByActorInTx) {
// Check if breakglass re-approval
if (isBreakGlassApprovalAttempt && existingReviewByActorInTx.status === ApprovalStatus.APPROVED) {
reviewForThisActorProcessing = existingReviewByActorInTx;
} else {
throw new BadRequestError({ message: "You have already reviewed this request" });
}
} else {
reviewForThisActorProcessing = await accessApprovalRequestReviewerDAL.create(
{
status,
requestId: accessApprovalRequest.id,
@ -385,19 +424,26 @@ export const accessApprovalRequestServiceFactory = ({
},
tx
);
}
const allReviews = [...existingReviews, newReview];
const otherReviews = existingReviews.filter((er) => er.reviewerUserId !== actorId);
const allUniqueReviews = [...otherReviews, reviewForThisActorProcessing];
const approvedReviews = allReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
const approvedReviews = allUniqueReviews.filter((r) => r.status === ApprovalStatus.APPROVED);
const meetsStandardApprovalThreshold = approvedReviews.length >= policy.approvals;
// approvals is the required number of approvals. If the number of approved reviews is equal to the number of required approvals, then the request is approved.
if (approvedReviews.length === policy.approvals) {
if (
reviewForThisActorProcessing.status === ApprovalStatus.APPROVED &&
(meetsStandardApprovalThreshold || isBreakGlassApprovalAttempt)
) {
const currentRequestState = await accessApprovalRequestDAL.findById(accessApprovalRequest.id, tx);
let privilegeIdToSet = currentRequestState?.privilegeId || null;
if (!privilegeIdToSet) {
if (accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
throw new BadRequestError({ message: "Temporary range is required for temporary access" });
}
let privilegeId: string | null = null;
if (!accessApprovalRequest.isTemporary && !accessApprovalRequest.temporaryRange) {
// Permanent access
const privilege = await additionalPrivilegeDAL.create(
@ -409,7 +455,7 @@ export const accessApprovalRequestServiceFactory = ({
},
tx
);
privilegeId = privilege.id;
privilegeIdToSet = privilege.id;
} else {
// Temporary access
const relativeTempAllocatedTimeInMs = ms(accessApprovalRequest.temporaryRange!);
@ -421,23 +467,61 @@ export const accessApprovalRequestServiceFactory = ({
projectId: accessApprovalRequest.projectId,
slug: `requested-privilege-${slugify(alphaNumericNanoId(12))}`,
permissions: JSON.stringify(accessApprovalRequest.permissions),
isTemporary: true,
isTemporary: true, // Explicitly set to true for the privilege
temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative,
temporaryRange: accessApprovalRequest.temporaryRange!,
temporaryAccessStartTime: startTime,
temporaryAccessEndTime: new Date(new Date(startTime).getTime() + relativeTempAllocatedTimeInMs)
temporaryAccessEndTime: new Date(startTime.getTime() + relativeTempAllocatedTimeInMs)
},
tx
);
privilegeId = privilege.id;
privilegeIdToSet = privilege.id;
}
await accessApprovalRequestDAL.updateById(accessApprovalRequest.id, { privilegeId }, tx);
await accessApprovalRequestDAL.updateById(
accessApprovalRequest.id,
{ privilegeId: privilegeIdToSet, status: ApprovalStatus.APPROVED },
tx
);
}
return newReview;
}
throw new BadRequestError({ message: "You have already reviewed this request" });
// Send notification if this was a breakglass approval
if (isBreakGlassApprovalAttempt) {
const cfg = getConfig();
const actingUser = await userDAL.findById(actorId, tx);
if (actingUser) {
const policyApproverUserIds = policy.approvers
.map((ap) => ap.userId)
.filter((id): id is string => typeof id === "string");
if (policyApproverUserIds.length > 0) {
const approverUsersForEmail = await userDAL.find({ $in: { id: policyApproverUserIds } }, { tx });
const recipientEmails = approverUsersForEmail
.map((appUser) => appUser.email)
.filter((email): email is string => !!email);
if (recipientEmails.length > 0) {
await smtpService.sendMail({
recipients: recipientEmails,
subjectLine: "Infisical Secret Access Policy Bypassed",
substitutions: {
projectName: project.name,
requesterFullName: `${actingUser.firstName} ${actingUser.lastName}`,
requesterEmail: actingUser.email,
bypassReason: bypassReason || "No reason provided",
secretPath: policy.secretPath || "/",
environment,
approvalUrl: `${cfg.SITE_URL}/secret-manager/${project.id}/approval`,
requestType: "access"
},
template: SmtpTemplates.AccessSecretRequestBypassed
});
}
}
}
}
return reviewForThisActorProcessing;
});
return reviewStatus;

View File

@ -17,6 +17,8 @@ export type TGetAccessRequestCountDTO = {
export type TReviewAccessRequestDTO = {
requestId: string;
status: ApprovalStatus;
envName?: string;
bypassReason?: string;
} & Omit<TProjectPermission, "projectId">;
export type TCreateAccessApprovalRequestDTO = {

View File

@ -1,7 +1,9 @@
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { OrgServiceActor } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
import { TLicenseServiceFactory } from "../../license/license-service";
import { listOCICompartments, listOCIVaultKeys, listOCIVaults } from "./oci-connection-fns";
import { TOCIConnection } from "./oci-connection-types";
@ -22,8 +24,23 @@ type TListOCIVaultKeysDTO = {
vaultOcid: string;
};
export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
// Enterprise check
export const checkPlan = async (licenseService: Pick<TLicenseServiceFactory, "getPlan">, orgId: string) => {
const plan = await licenseService.getPlan(orgId);
if (!plan.enterpriseAppConnections)
throw new BadRequestError({
message:
"Failed to use app connection due to plan restriction. Upgrade plan to access enterprise app connections."
});
};
export const ociConnectionService = (
getAppConnection: TGetAppConnectionFunc,
licenseService: Pick<TLicenseServiceFactory, "getPlan">
) => {
const listCompartments = async (connectionId: string, actor: OrgServiceActor) => {
await checkPlan(licenseService, actor.orgId);
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
try {
@ -36,6 +53,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
};
const listVaults = async ({ connectionId, compartmentOcid }: TListOCIVaultsDTO, actor: OrgServiceActor) => {
await checkPlan(licenseService, actor.orgId);
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
try {
@ -51,6 +70,8 @@ export const ociConnectionService = (getAppConnection: TGetAppConnectionFunc) =>
{ connectionId, compartmentOcid, vaultOcid }: TListOCIVaultKeysDTO,
actor: OrgServiceActor
) => {
await checkPlan(licenseService, actor.orgId);
const appConnection = await getAppConnection(AppConnection.OCI, connectionId, actor);
try {

View File

@ -2,7 +2,7 @@ import z from "zod";
import { DiscriminativePick } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import { AppConnection } from "../../../../services/app-connection/app-connection-enums";
import {
CreateOCIConnectionSchema,
OCIConnectionSchema,

View File

@ -1,3 +1,4 @@
import { ProjectType } from "@app/db/schemas";
import {
TCreateProjectTemplateDTO,
TUpdateProjectTemplateDTO
@ -9,6 +10,18 @@ import {
TSecretRotationV2Raw,
TUpdateSecretRotationV2DTO
} from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-types";
import {
SecretScanningDataSource,
SecretScanningScanStatus,
SecretScanningScanType
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-enums";
import {
TCreateSecretScanningDataSourceDTO,
TDeleteSecretScanningDataSourceDTO,
TTriggerSecretScanningDataSourceDTO,
TUpdateSecretScanningDataSourceDTO,
TUpdateSecretScanningFindingDTO
} from "@app/ee/services/secret-scanning-v2/secret-scanning-v2-types";
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
@ -20,7 +33,7 @@ import { AppConnection } from "@app/services/app-connection/app-connection-enums
import { TCreateAppConnectionDTO, TUpdateAppConnectionDTO } from "@app/services/app-connection/app-connection-types";
import { ActorType } from "@app/services/auth/auth-type";
import { CertExtendedKeyUsage, CertKeyAlgorithm, CertKeyUsage } from "@app/services/certificate/certificate-types";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-types";
import { CaStatus } from "@app/services/certificate-authority/certificate-authority-enums";
import { TIdentityTrustedIp } from "@app/services/identity/identity-types";
import { TAllowedFields } from "@app/services/identity-ldap-auth/identity-ldap-auth-types";
import { PkiItemType } from "@app/services/pki-collection/pki-collection-types";
@ -162,6 +175,12 @@ export enum EventType {
REVOKE_IDENTITY_AWS_AUTH = "revoke-identity-aws-auth",
GET_IDENTITY_AWS_AUTH = "get-identity-aws-auth",
LOGIN_IDENTITY_OCI_AUTH = "login-identity-oci-auth",
ADD_IDENTITY_OCI_AUTH = "add-identity-oci-auth",
UPDATE_IDENTITY_OCI_AUTH = "update-identity-oci-auth",
REVOKE_IDENTITY_OCI_AUTH = "revoke-identity-oci-auth",
GET_IDENTITY_OCI_AUTH = "get-identity-oci-auth",
LOGIN_IDENTITY_AZURE_AUTH = "login-identity-azure-auth",
ADD_IDENTITY_AZURE_AUTH = "add-identity-azure-auth",
UPDATE_IDENTITY_AZURE_AUTH = "update-identity-azure-auth",
@ -225,6 +244,7 @@ export enum EventType {
REMOVE_HOST_FROM_SSH_HOST_GROUP = "remove-host-from-ssh-host-group",
CREATE_CA = "create-certificate-authority",
GET_CA = "get-certificate-authority",
GET_CAS = "get-certificate-authorities",
UPDATE_CA = "update-certificate-authority",
DELETE_CA = "delete-certificate-authority",
RENEW_CA = "renew-certificate-authority",
@ -235,6 +255,7 @@ export enum EventType {
IMPORT_CA_CERT = "import-certificate-authority-cert",
GET_CA_CRLS = "get-certificate-authority-crls",
ISSUE_CERT = "issue-cert",
IMPORT_CERT = "import-cert",
SIGN_CERT = "sign-cert",
GET_CA_CERTIFICATE_TEMPLATES = "get-ca-certificate-templates",
GET_CERT = "get-cert",
@ -260,7 +281,9 @@ export enum EventType {
GET_PKI_SUBSCRIBER = "get-pki-subscriber",
ISSUE_PKI_SUBSCRIBER_CERT = "issue-pki-subscriber-cert",
SIGN_PKI_SUBSCRIBER_CERT = "sign-pki-subscriber-cert",
AUTOMATED_RENEW_SUBSCRIBER_CERT = "automated-renew-subscriber-cert",
LIST_PKI_SUBSCRIBER_CERTS = "list-pki-subscriber-certs",
GET_SUBSCRIBER_ACTIVE_CERT_BUNDLE = "get-subscriber-active-cert-bundle",
CREATE_KMS = "create-kms",
UPDATE_KMS = "update-kms",
DELETE_KMS = "delete-kms",
@ -309,7 +332,6 @@ export enum EventType {
CREATE_PROJECT_TEMPLATE = "create-project-template",
UPDATE_PROJECT_TEMPLATE = "update-project-template",
DELETE_PROJECT_TEMPLATE = "delete-project-template",
APPLY_PROJECT_TEMPLATE = "apply-project-template",
GET_APP_CONNECTIONS = "get-app-connections",
GET_AVAILABLE_APP_CONNECTIONS_DETAILS = "get-available-app-connections-details",
GET_APP_CONNECTION = "get-app-connection",
@ -369,7 +391,27 @@ export enum EventType {
MICROSOFT_TEAMS_WORKFLOW_INTEGRATION_LIST = "microsoft-teams-workflow-integration-list",
PROJECT_ASSUME_PRIVILEGE_SESSION_START = "project-assume-privileges-session-start",
PROJECT_ASSUME_PRIVILEGE_SESSION_END = "project-assume-privileges-session-end"
PROJECT_ASSUME_PRIVILEGE_SESSION_END = "project-assume-privileges-session-end",
SECRET_SCANNING_DATA_SOURCE_LIST = "secret-scanning-data-source-list",
SECRET_SCANNING_DATA_SOURCE_CREATE = "secret-scanning-data-source-create",
SECRET_SCANNING_DATA_SOURCE_UPDATE = "secret-scanning-data-source-update",
SECRET_SCANNING_DATA_SOURCE_DELETE = "secret-scanning-data-source-delete",
SECRET_SCANNING_DATA_SOURCE_GET = "secret-scanning-data-source-get",
SECRET_SCANNING_DATA_SOURCE_TRIGGER_SCAN = "secret-scanning-data-source-trigger-scan",
SECRET_SCANNING_DATA_SOURCE_SCAN = "secret-scanning-data-source-scan",
SECRET_SCANNING_RESOURCE_LIST = "secret-scanning-resource-list",
SECRET_SCANNING_SCAN_LIST = "secret-scanning-scan-list",
SECRET_SCANNING_FINDING_LIST = "secret-scanning-finding-list",
SECRET_SCANNING_FINDING_UPDATE = "secret-scanning-finding-update",
SECRET_SCANNING_CONFIG_GET = "secret-scanning-config-get",
SECRET_SCANNING_CONFIG_UPDATE = "secret-scanning-config-update",
UPDATE_ORG = "update-org",
CREATE_PROJECT = "create-project",
UPDATE_PROJECT = "update-project",
DELETE_PROJECT = "delete-project"
}
export const filterableSecretEvents: EventType[] = [
@ -1009,6 +1051,55 @@ interface GetIdentityAwsAuthEvent {
};
}
interface LoginIdentityOciAuthEvent {
type: EventType.LOGIN_IDENTITY_OCI_AUTH;
metadata: {
identityId: string;
identityOciAuthId: string;
identityAccessTokenId: string;
};
}
interface AddIdentityOciAuthEvent {
type: EventType.ADD_IDENTITY_OCI_AUTH;
metadata: {
identityId: string;
tenancyOcid: string;
allowedUsernames: string | null;
accessTokenTTL: number;
accessTokenMaxTTL: number;
accessTokenNumUsesLimit: number;
accessTokenTrustedIps: Array<TIdentityTrustedIp>;
};
}
interface DeleteIdentityOciAuthEvent {
type: EventType.REVOKE_IDENTITY_OCI_AUTH;
metadata: {
identityId: string;
};
}
interface UpdateIdentityOciAuthEvent {
type: EventType.UPDATE_IDENTITY_OCI_AUTH;
metadata: {
identityId: string;
tenancyOcid?: string;
allowedUsernames: string | null;
accessTokenTTL?: number;
accessTokenMaxTTL?: number;
accessTokenNumUsesLimit?: number;
accessTokenTrustedIps?: Array<TIdentityTrustedIp>;
};
}
interface GetIdentityOciAuthEvent {
type: EventType.GET_IDENTITY_OCI_AUTH;
metadata: {
identityId: string;
};
}
interface LoginIdentityAzureAuthEvent {
type: EventType.LOGIN_IDENTITY_AZURE_AUTH;
metadata: {
@ -1717,7 +1808,8 @@ interface CreateCa {
type: EventType.CREATE_CA;
metadata: {
caId: string;
dn: string;
name: string;
dn?: string;
};
}
@ -1725,7 +1817,15 @@ interface GetCa {
type: EventType.GET_CA;
metadata: {
caId: string;
dn: string;
name: string;
dn?: string;
};
}
interface GetCAs {
type: EventType.GET_CAS;
metadata: {
caIds: string[];
};
}
@ -1733,7 +1833,8 @@ interface UpdateCa {
type: EventType.UPDATE_CA;
metadata: {
caId: string;
dn: string;
name: string;
dn?: string;
status: CaStatus;
};
}
@ -1742,7 +1843,8 @@ interface DeleteCa {
type: EventType.DELETE_CA;
metadata: {
caId: string;
dn: string;
name: string;
dn?: string;
};
}
@ -1812,6 +1914,15 @@ interface IssueCert {
};
}
interface ImportCert {
type: EventType.IMPORT_CERT;
metadata: {
certId: string;
cn: string;
serialNumber: string;
};
}
interface SignCert {
type: EventType.SIGN_CERT;
metadata: {
@ -1979,7 +2090,7 @@ interface CreatePkiSubscriber {
caId?: string;
name: string;
commonName: string;
ttl: string;
ttl?: string;
subjectAlternativeNames: string[];
keyUsages: CertKeyUsage[];
extendedKeyUsages: CertExtendedKeyUsage[];
@ -2021,7 +2132,15 @@ interface IssuePkiSubscriberCert {
metadata: {
subscriberId: string;
name: string;
serialNumber: string;
serialNumber?: string;
};
}
interface AutomatedRenewPkiSubscriberCert {
type: EventType.AUTOMATED_RENEW_SUBSCRIBER_CERT;
metadata: {
subscriberId: string;
name: string;
};
}
@ -2043,6 +2162,16 @@ interface ListPkiSubscriberCerts {
};
}
interface GetSubscriberActiveCertBundle {
type: EventType.GET_SUBSCRIBER_ACTIVE_CERT_BUNDLE;
metadata: {
subscriberId: string;
name: string;
certId: string;
serialNumber: string;
};
}
interface CreateKmsEvent {
type: EventType.CREATE_KMS;
metadata: {
@ -2396,14 +2525,6 @@ interface DeleteProjectTemplateEvent {
};
}
interface ApplyProjectTemplateEvent {
type: EventType.APPLY_PROJECT_TEMPLATE;
metadata: {
template: string;
projectId: string;
};
}
interface GetAppConnectionsEvent {
type: EventType.GET_APP_CONNECTIONS;
metadata: {
@ -2858,6 +2979,154 @@ interface MicrosoftTeamsWorkflowIntegrationUpdateEvent {
};
}
interface SecretScanningDataSourceListEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_LIST;
metadata: {
type?: SecretScanningDataSource;
count: number;
dataSourceIds: string[];
};
}
interface SecretScanningDataSourceGetEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_GET;
metadata: {
type: SecretScanningDataSource;
dataSourceId: string;
};
}
interface SecretScanningDataSourceCreateEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_CREATE;
metadata: Omit<TCreateSecretScanningDataSourceDTO, "projectId"> & { dataSourceId: string };
}
interface SecretScanningDataSourceUpdateEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_UPDATE;
metadata: TUpdateSecretScanningDataSourceDTO;
}
interface SecretScanningDataSourceDeleteEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_DELETE;
metadata: TDeleteSecretScanningDataSourceDTO;
}
interface SecretScanningDataSourceTriggerScanEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_TRIGGER_SCAN;
metadata: TTriggerSecretScanningDataSourceDTO;
}
interface SecretScanningDataSourceScanEvent {
type: EventType.SECRET_SCANNING_DATA_SOURCE_SCAN;
metadata: {
scanId: string;
resourceId: string;
resourceType: string;
dataSourceId: string;
dataSourceType: string;
scanStatus: SecretScanningScanStatus;
scanType: SecretScanningScanType;
numberOfSecretsDetected?: number;
};
}
interface SecretScanningResourceListEvent {
type: EventType.SECRET_SCANNING_RESOURCE_LIST;
metadata: {
type: SecretScanningDataSource;
dataSourceId: string;
resourceIds: string[];
count: number;
};
}
interface SecretScanningScanListEvent {
type: EventType.SECRET_SCANNING_SCAN_LIST;
metadata: {
type: SecretScanningDataSource;
dataSourceId: string;
count: number;
};
}
interface SecretScanningFindingListEvent {
type: EventType.SECRET_SCANNING_FINDING_LIST;
metadata: {
findingIds: string[];
count: number;
};
}
interface SecretScanningFindingUpdateEvent {
type: EventType.SECRET_SCANNING_FINDING_UPDATE;
metadata: TUpdateSecretScanningFindingDTO;
}
interface SecretScanningConfigUpdateEvent {
type: EventType.SECRET_SCANNING_CONFIG_UPDATE;
metadata: {
content: string | null;
};
}
interface SecretScanningConfigReadEvent {
type: EventType.SECRET_SCANNING_CONFIG_GET;
metadata?: Record<string, never>; // not needed, based off projectId
}
interface OrgUpdateEvent {
type: EventType.UPDATE_ORG;
metadata: {
name?: string;
slug?: string;
authEnforced?: boolean;
scimEnabled?: boolean;
defaultMembershipRoleSlug?: string;
enforceMfa?: boolean;
selectedMfaMethod?: string;
allowSecretSharingOutsideOrganization?: boolean;
bypassOrgAuthEnabled?: boolean;
userTokenExpiration?: string;
secretsProductEnabled?: boolean;
pkiProductEnabled?: boolean;
kmsProductEnabled?: boolean;
sshProductEnabled?: boolean;
scannerProductEnabled?: boolean;
shareSecretsProductEnabled?: boolean;
};
}
interface ProjectCreateEvent {
type: EventType.CREATE_PROJECT;
metadata: {
name: string;
slug?: string;
type: ProjectType;
};
}
interface ProjectUpdateEvent {
type: EventType.UPDATE_PROJECT;
metadata: {
name?: string;
description?: string;
autoCapitalization?: boolean;
hasDeleteProtection?: boolean;
slug?: string;
secretSharing?: boolean;
pitVersionLimit?: number;
auditLogsRetentionDays?: number;
};
}
interface ProjectDeleteEvent {
type: EventType.DELETE_PROJECT;
metadata: {
id: string;
name: string;
};
}
export type Event =
| GetSecretsEvent
| GetSecretEvent
@ -2914,6 +3183,11 @@ export type Event =
| UpdateIdentityAwsAuthEvent
| GetIdentityAwsAuthEvent
| DeleteIdentityAwsAuthEvent
| LoginIdentityOciAuthEvent
| AddIdentityOciAuthEvent
| UpdateIdentityOciAuthEvent
| GetIdentityOciAuthEvent
| DeleteIdentityOciAuthEvent
| LoginIdentityAzureAuthEvent
| AddIdentityAzureAuthEvent
| DeleteIdentityAzureAuthEvent
@ -2977,6 +3251,7 @@ export type Event =
| IssueSshHostHostCert
| CreateCa
| GetCa
| GetCAs
| UpdateCa
| DeleteCa
| RenewCa
@ -2987,6 +3262,7 @@ export type Event =
| ImportCaCert
| GetCaCrls
| IssueCert
| ImportCert
| SignCert
| GetCaCertificateTemplates
| GetCert
@ -3012,7 +3288,9 @@ export type Event =
| GetPkiSubscriber
| IssuePkiSubscriberCert
| SignPkiSubscriberCert
| AutomatedRenewPkiSubscriberCert
| ListPkiSubscriberCerts
| GetSubscriberActiveCertBundle
| CreateKmsEvent
| UpdateKmsEvent
| DeleteKmsEvent
@ -3057,7 +3335,6 @@ export type Event =
| CreateProjectTemplateEvent
| UpdateProjectTemplateEvent
| DeleteProjectTemplateEvent
| ApplyProjectTemplateEvent
| GetAppConnectionsEvent
| GetAvailableAppConnectionsDetailsEvent
| GetAppConnectionEvent
@ -3119,4 +3396,21 @@ export type Event =
| MicrosoftTeamsWorkflowIntegrationGetTeamsEvent
| MicrosoftTeamsWorkflowIntegrationGetEvent
| MicrosoftTeamsWorkflowIntegrationListEvent
| MicrosoftTeamsWorkflowIntegrationUpdateEvent;
| MicrosoftTeamsWorkflowIntegrationUpdateEvent
| SecretScanningDataSourceListEvent
| SecretScanningDataSourceGetEvent
| SecretScanningDataSourceCreateEvent
| SecretScanningDataSourceUpdateEvent
| SecretScanningDataSourceDeleteEvent
| SecretScanningDataSourceTriggerScanEvent
| SecretScanningDataSourceScanEvent
| SecretScanningResourceListEvent
| SecretScanningScanListEvent
| SecretScanningFindingListEvent
| SecretScanningFindingUpdateEvent
| SecretScanningConfigUpdateEvent
| SecretScanningConfigReadEvent
| OrgUpdateEvent
| ProjectCreateEvent
| ProjectUpdateEvent
| ProjectDeleteEvent;

View File

@ -7,6 +7,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { NotFoundError } from "@app/lib/errors";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { expandInternalCa } from "@app/services/certificate-authority/certificate-authority-fns";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@ -14,7 +15,7 @@ import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns
import { TGetCaCrlsDTO, TGetCrlById } from "./certificate-authority-crl-types";
type TCertificateAuthorityCrlServiceFactoryDep = {
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findByIdWithAssociatedCa">;
certificateAuthorityCrlDAL: Pick<TCertificateAuthorityCrlDALFactory, "find" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
@ -37,7 +38,8 @@ export const certificateAuthorityCrlServiceFactory = ({
const caCrl = await certificateAuthorityCrlDAL.findById(crlId);
if (!caCrl) throw new NotFoundError({ message: `CRL with ID '${crlId}' not found` });
const ca = await certificateAuthorityDAL.findById(caCrl.caId);
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caCrl.caId);
if (!ca?.internalCa?.id) throw new NotFoundError({ message: `Internal CA with ID '${caCrl.caId}' not found` });
const keyId = await getProjectKmsCertificateKeyId({
projectId: ca.projectId,
@ -54,7 +56,7 @@ export const certificateAuthorityCrlServiceFactory = ({
const crl = new x509.X509Crl(decryptedCrl);
return {
ca,
ca: expandInternalCa(ca),
caCrl,
crl: crl.rawData
};
@ -64,8 +66,8 @@ export const certificateAuthorityCrlServiceFactory = ({
* 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 NotFoundError({ message: `CA with ID '${caId}' not found` });
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
if (!ca?.internalCa?.id) throw new NotFoundError({ message: `Internal CA with ID '${caId}' not found` });
const { permission } = await permissionService.getProjectPermission({
actor,
@ -108,7 +110,7 @@ export const certificateAuthorityCrlServiceFactory = ({
);
return {
ca,
ca: expandInternalCa(ca),
crls: decryptedCrls
};
};

View File

@ -6,7 +6,7 @@ import { isCertChainValid } from "@app/services/certificate/certificate-fns";
import { TCertificateAuthorityCertDALFactory } from "@app/services/certificate-authority/certificate-authority-cert-dal";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { getCaCertChain, getCaCertChains } from "@app/services/certificate-authority/certificate-authority-fns";
import { TCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/certificate-authority-service";
import { TInternalCertificateAuthorityServiceFactory } from "@app/services/certificate-authority/internal/internal-certificate-authority-service";
import { TCertificateTemplateDALFactory } from "@app/services/certificate-template/certificate-template-dal";
import { TCertificateTemplateServiceFactory } from "@app/services/certificate-template/certificate-template-service";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@ -16,10 +16,10 @@ import { TLicenseServiceFactory } from "../license/license-service";
import { convertRawCertsToPkcs7 } from "./certificate-est-fns";
type TCertificateEstServiceFactoryDep = {
certificateAuthorityService: Pick<TCertificateAuthorityServiceFactory, "signCertFromCa">;
internalCertificateAuthorityService: Pick<TInternalCertificateAuthorityServiceFactory, "signCertFromCa">;
certificateTemplateService: Pick<TCertificateTemplateServiceFactory, "getEstConfiguration">;
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "findById">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById">;
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "findById" | "findByIdWithAssociatedCa">;
certificateAuthorityCertDAL: Pick<TCertificateAuthorityCertDALFactory, "find" | "findById">;
projectDAL: Pick<TProjectDALFactory, "findOne" | "updateById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "decryptWithKmsKey" | "generateKmsKey">;
@ -29,7 +29,7 @@ type TCertificateEstServiceFactoryDep = {
export type TCertificateEstServiceFactory = ReturnType<typeof certificateEstServiceFactory>;
export const certificateEstServiceFactory = ({
certificateAuthorityService,
internalCertificateAuthorityService,
certificateTemplateService,
certificateTemplateDAL,
certificateAuthorityCertDAL,
@ -127,7 +127,7 @@ export const certificateEstServiceFactory = ({
});
}
const { certificate } = await certificateAuthorityService.signCertFromCa({
const { certificate } = await internalCertificateAuthorityService.signCertFromCa({
isInternal: true,
certificateTemplateId,
csr
@ -188,7 +188,7 @@ export const certificateEstServiceFactory = ({
}
}
const { certificate } = await certificateAuthorityService.signCertFromCa({
const { certificate } = await internalCertificateAuthorityService.signCertFromCa({
isInternal: true,
certificateTemplateId,
csr
@ -227,15 +227,15 @@ export const certificateEstServiceFactory = ({
});
}
const ca = await certificateAuthorityDAL.findById(certTemplate.caId);
if (!ca) {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(certTemplate.caId);
if (!ca?.internalCa?.id) {
throw new NotFoundError({
message: `Certificate Authority with ID '${certTemplate.caId}' not found`
message: `Internal Certificate Authority with ID '${certTemplate.caId}' not found`
});
}
const { caCert, caCertChain } = await getCaCertChain({
caCertId: ca.activeCaCertId as string,
caCertId: ca.internalCa.activeCaCertId as string,
certificateAuthorityDAL,
certificateAuthorityCertDAL,
projectDAL,

View File

@ -132,7 +132,11 @@ export const dynamicSecretLeaseServiceFactory = ({
let result;
try {
result = await selectedProvider.create(decryptedStoredInput, expireAt.getTime());
result = await selectedProvider.create({
inputs: decryptedStoredInput,
expireAt: expireAt.getTime(),
usernameTemplate: dynamicSecretCfg.usernameTemplate
});
} catch (error: unknown) {
if (error && typeof error === "object" && error !== null && "sqlMessage" in error) {
throw new BadRequestError({ message: error.sqlMessage as string });

View File

@ -11,6 +11,8 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
if (appCfg.isDevelopmentMode) return [host];
if (isGateway) return [host];
const reservedHosts = [appCfg.DB_HOST || getDbConnectionHost(appCfg.DB_CONNECTION_URI)].concat(
(appCfg.DB_READ_REPLICAS || []).map((el) => getDbConnectionHost(el.DB_CONNECTION_URI)),
getDbConnectionHost(appCfg.REDIS_URL),
@ -58,7 +60,7 @@ export const verifyHostInputValidity = async (host: string, isGateway = false) =
}
}
if (!isGateway && !(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
if (!(appCfg.DYNAMIC_SECRET_ALLOW_INTERNAL_IP || appCfg.ALLOW_INTERNAL_IP_CONNECTIONS)) {
const isInternalIp = inputHostIps.some((el) => isPrivateIp(el));
if (isInternalIp) throw new BadRequestError({ message: "Invalid db host" });
}

View File

@ -17,7 +17,8 @@ import { TSecretFolderDALFactory } from "@app/services/secret-folder/secret-fold
import { TDynamicSecretLeaseDALFactory } from "../dynamic-secret-lease/dynamic-secret-lease-dal";
import { TDynamicSecretLeaseQueueServiceFactory } from "../dynamic-secret-lease/dynamic-secret-lease-queue";
import { TProjectGatewayDALFactory } from "../gateway/project-gateway-dal";
import { TGatewayDALFactory } from "../gateway/gateway-dal";
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TDynamicSecretDALFactory } from "./dynamic-secret-dal";
import {
DynamicSecretStatus,
@ -44,9 +45,9 @@ type TDynamicSecretServiceFactoryDep = {
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
folderDAL: Pick<TSecretFolderDALFactory, "findBySecretPath" | "findBySecretPathMultiEnv">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
projectGatewayDAL: Pick<TProjectGatewayDALFactory, "findOne">;
gatewayDAL: Pick<TGatewayDALFactory, "findOne" | "find">;
resourceMetadataDAL: Pick<TResourceMetadataDALFactory, "insertMany" | "delete">;
};
@ -62,7 +63,7 @@ export const dynamicSecretServiceFactory = ({
dynamicSecretQueueService,
projectDAL,
kmsService,
projectGatewayDAL,
gatewayDAL,
resourceMetadataDAL
}: TDynamicSecretServiceFactoryDep) => {
const create = async ({
@ -77,7 +78,8 @@ export const dynamicSecretServiceFactory = ({
actorOrgId,
defaultTTL,
actorAuthMethod,
metadata
metadata,
usernameTemplate
}: TCreateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
@ -117,15 +119,31 @@ export const dynamicSecretServiceFactory = ({
const inputs = await selectedProvider.validateProviderInputs(provider.inputs);
let selectedGatewayId: string | null = null;
if (inputs && typeof inputs === "object" && "projectGatewayId" in inputs && inputs.projectGatewayId) {
const projectGatewayId = inputs.projectGatewayId as string;
if (inputs && typeof inputs === "object" && "gatewayId" in inputs && inputs.gatewayId) {
const gatewayId = inputs.gatewayId as string;
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
if (!projectGateway)
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
if (!gateway) {
throw new NotFoundError({
message: `Project gateway with ${projectGatewayId} not found`
message: `Gateway with ID ${gatewayId} not found`
});
selectedGatewayId = projectGateway.id;
}
const { permission: orgPermission } = await permissionService.getOrgPermission(
actor,
actorId,
gateway.orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
OrgPermissionSubjects.Gateway
);
selectedGatewayId = gateway.id;
}
const isConnected = await selectedProvider.validateConnection(provider.inputs);
@ -146,7 +164,8 @@ export const dynamicSecretServiceFactory = ({
defaultTTL,
folderId: folder.id,
name,
projectGatewayId: selectedGatewayId
gatewayId: selectedGatewayId,
usernameTemplate
},
tx
);
@ -182,7 +201,8 @@ export const dynamicSecretServiceFactory = ({
newName,
actorOrgId,
actorAuthMethod,
metadata
metadata,
usernameTemplate
}: TUpdateDynamicSecretDTO) => {
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
@ -255,20 +275,30 @@ export const dynamicSecretServiceFactory = ({
const updatedInput = await selectedProvider.validateProviderInputs(newInput);
let selectedGatewayId: string | null = null;
if (
updatedInput &&
typeof updatedInput === "object" &&
"projectGatewayId" in updatedInput &&
updatedInput?.projectGatewayId
) {
const projectGatewayId = updatedInput.projectGatewayId as string;
if (updatedInput && typeof updatedInput === "object" && "gatewayId" in updatedInput && updatedInput?.gatewayId) {
const gatewayId = updatedInput.gatewayId as string;
const projectGateway = await projectGatewayDAL.findOne({ id: projectGatewayId, projectId });
if (!projectGateway)
const [gateway] = await gatewayDAL.find({ id: gatewayId, orgId: actorOrgId });
if (!gateway) {
throw new NotFoundError({
message: `Project gateway with ${projectGatewayId} not found`
message: `Gateway with ID ${gatewayId} not found`
});
selectedGatewayId = projectGateway.id;
}
const { permission: orgPermission } = await permissionService.getOrgPermission(
actor,
actorId,
gateway.orgId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(orgPermission).throwUnlessCan(
OrgPermissionGatewayActions.AttachGateways,
OrgPermissionSubjects.Gateway
);
selectedGatewayId = gateway.id;
}
const isConnected = await selectedProvider.validateConnection(newInput);
@ -284,7 +314,8 @@ export const dynamicSecretServiceFactory = ({
defaultTTL,
name: newName ?? name,
status: null,
projectGatewayId: selectedGatewayId
gatewayId: selectedGatewayId,
usernameTemplate
},
tx
);

View File

@ -22,6 +22,7 @@ export type TCreateDynamicSecretDTO = {
name: string;
projectSlug: string;
metadata?: ResourceMetadataDTO;
usernameTemplate?: string | null;
} & Omit<TProjectPermission, "projectId">;
export type TUpdateDynamicSecretDTO = {
@ -34,6 +35,7 @@ export type TUpdateDynamicSecretDTO = {
inputs?: TProvider["inputs"];
projectSlug: string;
metadata?: ResourceMetadataDTO;
usernameTemplate?: string | null;
} & Omit<TProjectPermission, "projectId">;
export type TDeleteDynamicSecretDTO = {

View File

@ -132,9 +132,15 @@ const generatePassword = () => {
return customAlphabet(charset, 64)();
};
const generateUsername = () => {
const generateUsername = (usernameTemplate?: string | null) => {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
return `inf-${customAlphabet(charset, 32)()}`; // Username must start with an ascii letter, so we prepend the username with "inf-"
const randomUsername = `inf-${customAlphabet(charset, 32)()}`;
if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({
randomUsername,
unixTimestamp: Math.floor(Date.now() / 100)
});
};
export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
@ -168,13 +174,14 @@ export const AwsElastiCacheDatabaseProvider = (): TDynamicProviderFns => {
return true;
};
const create = async (inputs: unknown, expireAt: number) => {
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
const { inputs, expireAt, usernameTemplate } = data;
const providerInputs = await validateProviderInputs(inputs);
if (!(await validateConnection(providerInputs))) {
throw new BadRequestError({ message: "Failed to establish connection" });
}
const leaseUsername = generateUsername();
const leaseUsername = generateUsername(usernameTemplate);
const leasePassword = generatePassword();
const leaseExpiration = new Date(expireAt).toISOString();

View File

@ -16,6 +16,7 @@ import {
PutUserPolicyCommand,
RemoveUserFromGroupCommand
} from "@aws-sdk/client-iam";
import handlebars from "handlebars";
import { z } from "zod";
import { BadRequestError } from "@app/lib/errors";
@ -23,8 +24,14 @@ import { alphaNumericNanoId } from "@app/lib/nanoid";
import { DynamicSecretAwsIamSchema, TDynamicProviderFns } from "./models";
const generateUsername = () => {
return alphaNumericNanoId(32);
const generateUsername = (usernameTemplate?: string | null) => {
const randomUsername = alphaNumericNanoId(32);
if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({
randomUsername,
unixTimestamp: Math.floor(Date.now() / 100)
});
};
export const AwsIamProvider = (): TDynamicProviderFns => {
@ -53,11 +60,13 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
return isConnected;
};
const create = async (inputs: unknown) => {
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
const { inputs, usernameTemplate } = data;
const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs);
const username = generateUsername();
const username = generateUsername(usernameTemplate);
const { policyArns, userGroups, policyDocument, awsPath, permissionBoundaryPolicyArn } = providerInputs;
const createUserRes = await client.send(
new CreateUserCommand({

View File

@ -55,7 +55,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
return data.success;
};
const create = async (inputs: unknown) => {
const create = async ({ inputs }: { inputs: unknown }) => {
const providerInputs = await validateProviderInputs(inputs);
const data = await $getToken(providerInputs.tenantId, providerInputs.applicationId, providerInputs.clientSecret);
if (!data.success) {
@ -88,7 +88,7 @@ export const AzureEntraIDProvider = (): TDynamicProviderFns & {
const revoke = async (inputs: unknown, entityId: string) => {
// Creates a new password
await create(inputs);
await create({ inputs });
return { entityId };
};

View File

@ -14,8 +14,14 @@ const generatePassword = (size = 48) => {
return customAlphabet(charset, 48)(size);
};
const generateUsername = () => {
return alphaNumericNanoId(32);
const generateUsername = (usernameTemplate?: string | null) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({
randomUsername,
unixTimestamp: Math.floor(Date.now() / 100)
});
};
export const CassandraProvider = (): TDynamicProviderFns => {
@ -69,11 +75,12 @@ export const CassandraProvider = (): TDynamicProviderFns => {
return isConnected;
};
const create = async (inputs: unknown, expireAt: number) => {
const create = async (data: { inputs: unknown; expireAt: number; usernameTemplate?: string | null }) => {
const { inputs, expireAt, usernameTemplate } = data;
const providerInputs = await validateProviderInputs(inputs);
const client = await $getClient(providerInputs);
const username = generateUsername();
const username = generateUsername(usernameTemplate);
const password = generatePassword();
const { keyspace } = providerInputs;
const expiration = new Date(expireAt).toISOString();

View File

@ -1,4 +1,5 @@
import { Client as ElasticSearchClient } from "@elastic/elasticsearch";
import handlebars from "handlebars";
import { customAlphabet } from "nanoid";
import { z } from "zod";
@ -12,8 +13,14 @@ const generatePassword = () => {
return customAlphabet(charset, 64)();
};
const generateUsername = () => {
return alphaNumericNanoId(32);
const generateUsername = (usernameTemplate?: string | null) => {
const randomUsername = alphaNumericNanoId(32); // Username must start with an ascii letter, so we prepend the username with "inf-"
if (!usernameTemplate) return randomUsername;
return handlebars.compile(usernameTemplate)({
randomUsername,
unixTimestamp: Math.floor(Date.now() / 100)
});
};
export const ElasticSearchProvider = (): TDynamicProviderFns => {
@ -64,11 +71,12 @@ export const ElasticSearchProvider = (): TDynamicProviderFns => {
return infoResponse;
};
const create = async (inputs: unknown) => {
const create = async (data: { inputs: unknown; usernameTemplate?: string | null }) => {
const { inputs, usernameTemplate } = data;
const providerInputs = await validateProviderInputs(inputs);
const connection = await $getClient(providerInputs);
const username = generateUsername();
const username = generateUsername(usernameTemplate);
const password = generatePassword();
await connection.security.putUser({

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