Compare commits

..

346 Commits

Author SHA1 Message Date
53983d13f3 Fix: Delete access approval requests 2024-05-06 10:08:04 +02:00
5d9e47aec6 Fix: Migration timestamps and cleanup 2024-05-04 08:01:45 +02:00
be968be813 Fix: Fixed migration timestamp to match main 2024-05-04 07:53:22 +02:00
dc60d59e2e Merge branch 'daniel/fix-db-ref' of https://github.com/Infisical/infisical into daniel/fix-db-ref 2024-05-04 07:41:49 +02:00
e3f48e72b0 Feat: Secret approval deletion and more final changes 2024-05-04 07:41:44 +02:00
3c6b7aee9a Fix: Seperate groups / project users additional privileges 2024-05-04 07:41:44 +02:00
a183e94ff4 Schemas 2024-05-04 07:41:44 +02:00
b54e780443 Fix: Remove group-specific fields 2024-05-04 07:41:44 +02:00
5376bb72b3 Feat: Refactor to support groups 2024-05-04 07:41:44 +02:00
56d0d59ddc Feat: Group user additional privileges 2024-05-04 07:41:44 +02:00
ef9d4a4eee Update permission-dal.ts 2024-05-04 07:41:44 +02:00
873c6eea18 Fix: Updating approvers, now based on user ID 2024-05-04 07:41:44 +02:00
8d8e0bb794 Update project-membership-service.ts 2024-05-04 07:41:44 +02:00
348cf1c50c Fix: Cleanup secret & access approvals on project membership deletion 2024-05-04 07:41:44 +02:00
05669efdd8 Update group-project-service.ts 2024-05-04 07:41:44 +02:00
c302630551 Fix: Add project ID 2024-05-04 07:41:44 +02:00
2a4c9100be Fix ambiguous field 2024-05-04 07:41:44 +02:00
9ced5717ac Fix: Cleanup secret & access approvals on user group unassignment 2024-05-04 07:41:44 +02:00
b2f2541d0b Fix: Cleanup secret & access approvals on group deletion 2024-05-04 07:41:44 +02:00
3a9ad8d306 Update access-approval-request-service.ts 2024-05-04 07:41:44 +02:00
10207d03dd Fix: Delete potential privileges associated with access request on deletion 2024-05-04 07:41:44 +02:00
832dd62158 Schema update 2024-05-04 07:41:44 +02:00
df29f3499f Update 20240429175301_fix-db-reference-for-groups-and-project-memberships.ts 2024-05-04 07:41:44 +02:00
0d4c05f537 Update 20240429172301_access_approval_requests.ts 2024-05-04 07:41:44 +02:00
fb0407fec8 Fix: Duplicate users when user has both group access & project access 2024-05-04 07:41:44 +02:00
7d899463b4 Feat: Cleanup on group user removal 2024-05-04 07:41:44 +02:00
cfaf076352 Fix: Cleanup on membership delete & group disconnection 2024-05-04 07:41:44 +02:00
a875489172 Update access-approval-policy-router.ts 2024-05-04 07:41:44 +02:00
8634f8348b Chore: Cleanup 2024-05-04 07:41:44 +02:00
ad5b16d448 Type error 2024-05-04 07:41:44 +02:00
62e6acb7dc Update audit-log-types.ts 2024-05-04 07:41:44 +02:00
bf50eed8b0 Fix: Type errors 2024-05-04 07:41:44 +02:00
dcd69b5d99 Chore: Generate accurate schemas 2024-05-04 07:41:44 +02:00
dda98a0036 Removed comments 2024-05-04 07:41:44 +02:00
24ab66f61f Fix: Audit Log support for secret approval requests 2024-05-04 07:41:44 +02:00
9b97afad1c Update index.tsx 2024-05-04 07:41:44 +02:00
dca3832fd4 Update project-membership-types.ts 2024-05-04 07:41:44 +02:00
c3458a9d34 Update audit logs 2024-05-04 07:41:44 +02:00
76d371f13c Fix: Remove project membership dependency 2024-05-04 07:41:44 +02:00
7e9bcc5ce1 Chore: Spelling 2024-05-04 07:41:44 +02:00
029f2fa3af Fix: Refactor to use user ID's 2024-05-04 07:41:44 +02:00
0e9b2a8045 Update secret-approval-policy-service.ts 2024-05-04 07:41:44 +02:00
514cf07ba5 Fix: Refactor to use user ID's 2024-05-04 07:41:44 +02:00
55efc58566 Fix: Spelling and refactor to use user ID's 2024-05-04 07:41:44 +02:00
9b03f4984a Feat: Delete access approval request 2024-05-04 07:41:44 +02:00
a04f938b6c Update queries.tsx 2024-05-04 07:41:44 +02:00
596a22e9eb Fix: Update types 2024-05-04 07:41:44 +02:00
2ea01537c0 Update types.ts 2024-05-04 07:41:44 +02:00
ab57c658a8 Feat: Include group members option 2024-05-04 07:41:44 +02:00
89030c92c7 Fix: Secret approval refactor to user ID 2024-05-04 07:41:44 +02:00
d842aef714 Fix: Type errors 2024-05-04 07:41:44 +02:00
53089e26b9 Fix: Use user ID's instead of project memberships 2024-05-04 07:41:44 +02:00
be890b4189 Fix: Use user ID's instead of project memberships 2024-05-04 07:41:44 +02:00
cd0f126cf2 Update group-project-dal.ts 2024-05-04 07:41:44 +02:00
a092b9a19f Feat: Get group members 2024-05-04 07:41:44 +02:00
2f043849c9 Feat: Group support for project specific operations 2024-05-04 07:41:44 +02:00
d15fa9f176 Update index.ts 2024-05-04 07:41:44 +02:00
61508ec90a Feat: Delete access request 2024-05-04 07:41:44 +02:00
9d84bfa69e Feat: Additional priv support for groups 2024-05-04 07:41:44 +02:00
6a1d465778 Fix: Audit log support for user ID 2024-05-04 07:41:44 +02:00
5eff705486 Feat: Fix Groups for project membership specific operations 2024-05-04 07:41:44 +02:00
cd532bc20d Fix: Refactor to use new user ID field 2024-05-04 07:41:44 +02:00
18cdaaf024 Fix: Update for user ID 2024-05-04 07:41:44 +02:00
74e1dbdf9b Feat: Fix Groups for project membership specific operations (Manually modified) 2024-05-04 07:41:44 +02:00
64ab75748c Feat: Fix Groups for project membership specific operations 2024-05-04 07:41:44 +02:00
f7b689158d Update SpecificPrivilegeSection.tsx 2024-05-04 07:40:37 +02:00
19b9a31f0b Draft 2024-05-04 07:40:37 +02:00
0568cdcec6 Update instance recognition of offline license 2024-05-04 07:40:37 +02:00
a4bc459576 Fix: Duplicate access request check 2024-05-04 07:40:37 +02:00
b0b73acc21 Update SecretApprovalPage.tsx 2024-05-04 07:40:36 +02:00
07d66cbb65 Fix: Moved from email to username 2024-05-04 07:40:36 +02:00
ee97782860 Cleanup 2024-05-04 07:40:36 +02:00
c856de534b Fix: Move standalone components to individual files 2024-05-04 07:40:36 +02:00
eefd71f4cc Chore: Remove unused files 2024-05-04 07:40:36 +02:00
77e9609d0c Fix: Use username instead of email 2024-05-04 07:40:36 +02:00
afbbe5b7ba Fix: Columns 2024-05-04 07:40:36 +02:00
54d5cdedab Fix: Use username instead of email 2024-05-04 07:40:36 +02:00
9e12935a9f Feat: Badge component 2024-05-04 07:40:36 +02:00
101fa56d83 Fix: Moved Divider to v2 2024-05-04 07:40:36 +02:00
9bceb99110 Update index.ts 2024-05-04 07:40:36 +02:00
ca7a0a73be Fix: Pick 2024-05-04 07:40:36 +02:00
3632361f3c Chore: Moved verifyApprovers 2024-05-04 07:40:36 +02:00
f5c0274844 Fix: Make verifyApprovers independent on memberships 2024-05-04 07:40:36 +02:00
36a11387dd Fix: Made API endpoints more REST compliant 2024-05-04 07:40:36 +02:00
a82c94472a Chore: Cleaned up models 2024-05-04 07:40:36 +02:00
508f9610ca Fix: Improved migrations 2024-05-04 07:40:36 +02:00
59065c0648 Delete access-approval-request-secret-dal.ts 2024-05-04 07:40:36 +02:00
6443c94283 Fix: Don't display requested by when user has no access to read workspace members 2024-05-04 07:40:36 +02:00
26611881bc Fix: Don't display requested by when user has no access to read workspace members 2024-05-04 07:40:36 +02:00
2852989ac1 Fix: Add tooltip for clarity and fix wording 2024-05-04 07:40:36 +02:00
124bb7c205 Fix: Requesting approvals on previously rejected resources 2024-05-04 07:40:36 +02:00
697445cb1f Fix: Sort by createdAt 2024-05-04 07:40:36 +02:00
04108907ba Migration improvements 2024-05-04 07:40:36 +02:00
411cac2a31 Fixed bugs 2024-05-04 07:40:36 +02:00
afb9920fca Update SecretApprovalPage.tsx 2024-05-04 07:40:36 +02:00
ccf99d2465 Fix: Rebase errors 2024-05-04 07:40:36 +02:00
bca84f74c5 Removed unnessecary types 2024-05-04 07:40:36 +02:00
6c93973db7 Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
8d3f8c94fb Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
2eeb7dbc41 Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
f18624d2e4 style changes 2024-05-04 07:40:36 +02:00
42a49da17b Update licence-fns.ts 2024-05-04 07:40:36 +02:00
5d87ce866c Update SpecificPrivilegeSection.tsx 2024-05-04 07:40:36 +02:00
02d7f90ec2 Update generate-schema-types.ts 2024-05-04 07:40:36 +02:00
03564fc59b Update SecretApprovalPage.tsx 2024-05-04 07:40:36 +02:00
8669f5c39a Fix: Added support for request access 2024-05-04 07:40:36 +02:00
c2bd2e6963 Fix: Remove redundant code 2024-05-04 07:40:36 +02:00
eb23d114a2 Fix: Validate approvers access 2024-05-04 07:40:36 +02:00
dec2cd465b Feat: Request access (new routes) 2024-05-04 07:40:36 +02:00
4cdec49751 Feat: Request Access (migrations) 2024-05-04 07:40:36 +02:00
43967ef848 Feat: Request access 2024-05-04 07:40:36 +02:00
55046d4144 Draft 2024-05-04 07:40:36 +02:00
124acfd279 Fix: Multiple approvers acceptance bug 2024-05-04 07:40:36 +02:00
62e12269b8 Fix: Rename change -> secret 2024-05-04 07:40:36 +02:00
f03d8b718e Style: Fix styling 2024-05-04 07:40:36 +02:00
acf13df0f3 Capitalization 2024-05-04 07:40:36 +02:00
cb8ec57177 Removed unnessecary types 2024-05-04 07:40:36 +02:00
b543f2ce50 Remove unnessecary types and projectMembershipid 2024-05-04 07:40:36 +02:00
f852e629ef Renaming 2024-05-04 07:40:36 +02:00
58b74d97bb Update smtp-service.ts 2024-05-04 07:40:36 +02:00
ba12aab65a Feat: Find users by project membership ID's 2024-05-04 07:40:36 +02:00
952c4a3931 Feat: access request emails 2024-05-04 07:40:36 +02:00
4a1bae07ca Update index.ts 2024-05-04 07:40:36 +02:00
c24f72435a Update access-approval-request-types.ts 2024-05-04 07:40:36 +02:00
4bf378c28d Feat: Send emails for access requests 2024-05-04 07:40:36 +02:00
407c8e17d3 Feat: Request access, extract permission details 2024-05-04 07:40:36 +02:00
67b7fb819a Fix: Security vulnurbility making it possible to spoof env & secret path requested. 2024-05-04 07:40:36 +02:00
edfccb2ae2 Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
df8dc43bcf Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
0d610f2644 Update AccessApprovalRequest.tsx 2024-05-04 07:40:36 +02:00
a422d211fe style changes 2024-05-04 07:40:36 +02:00
f66d5e3d28 Fix: Status filtering & query invalidation 2024-05-04 07:40:36 +02:00
2c4e951fe2 Fix: Access request query invalidation 2024-05-04 07:40:36 +02:00
e23d2dff64 fix privilegeId issue 2024-05-04 07:40:35 +02:00
e7de6ad5d9 Fix: Request access permissions 2024-05-04 07:40:35 +02:00
ca0d79d664 Update licence-fns.ts 2024-05-04 07:40:35 +02:00
adc0552df0 Add count 2024-05-04 07:40:35 +02:00
cff79e7c8c Fix: Don't allow users to request access to the same resource with same permissions multiple times 2024-05-04 07:40:35 +02:00
450e653005 Removed unused parameter 2024-05-04 07:40:35 +02:00
c866e55d1b Removed logs 2024-05-04 07:40:35 +02:00
d66c2a85f4 Removed logs 2024-05-04 07:40:35 +02:00
3b8c0a5cb1 Update SpecificPrivilegeSection.tsx 2024-05-04 07:40:35 +02:00
b77f0fed45 Update generate-schema-types.ts 2024-05-04 07:40:35 +02:00
e8bc47b573 Update SecretApprovalPage.tsx 2024-05-04 07:40:35 +02:00
c6785eff3a Fix: Minor fixes 2024-05-04 07:40:35 +02:00
bc1a9055ee Create index.tsx 2024-05-04 07:40:35 +02:00
dbe1f2bcff Feat: Request access 2024-05-04 07:40:35 +02:00
15107ebfaa Feat: Request access 2024-05-04 07:40:35 +02:00
435a395a15 Feat: Request access 2024-05-04 07:40:35 +02:00
fe829af054 Fix: Move to project slug 2024-05-04 07:40:35 +02:00
bd9dc44a69 Fix: Move to project slug 2024-05-04 07:40:35 +02:00
765dd84d19 Fix: Move to project slug 2024-05-04 07:40:35 +02:00
ac100e17f4 Fix: Added support for request access 2024-05-04 07:40:35 +02:00
e349f9aa3b Feat: Request access 2024-05-04 07:40:35 +02:00
29c3c41ebb Update index.tsx 2024-05-04 07:40:35 +02:00
e4af0759b8 Fix: Improve disabled Select 2024-05-04 07:40:35 +02:00
c681774709 Fix: Access Request setup 2024-05-04 07:40:35 +02:00
f63f2d9c69 Fix: Danger color not working on disabled buttons 2024-05-04 07:40:35 +02:00
044662901a Fix: Remove redundant code 2024-05-04 07:40:35 +02:00
8cdb2082d9 Feat: Request Access 2024-05-04 07:40:35 +02:00
52d0f5e1be Feat: Request access 2024-05-04 07:40:35 +02:00
be1e7be0d5 Feat: Request access 2024-05-04 07:40:35 +02:00
e1b0bc1b97 Fix: Types mismatch 2024-05-04 07:40:35 +02:00
f05d1b9d95 Fix: Validate approvers access 2024-05-04 07:40:35 +02:00
fa2bd6a75e Feat: Request access 2024-05-04 07:40:35 +02:00
2402ce2a12 Fix: Access Approval Policy DAL bugs 2024-05-04 07:40:35 +02:00
f770a18d41 Feat: Request access (new routes) 2024-05-04 07:40:35 +02:00
8ab7470f74 Fix: Move to project slug 2024-05-04 07:40:35 +02:00
eb56c23db1 Feat: Request access (models) 2024-05-04 07:40:35 +02:00
14812adade Feat: Request Access (migrations) 2024-05-04 07:40:35 +02:00
99b1efffc7 Feat: Request access 2024-05-04 07:40:35 +02:00
af6189c82b Feat: Request access 2024-05-04 07:40:35 +02:00
b6ca18af5d Fix: Remove logs 2024-05-04 07:40:26 +02:00
ee7bb6d60d Feat: Request Access 2024-05-04 07:40:26 +02:00
bfde867ba7 Draft 2024-05-04 07:40:12 +02:00
1a20f3148c Feat: Secret approval deletion and more final changes 2024-05-04 07:38:17 +02:00
ce5b14222f Fix: Seperate groups / project users additional privileges 2024-05-04 07:38:17 +02:00
74a43d55f7 Schemas 2024-05-04 07:38:17 +02:00
85cce4274e Fix: Remove group-specific fields 2024-05-04 07:38:17 +02:00
9eb88836e9 Feat: Refactor to support groups 2024-05-04 07:38:17 +02:00
d6c9658747 Feat: Group user additional privileges 2024-05-04 07:38:17 +02:00
f9967c0cc8 Update permission-dal.ts 2024-05-04 07:38:17 +02:00
bd8dfe4089 Fix: Updating approvers, now based on user ID 2024-05-04 07:38:17 +02:00
03fcaadab2 Update project-membership-service.ts 2024-05-04 07:38:17 +02:00
d3a0a84815 Fix: Cleanup secret & access approvals on project membership deletion 2024-05-04 07:38:17 +02:00
49ae146470 Update group-project-service.ts 2024-05-04 07:38:17 +02:00
f73b362c84 Fix: Add project ID 2024-05-04 07:38:17 +02:00
d9043fa9e0 Fix ambiguous field 2024-05-04 07:38:17 +02:00
98f6dc8df9 Fix: Cleanup secret & access approvals on user group unassignment 2024-05-04 07:38:17 +02:00
12c67d921d Fix: Cleanup secret & access approvals on group deletion 2024-05-04 07:38:17 +02:00
7dea2ba916 Update access-approval-request-service.ts 2024-05-04 07:38:17 +02:00
ace27a3605 Fix: Delete potential privileges associated with access request on deletion 2024-05-04 07:38:17 +02:00
e85ea1a458 Schema update 2024-05-04 07:38:17 +02:00
fb16464fda Update 20240429175301_fix-db-reference-for-groups-and-project-memberships.ts 2024-05-04 07:38:17 +02:00
c6b636bb42 Update 20240429172301_access_approval_requests.ts 2024-05-04 07:38:17 +02:00
034ac68b58 Fix: Duplicate users when user has both group access & project access 2024-05-04 07:38:17 +02:00
33e2c52f14 Feat: Cleanup on group user removal 2024-05-04 07:38:17 +02:00
b435a06a92 Fix: Cleanup on membership delete & group disconnection 2024-05-04 07:38:17 +02:00
48c23db3f9 Update access-approval-policy-router.ts 2024-05-04 07:38:17 +02:00
3159972ec3 Chore: Cleanup 2024-05-04 07:38:17 +02:00
8a5c293a6e Type error 2024-05-04 07:38:17 +02:00
1d9c18d155 Update audit-log-types.ts 2024-05-04 07:38:17 +02:00
13945bb31d Fix: Type errors 2024-05-04 07:38:17 +02:00
9df9197cac Chore: Generate accurate schemas 2024-05-04 07:38:17 +02:00
3809729e31 Removed comments 2024-05-04 07:38:17 +02:00
03d29a4afc Fix: Audit Log support for secret approval requests 2024-05-04 07:38:17 +02:00
a4264335fe Update index.tsx 2024-05-04 07:38:16 +02:00
7752bab0f0 Update project-membership-types.ts 2024-05-04 07:38:16 +02:00
56a20dc397 Update audit logs 2024-05-04 07:38:16 +02:00
6f79d8bb6c Fix: Remove project membership dependency 2024-05-04 07:38:16 +02:00
044ac01100 Chore: Spelling 2024-05-04 07:38:16 +02:00
641c0308f9 Fix: Refactor to use user ID's 2024-05-04 07:38:16 +02:00
ecfb833797 Update secret-approval-policy-service.ts 2024-05-04 07:38:16 +02:00
256f14cf6a Fix: Refactor to use user ID's 2024-05-04 07:38:16 +02:00
32c28227b2 Fix: Spelling and refactor to use user ID's 2024-05-04 07:38:16 +02:00
3be6402727 Feat: Delete access approval request 2024-05-04 07:38:16 +02:00
90c09c64cb Update queries.tsx 2024-05-04 07:38:16 +02:00
d0da69b999 Fix: Update types 2024-05-04 07:38:16 +02:00
7fb3730b22 Update types.ts 2024-05-04 07:38:16 +02:00
49e154ddd1 Feat: Include group members option 2024-05-04 07:38:16 +02:00
3742976bcb Fix: Secret approval refactor to user ID 2024-05-04 07:38:16 +02:00
5695137f24 Fix: Type errors 2024-05-04 07:38:16 +02:00
13d7cfd41b Fix: Use user ID's instead of project memberships 2024-05-04 07:38:16 +02:00
81fc5d3c18 Fix: Use user ID's instead of project memberships 2024-05-04 07:38:16 +02:00
8e8f44895d Update group-project-dal.ts 2024-05-04 07:38:16 +02:00
45570490a0 Feat: Get group members 2024-05-04 07:38:16 +02:00
1add5d6a24 Feat: Group support for project specific operations 2024-05-04 07:38:16 +02:00
7ac0536236 Update index.ts 2024-05-04 07:38:16 +02:00
89e9f46ae5 Feat: Delete access request 2024-05-04 07:38:16 +02:00
e3728b8a61 Feat: Additional priv support for groups 2024-05-04 07:38:16 +02:00
92bbabde3c Fix: Audit log support for user ID 2024-05-04 07:38:16 +02:00
11b4c5381a Feat: Fix Groups for project membership specific operations 2024-05-04 07:38:16 +02:00
97496c1b3c Fix: Refactor to use new user ID field 2024-05-04 07:38:16 +02:00
3cac1acf08 Fix: Update for user ID 2024-05-04 07:38:16 +02:00
c3756b8cc0 Feat: Fix Groups for project membership specific operations (Manually modified) 2024-05-04 07:38:16 +02:00
8678c79c02 Feat: Fix Groups for project membership specific operations 2024-05-04 07:38:16 +02:00
d2f010d17d Update SpecificPrivilegeSection.tsx 2024-05-04 07:38:16 +02:00
5c8d5e8430 Draft 2024-05-04 07:38:16 +02:00
7c8d99875a Update instance recognition of offline license 2024-05-04 07:38:16 +02:00
ab30b0803f Fix: Duplicate access request check 2024-05-04 07:38:16 +02:00
e2d68f07d1 Update SecretApprovalPage.tsx 2024-05-04 07:38:16 +02:00
07ced66538 Fix: Moved from email to username 2024-05-04 07:38:16 +02:00
9cb0ec231b Cleanup 2024-05-04 07:38:16 +02:00
8b169b2b9e Fix: Move standalone components to individual files 2024-05-04 07:38:16 +02:00
b9c02264c7 Chore: Remove unused files 2024-05-04 07:38:16 +02:00
9f96a9d188 Fix: Use username instead of email 2024-05-04 07:38:16 +02:00
55f232a642 Fix: Columns 2024-05-04 07:38:16 +02:00
34ff65d09c Fix: Use username instead of email 2024-05-04 07:38:16 +02:00
fe38c79f68 Feat: Badge component 2024-05-04 07:38:16 +02:00
a8aecc378b Fix: Moved Divider to v2 2024-05-04 07:38:16 +02:00
9ce7161aea Update index.ts 2024-05-04 07:38:16 +02:00
1951ca723c Fix: Pick 2024-05-04 07:38:16 +02:00
416f85f7e2 Chore: Moved verifyApprovers 2024-05-04 07:38:16 +02:00
75bef6fc8b Fix: Make verifyApprovers independent on memberships 2024-05-04 07:38:16 +02:00
5fa6e8bcf2 Fix: Made API endpoints more REST compliant 2024-05-04 07:38:16 +02:00
f4a5d9c391 Chore: Cleaned up models 2024-05-04 07:38:16 +02:00
3c6b976d8f Fix: Improved migrations 2024-05-04 07:38:16 +02:00
787d2287a0 Delete access-approval-request-secret-dal.ts 2024-05-04 07:38:16 +02:00
92f73d66f0 Fix: Don't display requested by when user has no access to read workspace members 2024-05-04 07:38:16 +02:00
3cd8670064 Fix: Don't display requested by when user has no access to read workspace members 2024-05-04 07:38:15 +02:00
4e3dd15d67 Fix: Add tooltip for clarity and fix wording 2024-05-04 07:38:15 +02:00
4c97ba1221 Fix: Requesting approvals on previously rejected resources 2024-05-04 07:38:15 +02:00
b89128fb32 Fix: Sort by createdAt 2024-05-04 07:38:15 +02:00
c788c0cb80 Migration improvements 2024-05-04 07:38:15 +02:00
e3fde17622 Fixed bugs 2024-05-04 07:38:15 +02:00
2eb9f30ef5 Update SecretApprovalPage.tsx 2024-05-04 07:38:15 +02:00
9432f3ce4a Fix: Rebase errors 2024-05-04 07:38:15 +02:00
5393afbd05 Removed unnessecary types 2024-05-04 07:38:15 +02:00
cb304d9a10 Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
a5f29db670 Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
009b49685c Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
90d3f4d643 style changes 2024-05-04 07:38:15 +02:00
cc08d31300 Update licence-fns.ts 2024-05-04 07:38:15 +02:00
71deb7c62a Update SpecificPrivilegeSection.tsx 2024-05-04 07:38:15 +02:00
efec1c0a96 Update generate-schema-types.ts 2024-05-04 07:38:15 +02:00
efa4b7a4b6 Update SecretApprovalPage.tsx 2024-05-04 07:38:15 +02:00
65e0077d6c Fix: Added support for request access 2024-05-04 07:38:15 +02:00
1be311ffd9 Fix: Remove redundant code 2024-05-04 07:38:15 +02:00
3994962d0b Fix: Validate approvers access 2024-05-04 07:38:15 +02:00
0b9334f34c Feat: Request access (new routes) 2024-05-04 07:38:15 +02:00
2b4396547d Feat: Request Access (migrations) 2024-05-04 07:38:15 +02:00
761ec8dcc0 Feat: Request access 2024-05-04 07:38:15 +02:00
56e69bc5e9 Draft 2024-05-04 07:38:15 +02:00
067faef6a2 Fix: Multiple approvers acceptance bug 2024-05-04 07:38:15 +02:00
026b934a87 Fix: Rename change -> secret 2024-05-04 07:38:15 +02:00
90eef0495e Style: Fix styling 2024-05-04 07:38:15 +02:00
119fe97b14 Capitalization 2024-05-04 07:38:15 +02:00
dab3daee86 Removed unnessecary types 2024-05-04 07:38:15 +02:00
f2e344c11d Remove unnessecary types and projectMembershipid 2024-05-04 07:38:15 +02:00
df1a879e73 Renaming 2024-05-04 07:38:15 +02:00
fb21d4e13d Update smtp-service.ts 2024-05-04 07:38:15 +02:00
ca6f50a257 Feat: Find users by project membership ID's 2024-05-04 07:38:15 +02:00
26f0adbf7e Feat: access request emails 2024-05-04 07:38:15 +02:00
456d9ca5ce Update index.ts 2024-05-04 07:38:15 +02:00
e652fd962c Update access-approval-request-types.ts 2024-05-04 07:38:15 +02:00
bc16484f3f Feat: Send emails for access requests 2024-05-04 07:38:15 +02:00
4e87cc7c28 Feat: Request access, extract permission details 2024-05-04 07:38:15 +02:00
d9e2b99338 Fix: Security vulnurbility making it possible to spoof env & secret path requested. 2024-05-04 07:38:15 +02:00
9bac996c7a Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
089d57ea59 Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
14a17d638d Update AccessApprovalRequest.tsx 2024-05-04 07:38:15 +02:00
5d9755b332 style changes 2024-05-04 07:38:15 +02:00
4f6b73518e Fix: Status filtering & query invalidation 2024-05-04 07:38:15 +02:00
2f4965659c Fix: Access request query invalidation 2024-05-04 07:38:15 +02:00
2dc12693b0 fix privilegeId issue 2024-05-04 07:38:15 +02:00
5305139a55 Fix: Request access permissions 2024-05-04 07:38:15 +02:00
db72c07e81 Update licence-fns.ts 2024-05-04 07:38:15 +02:00
2f3ae5429a Add count 2024-05-04 07:38:15 +02:00
56e216c37c Fix: Don't allow users to request access to the same resource with same permissions multiple times 2024-05-04 07:38:14 +02:00
8db3544885 Removed unused parameter 2024-05-04 07:38:14 +02:00
f196c6a0ce Removed logs 2024-05-04 07:38:14 +02:00
246eecc23c Removed logs 2024-05-04 07:38:14 +02:00
013b744706 Update SpecificPrivilegeSection.tsx 2024-05-04 07:38:14 +02:00
fe68328aeb Update generate-schema-types.ts 2024-05-04 07:38:14 +02:00
1dcfd14431 Update SecretApprovalPage.tsx 2024-05-04 07:38:14 +02:00
f232f00f77 Fix: Minor fixes 2024-05-04 07:38:14 +02:00
82517477cb Create index.tsx 2024-05-04 07:38:14 +02:00
4e149cce81 Feat: Request access 2024-05-04 07:38:14 +02:00
5894cb4049 Feat: Request access 2024-05-04 07:38:14 +02:00
4938dda303 Feat: Request access 2024-05-04 07:38:14 +02:00
62112447a6 Fix: Move to project slug 2024-05-04 07:38:14 +02:00
bead911e0f Fix: Move to project slug 2024-05-04 07:38:14 +02:00
49987ca1e5 Fix: Move to project slug 2024-05-04 07:38:14 +02:00
cec083aa9b Fix: Added support for request access 2024-05-04 07:38:14 +02:00
9146079317 Feat: Request access 2024-05-04 07:38:14 +02:00
243ffc9904 Update index.tsx 2024-05-04 07:38:14 +02:00
b07d29faa2 Fix: Improve disabled Select 2024-05-04 07:38:14 +02:00
60fcc42d8c Fix: Access Request setup 2024-05-04 07:38:14 +02:00
fabf7181fa Fix: Danger color not working on disabled buttons 2024-05-04 07:38:14 +02:00
c9a7b6abb6 Fix: Remove redundant code 2024-05-04 07:38:14 +02:00
3c53befb3e Feat: Request Access 2024-05-04 07:38:14 +02:00
1f0cf6cc9b Feat: Request access 2024-05-04 07:38:14 +02:00
84c19a7554 Feat: Request access 2024-05-04 07:38:14 +02:00
d3ea91c54b Fix: Types mismatch 2024-05-04 07:38:14 +02:00
ecb58b8680 Fix: Validate approvers access 2024-05-04 07:38:14 +02:00
1972a3c6ed Feat: Request access 2024-05-04 07:38:14 +02:00
a0b1fb23df Fix: Access Approval Policy DAL bugs 2024-05-04 07:38:14 +02:00
5910c11d88 Feat: Request access (new routes) 2024-05-04 07:38:14 +02:00
a768496c5e Fix: Move to project slug 2024-05-04 07:38:14 +02:00
e59cc138d9 Feat: Request access (models) 2024-05-04 07:38:14 +02:00
7a7d41ca83 Feat: Request Access (migrations) 2024-05-04 07:38:14 +02:00
bd8b56a224 Feat: Request access 2024-05-04 07:38:14 +02:00
aa5bd117e6 Feat: Request access 2024-05-04 07:38:14 +02:00
e66e6a7490 Fix: Remove logs 2024-05-04 07:38:00 +02:00
e54f499026 Feat: Request Access 2024-05-04 07:38:00 +02:00
7a5e0e9463 Draft 2024-05-04 07:37:42 +02:00
730 changed files with 19660 additions and 24860 deletions

