Compare commits

..

184 Commits

Author SHA1 Message Date
Akhil Mohan
3260932741 fix: resovled be test failing as reusable job 2024-02-20 22:39:14 +05:30
Maidul Islam
f0e73474b7 add check out before integ tests run 2024-02-20 11:49:11 -05:00
Maidul Islam
7db829b0b5 Merge pull request #1408 from akhilmhdh/feat/integration-test-secret-ops
Integration test for secret ops
2024-02-20 11:40:26 -05:00
Maidul Islam
ccaa9fd96e add more test cases 2024-02-20 11:35:52 -05:00
Maidul Islam
b4db06c763 return empty string for decrypt fuction 2024-02-20 11:35:06 -05:00
Maidul Islam
3ebd2fdc6d Merge pull request #1426 from akhilmhdh/fix/identity-auth-identity-ops
feat: made identity crud with identity auth mode and api key for user me
2024-02-20 11:05:26 -05:00
Akhil Mohan
8d06a6c969 feat: made identity crud with identity auth mode and api key for user me 2024-02-20 21:08:34 +05:30
Akhil Mohan
2996efe9d5 feat: made secrets ops test to have both identity and jwt token based and test for identity 2024-02-20 20:58:10 +05:30
vmatsiiako
43879f6813 Update scanning-overview.mdx 2024-02-19 17:05:36 -08:00
Maidul Islam
d428fd055b update test envs 2024-02-19 16:52:59 -05:00
Maidul Islam
e4b89371f0 add env slug to make expect more strict 2024-02-19 16:08:21 -05:00
Maidul Islam
35d589a15f add more secret test cases 2024-02-19 15:04:49 -05:00
Maidul Islam
8d77f2d8f3 Merge pull request #1420 from Infisical/snyk-upgrade-5b2dd8f68969929536cb2ba586aae1b7
[Snyk] Upgrade aws-sdk from 2.1532.0 to 2.1545.0
2024-02-19 13:55:11 -05:00
Akhil Mohan
7070a69711 feat: made e2ee api test indepdent or stateless 2024-02-19 20:56:29 +05:30
Akhil Mohan
678306b350 feat: some name changes for better understanding on testing 2024-02-18 22:51:10 +05:30
Akhil Mohan
8864c811fe feat: updated to reuse and run integration api test before release 2024-02-18 22:47:24 +05:30
snyk-bot
79206efcd0 fix: upgrade aws-sdk from 2.1532.0 to 2.1545.0
Snyk has created this PR to upgrade aws-sdk from 2.1532.0 to 2.1545.0.

See this package in npm:
https://www.npmjs.com/package/aws-sdk

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-02-18 06:17:23 +00:00
Maidul Islam
06d30fc10f Merge pull request #1417 from Infisical/snyk-upgrade-7fe9aedc3b5777266707225885372828
[Snyk] Upgrade zustand from 4.4.7 to 4.5.0
2024-02-17 16:56:29 -05:00
Maidul Islam
abd28d9269 remove comment 2024-02-17 12:14:37 -05:00
Maidul Islam
c6c64b5499 trigger workflow 2024-02-17 12:12:12 -05:00
Maidul Islam
5481b84a94 patch package json 2024-02-17 16:34:27 +00:00
Maidul Islam
ab878e00c9 Merge pull request #1418 from akhilmhdh/fix/import-sec-500
fix: resolved secret import secrets failing when folder has empty secrets
2024-02-17 11:11:58 -05:00
Akhil Mohan
6773996d40 fix: resolved secret import secrets failing when folder has empty secrets 2024-02-17 20:45:01 +05:30
snyk-bot
2e20b38bce fix: upgrade zustand from 4.4.7 to 4.5.0
Snyk has created this PR to upgrade zustand from 4.4.7 to 4.5.0.

See this package in npm:
https://www.npmjs.com/package/zustand

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/53d4ecb6-6cc1-4918-aa73-bf9cae4ffd13?utm_source=github&utm_medium=referral&page=upgrade-pr
2024-02-17 04:00:15 +00:00
Maidul Islam
2d088a865f Merge pull request #1412 from ruflair/patch-1
Update docker.mdx
2024-02-16 13:17:28 -05:00
Lukas Ruflair
0a8ec6b9da Update docker.mdx
Fix typo in docker.mdx
2024-02-16 10:13:29 -08:00
Maidul Islam
01b29c3917 Merge pull request #1336 from Infisical/snyk-fix-71fbba54e9eda81c90db390106760c64
[Snyk] Fix for 4 vulnerabilities
2024-02-16 12:15:16 -05:00
Maidul Islam
5439ddeadf Merge branch 'main' into snyk-fix-71fbba54e9eda81c90db390106760c64 2024-02-16 12:12:48 -05:00
Maidul Islam
9d17d5277b Merge pull request #1403 from Infisical/daniel/improve-reminders
(Fix): Improve reminders
2024-02-16 12:00:09 -05:00
Maidul Islam
c70fc7826a Merge pull request #1361 from Infisical/snyk-fix-4a9e6187125617ed814113514828d4f5
[Snyk] Security upgrade nodemailer from 6.9.7 to 6.9.9
2024-02-16 11:59:43 -05:00
Maidul Islam
9ed2bb38c3 Merge branch 'main' into snyk-fix-4a9e6187125617ed814113514828d4f5 2024-02-16 11:59:31 -05:00
Maidul Islam
f458cf0d40 Merge pull request #1123 from jinsley8/fix-webhook-settings-ui
fix(frontend): Remove max-width to match other views
2024-02-16 11:40:25 -05:00
Maidul Islam
ce3dc86f78 add troubleshoot for ansible 2024-02-16 10:37:34 -05:00
Maidul Islam
d1927cb9cf Merge pull request #1299 from ORCID/docs/ansible-workaround
docs: cover ansible forking error
2024-02-16 10:36:37 -05:00
Maidul Islam
e80426f72e update signup complete route 2024-02-16 09:15:52 -05:00
Maidul Islam
f8a8ea2118 update interval text 2024-02-15 14:50:42 -05:00
Maidul Islam
f5cd68168b Merge pull request #1409 from Infisical/docker-compose-selfhost-docs
Docker compose docs with postgres
2024-02-15 12:58:06 -05:00
Maidul Islam
1a0a9a7402 docker compose docs with postgres 2024-02-15 12:54:22 -05:00
Daniel Hougaard
b74ce14d80 Update SecretItem.tsx 2024-02-15 18:52:25 +01:00
Akhil Mohan
afdc5e8531 Merge pull request #1406 from abdulhakkeempa/bug-fix-model-close-on-cancel
(Fix): Create API Modal closes on Cancel button
2024-02-15 22:39:21 +05:30
Akhil Mohan
b84579b866 feat(e2e): changed to root .env.test and added .env.test.example 2024-02-15 21:06:18 +05:30
Akhil Mohan
4f3cf046fa feat(e2e): fixed post clean up error on gh be integration action 2024-02-15 20:43:40 +05:30
Akhil Mohan
c71af00146 feat(e2e): added enc key on gh action for be test file 2024-02-15 20:38:44 +05:30
Akhil Mohan
793440feb6 feat(e2e): made rebased changes 2024-02-15 20:30:12 +05:30
Akhil Mohan
b24d748462 feat(e2e): added gh action for execution of integration tests on PR and release 2024-02-15 20:25:39 +05:30
Akhil Mohan
4c49119ac5 feat(e2e): added identity token secret access integration tests 2024-02-15 20:25:39 +05:30
Akhil Mohan
90f09c7a78 feat(e2e): added service token integration tests 2024-02-15 20:25:39 +05:30
Akhil Mohan
00876f788c feat(e2e): added secrets integration tests 2024-02-15 20:25:39 +05:30
Akhil Mohan
f09c48d79b feat(e2e): fixed seed file issue and added more seeding 2024-02-15 20:25:39 +05:30
abdulhakkeempa
57daeb71e6 Merge remote-tracking branch 'origin/main' into bug-fix-model-close-on-cancel 2024-02-15 10:47:50 +05:30
abdulhakkeempa
98b5f713a5 Fix: Cancel button working on Create API in Personal Settings 2024-02-15 10:09:45 +05:30
Daniel Hougaard
120d7e42bf Added secret reminder updating, and viewing existing values 2024-02-15 01:14:19 +01:00
Daniel Hougaard
c2bd259c12 Added secret reminders to sidebar (and formatting) 2024-02-15 01:14:05 +01:00
Daniel Hougaard
242d770098 Improved UX when trying to find reminder value 2024-02-15 01:12:48 +01:00
Maidul Islam
1855fc769d Update check-api-for-breaking-changes.yml 2024-02-14 15:42:45 -05:00
Maidul Islam
217fef65e8 update breaking change workflow to dev.yml 2024-02-14 15:37:21 -05:00
Maidul Islam
8a0fd62785 update docker compose name 2024-02-14 15:19:47 -05:00
BlackMagiq
c69601c14e Merge pull request #1402 from Infisical/identity-access-token-ttl
Patch identity access token + client secret TTL
2024-02-14 12:19:45 -08:00
Tuan Dang
faf6323a58 Patch identity ttl conversion 2024-02-14 12:09:56 -08:00
Maidul Islam
b82d1b6a5d update docker compose on readme 2024-02-13 19:50:58 -05:00
Maidul Islam
3dcda44c50 Merge pull request #1385 from akhilmhdh/feat/init-container
feat: changed docker compose to use init container pattern for running migration
2024-02-13 19:46:39 -05:00
Maidul Islam
f320b08ca8 rename docker compose and bring back make up-dev 2024-02-13 19:44:42 -05:00
Maidul Islam
df6e5674cf patch breaking change ci 2024-02-13 19:10:28 -05:00
Maidul Islam
6bac143a8e test without pg connection 2024-02-13 19:06:47 -05:00
Maidul Islam
38b93e499f logs on health check 2024-02-13 18:58:13 -05:00
Maidul Islam
a521538010 show all logs 2024-02-13 18:47:12 -05:00
Maidul Islam
8cc2553452 test workflow no db connection 2024-02-13 18:43:43 -05:00
Maidul Islam
b1cb9de001 correct -d mode 2024-02-13 18:39:28 -05:00
Maidul Islam
036256b350 add back -d mode 2024-02-13 18:37:04 -05:00
Maidul Islam
d3a06b82e6 update health check 2024-02-13 18:34:15 -05:00
Maidul Islam
87436cfb57 Update check-api-for-breaking-changes.yml 2024-02-13 18:04:49 -05:00
Vladyslav Matsiiako
5c58a4d1a3 added signup event and restyled admin flow 2024-02-13 13:15:54 -08:00
Maidul Islam
03a91b2c59 Merge pull request #1387 from akhilmhdh/chore/doc-openapi
chore: changed mintlify to directly get from prod openapi
2024-02-13 00:42:47 -05:00
Maidul Islam
751361bd54 add new propety to api 2024-02-13 00:38:59 -05:00
Maidul Islam
b4b88daf36 Revert "test breaking change"
This reverts commit 6546740bd9.
2024-02-13 00:37:49 -05:00
Maidul Islam
6546740bd9 test breaking change 2024-02-13 00:34:31 -05:00
Maidul Islam
b32558c66f add . 2024-02-13 00:29:49 -05:00
Maidul Islam
effd30857e fix typo 2024-02-12 22:45:35 -05:00
Maidul Islam
60998c8944 Merge pull request #1369 from akhilmhdh/chore/feature-x-guide
feat: added guides for new backend development
2024-02-12 22:35:16 -05:00
Maidul Islam
3c4d9fd4a9 delete docs in backend 2024-02-12 22:34:36 -05:00
Maidul Islam
ad70c783e8 add backend guide to contributor 2024-02-12 22:33:55 -05:00
Maidul Islam
7347362738 rephrase new feature development guide 2024-02-12 21:49:15 -05:00
vmatsiiako
4b7f2e808b Update overview.mdx 2024-02-12 15:06:56 -08:00
Maidul Islam
57f9d13189 Merge pull request #1397 from Infisical/infisical-jenkins
Rewrite Infisical Jenkins docs
2024-02-12 18:02:11 -05:00
Maidul Islam
bd2e8ac922 rewrite jenkins docs 2024-02-12 18:01:20 -05:00
Tuan Dang
79694750af Remove signup disable check for SAML 2024-02-12 11:43:55 -08:00
BlackMagiq
03db367a4e Merge pull request #1396 from Infisical/azure-saml
Add disableRequestedAuthnContext for azure saml
2024-02-12 10:40:19 -08:00
Tuan Dang
b0fb848a92 Add disableRequestedAuthnContext for azure saml 2024-02-12 10:35:38 -08:00
Akhil Mohan
4fdfcd50dc feat: changed check-api-breaking with oasdiff 2024-02-12 22:32:02 +05:30
Akhil Mohan
db205b855a feat: removed mongo comments from compose file 2024-02-12 14:52:19 +05:30
Akhil Mohan
e707f0d235 feat: added description and security over api written in docs 2024-02-12 14:19:49 +05:30
Maidul Islam
27f4225c44 Merge pull request #1391 from Infisical/ecs-docs
ECS docs with agent
2024-02-11 19:17:01 -05:00
BlackMagiq
d4db01bbde Merge pull request #1388 from Infisical/azure-ad
Correct spInitiated spelling in Azure SAML
2024-02-09 12:31:41 -08:00
Tuan Dang
39634b8aae Correct spInitiated spelling in Azure SAML 2024-02-09 12:17:29 -08:00
Tuan Dang
4815ff13ee Update SAML SSO docs to include enforce SAML SSO toggle 2024-02-09 10:36:23 -08:00
Maidul Islam
fb503756d9 Merge pull request #1372 from Infisical/org-based-auth
Org-Level Auth Enforcement for SAML Orgs and Enhancements for SAML SSO
2024-02-09 13:15:08 -05:00
Tuan Dang
069b0cd6fb Fix lint issue, add backend permission check for SAML SSO enforcement toggle 2024-02-09 10:02:12 -08:00
Tuan Dang
ed23bd40d2 Redirect to SAML portal upon enforcing SAML SSO 2024-02-09 09:50:20 -08:00
Tuan Dang
82181f078a Patch login with SAML when config is inactive 2024-02-09 09:35:51 -08:00
Akhil Mohan
48a97fb39d chore: changed mintlify to directly get from prod openapi 2024-02-09 22:53:27 +05:30
Maidul Islam
eeaee4409c revert swap 2024-02-09 12:08:11 -05:00
Tuan Dang
a9a5e92358 Empty 2024-02-09 09:02:22 -08:00
Maidul Islam
8d457bb0bf swap src des 2024-02-09 12:01:21 -05:00
Tuan Dang
5878a221f8 Run lint fix 2024-02-09 08:44:30 -08:00
Tuan Dang
fdbf59cd78 Redirect users to SAML portal on change org to SAML org 2024-02-09 08:31:19 -08:00
Akhil Mohan
d8ea26feb7 feat: changed docker compose to use init container pattern for migration 2024-02-09 13:12:25 +05:30
Tuan Dang
2cc2a91812 Change update slug fallback to undefined 2024-02-08 17:47:58 -08:00
Maidul Islam
92828b5295 add slug index 2024-02-08 20:47:33 -05:00
Tuan Dang
4e2f2281f9 Update orgScope to orgId naming for org-level auth ref, rewire user invite saml block to org authEnforce field 2024-02-08 17:29:21 -08:00
Akhil Mohan
70e083bae0 feat: open api-diff added detach mode 2024-02-08 22:20:01 +05:30
Akhil Mohan
6a943e275a Updated open-api diff gh action host docker ip 2024-02-08 22:05:35 +05:30
Maidul Islam
526dc6141b remove -d mode on docker compose 2024-02-08 11:04:26 -05:00
Maidul Islam
dcab9dcdda update docker compose up 2024-02-08 10:50:25 -05:00
Maidul Islam
1b0591def8 fix gha breaking change 2024-02-07 16:17:12 -05:00
Maidul Islam
4b4305bddc Merge pull request #1375 from akhilmhdh/feat/api-diff
feat: github workflow for api diff check, ts check and lint check on PR
2024-02-07 15:26:04 -05:00
Tuan Dang
22d89d791c Patch new org creation condition on SAML account signup, enable users to toggle auth methods regardless of what org they are in 2024-02-07 12:13:09 -08:00
Maidul Islam
fcaff76afa rename git hub action 2024-02-07 14:34:02 -05:00
Maidul Islam
ae9eb20189 set license server url default 2024-02-07 13:51:55 -05:00
Maidul Islam
3905d16a7c fix license server axios typo 2024-02-07 13:48:09 -05:00
Maidul Islam
ecafdb0d01 patch check for version 2024-02-07 13:11:53 -05:00
Tuan Dang
3f8ce42682 Merge remote-tracking branch 'origin' into org-based-auth 2024-02-07 09:49:13 -08:00
Tuan Dang
3ecfb3f9d2 Show usage and billing tab on cloud only 2024-02-07 09:45:42 -08:00
Tuan Dang
9011394c34 Add validation to org slug 2024-02-07 09:39:06 -08:00
Akhil Mohan
c0096ca64c Merge pull request #1378 from Infisical/patch-service-token-fetch
patch get secret by name
2024-02-07 23:00:20 +05:30
Maidul Islam
8bc952388c add log 2024-02-07 12:23:48 -05:00
Maidul Islam
eef29cd2d4 patch get secret by name 2024-02-07 12:11:58 -05:00
Akhil Mohan
6ef873f3a0 Merge pull request #1377 from Infisical/allow-name-initial-org
add initial org rename
2024-02-07 20:51:30 +05:30
Maidul Islam
fe99c12c0d add initial org rename 2024-02-07 10:18:41 -05:00
Akhil Mohan
8313245ae1 feat: github workflow for api diff check, ts check and lint check on PR 2024-02-07 15:13:08 +05:30
Akhil Mohan
332b0e2cc3 Merge pull request #1374 from Infisical/admin-ui-fix
fix admin dashboard styling
2024-02-07 12:18:09 +05:30
Vladyslav Matsiiako
8bc9a5fed6 fix admin dashboard styling 2024-02-06 22:45:58 -08:00
Maidul Islam
55e75bbbef Merge pull request #1373 from akhilmhdh/feat/patch-server-cfg-init
feat: fixed server cfg stale in replication
2024-02-07 01:09:42 -05:00
Akhil Mohan
61ff732ec0 feat: fixed server cfg stale in replication 2024-02-07 11:36:13 +05:30
Maidul Islam
609b224ca9 patch init sign up 2024-02-07 00:33:39 -05:00
Maidul Islam
c23e16105b debug: remove object freeze 2024-02-06 21:08:52 -05:00
Maidul Islam
c10f4ece51 test 2024-02-06 21:08:52 -05:00
Tuan Dang
fc7015de83 Add lockout-preventative step in saml config setup, add update org slug section in org settings, revise navigate to org flow to account for org-level auth enforced orgs 2024-02-06 15:51:24 -08:00
vmatsiiako
bcdb1b11bc Update role-based-access-controls.mdx 2024-02-06 13:36:08 -08:00
vmatsiiako
01d850f7e8 Update role-based-access-controls.mdx 2024-02-06 13:35:39 -08:00
Maidul Islam
2d1b60a520 Merge pull request #1362 from akhilmhdh/fix/tsup-cp-template
feat: enabled tsup code splitting and esm directory import, removed manual copy of files
2024-02-06 12:22:59 -05:00
Maidul Islam
8de2302d98 update comment 2024-02-06 12:22:04 -05:00
Maidul Islam
0529b50ad7 Merge pull request #1371 from akhilmhdh/fix/sort-order-ws-env
fix: resolved sort order for environment going unpredictable
2024-02-06 11:41:57 -05:00
Akhil Mohan
c74fe0ca73 fix: resolved sort order for environment going unpredictable 2024-02-06 16:40:31 +05:30
vmatsiiako
d5f8526a84 Update README.md 2024-02-05 17:31:44 -08:00
Tuan Dang
c1aa5c840c Add org-scoped auth to project-level endpoints 2024-02-05 14:48:02 -08:00
Maidul Islam
782ae7a41d Update values.yaml 2024-02-05 13:41:02 -05:00
Maidul Islam
d355956daf Merge pull request #1365 from Infisical/pg-ssl
Add Knex SSL configuration support
2024-02-05 12:36:49 -05:00
Akhil Mohan
dc146d0883 feat: fixed spelling errors 2024-02-05 22:28:40 +05:30
Akhil Mohan
24dd79b566 feat: added guides for new backend development 2024-02-05 16:31:03 +05:30
Tuan Dang
410476ecb5 Merge remote-tracking branch 'origin' into org-based-auth 2024-02-04 15:03:43 -08:00
Tuan Dang
f1c41be7d4 Resolve merge conflicts 2024-02-04 15:02:43 -08:00
Tuan Dang
f138973ac7 Add org-scoped auth to org-level endpoints, add migration file for org enableAuth field 2024-02-04 14:44:08 -08:00
Maidul Islam
5b9c0438a2 Merge pull request #1367 from Infisical/fix-ph-events
remove certain python sdk events
2024-02-04 16:24:38 -05:00
Maidul Islam
11399d73dc fix eslint errors 2024-02-04 16:24:01 -05:00
Vladyslav Matsiiako
38ed39c2f8 remove certain python sdk events 2024-02-04 09:37:56 -08:00
Tuan Dang
4e3827780f Merge remote-tracking branch 'origin' into pg-ssl 2024-02-03 15:16:47 -08:00
Tuan Dang
644cdf5a67 Add knex SSL configuration support 2024-02-03 15:16:43 -08:00
Maidul Islam
0d6ea0d69e Update values.yaml 2024-02-03 14:37:24 -05:00
Maidul Islam
237979a1c6 Merge pull request #1364 from Infisical/fix-ph-events
fix posthog events
2024-02-03 14:26:34 -05:00
Maidul Islam
4a566cf83f remove existent authData 2024-02-03 14:24:28 -05:00
Vladyslav Matsiiako
654b8ab5ca fix posthog events 2024-02-03 11:09:49 -08:00
Maidul Islam
ac0780266b remove await and add void 2024-02-03 12:53:42 -05:00
Maidul Islam
7a253ddcc7 update sort from createdAt to id 2024-02-02 12:43:43 -05:00
Maidul Islam
b65677a708 Merge pull request #1363 from akhilmhdh/feat/audit-log-desc
feat: enabled order by desc for audit log and added sort for couple of get queries
2024-02-02 12:07:57 -05:00
Maidul Islam
c1eb97ee53 revert port change 2024-02-02 11:51:27 -05:00
Akhil Mohan
937e48dbc5 feat: enabled order by desc for audit log and added sort for couple of get queries 2024-02-02 20:56:42 +05:30
Akhil Mohan
b3d4787e21 feat: enabled tsup code splitting and esm directory import, removed manual copy of files 2024-02-02 16:22:08 +05:30
Maidul Islam
72d46efba5 sort get secrets response for etags 2024-02-02 01:25:19 -05:00
Maidul Islam
b6eb08167f Update values.yaml 2024-02-01 22:45:54 -05:00
Maidul Islam
582472e4cc Update gamma values.yaml 2024-02-01 22:34:09 -05:00
Maidul Islam
3b3b76548b add etag 2024-02-01 20:49:07 -05:00
Maidul Islam
f8416ad891 add redis commander for local dev 2024-02-01 15:45:38 -05:00
snyk-bot
00650df501 fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-NODEMAILER-6219989
2024-02-01 20:01:54 +00:00
Akhil Mohan
31e49672d5 Merge pull request #1359 from Infisical/daniel/fix-list-workspaces-id
(Fix): Add ID to list workspaces endpoint
2024-02-01 13:21:06 +05:30
Daniel Hougaard
9248bdf463 Update organization-router.ts 2024-02-01 11:47:33 +04:00
Maidul Islam
87c061ae9b Merge pull request #1357 from Infisical/danie/fix-list-workspaces
(Fix): List workspaces organization ID
2024-02-01 01:21:11 -05:00
Daniel Hougaard
e9fa631c8f Update project-dal.ts 2024-02-01 10:14:17 +04:00
vmatsiiako
cff15b64c4 Merge pull request #1355 from Grraahaam/doc/pr-template-broken-links
fix: update broken contribution links
2024-01-31 18:58:24 -08:00
vmatsiiako
136f5a6052 Merge pull request #1318 from rpmccarter/main
remove deprecated basePath setting
2024-01-31 18:56:11 -08:00
Grraahaam
7e8f9ec9e4 fix: update broken contribution links 2024-01-30 23:19:21 +01:00
snyk-bot
6ff5fb69d4 fix: backend/package.json & backend/package-lock.json to reduce vulnerabilities
The following vulnerabilities are fixed with an upgrade:
- https://snyk.io/vuln/SNYK-JS-AXIOS-6124857
- https://snyk.io/vuln/SNYK-JS-AXIOS-6144788
- https://snyk.io/vuln/SNYK-JS-FASTIFYSWAGGERUI-6157561
- https://snyk.io/vuln/SNYK-JS-INFLIGHT-6095116
2024-01-27 15:06:50 +00:00
Ronan McCarter
202efce10d remove deprecated basePath setting 2024-01-22 15:50:31 -08:00
Giles Westwood
9fe2021d9f docs: cover ansible forking error 2024-01-10 22:33:38 +00:00
Jon Insley
fe2f2f972e fix(frontend): Remove max-width to match other views
This commit removes the max-width constraint on the WebhooksTab.tsx component, aligning it with the full-width layout consistency seen in other views. The previous max-width of 1024px resulted in unused space on larger screens.
2023-10-25 13:54:09 -04:00
227 changed files with 5993 additions and 8584 deletions

View File