View File

@ -63,7 +63,3 @@ CLIENT_SECRET_GITHUB_LOGIN=
CLIENT_ID_GITLAB_LOGIN=
CLIENT_SECRET_GITLAB_LOGIN=
CAPTCHA_SECRET=
NEXT_PUBLIC_CAPTCHA_SITE_KEY=

View File

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

View File

@ -40,7 +40,6 @@ jobs:
REDIS_URL: redis://172.17.0.1:6379
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
JWT_AUTH_SECRET: something-random
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
- uses: actions/setup-go@v5
with:
go-version: '1.21.5'
@ -74,4 +73,4 @@ jobs:
run: |
docker-compose -f "docker-compose.dev.yml" down
docker stop infisical-api
docker remove infisical-api
docker remove infisical-api

View File

@ -22,9 +22,6 @@ jobs:
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }}
CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }}
CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
goreleaser:
runs-on: ubuntu-20.04
@ -59,7 +56,7 @@ jobs:
- uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser-pro
version: v1.26.2-pro
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}

View File

@ -20,12 +20,7 @@ on:
required: true
CLI_TESTS_ENV_SLUG:
required: true
CLI_TESTS_USER_EMAIL:
required: true
CLI_TESTS_USER_PASSWORD:
required: true
CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE:
required: true
jobs:
test:
defaults:
@ -48,8 +43,5 @@ jobs:
CLI_TESTS_SERVICE_TOKEN: ${{ secrets.CLI_TESTS_SERVICE_TOKEN }}
CLI_TESTS_PROJECT_ID: ${{ secrets.CLI_TESTS_PROJECT_ID }}
CLI_TESTS_ENV_SLUG: ${{ secrets.CLI_TESTS_ENV_SLUG }}
CLI_TESTS_USER_EMAIL: ${{ secrets.CLI_TESTS_USER_EMAIL }}
CLI_TESTS_USER_PASSWORD: ${{ secrets.CLI_TESTS_USER_PASSWORD }}
INFISICAL_VAULT_FILE_PASSPHRASE: ${{ secrets.CLI_TESTS_INFISICAL_VAULT_FILE_PASSPHRASE }}
run: go test -v -count=1 ./test

View File

@ -38,16 +38,6 @@ jobs:
rm added_files.txt
git commit -m "chore: renamed new migration files to latest timestamp (gh-action)"
- name: Get PR details
id: pr_details
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
PR_MERGER=$(curl -s "https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER" | jq -r '.merged_by.login')
echo "PR Number: $PR_NUMBER"
echo "PR Merger: $PR_MERGER"
echo "pr_merger=$PR_MERGER" >> $GITHUB_OUTPUT
- name: Create Pull Request
if: env.SKIP_RENAME != 'true'
uses: peter-evans/create-pull-request@v6
@ -56,4 +46,3 @@ jobs:
commit-message: 'chore: renamed new migration files to latest UTC (gh-action)'
title: 'GH Action: rename new migration file timestamp'
branch-suffix: timestamp
reviewers: ${{ steps.pr_details.outputs.pr_merger }}

View File

@ -3,5 +3,4 @@ frontend/src/views/Project/MembersPage/components/IdentityTab/components/Identit
frontend/src/views/Project/MembersPage/components/IdentityTab/components/IdentityRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:304
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/MemberRbacSection.tsx:generic-api-key:206
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:292
docs/self-hosting/configuration/envars.mdx:generic-api-key:106
frontend/src/views/Project/MembersPage/components/MemberListTab/MemberRoleForm/SpecificPrivilegeSection.tsx:generic-api-key:451