@@ -3,16 +3,18 @@
# THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION # THIS IS A SAMPLE ENCRYPTION KEY AND SHOULD NEVER BE USED FOR PRODUCTION
ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218 ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# Required
DB_CONNECTION_URI=postgres://infisical:infisical@db:5432/infisical
# JWT # JWT
# Required secrets to sign JWT tokens # Required secrets to sign JWT tokens
# THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION # THIS IS A SAMPLE AUTH_SECRET KEY AND SHOULD NEVER BE USED FOR PRODUCTION
AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE= AUTH_SECRET=5lrMXKKWCVocS/uerPsl7V+TX/aaUaI7iDkgl3tSmLE=
# MongoDB # Postgres creds
# Backend will connect to the MongoDB instance at connection string MONGO_URL which can either be a ref POSTGRES_PASSWORD=infisical
# to the MongoDB container instance or Mongo Cloud POSTGRES_USER=infisical
# Required POSTGRES_DB=infisical
MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
# Redis # Redis
REDIS_URL=redis://redis:6379 REDIS_URL=redis://redis:6379

1
.env.migration.example Normal file
View File

@@ -0,0 +1 @@
DB_CONNECTION_URI=

4
.env.test.example Normal file
View File

@@ -0,0 +1,4 @@
REDIS_URL=redis://localhost:6379
DB_CONNECTION_URI=postgres://infisical:infisical@localhost/infisical?sslmode=disable
AUTH_SECRET=4bnfe4e407b8921c104518903515b218
ENCRYPTION_KEY=4bnfe4e407b8921c104518903515b218

View File