View File

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

View File

@ -85,13 +85,13 @@ To set up and run Infisical locally, make sure you have Git and Docker installed
Linux/macOS:
```console
git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker compose -f docker-compose.prod.yml up
git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.prod.yml up
```
Windows Command Prompt:
```console
git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker compose -f docker-compose.prod.yml up
git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.prod.yml up
```
Create an account at `http://localhost:80`

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -14,7 +14,6 @@ import { TLdapConfigServiceFactory } from "@app/ee/services/ldap-config/ldap-con
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { TProjectUserAdditionalPrivilegeServiceFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-service";
import { TRateLimitServiceFactory } from "@app/ee/services/rate-limit/rate-limit-service";
import { TSamlConfigServiceFactory } from "@app/ee/services/saml-config/saml-config-service";
import { TScimServiceFactory } from "@app/ee/services/scim/scim-service";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
@ -33,10 +32,6 @@ import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-se
import { TGroupProjectServiceFactory } from "@app/services/group-project/group-project-service";
import { TIdentityServiceFactory } from "@app/services/identity/identity-service";
import { TIdentityAccessTokenServiceFactory } from "@app/services/identity-access-token/identity-access-token-service";
import { TIdentityAwsAuthServiceFactory } from "@app/services/identity-aws-auth/identity-aws-auth-service";
import { TIdentityAzureAuthServiceFactory } from "@app/services/identity-azure-auth/identity-azure-auth-service";
import { TIdentityGcpAuthServiceFactory } from "@app/services/identity-gcp-auth/identity-gcp-auth-service";
import { TIdentityKubernetesAuthServiceFactory } from "@app/services/identity-kubernetes-auth/identity-kubernetes-auth-service";
import { TIdentityProjectServiceFactory } from "@app/services/identity-project/identity-project-service";
import { TIdentityUaServiceFactory } from "@app/services/identity-ua/identity-ua-service";
import { TIntegrationServiceFactory } from "@app/services/integration/integration-service";
@ -53,8 +48,6 @@ import { TSecretServiceFactory } from "@app/services/secret/secret-service";
import { TSecretBlindIndexServiceFactory } from "@app/services/secret-blind-index/secret-blind-index-service";
import { TSecretFolderServiceFactory } from "@app/services/secret-folder/secret-folder-service";
import { TSecretImportServiceFactory } from "@app/services/secret-import/secret-import-service";
import { TSecretReplicationServiceFactory } from "@app/services/secret-replication/secret-replication-service";
import { TSecretSharingServiceFactory } from "@app/services/secret-sharing/secret-sharing-service";
import { TSecretTagServiceFactory } from "@app/services/secret-tag/secret-tag-service";
import { TServiceTokenServiceFactory } from "@app/services/service-token/service-token-service";
import { TSuperAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
@ -110,7 +103,6 @@ declare module "fastify" {
projectKey: TProjectKeyServiceFactory;
projectRole: TProjectRoleServiceFactory;
secret: TSecretServiceFactory;
secretReplication: TSecretReplicationServiceFactory;
secretTag: TSecretTagServiceFactory;
secretImport: TSecretImportServiceFactory;
projectBot: TProjectBotServiceFactory;
@ -123,10 +115,6 @@ declare module "fastify" {
identityAccessToken: TIdentityAccessTokenServiceFactory;
identityProject: TIdentityProjectServiceFactory;
identityUa: TIdentityUaServiceFactory;
identityKubernetesAuth: TIdentityKubernetesAuthServiceFactory;
identityGcpAuth: TIdentityGcpAuthServiceFactory;
identityAwsAuth: TIdentityAwsAuthServiceFactory;
identityAzureAuth: TIdentityAzureAuthServiceFactory;
accessApprovalPolicy: TAccessApprovalPolicyServiceFactory;
accessApprovalRequest: TAccessApprovalRequestServiceFactory;
secretApprovalPolicy: TSecretApprovalPolicyServiceFactory;
@ -147,8 +135,6 @@ declare module "fastify" {
dynamicSecretLease: TDynamicSecretLeaseServiceFactory;
projectUserAdditionalPrivilege: TProjectUserAdditionalPrivilegeServiceFactory;
identityProjectAdditionalPrivilege: TIdentityProjectAdditionalPrivilegeServiceFactory;
secretSharing: TSecretSharingServiceFactory;
rateLimit: TRateLimitServiceFactory;
};
// this is exclusive use for middlewares in which we need to inject data
// everywhere else access using service layer

View File

@ -50,6 +50,9 @@ import {
TGroupProjectMemberships,
TGroupProjectMembershipsInsert,
TGroupProjectMembershipsUpdate,
TGroupProjectUserAdditionalPrivilege,
TGroupProjectUserAdditionalPrivilegeInsert,
TGroupProjectUserAdditionalPrivilegeUpdate,
TGroups,
TGroupsInsert,
TGroupsUpdate,
@ -59,18 +62,6 @@ import {
TIdentityAccessTokens,
TIdentityAccessTokensInsert,
TIdentityAccessTokensUpdate,
TIdentityAwsAuths,
TIdentityAwsAuthsInsert,
TIdentityAwsAuthsUpdate,
TIdentityAzureAuths,
TIdentityAzureAuthsInsert,
TIdentityAzureAuthsUpdate,
TIdentityGcpAuths,
TIdentityGcpAuthsInsert,
TIdentityGcpAuthsUpdate,
TIdentityKubernetesAuths,
TIdentityKubernetesAuthsInsert,
TIdentityKubernetesAuthsUpdate,
TIdentityOrgMemberships,
TIdentityOrgMembershipsInsert,
TIdentityOrgMembershipsUpdate,
@ -98,15 +89,6 @@ import {
TIntegrations,
TIntegrationsInsert,
TIntegrationsUpdate,
TKmsKeys,
TKmsKeysInsert,
TKmsKeysUpdate,
TKmsKeyVersions,
TKmsKeyVersionsInsert,
TKmsKeyVersionsUpdate,
TKmsRootConfig,
TKmsRootConfigInsert,
TKmsRootConfigUpdate,
TLdapConfigs,
TLdapConfigsInsert,
TLdapConfigsUpdate,
@ -149,9 +131,6 @@ import {
TProjectUserMembershipRoles,
TProjectUserMembershipRolesInsert,
TProjectUserMembershipRolesUpdate,
TRateLimit,
TRateLimitInsert,
TRateLimitUpdate,
TSamlConfigs,
TSamlConfigsInsert,
TSamlConfigsUpdate,
@ -188,9 +167,6 @@ import {
TSecretImports,
TSecretImportsInsert,
TSecretImportsUpdate,
TSecretReferences,
TSecretReferencesInsert,
TSecretReferencesUpdate,
TSecretRotationOutputs,
TSecretRotationOutputsInsert,
TSecretRotationOutputsUpdate,
@ -201,9 +177,6 @@ import {
TSecretScanningGitRisks,
TSecretScanningGitRisksInsert,
TSecretScanningGitRisksUpdate,
TSecretSharing,
TSecretSharingInsert,
TSecretSharingUpdate,
TSecretsInsert,
TSecretSnapshotFolders,
TSecretSnapshotFoldersInsert,
@ -320,6 +293,11 @@ declare module "knex/types/tables" {
TProjectUserMembershipRolesInsert,
TProjectUserMembershipRolesUpdate
>;
[TableName.GroupProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
TGroupProjectUserAdditionalPrivilege,
TGroupProjectUserAdditionalPrivilegeInsert,
TGroupProjectUserAdditionalPrivilegeUpdate
>;
[TableName.ProjectRoles]: Knex.CompositeTableType<TProjectRoles, TProjectRolesInsert, TProjectRolesUpdate>;
[TableName.ProjectUserAdditionalPrivilege]: Knex.CompositeTableType<
TProjectUserAdditionalPrivilege,
@ -328,11 +306,6 @@ declare module "knex/types/tables" {
>;
[TableName.ProjectKeys]: Knex.CompositeTableType<TProjectKeys, TProjectKeysInsert, TProjectKeysUpdate>;
[TableName.Secret]: Knex.CompositeTableType<TSecrets, TSecretsInsert, TSecretsUpdate>;
[TableName.SecretReference]: Knex.CompositeTableType<
TSecretReferences,
TSecretReferencesInsert,
TSecretReferencesUpdate
>;
[TableName.SecretBlindIndex]: Knex.CompositeTableType<
TSecretBlindIndexes,
TSecretBlindIndexesInsert,
@ -345,8 +318,6 @@ declare module "knex/types/tables" {
TSecretFolderVersionsInsert,
TSecretFolderVersionsUpdate
>;
[TableName.SecretSharing]: Knex.CompositeTableType<TSecretSharing, TSecretSharingInsert, TSecretSharingUpdate>;
[TableName.RateLimit]: Knex.CompositeTableType<TRateLimit, TRateLimitInsert, TRateLimitUpdate>;
[TableName.SecretTag]: Knex.CompositeTableType<TSecretTags, TSecretTagsInsert, TSecretTagsUpdate>;
[TableName.SecretImport]: Knex.CompositeTableType<TSecretImports, TSecretImportsInsert, TSecretImportsUpdate>;
[TableName.Integration]: Knex.CompositeTableType<TIntegrations, TIntegrationsInsert, TIntegrationsUpdate>;
@ -363,26 +334,6 @@ declare module "knex/types/tables" {
TIdentityUniversalAuthsInsert,
TIdentityUniversalAuthsUpdate
>;
[TableName.IdentityKubernetesAuth]: Knex.CompositeTableType<
TIdentityKubernetesAuths,
TIdentityKubernetesAuthsInsert,
TIdentityKubernetesAuthsUpdate
>;
[TableName.IdentityGcpAuth]: Knex.CompositeTableType<
TIdentityGcpAuths,
TIdentityGcpAuthsInsert,
TIdentityGcpAuthsUpdate
>;
[TableName.IdentityAwsAuth]: Knex.CompositeTableType<
TIdentityAwsAuths,
TIdentityAwsAuthsInsert,
TIdentityAwsAuthsUpdate
>;
[TableName.IdentityAzureAuth]: Knex.CompositeTableType<
TIdentityAzureAuths,
TIdentityAzureAuthsInsert,
TIdentityAzureAuthsUpdate
>;
[TableName.IdentityUaClientSecret]: Knex.CompositeTableType<
TIdentityUaClientSecrets,
TIdentityUaClientSecretsInsert,
@ -529,13 +480,5 @@ declare module "knex/types/tables" {
TSecretVersionTagJunctionInsert,
TSecretVersionTagJunctionUpdate
>;
// KMS service
[TableName.KmsServerRootConfig]: Knex.CompositeTableType<
TKmsRootConfig,
TKmsRootConfigInsert,
TKmsRootConfigUpdate
>;
[TableName.KmsKey]: Knex.CompositeTableType<TKmsKeys, TKmsKeysInsert, TKmsKeysUpdate>;
[TableName.KmsKeyVersion]: Knex.CompositeTableType<TKmsKeyVersions, TKmsKeyVersionsInsert, TKmsKeyVersionsUpdate>;
}
}

View File

@ -9,9 +9,8 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("name").notNullable();
t.integer("approvals").defaultTo(1).notNullable();
t.string("secretPath");
t.uuid("envId").notNullable();
t.string("secretPath");
t.foreign("envId").references("id").inTable(TableName.Environment).onDelete("CASCADE");
t.timestamps(true, true, true);
});
@ -21,8 +20,9 @@ export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.AccessApprovalPolicyApprover))) {
await knex.schema.createTable(TableName.AccessApprovalPolicyApprover, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("approverId").notNullable();
t.foreign("approverId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.uuid("approverUserId").nullable();
t.foreign("approverUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("policyId").notNullable();
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");

View File

@ -0,0 +1,37 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.GroupProjectUserAdditionalPrivilege))) {
await knex.schema.createTable(TableName.GroupProjectUserAdditionalPrivilege, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("slug", 60).notNullable();
t.uuid("groupProjectMembershipId").notNullable();
t.foreign("groupProjectMembershipId")
.references("id")
.inTable(TableName.GroupProjectMembership)
.onDelete("CASCADE");
t.uuid("requestedByUserId").notNullable();
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.boolean("isTemporary").notNullable().defaultTo(false);
t.string("temporaryMode");
t.string("temporaryRange"); // could be cron or relative time like 1H or 1minute etc
t.datetime("temporaryAccessStartTime");
t.datetime("temporaryAccessEndTime");
t.jsonb("permissions").notNullable();
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
}
export async function down(knex: Knex): Promise<void> {
await dropOnUpdateTrigger(knex, TableName.GroupProjectUserAdditionalPrivilege);
await knex.schema.dropTableIfExists(TableName.GroupProjectUserAdditionalPrivilege);
}

View File

@ -11,11 +11,26 @@ export async function up(knex: Knex): Promise<void> {
t.uuid("policyId").notNullable();
t.foreign("policyId").references("id").inTable(TableName.AccessApprovalPolicy).onDelete("CASCADE");
t.uuid("privilegeId").nullable();
t.foreign("privilegeId").references("id").inTable(TableName.ProjectUserAdditionalPrivilege).onDelete("CASCADE");
t.uuid("projectUserPrivilegeId").nullable();
t.foreign("projectUserPrivilegeId")
.references("id")
.inTable(TableName.ProjectUserAdditionalPrivilege)
.onDelete("CASCADE");
t.uuid("requestedBy").notNullable();
t.foreign("requestedBy").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.uuid("groupProjectUserPrivilegeId").nullable();
t.foreign("groupProjectUserPrivilegeId")
.references("id")
.inTable(TableName.GroupProjectUserAdditionalPrivilege)
.onDelete("CASCADE");
t.uuid("requestedByUserId").notNullable();
t.foreign("requestedByUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.uuid("projectMembershipId").nullable();
t.foreign("projectMembershipId").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.uuid("groupMembershipId").nullable();
t.foreign("groupMembershipId").references("id").inTable(TableName.GroupProjectMembership).onDelete("CASCADE");
// We use these values to create the actual privilege at a later point in time.
t.boolean("isTemporary").notNullable();
@ -31,14 +46,17 @@ export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.AccessApprovalRequestReviewer))) {
await knex.schema.createTable(TableName.AccessApprovalRequestReviewer, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.uuid("member").notNullable();
t.foreign("member").references("id").inTable(TableName.ProjectMembership).onDelete("CASCADE");
t.uuid("memberUserId").notNullable();
t.foreign("memberUserId").references("id").inTable(TableName.Users).onDelete("CASCADE");
t.string("status").notNullable();
t.uuid("requestId").notNullable();
t.foreign("requestId").references("id").inTable(TableName.AccessApprovalRequest).onDelete("CASCADE");
t.timestamps(true, true, true);
});
}
await createOnUpdateTrigger(knex, TableName.AccessApprovalRequestReviewer);
}

View File

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

View File

@ -1,54 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const isUsersTablePresent = await knex.schema.hasTable(TableName.Users);
if (isUsersTablePresent) {
const hasIsEmailVerifiedColumn = await knex.schema.hasColumn(TableName.Users, "isEmailVerified");
if (!hasIsEmailVerifiedColumn) {
await knex.schema.alterTable(TableName.Users, (t) => {
t.boolean("isEmailVerified").defaultTo(false);
});
}
// Backfilling the isEmailVerified to true where isAccepted is true
await knex(TableName.Users).update({ isEmailVerified: true }).where("isAccepted", true);
}
const isUserAliasTablePresent = await knex.schema.hasTable(TableName.UserAliases);
if (isUserAliasTablePresent) {
await knex.schema.alterTable(TableName.UserAliases, (t) => {
t.string("username").nullable().alter();
});
}
const isSuperAdminTablePresent = await knex.schema.hasTable(TableName.SuperAdmin);
if (isSuperAdminTablePresent) {
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
t.boolean("trustSamlEmails").defaultTo(false);
t.boolean("trustLdapEmails").defaultTo(false);
});
}
}
export async function down(knex: Knex): Promise<void> {
if (await knex.schema.hasColumn(TableName.Users, "isEmailVerified")) {
await knex.schema.alterTable(TableName.Users, (t) => {
t.dropColumn("isEmailVerified");
});
}
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustSamlEmails")) {
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
t.dropColumn("trustSamlEmails");
});
}
if (await knex.schema.hasColumn(TableName.SuperAdmin, "trustLdapEmails")) {
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
t.dropColumn("trustLdapEmails");
});
}
}

View File

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

View File

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

View File

@ -1,24 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.SecretReference))) {
await knex.schema.createTable(TableName.SecretReference, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.string("environment").notNullable();
t.string("secretPath").notNullable();
t.uuid("secretId").notNullable();
t.foreign("secretId").references("id").inTable(TableName.Secret).onDelete("CASCADE");
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.SecretReference);
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SecretReference);
await dropOnUpdateTrigger(knex, TableName.SecretReference);
}

View File

@ -1,36 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.IdentityKubernetesAuth))) {
await knex.schema.createTable(TableName.IdentityKubernetesAuth, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.bigInteger("accessTokenTTL").defaultTo(7200).notNullable();
t.bigInteger("accessTokenMaxTTL").defaultTo(7200).notNullable();
t.bigInteger("accessTokenNumUsesLimit").defaultTo(0).notNullable();
t.jsonb("accessTokenTrustedIps").notNullable();
t.timestamps(true, true, true);
t.uuid("identityId").notNullable().unique();
t.foreign("identityId").references("id").inTable(TableName.Identity).onDelete("CASCADE");
t.string("kubernetesHost").notNullable();
t.text("encryptedCaCert").notNullable();
t.string("caCertIV").notNullable();
t.string("caCertTag").notNullable();
t.text("encryptedTokenReviewerJwt").notNullable();
t.string("tokenReviewerJwtIV").notNullable();
t.string("tokenReviewerJwtTag").notNullable();
t.string("allowedNamespaces").notNullable();
t.string("allowedNames").notNullable();
t.string("allowedAudience").notNullable();
});
}
await createOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.IdentityKubernetesAuth);
await dropOnUpdateTrigger(knex, TableName.IdentityKubernetesAuth);
}