@@ -1,6 +1,6 @@
# Description 📣 # Description 📣
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. --> <!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. Here's how we expect a pull request to be : https://infisical.com/docs/contributing/getting-started/pull-requests -->
## Type ✨ ## Type ✨
@@ -19,4 +19,6 @@
--- ---
- [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/code-of-conduct). 📝 - [ ] I have read the [contributing guide](https://infisical.com/docs/contributing/getting-started/overview), agreed and acknowledged the [code of conduct](https://infisical.com/docs/contributing/getting-started/code-of-conduct). 📝
<!-- If you have any questions regarding contribution, here's the FAQ : https://infisical.com/docs/contributing/getting-started/faq -->

8
.github/values.yaml vendored
View File

@@ -19,11 +19,11 @@ infisical:
## @param backend.name Backend name ## @param backend.name Backend name
## ##
name: infisical name: infisical
replicaCount: 2 replicaCount: 3
image: image:
repository: infisical/infisical repository: infisical/staging_infisical
tag: "latest-postgres" tag: "latest"
pullPolicy: IfNotPresent pullPolicy: Always
deploymentAnnotations: deploymentAnnotations:
secrets.infisical.com/auto-reload: "true" secrets.infisical.com/auto-reload: "true"

View File

@@ -0,0 +1,75 @@
name: "Check API For Breaking Changes"
on:
pull_request:
types: [opened, synchronize]
paths:
- "backend/src/server/routes/**"
jobs:
check-be-api-changes:
name: Check API Changes
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout source
uses: actions/checkout@v3
# - name: Setup Node 20
# uses: actions/setup-node@v3
# with:
# node-version: "20"
# uncomment this when testing locally using nektos/act
- uses: KengoTODA/actions-setup-docker-compose@v1
if: ${{ env.ACT }}
name: Install `docker-compose` for local simulations
with:
version: "2.14.2"
- name: 📦Build the latest image
run: docker build --tag infisical-api .
working-directory: backend
- name: Start postgres and redis
run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis
- name: Start the server
run: |
echo "SECRET_SCANNING_GIT_APP_ID=793712" >> .env
echo "SECRET_SCANNING_PRIVATE_KEY=some-random" >> .env
echo "SECRET_SCANNING_WEBHOOK_SECRET=some-random" >> .env
docker run --name infisical-api -d -p 4000:4000 -e DB_CONNECTION_URI=$DB_CONNECTION_URI -e REDIS_URL=$REDIS_URL -e JWT_AUTH_SECRET=$JWT_AUTH_SECRET --env-file .env --entrypoint '/bin/sh' infisical-api -c "npm run migration:latest && ls && node dist/main.mjs"
env:
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
- uses: actions/setup-go@v5
with:
go-version: '1.21.5'
- name: Wait for container to be stable and check logs
run: |
SECONDS=0
HEALTHY=0
while [ $SECONDS -lt 60 ]; do
if docker ps | grep infisical-api | grep -q healthy; then
echo "Container is healthy."
HEALTHY=1
break
fi
echo "Waiting for container to be healthy... ($SECONDS seconds elapsed)"
docker logs infisical-api
sleep 2
SECONDS=$((SECONDS+2))
done
if [ $HEALTHY -ne 1 ]; then
echo "Container did not become healthy in time"
exit 1
fi
- name: Install openapi-diff
run: go install github.com/tufin/oasdiff@latest
- name: Running OpenAPI Spec diff action
run: oasdiff breaking https://app.infisical.com/api/docs/json http://localhost:4000/api/docs/json --fail-on ERR
- name: cleanup
run: |
docker-compose -f "docker-compose.dev.yml" down
docker stop infisical-api
docker remove infisical-api

View File

@@ -1,43 +0,0 @@
name: "Check Backend Pull Request"
on:
pull_request:
types: [opened, synchronize]
paths:
- "backend/**"
- "!backend/README.md"
- "!backend/.*"
- "backend/.eslintrc.js"
jobs:
check-be-pr:
name: Check
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: 🔧 Setup Node 16
uses: actions/setup-node@v3
with:
node-version: "16"
cache: "npm"
cache-dependency-path: backend/package-lock.json
- name: 📦 Install dependencies
run: npm ci --only-production
working-directory: backend
# - name: 🧪 Run tests
# run: npm run test:ci
# working-directory: backend
# - name: 📁 Upload test results
# uses: actions/upload-artifact@v3
# if: always()
# with:
# name: be-test-results
# path: |
# ./backend/reports
# ./backend/coverage
- name: 🏗️ Run build
run: npm run build
working-directory: backend

View File

@@ -0,0 +1,35 @@
name: "Check Backend PR types and lint"
on:
pull_request:
types: [opened, synchronize]
paths:
- "backend/**"
- "!backend/README.md"
- "!backend/.*"
- "backend/.eslintrc.js"
jobs:
check-be-pr:
name: Check TS and Lint
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: 🔧 Setup Node 20
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "npm"
cache-dependency-path: backend/package-lock.json
- name: Install dependencies
run: npm install
working-directory: backend
- name: Run type check
run: npm run type:check
working-directory: backend
- name: Run lint check
run: npm run lint
working-directory: backend

View File

@@ -5,9 +5,14 @@ on:
- "infisical/v*.*.*-postgres" - "infisical/v*.*.*-postgres"
jobs: jobs:
infisical-tests:
name: Run tests before deployment
# https://docs.github.com/en/actions/using-workflows/reusing-workflows#overview
uses: ./.github/workflows/run-backend-tests.yml
infisical-standalone: infisical-standalone:
name: Build infisical standalone image postgres name: Build infisical standalone image postgres
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [infisical-tests]
steps: steps:
- name: Extract version from tag - name: Extract version from tag
id: extract_version id: extract_version

47
.github/workflows/run-backend-tests.yml vendored Normal file
View File

@@ -0,0 +1,47 @@
name: "Run backend tests"
on:
pull_request:
types: [opened, synchronize]
paths:
- "backend/**"
- "!backend/README.md"
- "!backend/.*"
- "backend/.eslintrc.js"
workflow_call:
jobs:
check-be-pr:
name: Run integration test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- uses: KengoTODA/actions-setup-docker-compose@v1
if: ${{ env.ACT }}
name: Install `docker-compose` for local simulations
with:
version: "2.14.2"
- name: 🔧 Setup Node 20
uses: actions/setup-node@v3
with:
node-version: "20"
cache: "npm"
cache-dependency-path: backend/package-lock.json
- name: Install dependencies
run: npm install
working-directory: backend
- name: Start postgres and redis
run: touch .env && docker-compose -f docker-compose.dev.yml up -d db redis
- name: Start integration test
run: npm run test:e2e
working-directory: backend
env:
REDIS_URL: redis://172.17.0.1:6379
DB_CONNECTION_URI: postgres://infisical:infisical@172.17.0.1:5432/infisical?sslmode=disable
AUTH_SECRET: something-random
ENCRYPTION_KEY: 4bnfe4e407b8921c104518903515b218
- name: cleanup
run: |
docker-compose -f "docker-compose.dev.yml" down

2
.gitignore vendored
View File

@@ -6,7 +6,7 @@ node_modules
.env.gamma .env.gamma
.env.prod .env.prod
.env.infisical .env.infisical
.env.migration
*~ *~
*.swp *.swp
*.swo *.swo

View File

@@ -104,7 +104,6 @@ ENV NEXT_PUBLIC_INTERCOM_ID=$INTERCOM_ID \
WORKDIR / WORKDIR /
COPY --from=backend-runner /app /backend COPY --from=backend-runner /app /backend
COPY --from=backend-runner /app/dist/services/smtp/templates /backend/dist/templates
COPY --from=frontend-runner /app ./backend/frontend-build COPY --from=frontend-runner /app ./backend/frontend-build

View File

@@ -5,16 +5,10 @@ push:
docker-compose -f docker-compose.yml push docker-compose -f docker-compose.yml push
up-dev: up-dev:
docker-compose -f docker-compose.dev.yml up --build docker compose -f docker-compose.dev.yml up --build
up-pg-dev:
docker compose -f docker-compose.pg.yml up --build
i-dev:
infisical run -- docker-compose -f docker-compose.dev.yml up --build
up-prod: up-prod:
docker-compose -f docker-compose.yml up --build docker-compose -f docker-compose.prod.yml up --build
down: down:
docker-compose down docker-compose down

View File

@@ -33,7 +33,7 @@
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" /> <img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
</a> </a>
<a href="https://cloudsmith.io/~infisical/repos/"> <a href="https://cloudsmith.io/~infisical/repos/">
<img src="https://img.shields.io/badge/Downloads-2.58M-orange" alt="Cloudsmith downloads" /> <img src="https://img.shields.io/badge/Downloads-6.95M-orange" alt="Cloudsmith downloads" />
</a> </a>
<a href="https://infisical.com/slack"> <a href="https://infisical.com/slack">
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" /> <img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
@@ -53,17 +53,19 @@ We're on a mission to make secret management more accessible to everyone, not ju
## Features ## Features
- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.) - **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.).
- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand - **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand.
- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development - **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development and CI/CD.
- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like GitHub, Vercel, Netlify, and more - **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** to perform CRUD operation on secrets, users, projects, and any other resource in Infisical.
- [**Automatic Kubernetes deployment secret reloads**](https://infisical.com/docs/documentation/getting-started/kubernetes) - **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like [GitHub](https://infisical.com/docs/integrations/cicd/githubactions), [Vercel](https://infisical.com/docs/integrations/cloud/vercel), [AWS](https://infisical.com/docs/integrations/cloud/aws-secret-manager), and tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more.
- **[Complete control over your data](https://infisical.com/docs/self-hosting/overview)** - host it yourself on any infrastructure - **[Infisical Kubernetes operator](https://infisical.com/docs/documentation/getting-started/kubernetes)** to managed secrets in k8s, automatically reload deployments, and more.
- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery]()** to version every secret and project state - **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)** to inject secrets into your applications without modifying any code logic.
- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project - **[Self-hosting and on-prem](https://infisical.com/docs/self-hosting/overview)** to get complete control over your data.
- **Role-based Access Controls** per environment - **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)** to version every secret and project state.
- [**Simple on-premise deployments** to AWS, Digital Ocean, and more](https://infisical.com/docs/self-hosting/overview) - **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project.
- [**Secret Scanning and Leak Prevention**](https://infisical.com/docs/cli/scanning-overview) - **[Role-based Access Controls](https://infisical.com/docs/documentation/platform/role-based-access-controls)** to create permission sets on any resource in Infisica and assign those to user or machine identities.
- **[Simple on-premise deployments](https://infisical.com/docs/self-hosting/overview)** to AWS, Digital Ocean, and more.
- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)** to prevent secrets from leaking to git.
And much more. And much more.
@@ -82,13 +84,13 @@ To set up and run Infisical locally, make sure you have Git and Docker installed
Linux/macOS: Linux/macOS:
```console ```console
git clone https://github.com/Infisical/infisical && cd "$(basename $_ .git)" && cp .env.example .env && docker-compose -f docker-compose.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: Windows Command Prompt:
```console ```console
git clone https://github.com/Infisical/infisical && cd infisical && copy .env.example .env && docker-compose -f docker-compose.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` Create an account at `http://localhost:80`
@@ -115,9 +117,9 @@ Lean about Infisical's code scanning feature [here](https://infisical.com/docs/c
This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license. This repo available under the [MIT expat license](https://github.com/Infisical/infisical/blob/main/LICENSE), with the exception of the `ee` directory which will contain premium enterprise features requiring a Infisical license.
If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://cal.com/vmatsiiako/infisical-demo): If you are interested in managed Infisical Cloud of self-hosted Enterprise Offering, take a look at [our website](https://infisical.com/) or [book a meeting with us](https://infisical.cal.com/vlad/infisical-demo):
<a href="https://cal.com/vmatsiiako/infisical-demo"><img alt="Schedule a meeting" src="https://cal.com/book-with-cal-dark.svg" /></a> <a href="[https://infisical.cal.com/vlad/infisical-demo](https://infisical.cal.com/vlad/infisical-demo)"><img alt="Schedule a meeting" src="https://cal.com/book-with-cal-dark.svg" /></a>
## Security ## Security

View File

@@ -1,2 +1,3 @@
vitest-environment-infisical.ts vitest-environment-infisical.ts
vitest.config.ts vitest.config.ts
vitest.e2e.config.ts

View File

@@ -21,6 +21,18 @@ module.exports = {
tsconfigRootDir: __dirname tsconfigRootDir: __dirname
}, },
root: true, root: true,
overrides: [
{
files: ["./e2e-test/**/*"],
rules: {
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/no-unsafe-return": "off",
"@typescript-eslint/no-unsafe-call": "off",
}
}
],
rules: { rules: {
"@typescript-eslint/no-empty-function": "off", "@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-unsafe-enum-comparison": "off", "@typescript-eslint/no-unsafe-enum-comparison": "off",

View File

@@ -0,0 +1,71 @@
import { OrgMembershipRole } from "@app/db/schemas";
import { seedData1 } from "@app/db/seed-data";
export const createIdentity = async (name: string, role: string) => {
const createIdentityRes = await testServer.inject({
method: "POST",
url: "/api/v1/identities",
body: {
name,
role,
organizationId: seedData1.organization.id
},
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(createIdentityRes.statusCode).toBe(200);
return createIdentityRes.json().identity;
};
export const deleteIdentity = async (id: string) => {
const deleteIdentityRes = await testServer.inject({
method: "DELETE",
url: `/api/v1/identities/${id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(deleteIdentityRes.statusCode).toBe(200);
return deleteIdentityRes.json().identity;
};
describe("Identity v1", async () => {
test("Create identity", async () => {
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
expect(newIdentity.name).toBe("mac1");
expect(newIdentity.authMethod).toBeNull();
await deleteIdentity(newIdentity.id);
});
test("Update identity", async () => {
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
expect(newIdentity.name).toBe("mac1");
expect(newIdentity.authMethod).toBeNull();
const updatedIdentity = await testServer.inject({
method: "PATCH",
url: `/api/v1/identities/${newIdentity.id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
name: "updated-mac-1",
role: OrgMembershipRole.Member
}
});
expect(updatedIdentity.statusCode).toBe(200);
expect(updatedIdentity.json().identity.name).toBe("updated-mac-1");
await deleteIdentity(newIdentity.id);
});
test("Delete Identity", async () => {
const newIdentity = await createIdentity("mac1", OrgMembershipRole.Admin);
const deletedIdentity = await deleteIdentity(newIdentity.id);
expect(deletedIdentity.name).toBe("mac1");
});
});

View File

@@ -1,6 +1,7 @@
import { seedData1 } from "@app/db/seed-data";
import jsrp from "jsrp"; import jsrp from "jsrp";
import { seedData1 } from "@app/db/seed-data";
describe("Login V1 Router", async () => { describe("Login V1 Router", async () => {
// eslint-disable-next-line // eslint-disable-next-line
const client = new jsrp.client(); const client = new jsrp.client();

View File

@@ -1,6 +1,40 @@
import { seedData1 } from "@app/db/seed-data"; import { seedData1 } from "@app/db/seed-data";
import { DEFAULT_PROJECT_ENVS } from "@app/db/seeds/3-project"; import { DEFAULT_PROJECT_ENVS } from "@app/db/seeds/3-project";
const createProjectEnvironment = async (name: string, slug: string) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/workspace/${seedData1.project.id}/environments`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
name,
slug
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("environment");
return payload.environment;
};
const deleteProjectEnvironment = async (envId: string) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/workspace/${seedData1.project.id}/environments/${envId}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("environment");
return payload.environment;
};
describe("Project Environment Router", async () => { describe("Project Environment Router", async () => {
test("Get default environments", async () => { test("Get default environments", async () => {
const res = await testServer.inject({ const res = await testServer.inject({
@@ -31,24 +65,10 @@ describe("Project Environment Router", async () => {
expect(payload.workspace.environments.length).toBe(3); expect(payload.workspace.environments.length).toBe(3);
}); });
const mockProjectEnv = { name: "temp", slug: "temp", id: "" }; // id will be filled in create op const mockProjectEnv = { name: "temp", slug: "temp" }; // id will be filled in create op
test("Create environment", async () => { test("Create environment", async () => {
const res = await testServer.inject({ const newEnvironment = await createProjectEnvironment(mockProjectEnv.name, mockProjectEnv.slug);
method: "POST", expect(newEnvironment).toEqual(
url: `/api/v1/workspace/${seedData1.project.id}/environments`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
name: mockProjectEnv.name,
slug: mockProjectEnv.slug
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("environment");
expect(payload.environment).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
name: mockProjectEnv.name, name: mockProjectEnv.name,
@@ -59,14 +79,15 @@ describe("Project Environment Router", async () => {
updatedAt: expect.any(String) updatedAt: expect.any(String)
}) })
); );
mockProjectEnv.id = payload.environment.id; await deleteProjectEnvironment(newEnvironment.id);
}); });
test("Update environment", async () => { test("Update environment", async () => {
const newEnvironment = await createProjectEnvironment(mockProjectEnv.name, mockProjectEnv.slug);
const updatedName = { name: "temp#2", slug: "temp2" }; const updatedName = { name: "temp#2", slug: "temp2" };
const res = await testServer.inject({ const res = await testServer.inject({
method: "PATCH", method: "PATCH",
url: `/api/v1/workspace/${seedData1.project.id}/environments/${mockProjectEnv.id}`, url: `/api/v1/workspace/${seedData1.project.id}/environments/${newEnvironment.id}`,
headers: { headers: {
authorization: `Bearer ${jwtAuthToken}` authorization: `Bearer ${jwtAuthToken}`
}, },
@@ -82,7 +103,7 @@ describe("Project Environment Router", async () => {
expect(payload).toHaveProperty("environment"); expect(payload).toHaveProperty("environment");
expect(payload.environment).toEqual( expect(payload.environment).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: newEnvironment.id,
name: updatedName.name, name: updatedName.name,
slug: updatedName.slug, slug: updatedName.slug,
projectId: seedData1.project.id, projectId: seedData1.project.id,
@@ -91,61 +112,21 @@ describe("Project Environment Router", async () => {
updatedAt: expect.any(String) updatedAt: expect.any(String)
}) })
); );
mockProjectEnv.name = updatedName.name; await deleteProjectEnvironment(newEnvironment.id);
mockProjectEnv.slug = updatedName.slug;
}); });
test("Delete environment", async () => { test("Delete environment", async () => {
const res = await testServer.inject({ const newEnvironment = await createProjectEnvironment(mockProjectEnv.name, mockProjectEnv.slug);
method: "DELETE", const deletedProjectEnvironment = await deleteProjectEnvironment(newEnvironment.id);
url: `/api/v1/workspace/${seedData1.project.id}/environments/${mockProjectEnv.id}`, expect(deletedProjectEnvironment).toEqual(
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("environment");
expect(payload.environment).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: deletedProjectEnvironment.id,
name: mockProjectEnv.name, name: mockProjectEnv.name,
slug: mockProjectEnv.slug, slug: mockProjectEnv.slug,
position: 1, position: 4,
createdAt: expect.any(String), createdAt: expect.any(String),
updatedAt: expect.any(String) updatedAt: expect.any(String)
}) })
); );
}); });
// after all these opreations the list of environment should be still same
test("Default list of environment", async () => {
const res = await testServer.inject({
method: "GET",
url: `/api/v1/workspace/${seedData1.project.id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("workspace");
// check for default environments
expect(payload).toEqual({
workspace: expect.objectContaining({
name: seedData1.project.name,
id: seedData1.project.id,
slug: seedData1.project.slug,
environments: expect.arrayContaining([
expect.objectContaining(DEFAULT_PROJECT_ENVS[0]),
expect.objectContaining(DEFAULT_PROJECT_ENVS[1]),
expect.objectContaining(DEFAULT_PROJECT_ENVS[2])
])
})
});
// ensure only two default environments exist
expect(payload.workspace.environments.length).toBe(3);
});
}); });

View File

@@ -1,5 +1,40 @@
import { seedData1 } from "@app/db/seed-data"; import { seedData1 } from "@app/db/seed-data";
const createFolder = async (dto: { path: string; name: string }) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
name: dto.name,
path: dto.path
}
});
expect(res.statusCode).toBe(200);
return res.json().folder;
};
const deleteFolder = async (dto: { path: string; id: string }) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/folders/${dto.id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: dto.path
}
});
expect(res.statusCode).toBe(200);
return res.json().folder;
};
describe("Secret Folder Router", async () => { describe("Secret Folder Router", async () => {
test.each([ test.each([
{ name: "folder1", path: "/" }, // one in root { name: "folder1", path: "/" }, // one in root
@@ -7,30 +42,15 @@ describe("Secret Folder Router", async () => {
{ name: "folder2", path: "/" }, { name: "folder2", path: "/" },
{ name: "folder1", path: "/level1/level2" } // this should not create folder return same thing { name: "folder1", path: "/level1/level2" } // this should not create folder return same thing
])("Create folder $name in $path", async ({ name, path }) => { ])("Create folder $name in $path", async ({ name, path }) => {
const res = await testServer.inject({ const createdFolder = await createFolder({ path, name });
method: "POST",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
name,
path
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("folder");
// check for default environments // check for default environments
expect(payload).toEqual({ expect(createdFolder).toEqual(
folder: expect.objectContaining({ expect.objectContaining({
name, name,
id: expect.any(String) id: expect.any(String)
}) })
}); );
await deleteFolder({ path, id: createdFolder.id });
}); });
test.each([ test.each([
@@ -43,6 +63,8 @@ describe("Secret Folder Router", async () => {
}, },
{ path: "/level1/level2", expected: { folders: [{ name: "folder1" }], length: 1 } } { path: "/level1/level2", expected: { folders: [{ name: "folder1" }], length: 1 } }
])("Get folders $path", async ({ path, expected }) => { ])("Get folders $path", async ({ path, expected }) => {
const newFolders = await Promise.all(expected.folders.map(({ name }) => createFolder({ name, path })));
const res = await testServer.inject({ const res = await testServer.inject({
method: "GET", method: "GET",
url: `/api/v1/folders`, url: `/api/v1/folders`,
@@ -59,36 +81,22 @@ describe("Secret Folder Router", async () => {
expect(res.statusCode).toBe(200); expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload); const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("folders"); expect(payload).toHaveProperty("folders");
expect(payload.folders.length).toBe(expected.length); expect(payload.folders.length >= expected.folders.length).toBeTruthy();
expect(payload).toEqual({ folders: expected.folders.map((el) => expect.objectContaining(el)) }); expect(payload).toEqual({
}); folders: expect.arrayContaining(expected.folders.map((el) => expect.objectContaining(el)))
let toBeDeleteFolderId = "";
test("Update a deep folder", async () => {
const res = await testServer.inject({
method: "PATCH",
url: `/api/v1/folders/folder1`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
name: "folder-updated",
path: "/level1/level2"
}
}); });
expect(res.statusCode).toBe(200); await Promise.all(newFolders.map(({ id }) => deleteFolder({ path, id })));
const payload = JSON.parse(res.payload); });
expect(payload).toHaveProperty("folder");
expect(payload.folder).toEqual( test("Update a deep folder", async () => {
const newFolder = await createFolder({ name: "folder-updated", path: "/level1/level2" });
expect(newFolder).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
name: "folder-updated" name: "folder-updated"
}) })
); );
toBeDeleteFolderId = payload.folder.id;
const resUpdatedFolders = await testServer.inject({ const resUpdatedFolders = await testServer.inject({
method: "GET", method: "GET",
@@ -106,14 +114,16 @@ describe("Secret Folder Router", async () => {
expect(resUpdatedFolders.statusCode).toBe(200); expect(resUpdatedFolders.statusCode).toBe(200);
const updatedFolderList = JSON.parse(resUpdatedFolders.payload); const updatedFolderList = JSON.parse(resUpdatedFolders.payload);
expect(updatedFolderList).toHaveProperty("folders"); expect(updatedFolderList).toHaveProperty("folders");
expect(updatedFolderList.folders.length).toEqual(1);
expect(updatedFolderList.folders[0].name).toEqual("folder-updated"); expect(updatedFolderList.folders[0].name).toEqual("folder-updated");
await deleteFolder({ path: "/level1/level2", id: newFolder.id });
}); });
test("Delete a deep folder", async () => { test("Delete a deep folder", async () => {
const newFolder = await createFolder({ name: "folder-updated", path: "/level1/level2" });
const res = await testServer.inject({ const res = await testServer.inject({
method: "DELETE", method: "DELETE",
url: `/api/v1/folders/${toBeDeleteFolderId}`, url: `/api/v1/folders/${newFolder.id}`,
headers: { headers: {
authorization: `Bearer ${jwtAuthToken}` authorization: `Bearer ${jwtAuthToken}`
}, },

View File

@@ -1,32 +1,57 @@
import { seedData1 } from "@app/db/seed-data"; import { seedData1 } from "@app/db/seed-data";
describe("Secret Folder Router", async () => { const createSecretImport = async (importPath: string, importEnv: string) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/secret-imports`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/",
import: {
environment: importEnv,
path: importPath
}
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport;
};
const deleteSecretImport = async (id: string) => {
const res = await testServer.inject({
method: "DELETE",
url: `/api/v1/secret-imports/${id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/"
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
return payload.secretImport;
};
describe("Secret Import Router", async () => {
test.each([ test.each([
{ importEnv: "dev", importPath: "/" }, // one in root { importEnv: "dev", importPath: "/" }, // one in root
{ importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones { importEnv: "staging", importPath: "/" } // then create a deep one creating intermediate ones
])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => { ])("Create secret import $importEnv with path $importPath", async ({ importPath, importEnv }) => {
const res = await testServer.inject({
method: "POST",
url: `/api/v1/secret-imports`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/",
import: {
environment: importEnv,
path: importPath
}
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
// check for default environments // check for default environments
expect(payload.secretImport).toEqual( const payload = await createSecretImport(importPath, importEnv);
expect(payload).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
importPath: expect.any(String), importPath: expect.any(String),
@@ -37,10 +62,12 @@ describe("Secret Folder Router", async () => {
}) })
}) })
); );
await deleteSecretImport(payload.id);
}); });
let testSecretImportId = "";
test("Get secret imports", async () => { test("Get secret imports", async () => {
const createdImport1 = await createSecretImport("/", "dev");
const createdImport2 = await createSecretImport("/", "staging");
const res = await testServer.inject({ const res = await testServer.inject({
method: "GET", method: "GET",
url: `/api/v1/secret-imports`, url: `/api/v1/secret-imports`,
@@ -58,7 +85,6 @@ describe("Secret Folder Router", async () => {
const payload = JSON.parse(res.payload); const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImports"); expect(payload).toHaveProperty("secretImports");
expect(payload.secretImports.length).toBe(2); expect(payload.secretImports.length).toBe(2);
testSecretImportId = payload.secretImports[0].id;
expect(payload.secretImports).toEqual( expect(payload.secretImports).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ expect.objectContaining({
@@ -72,12 +98,20 @@ describe("Secret Folder Router", async () => {
}) })
]) ])
); );
await deleteSecretImport(createdImport1.id);
await deleteSecretImport(createdImport2.id);
}); });
test("Update secret import position", async () => { test("Update secret import position", async () => {
const res = await testServer.inject({ const devImportDetails = { path: "/", envSlug: "dev" };
const stagingImportDetails = { path: "/", envSlug: "staging" };
const createdImport1 = await createSecretImport(devImportDetails.path, devImportDetails.envSlug);
const createdImport2 = await createSecretImport(stagingImportDetails.path, stagingImportDetails.envSlug);
const updateImportRes = await testServer.inject({
method: "PATCH", method: "PATCH",
url: `/api/v1/secret-imports/${testSecretImportId}`, url: `/api/v1/secret-imports/${createdImport1.id}`,
headers: { headers: {
authorization: `Bearer ${jwtAuthToken}` authorization: `Bearer ${jwtAuthToken}`
}, },
@@ -91,8 +125,8 @@ describe("Secret Folder Router", async () => {
} }
}); });
expect(res.statusCode).toBe(200); expect(updateImportRes.statusCode).toBe(200);
const payload = JSON.parse(res.payload); const payload = JSON.parse(updateImportRes.payload);
expect(payload).toHaveProperty("secretImport"); expect(payload).toHaveProperty("secretImport");
// check for default environments // check for default environments
expect(payload.secretImport).toEqual( expect(payload.secretImport).toEqual(
@@ -102,7 +136,7 @@ describe("Secret Folder Router", async () => {
position: 2, position: 2,
importEnv: expect.objectContaining({ importEnv: expect.objectContaining({
name: expect.any(String), name: expect.any(String),
slug: expect.any(String), slug: expect.stringMatching(devImportDetails.envSlug),
id: expect.any(String) id: expect.any(String)
}) })
}) })
@@ -124,28 +158,19 @@ describe("Secret Folder Router", async () => {
expect(secretImportsListRes.statusCode).toBe(200); expect(secretImportsListRes.statusCode).toBe(200);
const secretImportList = JSON.parse(secretImportsListRes.payload); const secretImportList = JSON.parse(secretImportsListRes.payload);
expect(secretImportList).toHaveProperty("secretImports"); expect(secretImportList).toHaveProperty("secretImports");
expect(secretImportList.secretImports[1].id).toEqual(testSecretImportId); expect(secretImportList.secretImports[1].id).toEqual(createdImport1.id);
expect(secretImportList.secretImports[0].id).toEqual(createdImport2.id);
await deleteSecretImport(createdImport1.id);
await deleteSecretImport(createdImport2.id);
}); });
test("Delete secret import position", async () => { test("Delete secret import position", async () => {
const res = await testServer.inject({ const createdImport1 = await createSecretImport("/", "dev");
method: "DELETE", const createdImport2 = await createSecretImport("/", "staging");
url: `/api/v1/secret-imports/${testSecretImportId}`, const deletedImport = await deleteSecretImport(createdImport1.id);
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/"
}
});
expect(res.statusCode).toBe(200);
const payload = JSON.parse(res.payload);
expect(payload).toHaveProperty("secretImport");
// check for default environments // check for default environments
expect(payload.secretImport).toEqual( expect(deletedImport).toEqual(
expect.objectContaining({ expect.objectContaining({
id: expect.any(String), id: expect.any(String),
importPath: expect.any(String), importPath: expect.any(String),
@@ -175,5 +200,7 @@ describe("Secret Folder Router", async () => {
expect(secretImportList).toHaveProperty("secretImports"); expect(secretImportList).toHaveProperty("secretImports");
expect(secretImportList.secretImports.length).toEqual(1); expect(secretImportList.secretImports.length).toEqual(1);
expect(secretImportList.secretImports[0].position).toEqual(1); expect(secretImportList.secretImports[0].position).toEqual(1);
await deleteSecretImport(createdImport2.id);
}); });
}); });

View File

@@ -0,0 +1,579 @@
import crypto from "node:crypto";
import { SecretType, TSecrets } from "@app/db/schemas";
import { decryptSecret, encryptSecret, getUserPrivateKey, seedData1 } from "@app/db/seed-data";
import { decryptAsymmetric, decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
const createServiceToken = async (
scopes: { environment: string; secretPath: string }[],
permissions: ("read" | "write")[]
) => {
const projectKeyRes = await testServer.inject({
method: "GET",
url: `/api/v2/workspace/${seedData1.project.id}/encrypted-key`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
const projectKeyEnc = JSON.parse(projectKeyRes.payload);
const userInfoRes = await testServer.inject({
method: "GET",
url: "/api/v2/users/me",
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
const { user: userInfo } = JSON.parse(userInfoRes.payload);
const privateKey = await getUserPrivateKey(seedData1.password, userInfo);
const projectKey = decryptAsymmetric({
ciphertext: projectKeyEnc.encryptedKey,
nonce: projectKeyEnc.nonce,
publicKey: projectKeyEnc.sender.publicKey,
privateKey
});
const randomBytes = crypto.randomBytes(16).toString("hex");
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(projectKey, randomBytes);
const serviceTokenRes = await testServer.inject({
method: "POST",
url: "/api/v2/service-token",
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
name: "test-token",
workspaceId: seedData1.project.id,
scopes,
encryptedKey: ciphertext,
iv,
tag,
permissions,
expiresIn: null
}
});
expect(serviceTokenRes.statusCode).toBe(200);
const serviceTokenInfo = serviceTokenRes.json();
expect(serviceTokenInfo).toHaveProperty("serviceToken");
expect(serviceTokenInfo).toHaveProperty("serviceTokenData");
return `${serviceTokenInfo.serviceToken}.${randomBytes}`;
};
const deleteServiceToken = async () => {
const serviceTokenListRes = await testServer.inject({
method: "GET",
url: `/api/v1/workspace/${seedData1.project.id}/service-token-data`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(serviceTokenListRes.statusCode).toBe(200);
const serviceTokens = JSON.parse(serviceTokenListRes.payload).serviceTokenData as { name: string; id: string }[];
expect(serviceTokens.length).toBeGreaterThan(0);
const serviceTokenInfo = serviceTokens.find(({ name }) => name === "test-token");
expect(serviceTokenInfo).toBeDefined();
const deleteTokenRes = await testServer.inject({
method: "DELETE",
url: `/api/v2/service-token/${serviceTokenInfo?.id}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
}
});
expect(deleteTokenRes.statusCode).toBe(200);
};
const createSecret = async (dto: {
projectKey: string;
path: string;
key: string;
value: string;
comment: string;
type?: SecretType;
token: string;
}) => {
const createSecretReqBody = {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
type: dto.type || SecretType.Shared,
secretPath: dto.path,
...encryptSecret(dto.projectKey, dto.key, dto.value, dto.comment)
};
const createSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/${dto.key}`,
headers: {
authorization: `Bearer ${dto.token}`
},
body: createSecretReqBody
});
expect(createSecRes.statusCode).toBe(200);
const createdSecretPayload = JSON.parse(createSecRes.payload);
expect(createdSecretPayload).toHaveProperty("secret");
return createdSecretPayload.secret;
};
const deleteSecret = async (dto: { path: string; key: string; token: string }) => {
const deleteSecRes = await testServer.inject({
method: "DELETE",
url: `/api/v3/secrets/${dto.key}`,
headers: {
authorization: `Bearer ${dto.token}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: dto.path
}
});
expect(deleteSecRes.statusCode).toBe(200);
const updatedSecretPayload = JSON.parse(deleteSecRes.payload);
expect(updatedSecretPayload).toHaveProperty("secret");
return updatedSecretPayload.secret;
};
describe("Service token secret ops", async () => {
let serviceToken = "";
let projectKey = "";
let folderId = "";
beforeAll(async () => {
serviceToken = await createServiceToken(
[{ secretPath: "/**", environment: seedData1.environment.slug }],
["read", "write"]
);
// this is ensure cli service token decryptiong working fine
const serviceTokenInfoRes = await testServer.inject({
method: "GET",
url: "/api/v2/service-token",
headers: {
authorization: `Bearer ${serviceToken}`
}
});
expect(serviceTokenInfoRes.statusCode).toBe(200);
const serviceTokenInfo = serviceTokenInfoRes.json();
const serviceTokenParts = serviceToken.split(".");
projectKey = decryptSymmetric128BitHexKeyUTF8({
key: serviceTokenParts[3],
tag: serviceTokenInfo.tag,
ciphertext: serviceTokenInfo.encryptedKey,
iv: serviceTokenInfo.iv
});
// create a deep folder
const folderCreate = await testServer.inject({
method: "POST",
url: `/api/v1/folders`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
name: "folder",
path: "/nested1/nested2"
}
});
expect(folderCreate.statusCode).toBe(200);
folderId = folderCreate.json().folder.id;
});
afterAll(async () => {
await deleteServiceToken();
// create a deep folder
const deleteFolder = await testServer.inject({
method: "DELETE",
url: `/api/v1/folders/${folderId}`,
headers: {
authorization: `Bearer ${jwtAuthToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
path: "/nested1/nested2"
}
});
expect(deleteFolder.statusCode).toBe(200);
});
const testSecrets = [
{
path: "/",
secret: {
key: "ST-SEC",
value: "something-secret",
comment: "some comment"
}
},
{
path: "/nested1/nested2/folder",
secret: {
key: "NESTED-ST-SEC",
value: "something-secret",
comment: "some comment"
}
}
];
const getSecrets = async (environment: string, secretPath = "/") => {
const res = await testServer.inject({
method: "GET",
url: `/api/v3/secrets`,
headers: {
authorization: `Bearer ${serviceToken}`
},
query: {
secretPath,
environment,
workspaceId: seedData1.project.id
}
});
const secrets: TSecrets[] = JSON.parse(res.payload).secrets || [];
return secrets.map((el) => ({ ...decryptSecret(projectKey, el), type: el.type }));
};
test.each(testSecrets)("Create secret in path $path", async ({ secret, path }) => {
const createdSecret = await createSecret({ projectKey, path, ...secret, token: serviceToken });
const decryptedSecret = decryptSecret(projectKey, createdSecret);
expect(decryptedSecret.key).toEqual(secret.key);
expect(decryptedSecret.value).toEqual(secret.value);
expect(decryptedSecret.comment).toEqual(secret.comment);
expect(decryptedSecret.version).toEqual(1);
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
key: secret.key,
value: secret.value,
type: SecretType.Shared
})
])
);
await deleteSecret({ path, key: secret.key, token: serviceToken });
});
test.each(testSecrets)("Get secret by name in path $path", async ({ secret, path }) => {
await createSecret({ projectKey, path, ...secret, token: serviceToken });
const getSecByNameRes = await testServer.inject({
method: "GET",
url: `/api/v3/secrets/${secret.key}`,
headers: {
authorization: `Bearer ${serviceToken}`
},
query: {
secretPath: path,
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug
}
});
expect(getSecByNameRes.statusCode).toBe(200);
const getSecretByNamePayload = JSON.parse(getSecByNameRes.payload);
expect(getSecretByNamePayload).toHaveProperty("secret");
const decryptedSecret = decryptSecret(projectKey, getSecretByNamePayload.secret);
expect(decryptedSecret.key).toEqual(secret.key);
expect(decryptedSecret.value).toEqual(secret.value);
expect(decryptedSecret.comment).toEqual(secret.comment);
await deleteSecret({ path, key: secret.key, token: serviceToken });
});
test.each(testSecrets)("Update secret in path $path", async ({ path, secret }) => {
await createSecret({ projectKey, path, ...secret, token: serviceToken });
const updateSecretReqBody = {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
type: SecretType.Shared,
secretPath: path,
...encryptSecret(projectKey, secret.key, "new-value", secret.comment)
};
const updateSecRes = await testServer.inject({
method: "PATCH",
url: `/api/v3/secrets/${secret.key}`,
headers: {
authorization: `Bearer ${serviceToken}`
},
body: updateSecretReqBody
});
expect(updateSecRes.statusCode).toBe(200);
const updatedSecretPayload = JSON.parse(updateSecRes.payload);
expect(updatedSecretPayload).toHaveProperty("secret");
const decryptedSecret = decryptSecret(projectKey, updatedSecretPayload.secret);
expect(decryptedSecret.key).toEqual(secret.key);
expect(decryptedSecret.value).toEqual("new-value");
expect(decryptedSecret.comment).toEqual(secret.comment);
// list secret should have updated value
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining([
expect.objectContaining({
key: secret.key,
value: "new-value",
type: SecretType.Shared
})
])
);
await deleteSecret({ path, key: secret.key, token: serviceToken });
});
test.each(testSecrets)("Delete secret in path $path", async ({ secret, path }) => {
await createSecret({ projectKey, path, ...secret, token: serviceToken });
const deletedSecret = await deleteSecret({ path, key: secret.key, token: serviceToken });
const decryptedSecret = decryptSecret(projectKey, deletedSecret);
expect(decryptedSecret.key).toEqual(secret.key);
// shared secret deletion should delete personal ones also
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.not.arrayContaining([
expect.objectContaining({
key: secret.key,
type: SecretType.Shared
})
])
);
});
test.each(testSecrets)("Bulk create secrets in path $path", async ({ secret, path }) => {
const createSharedSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/batch`,
headers: {
authorization: `Bearer ${serviceToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretName: `BULK-${secret.key}-${i + 1}`,
...encryptSecret(projectKey, `BULK-${secret.key}-${i + 1}`, secret.value, secret.comment)
}))
}
});
expect(createSharedSecRes.statusCode).toBe(200);
const createSharedSecPayload = JSON.parse(createSharedSecRes.payload);
expect(createSharedSecPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
key: `BULK-${secret.key}-${i + 1}`,
type: SecretType.Shared
})
)
)
);
await Promise.all(
Array.from(Array(5)).map((_e, i) =>
deleteSecret({ path, token: serviceToken, key: `BULK-${secret.key}-${i + 1}` })
)
);
});
test.each(testSecrets)("Bulk create fail on existing secret in path $path", async ({ secret, path }) => {
await createSecret({ projectKey, ...secret, key: `BULK-${secret.key}-1`, path, token: serviceToken });
const createSharedSecRes = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/batch`,
headers: {
authorization: `Bearer ${serviceToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretName: `BULK-${secret.key}-${i + 1}`,
...encryptSecret(projectKey, `BULK-${secret.key}-${i + 1}`, secret.value, secret.comment)
}))
}
});
expect(createSharedSecRes.statusCode).toBe(400);
await deleteSecret({ path, key: `BULK-${secret.key}-1`, token: serviceToken });
});
test.each(testSecrets)("Bulk update secrets in path $path", async ({ secret, path }) => {
await Promise.all(
Array.from(Array(5)).map((_e, i) =>
createSecret({ projectKey, token: serviceToken, ...secret, key: `BULK-${secret.key}-${i + 1}`, path })
)
);
const updateSharedSecRes = await testServer.inject({
method: "PATCH",
url: `/api/v3/secrets/batch`,
headers: {
authorization: `Bearer ${serviceToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretName: `BULK-${secret.key}-${i + 1}`,
...encryptSecret(projectKey, `BULK-${secret.key}-${i + 1}`, "update-value", secret.comment)
}))
}
});
expect(updateSharedSecRes.statusCode).toBe(200);
const updateSharedSecPayload = JSON.parse(updateSharedSecRes.payload);
expect(updateSharedSecPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
key: `BULK-${secret.key}-${i + 1}`,
value: "update-value",
type: SecretType.Shared
})
)
)
);
await Promise.all(
Array.from(Array(5)).map((_e, i) =>
deleteSecret({ path, key: `BULK-${secret.key}-${i + 1}`, token: serviceToken })
)
);
});
test.each(testSecrets)("Bulk delete secrets in path $path", async ({ secret, path }) => {
await Promise.all(
Array.from(Array(5)).map((_e, i) =>
createSecret({ projectKey, token: serviceToken, ...secret, key: `BULK-${secret.key}-${i + 1}`, path })
)
);
const deletedSharedSecRes = await testServer.inject({
method: "DELETE",
url: `/api/v3/secrets/batch`,
headers: {
authorization: `Bearer ${serviceToken}`
},
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: path,
secrets: Array.from(Array(5)).map((_e, i) => ({
secretName: `BULK-${secret.key}-${i + 1}`
}))
}
});
expect(deletedSharedSecRes.statusCode).toBe(200);
const deletedSecretPayload = JSON.parse(deletedSharedSecRes.payload);
expect(deletedSecretPayload).toHaveProperty("secrets");
// bulk ones should exist
const secrets = await getSecrets(seedData1.environment.slug, path);
expect(secrets).toEqual(
expect.not.arrayContaining(
Array.from(Array(5)).map((_e, i) =>
expect.objectContaining({
key: `BULK-${secret.value}-${i + 1}`,
type: SecretType.Shared
})
)
)
);
});
});
describe("Service token fail cases", async () => {
test("Unauthorized secret path access", async () => {
const serviceToken = await createServiceToken(
[{ secretPath: "/", environment: seedData1.environment.slug }],
["read", "write"]
);
const fetchSecrets = await testServer.inject({
method: "GET",
url: "/api/v3/secrets",
query: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: "/nested/deep"
},
headers: {
authorization: `Bearer ${serviceToken}`
}
});
expect(fetchSecrets.statusCode).toBe(401);
expect(fetchSecrets.json().error).toBe("PermissionDenied");
await deleteServiceToken();
});
test("Unauthorized secret environment access", async () => {
const serviceToken = await createServiceToken(
[{ secretPath: "/", environment: seedData1.environment.slug }],
["read", "write"]
);
const fetchSecrets = await testServer.inject({
method: "GET",
url: "/api/v3/secrets",
query: {
workspaceId: seedData1.project.id,
environment: "prod",
secretPath: "/"
},
headers: {
authorization: `Bearer ${serviceToken}`
}
});
expect(fetchSecrets.statusCode).toBe(401);
expect(fetchSecrets.json().error).toBe("PermissionDenied");
await deleteServiceToken();
});
test("Unauthorized write operation", async () => {
const serviceToken = await createServiceToken(
[{ secretPath: "/", environment: seedData1.environment.slug }],
["read"]
);
const writeSecrets = await testServer.inject({
method: "POST",
url: `/api/v3/secrets/NEW`,
body: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
type: SecretType.Shared,
secretPath: "/",
// doesn't matter project key because this will fail before that due to read only access
...encryptSecret(crypto.randomBytes(16).toString("hex"), "NEW", "value", "")
},
headers: {
authorization: `Bearer ${serviceToken}`
}
});
expect(writeSecrets.statusCode).toBe(401);
expect(writeSecrets.json().error).toBe("PermissionDenied");
// but read access should still work fine
const fetchSecrets = await testServer.inject({
method: "GET",
url: "/api/v3/secrets",
query: {
workspaceId: seedData1.project.id,
environment: seedData1.environment.slug,
secretPath: "/"
},
headers: {
authorization: `Bearer ${serviceToken}`
}
});
expect(fetchSecrets.statusCode).toBe(200);
await deleteServiceToken();
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +1,30 @@
// import { main } from "@app/server/app"; // eslint-disable-next-line
import { initEnvConfig } from "@app/lib/config/env"; import "ts-node/register";
import dotenv from "dotenv"; import dotenv from "dotenv";
import jwt from "jsonwebtoken";
import knex from "knex"; import knex from "knex";
import path from "path"; import path from "path";
import { mockSmtpServer } from "./mocks/smtp";
import { initLogger } from "@app/lib/logger";
import jwt from "jsonwebtoken";
import "ts-node/register";
import { main } from "@app/server/app";
import { mockQueue } from "./mocks/queue";
import { AuthTokenType } from "@app/services/auth/auth-type";
import { seedData1 } from "@app/db/seed-data"; import { seedData1 } from "@app/db/seed-data";
import { initEnvConfig } from "@app/lib/config/env";
import { initLogger } from "@app/lib/logger";
import { main } from "@app/server/app";
import { AuthTokenType } from "@app/services/auth/auth-type";
dotenv.config({ path: path.join(__dirname, "../.env.test") }); import { mockQueue } from "./mocks/queue";
import { mockSmtpServer } from "./mocks/smtp";
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
export default { export default {
name: "knex-env", name: "knex-env",
transformMode: "ssr", transformMode: "ssr",
async setup() { async setup() {
const logger = await initLogger();
const cfg = initEnvConfig(logger);
const db = knex({ const db = knex({
client: "pg", client: "pg",
connection: process.env.DB_CONNECTION_URI, connection: cfg.DB_CONNECTION_URI,
migrations: { migrations: {
directory: path.join(__dirname, "../src/db/migrations"), directory: path.join(__dirname, "../src/db/migrations"),
extension: "ts", extension: "ts",
@@ -37,8 +41,6 @@ export default {
await db.seed.run(); await db.seed.run();
const smtp = mockSmtpServer(); const smtp = mockSmtpServer();
const queue = mockQueue(); const queue = mockQueue();
const logger = await initLogger();
const cfg = initEnvConfig(logger);
const server = await main({ db, smtp, logger, queue }); const server = await main({ db, smtp, logger, queue });
// @ts-expect-error type // @ts-expect-error type
globalThis.testServer = server; globalThis.testServer = server;

1853
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,12 +2,12 @@
"name": "backend", "name": "backend",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js", "main": "./dist/main.mjs",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1", "test": "echo \"Error: no test specified\" && exit 1",
"dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine", "dev": "tsx watch --clear-screen=false ./src/main.ts | pino-pretty --colorize --colorizeObjects --singleLine",
"dev:docker": "nodemon", "dev:docker": "nodemon",
"build": "rimraf dist && tsup && cp -R ./src/lib/validator/disposable_emails.txt ./dist && cp -R ./src/services/smtp/templates ./dist", "build": "tsup",
"start": "node dist/main.mjs", "start": "node dist/main.mjs",
"type:check": "tsc --noEmit", "type:check": "tsc --noEmit",
"lint:fix": "eslint --fix --ext js,ts ./src", "lint:fix": "eslint --fix --ext js,ts ./src",
@@ -24,8 +24,8 @@
"migration:latest": "knex --knexfile ./src/db/knexfile.ts --client pg migrate:latest", "migration:latest": "knex --knexfile ./src/db/knexfile.ts --client pg migrate:latest",
"migration:rollback": "knex --knexfile ./src/db/knexfile.ts migrate:rollback", "migration:rollback": "knex --knexfile ./src/db/knexfile.ts migrate:rollback",
"seed:new": "tsx ./scripts/create-seed-file.ts", "seed:new": "tsx ./scripts/create-seed-file.ts",
"seed:run": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run", "seed": "knex --knexfile ./src/db/knexfile.ts --client pg seed:run",
"db:reset": "npm run migration:rollback -- --all && npm run migration:latest && npm run seed:run" "db:reset": "npm run migration:rollback -- --all && npm run migration:latest"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "",
@@ -44,7 +44,13 @@
"@types/pg": "^8.10.9", "@types/pg": "^8.10.9",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/prompt-sync": "^4.2.3", "@types/prompt-sync": "^4.2.3",
"@types/resolve": "^1.20.6",
"@types/uuid": "^9.0.7", "@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "^3.6.1", "eslint-import-resolver-typescript": "^3.6.1",
"eslint-plugin-import": "^2.29.1", "eslint-plugin-import": "^2.29.1",
@@ -55,44 +61,41 @@
"prompt-sync": "^4.2.0", "prompt-sync": "^4.2.0",
"rimraf": "^5.0.5", "rimraf": "^5.0.5",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"tsc-alias": "^1.8.8",
"tsconfig-paths": "^4.2.0", "tsconfig-paths": "^4.2.0",
"tsup": "^8.0.1", "tsup": "^8.0.1",
"tsx": "^4.4.0", "tsx": "^4.4.0",
"typescript": "^5.3.2", "typescript": "^5.3.2",
"vite-tsconfig-paths": "^4.2.2", "vite-tsconfig-paths": "^4.2.2",
"vitest": "^1.0.4" "vitest": "^1.2.2"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-secrets-manager": "^3.485.0", "@aws-sdk/client-secrets-manager": "^3.485.0",
"@casl/ability": "^6.5.0", "@casl/ability": "^6.5.0",
"@fastify/cookie": "^9.2.0", "@fastify/cookie": "^9.2.0",
"@fastify/cors": "^8.4.1", "@fastify/cors": "^8.4.1",
"@fastify/etag": "^5.1.0",
"@fastify/formbody": "^7.4.0", "@fastify/formbody": "^7.4.0",
"@fastify/helmet": "^11.1.1", "@fastify/helmet": "^11.1.1",
"@fastify/passport": "^2.4.0", "@fastify/passport": "^2.4.0",
"@fastify/rate-limit": "^9.0.0", "@fastify/rate-limit": "^9.0.0",
"@fastify/session": "^10.7.0", "@fastify/session": "^10.7.0",
"@fastify/swagger": "^8.12.0", "@fastify/swagger": "^8.12.0",
"@fastify/swagger-ui": "^1.10.1", "@fastify/swagger-ui": "^2.1.0",
"@node-saml/passport-saml": "^4.0.4", "@node-saml/passport-saml": "^4.0.4",
"@octokit/rest": "^20.0.2", "@octokit/rest": "^20.0.2",
"@octokit/webhooks-types": "^7.3.1", "@octokit/webhooks-types": "^7.3.1",
"@serdnam/pino-cloudwatch-transport": "^1.0.4", "@serdnam/pino-cloudwatch-transport": "^1.0.4",
"@sindresorhus/slugify": "^2.2.1", "@sindresorhus/slugify": "^2.2.1",
"@typescript-eslint/eslint-plugin": "^6.20.0",
"@typescript-eslint/parser": "^6.20.0",
"@ucast/mongo2js": "^1.3.4", "@ucast/mongo2js": "^1.3.4",
"ajv": "^8.12.0", "ajv": "^8.12.0",
"argon2": "^0.31.2", "argon2": "^0.31.2",
"aws-sdk": "^2.1532.0", "aws-sdk": "^2.1545.0",
"axios": "^1.6.2", "axios": "^1.6.4",
"axios-retry": "^4.0.0", "axios-retry": "^4.0.0",
"bcrypt": "^5.1.1", "bcrypt": "^5.1.1",
"bullmq": "^5.1.1", "bullmq": "^5.1.1",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"eslint": "^8.56.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-config-airbnb-typescript": "^17.1.0",
"fastify": "^4.24.3", "fastify": "^4.24.3",
"fastify-plugin": "^4.5.1", "fastify-plugin": "^4.5.1",
"handlebars": "^4.7.8", "handlebars": "^4.7.8",
@@ -106,7 +109,7 @@
"mysql2": "^3.6.5", "mysql2": "^3.6.5",
"nanoid": "^5.0.4", "nanoid": "^5.0.4",
"node-cache": "^5.1.2", "node-cache": "^5.1.2",
"nodemailer": "^6.9.7", "nodemailer": "^6.9.9",
"ora": "^7.0.1", "ora": "^7.0.1",
"passport-github": "^1.1.0", "passport-github": "^1.1.0",
"passport-gitlab2": "^5.0.0", "passport-gitlab2": "^5.0.0",
@@ -115,7 +118,7 @@
"picomatch": "^3.0.1", "picomatch": "^3.0.1",
"pino": "^8.16.2", "pino": "^8.16.2",
"posthog-node": "^3.6.0", "posthog-node": "^3.6.0",
"probot": "^12.3.3", "probot": "^13.0.0",
"smee-client": "^2.0.0", "smee-client": "^2.0.0",
"tweetnacl": "^1.0.3", "tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1", "tweetnacl-util": "^0.15.1",

View File

@@ -7,11 +7,10 @@ import promptSync from "prompt-sync";
const prompt = promptSync({ sigint: true }); const prompt = promptSync({ sigint: true });
const migrationName = prompt("Enter name for seedfile: "); const migrationName = prompt("Enter name for seedfile: ");
const fileCounter = readdirSync(path.join(__dirname, "../src/db/seed")).length || 1; const fileCounter = readdirSync(path.join(__dirname, "../src/db/seeds")).length || 1;
execSync( execSync(
`npx knex seed:make --knexfile ${path.join( `npx knex seed:make --knexfile ${path.join(__dirname, "../src/db/knexfile.ts")} -x ts ${
__dirname, fileCounter + 1
"../src/db/knexfile.ts" }-${migrationName}`,
)} -x ts ${fileCounter}-${migrationName}`,
{ stdio: "inherit" } { stdio: "inherit" }
); );

View File

@@ -3,13 +3,9 @@ import dotenv from "dotenv";
import path from "path"; import path from "path";
import knex from "knex"; import knex from "knex";
import { writeFileSync } from "fs"; import { writeFileSync } from "fs";
import promptSync from "prompt-sync";
const prompt = promptSync({ sigint: true });
dotenv.config({ dotenv.config({
path: path.join(__dirname, "../.env"), path: path.join(__dirname, "../../.env.migration")
debug: true
}); });
const db = knex({ const db = knex({
@@ -94,17 +90,7 @@ const main = async () => {
.orderBy("table_name") .orderBy("table_name")
).filter((el) => !el.tableName.includes("_migrations")); ).filter((el) => !el.tableName.includes("_migrations"));
console.log("Select a table to generate schema");
console.table(tables);
console.log("all: all tables");
const selectedTables = prompt("Type table numbers comma seperated: ");
const tableNumbers =
selectedTables !== "all" ? selectedTables.split(",").map((el) => Number(el)) : [];
for (let i = 0; i < tables.length; i += 1) { for (let i = 0; i < tables.length; i += 1) {
// skip if not desired table
if (selectedTables !== "all" && !tableNumbers.includes(i)) continue;
const { tableName } = tables[i]; const { tableName } = tables[i];
const columns = await db(tableName).columnInfo(); const columns = await db(tableName).columnInfo();
const columnNames = Object.keys(columns); const columnNames = Object.keys(columns);
@@ -124,16 +110,16 @@ const main = async () => {
if (colInfo.nullable) { if (colInfo.nullable) {
ztype = ztype.concat(".nullable().optional()"); ztype = ztype.concat(".nullable().optional()");
} }
schema = schema.concat(`${!schema ? "\n" : ""} ${columnName}: ${ztype},\n`); schema = schema.concat(
`${!schema ? "\n" : ""} ${columnName}: ${ztype}${colNum === columnNames.length - 1 ? "" : ","}\n`
);
} }
const dashcase = tableName.split("_").join("-"); const dashcase = tableName.split("_").join("-");
const pascalCase = tableName const pascalCase = tableName
.split("_") .split("_")
.reduce( .reduce((prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`, "");
(prev, curr) => prev + `${curr.at(0)?.toUpperCase()}${curr.slice(1).toLowerCase()}`,
""
);
writeFileSync( writeFileSync(
path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`), path.join(__dirname, "../src/db/schemas", `${dashcase}.ts`),
`// Code generated by automation script, DO NOT EDIT. `// Code generated by automation script, DO NOT EDIT.
@@ -152,15 +138,6 @@ export type T${pascalCase}Insert = Omit<T${pascalCase}, TImmutableDBKeys>;
export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>; export type T${pascalCase}Update = Partial<Omit<T${pascalCase}, TImmutableDBKeys>>;
` `
); );
// const file = readFileSync(path.join(__dirname, "../src/db/schemas/index.ts"), "utf8");
// if (!file.includes(`export * from "./${dashcase};"`)) {
// appendFileSync(
// path.join(__dirname, "../src/db/schemas/index.ts"),
// `\nexport * from "./${dashcase}";`,
// "utf8"
// );
// }
} }
process.exit(0); process.exit(0);

View File

@@ -51,6 +51,7 @@ declare module "fastify" {
// used for mfa session authentication // used for mfa session authentication
mfa: { mfa: {
userId: string; userId: string;
orgId?: string;
user: TUsers; user: TUsers;
}; };
// identity injection. depending on which kinda of token the information is filled in auth // identity injection. depending on which kinda of token the information is filled in auth
@@ -58,6 +59,7 @@ declare module "fastify" {
permission: { permission: {
type: ActorType; type: ActorType;
id: string; id: string;
orgId?: string;
}; };
// passport data // passport data
passportUser: { passportUser: {

View File

@@ -1,10 +1,18 @@
import knex from "knex"; import knex from "knex";
export type TDbClient = ReturnType<typeof initDbConnection>; export type TDbClient = ReturnType<typeof initDbConnection>;
export const initDbConnection = (dbConnectionUri: string) => { export const initDbConnection = ({ dbConnectionUri, dbRootCert }: { dbConnectionUri: string; dbRootCert?: string }) => {
const db = knex({ const db = knex({
client: "pg", client: "pg",
connection: dbConnectionUri connection: {
connectionString: dbConnectionUri,
ssl: dbRootCert
? {
rejectUnauthorized: true,
ca: Buffer.from(dbRootCert, "base64").toString("ascii")
}
: false
}
}); });
return db; return db;

View File

@@ -5,9 +5,13 @@ import dotenv from "dotenv";
import type { Knex } from "knex"; import type { Knex } from "knex";
import path from "path"; import path from "path";
// Update with your config settings. // Update with your config settings. .
dotenv.config({ dotenv.config({
path: path.join(__dirname, "../../.env"), path: path.join(__dirname, "../../../.env.migration"),
debug: true
});
dotenv.config({
path: path.join(__dirname, "../../../.env"),
debug: true debug: true
}); });
export default { export default {

View File

@@ -0,0 +1,25 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.boolean("authEnforced").defaultTo(false);
t.index("slug");
});
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
t.datetime("lastUsed");
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable(TableName.Organization, (t) => {
t.dropColumn("authEnforced");
t.dropIndex("slug");
});
await knex.schema.alterTable(TableName.SamlConfig, (t) => {
t.dropColumn("lastUsed");
});
}

View File

@@ -13,7 +13,8 @@ export const OrganizationsSchema = z.object({
customerId: z.string().nullable().optional(), customerId: z.string().nullable().optional(),
slug: z.string(), slug: z.string(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date() updatedAt: z.date(),
authEnforced: z.boolean().default(false).nullable().optional()
}); });
export type TOrganizations = z.infer<typeof OrganizationsSchema>; export type TOrganizations = z.infer<typeof OrganizationsSchema>;

View File

@@ -22,7 +22,8 @@ export const SamlConfigsSchema = z.object({
certTag: z.string().nullable().optional(), certTag: z.string().nullable().optional(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
orgId: z.string().uuid() orgId: z.string().uuid(),
lastUsed: z.date().nullable().optional()
}); });
export type TSamlConfigs = z.infer<typeof SamlConfigsSchema>; export type TSamlConfigs = z.infer<typeof SamlConfigsSchema>;

View File

@@ -6,13 +6,14 @@ import nacl from "tweetnacl";
import { encodeBase64 } from "tweetnacl-util"; import { encodeBase64 } from "tweetnacl-util";
import { import {
decryptAsymmetric,
// decryptAsymmetric, // decryptAsymmetric,
decryptSymmetric, decryptSymmetric128BitHexKeyUTF8,
encryptAsymmetric, encryptAsymmetric,
encryptSymmetric encryptSymmetric128BitHexKeyUTF8
} from "@app/lib/crypto"; } from "@app/lib/crypto";
import { TUserEncryptionKeys } from "./schemas"; import { TSecrets, TUserEncryptionKeys } from "./schemas";
export const seedData1 = { export const seedData1 = {
id: "3dafd81d-4388-432b-a4c5-f735616868c1", id: "3dafd81d-4388-432b-a4c5-f735616868c1",
@@ -31,6 +32,14 @@ export const seedData1 = {
name: "Development", name: "Development",
slug: "dev" slug: "dev"
}, },
machineIdentity: {
id: "88fa7aed-9288-401e-a4c9-fa9430be62a0",
name: "mac1",
clientCredentials: {
id: "3f6135db-f237-421d-af66-a8f4e80d443b",
secret: "da35a5a5a7b57f977a9a73394506e878a7175d06606df43dc93e1472b10cf339"
}
},
token: { token: {
id: "a9dfafba-a3b7-42e3-8618-91abb702fd36" id: "a9dfafba-a3b7-42e3-8618-91abb702fd36"
} }
@@ -73,7 +82,7 @@ export const generateUserSrpKeys = async (password: string) => {
ciphertext: encryptedPrivateKey, ciphertext: encryptedPrivateKey,
iv: encryptedPrivateKeyIV, iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag tag: encryptedPrivateKeyTag
} = encryptSymmetric(privateKey, key.toString("base64")); } = encryptSymmetric128BitHexKeyUTF8(privateKey, key);
// create the protected key by encrypting the symmetric key // create the protected key by encrypting the symmetric key
// [key] with the derived key // [key] with the derived key
@@ -81,7 +90,7 @@ export const generateUserSrpKeys = async (password: string) => {
ciphertext: protectedKey, ciphertext: protectedKey,
iv: protectedKeyIV, iv: protectedKeyIV,
tag: protectedKeyTag tag: protectedKeyTag
} = encryptSymmetric(key.toString("hex"), derivedKey.toString("base64")); } = encryptSymmetric128BitHexKeyUTF8(key.toString("hex"), derivedKey);
return { return {
protectedKey, protectedKey,
@@ -107,32 +116,102 @@ export const getUserPrivateKey = async (password: string, user: TUserEncryptionK
raw: true raw: true
}); });
if (!derivedKey) throw new Error("Failed to derive key from password"); if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric({
const key = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.protectedKey as string, ciphertext: user.protectedKey as string,
iv: user.protectedKeyIV as string, iv: user.protectedKeyIV as string,
tag: user.protectedKeyTag as string, tag: user.protectedKeyTag as string,
key: derivedKey.toString("base64") key: derivedKey
}); });
const privateKey = decryptSymmetric({
const privateKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.encryptedPrivateKey, ciphertext: user.encryptedPrivateKey,
iv: user.iv, iv: user.iv,
tag: user.tag, tag: user.tag,
key key: Buffer.from(key, "hex")
}); });
return privateKey; return privateKey;
}; };
export const buildUserProjectKey = async (privateKey: string, publickey: string) => { export const buildUserProjectKey = (privateKey: string, publickey: string) => {
const randomBytes = crypto.randomBytes(16).toString("hex"); const randomBytes = crypto.randomBytes(16).toString("hex");
const { nonce, ciphertext } = encryptAsymmetric(randomBytes, publickey, privateKey); const { nonce, ciphertext } = encryptAsymmetric(randomBytes, publickey, privateKey);
return { nonce, ciphertext }; return { nonce, ciphertext };
}; };
// export const getUserProjectKey = async (privateKey: string) => { export const getUserProjectKey = async (privateKey: string, ciphertext: string, nonce: string, publicKey: string) => {
// const key = decryptAsymmetric({ return decryptAsymmetric({
// ciphertext: decryptFileKey.encryptedKey, ciphertext,
// nonce: decryptFileKey.nonce, nonce,
// publicKey: decryptFileKey.sender.publicKey, publicKey,
// privateKey: PRIVATE_KEY privateKey
// }); });
// }; };
export const encryptSecret = (encKey: string, key: string, value?: string, comment?: string) => {
// encrypt key
const {
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag
} = encryptSymmetric128BitHexKeyUTF8(key, encKey);
// encrypt value
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag
} = encryptSymmetric128BitHexKeyUTF8(value ?? "", encKey);
// encrypt comment
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag
} = encryptSymmetric128BitHexKeyUTF8(comment ?? "", encKey);
return {
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
};
};
export const decryptSecret = (decryptKey: string, encSecret: TSecrets) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
key: decryptKey,
ciphertext: encSecret.secretKeyCiphertext,
tag: encSecret.secretKeyTag,
iv: encSecret.secretKeyIV
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
key: decryptKey,
ciphertext: encSecret.secretValueCiphertext,
tag: encSecret.secretValueTag,
iv: encSecret.secretValueIV
});
const secretComment =
encSecret.secretCommentIV && encSecret.secretCommentTag && encSecret.secretCommentCiphertext
? decryptSymmetric128BitHexKeyUTF8({
key: decryptKey,
ciphertext: encSecret.secretCommentCiphertext,
tag: encSecret.secretCommentTag,
iv: encSecret.secretCommentIV
})
: "";
return {
key: secretKey,
value: secretValue,
comment: secretComment,
version: encSecret.version
};
};

View File

@@ -14,7 +14,8 @@ export async function seed(knex: Knex): Promise<void> {
const [user] = await knex(TableName.Users) const [user] = await knex(TableName.Users)
.insert([ .insert([
{ {
// @ts-expect-error exluded type id needs to be inserted here to keep it testable // eslint-disable-next-line
// @ts-ignore
id: seedData1.id, id: seedData1.id,
email: seedData1.email, email: seedData1.email,
superAdmin: true, superAdmin: true,
@@ -48,7 +49,8 @@ export async function seed(knex: Knex): Promise<void> {
]); ]);
await knex(TableName.AuthTokenSession).insert({ await knex(TableName.AuthTokenSession).insert({
// @ts-expect-error exluded type id needs to be inserted here to keep it testable // eslint-disable-next-line
// @ts-ignore
id: seedData1.token.id, id: seedData1.token.id,
userId: seedData1.id, userId: seedData1.id,
ip: "151.196.220.213", ip: "151.196.220.213",

View File

@@ -14,7 +14,8 @@ export async function seed(knex: Knex): Promise<void> {
const [org] = await knex(TableName.Organization) const [org] = await knex(TableName.Organization)
.insert([ .insert([
{ {
// @ts-expect-error exluded type id needs to be inserted here to keep it testable // eslint-disable-next-line
// @ts-ignore
id: seedData1.organization.id, id: seedData1.organization.id,
name: "infisical", name: "infisical",
slug: "infisical", slug: "infisical",

View File

@@ -1,7 +1,11 @@
import crypto from "node:crypto";
import { Knex } from "knex"; import { Knex } from "knex";
import { OrgMembershipRole, TableName } from "../schemas"; import { encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { seedData1 } from "../seed-data";
import { OrgMembershipRole, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
import { buildUserProjectKey, getUserPrivateKey, seedData1 } from "../seed-data";
export const DEFAULT_PROJECT_ENVS = [ export const DEFAULT_PROJECT_ENVS = [
{ name: "Development", slug: "dev" }, { name: "Development", slug: "dev" },
@@ -20,21 +24,32 @@ export async function seed(knex: Knex): Promise<void> {
name: seedData1.project.name, name: seedData1.project.name,
orgId: seedData1.organization.id, orgId: seedData1.organization.id,
slug: "first-project", slug: "first-project",
// @ts-expect-error exluded type id needs to be inserted here to keep it testable // eslint-disable-next-line
// @ts-ignore
id: seedData1.project.id id: seedData1.project.id
}) })
.returning("*"); .returning("*");
// await knex(TableName.ProjectKeys).insert({
// projectId: project.id,
// senderId: seedData1.id
// });
await knex(TableName.ProjectMembership).insert({ await knex(TableName.ProjectMembership).insert({
projectId: project.id, projectId: project.id,
role: OrgMembershipRole.Admin, role: OrgMembershipRole.Admin,
userId: seedData1.id userId: seedData1.id
}); });
const user = await knex(TableName.UserEncryptionKey).where({ userId: seedData1.id }).first();
if (!user) throw new Error("User not found");
const userPrivateKey = await getUserPrivateKey(seedData1.password, user);
const projectKey = buildUserProjectKey(userPrivateKey, user.publicKey);
await knex(TableName.ProjectKeys).insert({
projectId: project.id,
nonce: projectKey.nonce,
encryptedKey: projectKey.ciphertext,
receiverId: seedData1.id,
senderId: seedData1.id
});
// create default environments and default folders
const envs = await knex(TableName.Environment) const envs = await knex(TableName.Environment)
.insert( .insert(
DEFAULT_PROJECT_ENVS.map(({ name, slug }, index) => ({ DEFAULT_PROJECT_ENVS.map(({ name, slug }, index) => ({
@@ -46,4 +61,19 @@ export async function seed(knex: Knex): Promise<void> {
) )
.returning("*"); .returning("*");
await knex(TableName.SecretFolder).insert(envs.map(({ id }) => ({ name: "root", envId: id, parentId: null }))); await knex(TableName.SecretFolder).insert(envs.map(({ id }) => ({ name: "root", envId: id, parentId: null })));
// save secret secret blind index
const encKey = process.env.ENCRYPTION_KEY;
if (!encKey) throw new Error("Missing ENCRYPTION_KEY");
const salt = crypto.randomBytes(16).toString("base64");
const secretBlindIndex = encryptSymmetric128BitHexKeyUTF8(salt, encKey);
// insert secret blind index for project
await knex(TableName.SecretBlindIndex).insert({
projectId: project.id,
encryptedSaltCipherText: secretBlindIndex.ciphertext,
saltIV: secretBlindIndex.iv,
saltTag: secretBlindIndex.tag,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.UTF8
});
} }

View File

@@ -0,0 +1,83 @@
import bcrypt from "bcrypt";
import { Knex } from "knex";
import { IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
import { seedData1 } from "../seed-data";
export async function seed(knex: Knex): Promise<void> {
// Deletes ALL existing entries
await knex(TableName.Identity).del();
await knex(TableName.IdentityOrgMembership).del();
// Inserts seed entries
await knex(TableName.Identity).insert([
{
// eslint-disable-next-line
// @ts-ignore
id: seedData1.machineIdentity.id,
name: seedData1.machineIdentity.name,
authMethod: IdentityAuthMethod.Univeral
}
]);
const identityUa = await knex(TableName.IdentityUniversalAuth)
.insert([
{
identityId: seedData1.machineIdentity.id,
clientId: seedData1.machineIdentity.clientCredentials.id,
clientSecretTrustedIps: JSON.stringify([
{
type: "ipv4",
prefix: 0,
ipAddress: "0.0.0.0"
},
{
type: "ipv6",
prefix: 0,
ipAddress: "::"
}
]),
accessTokenTrustedIps: JSON.stringify([
{
type: "ipv4",
prefix: 0,
ipAddress: "0.0.0.0"
},
{
type: "ipv6",
prefix: 0,
ipAddress: "::"
}
]),
accessTokenTTL: 2592000,
accessTokenMaxTTL: 2592000,
accessTokenNumUsesLimit: 0
}
])
.returning("*");
const clientSecretHash = await bcrypt.hash(seedData1.machineIdentity.clientCredentials.secret, 10);
await knex(TableName.IdentityUaClientSecret).insert([
{
identityUAId: identityUa[0].id,
description: "",
clientSecretTTL: 0,
clientSecretNumUses: 0,
clientSecretNumUsesLimit: 0,
clientSecretPrefix: seedData1.machineIdentity.clientCredentials.secret.slice(0, 4),
clientSecretHash,
isClientSecretRevoked: false
}
]);
await knex(TableName.IdentityOrgMembership).insert([
{
identityId: seedData1.machineIdentity.id,
orgId: seedData1.organization.id,
role: OrgMembershipRole.Admin
}
]);
await knex(TableName.IdentityProjectMembership).insert({
identityId: seedData1.machineIdentity.id,
role: ProjectMembershipRole.Admin,
projectId: seedData1.project.id
});
}

View File

@@ -22,6 +22,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgPlansTableByBillCycle({ const data = await server.services.license.getOrgPlansTableByBillCycle({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
billingCycle: req.query.billingCycle billingCycle: req.query.billingCycle
}); });
@@ -43,6 +44,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const plan = await server.services.license.getOrgPlan({ const plan = await server.services.license.getOrgPlan({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return { plan }; return { plan };
@@ -85,6 +87,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.startOrgTrial({ const data = await server.services.license.startOrgTrial({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
success_url: req.body.success_url success_url: req.body.success_url
}); });
@@ -106,6 +109,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.createOrganizationPortalSession({ const data = await server.services.license.createOrganizationPortalSession({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -126,6 +130,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgBillingInfo({ const data = await server.services.license.getOrgBillingInfo({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -146,6 +151,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgPlanTable({ const data = await server.services.license.getOrgPlanTable({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -166,6 +172,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgBillingDetails({ const data = await server.services.license.getOrgBillingDetails({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -190,6 +197,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.updateOrgBillingDetails({ const data = await server.services.license.updateOrgBillingDetails({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
name: req.body.name, name: req.body.name,
email: req.body.email email: req.body.email
@@ -212,6 +220,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgPmtMethods({ const data = await server.services.license.getOrgPmtMethods({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -236,6 +245,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.addOrgPmtMethods({ const data = await server.services.license.addOrgPmtMethods({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
success_url: req.body.success_url, success_url: req.body.success_url,
cancel_url: req.body.cancel_url cancel_url: req.body.cancel_url
@@ -261,6 +271,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.delOrgPmtMethods({ const data = await server.services.license.delOrgPmtMethods({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
pmtMethodId: req.params.pmtMethodId pmtMethodId: req.params.pmtMethodId
}); });
@@ -284,6 +295,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgTaxIds({ const data = await server.services.license.getOrgTaxIds({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -310,6 +322,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.addOrgTaxId({ const data = await server.services.license.addOrgTaxId({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
type: req.body.type, type: req.body.type,
value: req.body.value value: req.body.value
@@ -335,6 +348,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.delOrgTaxId({ const data = await server.services.license.delOrgTaxId({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
taxId: req.params.taxId taxId: req.params.taxId
}); });
@@ -358,6 +372,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgTaxInvoices({ const data = await server.services.license.getOrgTaxInvoices({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;
@@ -380,6 +395,7 @@ export const registerLicenseRouter = async (server: FastifyZodProvider) => {
const data = await server.services.license.getOrgLicenses({ const data = await server.services.license.getOrgLicenses({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return data; return data;

View File

@@ -26,7 +26,12 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const role = await server.services.orgRole.createRole(req.permission.id, req.params.organizationId, req.body); const role = await server.services.orgRole.createRole(
req.permission.id,
req.params.organizationId,
req.body,
req.permission.orgId
);
return { role }; return { role };
} }
}); });
@@ -57,7 +62,8 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id, req.permission.id,
req.params.organizationId, req.params.organizationId,
req.params.roleId, req.params.roleId,
req.body req.body,
req.permission.orgId
); );
return { role }; return { role };
} }
@@ -82,7 +88,8 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
const role = await server.services.orgRole.deleteRole( const role = await server.services.orgRole.deleteRole(
req.permission.id, req.permission.id,
req.params.organizationId, req.params.organizationId,
req.params.roleId req.params.roleId,
req.permission.orgId
); );
return { role }; return { role };
} }
@@ -107,7 +114,11 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const roles = await server.services.orgRole.listRoles(req.permission.id, req.params.organizationId); const roles = await server.services.orgRole.listRoles(
req.permission.id,
req.params.organizationId,
req.permission.orgId
);
return { data: { roles } }; return { data: { roles } };
} }
}); });
@@ -130,7 +141,8 @@ export const registerOrgRoleRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const { permissions, membership } = await server.services.orgRole.getUserPermission( const { permissions, membership } = await server.services.orgRole.getUserPermission(
req.permission.id, req.permission.id,
req.params.organizationId req.params.organizationId,
req.permission.orgId
); );
return { permissions, membership }; return { permissions, membership };
} }

View File

@@ -30,7 +30,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.type, req.permission.type,
req.permission.id, req.permission.id,
req.params.projectId, req.params.projectId,
req.body req.body,
req.permission.orgId
); );
return { role }; return { role };
} }
@@ -63,7 +64,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.id, req.permission.id,
req.params.projectId, req.params.projectId,
req.params.roleId, req.params.roleId,
req.body req.body,
req.permission.orgId
); );
return { role }; return { role };
} }
@@ -89,7 +91,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
req.permission.type, req.permission.type,
req.permission.id, req.permission.id,
req.params.projectId, req.params.projectId,
req.params.roleId req.params.roleId,
req.permission.orgId
); );
return { role }; return { role };
} }
@@ -117,7 +120,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
const roles = await server.services.projectRole.listRoles( const roles = await server.services.projectRole.listRoles(
req.permission.type, req.permission.type,
req.permission.id, req.permission.id,
req.params.projectId req.params.projectId,
req.permission.orgId
); );
return { data: { roles } }; return { data: { roles } };
} }
@@ -143,7 +147,8 @@ export const registerProjectRoleRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const { permissions, membership } = await server.services.projectRole.getUserPermission( const { permissions, membership } = await server.services.projectRole.getUserPermission(
req.permission.id, req.permission.id,
req.params.projectId req.params.projectId,
req.permission.orgId
); );
return { data: { permissions, membership } }; return { data: { permissions, membership } };
} }

View File

@@ -11,6 +11,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:workspaceId/secret-snapshots", url: "/:workspaceId/secret-snapshots",
schema: { schema: {
description: "Return project secret snapshots ids",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -31,6 +38,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const secretSnapshots = await server.services.snapshot.listSnapshots({ const secretSnapshots = await server.services.snapshot.listSnapshots({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
...req.query ...req.query
}); });
@@ -60,6 +68,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const count = await server.services.snapshot.projectSecretSnapshotCount({ const count = await server.services.snapshot.projectSecretSnapshotCount({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
environment: req.query.environment, environment: req.query.environment,
path: req.query.path path: req.query.path
@@ -72,6 +81,13 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
url: "/:workspaceId/audit-logs", url: "/:workspaceId/audit-logs",
schema: { schema: {
description: "Return audit logs",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -112,6 +128,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const auditLogs = await server.services.auditLog.listProjectAuditLogs({ const auditLogs = await server.services.auditLog.listProjectAuditLogs({
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
...req.query, ...req.query,
auditLogActor: req.query.actor, auditLogActor: req.query.actor,

View File

@@ -13,13 +13,12 @@ import { FastifyRequest } from "fastify";
import { z } from "zod"; import { z } from "zod";
import { SamlConfigsSchema } from "@app/db/schemas"; import { SamlConfigsSchema } from "@app/db/schemas";
import { SamlProviders } from "@app/ee/services/saml-config/saml-config-types"; import { SamlProviders, TGetSamlCfgDTO } from "@app/ee/services/saml-config/saml-config-types";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger"; import { logger } from "@app/lib/logger";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
import { AuthMode } from "@app/services/auth/auth-type"; import { AuthMode } from "@app/services/auth/auth-type";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
type TSAMLConfig = { type TSAMLConfig = {
callbackUrl: string; callbackUrl: string;
@@ -28,6 +27,7 @@ type TSAMLConfig = {
cert: string; cert: string;
audience: string; audience: string;
wantAuthnResponseSigned?: boolean; wantAuthnResponseSigned?: boolean;
disableRequestedAuthnContext?: boolean;
}; };
export const registerSamlRouter = async (server: FastifyZodProvider) => { export const registerSamlRouter = async (server: FastifyZodProvider) => {
@@ -44,17 +44,30 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line // eslint-disable-next-line
getSamlOptions: async (req, done) => { getSamlOptions: async (req, done) => {
try { try {
const { ssoIdentifier } = req.params; const { samlConfigId, orgSlug } = req.params;
if (!ssoIdentifier) throw new BadRequestError({ message: "Missing sso identitier" });
const ssoConfig = await server.services.saml.getSaml({ let ssoLookupDetails: TGetSamlCfgDTO;
type: "ssoId",
id: ssoIdentifier if (orgSlug) {
}); ssoLookupDetails = {
if (!ssoConfig) throw new BadRequestError({ message: "SSO config not found" }); type: "orgSlug",
orgSlug
};
} else if (samlConfigId) {
ssoLookupDetails = {
type: "ssoId",
id: samlConfigId
};
} else {
throw new BadRequestError({ message: "Missing sso identitier or org slug" });
}
const ssoConfig = await server.services.saml.getSaml(ssoLookupDetails);
if (!ssoConfig || !ssoConfig.isActive)
throw new BadRequestError({ message: "Failed to authenticate with SAML SSO" });
const samlConfig: TSAMLConfig = { const samlConfig: TSAMLConfig = {
callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoIdentifier}`, callbackUrl: `${appCfg.SITE_URL}/api/v1/sso/saml2/${ssoConfig.id}`,
entryPoint: ssoConfig.entryPoint, entryPoint: ssoConfig.entryPoint,
issuer: ssoConfig.issuer, issuer: ssoConfig.issuer,
cert: ssoConfig.cert, cert: ssoConfig.cert,
@@ -64,7 +77,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
samlConfig.wantAuthnResponseSigned = false; samlConfig.wantAuthnResponseSigned = false;
} }
if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) { if (ssoConfig.authProvider === SamlProviders.AZURE_SAML) {
if (req.body.RelayState && JSON.parse(req.body.RelayState).spIntiaited) { samlConfig.disableRequestedAuthnContext = true;
if (req.body?.RelayState && JSON.parse(req.body.RelayState).spInitiated) {
samlConfig.audience = `spn:${ssoConfig.issuer}`; samlConfig.audience = `spn:${ssoConfig.issuer}`;
} }
} }
@@ -79,7 +93,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
// eslint-disable-next-line // eslint-disable-next-line
async (req, profile, cb) => { async (req, profile, cb) => {
try { try {
const serverCfg = getServerCfg();
if (!profile) throw new BadRequestError({ message: "Missing profile" }); if (!profile) throw new BadRequestError({ message: "Missing profile" });
const { firstName } = profile; const { firstName } = profile;
const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved const email = profile?.email ?? (profile?.emailAddress as string); // emailRippling is added because in Rippling the field `email` reserved
@@ -92,7 +105,6 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
email, email,
firstName: profile.firstName as string, firstName: profile.firstName as string,
lastName: profile.lastName as string, lastName: profile.lastName as string,
isSignupAllowed: Boolean(serverCfg.allowSignUp),
relayState: (req.body as { RelayState?: string }).RelayState, relayState: (req.body as { RelayState?: string }).RelayState,
authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string, authProvider: (req as unknown as FastifyRequest).ssoConfig?.authProvider as string,
orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string orgId: (req as unknown as FastifyRequest).ssoConfig?.orgId as string
@@ -108,11 +120,11 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
); );
server.route({ server.route({
url: "/redirect/saml2/:ssoIdentifier", url: "/redirect/saml2/organizations/:orgSlug",
method: "GET", method: "GET",
schema: { schema: {
params: z.object({ params: z.object({
ssoIdentifier: z.string().trim() orgSlug: z.string().trim()
}), }),
querystring: z.object({ querystring: z.object({
callback_port: z.string().optional() callback_port: z.string().optional()
@@ -134,11 +146,37 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
}); });
server.route({ server.route({
url: "/saml2/:ssoIdentifier", url: "/redirect/saml2/:samlConfigId",
method: "GET",
schema: {
params: z.object({
samlConfigId: z.string().trim()
}),
querystring: z.object({
callback_port: z.string().optional()
})
},
preValidation: (req, res) =>
(
passport.authenticate("saml", {
failureRedirect: "/",
additionalParams: {
RelayState: JSON.stringify({
spInitiated: true,
callbackPort: req.query.callback_port ?? ""
})
}
} as any) as any
)(req, res),
handler: () => {}
});
server.route({
url: "/saml2/:samlConfigId",
method: "POST", method: "POST",
schema: { schema: {
params: z.object({ params: z.object({
ssoIdentifier: z.string().trim() samlConfigId: z.string().trim()
}) })
}, },
preValidation: passport.authenticate("saml", { preValidation: passport.authenticate("saml", {
@@ -177,7 +215,8 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
isActive: z.boolean(), isActive: z.boolean(),
entryPoint: z.string(), entryPoint: z.string(),
issuer: z.string(), issuer: z.string(),
cert: z.string() cert: z.string(),
lastUsed: z.date().nullable().optional()
}) })
.optional() .optional()
} }
@@ -186,6 +225,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const saml = await server.services.saml.getSaml({ const saml = await server.services.saml.getSaml({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.query.organizationId, orgId: req.query.organizationId,
type: "org" type: "org"
}); });
@@ -214,6 +254,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const saml = await server.services.saml.createSamlCfg({ const saml = await server.services.saml.createSamlCfg({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId, orgId: req.body.organizationId,
...req.body ...req.body
}); });
@@ -244,6 +285,7 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
const saml = await server.services.saml.updateSamlCfg({ const saml = await server.services.saml.updateSamlCfg({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId, orgId: req.body.organizationId,
...req.body ...req.body
}); });

View File

@@ -34,6 +34,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({ const approval = await server.services.secretApprovalPolicy.createSecretApprovalPolicy({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body, ...req.body,
name: req.body.name ?? `${req.body.environment}-${nanoid(3)}` name: req.body.name ?? `${req.body.environment}-${nanoid(3)}`
@@ -71,6 +72,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({ const approval = await server.services.secretApprovalPolicy.updateSecretApprovalPolicy({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
secretPolicyId: req.params.sapId secretPolicyId: req.params.sapId
}); });
@@ -96,6 +98,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({ const approval = await server.services.secretApprovalPolicy.deleteSecretApprovalPolicy({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
secretPolicyId: req.params.sapId secretPolicyId: req.params.sapId
}); });
return { approval }; return { approval };
@@ -120,6 +123,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({ const approvals = await server.services.secretApprovalPolicy.getSecretApprovalPolicyByProjectId({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });
return { approvals }; return { approvals };
@@ -146,6 +150,7 @@ export const registerSecretApprovalPolicyRouter = async (server: FastifyZodProvi
const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({ const policy = await server.services.secretApprovalPolicy.getSecretApprovalPolicyOfFolder({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId, projectId: req.query.workspaceId,
...req.query ...req.query
}); });

View File

@@ -52,6 +52,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approvals = await server.services.secretApprovalRequest.getSecretApprovals({ const approvals = await server.services.secretApprovalRequest.getSecretApprovals({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });
@@ -80,6 +81,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approvals = await server.services.secretApprovalRequest.requestCount({ const approvals = await server.services.secretApprovalRequest.requestCount({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });
return { approvals }; return { approvals };
@@ -104,6 +106,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({ const { approval } = await server.services.secretApprovalRequest.mergeSecretApprovalRequest({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
approvalId: req.params.id approvalId: req.params.id
}); });
return { approval }; return { approval };
@@ -131,6 +134,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const review = await server.services.secretApprovalRequest.reviewApproval({ const review = await server.services.secretApprovalRequest.reviewApproval({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
approvalId: req.params.id, approvalId: req.params.id,
status: req.body.status status: req.body.status
}); });
@@ -159,6 +163,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approval = await server.services.secretApprovalRequest.updateApprovalStatus({ const approval = await server.services.secretApprovalRequest.updateApprovalStatus({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
approvalId: req.params.id, approvalId: req.params.id,
status: req.body.status status: req.body.status
}); });
@@ -266,6 +271,7 @@ export const registerSecretApprovalRequestRouter = async (server: FastifyZodProv
const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({ const approval = await server.services.secretApprovalRequest.getSecretApprovalDetails({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.id id: req.params.id
}); });
return { approval }; return { approval };

View File

@@ -30,6 +30,7 @@ export const registerSecretRotationProviderRouter = async (server: FastifyZodPro
const providers = await server.services.secretRotation.getProviderTemplates({ const providers = await server.services.secretRotation.getProviderTemplates({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return providers; return providers;

View File

@@ -40,6 +40,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotation = await server.services.secretRotation.createRotation({ const secretRotation = await server.services.secretRotation.createRotation({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId projectId: req.body.workspaceId
}); });
@@ -73,6 +74,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotation = await server.services.secretRotation.restartById({ const secretRotation = await server.services.secretRotation.restartById({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
rotationId: req.body.id rotationId: req.body.id
}); });
return { secretRotation }; return { secretRotation };
@@ -123,6 +125,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotations = await server.services.secretRotation.getByProjectId({ const secretRotations = await server.services.secretRotation.getByProjectId({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });
return { secretRotations }; return { secretRotations };
@@ -155,6 +158,7 @@ export const registerSecretRotationRouter = async (server: FastifyZodProvider) =
const secretRotation = await server.services.secretRotation.deleteById({ const secretRotation = await server.services.secretRotation.deleteById({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
rotationId: req.params.id rotationId: req.params.id
}); });
return { secretRotation }; return { secretRotation };

View File

@@ -22,6 +22,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const session = await server.services.secretScanning.createInstallationSession({ const session = await server.services.secretScanning.createInstallationSession({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.body.organizationId orgId: req.body.organizationId
}); });
return session; return session;
@@ -45,6 +46,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { installatedApp } = await server.services.secretScanning.linkInstallationToOrg({ const { installatedApp } = await server.services.secretScanning.linkInstallationToOrg({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body ...req.body
}); });
return installatedApp; return installatedApp;
@@ -65,6 +67,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({ const appInstallationCompleted = await server.services.secretScanning.getOrgInstallationStatus({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return { appInstallationCompleted }; return { appInstallationCompleted };
@@ -85,6 +88,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { risks } = await server.services.secretScanning.getRisksByOrg({ const { risks } = await server.services.secretScanning.getRisksByOrg({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId orgId: req.params.organizationId
}); });
return { risks }; return { risks };
@@ -106,6 +110,7 @@ export const registerSecretScanningRouter = async (server: FastifyZodProvider) =
const { risk } = await server.services.secretScanning.updateRiskStatus({ const { risk } = await server.services.secretScanning.updateRiskStatus({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.params.organizationId, orgId: req.params.organizationId,
riskId: req.params.riskId, riskId: req.params.riskId,
...req.body ...req.body

View File

@@ -27,6 +27,7 @@ export const registerSecretVersionRouter = async (server: FastifyZodProvider) =>
const secretVersions = await server.services.secret.getSecretVersions({ const secretVersions = await server.services.secret.getSecretVersions({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
limit: req.query.limit, limit: req.query.limit,
offset: req.query.offset, offset: req.query.offset,
secretId: req.params.secretId secretId: req.params.secretId

View File

@@ -46,6 +46,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
const secretSnapshot = await server.services.snapshot.getSnapshotData({ const secretSnapshot = await server.services.snapshot.getSnapshotData({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.secretSnapshotId id: req.params.secretSnapshotId
}); });
return { secretSnapshot }; return { secretSnapshot };
@@ -56,6 +57,13 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
url: "/:secretSnapshotId/rollback", url: "/:secretSnapshotId/rollback",
schema: { schema: {
description: "Roll back project secrets to those captured in a secret snapshot version.",
security: [
{
apiKeyAuth: [],
bearerAuth: []
}
],
params: z.object({ params: z.object({
secretSnapshotId: z.string().trim() secretSnapshotId: z.string().trim()
}), }),
@@ -70,6 +78,7 @@ export const registerSnapshotRouter = async (server: FastifyZodProvider) => {
const secretSnapshot = await server.services.snapshot.rollbackSnapshot({ const secretSnapshot = await server.services.snapshot.rollbackSnapshot({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.secretSnapshotId id: req.params.secretSnapshotId
}); });
return { secretSnapshot }; return { secretSnapshot };

View File

@@ -24,7 +24,8 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
const trustedIps = await server.services.trustedIp.listIpsByProjectId({ const trustedIps = await server.services.trustedIp.listIpsByProjectId({
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id actorId: req.permission.id,
actorOrgId: req.permission.orgId
}); });
return { trustedIps }; return { trustedIps };
} }
@@ -54,6 +55,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body ...req.body
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -97,6 +99,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
trustedIpId: req.params.trustedIpId, trustedIpId: req.params.trustedIpId,
...req.body ...req.body
}); });
@@ -137,6 +140,7 @@ export const registerTrustedIpRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
trustedIpId: req.params.trustedIpId trustedIpId: req.params.trustedIpId
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({

View File

@@ -38,7 +38,8 @@ export const auditLogDALFactory = (db: TDbClient) => {
}) })
) )
.limit(limit) .limit(limit)
.offset(offset); .offset(offset)
.orderBy("createdAt", "desc");
if (startDate) { if (startDate) {
void sqlQuery.where("createdAt", ">=", startDate); void sqlQuery.where("createdAt", ">=", startDate);
} }

View File

@@ -30,10 +30,11 @@ export const auditLogServiceFactory = ({
startDate, startDate,
actor, actor,
actorId, actorId,
actorOrgId,
projectId, projectId,
auditLogActor auditLogActor
}: TListProjectAuditLogDTO) => { }: TListProjectAuditLogDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.AuditLogs);
const auditLogs = await auditLogDAL.find({ const auditLogs = await auditLogDAL.find({
startDate, startDate,

View File

@@ -0,0 +1,27 @@
export const getDefaultOnPremFeatures = () => {
return {
_id: null,
slug: null,
tier: -1,
workspaceLimit: null,
workspacesUsed: 0,
memberLimit: null,
membersUsed: 0,
environmentLimit: null,
environmentsUsed: 0,
secretVersioning: true,
pitRecovery: false,
ipAllowlisting: true,
rbac: false,
customRateLimits: false,
customAlerts: false,
auditLogs: false,
auditLogsRetentionDays: 0,
samlSSO: false,
status: null,
trial_end: null,
has_used_trial: true,
secretApproval: false,
secretRotation: true
};
};

View File

@@ -44,7 +44,7 @@ type TLicenseServiceFactoryDep = {
export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>; export type TLicenseServiceFactory = ReturnType<typeof licenseServiceFactory>;
const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login"; const LICENSE_SERVER_CLOUD_LOGIN = "/api/auth/v1/license-server-login";
const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/licence-login"; const LICENSE_SERVER_ON_PREM_LOGIN = "/api/auth/v1/license-login";
const FEATURE_CACHE_KEY = (orgId: string, projectId?: string) => `${orgId}-${projectId || ""}`; const FEATURE_CACHE_KEY = (orgId: string, projectId?: string) => `${orgId}-${projectId || ""}`;
export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }: TLicenseServiceFactoryDep) => { export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }: TLicenseServiceFactoryDep) => {
@@ -92,7 +92,7 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
// else it would reach catch statement // else it would reach catch statement
isValidLicense = true; isValidLicense = true;
} catch (error) { } catch (error) {
logger.error(`init-license: encountered an error when init license [error]`, error); logger.error(error, `init-license: encountered an error when init license`);
} }
}; };
@@ -175,8 +175,14 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
}; };
// below all are api calls // below all are api calls
const getOrgPlansTableByBillCycle = async ({ orgId, actor, actorId, billingCycle }: TOrgPlansTableDTO) => { const getOrgPlansTableByBillCycle = async ({
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); orgId,
actor,
actorId,
actorOrgId,
billingCycle
}: TOrgPlansTableDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const { data } = await licenseServerCloudApi.request.get( const { data } = await licenseServerCloudApi.request.get(
`/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}` `/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
@@ -184,15 +190,15 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const getOrgPlan = async ({ orgId, actor, actorId, projectId }: TOrgPlanDTO) => { const getOrgPlan = async ({ orgId, actor, actorId, actorOrgId, projectId }: TOrgPlanDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const plan = await getPlan(orgId, projectId); const plan = await getPlan(orgId, projectId);
return plan; return plan;
}; };
const startOrgTrial = async ({ orgId, actorId, actor, success_url }: TStartOrgTrialDTO) => { const startOrgTrial = async ({ orgId, actorId, actor, actorOrgId, success_url }: TStartOrgTrialDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
@@ -213,8 +219,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return { url }; return { url };
}; };
const createOrganizationPortalSession = async ({ orgId, actorId, actor }: TCreateOrgPortalSession) => { const createOrganizationPortalSession = async ({ orgId, actorId, actor, actorOrgId }: TCreateOrgPortalSession) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Billing);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Billing);
@@ -260,8 +266,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return { url }; return { url };
}; };
const getOrgBillingInfo = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const getOrgBillingInfo = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -277,8 +283,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
}; };
// returns org current plan feature table // returns org current plan feature table
const getOrgPlanTable = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const getOrgPlanTable = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -293,8 +299,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const getOrgBillingDetails = async ({ orgId, actor, actorId }: TGetOrgBillInfoDTO) => { const getOrgBillingDetails = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgBillInfoDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -310,8 +316,15 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const updateOrgBillingDetails = async ({ actorId, actor, orgId, name, email }: TUpdateOrgBillingDetailsDTO) => { const updateOrgBillingDetails = async ({
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); actorId,
actor,
actorOrgId,
orgId,
name,
email
}: TUpdateOrgBillingDetailsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -330,8 +343,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const getOrgPmtMethods = async ({ orgId, actor, actorId }: TOrgPmtMethodsDTO) => { const getOrgPmtMethods = async ({ orgId, actor, actorId, actorOrgId }: TOrgPmtMethodsDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -349,8 +362,15 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return pmtMethods; return pmtMethods;
}; };
const addOrgPmtMethods = async ({ orgId, actor, actorId, success_url, cancel_url }: TAddOrgPmtMethodDTO) => { const addOrgPmtMethods = async ({
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); orgId,
actor,
actorId,
actorOrgId,
success_url,
cancel_url
}: TAddOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -371,8 +391,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return { url }; return { url };
}; };
const delOrgPmtMethods = async ({ actorId, actor, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => { const delOrgPmtMethods = async ({ actorId, actor, actorOrgId, orgId, pmtMethodId }: TDelOrgPmtMethodDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -388,8 +408,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const getOrgTaxIds = async ({ orgId, actor, actorId }: TGetOrgTaxIdDTO) => { const getOrgTaxIds = async ({ orgId, actor, actorId, actorOrgId }: TGetOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -406,8 +426,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return taxIds; return taxIds;
}; };
const addOrgTaxId = async ({ actorId, actor, orgId, type, value }: TAddOrgTaxIdDTO) => { const addOrgTaxId = async ({ actorId, actor, actorOrgId, orgId, type, value }: TAddOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -427,8 +447,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const delOrgTaxId = async ({ orgId, actor, actorId, taxId }: TDelOrgTaxIdDTO) => { const delOrgTaxId = async ({ orgId, actor, actorId, actorOrgId, taxId }: TDelOrgTaxIdDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -444,8 +464,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return data; return data;
}; };
const getOrgTaxInvoices = async ({ actorId, actor, orgId }: TOrgInvoiceDTO) => { const getOrgTaxInvoices = async ({ actorId, actor, actorOrgId, orgId }: TOrgInvoiceDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
@@ -461,8 +481,8 @@ export const licenseServiceFactory = ({ orgDAL, permissionService, licenseDAL }:
return invoices; return invoices;
}; };
const getOrgLicenses = async ({ orgId, actor, actorId }: TOrgLicensesDTO) => { const getOrgLicenses = async ({ orgId, actor, actorId, actorOrgId }: TOrgLicensesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Billing);
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);

View File

@@ -10,8 +10,10 @@ export const permissionDALFactory = (db: TDbClient) => {
try { try {
const membership = await db(TableName.OrgMembership) const membership = await db(TableName.OrgMembership)
.leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
.join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`)
.where("userId", userId) .where("userId", userId)
.where(`${TableName.OrgMembership}.orgId`, orgId) .where(`${TableName.OrgMembership}.orgId`, orgId)
.select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"))
.select("permissions") .select("permissions")
.select(selectAllTableCols(TableName.OrgMembership)) .select(selectAllTableCols(TableName.OrgMembership))
.first(); .first();
@@ -26,9 +28,11 @@ export const permissionDALFactory = (db: TDbClient) => {
try { try {
const membership = await db(TableName.IdentityOrgMembership) const membership = await db(TableName.IdentityOrgMembership)
.leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`) .leftJoin(TableName.OrgRoles, `${TableName.IdentityOrgMembership}.roleId`, `${TableName.OrgRoles}.id`)
.join(TableName.Organization, `${TableName.IdentityOrgMembership}.orgId`, `${TableName.Organization}.id`)
.where("identityId", identityId) .where("identityId", identityId)
.where(`${TableName.IdentityOrgMembership}.orgId`, orgId) .where(`${TableName.IdentityOrgMembership}.orgId`, orgId)
.select(selectAllTableCols(TableName.IdentityOrgMembership)) .select(selectAllTableCols(TableName.IdentityOrgMembership))
.select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"))
.select("permissions") .select("permissions")
.first(); .first();
return membership; return membership;
@@ -41,9 +45,15 @@ export const permissionDALFactory = (db: TDbClient) => {
try { try {
const membership = await db(TableName.ProjectMembership) const membership = await db(TableName.ProjectMembership)
.leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`) .leftJoin(TableName.ProjectRoles, `${TableName.ProjectMembership}.roleId`, `${TableName.ProjectRoles}.id`)
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`)
.where("userId", userId) .where("userId", userId)
.where(`${TableName.ProjectMembership}.projectId`, projectId) .where(`${TableName.ProjectMembership}.projectId`, projectId)
.select(selectAllTableCols(TableName.ProjectMembership)) .select(selectAllTableCols(TableName.ProjectMembership))
.select(
db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"),
db.ref("orgId").withSchema(TableName.Project)
)
.select("permissions") .select("permissions")
.first(); .first();

View File

@@ -94,12 +94,15 @@ export const permissionServiceFactory = ({
/* /*
* Get user permission in an organization * Get user permission in an organization
* */ * */
const getUserOrgPermission = async (userId: string, orgId: string) => { const getUserOrgPermission = async (userId: string, orgId: string, userOrgId?: string) => {
const membership = await permissionDAL.getOrgPermission(userId, orgId); const membership = await permissionDAL.getOrgPermission(userId, orgId);
if (!membership) throw new UnauthorizedError({ name: "User not in org" }); if (!membership) throw new UnauthorizedError({ name: "User not in org" });
if (membership.role === OrgMembershipRole.Custom && !membership.permissions) { if (membership.role === OrgMembershipRole.Custom && !membership.permissions) {
throw new BadRequestError({ name: "Custom permission not found" }); throw new BadRequestError({ name: "Custom permission not found" });
} }
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
}
return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
}; };
@@ -112,10 +115,10 @@ export const permissionServiceFactory = ({
return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; return { permission: buildOrgPermission(membership.role, membership.permissions), membership };
}; };
const getOrgPermission = async (type: ActorType, id: string, orgId: string) => { const getOrgPermission = async (type: ActorType, id: string, orgId: string, actorOrgId?: string) => {
switch (type) { switch (type) {
case ActorType.USER: case ActorType.USER:
return getUserOrgPermission(id, orgId); return getUserOrgPermission(id, orgId, actorOrgId);
case ActorType.IDENTITY: case ActorType.IDENTITY:
return getIdentityOrgPermission(id, orgId); return getIdentityOrgPermission(id, orgId);
default: default:
@@ -142,12 +145,17 @@ export const permissionServiceFactory = ({
}; };
// user permission for a project in an organization // user permission for a project in an organization
const getUserProjectPermission = async (userId: string, projectId: string) => { const getUserProjectPermission = async (userId: string, projectId: string, userOrgId?: string) => {
const membership = await permissionDAL.getProjectPermission(userId, projectId); const membership = await permissionDAL.getProjectPermission(userId, projectId);
if (!membership) throw new UnauthorizedError({ name: "User not in project" }); if (!membership) throw new UnauthorizedError({ name: "User not in project" });
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) { if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
throw new BadRequestError({ name: "Custom permission not found" }); throw new BadRequestError({ name: "Custom permission not found" });
} }
if (membership.orgAuthEnforced && membership.orgId !== userOrgId) {
throw new BadRequestError({ name: "Cannot access org-scoped resource" });
}
return { return {
permission: buildProjectPermission(membership.role, membership.permissions), permission: buildProjectPermission(membership.role, membership.permissions),
membership membership
@@ -160,6 +168,7 @@ export const permissionServiceFactory = ({
if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) { if (membership.role === ProjectMembershipRole.Custom && !membership.permissions) {
throw new BadRequestError({ name: "Custom permission not found" }); throw new BadRequestError({ name: "Custom permission not found" });
} }
return { return {
permission: buildProjectPermission(membership.role, membership.permissions), permission: buildProjectPermission(membership.role, membership.permissions),
membership membership
@@ -184,6 +193,8 @@ export const permissionServiceFactory = ({
: { : {
permission: MongoAbility<ProjectPermissionSet, MongoQuery>; permission: MongoAbility<ProjectPermissionSet, MongoQuery>;
membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & { membership: (T extends ActorType.USER ? TProjectMemberships : TIdentityProjectMemberships) & {
orgAuthEnforced: boolean;
orgId: string;
permissions?: unknown; permissions?: unknown;
}; };
}; };
@@ -191,11 +202,12 @@ export const permissionServiceFactory = ({
const getProjectPermission = async <T extends ActorType>( const getProjectPermission = async <T extends ActorType>(
type: T, type: T,
id: string, id: string,
projectId: string projectId: string,
actorOrgId?: string
): Promise<TProjectPermissionRT<T>> => { ): Promise<TProjectPermissionRT<T>> => {
switch (type) { switch (type) {
case ActorType.USER: case ActorType.USER:
return getUserProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>; return getUserProjectPermission(id, projectId, actorOrgId) as Promise<TProjectPermissionRT<T>>;
case ActorType.SERVICE: case ActorType.SERVICE:
return getServiceTokenProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>; return getServiceTokenProjectPermission(id, projectId) as Promise<TProjectPermissionRT<T>>;
case ActorType.IDENTITY: case ActorType.IDENTITY:

View File

@@ -1,10 +1,31 @@
import { TDbClient } from "@app/db"; import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas"; import { TableName } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify } from "@app/lib/knex"; import { ormify } from "@app/lib/knex";
export type TSamlConfigDALFactory = ReturnType<typeof samlConfigDALFactory>; export type TSamlConfigDALFactory = ReturnType<typeof samlConfigDALFactory>;
export const samlConfigDALFactory = (db: TDbClient) => { export const samlConfigDALFactory = (db: TDbClient) => {
const samlCfgOrm = ormify(db, TableName.SamlConfig); const samlCfgOrm = ormify(db, TableName.SamlConfig);
return samlCfgOrm;
const findEnforceableSamlCfg = async (orgId: string) => {
try {
const samlCfg = await db(TableName.SamlConfig)
.where({
orgId,
isActive: true
})
.whereNotNull("lastUsed")
.first();
return samlCfg;
} catch (error) {
throw new DatabaseError({ error, name: "Find org by id" });
}
};
return {
...samlCfgOrm,
findEnforceableSamlCfg
};
}; };

View File

@@ -18,7 +18,7 @@ import {
infisicalSymmetricEncypt infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption"; } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors"; import { BadRequestError } from "@app/lib/errors";
import { AuthTokenType } from "@app/services/auth/auth-type"; import { AuthMethod, AuthTokenType } from "@app/services/auth/auth-type";
import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal"; import { TOrgBotDALFactory } from "@app/services/org/org-bot-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal"; import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TUserDALFactory } from "@app/services/user/user-dal"; import { TUserDALFactory } from "@app/services/user/user-dal";
@@ -27,18 +27,15 @@ import { TLicenseServiceFactory } from "../license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission"; import { OrgPermissionActions, OrgPermissionSubjects } from "../permission/org-permission";
import { TPermissionServiceFactory } from "../permission/permission-service"; import { TPermissionServiceFactory } from "../permission/permission-service";
import { TSamlConfigDALFactory } from "./saml-config-dal"; import { TSamlConfigDALFactory } from "./saml-config-dal";
import { import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } from "./saml-config-types";
SamlProviders,
TCreateSamlCfgDTO,
TGetSamlCfgDTO,
TSamlLoginDTO,
TUpdateSamlCfgDTO
} from "./saml-config-types";
type TSamlConfigServiceFactoryDep = { type TSamlConfigServiceFactoryDep = {
samlConfigDAL: TSamlConfigDALFactory; samlConfigDAL: TSamlConfigDALFactory;
userDAL: Pick<TUserDALFactory, "create" | "findUserByEmail" | "transaction" | "updateById">; userDAL: Pick<TUserDALFactory, "create" | "findUserByEmail" | "transaction" | "updateById">;
orgDAL: Pick<TOrgDALFactory, "createMembership" | "updateMembershipById" | "findMembership" | "findOrgById">; orgDAL: Pick<
TOrgDALFactory,
"createMembership" | "updateMembershipById" | "findMembership" | "findOrgById" | "findOne" | "updateById"
>;
orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">; orgBotDAL: Pick<TOrgBotDALFactory, "findOne" | "create" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
@@ -57,6 +54,7 @@ export const samlConfigServiceFactory = ({
const createSamlCfg = async ({ const createSamlCfg = async ({
cert, cert,
actor, actor,
actorOrgId,
orgId, orgId,
issuer, issuer,
actorId, actorId,
@@ -64,7 +62,7 @@ export const samlConfigServiceFactory = ({
entryPoint, entryPoint,
authProvider authProvider
}: TCreateSamlCfgDTO) => { }: TCreateSamlCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId); const plan = await licenseService.getPlan(orgId);
@@ -140,12 +138,14 @@ export const samlConfigServiceFactory = ({
certIV, certIV,
certTag certTag
}); });
return samlConfig; return samlConfig;
}; };
const updateSamlCfg = async ({ const updateSamlCfg = async ({
orgId, orgId,
actor, actor,
actorOrgId,
cert, cert,
actorId, actorId,
issuer, issuer,
@@ -153,7 +153,7 @@ export const samlConfigServiceFactory = ({
entryPoint, entryPoint,
authProvider authProvider
}: TUpdateSamlCfgDTO) => { }: TUpdateSamlCfgDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.Sso);
const plan = await licenseService.getPlan(orgId); const plan = await licenseService.getPlan(orgId);
if (!plan.samlSSO) if (!plan.samlSSO)
@@ -162,7 +162,7 @@ export const samlConfigServiceFactory = ({
"Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration." "Failed to update SAML SSO configuration due to plan restriction. Upgrade plan to update SSO configuration."
}); });
const updateQuery: TSamlConfigsUpdate = { authProvider, isActive }; const updateQuery: TSamlConfigsUpdate = { authProvider, isActive, lastUsed: null };
const orgBot = await orgBotDAL.findOne({ orgId }); const orgBot = await orgBotDAL.findOne({ orgId });
if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" }); if (!orgBot) throw new BadRequestError({ message: "Org bot not found", name: "OrgBotNotFound" });
const key = infisicalSymmetricDecrypt({ const key = infisicalSymmetricDecrypt({
@@ -195,6 +195,8 @@ export const samlConfigServiceFactory = ({
updateQuery.certTag = certTag; updateQuery.certTag = certTag;
} }
const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery); const [ssoConfig] = await samlConfigDAL.update({ orgId }, updateQuery);
await orgDAL.updateById(orgId, { authEnforced: false });
return ssoConfig; return ssoConfig;
}; };
@@ -203,6 +205,10 @@ export const samlConfigServiceFactory = ({
if (dto.type === "org") { if (dto.type === "org") {
ssoConfig = await samlConfigDAL.findOne({ orgId: dto.orgId }); ssoConfig = await samlConfigDAL.findOne({ orgId: dto.orgId });
if (!ssoConfig) return; if (!ssoConfig) return;
} else if (dto.type === "orgSlug") {
const org = await orgDAL.findOne({ slug: dto.orgSlug });
if (!org) return;
ssoConfig = await samlConfigDAL.findOne({ orgId: org.id });
} else if (dto.type === "ssoId") { } else if (dto.type === "ssoId") {
// TODO: // TODO:
// We made this change because saml config ids were not moved over during the migration // We made this change because saml config ids were not moved over during the migration
@@ -227,7 +233,12 @@ export const samlConfigServiceFactory = ({
// when dto is type id means it's internally used // when dto is type id means it's internally used
if (dto.type === "org") { if (dto.type === "org") {
const { permission } = await permissionService.getOrgPermission(dto.actor, dto.actorId, ssoConfig.orgId); const { permission } = await permissionService.getOrgPermission(
dto.actor,
dto.actorId,
ssoConfig.orgId,
dto.actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.Sso);
} }
const { const {
@@ -284,35 +295,20 @@ export const samlConfigServiceFactory = ({
isActive: ssoConfig.isActive, isActive: ssoConfig.isActive,
entryPoint, entryPoint,
issuer, issuer,
cert cert,
lastUsed: ssoConfig.lastUsed
}; };
}; };
const samlLogin = async ({ const samlLogin = async ({ firstName, email, lastName, authProvider, orgId, relayState }: TSamlLoginDTO) => {
firstName,
email,
lastName,
authProvider,
orgId,
relayState,
isSignupAllowed
}: TSamlLoginDTO) => {
const appCfg = getConfig(); const appCfg = getConfig();
let user = await userDAL.findUserByEmail(email); let user = await userDAL.findUserByEmail(email);
const isSamlSignUpDisabled = !isSignupAllowed && !user;
if (isSamlSignUpDisabled) throw new BadRequestError({ message: "User signup disabled", name: "Saml SSO login" });
const organization = await orgDAL.findOrgById(orgId); const organization = await orgDAL.findOrgById(orgId);
if (!organization) throw new BadRequestError({ message: "Org not found" }); if (!organization) throw new BadRequestError({ message: "Org not found" });
if (user) { if (user) {
const hasSamlEnabled = (user.authMethods || []).some((method) =>
Object.values(SamlProviders).includes(method as SamlProviders)
);
await userDAL.transaction(async (tx) => { await userDAL.transaction(async (tx) => {
if (!hasSamlEnabled) {
await userDAL.updateById(user.id, { authMethods: [authProvider] }, tx);
}
const [orgMembership] = await orgDAL.findMembership({ userId: user.id, orgId }, { tx }); const [orgMembership] = await orgDAL.findMembership({ userId: user.id, orgId }, { tx });
if (!orgMembership) { if (!orgMembership) {
await orgDAL.createMembership( await orgDAL.createMembership(
@@ -342,7 +338,7 @@ export const samlConfigServiceFactory = ({
email, email,
firstName, firstName,
lastName, lastName,
authMethods: [authProvider] authMethods: [AuthMethod.EMAIL]
}, },
tx tx
); );
@@ -378,6 +374,9 @@ export const samlConfigServiceFactory = ({
expiresIn: appCfg.JWT_PROVIDER_AUTH_LIFETIME expiresIn: appCfg.JWT_PROVIDER_AUTH_LIFETIME
} }
); );
await samlConfigDAL.update({ orgId }, { lastUsed: new Date() });
return { isUserCompleted, providerAuthToken }; return { isUserCompleted, providerAuthToken };
}; };

View File

@@ -25,7 +25,11 @@ export type TUpdateSamlCfgDTO = Partial<{
TOrgPermission; TOrgPermission;
export type TGetSamlCfgDTO = export type TGetSamlCfgDTO =
| { type: "org"; orgId: string; actor: ActorType; actorId: string } | { type: "org"; orgId: string; actor: ActorType; actorId: string; actorOrgId?: string }
| {
type: "orgSlug";
orgSlug: string;
}
| { | {
type: "ssoId"; type: "ssoId";
id: string; id: string;
@@ -37,7 +41,6 @@ export type TSamlLoginDTO = {
lastName?: string; lastName?: string;
authProvider: string; authProvider: string;
orgId: string; orgId: string;
isSignupAllowed: boolean;
// saml thingy // saml thingy
relayState?: string; relayState?: string;
}; };

View File

@@ -44,6 +44,7 @@ export const secretApprovalPolicyServiceFactory = ({
name, name,
actor, actor,
actorId, actorId,
actorOrgId,
approvals, approvals,
approvers, approvers,
projectId, projectId,
@@ -53,7 +54,7 @@ export const secretApprovalPolicyServiceFactory = ({
if (approvals > approvers.length) if (approvals > approvers.length)
throw new BadRequestError({ message: "Approvals cannot be greater than approvers" }); throw new BadRequestError({ message: "Approvals cannot be greater than approvers" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, ProjectPermissionActions.Create,
ProjectPermissionSub.SecretApproval ProjectPermissionSub.SecretApproval
@@ -96,13 +97,19 @@ export const secretApprovalPolicyServiceFactory = ({
name, name,
actorId, actorId,
actor, actor,
actorOrgId,
approvals, approvals,
secretPolicyId secretPolicyId
}: TUpdateSapDTO) => { }: TUpdateSapDTO) => {
const secretApprovalPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); const secretApprovalPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId);
if (!secretApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); if (!secretApprovalPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, secretApprovalPolicy.projectId); const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
secretApprovalPolicy.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretApproval);
const updatedSap = await secretApprovalPolicyDAL.transaction(async (tx) => { const updatedSap = await secretApprovalPolicyDAL.transaction(async (tx) => {
@@ -145,11 +152,16 @@ export const secretApprovalPolicyServiceFactory = ({
}; };
}; };
const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId }: TDeleteSapDTO) => { const deleteSecretApprovalPolicy = async ({ secretPolicyId, actor, actorId, actorOrgId }: TDeleteSapDTO) => {
const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId); const sapPolicy = await secretApprovalPolicyDAL.findById(secretPolicyId);
if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" }); if (!sapPolicy) throw new BadRequestError({ message: "Secret approval policy not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, sapPolicy.projectId); const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
sapPolicy.projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete, ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretApproval ProjectPermissionSub.SecretApproval
@@ -159,8 +171,8 @@ export const secretApprovalPolicyServiceFactory = ({
return sapPolicy; return sapPolicy;
}; };
const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, projectId }: TListSapDTO) => { const getSecretApprovalPolicyByProjectId = async ({ actorId, actor, actorOrgId, projectId }: TListSapDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretApproval);
const sapPolicies = await secretApprovalPolicyDAL.find({ projectId }); const sapPolicies = await secretApprovalPolicyDAL.find({ projectId });
@@ -188,10 +200,11 @@ export const secretApprovalPolicyServiceFactory = ({
projectId, projectId,
actor, actor,
actorId, actorId,
actorOrgId,
environment, environment,
secretPath secretPath
}: TGetBoardSapDTO) => { }: TGetBoardSapDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read, ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { secretPath, environment }) subject(ProjectPermissionSub.Secrets, { secretPath, environment })

View File

@@ -73,10 +73,15 @@ export const secretApprovalRequestServiceFactory = ({
secretVersionDAL, secretVersionDAL,
secretQueueService secretQueueService
}: TSecretApprovalRequestServiceFactoryDep) => { }: TSecretApprovalRequestServiceFactoryDep) => {
const requestCount = async ({ projectId, actor, actorId }: TApprovalRequestCountDTO) => { const requestCount = async ({ projectId, actor, actorId, actorOrgId }: TApprovalRequestCountDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const { membership } = await permissionService.getProjectPermission(actor as ActorType.USER, actorId, projectId); const { membership } = await permissionService.getProjectPermission(
actor as ActorType.USER,
actorId,
projectId,
actorOrgId
);
const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id); const count = await secretApprovalRequestDAL.findProjectRequestCount(projectId, membership.id);
return count; return count;
@@ -86,6 +91,7 @@ export const secretApprovalRequestServiceFactory = ({
projectId, projectId,
actorId, actorId,
actor, actor,
actorOrgId,
status, status,
environment, environment,
committer, committer,
@@ -94,7 +100,7 @@ export const secretApprovalRequestServiceFactory = ({
}: TListApprovalsDTO) => { }: TListApprovalsDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId); const { membership } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
const approvals = await secretApprovalRequestDAL.findByProjectId({ const approvals = await secretApprovalRequestDAL.findByProjectId({
projectId, projectId,
committer, committer,
@@ -107,7 +113,7 @@ export const secretApprovalRequestServiceFactory = ({
return approvals; return approvals;
}; };
const getSecretApprovalDetails = async ({ actor, actorId, id }: TSecretApprovalDetailsDTO) => { const getSecretApprovalDetails = async ({ actor, actorId, actorOrgId, id }: TSecretApprovalDetailsDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const secretApprovalRequest = await secretApprovalRequestDAL.findById(id); const secretApprovalRequest = await secretApprovalRequestDAL.findById(id);
@@ -117,7 +123,8 @@ export const secretApprovalRequestServiceFactory = ({
const { membership } = await permissionService.getProjectPermission( const { membership } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
secretApprovalRequest.projectId secretApprovalRequest.projectId,
actorOrgId
); );
if ( if (
membership.role !== ProjectMembershipRole.Admin && membership.role !== ProjectMembershipRole.Admin &&
@@ -134,7 +141,7 @@ export const secretApprovalRequestServiceFactory = ({
return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets }; return { ...secretApprovalRequest, secretPath: secretPath?.[0]?.path || "/", commits: secrets };
}; };
const reviewApproval = async ({ approvalId, actor, status, actorId }: TReviewRequestDTO) => { const reviewApproval = async ({ approvalId, actor, status, actorId, actorOrgId }: TReviewRequestDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
@@ -143,7 +150,8 @@ export const secretApprovalRequestServiceFactory = ({
const { membership } = await permissionService.getProjectPermission( const { membership } = await permissionService.getProjectPermission(
ActorType.USER, ActorType.USER,
actorId, actorId,
secretApprovalRequest.projectId secretApprovalRequest.projectId,
actorOrgId
); );
if ( if (
membership.role !== ProjectMembershipRole.Admin && membership.role !== ProjectMembershipRole.Admin &&
@@ -175,7 +183,7 @@ export const secretApprovalRequestServiceFactory = ({
return reviewStatus; return reviewStatus;
}; };
const updateApprovalStatus = async ({ actorId, status, approvalId, actor }: TStatusChangeDTO) => { const updateApprovalStatus = async ({ actorId, status, approvalId, actor, actorOrgId }: TStatusChangeDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
@@ -184,7 +192,8 @@ export const secretApprovalRequestServiceFactory = ({
const { membership } = await permissionService.getProjectPermission( const { membership } = await permissionService.getProjectPermission(
ActorType.USER, ActorType.USER,
actorId, actorId,
secretApprovalRequest.projectId secretApprovalRequest.projectId,
actorOrgId
); );
if ( if (
membership.role !== ProjectMembershipRole.Admin && membership.role !== ProjectMembershipRole.Admin &&
@@ -207,13 +216,18 @@ export const secretApprovalRequestServiceFactory = ({
return { ...secretApprovalRequest, ...updatedRequest }; return { ...secretApprovalRequest, ...updatedRequest };
}; };
const mergeSecretApprovalRequest = async ({ approvalId, actor, actorId }: TMergeSecretApprovalRequestDTO) => { const mergeSecretApprovalRequest = async ({
approvalId,
actor,
actorId,
actorOrgId
}: TMergeSecretApprovalRequestDTO) => {
const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId); const secretApprovalRequest = await secretApprovalRequestDAL.findById(approvalId);
if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" }); if (!secretApprovalRequest) throw new BadRequestError({ message: "Secret approval request not found" });
if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" }); if (actor !== ActorType.USER) throw new BadRequestError({ message: "Must be a user" });
const { policy, folderId, projectId } = secretApprovalRequest; const { policy, folderId, projectId } = secretApprovalRequest;
const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId); const { membership } = await permissionService.getProjectPermission(ActorType.USER, actorId, projectId, actorOrgId);
if ( if (
membership.role !== ProjectMembershipRole.Admin && membership.role !== ProjectMembershipRole.Admin &&
secretApprovalRequest.committerId !== membership.id && secretApprovalRequest.committerId !== membership.id &&
@@ -401,6 +415,7 @@ export const secretApprovalRequestServiceFactory = ({
data, data,
actorId, actorId,
actor, actor,
actorOrgId,
policy, policy,
projectId, projectId,
secretPath, secretPath,
@@ -408,7 +423,12 @@ export const secretApprovalRequestServiceFactory = ({
}: TGenerateSecretApprovalRequestDTO) => { }: TGenerateSecretApprovalRequestDTO) => {
if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" }); if (actor === ActorType.SERVICE) throw new BadRequestError({ message: "Cannot use service token" });
const { permission, membership } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission, membership } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read, ProjectPermissionActions.Read,
subject(ProjectPermissionSub.Secrets, { environment, secretPath }) subject(ProjectPermissionSub.Secrets, { environment, secretPath })

View File

@@ -14,13 +14,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "../permission/pr
import { TSecretRotationDALFactory } from "./secret-rotation-dal"; import { TSecretRotationDALFactory } from "./secret-rotation-dal";
import { TSecretRotationQueueFactory } from "./secret-rotation-queue"; import { TSecretRotationQueueFactory } from "./secret-rotation-queue";
import { TSecretRotationEncData } from "./secret-rotation-queue/secret-rotation-queue-types"; import { TSecretRotationEncData } from "./secret-rotation-queue/secret-rotation-queue-types";
import { import { TCreateSecretRotationDTO, TDeleteDTO, TListByProjectIdDTO, TRestartDTO } from "./secret-rotation-types";
TCreateSecretRotationDTO,
TDeleteDTO,
TGetByIdDTO,
TListByProjectIdDTO,
TRestartDTO
} from "./secret-rotation-types";
import { rotationTemplates } from "./templates"; import { rotationTemplates } from "./templates";
type TSecretRotationServiceFactoryDep = { type TSecretRotationServiceFactoryDep = {
@@ -45,8 +39,8 @@ export const secretRotationServiceFactory = ({
folderDAL, folderDAL,
secretDAL secretDAL
}: TSecretRotationServiceFactoryDep) => { }: TSecretRotationServiceFactoryDep) => {
const getProviderTemplates = async ({ actor, actorId, projectId }: TProjectPermission) => { const getProviderTemplates = async ({ actor, actorId, actorOrgId, projectId }: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
return { return {
@@ -59,6 +53,7 @@ export const secretRotationServiceFactory = ({
projectId, projectId,
actorId, actorId,
actor, actor,
actorOrgId,
inputs, inputs,
outputs, outputs,
interval, interval,
@@ -66,7 +61,7 @@ export const secretRotationServiceFactory = ({
secretPath, secretPath,
environment environment
}: TCreateSecretRotationDTO) => { }: TCreateSecretRotationDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRotation ProjectPermissionSub.SecretRotation
@@ -144,23 +139,14 @@ export const secretRotationServiceFactory = ({
return secretRotation; return secretRotation;
}; };
const getById = async ({ rotationId, actor, actorId }: TGetByIdDTO) => { const getByProjectId = async ({ actorId, projectId, actor, actorOrgId }: TListByProjectIdDTO) => {
const [doc] = await secretRotationDAL.find({ id: rotationId }); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
if (!doc) throw new BadRequestError({ message: "Rotation not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
return doc;
};
const getByProjectId = async ({ actorId, projectId, actor }: TListByProjectIdDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRotation);
const doc = await secretRotationDAL.find({ projectId }); const doc = await secretRotationDAL.find({ projectId });
return doc; return doc;
}; };
const restartById = async ({ actor, actorId, rotationId }: TRestartDTO) => { const restartById = async ({ actor, actorId, actorOrgId, rotationId }: TRestartDTO) => {
const doc = await secretRotationDAL.findById(rotationId); const doc = await secretRotationDAL.findById(rotationId);
if (!doc) throw new BadRequestError({ message: "Rotation not found" }); if (!doc) throw new BadRequestError({ message: "Rotation not found" });
@@ -171,18 +157,18 @@ export const secretRotationServiceFactory = ({
message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation." message: "Failed to add secret rotation due to plan restriction. Upgrade plan to add secret rotation."
}); });
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SecretRotation);
await secretRotationQueue.removeFromQueue(doc.id, doc.interval); await secretRotationQueue.removeFromQueue(doc.id, doc.interval);
await secretRotationQueue.addToQueue(doc.id, doc.interval); await secretRotationQueue.addToQueue(doc.id, doc.interval);
return doc; return doc;
}; };
const deleteById = async ({ actor, actorId, rotationId }: TDeleteDTO) => { const deleteById = async ({ actor, actorId, actorOrgId, rotationId }: TDeleteDTO) => {
const doc = await secretRotationDAL.findById(rotationId); const doc = await secretRotationDAL.findById(rotationId);
if (!doc) throw new BadRequestError({ message: "Rotation not found" }); if (!doc) throw new BadRequestError({ message: "Rotation not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, doc.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Delete, ProjectPermissionActions.Delete,
ProjectPermissionSub.SecretRotation ProjectPermissionSub.SecretRotation
@@ -197,7 +183,6 @@ export const secretRotationServiceFactory = ({
return { return {
getProviderTemplates, getProviderTemplates,
getById,
getByProjectId, getByProjectId,
createRotation, createRotation,
restartById, restartById,

View File

@@ -18,7 +18,3 @@ export type TDeleteDTO = {
export type TRestartDTO = { export type TRestartDTO = {
rotationId: string; rotationId: string;
} & Omit<TProjectPermission, "projectId">; } & Omit<TProjectPermission, "projectId">;
export type TGetByIdDTO = {
rotationId: string;
} & Omit<TProjectPermission, "projectId">;

View File

@@ -39,8 +39,8 @@ export const secretScanningServiceFactory = ({
permissionService, permissionService,
secretScanningQueue secretScanningQueue
}: TSecretScanningServiceFactoryDep) => { }: TSecretScanningServiceFactoryDep) => {
const createInstallationSession = async ({ actor, orgId, actorId }: TInstallAppSessionDTO) => { const createInstallationSession = async ({ actor, orgId, actorId, actorOrgId }: TInstallAppSessionDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const sessionId = crypto.randomBytes(16).toString("hex"); const sessionId = crypto.randomBytes(16).toString("hex");
@@ -48,11 +48,17 @@ export const secretScanningServiceFactory = ({
return { sessionId }; return { sessionId };
}; };
const linkInstallationToOrg = async ({ sessionId, actorId, installationId, actor }: TLinkInstallSessionDTO) => { const linkInstallationToOrg = async ({
sessionId,
actorId,
installationId,
actor,
actorOrgId
}: TLinkInstallSessionDTO) => {
const session = await gitAppInstallSessionDAL.findOne({ sessionId }); const session = await gitAppInstallSessionDAL.findOne({ sessionId });
if (!session) throw new UnauthorizedError({ message: "Session not found" }); if (!session) throw new UnauthorizedError({ message: "Session not found" });
const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, session.orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SecretScanning);
const installatedApp = await gitAppOrgDAL.transaction(async (tx) => { const installatedApp = await gitAppOrgDAL.transaction(async (tx) => {
await gitAppInstallSessionDAL.deleteById(session.id, tx); await gitAppInstallSessionDAL.deleteById(session.id, tx);
@@ -83,23 +89,23 @@ export const secretScanningServiceFactory = ({
return { installatedApp }; return { installatedApp };
}; };
const getOrgInstallationStatus = async ({ actorId, orgId, actor }: TGetOrgInstallStatusDTO) => { const getOrgInstallationStatus = async ({ actorId, orgId, actor, actorOrgId }: TGetOrgInstallStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const appInstallation = await gitAppOrgDAL.findOne({ orgId }); const appInstallation = await gitAppOrgDAL.findOne({ orgId });
return Boolean(appInstallation); return Boolean(appInstallation);
}; };
const getRisksByOrg = async ({ actor, orgId, actorId }: TGetOrgRisksDTO) => { const getRisksByOrg = async ({ actor, orgId, actorId, actorOrgId }: TGetOrgRisksDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SecretScanning);
const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] }); const risks = await secretScanningDAL.find({ orgId }, { sort: [["createdAt", "desc"]] });
return { risks }; return { risks };
}; };
const updateRiskStatus = async ({ actorId, orgId, actor, riskId, status }: TUpdateRiskStatusDTO) => { const updateRiskStatus = async ({ actorId, orgId, actor, actorOrgId, riskId, status }: TUpdateRiskStatusDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId); const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning); ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Edit, OrgPermissionSubjects.SecretScanning);
const isRiskResolved = Boolean( const isRiskResolved = Boolean(

View File

@@ -58,9 +58,10 @@ export const secretSnapshotServiceFactory = ({
projectId, projectId,
actorId, actorId,
actor, actor,
actorOrgId,
path path
}: TProjectSnapshotCountDTO) => { }: TProjectSnapshotCountDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const folder = await folderDAL.findBySecretPath(projectId, environment, path); const folder = await folderDAL.findBySecretPath(projectId, environment, path);
@@ -75,11 +76,12 @@ export const secretSnapshotServiceFactory = ({
projectId, projectId,
actorId, actorId,
actor, actor,
actorOrgId,
path, path,
limit = 20, limit = 20,
offset = 0 offset = 0
}: TProjectSnapshotListDTO) => { }: TProjectSnapshotListDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const folder = await folderDAL.findBySecretPath(projectId, environment, path); const folder = await folderDAL.findBySecretPath(projectId, environment, path);
@@ -89,10 +91,10 @@ export const secretSnapshotServiceFactory = ({
return snapshots; return snapshots;
}; };
const getSnapshotData = async ({ actorId, actor, id }: TGetSnapshotDataDTO) => { const getSnapshotData = async ({ actorId, actor, actorOrgId, id }: TGetSnapshotDataDTO) => {
const snapshot = await snapshotDAL.findSecretSnapshotDataById(id); const snapshot = await snapshotDAL.findSecretSnapshotDataById(id);
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
return snapshot; return snapshot;
}; };
@@ -143,11 +145,11 @@ export const secretSnapshotServiceFactory = ({
} }
}; };
const rollbackSnapshot = async ({ id: snapshotId, actor, actorId }: TRollbackSnapshotDTO) => { const rollbackSnapshot = async ({ id: snapshotId, actor, actorId, actorOrgId }: TRollbackSnapshotDTO) => {
const snapshot = await snapshotDAL.findById(snapshotId); const snapshot = await snapshotDAL.findById(snapshotId);
if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" }); if (!snapshot) throw new BadRequestError({ message: "Snapshot not found" });
const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, snapshot.projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create, ProjectPermissionActions.Create,
ProjectPermissionSub.SecretRollback ProjectPermissionSub.SecretRollback

View File

@@ -26,8 +26,8 @@ export const trustedIpServiceFactory = ({
licenseService, licenseService,
projectDAL projectDAL
}: TTrustedIpServiceFactoryDep) => { }: TTrustedIpServiceFactoryDep) => {
const listIpsByProjectId = async ({ projectId, actor, actorId }: TProjectPermission) => { const listIpsByProjectId = async ({ projectId, actor, actorId, actorOrgId }: TProjectPermission) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
const trustedIps = await trustedIpDAL.find({ const trustedIps = await trustedIpDAL.find({
projectId projectId
@@ -35,8 +35,16 @@ export const trustedIpServiceFactory = ({
return trustedIps; return trustedIps;
}; };
const addProjectIp = async ({ projectId, actorId, actor, ipAddress: ip, comment, isActive }: TCreateIpDTO) => { const addProjectIp = async ({
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); projectId,
actorId,
actor,
actorOrgId,
ipAddress: ip,
comment,
isActive
}: TCreateIpDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);
@@ -65,8 +73,16 @@ export const trustedIpServiceFactory = ({
return { trustedIp, project }; // for audit log return { trustedIp, project }; // for audit log
}; };
const updateProjectIp = async ({ projectId, actorId, actor, ipAddress: ip, comment, trustedIpId }: TUpdateIpDTO) => { const updateProjectIp = async ({
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); projectId,
actorId,
actor,
actorOrgId,
ipAddress: ip,
comment,
trustedIpId
}: TUpdateIpDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);
@@ -97,8 +113,8 @@ export const trustedIpServiceFactory = ({
return { trustedIp, project }; // for audit log return { trustedIp, project }; // for audit log
}; };
const deleteProjectIp = async ({ projectId, actorId, actor, trustedIpId }: TDeleteIpDTO) => { const deleteProjectIp = async ({ projectId, actorId, actor, actorOrgId, trustedIpId }: TDeleteIpDTO) => {
const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId); const { permission } = await permissionService.getProjectPermission(actor, actorId, projectId, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.IpAllowList);
const project = await projectDAL.findById(projectId); const project = await projectDAL.findById(projectId);

View File

@@ -15,9 +15,11 @@ const envSchema = z
PORT: z.coerce.number().default(4000), PORT: z.coerce.number().default(4000),
REDIS_URL: zpStr(z.string()), REDIS_URL: zpStr(z.string()),
HOST: zpStr(z.string().default("localhost")), HOST: zpStr(z.string().default("localhost")),
DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database conntection string")), DB_CONNECTION_URI: zpStr(z.string().describe("Postgres database connection string")),
DB_ROOT_CERT: zpStr(z.string().describe("Postgres database base64-encoded CA cert").optional()),
NODE_ENV: z.enum(["development", "test", "production"]).default("production"), NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
SALT_ROUNDS: z.coerce.number().default(10), SALT_ROUNDS: z.coerce.number().default(10),
INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()),
// TODO(akhilmhdh): will be changed to one // TODO(akhilmhdh): will be changed to one
ENCRYPTION_KEY: zpStr(z.string().optional()), ENCRYPTION_KEY: zpStr(z.string().optional()),
ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()), ROOT_ENCRYPTION_KEY: zpStr(z.string().optional()),
@@ -93,7 +95,7 @@ const envSchema = z
SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()), SECRET_SCANNING_GIT_APP_ID: zpStr(z.string().optional()),
SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()), SECRET_SCANNING_PRIVATE_KEY: zpStr(z.string().optional()),
// LICENCE // LICENCE
LICENSE_SERVER_URL: zpStr(z.string().optional()), LICENSE_SERVER_URL: zpStr(z.string().optional().default("https://portal.infisical.com")),
LICENSE_SERVER_KEY: zpStr(z.string().optional()), LICENSE_SERVER_KEY: zpStr(z.string().optional()),
LICENSE_KEY: zpStr(z.string().optional()), LICENSE_KEY: zpStr(z.string().optional()),
STANDALONE_MODE: z STANDALONE_MODE: z

View File

@@ -44,7 +44,7 @@ export const encryptSymmetric = (plaintext: string, key: string) => {
}; };
}; };
export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string) => { export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string | Buffer) => {
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16); const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16);
const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv); const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
@@ -58,7 +58,12 @@ export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string)
}; };
}; };
export const decryptSymmetric128BitHexKeyUTF8 = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => { export const decryptSymmetric128BitHexKeyUTF8 = ({
ciphertext,
iv,
tag,
key
}: Omit<TDecryptSymmetricInput, "key"> & { key: string | Buffer }): string => {
const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64")); const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64"));
decipher.setAuthTag(Buffer.from(tag, "base64")); decipher.setAuthTag(Buffer.from(tag, "base64"));

View File

@@ -4,12 +4,14 @@ export type TOrgPermission = {
actor: ActorType; actor: ActorType;
actorId: string; actorId: string;
orgId: string; orgId: string;
actorOrgId?: string;
}; };
export type TProjectPermission = { export type TProjectPermission = {
actor: ActorType; actor: ActorType;
actorId: string; actorId: string;
projectId: string; projectId: string;
actorOrgId?: string;
}; };
export type RequiredKeys<T> = { export type RequiredKeys<T> = {

View File

@@ -12,7 +12,11 @@ dotenv.config();
const run = async () => { const run = async () => {
const logger = await initLogger(); const logger = await initLogger();
const appCfg = initEnvConfig(logger); const appCfg = initEnvConfig(logger);
const db = initDbConnection(appCfg.DB_CONNECTION_URI); const db = initDbConnection({
dbConnectionUri: appCfg.DB_CONNECTION_URI,
dbRootCert: appCfg.DB_ROOT_CERT
});
const smtp = smtpServiceFactory(formatSmtpConfig()); const smtp = smtpServiceFactory(formatSmtpConfig());
const queue = queueServiceFactory(appCfg.REDIS_URL); const queue = queueServiceFactory(appCfg.REDIS_URL);
@@ -36,7 +40,7 @@ const run = async () => {
port: appCfg.PORT, port: appCfg.PORT,
host: appCfg.HOST, host: appCfg.HOST,
listenTextResolver: (address) => { listenTextResolver: (address) => {
bootstrap(); void bootstrap();
return address; return address;
} }
}); });

View File

@@ -5,6 +5,7 @@ import type { FastifyCookieOptions } from "@fastify/cookie";
import cookie from "@fastify/cookie"; import cookie from "@fastify/cookie";
import type { FastifyCorsOptions } from "@fastify/cors"; import type { FastifyCorsOptions } from "@fastify/cors";
import cors from "@fastify/cors"; import cors from "@fastify/cors";
import fastifyEtag from "@fastify/etag";
import fastifyFormBody from "@fastify/formbody"; import fastifyFormBody from "@fastify/formbody";
import helmet from "@fastify/helmet"; import helmet from "@fastify/helmet";
import type { FastifyRateLimitOptions } from "@fastify/rate-limit"; import type { FastifyRateLimitOptions } from "@fastify/rate-limit";
@@ -13,11 +14,10 @@ import fasitfy from "fastify";
import { Knex } from "knex"; import { Knex } from "knex";
import { Logger } from "pino"; import { Logger } from "pino";
import { getConfig } from "@app/lib/config/env";
import { TQueueServiceFactory } from "@app/queue"; import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service"; import { TSmtpService } from "@app/services/smtp/smtp-service";
import { getConfig } from "@lib/config/env";
import { globalRateLimiterCfg } from "./config/rateLimiter"; import { globalRateLimiterCfg } from "./config/rateLimiter";
import { fastifyErrHandler } from "./plugins/error-handler"; import { fastifyErrHandler } from "./plugins/error-handler";
import { registerExternalNextjs } from "./plugins/external-nextjs"; import { registerExternalNextjs } from "./plugins/external-nextjs";
@@ -37,8 +37,9 @@ type TMain = {
export const main = async ({ db, smtp, logger, queue }: TMain) => { export const main = async ({ db, smtp, logger, queue }: TMain) => {
const appCfg = getConfig(); const appCfg = getConfig();
const server = fasitfy({ const server = fasitfy({
logger, logger: appCfg.NODE_ENV === "test" ? false : logger,
trustProxy: true, trustProxy: true,
connectionTimeout: 30 * 1000,
ignoreTrailingSlash: true ignoreTrailingSlash: true
}).withTypeProvider<ZodTypeProvider>(); }).withTypeProvider<ZodTypeProvider>();
@@ -50,6 +51,8 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
secret: appCfg.COOKIE_SECRET_SIGN_KEY secret: appCfg.COOKIE_SECRET_SIGN_KEY
}); });
await server.register(fastifyEtag);
await server.register<FastifyCorsOptions>(cors, { await server.register<FastifyCorsOptions>(cors, {
credentials: true, credentials: true,
origin: appCfg.SITE_URL || true origin: appCfg.SITE_URL || true
@@ -72,7 +75,7 @@ export const main = async ({ db, smtp, logger, queue }: TMain) => {
if (appCfg.isProductionMode) { if (appCfg.isProductionMode) {
await server.register(registerExternalNextjs, { await server.register(registerExternalNextjs, {
standaloneMode: appCfg.STANDALONE_MODE, standaloneMode: appCfg.STANDALONE_MODE,
dir: path.join(__dirname, "../"), dir: path.join(__dirname, "../../"),
port: appCfg.PORT port: appCfg.PORT
}); });
} }

View File

@@ -12,9 +12,9 @@ type BootstrapOpt = {
db: Knex; db: Knex;
}; };
const bootstrapCb = () => { const bootstrapCb = async () => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (!serverCfg.initialized) { if (!serverCfg.initialized) {
console.info(`Welcome to Infisical console.info(`Welcome to Infisical

View File

@@ -10,6 +10,7 @@ import { TIdentityAccessTokenJwtPayload } from "@app/services/identity-access-to
export type TAuthMode = export type TAuthMode =
| { | {
orgId?: string;
authMode: AuthMode.JWT; authMode: AuthMode.JWT;
actor: ActorType.USER; actor: ActorType.USER;
userId: string; userId: string;
@@ -21,6 +22,7 @@ export type TAuthMode =
actor: ActorType.USER; actor: ActorType.USER;
userId: string; userId: string;
user: TUsers; user: TUsers;
orgId?: string;
} }
| { | {
authMode: AuthMode.SERVICE_TOKEN; authMode: AuthMode.SERVICE_TOKEN;
@@ -82,8 +84,8 @@ export const injectIdentity = fp(async (server: FastifyZodProvider) => {
switch (authMode) { switch (authMode) {
case AuthMode.JWT: { case AuthMode.JWT: {
const { user, tokenVersionId } = await server.services.authToken.fnValidateJwtIdentity(token); const { user, tokenVersionId, orgId } = await server.services.authToken.fnValidateJwtIdentity(token);
req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor }; req.auth = { authMode: AuthMode.JWT, user, userId: user.id, tokenVersionId, actor, orgId };
break; break;
} }
case AuthMode.IDENTITY_ACCESS_TOKEN: { case AuthMode.IDENTITY_ACCESS_TOKEN: {

View File

@@ -9,7 +9,7 @@ export const injectPermission = fp(async (server) => {
if (!req.auth) return; if (!req.auth) return;
if (req.auth.actor === ActorType.USER) { if (req.auth.actor === ActorType.USER) {
req.permission = { type: ActorType.USER, id: req.auth.userId }; req.permission = { type: ActorType.USER, id: req.auth.userId, orgId: req.auth?.orgId };
} else if (req.auth.actor === ActorType.IDENTITY) { } else if (req.auth.actor === ActorType.IDENTITY) {
req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId }; req.permission = { type: ActorType.IDENTITY, id: req.auth.identityId };
} else if (req.auth.actor === ActorType.SERVICE) { } else if (req.auth.actor === ActorType.SERVICE) {

View File

@@ -45,6 +45,9 @@ export const registerExternalNextjs = async (
server.route({ server.route({
method: ["GET", "PUT", "PATCH", "POST", "DELETE"], method: ["GET", "PUT", "PATCH", "POST", "DELETE"],
url: "/*", url: "/*",
schema: {
hide: true
},
handler: (req, res) => handler: (req, res) =>
nextApp nextApp
.getRequestHandler()(req.raw, res.raw) .getRequestHandler()(req.raw, res.raw)

View File

@@ -25,13 +25,13 @@ export const fastifySwagger = fp(async (fastify) => {
], ],
components: { components: {
securitySchemes: { securitySchemes: {
bearer: { bearerAuth: {
type: "http", type: "http",
scheme: "bearer", scheme: "bearer",
bearerFormat: "JWT", bearerFormat: "JWT",
description: "A service token in Infisical" description: "An access token in Infisical"
}, },
apiKey: { apiKeyAuth: {
type: "apiKey", type: "apiKey",
in: "header", in: "header",
name: "X-API-Key", name: "X-API-Key",
@@ -43,6 +43,7 @@ export const fastifySwagger = fp(async (fastify) => {
}); });
await fastify.register(swaggerUI, { await fastify.register(swaggerUI, {
routePrefix: "/docs" routePrefix: "/api/docs",
prefix: "/api/docs"
}); });
}); });

View File

@@ -513,9 +513,9 @@ export const registerRoutes = async (
}) })
} }
}, },
handler: () => { handler: async () => {
const cfg = getConfig(); const cfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
return { return {
date: new Date(), date: new Date(),
message: "Ok" as const, message: "Ok" as const,

View File

@@ -20,8 +20,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
}) })
} }
}, },
handler: () => { handler: async () => {
const config = getServerCfg(); const config = await getServerCfg();
return { config }; return { config };
} }
}); });
@@ -72,13 +72,14 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
200: z.object({ 200: z.object({
message: z.string(), message: z.string(),
user: UsersSchema, user: UsersSchema,
token: z.string() token: z.string(),
new: z.string()
}) })
} }
}, },
handler: async (req, res) => { handler: async (req, res) => {
const appCfg = getConfig(); const appCfg = getConfig();
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (serverCfg.initialized) if (serverCfg.initialized)
throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" }); throw new UnauthorizedError({ name: "Admin sign up", message: "Admin has been created" });
const { user, token } = await server.services.superAdmin.adminSignUp({ const { user, token } = await server.services.superAdmin.adminSignUp({
@@ -107,7 +108,8 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
return { return {
message: "Successfully set up admin account", message: "Successfully set up admin account",
user: user.user, user: user.user,
token: token.access token: token.access,
new: "123"
}; };
} }
}); });

View File

@@ -88,7 +88,8 @@ export const registerAuthRoutes = async (server: FastifyZodProvider) => {
authTokenType: AuthTokenType.ACCESS_TOKEN, authTokenType: AuthTokenType.ACCESS_TOKEN,
userId: decodedToken.userId, userId: decodedToken.userId,
tokenVersionId: tokenVersion.id, tokenVersionId: tokenVersion.id,
accessVersion: tokenVersion.accessVersion accessVersion: tokenVersion.accessVersion,
organizationId: decodedToken.organizationId
}, },
appCfg.AUTH_SECRET, appCfg.AUTH_SECRET,
{ expiresIn: appCfg.JWT_AUTH_LIFETIME } { expiresIn: appCfg.JWT_AUTH_LIFETIME }

View File

@@ -29,6 +29,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
const bot = await server.services.projectBot.findBotByProjectId({ const bot = await server.services.projectBot.findBotByProjectId({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId projectId: req.params.projectId
}); });
return { bot }; return { bot };
@@ -68,6 +69,7 @@ export const registerProjectBotRouter = async (server: FastifyZodProvider) => {
const bot = await server.services.projectBot.setBotActiveState({ const bot = await server.services.projectBot.setBotActiveState({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
botId: req.params.botId, botId: req.params.botId,
botKey: req.body.botKey, botKey: req.body.botKey,
isActive: req.body.isActive isActive: req.body.isActive

View File

@@ -5,6 +5,7 @@ export const registerIdentityAccessTokenRouter = async (server: FastifyZodProvid
url: "/token/renew", url: "/token/renew",
method: "POST", method: "POST",
schema: { schema: {
description: "Renew access token",
body: z.object({ body: z.object({
accessToken: z.string().trim() accessToken: z.string().trim()
}), }),

View File

@@ -9,8 +9,14 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "POST", method: "POST",
url: "/", url: "/",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Create identity",
security: [
{
bearerAuth: []
}
],
body: z.object({ body: z.object({
name: z.string().trim(), name: z.string().trim(),
organizationId: z.string().trim(), organizationId: z.string().trim(),
@@ -26,6 +32,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.createIdentity({ const identity = await server.services.identity.createIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
orgId: req.body.organizationId orgId: req.body.organizationId
}); });
@@ -49,8 +56,14 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:identityId", url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -68,6 +81,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.updateIdentity({ const identity = await server.services.identity.updateIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.identityId, id: req.params.identityId,
...req.body ...req.body
}); });
@@ -91,8 +105,14 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
server.route({ server.route({
method: "DELETE", method: "DELETE",
url: "/:identityId", url: "/:identityId",
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Delete identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -106,6 +126,7 @@ export const registerIdentityRouter = async (server: FastifyZodProvider) => {
const identity = await server.services.identity.deleteIdentity({ const identity = await server.services.identity.deleteIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.identityId id: req.params.identityId
}); });

View File

@@ -24,6 +24,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
url: "/universal-auth/login", url: "/universal-auth/login",
method: "POST", method: "POST",
schema: { schema: {
description: "Login with Universal Auth",
body: z.object({ body: z.object({
clientId: z.string().trim(), clientId: z.string().trim(),
clientSecret: z.string().trim() clientSecret: z.string().trim()
@@ -67,6 +68,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Attach Universal Auth configuration onto identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string().trim() identityId: z.string().trim()
}), }),
@@ -112,6 +119,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const identityUniversalAuth = await server.services.identityUa.attachUa({ const identityUniversalAuth = await server.services.identityUa.attachUa({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
identityId: req.params.identityId identityId: req.params.identityId
}); });
@@ -140,6 +148,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "PATCH", method: "PATCH",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update Universal Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -178,6 +192,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const identityUniversalAuth = await server.services.identityUa.updateUa({ const identityUniversalAuth = await server.services.identityUa.updateUa({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
identityId: req.params.identityId identityId: req.params.identityId
}); });
@@ -207,6 +222,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Retrieve Universal Auth configuration on identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -220,6 +241,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const identityUniversalAuth = await server.services.identityUa.getIdentityUa({ const identityUniversalAuth = await server.services.identityUa.getIdentityUa({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId identityId: req.params.identityId
}); });
@@ -243,6 +265,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Create Universal Auth Client Secret for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -262,6 +290,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({ const { clientSecret, clientSecretData, orgId } = await server.services.identityUa.createUaClientSecret({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId, identityId: req.params.identityId,
...req.body ...req.body
}); });
@@ -287,6 +316,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "GET", method: "GET",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "List Universal Auth Client Secrets for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string() identityId: z.string()
}), }),
@@ -300,6 +335,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({ const { clientSecrets: clientSecretData, orgId } = await server.services.identityUa.getUaClientSecrets({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId identityId: req.params.identityId
}); });
@@ -322,6 +358,12 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
method: "POST", method: "POST",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Revoke Universal Auth Client Secrets for identity",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
identityId: z.string(), identityId: z.string(),
clientSecretId: z.string() clientSecretId: z.string()
@@ -336,6 +378,7 @@ export const registerIdentityUaRouter = async (server: FastifyZodProvider) => {
const clientSecretData = await server.services.identityUa.revokeUaClientSecret({ const clientSecretData = await server.services.identityUa.revokeUaClientSecret({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId, identityId: req.params.identityId,
clientSecretId: req.params.clientSecretId clientSecretId: req.params.clientSecretId
}); });

View File

@@ -53,6 +53,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.getIntegrationAuth({ const integrationAuth = await server.services.integrationAuth.getIntegrationAuth({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId id: req.params.integrationAuthId
}); });
return { integrationAuth }; return { integrationAuth };
@@ -78,6 +79,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuths({ const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuths({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
integration: req.query.integration, integration: req.query.integration,
projectId: req.query.projectId projectId: req.query.projectId
}); });
@@ -115,6 +117,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuthById({ const integrationAuth = await server.services.integrationAuth.deleteIntegrationAuthById({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId id: req.params.integrationAuthId
}); });
@@ -154,6 +157,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.oauthExchange({ const integrationAuth = await server.services.integrationAuth.oauthExchange({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body ...req.body
}); });
@@ -196,6 +200,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({ const integrationAuth = await server.services.integrationAuth.saveIntegrationToken({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body ...req.body
}); });
@@ -242,6 +247,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const apps = await server.services.integrationAuth.getIntegrationApps({ const apps = await server.services.integrationAuth.getIntegrationApps({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
...req.query ...req.query
}); });
@@ -272,6 +278,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const teams = await server.services.integrationAuth.getIntegrationAuthTeams({ const teams = await server.services.integrationAuth.getIntegrationAuthTeams({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId id: req.params.integrationAuthId
}); });
return { teams }; return { teams };
@@ -299,6 +306,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const branches = await server.services.integrationAuth.getVercelBranches({ const branches = await server.services.integrationAuth.getVercelBranches({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
appId: req.query.appId appId: req.query.appId
}); });
@@ -327,6 +335,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const groups = await server.services.integrationAuth.getChecklyGroups({ const groups = await server.services.integrationAuth.getChecklyGroups({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
accountId: req.query.accountId accountId: req.query.accountId
}); });
@@ -352,6 +361,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const orgs = await server.services.integrationAuth.getQoveryOrgs({ const orgs = await server.services.integrationAuth.getQoveryOrgs({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId id: req.params.integrationAuthId
}); });
return { orgs }; return { orgs };
@@ -379,6 +389,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const projects = await server.services.integrationAuth.getQoveryProjects({ const projects = await server.services.integrationAuth.getQoveryProjects({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
orgId: req.query.orgId orgId: req.query.orgId
}); });
@@ -407,6 +418,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const environments = await server.services.integrationAuth.getQoveryEnvs({ const environments = await server.services.integrationAuth.getQoveryEnvs({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
projectId: req.query.projectId projectId: req.query.projectId
}); });
@@ -435,6 +447,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const apps = await server.services.integrationAuth.getQoveryApps({ const apps = await server.services.integrationAuth.getQoveryApps({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
environmentId: req.query.environmentId environmentId: req.query.environmentId
}); });
@@ -463,6 +476,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const containers = await server.services.integrationAuth.getQoveryContainers({ const containers = await server.services.integrationAuth.getQoveryContainers({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
environmentId: req.query.environmentId environmentId: req.query.environmentId
}); });
@@ -491,6 +505,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const jobs = await server.services.integrationAuth.getQoveryJobs({ const jobs = await server.services.integrationAuth.getQoveryJobs({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
environmentId: req.query.environmentId environmentId: req.query.environmentId
}); });
@@ -519,6 +534,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const environments = await server.services.integrationAuth.getRailwayEnvironments({ const environments = await server.services.integrationAuth.getRailwayEnvironments({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
appId: req.query.appId appId: req.query.appId
}); });
@@ -547,6 +563,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const services = await server.services.integrationAuth.getRailwayServices({ const services = await server.services.integrationAuth.getRailwayServices({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
appId: req.query.appId appId: req.query.appId
}); });
@@ -582,6 +599,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const workspaces = await server.services.integrationAuth.getBitbucketWorkspaces({ const workspaces = await server.services.integrationAuth.getBitbucketWorkspaces({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId id: req.params.integrationAuthId
}); });
return { workspaces }; return { workspaces };
@@ -614,6 +632,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const secretGroups = await server.services.integrationAuth.getNorthFlankSecretGroups({ const secretGroups = await server.services.integrationAuth.getNorthFlankSecretGroups({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
appId: req.query.appId appId: req.query.appId
}); });
@@ -647,6 +666,7 @@ export const registerIntegrationAuthRouter = async (server: FastifyZodProvider)
const buildConfigs = await server.services.integrationAuth.getTeamcityBuildConfigs({ const buildConfigs = await server.services.integrationAuth.getTeamcityBuildConfigs({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationAuthId, id: req.params.integrationAuthId,
appId: req.query.appId appId: req.query.appId
}); });

View File

@@ -50,6 +50,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
const { integration, integrationAuth } = await server.services.integration.createIntegration({ const { integration, integrationAuth } = await server.services.integration.createIntegration({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body ...req.body
}); });
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
@@ -107,6 +108,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
const integration = await server.services.integration.updateIntegration({ const integration = await server.services.integration.updateIntegration({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationId, id: req.params.integrationId,
...req.body ...req.body
}); });
@@ -132,6 +134,7 @@ export const registerIntegrationRouter = async (server: FastifyZodProvider) => {
const integration = await server.services.integration.deleteIntegration({ const integration = await server.services.integration.deleteIntegration({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.integrationId id: req.params.integrationId
}); });

View File

@@ -26,7 +26,8 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
const completeInviteLink = await server.services.org.inviteUserToOrganization({ const completeInviteLink = await server.services.org.inviteUserToOrganization({
orgId: req.body.organizationId, orgId: req.body.organizationId,
userId: req.permission.id, userId: req.permission.id,
inviteeEmail: req.body.inviteeEmail inviteeEmail: req.body.inviteeEmail,
actorOrgId: req.permission.orgId
}); });
return { return {

View File

@@ -37,7 +37,11 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const organization = await server.services.org.findOrganizationById(req.permission.id, req.params.organizationId); const organization = await server.services.org.findOrganizationById(
req.permission.id,
req.params.organizationId,
req.permission.orgId
);
return { organization }; return { organization };
} }
}); });
@@ -68,17 +72,29 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const users = await server.services.org.findAllOrgMembers(req.permission.id, req.params.organizationId); const users = await server.services.org.findAllOrgMembers(
req.permission.id,
req.params.organizationId,
req.permission.orgId
);
return { users }; return { users };
} }
}); });
server.route({ server.route({
method: "PATCH", method: "PATCH",
url: "/:organizationId/name", url: "/:organizationId",
schema: { schema: {
params: z.object({ organizationId: z.string().trim() }), params: z.object({ organizationId: z.string().trim() }),
body: z.object({ name: z.string().trim() }), body: z.object({
name: z.string().trim().optional(),
slug: z
.string()
.trim()
.regex(/^[a-zA-Z0-9-]+$/, "Name must only contain alphanumeric characters or hyphens")
.optional(),
authEnforced: z.boolean().optional()
}),
response: { response: {
200: z.object({ 200: z.object({
message: z.string(), message: z.string(),
@@ -88,11 +104,14 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.JWT]), onRequest: verifyAuth([AuthMode.JWT]),
handler: async (req) => { handler: async (req) => {
const organization = await server.services.org.updateOrgName( const organization = await server.services.org.updateOrg({
req.permission.id, actor: req.permission.type,
req.params.organizationId, actorId: req.permission.id,
req.body.name actorOrgId: req.permission.orgId,
); orgId: req.params.organizationId,
data: req.body
});
return { return {
message: "Successfully changed organization name", message: "Successfully changed organization name",
organization organization
@@ -115,7 +134,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
handler: async (req) => { handler: async (req) => {
const incidentContactsOrg = await req.server.services.org.findIncidentContacts( const incidentContactsOrg = await req.server.services.org.findIncidentContacts(
req.permission.id, req.permission.id,
req.params.organizationId req.params.organizationId,
req.permission.orgId
); );
return { incidentContactsOrg }; return { incidentContactsOrg };
} }
@@ -138,7 +158,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
const incidentContactsOrg = await req.server.services.org.createIncidentContact( const incidentContactsOrg = await req.server.services.org.createIncidentContact(
req.permission.id, req.permission.id,
req.params.organizationId, req.params.organizationId,
req.body.email req.body.email,
req.permission.orgId
); );
return { incidentContactsOrg }; return { incidentContactsOrg };
} }
@@ -160,7 +181,8 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
const incidentContactsOrg = await req.server.services.org.deleteIncidentContact( const incidentContactsOrg = await req.server.services.org.deleteIncidentContact(
req.permission.id, req.permission.id,
req.params.organizationId, req.params.organizationId,
req.params.incidentContactId req.params.incidentContactId,
req.permission.orgId
); );
return { incidentContactsOrg }; return { incidentContactsOrg };
} }

View File

@@ -10,6 +10,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments", url: "/:workspaceId/environments",
method: "POST", method: "POST",
schema: { schema: {
description: "Create environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -30,6 +37,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
const environment = await server.services.projectEnv.createEnvironment({ const environment = await server.services.projectEnv.createEnvironment({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
...req.body ...req.body
}); });
@@ -57,6 +65,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments/:id", url: "/:workspaceId/environments/:id",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
id: z.string().trim() id: z.string().trim()
@@ -79,6 +94,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
const { environment, old } = await server.services.projectEnv.updateEnvironment({ const { environment, old } = await server.services.projectEnv.updateEnvironment({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
id: req.params.id, id: req.params.id,
...req.body ...req.body
@@ -112,6 +128,13 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
url: "/:workspaceId/environments/:id", url: "/:workspaceId/environments/:id",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete environment",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
id: z.string().trim() id: z.string().trim()
@@ -129,6 +152,7 @@ export const registerProjectEnvRouter = async (server: FastifyZodProvider) => {
const environment = await server.services.projectEnv.deleteEnvironment({ const environment = await server.services.projectEnv.deleteEnvironment({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
id: req.params.id id: req.params.id
}); });

View File

@@ -30,6 +30,7 @@ export const registerProjectKeyRouter = async (server: FastifyZodProvider) => {
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
nonce: req.body.key.nonce, nonce: req.body.key.nonce,
receiverId: req.body.key.userId, receiverId: req.body.key.userId,
encryptedKey: req.body.key.encryptedKey encryptedKey: req.body.key.encryptedKey

View File

@@ -10,6 +10,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships", url: "/:workspaceId/memberships",
method: "GET", method: "GET",
schema: { schema: {
description: "Return project user memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim() workspaceId: z.string().trim()
}), }),
@@ -35,6 +42,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
const memberships = await server.services.projectMembership.getProjectMemberships({ const memberships = await server.services.projectMembership.getProjectMemberships({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { memberships }; return { memberships };
@@ -70,6 +78,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
const data = await server.services.projectMembership.addUsersToProject({ const data = await server.services.projectMembership.addUsersToProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
members: req.body.members members: req.body.members
}); });
@@ -94,6 +103,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships/:membershipId", url: "/:workspaceId/memberships/:membershipId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
membershipId: z.string().trim() membershipId: z.string().trim()
@@ -112,6 +128,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
const membership = await server.services.projectMembership.updateProjectMembership({ const membership = await server.services.projectMembership.updateProjectMembership({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
membershipId: req.params.membershipId, membershipId: req.params.membershipId,
role: req.body.role role: req.body.role
@@ -138,6 +155,13 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
url: "/:workspaceId/memberships/:membershipId", url: "/:workspaceId/memberships/:membershipId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete project user membership",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
membershipId: z.string().trim() membershipId: z.string().trim()
@@ -153,6 +177,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider
const membership = await server.services.projectMembership.deleteProjectMembership({ const membership = await server.services.projectMembership.deleteProjectMembership({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
membershipId: req.params.membershipId membershipId: req.params.membershipId
}); });

View File

@@ -46,6 +46,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const publicKeys = await server.services.projectKey.getProjectPublicKeys({ const publicKeys = await server.services.projectKey.getProjectPublicKeys({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { publicKeys }; return { publicKeys };
@@ -81,7 +82,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const users = await server.services.projectMembership.getProjectMemberships({ const users = await server.services.projectMembership.getProjectMemberships({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
projectId: req.params.workspaceId projectId: req.params.workspaceId,
actorOrgId: req.permission.orgId
}); });
return { users }; return { users };
} }
@@ -122,6 +124,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const workspace = await server.services.project.getAProject({ const workspace = await server.services.project.getAProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { workspace }; return { workspace };
@@ -148,6 +151,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
orgId: req.body.organizationId, orgId: req.body.organizationId,
actorOrgId: req.permission.orgId,
workspaceName: req.body.workspaceName workspaceName: req.body.workspaceName
}); });
return { workspace }; return { workspace };
@@ -172,6 +176,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const workspace = await server.services.project.deleteProject({ const workspace = await server.services.project.deleteProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { workspace }; return { workspace };
@@ -200,6 +205,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const workspace = await server.services.project.updateName({ const workspace = await server.services.project.updateName({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
name: req.body.name name: req.body.name
}); });
@@ -232,6 +238,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const workspace = await server.services.project.toggleAutoCapitalization({ const workspace = await server.services.project.toggleAutoCapitalization({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
autoCapitalization: req.body.autoCapitalization autoCapitalization: req.body.autoCapitalization
}); });
@@ -264,6 +271,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const { invitee, latestKey } = await server.services.projectMembership.inviteUserToProject({ const { invitee, latestKey } = await server.services.projectMembership.inviteUserToProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId, projectId: req.params.workspaceId,
email: req.body.email email: req.body.email
}); });
@@ -309,6 +317,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const integrations = await server.services.integration.listIntegrationByProject({ const integrations = await server.services.integration.listIntegrationByProject({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { integrations }; return { integrations };
@@ -333,6 +342,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({ const authorizations = await server.services.integrationAuth.listIntegrationAuthByProjectId({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { authorizations }; return { authorizations };
@@ -357,6 +367,7 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
const serviceTokenData = await server.services.serviceToken.getProjectServiceTokens({ const serviceTokenData = await server.services.serviceToken.getProjectServiceTokens({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
projectId: req.params.workspaceId projectId: req.params.workspaceId
}); });
return { serviceTokenData }; return { serviceTokenData };

View File

@@ -11,6 +11,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "POST", method: "POST",
schema: { schema: {
description: "Create folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -31,6 +38,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
const folder = await server.services.folder.createFolder({ const folder = await server.services.folder.createFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
path path
@@ -56,6 +64,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/:folderId", url: "/:folderId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
// old way this was name // old way this was name
folderId: z.string() folderId: z.string()
@@ -80,6 +95,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
const { folder, old } = await server.services.folder.updateFolder({ const { folder, old } = await server.services.folder.updateFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
id: req.params.folderId, id: req.params.folderId,
@@ -107,6 +123,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/:folderId", url: "/:folderId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete a folder",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
folderId: z.string() folderId: z.string()
}), }),
@@ -129,6 +152,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
const folder = await server.services.folder.deleteFolder({ const folder = await server.services.folder.deleteFolder({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
id: req.params.folderId, id: req.params.folderId,
@@ -155,6 +179,13 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "GET", method: "GET",
schema: { schema: {
description: "Get folders",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -174,6 +205,7 @@ export const registerSecretFolderRouter = async (server: FastifyZodProvider) =>
const folders = await server.services.folder.getFolders({ const folders = await server.services.folder.getFolders({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId, projectId: req.query.workspaceId,
path path

View File

@@ -11,6 +11,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "POST", method: "POST",
schema: { schema: {
description: "Create secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
body: z.object({ body: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -36,6 +43,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
const secretImport = await server.services.secretImport.createImport({ const secretImport = await server.services.secretImport.createImport({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
data: req.body.import data: req.body.import
@@ -64,6 +72,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/:secretImportId", url: "/:secretImportId",
method: "PATCH", method: "PATCH",
schema: { schema: {
description: "Update secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretImportId: z.string().trim() secretImportId: z.string().trim()
}), }),
@@ -97,6 +112,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
const secretImport = await server.services.secretImport.updateImport({ const secretImport = await server.services.secretImport.updateImport({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body, ...req.body,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
@@ -126,6 +142,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/:secretImportId", url: "/:secretImportId",
method: "DELETE", method: "DELETE",
schema: { schema: {
description: "Delete secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
secretImportId: z.string().trim() secretImportId: z.string().trim()
}), }),
@@ -150,6 +173,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
const secretImport = await server.services.secretImport.deleteImport({ const secretImport = await server.services.secretImport.deleteImport({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
id: req.params.secretImportId, id: req.params.secretImportId,
...req.body, ...req.body,
projectId: req.body.workspaceId projectId: req.body.workspaceId
@@ -178,6 +202,13 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
url: "/", url: "/",
method: "GET", method: "GET",
schema: { schema: {
description: "Get secret imports",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
querystring: z.object({ querystring: z.object({
workspaceId: z.string().trim(), workspaceId: z.string().trim(),
environment: z.string().trim(), environment: z.string().trim(),
@@ -201,6 +232,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
const secretImports = await server.services.secretImport.getImports({ const secretImports = await server.services.secretImport.getImports({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });
@@ -253,6 +285,7 @@ export const registerSecretImportRouter = async (server: FastifyZodProvider) =>
const importedSecrets = await server.services.secretImport.getSecretsFromImports({ const importedSecrets = await server.services.secretImport.getSecretsFromImports({
actorId: req.permission.id, actorId: req.permission.id,
actor: req.permission.type, actor: req.permission.type,
actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });

View File

@@ -23,6 +23,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
const workspaceTags = await server.services.secretTag.getProjectTags({ const workspaceTags = await server.services.secretTag.getProjectTags({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId projectId: req.params.projectId
}); });
return { workspaceTags }; return { workspaceTags };
@@ -52,6 +53,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
const workspaceTag = await server.services.secretTag.createTag({ const workspaceTag = await server.services.secretTag.createTag({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId, projectId: req.params.projectId,
...req.body ...req.body
}); });
@@ -78,6 +80,7 @@ export const registerSecretTagRouter = async (server: FastifyZodProvider) => {
const workspaceTag = await server.services.secretTag.deleteTag({ const workspaceTag = await server.services.secretTag.deleteTag({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.tagId id: req.params.tagId
}); });
return { workspaceTag }; return { workspaceTag };

View File

@@ -42,7 +42,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
async (req, _accessToken, _refreshToken, profile, cb) => { async (req, _accessToken, _refreshToken, profile, cb) => {
try { try {
const email = profile?.emails?.[0]?.value; const email = profile?.emails?.[0]?.value;
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
if (!email) if (!email)
throw new BadRequestError({ throw new BadRequestError({
message: "Email not found", message: "Email not found",
@@ -84,7 +84,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
try { try {
const ghEmails = await fetchGithubEmails(accessToken); const ghEmails = await fetchGithubEmails(accessToken);
const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0]; const { email } = ghEmails.filter((gitHubEmail) => gitHubEmail.primary)[0];
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
email, email,
firstName: profile.displayName, firstName: profile.displayName,
@@ -120,7 +120,7 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => { async (req: any, _accessToken: string, _refreshToken: string, profile: any, cb: any) => {
try { try {
const email = profile.emails[0].value; const email = profile.emails[0].value;
const serverCfg = getServerCfg(); const serverCfg = await getServerCfg();
const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({ const { isUserCompleted, providerAuthToken } = await server.services.login.oauth2Login({
email, email,
firstName: profile.displayName, firstName: profile.displayName,

View File

@@ -47,6 +47,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
const webhook = await server.services.webhook.createWebhook({ const webhook = await server.services.webhook.createWebhook({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.body.workspaceId, projectId: req.body.workspaceId,
...req.body ...req.body
}); });
@@ -92,6 +93,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
const webhook = await server.services.webhook.updateWebhook({ const webhook = await server.services.webhook.updateWebhook({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.webhookId, id: req.params.webhookId,
isDisabled: req.body.isDisabled isDisabled: req.body.isDisabled
}); });
@@ -128,6 +130,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
const webhook = await server.services.webhook.deleteWebhook({ const webhook = await server.services.webhook.deleteWebhook({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.webhookId id: req.params.webhookId
}); });
@@ -169,6 +172,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
const webhook = await server.services.webhook.testWebhook({ const webhook = await server.services.webhook.testWebhook({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
id: req.params.webhookId id: req.params.webhookId
}); });
return { message: "Successfully tested webhook", webhook }; return { message: "Successfully tested webhook", webhook };
@@ -200,6 +204,7 @@ export const registerWebhookRouter = async (server: FastifyZodProvider) => {
const webhooks = await server.services.webhook.listWebhooks({ const webhooks = await server.services.webhook.listWebhooks({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
...req.query, ...req.query,
projectId: req.query.workspaceId projectId: req.query.workspaceId
}); });

View File

@@ -10,6 +10,13 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
url: "/:orgId/identity-memberships", url: "/:orgId/identity-memberships",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Return organization identity memberships",
security: [
{
bearerAuth: [],
apiKeyAuth: []
}
],
params: z.object({ params: z.object({
orgId: z.string().trim() orgId: z.string().trim()
}), }),
@@ -34,6 +41,7 @@ export const registerIdentityOrgRouter = async (server: FastifyZodProvider) => {
const identityMemberships = await server.services.identity.listOrgIdentities({ const identityMemberships = await server.services.identity.listOrgIdentities({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
orgId: req.params.orgId orgId: req.params.orgId
}); });
return { identityMemberships }; return { identityMemberships };

View File

@@ -32,6 +32,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
const identityMembership = await server.services.identityProject.createProjectIdentity({ const identityMembership = await server.services.identityProject.createProjectIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId, identityId: req.params.identityId,
projectId: req.params.projectId, projectId: req.params.projectId,
role: req.body.role role: req.body.role
@@ -45,6 +46,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships/:identityId", url: "/:projectId/identity-memberships/:identityId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Update project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim(), projectId: z.string().trim(),
identityId: z.string().trim() identityId: z.string().trim()
@@ -62,6 +69,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
const identityMembership = await server.services.identityProject.updateProjectIdentity({ const identityMembership = await server.services.identityProject.updateProjectIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId, identityId: req.params.identityId,
projectId: req.params.projectId, projectId: req.params.projectId,
role: req.body.role role: req.body.role
@@ -75,6 +83,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships/:identityId", url: "/:projectId/identity-memberships/:identityId",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Delete project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim(), projectId: z.string().trim(),
identityId: z.string().trim() identityId: z.string().trim()
@@ -89,6 +103,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
const identityMembership = await server.services.identityProject.deleteProjectIdentity({ const identityMembership = await server.services.identityProject.deleteProjectIdentity({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
identityId: req.params.identityId, identityId: req.params.identityId,
projectId: req.params.projectId projectId: req.params.projectId
}); });
@@ -101,6 +116,12 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
url: "/:projectId/identity-memberships", url: "/:projectId/identity-memberships",
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
schema: { schema: {
description: "Return project identity memberships",
security: [
{
bearerAuth: []
}
],
params: z.object({ params: z.object({
projectId: z.string().trim() projectId: z.string().trim()
}), }),
@@ -125,6 +146,7 @@ export const registerIdentityProjectRouter = async (server: FastifyZodProvider)
const identityMemberships = await server.services.identityProject.listProjectIdentities({ const identityMemberships = await server.services.identityProject.listProjectIdentities({
actor: req.permission.type, actor: req.permission.type,
actorId: req.permission.id, actorId: req.permission.id,
actorOrgId: req.permission.orgId,
projectId: req.params.projectId projectId: req.params.projectId
}); });
return { identityMemberships }; return { identityMemberships };

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