View File

@ -1,43 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
await knex.schema.alterTable(TableName.Integration, (t) => {
if (!hasIsSyncedColumn) {
t.boolean("isSynced").nullable();
}
if (!hasSyncMessageColumn) {
t.text("syncMessage").nullable();
}
if (!hasLastSyncJobId) {
t.string("lastSyncJobId").nullable();
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasIsSyncedColumn = await knex.schema.hasColumn(TableName.Integration, "isSynced");
const hasSyncMessageColumn = await knex.schema.hasColumn(TableName.Integration, "syncMessage");
const hasLastSyncJobId = await knex.schema.hasColumn(TableName.Integration, "lastSyncJobId");
await knex.schema.alterTable(TableName.Integration, (t) => {
if (hasIsSyncedColumn) {
t.dropColumn("isSynced");
}
if (hasSyncMessageColumn) {
t.dropColumn("syncMessage");
}
if (hasLastSyncJobId) {
t.dropColumn("lastSyncJobId");
}
});
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,24 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
if (await knex.schema.hasTable(TableName.Secret)) {
await knex.schema.alterTable(TableName.Secret, (t) => {
if (doesFolderIdExist && doesUserIdExist) t.index(["folderId", "userId"]);
});
}
}
export async function down(knex: Knex): Promise<void> {
const doesFolderIdExist = await knex.schema.hasColumn(TableName.Secret, "folderId");
const doesUserIdExist = await knex.schema.hasColumn(TableName.Secret, "userId");
if (await knex.schema.hasTable(TableName.Secret)) {
await knex.schema.alterTable(TableName.Secret, (t) => {
if (doesUserIdExist && doesFolderIdExist) t.dropIndex(["folderId", "userId"]);
});
}
}

View File

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

View File

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

View File

@ -1,43 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
await knex.schema.alterTable(TableName.Users, (t) => {
if (!hasConsecutiveFailedMfaAttempts) {
t.integer("consecutiveFailedMfaAttempts").defaultTo(0);
}
if (!hasIsLocked) {
t.boolean("isLocked").defaultTo(false);
}
if (!hasTemporaryLockDateEnd) {
t.dateTime("temporaryLockDateEnd").nullable();
}
});
}
export async function down(knex: Knex): Promise<void> {
const hasConsecutiveFailedMfaAttempts = await knex.schema.hasColumn(TableName.Users, "consecutiveFailedMfaAttempts");
const hasIsLocked = await knex.schema.hasColumn(TableName.Users, "isLocked");
const hasTemporaryLockDateEnd = await knex.schema.hasColumn(TableName.Users, "temporaryLockDateEnd");
await knex.schema.alterTable(TableName.Users, (t) => {
if (hasConsecutiveFailedMfaAttempts) {
t.dropColumn("consecutiveFailedMfaAttempts");
}
if (hasIsLocked) {
t.dropColumn("isLocked");
}
if (hasTemporaryLockDateEnd) {
t.dropColumn("temporaryLockDateEnd");
}
});
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,31 +0,0 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils";
export async function up(knex: Knex): Promise<void> {
if (!(await knex.schema.hasTable(TableName.RateLimit))) {
await knex.schema.createTable(TableName.RateLimit, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.integer("readRateLimit").defaultTo(600).notNullable();
t.integer("writeRateLimit").defaultTo(200).notNullable();
t.integer("secretsRateLimit").defaultTo(60).notNullable();
t.integer("authRateLimit").defaultTo(60).notNullable();
t.integer("inviteUserRateLimit").defaultTo(30).notNullable();
t.integer("mfaRateLimit").defaultTo(20).notNullable();
t.integer("creationLimit").defaultTo(30).notNullable();
t.integer("publicEndpointLimit").defaultTo(30).notNullable();
t.timestamps(true, true, true);
});
await createOnUpdateTrigger(knex, TableName.RateLimit);
// create init rate limit entry with defaults
await knex(TableName.RateLimit).insert({});
}
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.RateLimit);
await dropOnUpdateTrigger(knex, TableName.RateLimit);
}

View File

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

View File

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

View File

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

View File

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

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 GroupProjectUserAdditionalPrivilegeSchema = z.object({
id: z.string().uuid(),
slug: z.string(),
groupProjectMembershipId: z.string().uuid(),
requestedByUserId: z.string().uuid(),
isTemporary: z.boolean().default(false),
temporaryMode: z.string().nullable().optional(),
temporaryRange: z.string().nullable().optional(),
temporaryAccessStartTime: z.date().nullable().optional(),
temporaryAccessEndTime: z.date().nullable().optional(),
permissions: z.unknown(),
createdAt: z.date(),
updatedAt: z.date()
});
export type TGroupProjectUserAdditionalPrivilege = z.infer<typeof GroupProjectUserAdditionalPrivilegeSchema>;
export type TGroupProjectUserAdditionalPrivilegeInsert = Omit<
z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>,
TImmutableDBKeys
>;
export type TGroupProjectUserAdditionalPrivilegeUpdate = Partial<
Omit<z.input<typeof GroupProjectUserAdditionalPrivilegeSchema>, TImmutableDBKeys>
>;

View File

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

View File

@ -1,26 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const IdentityAzureAuthsSchema = z.object({
id: z.string().uuid(),
accessTokenTTL: z.coerce.number().default(7200),
accessTokenMaxTTL: z.coerce.number().default(7200),
accessTokenNumUsesLimit: z.coerce.number().default(0),
accessTokenTrustedIps: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid(),
tenantId: z.string(),
resource: z.string(),
allowedServicePrincipalIds: z.string()
});
export type TIdentityAzureAuths = z.infer<typeof IdentityAzureAuthsSchema>;
export type TIdentityAzureAuthsInsert = Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>;
export type TIdentityAzureAuthsUpdate = Partial<Omit<z.input<typeof IdentityAzureAuthsSchema>, TImmutableDBKeys>>;

View File

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

View File

@ -1,35 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const IdentityKubernetesAuthsSchema = z.object({
id: z.string().uuid(),
accessTokenTTL: z.coerce.number().default(7200),
accessTokenMaxTTL: z.coerce.number().default(7200),
accessTokenNumUsesLimit: z.coerce.number().default(0),
accessTokenTrustedIps: z.unknown(),
createdAt: z.date(),
updatedAt: z.date(),
identityId: z.string().uuid(),
kubernetesHost: z.string(),
encryptedCaCert: z.string(),
caCertIV: z.string(),
caCertTag: z.string(),
encryptedTokenReviewerJwt: z.string(),
tokenReviewerJwtIV: z.string(),
tokenReviewerJwtTag: z.string(),
allowedNamespaces: z.string(),
allowedNames: z.string(),
allowedAudience: z.string()
});
export type TIdentityKubernetesAuths = z.infer<typeof IdentityKubernetesAuthsSchema>;
export type TIdentityKubernetesAuthsInsert = Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>;
export type TIdentityKubernetesAuthsUpdate = Partial<
Omit<z.input<typeof IdentityKubernetesAuthsSchema>, TImmutableDBKeys>
>;

View File

@ -14,13 +14,10 @@ export * from "./git-app-install-sessions";
export * from "./git-app-org";
export * from "./group-project-membership-roles";
export * from "./group-project-memberships";
export * from "./group-project-user-additional-privilege";
export * from "./groups";
export * from "./identities";
export * from "./identity-access-tokens";
export * from "./identity-aws-auths";
export * from "./identity-azure-auths";
export * from "./identity-gcp-auths";
export * from "./identity-kubernetes-auths";
export * from "./identity-org-memberships";
export * from "./identity-project-additional-privilege";
export * from "./identity-project-membership-role";
@ -30,9 +27,6 @@ export * from "./identity-universal-auths";
export * from "./incident-contacts";
export * from "./integration-auths";
export * from "./integrations";
export * from "./kms-key-versions";
export * from "./kms-keys";
export * from "./kms-root-config";
export * from "./ldap-configs";
export * from "./ldap-group-maps";
export * from "./models";
@ -48,7 +42,6 @@ export * from "./project-roles";
export * from "./project-user-additional-privilege";
export * from "./project-user-membership-roles";
export * from "./projects";
export * from "./rate-limit";
export * from "./saml-configs";
export * from "./scim-tokens";
export * from "./secret-approval-policies";
@ -61,11 +54,9 @@ export * from "./secret-blind-indexes";
export * from "./secret-folder-versions";
export * from "./secret-folders";
export * from "./secret-imports";
export * from "./secret-references";
export * from "./secret-rotation-outputs";
export * from "./secret-rotations";
export * from "./secret-scanning-git-risks";
export * from "./secret-sharing";
export * from "./secret-snapshot-folders";
export * from "./secret-snapshot-secrets";
export * from "./secret-snapshots";

View File

@ -28,10 +28,7 @@ export const IntegrationsSchema = z.object({
secretPath: z.string().default("/"),
createdAt: z.date(),
updatedAt: z.date(),
lastUsed: z.date().nullable().optional(),
isSynced: z.boolean().nullable().optional(),
syncMessage: z.string().nullable().optional(),
lastSyncJobId: z.string().nullable().optional()
lastUsed: z.date().nullable().optional()
});
export type TIntegrations = z.infer<typeof IntegrationsSchema>;

View File

@ -1,21 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsKeyVersionsSchema = z.object({
id: z.string().uuid(),
encryptedKey: zodBuffer,
version: z.number(),
kmsKeyId: z.string().uuid()
});
export type TKmsKeyVersions = z.infer<typeof KmsKeyVersionsSchema>;
export type TKmsKeyVersionsInsert = Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>;
export type TKmsKeyVersionsUpdate = Partial<Omit<z.input<typeof KmsKeyVersionsSchema>, TImmutableDBKeys>>;

View File

@ -1,26 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsKeysSchema = z.object({
id: z.string().uuid(),
encryptedKey: zodBuffer,
encryptionAlgorithm: z.string(),
version: z.number().default(1),
description: z.string().nullable().optional(),
isDisabled: z.boolean().default(false).nullable().optional(),
isReserved: z.boolean().default(true).nullable().optional(),
projectId: z.string().nullable().optional(),
orgId: z.string().uuid().nullable().optional()
});
export type TKmsKeys = z.infer<typeof KmsKeysSchema>;
export type TKmsKeysInsert = Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>;
export type TKmsKeysUpdate = Partial<Omit<z.input<typeof KmsKeysSchema>, TImmutableDBKeys>>;

View File

@ -1,19 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const KmsRootConfigSchema = z.object({
id: z.string().uuid(),
encryptedRootKey: zodBuffer
});
export type TKmsRootConfig = z.infer<typeof KmsRootConfigSchema>;
export type TKmsRootConfigInsert = Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>;
export type TKmsRootConfigUpdate = Partial<Omit<z.input<typeof KmsRootConfigSchema>, TImmutableDBKeys>>;

View File

@ -18,7 +18,6 @@ export enum TableName {
IncidentContact = "incident_contacts",
UserAction = "user_actions",
SuperAdmin = "super_admin",
RateLimit = "rate_limit",
ApiKey = "api_keys",
Project = "projects",
ProjectBot = "project_bots",
@ -26,11 +25,10 @@ export enum TableName {
ProjectMembership = "project_memberships",
ProjectRoles = "project_roles",
ProjectUserAdditionalPrivilege = "project_user_additional_privilege",
GroupProjectUserAdditionalPrivilege = "group_project_user_additional_privilege",
ProjectUserMembershipRole = "project_user_membership_roles",
ProjectKeys = "project_keys",
Secret = "secrets",
SecretReference = "secret_references",
SecretSharing = "secret_sharing",
SecretBlindIndex = "secret_blind_indexes",
SecretVersion = "secret_versions",
SecretFolder = "secret_folders",
@ -47,11 +45,7 @@ export enum TableName {
Identity = "identities",
IdentityAccessToken = "identity_access_tokens",
IdentityUniversalAuth = "identity_universal_auths",
IdentityKubernetesAuth = "identity_kubernetes_auths",
IdentityGcpAuth = "identity_gcp_auths",
IdentityAzureAuth = "identity_azure_auths",
IdentityUaClientSecret = "identity_ua_client_secrets",
IdentityAwsAuth = "identity_aws_auths",
IdentityOrgMembership = "identity_org_memberships",
IdentityProjectMembership = "identity_project_memberships",
IdentityProjectMembershipRole = "identity_project_membership_role",
@ -82,11 +76,7 @@ export enum TableName {
DynamicSecretLease = "dynamic_secret_leases",
// junction tables with tags
JnSecretTag = "secret_tag_junction",
SecretVersionTag = "secret_version_tag_junction",
// KMS Service
KmsServerRootConfig = "kms_root_config",
KmsKey = "kms_keys",
KmsKeyVersion = "kms_key_versions"
SecretVersionTag = "secret_version_tag_junction"
}
export type TImmutableDBKeys = "id" | "createdAt" | "updatedAt";
@ -153,9 +143,5 @@ export enum ProjectUpgradeStatus {
}
export enum IdentityAuthMethod {
Univeral = "universal-auth",
KUBERNETES_AUTH = "kubernetes-auth",
GCP_AUTH = "gcp-auth",
AWS_AUTH = "aws-auth",
AZURE_AUTH = "azure-auth"
Univeral = "universal-auth"
}

View File

@ -16,8 +16,7 @@ export const ProjectsSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
version: z.number().default(1),
upgradeStatus: z.string().nullable().optional(),
pitVersionLimit: z.number().default(10)
upgradeStatus: z.string().nullable().optional()
});
export type TProjects = z.infer<typeof ProjectsSchema>;

View File

@ -1,26 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const RateLimitSchema = z.object({
id: z.string().uuid(),
readRateLimit: z.number().default(600),
writeRateLimit: z.number().default(200),
secretsRateLimit: z.number().default(60),
authRateLimit: z.number().default(60),
inviteUserRateLimit: z.number().default(30),
mfaRateLimit: z.number().default(20),
creationLimit: z.number().default(30),
publicEndpointLimit: z.number().default(30),
createdAt: z.date(),
updatedAt: z.date()
});
export type TRateLimit = z.infer<typeof RateLimitSchema>;
export type TRateLimitInsert = Omit<z.input<typeof RateLimitSchema>, TImmutableDBKeys>;
export type TRateLimitUpdate = Partial<Omit<z.input<typeof RateLimitSchema>, TImmutableDBKeys>>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,26 +0,0 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { TImmutableDBKeys } from "./models";
export const SecretSharingSchema = z.object({
id: z.string().uuid(),
encryptedValue: z.string(),
iv: z.string(),
tag: z.string(),
hashedHex: z.string(),
expiresAt: z.date(),
userId: z.string().uuid(),
orgId: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
expiresAfterViews: z.number().nullable().optional()
});
export type TSecretSharing = z.infer<typeof SecretSharingSchema>;
export type TSecretSharingInsert = Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>;
export type TSecretSharingUpdate = Partial<Omit<z.input<typeof SecretSharingSchema>, TImmutableDBKeys>>;

View File

@ -14,9 +14,7 @@ export const SuperAdminSchema = z.object({
createdAt: z.date(),
updatedAt: z.date(),
allowedSignUpDomain: z.string().nullable().optional(),
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000"),
trustSamlEmails: z.boolean().default(false).nullable().optional(),
trustLdapEmails: z.boolean().default(false).nullable().optional()
instanceId: z.string().uuid().default("00000000-0000-0000-0000-000000000000")
});
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
import { registerAccessApprovalPolicyRouter } from "./access-approval-policy-router";
import { registerAccessApprovalRequestRouter } from "./access-approval-request-router";
import { registerAuditLogStreamRouter } from "./audit-log-stream-router";
import { registerDynamicSecretLeaseRouter } from "./dynamic-secret-lease-router";
import { registerDynamicSecretRouter } from "./dynamic-secret-router";
import { registerGroupRouter } from "./group-router";
@ -10,7 +10,6 @@ import { registerLicenseRouter } from "./license-router";
import { registerOrgRoleRouter } from "./org-role-router";
import { registerProjectRoleRouter } from "./project-role-router";
import { registerProjectRouter } from "./project-router";
import { registerRateLimitRouter } from "./rate-limit-router";
import { registerSamlRouter } from "./saml-router";
import { registerScimRouter } from "./scim-router";
import { registerSecretApprovalPolicyRouter } from "./secret-approval-policy-router";
@ -46,7 +45,6 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => {
await server.register(registerAccessApprovalPolicyRouter, { prefix: "/access-approvals/policies" });
await server.register(registerAccessApprovalRequestRouter, { prefix: "/access-approvals/requests" });
await server.register(registerRateLimitRouter, { prefix: "/rate-limit" });
await server.register(
async (dynamicSecretRouter) => {

View File

@ -18,7 +18,6 @@ import { LdapConfigsSchema, LdapGroupMapsSchema } from "@app/db/schemas";
import { TLDAPConfig } from "@app/ee/services/ldap-config/ldap-config-types";
import { isValidLdapFilter, searchGroups } from "@app/ee/services/ldap-config/ldap-fns";
import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@ -53,7 +52,6 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line
async (req: IncomingMessage, user, cb) => {
try {
if (!user.email) throw new BadRequestError({ message: "Invalid request. Missing email." });
const ldapConfig = (req as unknown as FastifyRequest).ldapConfig as TLDAPConfig;
let groups: { dn: string; cn: string }[] | undefined;
@ -76,7 +74,7 @@ export const registerLdapRouter = async (server: FastifyZodProvider) => {
username: user.uid,
firstName: user.givenName ?? user.cn ?? "",
lastName: user.sn ?? "",
email: user.mail,
emails: user.mail ? [user.mail] : [],
groups,
relayState: ((req as unknown as FastifyRequest).body as { RelayState?: string }).RelayState,
orgId: (req as unknown as FastifyRequest).ldapConfig.organization

View File

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

View File

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

View File

@ -1,75 +0,0 @@
import { z } from "zod";
import { RateLimitSchema } from "@app/db/schemas";
import { BadRequestError } from "@app/lib/errors";
import { readLimit } from "@app/server/config/rateLimiter";
import { verifySuperAdmin } from "@app/server/plugins/auth/superAdmin";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type";
export const registerRateLimitRouter = async (server: FastifyZodProvider) => {
server.route({
method: "GET",
url: "/",
config: {
rateLimit: readLimit
},
schema: {
response: {
200: z.object({
rateLimit: RateLimitSchema
})
}
},
onRequest: (req, res, done) => {
verifyAuth([AuthMode.JWT])(req, res, () => {
verifySuperAdmin(req, res, done);
});
},
handler: async () => {
const rateLimit = await server.services.rateLimit.getRateLimits();
if (!rateLimit) {
throw new BadRequestError({
name: "Get Rate Limit Error",
message: "Rate limit configuration does not exist."
});
}
return { rateLimit };
}
});
server.route({
method: "PUT",
url: "/",
config: {
rateLimit: readLimit
},
onRequest: (req, res, done) => {
verifyAuth([AuthMode.JWT])(req, res, () => {
verifySuperAdmin(req, res, done);
});
},
schema: {
body: z.object({
readRateLimit: z.number(),
writeRateLimit: z.number(),
secretsRateLimit: z.number(),
authRateLimit: z.number(),
inviteUserRateLimit: z.number(),
mfaRateLimit: z.number(),
creationLimit: z.number(),
publicEndpointLimit: z.number()
}),
response: {
200: z.object({
rateLimit: RateLimitSchema
})
}
},
handler: async (req) => {
const rateLimit = await server.services.rateLimit.updateRateLimit(req.body);
return { rateLimit };
}
});
};

View File

@ -102,12 +102,12 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
if (!profile) throw new BadRequestError({ message: "Missing profile" });
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
if (!email || !profile.firstName) {
if (!profile.email || !profile.firstName) {
throw new BadRequestError({ message: "Invalid request. Missing email or first name" });
}
const { isUserCompleted, providerAuthToken } = await server.services.saml.samlLogin({
externalId: profile.nameID,
username: profile.nameID ?? email,
email,
firstName: profile.firstName as string,
lastName: profile.lastName as string,

View File

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

View File

@ -1,7 +1,6 @@
import { nanoid } from "nanoid";
import { z } from "zod";
import { removeTrailingSlash } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { sapPubSchema } from "@app/server/routes/sanitizedSchemas";
@ -20,11 +19,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
workspaceId: z.string(),
name: z.string().optional(),
environment: z.string(),
secretPath: z
.string()
.optional()
.nullable()
.transform((val) => (val ? removeTrailingSlash(val) : val)),
secretPath: z.string().optional().nullable(),
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1)
})
@ -68,11 +63,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
name: z.string().optional(),
approvers: z.string().array().min(1),
approvals: z.number().min(1).default(1),
secretPath: z
.string()
.optional()
.nullable()
.transform((val) => (val ? removeTrailingSlash(val) : val))
secretPath: z.string().optional().nullable()
})
.refine((data) => data.approvals <= data.approvers.length, {
path: ["approvals"],
@ -139,7 +130,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
}),
response: {
200: z.object({
approvals: sapPubSchema.merge(z.object({ approvers: z.string().array() })).array()
approvals: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).array()
})
}
},
@ -166,11 +157,11 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
querystring: z.object({
workspaceId: z.string().trim(),
environment: z.string().trim(),
secretPath: z.string().trim().transform(removeTrailingSlash)
secretPath: z.string().trim()
}),
response: {
200: z.object({
policy: sapPubSchema.merge(z.object({ approvers: z.string().array() })).optional()
policy: sapPubSchema.merge(z.object({ approvers: z.string().nullish().array() })).optional()
})
}
},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -575,9 +575,6 @@ export const licenseServiceFactory = ({
getInstanceType() {
return instanceType;
},
get onPremFeatures() {
return onPremFeatures;
},
getPlan,
updateSubscriptionOrgMemberCount,
refreshPlan,

View File

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

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