Compare commits

..

1578 Commits

Author SHA1 Message Date
625fa0725e add approvals-needed and sent api 2023-02-26 19:05:36 -05:00
e32cb8c24f add index for requestedChanges.approvers.userId 2023-02-26 19:04:44 -05:00
de724d2804 improve add/remove approvers into workspace 2023-02-26 18:29:35 -05:00
42d94521a4 extend Secres schema in requestedChangeSchema 2023-02-26 15:15:41 -05:00
203a603769 complete merge API and improve reject/approve api 2023-02-26 15:15:41 -05:00
e1a88b2d1a add method to check required secret fields 2023-02-26 15:15:41 -05:00
53237dd52c merge approval requests 2023-02-26 15:15:41 -05:00
6a906b17ad add approvals request reject, approve and merge api 2023-02-26 15:15:41 -05:00
f38a364d3b add approve request api 2023-02-26 15:15:41 -05:00
4749e243bb get approvals by requester & improve create request 2023-02-26 15:15:41 -05:00
eb055e8b16 create approval 2023-02-26 15:15:41 -05:00
0e17c9a6db Merge pull request #383 from 5h4k4r/use-multi-stage-build
Builds Docker image of backend component with multi-stage Dockerfile
2023-02-26 10:50:56 -05:00
1c4dd78dea use node alpine base image 2023-02-26 10:49:01 -05:00
23418b3a09 Builds Docker image of backend component with multi-stage Dockerfile
Updates package.json `npm start` command
Delete unnecessary `npm run start` commands

Signed-off-by: Shakar <5h4k4r.b4kr@gmail.com>
2023-02-26 14:50:49 +03:00
0f143adbde Merge pull request #377 from caioluis/main
feat(webui localization): add support for pt-PT
2023-02-25 12:01:51 -08:00
1f3f4b7900 Merge pull request #353 from Grraahaam/chore/helm-docs
chore(docs): helm charts + local cluster setup script
2023-02-25 13:25:47 -05:00
2c5f26380e Merge pull request #373 from jon4hz/default-env
fix: properly support default environment
2023-02-25 13:08:00 -05:00
8f974fb087 Add docs for default environment 2023-02-25 13:07:07 -05:00
a0722b4ca5 Merge pull request #372 from jon4hz/format-cfg
fix: pretty print workspace file
2023-02-25 12:42:46 -05:00
41e039578a Merge pull request #374 from jon4hz/shell
fix: always execute cmd in subshell
2023-02-25 12:39:08 -05:00
c89e8e8a96 Merge pull request #375 from jon4hz/workspace
feat: search for workspace config in parent dir
2023-02-25 11:27:34 -05:00
cac83ab927 add debug log to workspace path location 2023-02-25 11:25:49 -05:00
0f0b894363 Revert "feat(webui-localization): fix and update pt-br copies"
I was dizzy and forgot to make a branch

This reverts commit 43f9af1bc6d27b0e6fc477535f5a17d963df6b51.

Signed-off-by: Caio Gomes <ocaioluis@gmail.com>
2023-02-25 08:05:43 +00:00
43f9af1bc6 feat(webui-localization): fix and update pt-br copies 2023-02-25 08:04:02 +00:00
f5ed14c84c fix(localization): add pt-PT to the options and fix a copy 2023-02-25 07:22:34 +00:00
2dd57d7c73 feat(localization): add locales for pt-PT 2023-02-25 06:53:02 +00:00
0b1891b64a Merge pull request #371 from jon4hz/env-filter
Improve env filter
2023-02-24 23:12:20 -05:00
5614b0f58a nit: method name change 2023-02-24 22:21:20 -05:00
3bb178976d Remove auto delete index 2023-02-24 21:57:13 -05:00
1777f98aef feat: search for workspace config in parent dir 2023-02-25 01:25:39 +01:00
45e3706335 fix: always execute cmd in subshell 2023-02-25 00:19:58 +01:00
337ed1fc46 fix: properly support default environment 2023-02-24 23:20:30 +01:00
d1ea76e5a0 fix: pretty print workspace file 2023-02-24 22:28:28 +01:00
4a72d725b1 fix: use function to get secrets by key 2023-02-24 22:03:50 +01:00
1693db3199 fix: preallocate map size 2023-02-24 22:03:13 +01:00
1ff42991b3 fix: improve filtering of reserved env vars 2023-02-24 21:57:48 +01:00
978423ba5b Merge pull request #364 from alexdanilowicz/patch-3
chore(docs): Update README MIT License badge link
2023-02-24 11:41:28 -08:00
4d0dc0d7b7 Update README.md 2023-02-24 11:40:03 -08:00
3817e666a9 Merge pull request #365 from Infisical/travisci
Add Travis CI Docs
2023-02-24 16:44:15 +07:00
b61350f6a4 Add Travis CI docs, minor edits 2023-02-24 16:37:26 +07:00
0fb1a1dc6f Merge remote-tracking branch 'origin' into travisci 2023-02-24 16:08:53 +07:00
9eefc87b7a Merge pull request #350 from Aashish-Upadhyay-101/Aashish-Upadhyay-101/TravisCI-integration
Feat: travis ci integration
2023-02-24 16:08:19 +07:00
53d35757ee Make minor changes to TravisCI sync, faster, reliable 2023-02-24 15:59:12 +07:00
e80e8e00b1 Replace axios with request 2023-02-24 15:31:46 +07:00
0b08e574c7 Merge remote-tracking branch 'refs/remotes/origin/main' into Aashish-Upadhyay-101/TravisCI-integration 2023-02-24 13:36:35 +05:45
499323d0e3 Sync travis-ci 2023-02-24 13:36:17 +05:45
89ad2f163a chore(docs): README
Another small README typo that I noticed. Links to a different repo.
2023-02-23 23:27:28 -08:00
7f04617b7d update brew command to update cli 2023-02-23 19:37:19 -05:00
44904628bc Merge pull request #360 from bngmnn/main
add german as readme language
2023-02-23 15:10:44 -08:00
fafde7b1ad update other languages' readme files with 'de' flag 2023-02-23 23:56:48 +01:00
7e65314670 add german readme 2023-02-23 23:56:17 +01:00
df52c56e83 patch login bug in cli 2023-02-23 14:14:41 -05:00
4276fb54cc Add docs for git branch mapping 2023-02-23 13:07:18 -05:00
bb5a0db79c Merge pull request #359 from Infisical/axios-retry
Add axios-retry for Vercel integration for now
2023-02-24 00:59:29 +07:00
b906048ea1 Add axios-retry for Vercel integration for now 2023-02-24 00:52:43 +07:00
7ce9c816c5 Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-23 21:38:21 +07:00
3fef6e4849 Replace Promise.all with for-await Vercel getting envars 2023-02-23 21:38:11 +07:00
e7ce1e36e7 Update README.md 2023-02-23 06:25:48 -08:00
734c915206 Merge pull request #358 from alisson-acioli/main
Readme translation to PT-BR.
2023-02-23 06:23:35 -08:00
783174adc6 Add for-await for better Vercel integration reliability 2023-02-23 20:21:17 +07:00
d769db7668 integrações world 2023-02-23 09:52:58 -03:00
00e532fce4 remove english 2023-02-23 09:52:08 -03:00
7cf8cba54b remove word 2023-02-23 09:51:30 -03:00
70b26811d9 Remove word API 2023-02-23 09:50:13 -03:00
e7aafecbc2 Update language readme options 2023-02-23 09:49:03 -03:00
949fb052cd Readme PT-BR 2023-02-23 09:45:58 -03:00
fcb1f5a51b Merge pull request #357 from Infisical/vercel-integration-patch
Patch Vercel case where secrets can be of type plain and sensitive
2023-02-23 16:53:44 +07:00
e24f70b891 Patch Vercel case where secrets can be of type plain and sensitive 2023-02-23 16:47:21 +07:00
bd233ebe9b allow git branch mapping to env 2023-02-23 00:00:56 -05:00
f92269f2ec Merge pull request #274 from Grraahaam/feat/pr-template
feat(docs): added a base pull request template
2023-02-23 11:16:19 +07:00
2143db5eb5 Update messaging 2023-02-22 17:21:44 -08:00
0c72f50b5e Updated readme's 2023-02-22 16:29:53 -08:00
3c4c616242 update 2fa prompt in cli 2023-02-22 18:52:09 -05:00
153baad49f Merge pull request #349 from ImBIOS/docs/translation-indonesia
translate: add Bahasa Indonesia for README
2023-02-22 15:22:17 -08:00
75a2ab636c Merge pull request #354 from akhilmhdh/feat/table-loader
Table loading state and empty states
2023-02-22 14:57:23 -08:00
05a77e612c Minor style updates 2023-02-22 14:54:59 -08:00
d02bc06dce Merge pull request #355 from alexdanilowicz/patch-2
docs: fix nit typos in README
2023-02-22 14:54:53 -05:00
e1f88f1a7b docs: fix nit typos in README
Fix 'development' typo in sub header of README and do not hyphenate use-cases.
2023-02-22 11:51:41 -08:00
86a2647134 feat(ui): added table skeleton and loading for settings page,
fix(ui): resolved missing loading state in add new member and whitespace in project settings page
2023-02-22 23:29:26 +05:30
621b640af4 feat(ui): added new components empty state and skeleton 2023-02-22 23:27:36 +05:30
40c80f417c chore(script): added warning 2023-02-22 10:30:33 +01:00
7bb2c1c278 fix(script): auto generate secrets at runtime 2023-02-22 10:25:08 +01:00
a5278affe6 chore(docs): improved charts related documentation 2023-02-22 10:18:22 +01:00
2f953192d6 feat(script): kind local development setup 2023-02-22 10:18:22 +01:00
af64582efd Merge pull request #352 from Infisical/socket-labs-smtp
Add support and docs for SocketLabs email SMTP
2023-02-22 15:47:53 +07:00
6ad70f24a2 Add support and docs for SocketLabs email SMTP 2023-02-22 15:44:47 +07:00
8bf8968588 Other frontend configurations for travis-ci 2023-02-22 13:36:22 +05:45
7e9ce0360a Create.tsx for travis-ci 2023-02-22 13:26:10 +05:45
1d35c41dcb Authorize.tsx for travis-ci 2023-02-22 13:20:34 +05:45
824315f773 model steup for travis-ci 2023-02-22 13:14:06 +05:45
8a74799d64 variable setup for travis-ci 2023-02-22 12:52:42 +05:45
f0f6e8a988 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-22 12:29:49 +05:45
89bc9a823c translate: add Bahasa Indonesia for README 2023-02-22 12:03:57 +07:00
40250b7ecf Merge pull request #344 from Infisical/ip-alerts
New device (IP and user agent) login detection and alert
2023-02-21 18:11:16 +07:00
2d6d32923d Finish alert for new device login detection 2023-02-21 18:01:26 +07:00
7cb6aee3f7 Add docs for MFA 2023-02-21 16:08:09 +07:00
469d042f4b Add CircleCI docs 2023-02-21 13:07:17 +07:00
c38ccdb915 Merge pull request #343 from Infisical/mfa
MFA
2023-02-21 12:47:56 +07:00
baaa92427f Remove dependency cycle 2023-02-21 12:43:17 +07:00
1ff2c61b3a Remove storage of protected key 2023-02-21 12:31:19 +07:00
0b356e0e83 Update README.md 2023-02-20 21:14:50 -08:00
eb55c053eb Merge pull request #341 from umrak11/feature/improve-backup-pdf-generation
Refactored and improved PDF backup generation
2023-02-20 20:56:15 -08:00
07b307e4b1 Merge pull request #333 from esau-morais/issue-318
Add missing `forgot-password` in pt-BR and save selected env on URL
2023-02-20 20:44:27 -08:00
5bee6a5e24 Merge branch 'issue-318' of https://github.com/esau-morais/infisical into issue-318 2023-02-20 20:41:00 -08:00
bdc99e34cc Fix TS issue 2023-02-20 20:40:32 -08:00
cee10fb507 Merge branch 'main' into issue-318 2023-02-20 20:36:44 -08:00
74e78bb967 Merge remote-tracking branch 'origin' into mfa 2023-02-21 11:31:17 +07:00
ea5811c24c Fixed the bug with the tab indicators 2023-02-20 20:30:03 -08:00
d31b7ae4af Merge pull request #335 from animeshdas2000/fix/undefined-url
Fix: After trying to delete the last remaining project, it keeps loading and returns undefined in the URL
2023-02-20 19:54:41 -08:00
75eac1b972 Add LoginSRPDetail to v2 auth route 2023-02-21 10:52:31 +07:00
c65ce14de3 Merge branch 'main' into fix/undefined-url 2023-02-20 19:49:04 -08:00
f8c4ccd64c Updated readme 2023-02-20 19:17:04 -08:00
43ce222725 Minor style updates 2023-02-20 18:12:29 -08:00
c7ebeecb6b Merge pull request #337 from akhilmhdh/feat/new-org-settings
Revamped the org settings page
2023-02-20 17:25:27 -08:00
243c6ca22e feat: changed delete org membership to v2 2023-02-20 23:22:34 +05:30
66f1c57a2a feat(ui): completed org settings page revamp 2023-02-20 23:22:34 +05:30
c0d1495761 feat(ui): api hooks for new org settings page 2023-02-20 23:22:34 +05:30
e5f6ed3dc7 feat(ui): components changes for new org settings page 2023-02-20 23:22:32 +05:30
ab62d91b09 Refactored and improved PDF backup generation 2023-02-20 10:12:38 +01:00
59beabb445 Fix change password private key removal 2023-02-20 13:27:16 +07:00
d5bc377e3d Added notifications to 2FA and fixed state 2023-02-19 20:11:24 -08:00
2bdb20f42f Merge pull request #339 from Infisical/mfa
MFA
2023-02-19 18:33:11 -05:00
0062df58a2 Fixed dashboard for the corner case of purely personal secrets 2023-02-19 12:02:30 -08:00
b6bbfc08ad increase helm chart 2023-02-19 12:52:28 -05:00
5baccc73c9 Add mongo root password 2023-02-19 12:52:28 -05:00
20e7eae4fe Add more Accept-Encoding to integrations syncs 2023-02-19 13:42:37 +07:00
8432f71d58 add secrets auto reload 2023-02-19 00:44:38 -05:00
604c22d64d Merge remote-tracking branch 'origin' into mfa 2023-02-19 12:41:23 +07:00
c1deb08df8 always pull gamma iamge 2023-02-19 00:08:07 -05:00
66f201746f update gamma values 2023-02-19 00:06:38 -05:00
1c61ffbd36 patch upload script 2023-02-19 00:00:58 -05:00
e5ba8eb281 fix upload script 2023-02-18 23:50:35 -05:00
f542e07c33 download dependency for helm chart in upload step 2023-02-18 23:40:10 -05:00
1082d7f869 update helm docs for self install 2023-02-18 23:28:52 -05:00
4a3adaa347 Begin in-memory privat key storage 2023-02-19 10:54:54 +07:00
1659dab87d Merge pull request #314 from Grraahaam/feat/helm-mongodb-persistence
feat(chart): mongodb persistence
2023-02-18 18:06:39 -05:00
d88599714f update helm chart to refect dep update 2023-02-18 11:33:39 -05:00
71bf56a2b7 Update k8 operator dependencies 2023-02-18 11:32:46 -05:00
0fba78ad16 Update http net package 2023-02-18 10:16:27 -05:00
92560f5e1f Merge remote-tracking branch 'origin' into mfa 2023-02-18 16:49:51 +07:00
0d484b93eb Merge pull request #338 from Infisical/smoothen-integrations
Smoothen integrations
2023-02-18 16:30:56 +07:00
5f3b8c55b8 Merge branch 'main' into smoothen-integrations 2023-02-18 16:27:58 +07:00
553416689c Fix merge conflicts with main 2023-02-18 16:23:05 +07:00
b0744fd21d Wired frontend to use the batch stucture 2023-02-18 00:02:52 -08:00
be38844a5b Add 2FA in CLI 2023-02-17 20:48:19 -05:00
54e2b661bc ran prettier to fix indentation 2023-02-17 14:20:58 +05:30
b81d8eba25 added notification to .env errors 2023-02-16 21:05:38 -08:00
dbcd2b0988 Patch undefined req.secrets 2023-02-17 11:56:06 +07:00
1d11f11eaf Refactor login cmd 2023-02-16 15:49:14 -08:00
f2d7401d1d Add support for version 2 auth 2023-02-16 15:47:59 -08:00
91cb9750b4 Merge branch 'main' into fix/undefined-url 2023-02-17 00:33:55 +05:30
3e0d4cb70a fixed infinite loading in dashboard 2023-02-17 00:31:00 +05:30
dab677b360 Merge branch 'smoothen-integrations' of https://github.com/Infisical/infisical into smoothen-integrations 2023-02-17 01:18:31 +07:00
625c0785b5 Add validation to batch secret endpoint 2023-02-17 01:12:13 +07:00
540a8b4201 Fixed the bug with updating tags 2023-02-16 09:53:33 -08:00
11f86da1f6 Fixed the bug with updating tags 2023-02-16 09:35:57 -08:00
ab5ffa9ee6 Updated billing in the settings tab 2023-02-16 08:13:49 -08:00
65bec23292 Begin rewiring frontend to use batch route for CRUD secret ops 2023-02-16 22:43:53 +07:00
635ae941d7 Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-15 23:56:33 -08:00
a9753fb784 Update the text in start guide 2023-02-15 23:56:16 -08:00
b587d9b35a Update README.md 2023-02-15 23:03:51 -08:00
aa68bc05d9 Updated 'are you sure?' message 2023-02-15 13:46:13 -08:00
66566a401f Updated the infisical guide 2023-02-15 13:23:14 -08:00
5aa75ecd3f feat: save selected env on url 2023-02-15 12:46:54 -03:00
0a77f9a0c8 fix(i18n): add missing forgot-password in pt-BR 2023-02-15 11:06:48 -03:00
b5d4cfed03 chore(docs): chart documentation generation 2023-02-15 09:43:17 +01:00
c57394bdab Merge remote-tracking branch 'origin' into mfa 2023-02-15 11:44:42 +07:00
da857f321b allow to set default env in project file 2023-02-14 19:57:27 -08:00
754ea09400 Add simple tries left for MFA 2023-02-15 10:28:23 +07:00
f28a2ea151 Small nits 2023-02-14 18:40:02 -08:00
c7dd028771 Update README.md 2023-02-14 18:34:41 -08:00
3c94bacda9 Update README.md 2023-02-14 18:31:45 -08:00
8e85847de3 improve example .env file format 2023-02-14 17:33:24 -08:00
0c10bbb569 send error message to standard error out 2023-02-14 13:43:40 -08:00
b710944630 Add more edge-cases to MFA 2023-02-15 01:29:40 +07:00
280f482fc8 Fix merge conflicts 2023-02-14 17:40:42 +07:00
e1ad8fbee8 Refactoring functions into services, helper functions, hooks, patch bugs 2023-02-14 17:38:58 +07:00
56ca6039ba fix(chart): backend service typos 2023-02-14 10:50:34 +01:00
fba54ae0c6 Add tags query to secrets api 2023-02-13 22:28:59 -08:00
e243c72ca6 add tags flag to secrets related command 2023-02-13 22:28:30 -08:00
23ea6fd4f9 filter secrets by tags 2023-02-13 20:51:43 -08:00
3f9f2ef238 Merge pull request #329 from fervillarrealm/feature/52-save-changes-user-leaving-dashboard
feat(ui): save changes when user leaving dashboard
2023-02-13 19:47:10 -08:00
77cb20f5c7 Fixed a TS error 2023-02-13 19:44:18 -08:00
ddf630c269 Fixed a TS error 2023-02-13 19:00:43 -08:00
39adb9a0c2 Merge pull request #328 from akhilmhdh/feat/ui-improvements
feat(ui): add new button style, improved select ui and linted app layout
2023-02-13 17:44:41 -08:00
97fde96b7b Merge branch 'main' into feat/ui-improvements 2023-02-13 17:33:23 -08:00
190391e493 Fixed bugs with organizations and sidebars 2023-02-13 17:27:21 -08:00
d3fcb69c50 fix(chart): backend service missing configuration 2023-02-14 01:18:41 +01:00
2db4a29ad7 chore(docs): chart setup + chart release notes.txt 2023-02-14 01:18:41 +01:00
4df82a6ff1 feat(chart): mailhog for local development 2023-02-14 01:18:41 +01:00
cdf73043e1 fix(chart): mongodb custom users + docs 2023-02-14 01:18:41 +01:00
ca07d1c50e fix(chart): helpers template auth.password typo 2023-02-14 01:18:41 +01:00
868011479b feat(chart): mongodb persistence 2023-02-14 01:18:41 +01:00
6f6df3e63a Update approverSchema 2023-02-13 11:27:02 -08:00
23c740d225 setHasUnsavedChanges to false when user selects another env and they agree to not save changes 2023-02-13 11:07:22 -06:00
702d4de3b5 feature/52-save-changes-user-leaving-dashboard 2023-02-13 10:18:52 -06:00
445fa35ab5 Add Aashish to README 2023-02-13 18:55:09 +07:00
9868476965 Merge pull request #298 from Aashish-Upadhyay-101/circleci-integration-branch
Circleci integration branch
2023-02-13 18:03:22 +07:00
bfa6b955ca handleAuthorizedIntegrationOptionPress case circleci 2023-02-13 16:29:06 +05:45
13b1805d04 Checkpoint 2 2023-02-13 17:11:31 +07:00
c233fd8ed1 Merge remote-tracking branch 'refs/remotes/origin/main' 2023-02-13 15:39:38 +05:45
90f5934440 projects displaying issue fixed using circleci v1.1 2023-02-13 15:32:37 +05:45
30b2b85446 Fix merge conflicts 2023-02-13 15:24:19 +07:00
0adc3d2027 create secret approval data model 2023-02-12 23:20:27 -08:00
e53fd110f6 Checkpoint weaving frontend and backend MFA 2023-02-13 12:29:36 +07:00
edf0294d51 Remove docker fine cli 2023-02-12 19:59:40 -08:00
8850b44115 Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-12 17:54:55 -08:00
17f9e53779 Updated the dashabord, members, and settings pages 2023-02-12 17:54:22 -08:00
a61233d2ba Release docker images for cli 2023-02-12 14:22:59 -08:00
2022988e77 Only allow sign up when invted 2023-02-12 10:34:32 -08:00
409de81bd2 Allow sign up disable 2023-02-12 09:34:52 -08:00
2b289ddf77 feat(ui): add new button style, improved select ui and linted app layout 2023-02-12 17:31:22 +05:30
b066a55ead Show only secret keys if write only access 2023-02-11 23:41:51 -08:00
8dfc0138f5 circleci project name issue fixed 2023-02-12 09:41:34 +05:45
517f508e44 circleci Current Integrations section error fixed 2023-02-12 08:32:04 +05:45
2f1a671121 add workspace-memberships api 2023-02-11 15:16:33 -08:00
2fb4b261a8 Turn off auto delete and manual check ttl for token 2023-02-11 11:08:46 -08:00
9c3c745fdf small changes 2023-02-11 18:10:58 +05:45
6a75147719 circleci-done 2023-02-11 17:57:01 +05:45
295b363d8a Merge remote-tracking branch 'refs/remotes/origin/main' into circleci-integration-branch 2023-02-11 17:55:59 +05:45
d96b5943b9 circleci integration create.jsx and authorize.jsx created 2023-02-11 17:08:03 +05:45
17406e413d Merge remote-tracking branch 'origin/2fa' into mfa 2023-02-11 16:08:13 +07:00
9b219f67b0 Update package.json 2023-02-11 16:07:32 +07:00
8fd2578a6d fixed the bug with no projects 2023-02-10 23:16:37 -08:00
cc809a6bc0 Merge pull request #315 from akhilmhdh/feat/new-layout
Feat new layout synced with api changes
2023-02-10 22:20:11 -08:00
66659c8fc8 Bug/typo/style fixes and some minor improvements 2023-02-10 22:17:39 -08:00
31293bbe06 Remove tags from secrets when tag is deleted 2023-02-10 19:33:40 -08:00
1c3488f8db add reset infisical docs 2023-02-10 17:41:31 -08:00
20e536cec0 Remove printing pathToDir 2023-02-10 17:25:01 -08:00
e8b498ca6d Minor style tweaks 2023-02-10 16:45:31 -08:00
b82f8606a8 add ValidateEnvironmentName method 2023-02-10 15:08:12 -08:00
ab27fbccf7 add reset command 2023-02-10 14:19:04 -08:00
d50de9366b Add docs for generate-example-env command 2023-02-10 12:29:47 -08:00
4c56bca4e7 Remove newline after heading in .sample-env 2023-02-10 12:24:29 -08:00
a60774a3f4 Merge pull request #327 from Infisical/parameter-store
Add support and docs for AWS parameter store and secret manager
2023-02-11 01:52:29 +07:00
03426ee7f2 Fix lint errors 2023-02-11 01:49:53 +07:00
428022d1a2 Add support and docs for AWS parameter store and secret manager 2023-02-11 01:40:18 +07:00
b5bcd0a308 feat(ui): updated merge conflicts in layout with new design 2023-02-10 22:09:22 +05:30
03c72ea00f feat(ui): added back layout change made for integrations page 2023-02-10 22:06:02 +05:30
a486390015 feat(ui): added new layout 2023-02-10 22:05:59 +05:30
8dc47110a0 feat(ui): added org context and user context 2023-02-10 22:03:57 +05:30
52a6fe64a7 feat(ui): new layout added queries 2023-02-10 22:03:57 +05:30
081ef94399 hard code site url frontend 2023-02-09 22:49:58 -08:00
eebde3ad12 Updated env variables and emails 2023-02-09 22:27:30 -08:00
669861d7a8 General frontend structure for 2FA - done 2023-02-09 15:49:47 -08:00
6ab6147ac8 Fixed service token bug 2023-02-09 13:40:33 -08:00
dd7e8d254b Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-09 18:24:23 +07:00
2765f7e488 Fix Vercel get apps response encoding 2023-02-09 18:24:10 +07:00
2d3a276dc2 Merge pull request #309 from RashidUjang/fix/issue-308-sidebar-issue
fix: handle duplicate edge case for sidebar loading
2023-02-08 23:50:41 -08:00
55eddee6ce Returned back @RashidUjang's change with secretIds 2023-02-08 23:48:25 -08:00
ab751d0db3 Merge branch 'main' into fix/issue-308-sidebar-issue 2023-02-08 23:42:46 -08:00
b2bd0ba340 Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-08 23:38:25 -08:00
224fa25fdf Minor style fixes 2023-02-08 23:38:00 -08:00
e6539a5566 Merge remote-tracking branch 'refs/remotes/origin/main' into circleci-integration-branch 2023-02-09 13:16:43 +05:45
6115a311ad Merge pull request #317 from Infisical/gen-example-env-command
generate example .env file command
2023-02-08 18:51:23 -08:00
a685ac3e73 update regex to capature comment 2023-02-08 18:48:45 -08:00
9a22975732 When comments are empty, return empty byte 2023-02-08 17:29:35 -08:00
cd0b2e3a26 Change default secret comments 2023-02-08 14:36:56 -08:00
80a3c196ae Fixed errors with undefined tags 2023-02-08 14:32:57 -08:00
b0c541f8dc generate example .env file command 2023-02-08 13:46:57 -08:00
6188b04544 Switch azure integration off 2023-02-08 13:53:12 +07:00
8ba4f964d4 Switch Azure KV integration on 2023-02-08 13:42:49 +07:00
0d2caddb12 Merge pull request #292 from HasanMansoor4/auto-capitalization-toggle
Auto capitalization toggle for secrets
2023-02-07 21:55:05 -08:00
4570c35658 Merge pull request #313 from Infisical/debug-new-integrations
Fix more encoding issues with integrations
2023-02-08 12:38:49 +07:00
72f7d81b80 Fix more encoding issues with integrations 2023-02-08 12:38:15 +07:00
231fa61805 Merge branch 'main' into auto-capitalization-toggle 2023-02-07 21:32:29 -08:00
9f74affd3a Merge pull request #300 from kanhaiya38/feat/merge-env
feat(ui): allow user to merge secrets while uploading file
2023-02-07 21:29:38 -08:00
f58e1e1d6c Minor style changes 2023-02-07 21:27:21 -08:00
074cf695b2 Merge branch 'main' into feat/merge-env 2023-02-07 19:57:50 -08:00
07c056523f circle-ci integration done 2023-02-08 09:24:48 +05:45
65eb037020 Merge branch 'main' into auto-capitalization-toggle 2023-02-08 05:23:41 +03:00
c84add0a2a Merge pull request #312 from Infisical/secret-tagging
Added tags to secrets in the dashboard
2023-02-07 16:57:01 -08:00
ace0e9c56f Fixed the bug of wrong data structure 2023-02-07 16:54:13 -08:00
498705f330 Fixed the login error with tags 2023-02-07 16:47:05 -08:00
7892624709 Added tags to secrets in the dashboard 2023-02-07 16:29:15 -08:00
d8889beaf7 mark gitlab as complete 2023-02-07 12:58:39 -08:00
6e67304e92 Update wording of k8 2023-02-07 12:54:09 -08:00
8b23e89a64 add k8 diagram 2023-02-07 12:38:58 -08:00
7611b999fe Merge pull request #311 from Infisical/debug-new-integrations
Patch encoding header issue for some integrations for getting their apps
2023-02-08 01:30:02 +07:00
aba8feb985 Patch encoding header issue for some integrations for getting their apps 2023-02-08 01:28:46 +07:00
747cc1134c Merge pull request #310 from Infisical/refactor-integration-pages
Refactor integration pages into separate steps for authorization and integration creation.
2023-02-07 23:29:42 +07:00
db05412865 Fix incorrect imports, build errors 2023-02-07 23:27:21 +07:00
679b1d9c23 Move existing integration authorization and creation into separate steps 2023-02-07 23:10:31 +07:00
a37cf91702 fix: handle duplicate edge case for sidebar loading
This changes the SideBar's data prop to be filtered by id instead of key.

fixes issue #308
2023-02-07 21:35:13 +08:00
80d219c3e0 circle-ci integration on progress 2023-02-07 13:20:39 +05:45
5ea5887146 Begin refactoring all integrations to separate integration pages by step 2023-02-07 11:48:17 +07:00
13838861fb Merge pull request #305 from Infisical/azure
Finish v1 Azure Key Vault integration
2023-02-06 18:15:57 +07:00
09c60322db Merge branch 'main' into azure 2023-02-06 18:15:44 +07:00
68bf0b9efe Finish v1 Azure Key Vault integration 2023-02-06 17:57:47 +07:00
3ec68daf2e Merge branch 'main' into auto-capitalization-toggle 2023-02-06 11:17:08 +03:00
9fafe02e16 Merge branch 'main' into feat/merge-env 2023-02-05 23:16:19 -08:00
56da34d343 Merge pull request #304 from Infisical/secret-tagging
Revamped the dashboard look
2023-02-05 20:36:49 -08:00
086dd621b5 Revamped the dashabord look 2023-02-05 20:29:27 -08:00
56a14925da Add githlab to integ overview 2023-02-05 19:23:52 -08:00
c13cb23942 Add gitlab integ docs 2023-02-05 19:21:07 -08:00
31df4a26fa Update cli docs to be more clear and consistent 2023-02-05 16:05:34 -08:00
9f9273bb02 Add tags support for secrets 2023-02-05 12:54:42 -08:00
86fd876850 change api from post to patch, fix spelling mistakes 2023-02-05 20:51:53 +03:00
b56d9287e4 feat(ui): allow user to merge secrets while uploading file 2023-02-05 18:07:54 +05:30
a35e235744 remove console log 2023-02-05 06:25:40 +03:00
77a44b4490 Refactor into component and use React Query 2023-02-05 06:21:58 +03:00
594f846943 Merge remote-tracking branch 'origin/main' into auto-capitalization-toggle 2023-02-05 03:19:06 +03:00
8ae43cdcf6 Merge pull request #296 from akhilmhdh/fix/ws-redirect
feat(ui): removed workspace context redirect and added redirect when ws is deleted
2023-02-04 10:50:23 -08:00
1d72d310e5 Add offline support to faq 2023-02-04 08:48:01 -08:00
b0ffac2f00 fetch apps from circleci 2023-02-04 16:50:34 +05:45
5ba851adff circleci-integration-setup 2023-02-04 15:28:04 +05:45
e72e6cf2b7 feat(ui): removed workspace context redirect and added redirect when project is deleted 2023-02-04 14:24:10 +05:30
0ac40acc40 Merge pull request #295 from mocherfaoui/inf-compare-secrets
add new modal to compare secrets across environments
2023-02-03 23:55:17 -08:00
56710657bd Minor styling updates 2023-02-03 23:49:03 -08:00
92f4979715 Merge branch 'main' into inf-compare-secrets 2023-02-03 21:24:24 -08:00
1e9118df33 delete backup secrets when new user login 2023-02-03 21:14:56 -08:00
e16c0e53ff Add offline secrets fetch feature 2023-02-03 21:02:36 -08:00
0d57a26925 Add token flag to export command 2023-02-03 21:02:36 -08:00
1bd180596e Merge pull request #294 from akhilmhdh/feat/new-settings-page
New Project Settings Page
2023-02-03 20:11:30 -08:00
fca003dfd7 Minor typos fixed and style changes 2023-02-03 20:09:28 -08:00
f1ef23874c Add token flag to read secrets via service token 2023-02-03 16:55:40 -08:00
16883cf168 make some params optional 2023-02-03 22:34:18 +01:00
1781b71399 add new modal to compare secrets across environments 2023-02-03 22:33:39 +01:00
fb62fa4d32 feat(ui): updated select design due to rebase changes 2023-02-03 22:59:01 +05:30
ed148a542d feat(ui): implemented the new project settings page 2023-02-03 22:22:51 +05:30
a4f7843727 feat(ui): global workspace and subscription context 2023-02-03 22:21:14 +05:30
48cd84ce77 feat(ui): fine tuning components library with exiting app design 2023-02-03 22:21:09 +05:30
3859a7e09b feat(ui): added new react-query hooks for settings page 2023-02-03 22:20:04 +05:30
76d0127029 Add docs for PM2 integration, update Docker/Docker-Compose integration docs 2023-02-03 15:50:26 +07:00
a94cd8c85c Merge pull request #293 from Infisical/ip-address
Ip address
2023-02-03 12:49:32 +07:00
ee555f3f15 Rename loginSRPDetail file 2023-02-03 12:46:14 +07:00
bd230a8b7d Remove comment from loginSRPDetail 2023-02-03 12:40:35 +07:00
a4926d8833 Add back requestIp middleware 2023-02-03 12:32:54 +07:00
7560d2f673 Merge remote-tracking branch 'origin' into ip-address 2023-02-03 11:02:07 +07:00
44b2bc1795 modify method to check for cli updates 2023-02-02 12:58:05 -08:00
3ccc6e5d5c Merge pull request #280 from Neeraj138/faster-redirect-from-login
login.tsx: Faster redirect from login to dashboard.
2023-02-02 08:46:04 -08:00
ccb579ecfd Merge pull request #123 from Infisical/snyk-upgrade-168622761b1452230387c1e39953ec92
[Snyk] Upgrade @sentry/node from 7.19.0 to 7.21.1
2023-02-02 08:34:01 -08:00
29f5e8aa78 Merge branch 'main' into snyk-upgrade-168622761b1452230387c1e39953ec92 2023-02-02 08:31:38 -08:00
d64357af61 Merge tag 'main' into snyk-upgrade-168622761b1452230387c1e39953ec92 2023-02-02 08:17:27 -08:00
37c91ae652 Merge pull request #122 from Infisical/snyk-upgrade-b8de592fd7591ed26eb63611e9e90c65
[Snyk] Upgrade @sentry/tracing from 7.19.0 to 7.21.1
2023-02-02 08:16:49 -08:00
3a4cfa0834 Merge branch 'main' into snyk-upgrade-b8de592fd7591ed26eb63611e9e90c65 2023-02-02 08:14:54 -08:00
cef45c2155 Merge tag 'main' into snyk-upgrade-b8de592fd7591ed26eb63611e9e90c65 2023-02-02 08:10:42 -08:00
5143fc6eee Merge pull request #126 from Infisical/snyk-upgrade-69b188452db2966945d5ae119d7209d2
[Snyk] Upgrade mongoose from 6.7.2 to 6.7.3
2023-02-02 08:04:40 -08:00
186382619c Merge branch 'main' into snyk-upgrade-69b188452db2966945d5ae119d7209d2 2023-02-02 08:02:22 -08:00
91e70c5476 Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-02 07:59:11 -08:00
216ace9f61 Updated readme and its translations; added contributors 2023-02-02 07:59:02 -08:00
6b99582a66 Merge pull request #124 from Infisical/snyk-upgrade-356fdb4c3069d260010f638026680c3c
[Snyk] Upgrade axios from 1.1.3 to 1.2.0
2023-02-02 07:56:11 -08:00
ea0fe1b92e Merge branch 'main' into snyk-upgrade-356fdb4c3069d260010f638026680c3c 2023-02-02 07:53:26 -08:00
72810acf2e Merge pull request #284 from KunalSin9h/fix-pdf-login-url
fix site url on pdf to be .env/SITE_URL & typo in website titles & Wrong Copyright message
2023-02-02 07:46:56 -08:00
a013768313 fix copyright label in go source 2023-02-02 19:45:24 +05:30
a660261678 fix type in Title -> 2023-02-02 19:25:36 +05:30
7d181f334c fix site url on pdf to be .env/SITE_URL 2023-02-02 18:49:58 +05:30
46ab27af1a Merge branch 'main' of https://github.com/Infisical/infisical 2023-02-01 22:29:49 -08:00
25bb966a32 Added ability to change a role in an organization 2023-02-01 22:29:33 -08:00
c086579260 Merge pull request #281 from jon4hz/main
fix homebrew
2023-02-01 21:44:22 -08:00
3d14bc9a00 remove env name check 2023-02-01 20:31:25 -08:00
75cd7a0f15 integrate frontend with backend for auto capitalization setting 2023-02-02 05:30:22 +03:00
4722bb8fcd add auto capitalization api controllers and routes with mongo schema updated 2023-02-02 05:27:07 +03:00
f2175b948c Merge pull request #282 from nirga/main
chore: fix typo in quick start guide
2023-02-01 13:17:54 -08:00
6f3d102ecb chore: fix typo in quick start guide 2023-02-01 23:15:39 +02:00
54fa39f347 Fixed issues with breadcrumbs and redirects of forgot password 2023-02-01 12:22:41 -08:00
52697dea97 login.tsx: Faster redirect from login to dashboard. 2023-02-01 19:02:37 +05:30
c99b207e9e ci: maybe fix brew 2023-02-01 14:28:12 +01:00
4886537a56 Revert "Revert "Merge pull request #279 from jon4hz/main""
This reverts commit 1878bed10a1e06a8340d8b02385a8d0081394d61.
2023-02-01 14:19:49 +01:00
ca688764a3 Add login/logout logs 2023-02-01 11:56:38 +07:00
71cf54c28b add auto cli version to all-other-builds 2023-01-31 20:19:42 -08:00
1878bed10a Revert "Merge pull request #279 from jon4hz/main"
This reverts commit 87fd5e33f11a354a622990fb58d185d8094f29c6, reversing
changes made to 2c4e066f6421c461e28129fedc14fb6fb6b2b1b9.
2023-01-31 20:17:15 -08:00
87fd5e33f1 Merge pull request #279 from jon4hz/main
CI Improvements
2023-01-31 19:39:46 -08:00
ffda30bd65 ci: mark goreleaser snapshots as such 2023-02-01 03:54:22 +01:00
716795532e ci: bump goreleaser action 2023-02-01 03:38:18 +01:00
f9ff99748b ci: remove obsolete var 2023-02-01 03:37:07 +01:00
723fa153be ci: completion and manpages for homebrew 2023-02-01 03:36:26 +01:00
1871d1a842 fix: improve goreleaser 2023-02-01 03:35:54 +01:00
2c4e066f64 bring back auto cli version in CI 2023-01-31 17:34:50 -08:00
b371dad506 Increase cli version 2023-01-31 17:22:44 -08:00
a6d4431940 Auto add cli version from tag 2023-01-31 17:03:19 -08:00
871d80aad5 when login expired, do not ask to override login 2023-01-31 16:37:56 -08:00
6711979445 Disallow service token creation based on permission 2023-01-31 09:24:55 -08:00
cb080b356c increase cli version 2023-01-30 22:17:02 -08:00
9950c5e02d empty commit 2023-01-30 22:15:44 -08:00
22a11be4e0 Update host rules for permissioning 2023-01-30 21:38:09 -08:00
6e01c80282 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-30 21:14:41 -08:00
4e14f84df9 Allow editing personal permissions 2023-01-30 21:14:22 -08:00
55522404b4 Merge pull request #275 from Infisical/dependabot/npm_and_yarn/backend/cookiejar-2.1.4
Bump cookiejar from 2.1.3 to 2.1.4 in /backend
2023-01-30 20:37:44 -08:00
4ef8c273f7 Wired access controls for environemnts to frontend 2023-01-30 20:36:04 -08:00
61c17ccc5e update getAllAccessibleEnvironmentsOfWorkspace controller 2023-01-30 19:39:45 -08:00
2832476c2b Add write permission status 2023-01-30 19:38:40 -08:00
c0fc74b62a Add write permission status 2023-01-30 19:22:52 -08:00
bb752863fa Fix merge conflicts 2023-01-30 19:48:12 +07:00
54caaffe3a Bump cookiejar from 2.1.3 to 2.1.4 in /backend
Bumps [cookiejar](https://github.com/bmeck/node-cookiejar) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/bmeck/node-cookiejar/releases)
- [Commits](https://github.com/bmeck/node-cookiejar/commits)

---
updated-dependencies:
- dependency-name: cookiejar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 12:41:45 +00:00
cf5603c8e3 Finish preliminary backwards-compatible transition from user encryption scheme v1 to v2 with argon2 and protected key 2023-01-30 19:38:13 +07:00
77b1011207 feat(docs): added a pull request template 2023-01-30 11:54:52 +01:00
55f0a491cb Release fly.io integrartion 2023-01-29 22:38:20 -08:00
a940fa210a Add deny api/get envs api 2023-01-29 21:12:41 -08:00
5162ba9b91 add basic auth model for Organization 2023-01-29 21:12:41 -08:00
3b6022de64 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-29 15:55:22 -08:00
bf743f5f72 Make the loading animation smaller 2023-01-29 15:55:01 -08:00
3e177539d5 Remove state from password controllers 2023-01-29 15:48:42 -08:00
5743dd3a8c Merge pull request #272 from Neeraj138/subscription-check
Add check for subscriptions call before setting the current plan
2023-01-29 09:25:04 -08:00
9f8ad95a59 Revert "correct tags in docker image workflow"
This reverts commit 3ef2ac8a77b50c1fbac1fa2173acccbf1736a011.
2023-01-29 09:17:22 -08:00
3c05a4cebd Add check for subscriptions call before setting the current plan 2023-01-29 14:16:05 +05:30
bc955a9afd increase cli version 2023-01-28 22:32:23 -08:00
ec8d86e662 Merge pull request #256 from akhilmhdh/feat/react-query
feat(ui): added new auth guard with react-query and axios
2023-01-29 12:21:08 +07:00
bc70bedb78 Fixed the bug with empty variables 2023-01-28 20:41:54 -08:00
7a4b77ce59 Update README.md 2023-01-28 14:31:29 -08:00
8600cee54c Merge pull request #265 from sanyamjain04/tailwind-plugin
added prettier-plugin-tailwindcss
2023-01-28 14:14:51 -08:00
fe9573ea3c Merge pull request #264 from asheliahut/patch-1
Include Id on project
2023-01-28 14:11:32 -08:00
61db6c54c2 Merge pull request #269 from kimcore/main
Skip update check if github returns non-200
2023-01-28 14:08:35 -08:00
65093c73c5 Merge pull request #257 from mocherfaoui/inf-nsc-pt
New secrets are now added to the top in the dashboard UI
2023-01-28 13:54:24 -08:00
9986521e41 Merge pull request #270 from kimcore/readme-ko
Translate README.md to korean
2023-01-28 13:35:13 -08:00
655f015109 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-28 12:53:51 -08:00
3cea59ce5d Improved docs SEO 2023-01-28 12:53:44 -08:00
a184192452 Inform k8 self host about latest tags 2023-01-28 12:34:04 -08:00
2dbcab32d5 update gamma pull image policy 2023-01-28 12:03:53 -08:00
13aeeb4731 console.log in posthog 2023-01-28 11:22:16 -08:00
233a468127 Revert "add console.log for post"
This reverts commit dd960aa5f045f62a556e67f81bf172372401a465.
2023-01-28 11:22:16 -08:00
8a9e05b08f Revert "add test comment for docker build issue"
This reverts commit fdac590a023433113ae21295dbe1abf165fb5500.
2023-01-28 11:22:16 -08:00
3ef2ac8a77 correct tags in docker image workflow 2023-01-28 11:14:00 -08:00
fdac590a02 add test comment for docker build issue 2023-01-28 10:25:42 -08:00
dd960aa5f0 add console.log for post 2023-01-28 10:04:34 -08:00
0bd9a848c4 add back depot 2023-01-28 09:53:06 -08:00
1b86c58f91 remove depot from docker build 2023-01-28 09:24:58 -08:00
d5166d343d Remove depot docker 2023-01-28 09:17:54 -08:00
b315cf6022 Translate README.md to korean 2023-01-29 00:20:10 +09:00
37de32ec90 return proper error 2023-01-28 23:13:36 +09:00
6eb81802c3 Skip update check if github returns non-200 2023-01-28 23:06:37 +09:00
e6068a6f7f Merge pull request #247 from samsbg/main
Adding Spanish translation to the README 🌎ES
2023-01-27 18:39:35 -08:00
c059c088d1 update k8 selfhost docs values.yaml file 2023-01-27 12:42:47 -08:00
b530847edc increase chart version 2023-01-27 12:34:46 -08:00
c87c2dadd7 add readinessProbe check for pods 2023-01-27 12:31:46 -08:00
7b1ff04436 add deployment annotations 2023-01-27 10:45:42 -08:00
83aa440b62 Remove mongo url from envs 2023-01-27 10:43:23 -08:00
a555ef836b remove default sensitive keys 2023-01-27 09:33:49 -08:00
528601e442 Merge pull request #266 from Infisical/patch-empty-values
Allow empty values for secrets
2023-01-27 22:16:24 +07:00
13acb19e9f Allow empty values for secrets 2023-01-27 22:07:56 +07:00
079063157f added prettier-plugin-tailwindcss 2023-01-27 12:16:23 +05:30
e38933c0b3 Include Id on project
The project should have its id exposed.
2023-01-26 19:32:44 -08:00
d09b406c4e Merge pull request #262 from kmlgkcy/turkish-translation
translation: Turkish
2023-01-26 16:46:00 -08:00
a5eba8e722 Updated the billing engine for Cloud 2023-01-26 16:32:51 -08:00
7acb4cc22a fix helm deploymentAnnotations 2023-01-26 14:43:53 -08:00
b95ab6c6a1 added deploymentAnnotations to helm chart 2023-01-26 14:17:51 -08:00
038445e13e change from cal.com to calendly 2023-01-26 11:30:36 -08:00
07e9dd5a39 add managed secrets to deployment in gamma 2023-01-26 00:39:08 -08:00
6ec520d358 update helm values for k8 self host 2023-01-26 00:30:54 -08:00
06bfd2429b Update gemma helm chart with auto reload 2023-01-26 00:17:07 -08:00
099c4836e6 update helm charts to be more flexible 2023-01-26 00:14:07 -08:00
ddf8ceb45d translation: Turkish 2023-01-26 10:46:47 +03:00
8a49e0817a add error to failed org creation 2023-01-25 21:58:53 -08:00
88908297f5 add error object to log 2023-01-25 21:30:32 -08:00
cf0e111c09 increase replica count for gamma 2023-01-25 20:12:41 -08:00
ae0ee727fa Make backend login stateless 2023-01-25 20:09:57 -08:00
be2945c445 Merge pull request #259 from Infisical/stripe-adjustment
Update backend envars types and add STRIPE_PRODUCT_TEAM envar
2023-01-26 10:22:54 +07:00
237a10da1e Update backend envars types and add STRIPE_PRODUCT_TEAM envar 2023-01-26 10:20:42 +07:00
1baf14084d new secrets are added to the top 2023-01-25 19:55:48 +01:00
a6387e7552 feat(ui): added new auth guard with react-query and axios 2023-01-26 00:14:01 +05:30
a6f480d3f8 increase CLI 2023-01-24 19:59:45 -08:00
0413059fbe patch executeMultipleCommandWithEnvs when no /bin/zsh 2023-01-24 19:59:45 -08:00
65f049f6ac Merge pull request #254 from franky47/patch-1
docs: Fix typo in encryption overview
2023-01-24 19:51:10 -08:00
62f886a3b3 docs: Fix typo in encryption overview 2023-01-25 04:31:04 +01:00
271ca148e3 Make support link clickable 2023-01-24 11:01:49 -08:00
8aa294309f remove icon from support link 2023-01-24 10:53:46 -08:00
ca3233110b add support link for 1 on 1 in docs 2023-01-24 10:52:09 -08:00
1e4f6a4b9d increase CLI 2023-01-24 00:10:42 -08:00
a73fc6de19 add cli version check before every command 2023-01-24 00:08:42 -08:00
0bb750488b fix typo in docs 2023-01-23 23:00:24 -08:00
32f98f83c5 update nav name for development instructions 2023-01-23 22:56:41 -08:00
6943785ce5 remove stay alive 2023-01-23 22:55:14 -08:00
86558a8221 simplify contribution docs 2023-01-23 22:55:14 -08:00
f2c35a302d Merge pull request #239 from akhilmhdh/feat/component-update-1
Infisical component library foundations
2023-01-23 21:47:28 -08:00
0794b6132a auto create user for dev mode 2023-01-23 20:57:59 -08:00
062c287e75 feat(ui): changed to interfonts 2023-01-23 22:10:51 +05:30
e67d68a7b9 feat(ui): implemented basic table component 2023-01-23 22:10:51 +05:30
054acc689a feat(ui): implemented dropdown component 2023-01-23 22:10:51 +05:30
9b95d18b85 feat(ui): implemented switch component 2023-01-23 22:10:51 +05:30
7f9bc77253 feat(ui): added checkbox component 2023-01-23 22:10:51 +05:30
b92907aca6 feat(ui): added textarea component 2023-01-23 22:10:51 +05:30
c4ee03c73b featIui): added menu component 2023-01-23 22:10:51 +05:30
89ba80740b feat: added card and modal component 2023-01-23 22:10:50 +05:30
606a5e5317 feat(ui): added card component 2023-01-23 22:10:50 +05:30
f859bf528e feat(ui): added icon button component, updated secondary button style and added select to barrel export 2023-01-23 22:10:50 +05:30
ad504fa84e feat(ui) added select, spinner components 2023-01-23 22:10:50 +05:30
e7ac74c5a0 feat(ui): implemented form control components 2023-01-23 22:10:50 +05:30
b80504ae00 feat(ui): implemented new input component 2023-01-23 22:10:50 +05:30
68f1887d66 feat(ui): implemented button component 2023-01-23 22:10:50 +05:30
5cadb9e2f9 Finish MFA v1 and refactor all tokens into separate TokenService with modified collection 2023-01-23 22:10:15 +07:00
201c8352e3 fix typo in k8 self host 2023-01-22 16:50:45 -08:00
a0f0ffe566 improve i-dev command 2023-01-22 14:03:09 -08:00
4b4e8e2bfc Update docker integration 2023-01-22 12:50:06 -08:00
4db4c172c1 Fix the start guide redirect issue 2023-01-22 00:35:36 -08:00
08c54a910f Adding Spanish translation to the README 2023-01-22 01:42:25 -06:00
00fee63ff3 Merge pull request #246 from Infisical/more-integrations
Adjust integration bot authorization sequence
2023-01-22 11:17:15 +07:00
6b80cd6590 Modify case where integration bot was authorized but user didn't finish inputting their PAT -> should result in not sharing keys with bot 2023-01-22 11:12:47 +07:00
840efbdc2f Update API_URL to INFISICAL_API_URL 2023-01-21 13:14:39 -08:00
b91dc9e43e increase cli version 2023-01-21 13:04:14 -08:00
7470cd7af5 Merge pull request #242 from asheliahut/add-domain-env
Allow INFISICAL_URL to use domain for self-hosted
2023-01-21 12:56:24 -08:00
d3a6977938 update docs for change cli api 2023-01-21 12:53:18 -08:00
7cc341ea40 update INFISICAL_DEFAULT_API_URL constant name 2023-01-21 12:26:26 -08:00
5297133c07 Set INFISICAL_URL to nothing 2023-01-21 12:25:56 -08:00
7a6230f2f8 Change INFISICAL_URL to API_URL 2023-01-21 12:24:24 -08:00
ffe66a3b8e Merge pull request #244 from caioluis/main
docs(README): fix typo - Portueguese should be Portuguese
2023-01-21 12:10:23 -08:00
936cd51f29 Update README.md 2023-01-21 11:13:38 -08:00
0c24671d8b Merge pull request #245 from Infisical/more-integrations
Render & Fly.io integrations, reduce page reloads for integrations page.
2023-01-22 00:20:09 +07:00
6969593b38 Fix Mintlify mint.json issue and list Render, Fly.io integrations as availalbe 2023-01-22 00:18:00 +07:00
0c351c0925 Add Render, Fly.io integrations and reduce integrations page reloads 2023-01-22 00:09:37 +07:00
656c408034 docs(README): fix typo - Portueguese should be Portuguese 2023-01-21 15:43:38 +00:00
74fb64bbb9 fix edge case of input same as default clobber env 2023-01-21 01:05:25 -08:00
3af85f9fba fix typo of company name 2023-01-21 00:26:52 -08:00
3c282460b2 Allow INFISCAL_URL to use domain for self-hosted 2023-01-21 00:18:43 -08:00
68b7e6e5ab Merge pull request #241 from alexdanilowicz/patch-1
docs(README): nit typo - Frence should say French
2023-01-20 18:10:42 -08:00
9594157f3e docs(README): nit typo - Frence to French
Update the translations section to say French instead of Frence.
2023-01-20 17:49:43 -08:00
b6ed6ad61e increase cli version 2023-01-19 17:10:03 -08:00
3fc68ffc50 patch secret override for run/export command 2023-01-19 17:05:18 -08:00
0613e1115d Update k8 self host docs 2023-01-19 15:38:06 -08:00
6567c3bddf Fixed the bug with redirect during signup invite 2023-01-18 13:21:30 -08:00
b7115d8862 Merge pull request #238 from akhilmhdh/feat/storybook
feat(frontend): added storybook with tailwind integration
2023-01-18 13:11:42 -08:00
83899bebc8 feat(frontend): added storybook with tailwind integration
chore(frontend): added some required radix components
2023-01-18 23:45:54 +05:30
06803519e6 Reduce page reloads for integrations page 2023-01-18 17:38:52 +07:00
3a6b2084bc Patch GitHub integration for organization repos by including correct owner 2023-01-18 16:33:24 +07:00
2235069e78 Merge pull request #183 from jon4hz/helm
Helm updates
2023-01-17 22:57:25 -08:00
15698c5036 Increase chart version 2023-01-17 22:44:34 -08:00
6ac8e057b0 set frontend env to empty {} 2023-01-17 22:38:56 -08:00
375412b45d Allow mongo connection string based on type 2023-01-17 22:34:19 -08:00
e47530dc71 Allowed upper case for environment names 2023-01-17 22:00:52 -08:00
93150199a4 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-18 10:54:07 +07:00
900f69f336 Uncomment GitHub/Netlify integrations 2023-01-18 10:53:57 +07:00
c556820646 Merge pull request #235 from akhilmhdh/chore/eslint-frontend
airbnb eslint and sorting
2023-01-17 19:05:38 -08:00
18fbe82535 Fixed minor bugs during code cleaning 2023-01-17 19:03:43 -08:00
7ae73d1b62 chore(frontend): added rule to seperate out @app imports and linted 2023-01-17 21:06:14 +05:30
cf7834bfc3 chore(frontend): fixed all eslint errors 2023-01-17 21:06:14 +05:30
9f82e2d836 correct host api for k8 2023-01-16 18:01:20 -08:00
f20af1f5f8 Improve k8 docs and add docs for auto redeploy 2023-01-16 17:29:02 -08:00
8343f8ea0d Update k8 helm chart version 2023-01-16 16:55:24 -08:00
74c0dcd1f5 Remove the use of error channel from go routine 2023-01-16 16:52:59 -08:00
40696e4095 increase CLI version 2023-01-16 15:11:49 -08:00
614a2558f5 Patch IP recognition 2023-01-17 00:50:35 +07:00
56aec216c1 Adjust app-wide API limit 2023-01-17 00:10:11 +07:00
b359fb5f3b Adjust app-wide rate limiter 2023-01-16 23:48:03 +07:00
1fbbbab602 Allow new channel types 2023-01-16 08:45:21 -08:00
89697df85e Increase rate limits for API 2023-01-16 22:09:58 +07:00
37ee8148c6 revert default api domain 2023-01-15 23:57:13 -08:00
9e55102816 Switch to v2/secrets CURD api for cli 2023-01-15 23:56:06 -08:00
b8fa5e8a89 add method to get channel from user agent 2023-01-15 23:55:13 -08:00
3ba636f300 switch k8-operator to secrets v2api 2023-01-15 23:12:11 -08:00
da3742f600 set userid and email based on presence of service token/jwt 2023-01-15 22:03:14 -08:00
35f4d27ab0 Populate service token user 2023-01-15 21:40:19 -08:00
cf123d1887 service token create - change env to slug 2023-01-15 21:40:19 -08:00
b3816bd828 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-16 10:53:16 +07:00
7c7c9dea40 Add Infisical API to README 2023-01-16 10:53:07 +07:00
eabe406ab0 Merge pull request #229 from Infisical/expand-open-api
Fill in more example values for OpenAPI schema
2023-01-16 10:14:50 +07:00
2ae617cda6 Fill in more example values for OpenAPI schema 2023-01-16 10:13:56 +07:00
1b16066335 Merge pull request #228 from Infisical/expand-open-api
Add images to API reference authentication for getting API keys and n…
2023-01-16 10:02:08 +07:00
da251d3d2d Add images to API reference authentication for getting API keys and notes on crypto 2023-01-16 09:58:48 +07:00
818efe61f4 Merge pull request #227 from Infisical/k8-new-service-token-and-auto-redeploy
add auto redeploy, new secrets api, and new service token
2023-01-15 17:03:11 -08:00
9f08b04c92 update secrets-operator helm chart 2023-01-15 17:01:31 -08:00
41d17c930a update kubectl install configs 2023-01-15 17:00:06 -08:00
63f22c554a add auto redeploy, new secrets api, and new service token 2023-01-15 16:47:09 -08:00
cba57cf317 Updated readme.md 2023-01-15 16:44:44 -08:00
9a28e5b4bc Added to auto-redirect to the no projects page 2023-01-15 16:06:44 -08:00
a2689002d3 Merge pull request #226 from akhilmhdh/chore/move-to-src
chore(frontend): changed source code to src folder from root
2023-01-15 14:58:17 -08:00
e7a9b83877 Merge branch 'main' into chore/move-to-src 2023-01-15 14:37:04 -08:00
813db9dbbc Added volumes and deleted logs 2023-01-15 14:25:29 -08:00
72d52c9941 Fixing merge conflicts for the folder structure 2023-01-15 13:42:17 -08:00
4c2b9d4703 Solving merge conflicts 2023-01-15 13:40:03 -08:00
b1f7505f30 Fixed the redirectbug with deleting a certain workspace 2023-01-15 13:31:57 -08:00
63e9d83ba4 chore(frontend): changed source code to src folder from root 2023-01-16 00:34:22 +05:30
1534a47adc Fixed the redirectbug with adding a new workspace 2023-01-15 10:45:41 -08:00
c563548a1c Merge pull request #224 from akhilmhdh/feat/migration-ts-v2
feat(frontend): migrated to ts completed.
2023-01-15 09:57:15 -08:00
a633a3534d Merge pull request #225 from Infisical/expand-open-api
Add new organization endpoints to API reference
2023-01-16 00:04:08 +07:00
992357cbc4 Add new organization endpoints to API reference 2023-01-16 00:00:04 +07:00
ffc3562709 feat(frontend): migrated to ts completed. 2023-01-15 21:34:54 +05:30
f19db530b1 Merge pull request #223 from Infisical/api-keys
API Keys V1
2023-01-15 14:49:21 +07:00
061a9c8583 Fix build errors 2023-01-15 14:47:23 +07:00
b8fbc36b2d Fix faulty import 2023-01-15 14:40:45 +07:00
e364faaffd Complete v1 API Key 2023-01-15 14:34:16 +07:00
b3246778f2 Merge remote-tracking branch 'origin' into api-keys 2023-01-15 14:26:27 +07:00
74b76eda7e Complete v1 API Key 2023-01-15 14:25:23 +07:00
564367d5fd Merge pull request #222 from akhilmhdh/feat/migration-ts
migration(frontend): migrated frontend files to ts execpt dialog component
2023-01-14 16:00:53 -08:00
fd2966610c fix: typo 2023-01-14 19:58:08 +01:00
c23b291f25 fix: mongodb connection 2023-01-14 19:51:49 +01:00
67365e5480 migration(frontend): migrated frontend files except some components to ts 2023-01-15 00:13:25 +05:30
4df205dea6 Fix README 2023-01-14 21:43:36 +07:00
32928bf45c Fix merge conflicts 2023-01-14 21:27:55 +07:00
ea98f9be3c Merge pull request #221 from Infisical/api-docs
API Reference Docs v1
2023-01-14 21:09:00 +07:00
5085376f11 Check v2 workspace membership routes (currently not routed) 2023-01-14 21:07:17 +07:00
e2b4adb2e9 Complete v1 API reference docs, pre-launch 2023-01-14 19:06:43 +07:00
315810bd74 Complete v1 API reference docs, pre-launch 2023-01-14 19:02:12 +07:00
7e9ba3b6e2 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-13 22:59:54 -08:00
08dd5174b3 Making the dashboard less clunky 2023-01-13 22:59:43 -08:00
e552be0a81 add deployment error check for gamma 2023-01-13 20:42:23 -08:00
3cd9241aee increase cli version 2023-01-13 19:52:29 -08:00
9ca544f680 Merge pull request #219 from imakecodes/feature/adding-export-dotenv-export-format
feat(CLI): adding new export format (dotenv-export)
2023-01-13 19:50:10 -08:00
98d84b6717 add FormatDotEnvExport to list of available formats 2023-01-13 19:48:40 -08:00
b63360813a Continue API reference development 2023-01-14 09:48:13 +07:00
5d8c4ad03f Changed the formulation to secrets and configs 2023-01-13 17:32:36 -08:00
3e6206951e updated Readme with new contributors 2023-01-13 17:19:22 -08:00
3bc7f2aa7c Merge pull request #199 from Gabriellopes232/language-support-ptbr
Language Support pt-BR
2023-01-13 17:03:43 -08:00
72b8dbda15 Merge branch 'main' into language-support-ptbr 2023-01-13 16:50:19 -08:00
439e86d763 Merge pull request #186 from akhilmhdh/feat/#31
feat(#31): implemented api for environment crud operations
2023-01-13 16:45:35 -08:00
71fbf519ce Minor style changes - capitalization 2023-01-13 16:17:40 -08:00
d386f2702d Minor style changes to integrations 2023-01-13 16:05:50 -08:00
986434d66a Add infisical in Makefile for docker compose 2023-01-13 14:01:23 -08:00
30d84ede41 Merge pull request #220 from Infisical/gamma-auto-deploy
Gamma auto deploy
2023-01-13 13:42:34 -08:00
87a3f9a03c delete gamma deploy workflow file 2023-01-13 13:41:56 -08:00
64d1f252e2 Rename workflow file 2023-01-13 13:39:30 -08:00
092e4a55bd enable auto deploy to gamma 2023-01-13 13:13:15 -08:00
a00e6df59f add manual approval setp 2023-01-13 13:09:57 -08:00
189d24589e correct needs fields in gha 2023-01-13 12:53:53 -08:00
17bae52830 gamma deployment after image build 2023-01-13 12:52:07 -08:00
323701d432 add gha upgrade helmchart 2023-01-13 12:38:56 -08:00
593765cb24 cat values file after downloading 2023-01-13 12:31:55 -08:00
fa60784a6b Add files via upload 2023-01-13 12:27:14 -08:00
eb9a8e0285 echo values file 2023-01-13 12:16:36 -08:00
d1f296b7e7 fix indent gamma deploy gha 2023-01-13 12:11:05 -08:00
dc6d036d86 write helm values to file form secret 2023-01-13 12:08:47 -08:00
58aee0239f docs: adding documentation about dotenv-export 2023-01-13 16:59:22 -03:00
799a839940 feat: adding new export format 2023-01-13 16:52:15 -03:00
0242707e33 gh action test kubectl 2023-01-13 11:49:38 -08:00
9974f889f3 Merge pull request #218 from Infisical/gamma-auto-deploy
Add github action for gamma deploy
2023-01-13 11:45:01 -08:00
a8f38a5367 Add github action for gamma deploy 2023-01-13 11:40:29 -08:00
61318f28f7 Remove netlify and gh 2023-01-13 09:18:41 -08:00
036d32aeba feat(#31): added multi env support in integration 2023-01-13 21:09:59 +05:30
d03eff4f46 Merge pull request #216 from Infisical/check-smtp-setup
Add SMTP support for AWS SES and docs for it
2023-01-13 20:26:12 +07:00
29592a1e9e Add SMTP support for AWS SES and docs for it 2023-01-13 20:25:13 +07:00
0f151fcd7a Merge pull request #215 from Infisical/patch-integrations
Patch integrations
2023-01-13 18:19:08 +07:00
cbd8302afe Add temp patch for CRUD ops race conditions 2023-01-13 18:15:17 +07:00
6992c51e17 Working fixing integrations race condition 2023-01-13 16:36:34 +07:00
91f1090568 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-13 01:29:26 -08:00
6c61aef526 hotfix: fix the bug with pushing multiple envars in a sequence 2023-01-13 01:29:14 -08:00
b67abf94d4 Fixing minor bugs for custom environments 2023-01-13 01:02:43 -08:00
9d4ea2dcda Continue api-reference docs 2023-01-13 15:04:46 +07:00
f57f3e6475 enable integ 2023-01-12 23:27:10 -08:00
d958341154 Corrected the telemetery event name 2023-01-12 22:15:17 -08:00
61f767e895 Corrected the telemetery event name 2023-01-12 22:12:23 -08:00
d579684d2f increase version 2023-01-12 22:00:06 -08:00
35466a7f4a Modify get secrets logic 2023-01-12 21:58:03 -08:00
95177074e3 Merge branch 'main' into feat/#31 2023-01-12 17:24:48 -08:00
efd5016977 Added frontend for api-keys 2023-01-12 17:08:40 -08:00
1ac94ee940 selectively get user email from service toke/jwt 2023-01-12 16:38:12 -08:00
dc76be3d22 Merge pull request #210 from Grraahaam/feat/translation-fr
feat: adding support for fr language 🌎🇫🇷
2023-01-12 16:10:02 -08:00
a707fe1498 disable integration 2023-01-12 15:56:49 -08:00
71f60f1589 Update modify secrets api v2 so that fields are optional 2023-01-12 15:31:58 -08:00
47fd48b7b0 Fixed the TS error during signup 2023-01-12 13:57:00 -08:00
07c65ded40 Refactored the logic for frontend dashboard 2023-01-12 01:05:13 -08:00
84700308f5 feat(#31): implemented ui for multi env and integrated api with backend
fix(#31): fixed all v2 release conflict
2023-01-11 23:12:05 +05:30
9116bf3344 feat(ui): implemented ui for env management table 2023-01-11 23:10:11 +05:30
3ad3e19bcf feat(#31): implemented api for environment crud operations 2023-01-11 23:10:11 +05:30
861639de27 Merge pull request #213 from Infisical/patch-integrations
Patch Vercel API teamId requirement for team integrations
2023-01-11 17:04:21 +07:00
37ed27111a Patch Vercel API teamId requirement for team integrations 2023-01-11 16:53:53 +07:00
c527efad94 Revert "Disabled integrations for now"
This reverts commit 389f5c4f211de8f45b459d749ec18358a5263220.
2023-01-11 00:24:05 -05:00
389f5c4f21 Disabled integrations for now 2023-01-10 21:11:23 -08:00
acaae0b82c Merge pull request #212 from Infisical/patch-integrations
Bring back sync integrations to CRUD secrets routes
2023-01-11 11:23:43 +07:00
b8f102493e Bring back sync integrations to CRUD secrets routes 2023-01-11 11:19:56 +07:00
286184ab48 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-11 10:20:34 +07:00
c0f0d699b4 Add files to api-docs branch 2023-01-11 10:18:44 +07:00
0f043605d9 Fix merge conflicts 2023-01-11 10:08:44 +07:00
9ff0b7bc18 Minor changes to README 2023-01-11 10:03:40 +07:00
0b281a02d0 fix(i18n): add default empty string 2023-01-11 01:54:16 +01:00
d7b046236b Merge branch 'Infisical:main' into feat/translation-fr 2023-01-11 01:38:47 +01:00
d9b7f69838 fix(lang): add remaining translation on login + signin 2023-01-11 01:32:53 +01:00
16d2746749 fix(lang): add langugageMap fr 2023-01-11 01:30:15 +01:00
9ce4a52b8d Remove posthog for sev2 2023-01-10 19:07:50 -05:00
0fab5d32f2 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-10 14:08:12 -08:00
3fd5b521bb Removed service token logs 2023-01-10 14:08:02 -08:00
b8a750a31d Merge pull request #209 from Infisical/depot-docker
Depot docker
2023-01-10 16:50:55 -05:00
e51046fe62 remove QEMU 2023-01-10 16:47:09 -05:00
7fde55414a add depot token 2023-01-10 16:36:06 -05:00
db639b1a89 add project id to depot 2023-01-10 16:31:02 -05:00
fbe2297ed6 Add depot 2023-01-10 16:28:48 -05:00
63a739d626 Removed posthog logs 2023-01-10 12:48:27 -08:00
2212c351ca Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-10 11:39:43 -08:00
946fbe4716 Disabled integrations for now 2023-01-10 11:39:34 -08:00
1dbd121aa4 Try different keys for workflow cache 2023-01-10 14:30:52 -05:00
357d15b034 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-10 10:56:02 -08:00
a3db20cacf Fixed the bug with wrong project id in local storage 2023-01-10 10:55:52 -08:00
0ae73e873f Merge branch 'Infisical:main' into feat/translation-fr 2023-01-10 19:36:10 +01:00
b8edcab0d5 delete push-frontend-image-docker 2023-01-10 13:25:34 -05:00
be8a274e5a create separate frontend workflow 2023-01-10 13:20:37 -05:00
06f8826d67 Merge pull request #208 from Infisical/docker-cache
Docker cache
2023-01-10 12:01:51 -05:00
97f77dcada set push to true for backend workflow 2023-01-10 11:59:17 -05:00
e4d302b7e1 Add cache to build step in backend 2023-01-10 11:56:42 -05:00
3eb2209eb8 add cache to build after test step 2023-01-10 11:35:58 -05:00
e7c75b544d Fixed the discrepancies between projectIds in url and local storage 2023-01-10 08:35:14 -08:00
07e6eb88ea use github cache 2023-01-10 11:21:14 -05:00
c81320c09d remove frontend testing 2023-01-10 11:14:40 -05:00
b10e28b9b5 set push to false to test cache 2023-01-10 10:44:51 -05:00
5409bdb0cb Add local cache 2023-01-10 10:37:53 -05:00
35c6e1d668 Merge pull request #207 from Grraahaam/fix/typos
fix: readme and translation typos
2023-01-10 10:18:45 -05:00
d1467348d1 Update release_build.yml 2023-01-10 09:21:13 -05:00
b1ccb93d85 Update docker-image.yml 2023-01-10 09:19:55 -05:00
68c3b508e3 fix(doc): readme typos 2023-01-10 14:57:37 +01:00
1f68b8966d fix(front): translation typos 2023-01-10 14:57:22 +01:00
ef2da28cbe Merge branch 'Infisical:main' into feat/translation-fr 2023-01-10 14:53:01 +01:00
7fe706ad0d fix(lang): configured fr locale 2023-01-10 14:50:28 +01:00
a686462392 feat(lang): translated signup.json 2023-01-10 14:48:20 +01:00
878ca69f43 feat(lang): translated settings-project.json 2023-01-10 14:48:08 +01:00
ea9e185a65 feat(lang): translated settings-personal.json 2023-01-10 14:48:00 +01:00
1394368a43 feat(lang): translated settings-org.json 2023-01-10 14:47:50 +01:00
77b34467b9 feat(lang): translated settings-members.json 2023-01-10 14:47:19 +01:00
ee7cf7920d feat(lang): translated section-token.json 2023-01-10 14:46:55 +01:00
5bc8046f3f feat(lang): translated section-password.json 2023-01-10 14:46:46 +01:00
1423d05b52 feat(lang): translated section-members.json 2023-01-10 14:46:36 +01:00
1d0f51bb42 feat(lang): translated nav.json 2023-01-10 14:46:22 +01:00
aaa771a7b7 feat(lang): translated section-incident.json 2023-01-10 14:46:12 +01:00
2f67025376 feat(lang): translated login.json 2023-01-10 14:45:18 +01:00
f1c52fe332 feat(lang): translated integrations.json 2023-01-10 14:45:07 +01:00
1a90f27d6a feat(lang): translated dashboard.json 2023-01-10 14:44:56 +01:00
de1b75d99e feat(lang): translated common.json 2023-01-10 14:44:44 +01:00
295e93ac17 feat(lang): translated billing.json 2023-01-10 14:44:35 +01:00
0c59007fa8 feat(lang): translated activity.json 2023-01-10 14:44:23 +01:00
cbfd35e181 Merge pull request #202 from Infisical/ph-telemetry
Draft: Telemetry for v2 routes
2023-01-10 15:27:41 +07:00
9b266309c2 Merge branch 'ph-telemetry' of https://github.com/Infisical/infisical into ph-telemetry 2023-01-10 15:22:12 +07:00
cc46b575b7 Delete array/brackets in /v2/secrets DELETE route 2023-01-10 15:21:50 +07:00
08ab27cad8 Merge branch 'ph-telemetry' of https://github.com/Infisical/infisical into ph-telemetry 2023-01-10 00:08:16 -08:00
387ef17038 Fix some dashabord bugs 2023-01-10 00:08:06 -08:00
b71ba35a22 Fix inviting existing user to org 2023-01-10 14:56:34 +07:00
c2a03e4e0c Comment out yaml fo rnow 2023-01-09 23:33:36 -08:00
266d8b7775 Fixed the bug with creating projects 2023-01-09 23:06:10 -08:00
52f234675a Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-10 12:41:01 +07:00
0b2ac0470d Add activity logs docs 2023-01-10 12:40:52 +07:00
b1f62ffd35 Finish secret versioning docs 2023-01-10 12:04:53 +07:00
556a646dce Added sharing keys with a user while creating a new project 2023-01-09 19:01:22 -08:00
9762b580a5 fix typo in login message 2023-01-09 20:53:12 -05:00
9aa8bfa1a2 check if err is not nil first before checking error prefix 2023-01-09 19:43:01 -05:00
60a03cad98 Add error for when no login/no token 2023-01-09 19:28:22 -05:00
b702f29c46 Add warning log 2023-01-09 19:27:15 -05:00
12e104e12a Fix windows run bug by adding proper split on envior 2023-01-09 19:26:20 -05:00
b6ce660a3c add self recover when key not found in keychain 2023-01-09 19:22:03 -05:00
b03bd5fa08 set keyring to use defult keychain 2023-01-09 19:20:20 -05:00
6bd908f4cb allow viewing all secrets with service token 2023-01-09 19:18:43 -05:00
518606425a allow export to run with service token 2023-01-09 19:18:15 -05:00
ce7d411f29 Merge branch 'main' into ph-telemetry 2023-01-09 13:26:55 -08:00
933fed5da6 Got rid of i18n logs 2023-01-09 13:15:54 -08:00
486aa139c2 Changed frontend to use the new secrets routes 2023-01-09 13:14:07 -08:00
e3bf2791ee Continue PIT docs 2023-01-10 01:18:55 +07:00
f9e6ac2496 Add basic swagger autogen 2023-01-09 12:49:17 -05:00
a55b271525 Merge pull request #204 from cerrussell/patch-1
Issue 159 - Docker image tags
2023-01-09 08:46:25 -05:00
b6189a90f4 Merge branch 'ph-telemetry' of https://github.com/Infisical/infisical into ph-telemetry 2023-01-09 17:11:51 +07:00
d2c77d9985 Patch integrations Secret querying by workspaceId 2023-01-09 17:11:33 +07:00
6ce12c71e1 Merge pull request #205 from Infisical/activity-logs
Add endpoints for rollbacks and secret versions
2023-01-09 00:56:28 -08:00
8d53d2e4b1 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-09 10:38:51 +07:00
bd5dad71d4 Correct logging references 2023-01-09 10:27:36 +07:00
0bf8661350 fix: pattern folder based on i18next locales 2023-01-08 16:09:57 -03:00
35d23cf55c Finish preliminary /v2/secrets routes for batch/single CRUD secrets endpoints 2023-01-09 01:03:40 +07:00
69b819e7c4 refactor: adding translate pt-br in signup.json archive 2023-01-08 09:26:04 -03:00
d870ecc62a refactor: adding translate pt-br in setting-project.json archive 2023-01-08 09:25:52 -03:00
c0a0252cf5 refactor: adding translate pt-br in setting-personal.json archive 2023-01-08 09:25:38 -03:00
2f5186634c refactor: adding translate pt-br in setting-org.json archive 2023-01-08 09:25:27 -03:00
36525325fd refactor: adding translate pt-br in setting-members.json archive 2023-01-08 09:25:16 -03:00
a990a5ee7d refactor: adding translate pt-br in section-token.json archive 2023-01-08 09:24:55 -03:00
f2372bb265 refactor: adding translate pt-br in section-password.json archive 2023-01-08 09:24:44 -03:00
8c0046be87 refactor: adding translate pt-br in section-members.json archive 2023-01-08 09:24:29 -03:00
556858d1a8 refactor: adding translate pt-br in section-incident.json archive 2023-01-08 09:24:12 -03:00
2b147fce6e refactor: adding translate pt-br in nav.json archive 2023-01-08 09:23:31 -03:00
553be71ddf refactor: translate pt-br in login.json archive 2023-01-08 09:23:17 -03:00
9241020eb2 Added back latest tag 2023-01-08 03:12:30 -05:00
7e33f48a3b Added short commit to tag action 2023-01-08 03:06:40 -05:00
0312891f8b Merge pull request #203 from Infisical/signup-flow
Refactor of the signup flow
2023-01-07 16:50:38 -08:00
6de4eca4fc Refactored signup and added team invitation step 2023-01-07 16:40:28 -08:00
b0fb86a6ac Added docs link to the dashboard 2023-01-07 11:46:17 -08:00
96b254d7c3 refactor: adding translate pt-br in login.json archive 2023-01-07 13:51:16 -03:00
3f1eaa8d42 refactor: adding translate pt-br in integrations.json archive 2023-01-07 13:51:04 -03:00
3e56fe95d2 refactor: adding translate pt-br in dashboard.json archive 2023-01-07 13:50:51 -03:00
15553e972a refactor: adding translate pt-br in common.json archive 2023-01-07 12:09:43 -03:00
47ab0b4a0f Add endpoints for rolling back a workspace to a secret snapshot and rolling back a secret to a version 2023-01-07 20:12:53 +07:00
f3f6871d81 UX fixes around the app 2023-01-06 22:39:54 -08:00
a438b8b91b Merge pull request #187 from Infisical/snyk-upgrade-424d98e46758fcf23e4a0e06a413eb47
[Snyk] Upgrade @stripe/stripe-js from 1.36.0 to 1.46.0
2023-01-06 20:44:09 -08:00
498571b4fb Merge branch 'main' into snyk-upgrade-424d98e46758fcf23e4a0e06a413eb47 2023-01-06 20:41:44 -08:00
89136aab24 Merge pull request #197 from JoaoVictor6/confirm-secret-key-delete
Add popup before secret delete
2023-01-06 20:40:40 -08:00
eed6c75836 Merge pull request #188 from mocherfaoui/import-export-secrets
add ability to import/export secrets with comments
2023-01-06 20:26:36 -08:00
51368e6598 Trying to fix the telemetry issue in a pr check 2023-01-06 20:23:26 -08:00
7e534629ff Started adding telemetry to v2 routes 2023-01-06 17:32:19 -08:00
2c221dbb03 Fixed the missing field TS error 2023-01-06 17:08:27 -08:00
88ca056abb Fixing the typescript error 2023-01-06 17:04:46 -08:00
17133cd61b Fixing the yaml dependency version issue x2 2023-01-06 17:02:05 -08:00
2bbea36ce8 Fixing the yaml dependency version issue 2023-01-06 16:59:51 -08:00
5e03a54fa8 Merge branch 'main' into import-export-secrets 2023-01-06 16:07:11 -08:00
53273df51f Add single secrets v2 operations 2023-01-06 18:47:52 -05:00
a04fe00563 fix health check import 2023-01-06 15:55:30 -05:00
6afb276b35 Only show pass phrase env if env is not set 2023-01-06 15:13:55 -05:00
cb60151c0e Add status api 2023-01-06 10:51:15 -05:00
4c32f3dfd0 refactor: adding translate pt-br billing archive 2023-01-06 12:08:20 -03:00
c0d7b4ea88 feat: add "boilerplate" json 2023-01-06 11:41:32 -03:00
e6c631586a refactor: add "pt-BR" option 2023-01-06 11:40:51 -03:00
3e102fee3d chore: add language locale 2023-01-06 11:40:21 -03:00
9386efd7c4 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-05 23:15:26 -08:00
d90affbe87 UI bug fixes for the dashboard 2023-01-05 23:15:05 -08:00
7152e16288 add message to avoid typing in file passphrase 2023-01-06 01:30:08 -05:00
0d8e1042ba update crud CLI docs and remove projectID from run command 2023-01-06 01:18:12 -05:00
08dc4532f4 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-06 13:09:32 +07:00
85be609290 Remove comment from posthog fn 2023-01-06 00:59:54 -05:00
37998b84a9 Add logs for posthog in frontend 2023-01-05 23:15:30 -05:00
37c66c2499 update variable to check telemetry 2023-01-05 22:12:54 -05:00
12a9b60cc5 refactor: use DeleteActionButton component and improve types 2023-01-05 22:35:03 -03:00
4c79aadc22 feat: create dialog and button for confirm delete 2023-01-05 22:34:18 -03:00
a87dc2fcb9 refactor: add new sentences
I would add it in the Korean folder, but I don't know it :(
2023-01-05 22:33:05 -03:00
b60f0c1556 Increase cli version and decode hex for service token encryption key 2023-01-05 17:55:49 -05:00
054b3e3450 Merge branch 'main' of https://github.com/Infisical/infisical 2023-01-05 14:52:07 -08:00
de9d832669 Temporarily remove integrations as available 2023-01-05 14:24:16 -08:00
68b99b9f00 central logging and 2v service token 2023-01-05 16:58:10 -05:00
2c8c7a1777 add helper functions to check workspace and login status 2023-01-05 16:58:10 -05:00
764636cd47 Add service token v2 api into cli 2023-01-05 16:58:10 -05:00
c921eb8781 Telemetry and graphic changes 2023-01-05 13:11:13 -08:00
5a19f8ed32 Hotfix: state updates for dashabord and service tokens 2023-01-05 09:46:04 -08:00
8ddcccabfa Merge pull request #193 from Infisical/activity-logs
Activity logs
2023-01-05 19:50:32 +07:00
db36b81b0c Update package-lock.json 2023-01-05 19:47:31 +07:00
85cb3a11aa Fix frontend endpoints for service tokens and patch secret index.d.ts error 2023-01-05 19:25:33 +07:00
6e125b9e74 Merge remote-tracking branch 'origin' into activity-logs 2023-01-05 18:23:43 +07:00
4dce7e87dc Merge pull request #195 from Infisical/dependabot/npm_and_yarn/backend/json5-2.2.3
Bump json5 from 2.2.1 to 2.2.3 in /backend
2023-01-04 21:43:31 -08:00
ca2f44be54 Bump json5 from 2.2.1 to 2.2.3 in /backend
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-05 05:41:35 +00:00
021250a58c Update dependencies 2023-01-04 21:40:37 -08:00
3f0eefb091 Merge branch 'main' into activity-logs 2023-01-04 21:12:24 -08:00
cc408d8908 Update channel in workspace v2 controller 2023-01-05 12:05:49 +07:00
8b48205881 Fixed the service token bugs 2023-01-04 20:22:51 -08:00
098ae8533f Begin API key docs mint.json 2023-01-05 10:39:43 +07:00
9cf28fef5f Service tokens update on frontend 2023-01-04 18:54:45 -08:00
6c88c4dc36 Updated the image for signup invites 2023-01-04 17:34:30 -08:00
5428766bf6 modify getServiceTokenData to return single json 2023-01-04 20:17:11 -05:00
347b7201de Finished secret snapshots 2023-01-04 17:11:07 -08:00
d75d9ec324 Add get call secrets route for service token and jwt 2023-01-04 20:05:00 -05:00
880f4d25a9 print all errors during backend dev 2023-01-04 17:35:40 -05:00
fba40b5d4b print requestError logs in backend when in dev mode 2023-01-04 15:32:45 -05:00
68c488b8ee Add acceptedAuthModes for v2 secrets 2023-01-04 14:58:16 -05:00
68a8471292 remove accepted roles from secrets v2 api 2023-01-04 10:58:00 -05:00
7e026e82bb Merge crud cli api and cli changes 2023-01-04 10:43:57 -05:00
fe05732c46 update host to prod host 2023-01-04 10:39:32 -05:00
df7340e440 Fix merge conflicts 2023-01-04 21:31:58 +07:00
e364094d0d Merge pull request #192 from Infisical/cleaning
Remove accept statuses
2023-01-04 21:26:48 +07:00
136fda37f2 Merge remote-tracking branch 'origin' into cleaning 2023-01-04 21:24:38 +07:00
54676c630e Remove accept statuses 2023-01-04 21:14:25 +07:00
d7dd65b181 Merge pull request #191 from Infisical/api-key
Add API Key auth mode
2023-01-04 20:44:38 +07:00
d3efe351f1 Add DELETE route to API keys 2023-01-04 20:38:37 +07:00
c7fb9209c4 Complete v1 support for API key auth mode 2023-01-04 20:27:16 +07:00
8c7c41e091 Merge remote-tracking branch 'origin' into api-key 2023-01-04 18:15:54 +07:00
58830eab79 Move get service token data to v2 routes 2023-01-04 18:15:32 +07:00
ff0b053d12 Begin API Key functionality 2023-01-04 18:04:53 +07:00
15db792058 Patch requireAuth middleware in getting secret snapshot by id 2023-01-04 15:04:09 +07:00
5967a5cdba Add endpoint to return count of secret snapshots for a workspace 2023-01-04 10:00:05 +07:00
078c67f27c Add crud cli docs 2023-01-03 17:43:40 -05:00
3e945dd552 move v2 secret api to controller 2023-01-03 16:40:08 -05:00
59f5ad7710 add expand flag to crud sli 2023-01-03 16:39:33 -05:00
7e71e3ca57 v1 crud secrets complete 2023-01-03 16:09:47 -05:00
fb394de428 Remove unecessary imports 2023-01-03 16:02:05 +07:00
9727075b0b Resolve merge conflicts 2023-01-03 15:59:37 +07:00
c7c5a947d2 Modify secret snapshots to point to secret versions 2023-01-03 15:53:06 +07:00
9d0e269a2a Moved project id from dashboard to settings 2023-01-02 20:41:20 -08:00
92ab29f746 Merge branch 'activity-logs' of https://github.com/Infisical/infisical into activity-logs 2023-01-02 20:17:36 -08:00
fe0c466523 Moved the delete button to the sidebar 2023-01-02 20:17:16 -08:00
679db32de9 Begin docs for secret versioning, snapshots, and audit logs 2023-01-03 10:49:58 +07:00
daf8a73529 add dynmaic workspace and user creds for secrets cmd 2023-01-02 22:41:15 -05:00
d0949b2e19 Fixed the sorting buf with version history 2023-01-02 18:53:56 -08:00
212ca72c7b Merge pull request #190 from Infisical/service-token-v2
Infisical Token V2
2023-01-03 09:36:56 +07:00
48defca012 Merge remote-tracking branch 'origin' into service-token-v2 2023-01-03 09:34:48 +07:00
6845e9129a Updated icon for activity logs 2023-01-02 18:33:24 -08:00
e9601307ef Move service token data routes and controllers to v2 2023-01-03 09:33:00 +07:00
0ff8194cf8 Modify getWorkspaceLogs to accept sortBy query param 2023-01-03 09:19:07 +07:00
14286795e9 Merge branch 'activity-logs' of https://github.com/Infisical/infisical into activity-logs 2023-01-03 08:19:43 +07:00
ae5320e4fa Finished activity logs V1 2023-01-02 14:20:39 -08:00
03b7d3a5ce Wired frontend for logs 2023-01-02 09:57:02 -08:00
408eb482f1 remove --ignore-scripts for backend temporary 2023-01-02 11:26:27 -05:00
ccb1c31413 add set command for crud cli 2023-01-02 11:24:41 -05:00
a07d4e6dd1 update types name for secrets v2 api 2023-01-02 11:23:48 -05:00
72a9343a02 Fix merge conflicts 2023-01-02 22:51:25 +07:00
e99ee94a7b Modify service token format 2023-01-02 22:43:00 +07:00
4af839040e Patch actionNames on getWorkspacelogs 2023-01-02 15:43:21 +07:00
1c2a43ceea Clean unecessary imports 2023-01-02 15:24:28 +07:00
029443161f Modularize prepareDatabasse into initSecretVersioning 2023-01-02 14:52:08 +07:00
a8f0c391bc Finish v1 audit logs, secret versioning, version all unversioned secrets 2023-01-02 14:18:49 +07:00
0167342722 Improved frontend for activity logs 2023-01-01 18:27:31 -08:00
ac4b67d98e delete, get and create via cli 2023-01-01 19:22:09 -05:00
f2bd4aec39 Show full validation error 2023-01-01 13:13:03 -05:00
776b4c2922 update types for request body in secrets v2 api 2023-01-01 11:18:00 -05:00
939e9ba075 rename secret route with workspace and environment hierarchy 2023-01-01 10:39:23 -05:00
f015e6be6e Add batch delete api and batch create api 2023-01-01 02:12:24 -05:00
4576e8f6a7 Merge branch 'activity-logs' of https://github.com/Infisical/infisical into activity-logs 2023-01-01 11:18:58 +07:00
9c83808e2e Added populate statement 2022-12-31 20:17:40 -08:00
ce66e55c8e Merge remote-tracking branch 'origin' into activity-logs 2023-01-01 10:55:39 +07:00
0aff94cfb3 Add action error 2023-01-01 10:55:23 +07:00
4dac65eb8a Begin action route for getting an action by id 2023-01-01 10:54:23 +07:00
3c349b1e28 Merge remote-tracking branch 'origin' into activity-logs 2023-01-01 10:36:31 +07:00
6f054d8f2c Add requireSecretAuth middleware 2023-01-01 10:36:07 +07:00
b8a64714d2 Refactor auth middleware to accept multiple auth modes 2023-01-01 09:24:20 +07:00
3c6b1e51b5 Add non try catch error handle and fix bulk patch 2022-12-31 20:43:49 -05:00
7e4bf7f44b Continue developing service token v2 2023-01-01 07:10:47 +07:00
a5e8741442 update json5 2022-12-31 17:57:07 -05:00
60445727e9 merge with own change 2022-12-31 17:48:56 -05:00
618dc10e45 Added .NET to available frameworks 2022-12-31 01:11:13 -05:00
01d969190b Begin service token data refactor 2022-12-30 23:57:21 +03:00
9239b66b4b add new dependency: yaml 2022-12-30 01:06:00 +01:00
3715114232 add ability to export secrets with comments 2022-12-30 01:05:19 +01:00
5ef4e4cecb add ability to import secrets with comments 2022-12-30 01:03:07 +01:00
53502e22f4 fix: comments 2022-12-29 01:35:13 +01:00
4f808a24bb fix: upgrade @stripe/stripe-js from 1.36.0 to 1.46.0
Snyk has created this PR to upgrade @stripe/stripe-js from 1.36.0 to 1.46.0.

See this package in npm:
https://www.npmjs.com/package/@stripe/stripe-js

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
2022-12-28 21:49:13 +00:00
6fa84bf0cb Fix merge conflicts 2022-12-28 13:42:27 -05:00
fc61849120 Added .NET to available frameworks 2022-12-28 12:02:42 -05:00
b062c44742 Merge pull request #184 from jon4hz/dotnet
docs: add dotnet example
2022-12-28 11:34:13 -05:00
9ea12b93f7 Merge pull request #177 from jon4hz/completion
ci: add autocompletion and manpage
2022-12-28 11:14:02 -05:00
c409a89e93 docs: add dotnet example 2022-12-28 16:53:28 +01:00
d683e385ae fix: add support for custom annotations 2022-12-28 16:36:33 +01:00
4880cd84dc refactor: naming, labels and selectors 2022-12-28 15:42:47 +01:00
da5800c268 fix: allow setting of nodeport 2022-12-28 15:25:47 +01:00
21439761c3 fix: allow frontend service type overrides 2022-12-28 15:16:36 +01:00
bef857a7dc fix: allow image overrides 2022-12-28 15:02:52 +01:00
a36a59a4c0 Merge pull request #164 from Infisical/snyk-upgrade-1e6565738ff7cb9c1330ef01a3b0c7f1
[Snyk] Upgrade next from 12.3.1 to 12.3.4
2022-12-27 21:53:36 -05:00
b8e5a2c5c2 Merge branch 'main' into snyk-upgrade-1e6565738ff7cb9c1330ef01a3b0c7f1 2022-12-27 21:51:56 -05:00
e664a8b307 Added resuest for translations to README.md 2022-12-27 21:48:38 -05:00
146d683e75 Added translations to integrations 2022-12-27 21:22:12 -05:00
2032318491 Added translations to the sidebar 2022-12-27 20:56:16 -05:00
d4925e090d Fixed merge conflicts with translations; updated starting secrets 2022-12-27 19:12:38 -05:00
c9dc0243b6 Merge pull request #46 from gangjun06/feat/39
Localize Web UI (#39)
2022-12-27 18:37:12 -05:00
cc2803acee Fixed the merge conflicts on the signup page 2022-12-27 18:32:12 -05:00
c9d71ad887 Fixing react hook error 2022-12-27 18:24:27 -05:00
1459370458 Fixing more merge conflicts 2022-12-27 18:22:48 -05:00
cbb99844f1 Fixing merge conflicts for pages/users 2022-12-27 18:19:56 -05:00
f6faad267c Returned the missing package 2022-12-27 18:11:00 -05:00
74d883c15a Fixing merge conflicts 2022-12-27 17:57:14 -05:00
876c5f51c2 Fixed the bugs with secret overrides and the sidebar 2022-12-27 16:46:15 -05:00
4c43bdac93 Merge pull request #180 from akhilmhdh/fix/failed-workspace-membership-invite
fix(backend): resolved workspace membership invite failure
2022-12-27 16:13:09 -05:00
bb4d3ba581 Connected version history to backend 2022-12-27 15:02:50 -05:00
2c63559303 Updated request new invite illustration 2022-12-27 14:07:30 -05:00
c653f807f4 fix(api-frontend): resolved failure in inviting existing infisical users to organization 2022-12-27 23:22:47 +05:30
c28d857086 fix(backend): resolved workspace membership invite failure 2022-12-27 23:22:47 +05:30
babf35b44e Added translations to /noprojects route 2022-12-27 12:32:07 -05:00
16f240596a Add audit logs to pulls, still need to refactor 2022-12-27 12:30:33 -05:00
9497a26eb2 Add v1 audit log backend models and wiring to push secrets 2022-12-27 12:12:39 -05:00
cc251ba8ae Fixed the home directory transations 2022-12-27 11:52:23 -05:00
752a2a9085 Update README.md 2022-12-27 09:48:00 -05:00
019e90dc77 Fix merge conflicts 2022-12-27 09:34:07 -05:00
76da449463 fix(frontend): provided href invalid error 2022-12-27 15:59:11 +09:00
f550e4bc87 Merge pull request #179 from Infisical/new-routing
Migrated `POST /v1/secret/:workspaceId` to `POST /v2/workspace/:workspaceId/secrets`
2022-12-26 23:02:40 -05:00
f93594b62f Migrate POST /v1/secret/:workspaceId to /v2/workspace/:workspaceId/secrets and cleared room for /v2 secret routes 2022-12-26 22:50:59 -05:00
924e3d78a3 Merge remote-tracking branch 'origin' into new-routing 2022-12-26 21:45:45 -05:00
07c34c490f Begin moving /secret/workspaceId routes to /workspace/workspaceId 2022-12-26 21:45:26 -05:00
f3e3a9edf1 Added commenting functionality (#135) 2022-12-26 21:28:55 -05:00
ab3f3600e5 Merge pull request #178 from Infisical/new-routing
Create route and controller v1/v2 folder structure separation
2022-12-26 21:07:46 -05:00
229fef8874 Create route and controller v1/v2 folder structure separation 2022-12-26 21:02:39 -05:00
91dbbee9db install docs for arch linux 2022-12-26 20:34:05 -05:00
9ef6f9e554 ci: add autocompletion and manpage 2022-12-27 01:35:03 +01:00
2473ad03fa Update cli to 0.1.16 to trigger arch linux release 2022-12-26 19:28:18 -05:00
d284040c45 Merge pull request #176 from jon4hz/aur
ci: add aur release
2022-12-26 19:25:51 -05:00
addf04d54d Merge remote-tracking branch 'origin' into service-token-v2 2022-12-26 18:50:55 -05:00
09a04d456e chore: set license to MIT 2022-12-27 00:24:19 +01:00
a9c73ab04c ci: add aur release 2022-12-27 00:19:49 +01:00
a07bfbe4f8 Merge pull request #171 from Infisical/secret-versioning 2022-12-26 17:55:12 -05:00
e3b051226a Merge remote-tracking branch 'origin' into secret-versioning 2022-12-26 17:53:10 -05:00
0c6dfbe4b4 Fix commonjs import/export for patchRouterParam and make secret versioning/snapshots compatible with prev unversioned secrets 2022-12-26 17:52:13 -05:00
0e78336a6e Merge pull request #169 from Infisical/dashboard-sidebar
Added dashboard sidebar
2022-12-26 17:16:12 -05:00
740100a606 Fixed merge conflicts 2022-12-26 17:13:16 -05:00
0a108cbf07 Removed version history for now 2022-12-26 16:48:27 -05:00
dcbf8525bd update secret override desc 2022-12-26 16:01:19 -05:00
79ddada537 Add secret override to docs 2022-12-26 16:01:19 -05:00
44da5da300 update CLI version 2022-12-26 16:01:19 -05:00
9e28ba9b84 Add secret override flag to CLI 2022-12-26 16:01:19 -05:00
454d1d304a Update README.md 2022-12-26 15:10:27 -05:00
8f8c501a64 Merge pull request #172 from akhilmhdh/chore/cleanup-remove-react-import
chore(frontend): removed unneccessary react import in project
2022-12-26 15:08:51 -05:00
5294fe9302 Style updates 2022-12-26 14:58:07 -05:00
665c6b1a6d chore(frontend): removed unneccessary react import in project 2022-12-27 00:42:23 +05:30
676f340928 Fixed the TS error 2022-12-26 11:10:32 -05:00
c556072b5d Fixed the duplicate error 2022-12-26 10:59:47 -05:00
a4285df0ff Added welcome email to new users 2022-12-26 09:39:32 -05:00
cfea0dc66f chore(frontend): add useEffect to _app for translate 2022-12-26 22:41:56 +09:00
991e4b7bc6 chore(frontend): update some files about translate 2022-12-26 22:14:42 +09:00
5b8337ac41 Merge remote-tracking branch 'upstream/main' into feat/39 2022-12-26 22:11:09 +09:00
bd97e9ebef fix(frontend): navigate not working 2022-12-26 20:33:48 +09:00
20050bcba1 solved the bug with duplicate management 2022-12-25 21:37:53 -05:00
9f724b5ede Refactor EE secret versioning/snapshot access 2022-12-25 20:04:27 -05:00
8f765cba57 Merge remote-tracking branch 'origin' into secret-versioning 2022-12-25 20:03:55 -05:00
888d28d6b9 Continue work on API key 2022-12-25 19:19:56 -05:00
f642a46924 Merge pull request #165 from naorpeled/feat/frontend/adjust-registration-styles
feat(frontend/signup+login): improve accessibility and UX
2022-12-25 17:32:38 -05:00
f020b553b3 Solving merge conflicts 2022-12-25 17:28:55 -05:00
26fe1dd821 Move secret versioning and snapshot functionality into ee and begin license scoping 2022-12-25 17:08:21 -05:00
28335ceabd update version 2022-12-25 16:11:10 -05:00
ee0a95c1db add faq link when key ring error 2022-12-25 16:10:33 -05:00
78d4fb2300 create config dir if it doesn't exist 2022-12-25 16:02:14 -05:00
8c10ae78f5 update cli version 2022-12-25 14:59:59 -05:00
dd7b2dc1ed Remove creating instance on PersistentPreRun 2022-12-25 14:58:51 -05:00
3b6b9d41d1 Remove creating instance on PersistentPreRun 2022-12-25 14:47:46 -05:00
ff3370819d Merge remote-tracking branch 'origin/main' into secret-versioning 2022-12-25 14:31:35 -05:00
f37fc9c59d Small modifications to secret versioning/snapshot 2022-12-25 14:30:02 -05:00
859f4226ae update vault command Description 2022-12-25 14:16:45 -05:00
9d0c293600 Update vault docs and add cli FAQ 2022-12-25 14:08:34 -05:00
bec139315f Merge remote-tracking branch 'origin/main' into feat/frontend/adjust-registration-styles 2022-12-25 20:32:33 +02:00
b454a0400a Merge pull request #170 from Infisical/cross-compile-cgo
split builds for cross compile
2022-12-25 13:31:49 -05:00
33805ad385 split builds for cross compile 2022-12-25 13:04:59 -05:00
890aff813b Merge remote-tracking branch 'origin/main' into secret-versioning 2022-12-25 12:46:57 -05:00
d869968f88 Begin api-key functionality on backend 2022-12-25 12:45:43 -05:00
142ed1541c Merge branch 'main' into feat/frontend/adjust-registration-styles 2022-12-25 12:34:10 -05:00
f575ae84e0 removed console.log 2022-12-25 11:47:13 -05:00
0cb26a9495 Added memoization and did performance optimizations 2022-12-25 10:11:13 -05:00
d89af29070 Refactored dashboard to TS - still some bugs and inefficiencies 2022-12-25 00:33:37 -05:00
7f51aaf451 Add vault docs 2022-12-25 00:04:52 -05:00
2513250993 Update package-lock 2022-12-24 20:45:35 -05:00
c3b2e08a23 Merge pull request #152 from Zamion101/feat/error-handling
Refactoring backend codebase to use central error handling and logging
2022-12-24 20:43:40 -05:00
a12ae350a7 Merge branch 'main' into feat/error-handling 2022-12-24 20:34:55 -05:00
9c769853b4 Patch secret-override mechanism with versioning/snapshots 2022-12-24 20:01:33 -05:00
f22d4277dc add support for multiple keyring 2022-12-24 19:11:29 -05:00
d72e113570 feat: Patch Router#handle to catch Promise Rejection
In order to catch Promise rejections inside and outside of middlewares
as well as in route logic we need to patch `Router#handle` and add
.catch() to the function. With that addition it is possible to
catch rejections and handle inside `requestErrorHandler` middleware.
2022-12-24 23:36:58 +01:00
22d6aa8e46 fix: Fixed wrong naming for error handler middleware 2022-12-24 21:45:53 +01:00
6216f70950 chore: Split error handler middleware to a seperate file 2022-12-24 21:43:02 +01:00
9bbf380741 Merge remote-tracking branch 'origin' into secret-versioning 2022-12-24 14:53:23 -05:00
28818db757 refactor: Prefer use of RequestError and next() function on middlewares
Added:
- New error types such as `IntegrationNotFoundError`,
  `WorkspaceNotFoundError`, `AccountNotFoundError' and more.

Refactored:
- Refactored most of the middlewares and very little number of helper
  functions to use RequestError
- Deleted unused imports

Changed:
- Some of the error types in middlewares changed to more related error
  types.
- Environment variable of 'VERBOSE_ERROR_OUTPUT' changed to more
  reliable validation method in `config/index.ts` as per @dangtony98
  requested.
2022-12-24 20:52:26 +01:00
f7e3e48038 Merge branch 'secret-versioning' of https://github.com/Infisical/infisical into secret-versioning 2022-12-24 14:52:14 -05:00
c4ebea7422 Finish get secret versions route 2022-12-24 14:51:09 -05:00
dca3bd4fbb Complete v1 secret versioning and project secret snapshots 2022-12-24 14:51:09 -05:00
79333657ed Add parseInt to SMTP_PORT envar 2022-12-24 14:25:39 -05:00
3bcc4ead1f Correct SMTP_HOST default envar in docs 2022-12-24 14:22:14 -05:00
3341b08b22 Revamp contributing development docs 2022-12-24 14:21:21 -05:00
d4bab09927 Add self-hosting email configuration docs + examples, correct SMTP setup for SendGrid/Mailgun-specific setup 2022-12-24 11:10:56 -05:00
9d0efc5a22 Updated contributors and links 2022-12-23 23:26:25 -05:00
34d7db5c1e Merge pull request #166 from naorpeled/feat/frontend/add-resend-code-to-signup
feat(frontend/signup): add resend code functionality
2022-12-23 23:22:31 -05:00
205bf70861 Added overrides for secrets 2022-12-23 23:00:26 -05:00
5eefe75dac initial commit 2022-12-24 04:00:13 +02:00
417eddaeff cleanup 2022-12-24 03:43:03 +02:00
6405e9f43f cleanup 2022-12-24 03:37:10 +02:00
13e7883373 wip 2022-12-24 03:34:27 +02:00
d25f4ccc89 wip 2022-12-24 03:33:43 +02:00
71a7497ea7 initial commit 2022-12-24 03:28:58 +02:00
7e4454b2c7 fix: upgrade next from 12.3.1 to 12.3.4
Snyk has created this PR to upgrade next from 12.3.1 to 12.3.4.

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

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
2022-12-23 21:45:05 +00:00
e4e0370dad Complete v1 secret versioning and project secret snapshots 2022-12-23 10:06:37 -05:00
5324355006 Merge pull request #158 from Infisical/snyk-upgrade-678d7b4eeea6a4d9172b69ed3febbea2
[Snyk] Upgrade posthog-node from 2.2.0 to 2.2.2
2022-12-23 08:28:20 -05:00
baba99e19b Added 404 page 2022-12-23 08:15:46 -05:00
54f99f3db0 Added contributor to Readme 2022-12-23 07:50:09 -05:00
fc32647201 Embed project id into exported service tokens 2022-12-22 22:42:45 -05:00
87a9a587b9 Added state management to the dashboard sidebar 2022-12-22 21:42:24 -05:00
4c62783a8e Merge pull request #161 from wjhurley/patch-1
Fix typo in quickstart
2022-12-22 15:01:53 -05:00
ae6de31774 Fix typo in quickstart 2022-12-22 19:59:55 +00:00
41f6255b7b updated the nopojects page design 2022-12-22 14:53:27 -05:00
e4c3db074d Added contributor 2022-12-22 14:32:17 -05:00
13a650fb47 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-22 14:31:29 -05:00
d2c2ca89cb Added dragon to no projects and addec contributor to readme 2022-12-22 14:28:29 -05:00
fd6b3e96a3 Update GitHub actions docs and README integrations 2022-12-22 14:22:45 -05:00
092436b6a3 Finished the first ddraft of the dashboard sidebar 2022-12-22 14:15:12 -05:00
95ff1f185b Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-22 14:09:43 -05:00
ed2558a7ad Add GitHub Actions docs and remove GET Infisical public key route 2022-12-22 14:09:34 -05:00
c20f63633b update k8 self hosting doc 2022-12-22 13:39:19 -05:00
dc567b88ca update k8 self hosting doc 2022-12-22 13:39:19 -05:00
509a92a00d Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-22 13:36:23 -05:00
3f2d011fba Update contributing dev docs and envars, remove PUBLIC_KEY, PRIVATE_KEY server envar requirement 2022-12-22 13:36:15 -05:00
ca8bc212a3 update helm install command 2022-12-22 13:35:36 -05:00
ec0313ffa0 update helm chart 2022-12-22 13:26:51 -05:00
d8d3e02e92 Add ingress annotation as default value 2022-12-22 13:26:13 -05:00
ea5a758959 fix: upgrade posthog-node from 2.2.0 to 2.2.2
Snyk has created this PR to upgrade posthog-node from 2.2.0 to 2.2.2.

See this package in npm:
https://www.npmjs.com/package/posthog-node

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
2022-12-22 18:07:27 +00:00
e0973f0b28 Delete close_inactive_issues.yml 2022-12-22 13:04:30 -05:00
06e6443225 Merge pull request #149 from arthurzenika/patch-1
fix(helm-charts/frontend): port listenning to 3000
2022-12-22 13:02:53 -05:00
843757fcf5 fix: Wrong error handler middleware position
Error handler is a middleware that captures errors from throw and
next(...), so in order to be able to handle it needs to come after
all of the routing and other middlewares. Previously it was before
routuing logic and wasn't working, now it works as intended.

Also added check for LogLevel to Sentry for not sending false-positive
errors like `BadRequestError`, `UnauthorizedRequestError` and etc.
2022-12-22 17:51:53 +01:00
1dc03399ce Update close_inactive_issues.yml 2022-12-22 10:52:09 -05:00
a939d98099 Update README.md 2022-12-22 00:50:05 -05:00
55996440da Merge pull request #155 from jonerrr/main
Update envars for SMTP for greater versatility.
2022-12-22 00:22:47 -05:00
9643c75b42 Merge pull request #156 from Infisical/dependabot/npm_and_yarn/backend/jsonwebtoken-9.0.0
Bump jsonwebtoken from 8.5.1 to 9.0.0 in /backend
2022-12-21 23:42:45 -05:00
ebdf04b39f Merge pull request #125 from Infisical/snyk-upgrade-5062255a2e34f7370d0aefcb1261369d
[Snyk] Upgrade posthog-node from 2.1.0 to 2.2.0
2022-12-21 23:41:38 -05:00
6f40427425 Bump jsonwebtoken from 8.5.1 to 9.0.0 in /backend
Bumps [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) from 8.5.1 to 9.0.0.
- [Release notes](https://github.com/auth0/node-jsonwebtoken/releases)
- [Changelog](https://github.com/auth0/node-jsonwebtoken/blob/master/CHANGELOG.md)
- [Commits](https://github.com/auth0/node-jsonwebtoken/compare/v8.5.1...v9.0.0)

---
updated-dependencies:
- dependency-name: jsonwebtoken
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-22 04:40:42 +00:00
aaa87b2b85 Fix errors and complete v1 full-loop of GitHub integration with repository secrets 2022-12-21 23:16:50 -05:00
7763e33de6 Fix errors and complete v1 full-loop of GitHub integration with repository secrets 2022-12-21 23:15:22 -05:00
68bf1f6c8a update env vars for mail 2022-12-21 21:27:37 -05:00
6f5d7a831b refactor: Refactored middlewares to use RequestError
Refactored middlewares to use RequestError rather than using
`try {...}catch (err){...}`. With this change it's possible to manage
all error details within one place.

Added:
- Added Sentry.captureException to Error Handler
2022-12-22 01:42:02 +01:00
bd9041a62c feat: Add central error handler
Added:
- Added Midleware to capture and handle all errors
    - will catch error from `throw ...` and `next(...)`
- Added RequestError base error class to build consistent error objects
- Added common errors like `UnauthorizedRequestError`, `BadRequestError`
- Added consistent Logging solution using `winston`
    - Supports Loki Transporter using `winston-loki`
- Outputing Legal disclaimer to console when `TELEMETRY_ENABLED=true`

Changed:
- Changed console.log to getLogger() favor of using consistent logging
2022-12-22 00:55:41 +01:00
54b0285cbd Merge pull request #130 from gmgale/bot
Bot - Started Github integration
2022-12-21 17:11:41 -05:00
96caa587d6 Merge branch 'bot' into bot 2022-12-21 17:11:09 -05:00
009f9c6842 Continue developing activity logs backend 2022-12-21 16:27:04 -05:00
2d3255edc0 Merge pull request #140 from reginaldbondoc/main
Backend test setup
2022-12-21 16:06:58 -05:00
0ee563948d Update package-lock 2022-12-21 20:34:44 +01:00
95b8d27c59 Merge branch 'main' of github.com:reginaldbondoc/infisical 2022-12-21 20:33:14 +01:00
5790af7b8b Merge branch 'Infisical:main' into main 2022-12-21 20:32:59 +01:00
a3609b9b6f Align with comments 2022-12-21 20:32:38 +01:00
6e50adb9ff Fix merge conflicts 2022-12-21 11:36:47 -05:00
42a0a9d0c8 fix(helm-charts/frontend): port listenning to 3000 2022-12-21 17:34:35 +01:00
72664c5bb3 Merge branch 'activity-logs' of https://github.com/Infisical/infisical into activity-logs 2022-12-21 11:10:11 -05:00
f31424dc6a update helm chart to make envfrom optional 2022-12-21 10:49:10 -05:00
2bd9f4e6dd Merge pull request #128 from SH5H/fixError
Fix login runtime error
2022-12-21 07:55:36 -05:00
fdcf2dd716 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-21 01:55:30 -05:00
78a52422a2 Patch Netlify integration cases, add new docs for Heroku, Vercel, and Netlify 2022-12-21 01:55:23 -05:00
4b261c4a87 Update README.md 2022-12-20 22:53:02 -05:00
d93bdff2d4 Merge pull request #144 from Zamion101/fix/issue-142
Fix `JWT_SERVICE_SECRET` not present in docs
2022-12-20 22:06:44 -05:00
66cf2f6091 Update develoing.mdx related to issue #142 2022-12-21 02:45:42 +01:00
44482dda26 fix: Relocate 'JWT_SERVICE_SECRET' related to issue #142 2022-12-21 02:35:05 +01:00
f7b110759d add support for arch linux 2022-12-20 20:11:28 -05:00
45f92bdd93 update infisical helm chart version 2022-12-20 18:49:36 -05:00
ed8134da4f allow helm template to add secret ref 2022-12-20 18:48:53 -05:00
75e3a3b2d6 Merge branch 'Infisical:main' into main 2022-12-20 21:24:11 +01:00
958ef560f7 Merge pull request #6 from reginaldbondoc/reginaldbondoc-patch-5
Clean up
2022-12-20 21:23:58 +01:00
2eb2f3ff1c Use GH secret 2022-12-20 21:19:04 +01:00
4aaa01fb0e Clean up 2022-12-20 20:39:55 +01:00
83c3745045 PR comment on coverage 2022-12-20 20:39:08 +01:00
ecbacfb0c9 Make coverage comment 2022-12-20 20:34:26 +01:00
507b0bd97f Add yaml output 2022-12-20 14:10:53 -05:00
2bff7bbb5a Merge pull request #139 from Infisical/cli-multi-command
Cli multi command support
2022-12-20 13:19:10 -05:00
b09ae054dd Add docs for chained commands 2022-12-20 13:18:20 -05:00
dc9c6b9d13 add chained command support 2022-12-20 12:54:41 -05:00
fcf300fa96 Set output mode and file 2022-12-20 18:14:58 +01:00
3eb33d22cf Back to report.json 2022-12-20 17:55:05 +01:00
8a209049c5 Point to different coverage file 2022-12-20 17:49:29 +01:00
eee40fd583 Fix report steps 2022-12-20 16:50:43 +01:00
483d4f831d Merge branch 'main' of github.com:reginaldbondoc/infisical 2022-12-20 15:50:07 +01:00
4a1cf88ab4 Use different GHA for downloading artifact 2022-12-20 14:15:24 +01:00
f01e8cb33b add multi command 2022-12-19 20:21:23 -05:00
be35da7ac4 Merge pull request #2 from reginaldbondoc/reginaldbondoc-patch-1
Update app.ts
2022-12-20 01:27:32 +01:00
7eedefa0c9 Update app.ts 2022-12-20 01:17:04 +01:00
113ec55681 Correct workflow test 2022-12-20 01:13:13 +01:00
bd207dc65f Merge branch 'main' of github.com:reginaldbondoc/infisical 2022-12-20 01:04:33 +01:00
fc906e7dc7 Setup test, break apart entrypoint, & align GHA workflow 2022-12-20 00:56:53 +01:00
3ba62d1d97 Update README.md 2022-12-19 13:05:27 -05:00
5816ce3bf7 Patch lint integration issues on frontend 2022-12-19 12:14:58 -05:00
2d77fe9ca3 Fix lint issues 2022-12-19 12:05:19 -05:00
8bfab95cc7 Merging upstream changes. 2022-12-19 10:58:57 +01:00
11e64febdb Added frontend for Github integration. 2022-12-19 10:24:16 +01:00
7d280d4e30 Added event filter for logs 2022-12-18 21:53:21 -05:00
6bb24933bf Add back health check and fix json file not found 2022-12-18 20:10:17 -05:00
fbc38c553a Remove tests from backend docker img 2022-12-18 19:27:45 -05:00
6ba4701db8 delete Dockerfile.prod from frontend 2022-12-18 19:27:45 -05:00
c15a9301af Temp disable healthcheck 2022-12-18 19:16:56 -05:00
91052df5f9 Fix Typescript issues for frontend integrations 2022-12-18 18:57:50 -05:00
6ea26c135a Merge branch 'bot' 2022-12-18 17:34:18 -05:00
648e3e3bbf Continue developing log schema 2022-12-18 17:19:09 -05:00
5444382d5a Remove unecessary import in integrations 2022-12-18 16:27:38 -05:00
200bb12ad8 Move CLIENT_ID envars to be fetched from backend for (cloud) integration options 2022-12-18 16:10:58 -05:00
1447e055d1 Remove SITE_URL from frontend 2022-12-18 14:30:31 -05:00
4dac03ab94 Patch undefined siteId passthrough to API 2022-12-18 14:09:04 -05:00
20ea50bfaf Added intercom to docs 2022-12-18 13:32:08 -05:00
5474096ca9 Move Stay Up-to-Date to bottom to give integrations more spot light 2022-12-18 12:47:33 -05:00
06c1827f38 Remove prettier 2022-12-18 12:29:21 -05:00
9b7f036fd0 Merge pull request #127 from SH5H/testtest
Adding automatic linter for checking commit
2022-12-18 12:25:34 -05:00
547555591b Refactor integrations logic and replace hardcoded client ids with envars 2022-12-18 12:18:50 -05:00
516819507a Merge tag 'main' of https://github.com/Infisical/infisical into bot 2022-12-18 12:11:00 -05:00
75020568d3 Started Github integration. 2022-12-18 17:45:31 +01:00
02e5be20c2 Update README.md 2022-12-18 08:39:40 -05:00
6ed8f8af08 Fix login runtime error 2022-12-18 15:50:46 +09:00
de11c50563 Delete Prettier 2022-12-18 15:41:22 +09:00
33dddd440c move coming soon integ to bottom 2022-12-17 23:58:45 -05:00
19b909cd12 modify verify step in docs 2022-12-17 23:57:06 -05:00
cd59ca745d Add kubernetes operator docs 2022-12-17 23:48:12 -05:00
9d41f753f4 Added Intercom to Docs 2022-12-17 21:43:13 -05:00
939826f28c Merge branch 'logging' into activity-logs 2022-12-17 20:22:28 -05:00
fae27a0b6e Changed text for the activity page 2022-12-17 20:21:25 -05:00
2e84b7e354 Initial schema ideas for logging 2022-12-17 15:10:30 -05:00
e013a4ab93 remove namespace from sample 2022-12-17 13:59:54 -05:00
19daf1410a allow host api in spec and update spec names 2022-12-17 13:27:36 -05:00
4c29c88fde Adding automatic linter for checking commit 2022-12-18 00:36:08 +09:00
9218d2a653 Fixed the padding issue in the login page 2022-12-17 08:48:10 -05:00
6af59e47f5 yaml for kubectl install secrets operator 2022-12-16 16:48:21 -05:00
8183e61403 Update k8 read me 2022-12-16 16:37:31 -05:00
b4c616edd6 set image name for k8 2022-12-16 16:35:49 -05:00
c12eeac9b3 Add placeholder upcoming integrations to docs 2022-12-16 16:35:08 -05:00
033275ed69 update read me helm chart 2022-12-16 16:14:05 -05:00
a799e1bffc Add new workflow to push k8 operator to prod 2022-12-16 16:12:13 -05:00
36300cd19d Begin personal access token-based integrations 2022-12-16 15:44:50 -05:00
c8633bf546 Add new workflow to push k8 operator to prod 2022-12-16 13:57:04 -05:00
4ad4efe9a5 Added a basic framework for activity logs 2022-12-15 23:35:52 -05:00
7fe2e15a98 add substitution into k8 and fix loading token 2022-12-15 22:25:44 -05:00
72bf160f2e add secrets ready to sync condition 2022-12-15 20:02:22 -05:00
0ef9db99b4 Add reconcile loop 2022-12-15 19:08:30 -05:00
805f733499 authenticate k8 operator via token 2022-12-15 16:36:33 -05:00
595dc78e75 Add kubebuiler and types for secret groups 2022-12-15 16:29:43 -05:00
6c7d232a9e Fix merge conflicts for index 2022-12-15 15:31:14 -05:00
35fd1520e2 Add integration auth revocation 2022-12-15 15:27:01 -05:00
3626ef2ec2 add yaml export 2022-12-15 14:20:20 -05:00
a49fcf49f1 Rotate test OAuth2 token 2022-12-14 18:43:21 -05:00
787e54fb91 Finish Netlify integration v1 full-loop 2022-12-14 18:18:21 -05:00
59ab4bf7f9 fix: upgrade mongoose from 6.7.2 to 6.7.3
Snyk has created this PR to upgrade mongoose from 6.7.2 to 6.7.3.

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

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
2022-12-14 18:05:06 +00:00
0c30ee8ffa fix: upgrade posthog-node from 2.1.0 to 2.2.0
Snyk has created this PR to upgrade posthog-node from 2.1.0 to 2.2.0.

See this package in npm:
https://www.npmjs.com/package/posthog-node

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
2022-12-14 18:05:02 +00:00
d4bc92bd5b fix: upgrade axios from 1.1.3 to 1.2.0
Snyk has created this PR to upgrade axios from 1.1.3 to 1.2.0.

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

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
2022-12-14 18:04:59 +00:00
7efdbeb787 fix: upgrade @sentry/node from 7.19.0 to 7.21.1
Snyk has created this PR to upgrade @sentry/node from 7.19.0 to 7.21.1.

See this package in npm:
https://www.npmjs.com/package/@sentry/node

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
2022-12-14 18:04:55 +00:00
43daff29dc fix: upgrade @sentry/tracing from 7.19.0 to 7.21.1
Snyk has created this PR to upgrade @sentry/tracing from 7.19.0 to 7.21.1.

See this package in npm:
https://www.npmjs.com/package/@sentry/tracing

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
2022-12-14 18:04:51 +00:00
90537f2e6d Fixed the drag & drop zone 2022-12-14 00:36:05 -05:00
c9448656bf Fixed the TS error with signup invites 2022-12-13 23:30:53 -05:00
f3900213b5 Add telemetry output at backend container start 2022-12-13 21:59:12 -05:00
fe17d8459b Begin Netlify integration 2022-12-13 21:12:40 -05:00
2f54c4dd7e add sharp for prod nextjs build 2022-12-13 20:07:53 -05:00
c33b043f5f remove cookie path temporary 2022-12-13 18:54:45 -05:00
5db60c0dad update helm template; check for non string 2022-12-13 17:55:49 -05:00
1a3d3906da Change jid path 2022-12-13 16:25:19 -05:00
d86c335671 Begin Netlify integration 2022-12-13 15:47:27 -05:00
62f0b3f6df Patch EMAIL_TOKEN_LIFETIME expiring early 2022-12-13 15:22:07 -05:00
3e623922b4 Preliminary Vercel integration 2022-12-13 13:59:21 -05:00
d1c38513f7 update chart version 2022-12-13 12:48:29 -05:00
63253d515f quote secrets for template 2022-12-13 12:46:35 -05:00
584d309b80 Fixed the TS bug in signupinvite 2022-12-13 09:52:30 -05:00
07bb3496e7 Added the account recovery flow 2022-12-12 20:42:16 -05:00
c83c75db96 Merge pull request #116 from reginaldbondoc/main
Add healthchecks and test image before push
2022-12-12 13:31:44 -05:00
bcd18ab0af Ignore linting healthcheck & exclue in rate-limiting 2022-12-12 19:11:44 +01:00
1ea75eb840 Merge branch 'Infisical:main' into main 2022-12-12 18:15:13 +01:00
271c810692 Remove awkward lag when integration is loading its apps 2022-12-12 10:58:49 -05:00
dd05e2ac01 Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-12 10:58:26 -05:00
c6c2cfaaa5 Continue trimming frontend integration page and renaming variables 2022-12-12 10:36:13 -05:00
6f90064400 Add required backup key return fields for GET backup key 2022-12-12 08:31:20 -05:00
397c15d61e Continue integration frontend refactor 2022-12-12 08:26:09 -05:00
9e3ac6c31d Fix merge conflicts 2022-12-12 00:26:37 -05:00
10d57e9d88 Modularize integrations into json files, continue refactoring integrations frontend 2022-12-12 00:23:13 -05:00
95a1e9560e Added automatic secret sorting on the first pull 2022-12-11 21:28:59 -05:00
11e0790f13 Merge pull request #117 from LemmyMwaura/secrets-dash-jumping-bug-#51
🐛 Dashboard bugfix - Jumping Secrets #51
2022-12-11 21:20:04 -05:00
099ddd6805 fix:quotes - prettier 2022-12-12 01:08:59 +03:00
182db69ee1 Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-11 17:06:23 -05:00
01982b585f Add empty Vercel docs for integration reference 2022-12-11 17:02:06 -05:00
752ebaa3eb Add healthchecks and test image before push 2022-12-11 22:36:46 +01:00
117acce3f6 Merge branch 'main' of github.com:LemmyMwaura/infisical into secrets-dash-jumping-bug-#51 2022-12-11 23:50:35 +03:00
f94bf1f206 feat:only sort on reorder and cleanup changes 2022-12-11 23:31:31 +03:00
74d5586005 Modularize integration sections in frontend 2022-12-11 15:20:10 -05:00
8896e1232b Merge pull request #114 from Infisical/account-recovery
Begin reset password backend functionality
2022-12-11 14:55:54 -05:00
d456dcef28 refactor(perf):go back to passing index and using it to mutate keypair 2022-12-11 22:25:16 +03:00
eae2fc813a Convert JS to TS (#47) 2022-12-11 13:06:25 -05:00
7cdafe0eed Updated notifications 2022-12-11 13:01:40 -05:00
d410b42a34 Fix more merge conflicts and continue cleaning up integrations frontend 2022-12-11 12:21:49 -05:00
bacf9f2d91 Fix merge conflicts frontend integrations 2022-12-11 11:45:47 -05:00
3fc6b0c194 Refactoring integrations frontend (cleanup) 2022-12-11 11:44:19 -05:00
9dc1645559 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-11 11:38:25 -05:00
158c51ff3c Merge pull request #115 from SH5H/main
Convert JS to TS
2022-12-11 11:31:21 -05:00
864757e428 fix: add new row as map instead of list, bug fix due to updates 2022-12-11 17:58:41 +03:00
fa53a9e41d refactor: remove unused index values 2022-12-11 17:54:19 +03:00
36372ebef3 feat: find keypair by id during mutation 2022-12-11 17:53:49 +03:00
b3a50d657d refactor: call modify fns with id instead of index 2022-12-11 17:00:25 +03:00
c2f5f19f55 refactor: instead of the index and we pass the keypair id 2022-12-11 16:59:32 +03:00
2b6e69ce1b Fix Build Error 2022-12-11 15:51:29 +09:00
c4b4829694 Linting 2022-12-11 15:07:53 +09:00
d503102f75 Change tsconfig 2022-12-11 15:02:15 +09:00
4a14753b8c Convert TS to JS 2022-12-11 15:02:15 +09:00
2c834040b4 Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-10 23:25:46 -05:00
6f682250b6 Begin reset password backend functionality 2022-12-10 23:06:54 -05:00
52285a1f38 added long term secret token options 2022-12-10 22:29:47 -05:00
0e53b78708 feat(frontend): update translate library 2022-12-11 10:54:53 +09:00
31d6191251 Updated query-string package 2022-12-10 19:01:20 -05:00
d14ed06d4f Change frontend integration-bot wording 2022-12-10 17:46:14 -05:00
e2a84ce52e Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-10 17:00:10 -05:00
999f668f39 Merge pull request #112 from reginaldbondoc/main
Add local SMTP server for development
2022-12-10 16:56:36 -05:00
6b546034f4 Use Sentry instead of console logs 2022-12-10 22:05:19 +01:00
c2eaea21f0 Modify frontend to be compatible with full-loop for bot-based integrations 2022-12-10 15:43:22 -05:00
436f408fa8 Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-10 15:41:04 -05:00
da999107f3 Add local SMTP server for development 2022-12-10 21:10:05 +01:00
00ea296138 update cli install to remove $ from commands 2022-12-10 14:27:50 -05:00
968af64ee7 refactor: set data as a map instead of list 2022-12-10 22:24:02 +03:00
589e811e9b disable eslint for TELEMETRY_CAPTURING_ENABLED 2022-12-10 13:49:37 -05:00
4f312cfd1a update helm default values 2022-12-10 13:42:21 -05:00
3dccfc5404 add envs to frontend k8 2022-12-10 13:39:00 -05:00
2b3a996114 Update helm values example 2022-12-10 12:15:04 -05:00
93da106dbc test 2022-12-10 12:15:04 -05:00
7e544fcac8 Update Chart.yaml 2022-12-10 11:51:34 -05:00
c2eac43b4f remove prepare temporary 2022-12-10 11:21:35 -05:00
138acd28e8 Updated Infisical onboarding guide 2022-12-10 10:18:57 -05:00
1195398f15 update package lock 2022-12-10 09:35:32 -05:00
1fff273abb modify eslint rules 2022-12-10 09:28:19 -05:00
b586fcfd2e Add eslint disable next line 2022-12-10 09:26:54 -05:00
45ad639eaf Merge pull request #111 from SH5H/main
Add husky and lint-staged
2022-12-10 09:25:02 -05:00
e5342bd757 Added contributor to README.md 2022-12-10 08:54:35 -05:00
834c32aa7e Merge pull request #108 from adrianmarinwork/main
Updated the start developing guide as suggested in issue #75
2022-12-10 08:48:06 -05:00
df4dcc87e7 Improved MongoDB environment variables part
I have improved MongoDB environment variables part as requested in the pull request #108
2022-12-10 14:46:19 +01:00
d883c7ea96 Bring the last version of package file 2022-12-10 15:25:40 +09:00
5ed02955f8 Update lock file with npm audit fix command 2022-12-10 15:21:42 +09:00
064a9eb9cb Update root lock file 2022-12-10 15:13:23 +09:00
f805691c4f Update lock file in the frontend 2022-12-10 15:01:38 +09:00
4548931df3 Downgrade the version of lock file in frontend 2022-12-10 14:58:13 +09:00
6d1dc3845b Update README.md 2022-12-09 22:58:10 -05:00
40b42fdcb5 Update package-lock.json 2022-12-10 12:36:18 +09:00
ad907fa373 Remove useless dependencies 2022-12-10 12:28:46 +09:00
ae1088d3f6 Merge pull request #110 from arjunyel/password-autocomplete
Add autocomplete to sign in/up forms
2022-12-09 22:27:20 -05:00
9a3caac75f Add husky and lint-staged 2022-12-10 12:13:48 +09:00
1808ab6db8 fix variable assignment when pulling via token 2022-12-09 19:44:00 -05:00
aa554405c1 fix variable assignment when pulling via token 2022-12-09 19:36:55 -05:00
cd70128ff8 Add autocomplete to sign in/up forms 2022-12-09 17:26:08 -06:00
f49fe3962d Added progress bar for the Infisical Guide 2022-12-09 18:02:22 -05:00
9ee0c8f1b7 Fixed typescript issue for signupinvites 2022-12-09 17:20:17 -05:00
a9bd878057 Updated the start developing guide 2022-12-09 22:18:20 +01:00
a58f91f06b Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-09 13:23:52 -05:00
059f15b172 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-09 13:20:07 -05:00
caddb45394 Revise integrations docs 2022-12-09 13:00:07 -05:00
8266c4dd6d Fixed the undefined private key issue 2022-12-09 12:45:47 -05:00
9f82220f4e Remove frontend/backend sync operation for envars to integrations in favor of bot 2022-12-09 10:59:54 -05:00
3d25baa319 Add parseInt to token expiration 2022-12-09 10:10:38 -05:00
a8dfcae777 Merge pull request #106 from wanjohiryan/patch-2 2022-12-09 08:34:35 -05:00
228c8a7609 Rectified typo in README :) 2022-12-09 11:27:25 +03:00
a763d8b8ed Delete actions folder 2022-12-08 23:55:33 -05:00
d4e0a4992c Merge branch 'main' of https://github.com/Infisical/infisical into bot 2022-12-08 23:27:08 -05:00
1757f0d690 Complete v1 loop for bot-based integrations 2022-12-08 23:22:44 -05:00
96ebe3e3d2 feat(frontend): setting next-i18next 2022-12-09 09:20:37 +09:00
d516b295bf chore(frontend): add next-i18next library 2022-12-09 09:10:14 +09:00
b25908d91f add mono repo support 2022-12-08 19:02:39 -05:00
68d51d402a update docker docs, fix links, add export cmd 2022-12-08 19:02:17 -05:00
aa218d2ddc Update email regex 2022-12-08 11:23:53 -05:00
c36aa3591a Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-08 09:01:36 -05:00
58b252a9e9 Loosen email regex 2022-12-08 09:01:24 -05:00
dba4c03e37 Merge pull request #102 from Infisical/mv-turtle-patch-1
Update README.md
2022-12-07 23:49:35 -05:00
7b1be82bac Update README.md 2022-12-07 23:45:29 -05:00
3c449750d3 Merge pull request #92 from asharonbaltazar/feat-add-notif-timeout
Add Notification Timeout
2022-12-07 19:59:39 -05:00
006f61a0e8 Merge pull request #99 from reginaldbondoc/add-initial-quality-gate
Add PR gate workflows
2022-12-07 18:13:34 -05:00
ec85bfca04 Fix frontend build-arg 2022-12-07 23:45:19 +01:00
27353848c1 Add PR gate workflows 2022-12-07 23:25:41 +01:00
da6a8ccdea fix typo for layout 2022-12-07 16:45:45 -05:00
b5a4e42281 Merge pull request #98 from reginaldbondoc/main
Fix workspace type definition
2022-12-07 16:33:25 -05:00
1212b5a9db Change name 2022-12-07 22:23:47 +01:00
d99e21a91b Fix workspace type 2022-12-07 22:20:25 +01:00
112fc77a06 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-07 15:08:52 -05:00
c23be77738 Fixed the typescript error in Layout 2022-12-07 15:08:44 -05:00
0cea019bc2 Update push to docker action 2022-12-07 14:26:56 -05:00
1218dc09ed Merge pull request #93 from Infisical/snyk-upgrade-5c827737012080ebf6157c6e6fcd4f49
[Snyk] Upgrade typescript from 4.8.4 to 4.9.3
2022-12-07 13:47:22 -05:00
b89a221a5a Merge pull request #94 from Infisical/snyk-upgrade-5d7f8d449135ab93a65e39dda5bbc136
[Snyk] Upgrade @sentry/tracing from 7.17.4 to 7.19.0
2022-12-07 13:47:03 -05:00
c4124cc865 Merge branch 'main' into snyk-upgrade-5d7f8d449135ab93a65e39dda5bbc136 2022-12-07 13:46:50 -05:00
94b7a0aead Added another contributing option to readme 2022-12-07 13:44:11 -05:00
689f1d0d43 Merge pull request #95 from Infisical/snyk-upgrade-2f45d7571b1d2d04216e214a24b9e6c3
[Snyk] Upgrade @sentry/node from 7.17.4 to 7.19.0
2022-12-07 13:43:39 -05:00
c75c24d44e Merge pull request #96 from Infisical/snyk-upgrade-35bcf68242318cba0ddafd09b0bc335e
[Snyk] Upgrade express-rate-limit from 6.6.0 to 6.7.0
2022-12-07 13:43:26 -05:00
febdf48dea Merge pull request #97 from Infisical/snyk-upgrade-7100c673d3b4c2351515934460524fde
[Snyk] Upgrade mongoose from 6.7.1 to 6.7.2
2022-12-07 13:43:10 -05:00
688b383d8b Merge pull request #80 from reginaldbondoc/I-36-use-pre-built-frontend-image
I-36 Use pre-built frontend image instead of building Next.js app on boot
2022-12-07 13:36:43 -05:00
9436f40eac Merge pull request #90 from edgarrmondragon/feat/export-cmd
Add an `export` command
2022-12-07 13:35:26 -05:00
d45eff621b fix: upgrade mongoose from 6.7.1 to 6.7.2
Snyk has created this PR to upgrade mongoose from 6.7.1 to 6.7.2.

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

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
2022-12-07 18:15:40 +00:00
761a60a216 fix: upgrade express-rate-limit from 6.6.0 to 6.7.0
Snyk has created this PR to upgrade express-rate-limit from 6.6.0 to 6.7.0.

See this package in npm:
https://www.npmjs.com/package/express-rate-limit

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
2022-12-07 18:15:35 +00:00
0466bf4e3d fix: upgrade @sentry/node from 7.17.4 to 7.19.0
Snyk has created this PR to upgrade @sentry/node from 7.17.4 to 7.19.0.

See this package in npm:
https://www.npmjs.com/package/@sentry/node

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
2022-12-07 18:15:31 +00:00
16a1366e6c fix: upgrade @sentry/tracing from 7.17.4 to 7.19.0
Snyk has created this PR to upgrade @sentry/tracing from 7.17.4 to 7.19.0.

See this package in npm:
https://www.npmjs.com/package/@sentry/tracing

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
2022-12-07 18:15:27 +00:00
b06c8d241e fix: upgrade typescript from 4.8.4 to 4.9.3
Snyk has created this PR to upgrade typescript from 4.8.4 to 4.9.3.

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

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
2022-12-07 18:15:22 +00:00
7bbaf4fee8 feat: move notifs to bottom right 2022-12-07 12:16:53 -05:00
91c8fd14df feat: add notif type 2022-12-07 12:15:17 -05:00
9e1112eb52 fix: hide notifs wrapper if no notifs 2022-12-07 12:04:53 -05:00
6050e65a59 fix: notif type is optional 2022-12-07 12:02:01 -05:00
bbc23aca55 Use encoding/csv to build the CSV output 2022-12-07 10:59:10 -06:00
9098bdc751 refactor: Change type to ensure notifs within state are Required<> 2022-12-07 11:58:17 -05:00
251426b559 feat: Add notification timeout 2022-12-07 11:57:39 -05:00
793eaee0c6 Address PR feedback 2022-12-07 10:54:24 -06:00
349865e6ef fix: clearNotification now requires string 2022-12-07 11:52:51 -05:00
add3075439 feat: create prop for Notification; small readability changes 2022-12-07 11:52:18 -05:00
3db1ff2411 modify error model 2022-12-07 11:06:30 -05:00
8cd9e20fa6 Update readme star gif 2022-12-07 09:30:38 -05:00
5910bfbb4d Merge remote-tracking branch 'upstream/main' into feat/39 2022-12-07 20:45:49 +09:00
29ab13430f Merge branch 'main' into I-36-use-pre-built-frontend-image 2022-12-07 09:18:50 +01:00
90eb292721 Add an export command 2022-12-07 01:44:06 -06:00
5e1f6d3884 Merge pull request #84 from SH5H/main
Convert JS to TS
2022-12-06 23:36:56 -05:00
1310b176a9 Update README.md 2022-12-06 21:23:28 -05:00
ccf1010e94 Merge pull request #82 from asharonbaltazar/feat-error-notifs
Add Error Notifications
2022-12-06 21:09:07 -05:00
d46bf54a8d fix: wrap pushKeys args in obj 2022-12-06 21:07:40 -05:00
93703475fe Limit replacement to JS files only 2022-12-06 22:45:26 +01:00
991b10cc17 Convert JS to TS 2022-12-07 04:58:27 +09:00
60fcd34af5 Merge branch 'main' into feat-error-notifs 2022-12-06 13:10:31 -05:00
f60e0cf7ee feat: separate components from Notification Provider 2022-12-06 12:50:49 -05:00
9071fafd06 feat: fix import paths 2022-12-06 12:50:09 -05:00
9499aa1097 feat: move NotificationProvider into Notifications folder 2022-12-06 12:49:44 -05:00
389bf0b41f feat: add NotificationProvider to _app.tsx 2022-12-06 12:26:19 -05:00
6ed5b9e706 feat: create initial NotificationProvider component 2022-12-06 12:25:55 -05:00
209673d744 Merge branch 'main' into I-36-use-pre-built-frontend-image 2022-12-06 18:13:10 +01:00
baacc310bb Merge pull request #77 from SH5H/jsconvert
Convert JS to TS
2022-12-06 09:04:50 -05:00
242f7b80e7 Merge remote-tracking branch 'upstream/main' into feat/39 2022-12-06 22:07:29 +09:00
a0abf5339f translate(frontend): many files 2022-12-06 21:59:53 +09:00
1e16a18469 Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-06 02:28:40 -05:00
df1ade4f5f Changed [NAME]'s project to Example Project 2022-12-06 02:28:34 -05:00
944cc5b32c Fixed the bug with start keys 2022-12-06 02:27:28 -05:00
46fe724012 Begin developing bot, event, and integration pipeline 2022-12-06 00:23:16 -05:00
a83d536ea4 Improve GH Action workflow for building images 2022-12-06 00:17:24 +01:00
1454911085 Merge branch 'Infisical:main' into I-36-use-pre-built-frontend-image 2022-12-05 23:40:14 +01:00
9e73b3431e Delete workspace type file 2022-12-06 06:15:58 +09:00
4a105a72e9 Update comment for new util 2022-12-06 05:55:14 +09:00
a47decd31f Convert JS to TS 2022-12-06 05:53:34 +09:00
c5a422fe64 update self host docs 2022-12-05 15:26:20 -05:00
bb47f7a92f Pre-bake some vars and change telemetry handling 2022-12-05 21:17:59 +01:00
13f2ab9425 update steps number 2022-12-05 14:41:09 -05:00
ac2c50b161 Merge pull request #76 from Infisical/upload-helm-chart-action
Upload helm chart action and docs for k8
2022-12-05 14:33:03 -05:00
afb374ff13 Add docs for k8 support 2022-12-05 14:32:16 -05:00
e98b76cba5 remove manual namespace 2022-12-05 12:57:18 -05:00
3e2ed62e50 update readme of helm repo 2022-12-05 12:31:48 -05:00
8e15dfc3d9 Merge pull request #73 from Infisical/upload-helm-chart-action
Upload helm chart action
2022-12-05 12:20:32 -05:00
6fb22b68dd update workflow file name 2022-12-05 12:17:06 -05:00
05a19a2201 Rewrite upload steps action 2022-12-05 12:13:52 -05:00
9ee5f3d41b upload helm chart to cloudsmith 2022-12-05 12:04:23 -05:00
142a38ae3c Merge branch 'main' into I-36-use-pre-built-frontend-image 2022-12-05 17:50:11 +01:00
e67620c3ce helm release on dispatch 2022-12-05 10:48:18 -05:00
e8e6b72422 pause helm chart release 2022-12-05 10:46:56 -05:00
4cc4edcb7e Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-05 10:45:09 -05:00
2acea4f085 push new cli release to populate release page 2022-12-05 10:45:02 -05:00
0dd546813a Create helm-chart-release.yaml 2022-12-05 10:37:33 -05:00
d82dfa5504 create helm repo install file 2022-12-05 10:28:49 -05:00
b13b0693ba Updated menu in docs 2022-12-05 00:11:47 -05:00
e00c3ab9e2 Expanded contributing docs 2022-12-05 00:07:03 -05:00
088668e1b0 Fix let -> const 2022-12-04 23:18:39 -05:00
b21cb521da Tranforming more components to typescript (#47) 2022-12-04 23:03:57 -05:00
90ef67399b Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-04 22:37:59 -05:00
5e87a317fa helm mongo db deployment 2022-12-04 22:37:51 -05:00
6cdbc834ab Tranforming more components to typescript (#47) 2022-12-04 22:14:13 -05:00
b677ab6429 feat: add react TS types 2022-12-04 22:03:07 -05:00
3b002abcb6 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-04 17:57:35 -05:00
89c625750a complete helm chart without DB 2022-12-04 17:57:28 -05:00
3de79426e2 Merge pull request #71 from Infisical/maidul98-fixing-dockerhub-upload
Update docker-image.yml to push to docker hub
2022-12-04 13:11:54 -05:00
02284f350b Update docker-image.yml 2022-12-04 13:05:16 -05:00
0e011da41c Update docker-image.yml 2022-12-04 13:01:38 -05:00
4fca41f62c Update docker-image.yml 2022-12-04 12:59:37 -05:00
f7044d37cb Update docker-image.yml 2022-12-04 12:55:10 -05:00
2299cff7d8 Update docker-image.yml 2022-12-04 12:52:29 -05:00
9c66062e6e Update docker-image.yml 2022-12-04 12:49:17 -05:00
127f77d1ce Update docker-image.yml 2022-12-04 12:46:15 -05:00
040fa815df Merge pull request #70 from SH5H/lintRule
Add Lint rules
2022-12-04 10:36:29 -05:00
0eff4a7389 Change eslint config file format 2022-12-05 00:16:05 +09:00
43bf99e659 Add eslint rules 2022-12-05 00:15:22 +09:00
3bb3fd3531 Added another quick start step 2022-12-04 09:22:12 -05:00
2a1cb7c00d Merge branch 'main' of https://github.com/Infisical/infisical 2022-12-04 08:26:41 -05:00
74467320cb Updated onboarding example secrets 2022-12-04 08:26:38 -05:00
86b12b16bf Merge pull request #66 from SH5H/main
Update dashboard component
2022-12-04 07:38:42 -05:00
a5d509c541 Change DropZone component to tsx 2022-12-04 20:46:23 +09:00
249635f0cc Remove useless type 2022-12-04 20:45:56 +09:00
20d8d255cb Added a quick start guide for new users 2022-12-03 23:23:32 -05:00
57762ab73c Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-03 20:29:48 -05:00
656d375af0 add frontend/backend deployment Helm 2022-12-03 20:29:42 -05:00
f5035a4169 Update README.md 2022-12-03 20:10:56 -05:00
5f2d3056f1 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-12-03 19:59:26 -05:00
fa41baa8b6 Update dashboard component 2022-12-04 09:58:51 +09:00
fe15af4c28 add Helm chart 2022-12-03 19:58:23 -05:00
986a9449cf Merge pull request #63 from SH5H/fixBabelError
Fix babel error
2022-12-03 18:46:49 -05:00
de7e5016dd Fix babel error 2022-12-04 08:32:43 +09:00
8bbd5a1184 Fix typo in readme 2022-12-04 08:32:33 +09:00
212f1feeb6 Update README.md 2022-12-03 14:21:56 -05:00
4ae88b2e47 Fix merge conflict 2022-12-03 17:14:48 +01:00
43fb35381f Merge branch 'I-36-use-pre-built-frontend-image' of github.com:reginaldbondoc/infisical into I-36-use-pre-built-frontend-image 2022-12-03 16:24:05 +01:00
023c744a8e Merge branch 'main' into I-36-use-pre-built-frontend-image 2022-12-03 13:11:23 +01:00
da419361cb Update README.md 2022-12-02 12:23:20 -05:00
4c94ddd1b2 feat(frontend): add change language button in login page 2022-12-02 21:14:28 +09:00
6cebe171d9 feat(frontend): add change language button in personal setting 2022-12-02 21:09:51 +09:00
9685af21f3 translate(frontend): update namespace names, move translate keys, translate some keys into korean 2022-12-02 20:52:26 +09:00
42087923e0 remove scrollbars 2022-12-01 09:12:48 -05:00
906cedd168 remove scrollbars 2022-12-01 09:07:13 -05:00
f659be446d Fixed the UI bug with overflowing dots (#30) 2022-11-30 21:21:50 -05:00
63c4cfa651 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-30 20:55:35 -05:00
adf27351a3 Updated layout component 2022-11-30 20:55:32 -05:00
c1d4002551 Add a contributor to README.md 2022-11-30 19:46:10 -05:00
1c56c34211 Merge pull request #59 from reginaldbondoc/I-36-remove-dependency-to-path-const
I-36 Remove dependency to PATH const
2022-11-30 18:48:29 -05:00
d8aa5b5ff4 I-36 Remove dependency to PATH const 2022-11-30 23:39:23 +01:00
a5618681df Merge branch 'Infisical:main' into I-36-use-pre-built-frontend-image 2022-11-30 22:46:52 +01:00
a84fc847db Merge branch 'main' into I-36-use-pre-built-frontend-image 2022-11-30 22:10:27 +01:00
f8e7c3c7c6 Update menu sidebar design 2022-11-30 15:14:32 -05:00
f9bf418bf8 Fix the issues with scrolling in Dashboard Input Field 2022-11-30 14:36:26 -05:00
7950085fba hotfix: remove placeholder for dashboard input field 2022-11-30 08:45:19 -05:00
9e0860b9a6 hotfix: remove placeholder for dashboard input field 2022-11-30 08:43:04 -05:00
c3427d110a Change CSP references from SITE_URL to self 2022-11-29 23:41:31 -05:00
0fde680a11 Fixed the useRef issue 2022-11-29 22:27:41 -05:00
ef248e3944 Fixed the issue with overflowing secrets (#30) 2022-11-29 22:11:47 -05:00
c940e1ad16 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-29 20:27:44 -05:00
aa700f7670 Fixed the issue with dependencies order 2022-11-29 20:27:40 -05:00
f30da163d8 Modify integration prerequisite steps 2022-11-29 20:09:13 -05:00
1f63454f8d Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-29 20:02:16 -05:00
8e2794f6ab Update the list of integrations in the app 2022-11-29 20:02:14 -05:00
6468b356a6 Add Django, Rails integration instructions to docs and move cli login to overview page 2022-11-29 19:38:41 -05:00
c6777e43ed Add contributors to README.md 2022-11-29 17:00:47 -05:00
c68eaa613c Use pre-built frontend image in prod compose file 2022-11-29 21:09:50 +01:00
00dde5c2b4 Remove Angular integration from docs (not applicable), patch stripe subscriptions attr in billing page 2022-11-29 11:39:38 -05:00
ad19e33638 Merge pull request #57 from 0xflotus/patch-1
fix: small typo error
2022-11-29 10:22:42 -05:00
4117781cd1 fix: small typo error 2022-11-29 16:18:17 +01:00
11d169ad23 Add Nuxt, Remix, Vue framework integration instructions to docs 2022-11-29 01:22:06 -05:00
91827aed3e Fixed the issue with z-index for menu popups 2022-11-28 23:37:54 -05:00
fe339d9c0f Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-28 23:20:53 -05:00
e818029b48 Remove apostrophes because of eslint 2022-11-28 23:20:48 -05:00
4f5ad07ace Add more framework integrations to docs 2022-11-28 22:34:31 -05:00
3ece5a0390 Update README.md 2022-11-28 21:38:33 -05:00
078dbde45a Update README.md 2022-11-28 21:37:47 -05:00
8953fdf1d8 Updated integrations in README.md 2022-11-28 21:12:10 -05:00
bccee0c94d change link for infisical cloud to infisical.com instead of sign up page 2022-11-28 19:28:54 -05:00
7447288e5c Add integrations anchor and preliminary framework integrations to docs 2022-11-28 09:57:17 -05:00
7ab2289c99 Added more function specifications 2022-11-28 09:04:19 -05:00
1b07199383 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-28 08:41:55 -05:00
3c7cd01dd5 Converted crypto.js to typescript (#47) 2022-11-28 08:41:47 -05:00
4cfb275186 Merge branch 'main' into docs 2022-11-27 23:38:24 -05:00
0a9f4ffc4d Restructure docs around new anchors 2022-11-27 23:36:50 -05:00
e18a44f723 Delete linter.yml 2022-11-27 22:39:54 -05:00
62c2be255d Add a badge to README.md 2022-11-27 22:32:15 -05:00
914a78fb15 Merge remote-tracking branch 'upstream/main' into feat/39 2022-11-28 12:24:20 +09:00
3246d6b6df Removed useless pictures 2022-11-27 22:23:40 -05:00
5f670cd104 disable linter
disable linter  as there are issues with analyzing go
2022-11-27 21:44:34 -05:00
6722bd7bea Converted Error and ListBox to typescript (#47) 2022-11-27 20:51:41 -05:00
56acc4f888 Create linter.yml 2022-11-27 20:27:58 -05:00
798eb67296 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-27 19:10:55 -05:00
2fe45ec898 Added frontend checks for secret names (#35) 2022-11-27 19:10:49 -05:00
4d0969fbc3 Remove swagger in favor of Mintlify 2022-11-27 18:32:00 -05:00
c4711fc328 Merge branch 'main' into swagger 2022-11-27 15:58:31 -05:00
938c7bdb93 Initialize Swagger in dev, document /login1 for demonstration 2022-11-27 15:57:45 -05:00
634d5fe5c3 Converted Button component to typescript (#47) 2022-11-27 14:32:27 -05:00
1961b92340 Merge pull request #45 from gangjun06/refactor2
Edit frontend .prettierrc config
2022-11-27 08:29:20 -05:00
ef234a270f lint(frontend): apply two space instead of tab 2022-11-27 16:17:21 +09:00
66d2a2724e ref(frontend): add ~/const path 2022-11-27 16:12:25 +09:00
2729b409e6 lint(frontend): edit .prettierrc config 2022-11-27 15:51:34 +09:00
2c1398e71c translate(frontend): some setting page 2022-11-27 15:47:02 +09:00
14bffebc55 feat(frontend): update i18n default loader 2022-11-27 14:58:55 +09:00
c14d1d4fcc translate(frontend): dashboard page 2022-11-27 14:18:17 +09:00
20e5100bc4 translate(frontend): update namespaces 2022-11-27 13:56:18 +09:00
4bdb48d8f6 translate(frontend): navbar 2022-11-27 13:09:59 +09:00
8dfcc1f505 translate(frontend): add login, signup page translate 2022-11-27 12:21:23 +09:00
f5d2836199 add close inactive issues
will be marked stale after 30 days and if 14 days after it is still stale then it will be closed.
2022-11-26 18:10:46 -05:00
065b37ac11 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-26 17:55:31 -05:00
ca3b2fa1a0 Update Heroku docs 2022-11-26 17:55:26 -05:00
4ea284a1c5 Patch passing through stripe envars into frontend 2022-11-26 17:54:52 -05:00
4e58bbb13b Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-26 17:38:11 -05:00
3636e55604 rename substitute flag to expand and add to docs 2022-11-26 17:38:00 -05:00
a027b77479 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-26 16:56:39 -05:00
802f3678f7 Link integrations in README 2022-11-26 16:56:34 -05:00
a18e04a9a2 Merge branch 'substitute_envs' into main 2022-11-26 16:56:26 -05:00
b12856363e Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-26 16:55:34 -05:00
c1089497b7 Update folder structure 2022-11-26 16:55:25 -05:00
b9665786c8 Fix links in README 2022-11-26 16:54:50 -05:00
746ded9a53 Add substitute flag for run 2022-11-26 16:54:36 -05:00
dc3255adb7 Restructure and add quickstart to docs 2022-11-26 16:48:09 -05:00
b6e94ed9ec Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-26 16:45:28 -05:00
6fcf35a7bc updated the github picture 2022-11-26 14:51:13 -05:00
92c163d2fe Fix the background color bug in DashboardInput Field 2022-11-26 14:09:49 -05:00
b943264639 Add secret generation setting 2022-11-26 13:25:19 -05:00
02e969162a removed not needed dependencies from package-lock.json 2022-11-26 10:48:41 -05:00
b5f370aa5a removed not needed dependencies 2022-11-26 10:42:27 -05:00
b82eee1cc8 Remove yarn 2022-11-26 10:41:26 -05:00
8be8826e86 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-26 10:38:51 -05:00
ca9905a1ed Update contributors 2022-11-26 10:37:31 -05:00
f68468c6db Merge pull request #38 from gangjun06/refactor
refactoring frontend (add eslint, absolute import)
2022-11-26 10:31:05 -05:00
1b0e5d3b29 feat(frontend): setup next-translate package 2022-11-27 00:03:14 +09:00
825ea2aa3d chore: undo edit className 2022-11-26 19:48:11 +09:00
fa40bdaf17 ref(frontend): apply eslint simple import sort 2022-11-26 18:20:12 +09:00
568042fac0 ref(frontend): fix file or disable eslint rules 2022-11-26 18:20:02 +09:00
f2329884f8 chore(frontend): apply eslint config 2022-11-26 18:19:55 +09:00
22c184840c ref(frontend): update to absolute import 2022-11-26 18:19:41 +09:00
001df70e26 ref(frontend): add jsconfig 2022-11-26 16:03:36 +09:00
7d289d5180 rough imp, unable to debug further recursion 2022-11-25 17:51:28 -05:00
1bbe0e48c6 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-25 16:55:51 -05:00
22e7137e74 Add telemetry to frontend 2022-11-25 16:08:56 -05:00
22193bdac1 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-25 14:49:23 -05:00
00215eeedd Restructure docs for better navigation 2022-11-25 14:49:21 -05:00
d70d1f23d8 Update issue templates 2022-11-25 12:47:11 -05:00
3dd2ef7475 Update github issue template 2022-11-25 11:03:06 -05:00
ca384aeb1a Add Star-Infisical gif to Readme 2022-11-25 01:05:33 -05:00
f2a9544bbc Add support for referencing on frontend; updated Readme 2022-11-25 01:04:07 -05:00
d21bb11712 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-24 13:30:00 -05:00
5e04352725 fixed links to login, signup, blog, and docs 2022-11-24 13:29:31 -05:00
ac7351cf21 Create feature_request template 2022-11-24 13:12:56 -05:00
7e4b38a2f1 Add opt-out backend telemetry and fix dev compose frontend envars 2022-11-23 19:25:48 -05:00
b0eff2a9d3 Add opt-out backend telemetry 2022-11-23 19:23:07 -05:00
e02fa7bfd6 Update deployment config and docs 2022-11-23 14:16:53 -05:00
a35dedd7bb Add back passing NEXT_PUBLIC_ENV to frontend 2022-11-22 16:47:29 -05:00
094704ccd9 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-22 09:36:29 -05:00
76f9e3e856 update cli install with sudo 2022-11-22 09:36:24 -05:00
518872da0d Bring back auth/signup-specific rate limiters 2022-11-21 17:28:50 -05:00
5db5c6424a set docker build workflow manually 2022-11-21 13:36:54 -05:00
9c9fcde8b1 Update docker-image.yml 2022-11-21 13:33:56 -05:00
2439cbe095 update path to docker file for action 2022-11-21 13:31:40 -05:00
1c8e95f7e4 Update docker-image.yml 2022-11-21 13:21:02 -05:00
ab5779622a auto upload to docker hub action 2022-11-21 13:17:58 -05:00
fd3734192c Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-21 12:35:37 -05:00
74487b5307 Update broken contributing link 2022-11-21 12:35:33 -05:00
d1198049bf Update issue templates 2022-11-21 12:28:43 -05:00
0d4ce34730 Fix token spelling typo 2022-11-21 10:34:30 -05:00
47e1a81044 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-21 09:56:40 -05:00
505313c0d0 updated docker compose docs 2022-11-21 09:56:36 -05:00
f9879ce9af Update README 2022-11-21 09:10:52 -05:00
fd99b10fc4 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-21 08:49:20 -05:00
0b91fd69d6 add --republish to cloudsmith script 2022-11-21 08:49:14 -05:00
e05473ee8c Merge pull request #24 from Infisical/snyk-upgrade-841c2fe6b5fe2bb47e045ce10eedf269
[Snyk] Upgrade posthog-js from 1.32.4 to 1.34.0
2022-11-21 08:43:55 -05:00
b84538f670 update version 2022-11-21 08:31:30 -05:00
fd988eb63f Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-21 08:29:12 -05:00
3689d75bde update login email regex check 2022-11-21 08:29:06 -05:00
ebe6be201a Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-20 23:44:54 -05:00
4778e1ce6f Fix compose file platform target 2022-11-20 23:44:51 -05:00
e188524a93 update to new intro video for docs 2022-11-20 23:34:13 -05:00
676f5e121a update docker compose docs 2022-11-20 23:24:41 -05:00
d3189fda58 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-20 23:00:52 -05:00
7ce447efe4 get started to read me 2022-11-20 23:00:46 -05:00
d8b239892e Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-20 22:54:42 -05:00
896760903a Refactor envars for easier self-hosting 2022-11-20 22:54:38 -05:00
11b7309301 ignore .infisical.json 2022-11-20 22:51:57 -05:00
16061a0b8d increase version and fix infisical token name 2022-11-20 22:50:40 -05:00
fc49eaae18 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-20 18:47:36 -05:00
2f1e2acc69 Update to new backend 2022-11-20 18:47:21 -05:00
0f6675942d Remove build context for prod compose file 2022-11-20 12:54:03 -05:00
a8fbca6625 Update self-hosting docs 2022-11-20 12:49:32 -05:00
2420a41bb7 Added helpful message to show secrets are being injected 2022-11-20 00:24:19 -05:00
47ad4f0620 update .deb files to be any-distro/any-version 2022-11-20 00:12:10 -05:00
5ee323ee26 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-19 23:52:30 -05:00
e64ba7e0f2 merge with tony's docs changes 2022-11-19 23:52:25 -05:00
43c4303b68 Add LICENSE 2022-11-19 23:51:35 -05:00
83f56e0621 docker compose docs 2022-11-19 23:50:19 -05:00
067d8ff025 Update development docs 2022-11-19 22:04:07 -05:00
0f3e29bb26 Remove security card from intro in docs 2022-11-19 21:12:43 -05:00
870a66cc5b Add security section to docs and new YT video 2022-11-19 21:07:50 -05:00
59ac40b09d fix: upgrade posthog-js from 1.32.4 to 1.34.0
Snyk has created this PR to upgrade posthog-js from 1.32.4 to 1.34.0.

See this package in npm:
https://www.npmjs.com/package/posthog-js

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
2022-11-19 22:35:10 +00:00
67b21e8705 Update docs for Infisical Token 2022-11-19 15:07:03 -05:00
af3b1e8359 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-19 14:16:29 -05:00
2062d667e8 change flag --stage to --env 2022-11-19 14:16:17 -05:00
b164a2f7ac change flag --stage to --env 2022-11-19 11:35:13 -05:00
321b040fe7 Merge branch 'main' of https://github.com/Infisical/infisical 2022-11-19 10:24:41 -05:00
96cbdfdaca Continue modifying docs 2022-11-19 10:24:39 -05:00
e66c30b855 update go dependencies 2022-11-19 10:09:27 -05:00
7c78b0f443 Merge pull request #19 from hanywang2/main
Update anchor colors and background image
2022-11-18 21:49:43 -05:00
f832fdfb0c Update anchor colors and background image 2022-11-19 01:04:54 +00:00
0f6756f2f1 Update README 2022-11-18 18:35:22 -05:00
82621e34a8 Merge branch 'main' of https://github.com/Infisical/infisical into main 2022-11-18 18:25:14 -05:00
94abacbf61 Update docs for cli install to use tabs 2022-11-18 18:24:58 -05:00
45466741f1 Update CONTRIBUTING.md 2022-11-18 18:16:53 -05:00
f38ec6605d Resolve merge with README/docs 2022-11-18 18:13:48 -05:00
baa0a21b38 Add boilerplate nginx setup for prod self-hosting 2022-11-18 18:12:27 -05:00
cf216dfbbf Added a new contributor 2022-11-18 18:06:21 -05:00
8cef83a90b Merge pull request #18 from tobias-mintlify/patch-1
Switch to new gradient syntax
2022-11-18 15:01:22 -05:00
41ce9cea7c Switch to new gradient syntax 2022-11-18 14:58:03 -05:00
688aa856ab fix cloud smith upload to cd into dist 2022-11-18 00:35:16 -05:00
1209 changed files with 119486 additions and 25998 deletions

View File

@ -1,21 +1,19 @@
# Keys
# Required keys for platform encryption/decryption ops
PRIVATE_KEY=replace_with_nacl_sk
PUBLIC_KEY=replace_with_nacl_pk
ENCRYPTION_KEY=replace_with_lengthy_secure_hex
# Required key for platform encryption/decryption ops
ENCRYPTION_KEY=6c1fe4e407b8911c104518103505b218
# JWT
# Required secrets to sign JWT tokens
JWT_SIGNUP_SECRET=replace_with_lengthy_secure_hex
JWT_REFRESH_SECRET=replace_with_lengthy_secure_hex
JWT_AUTH_SECRET=replace_with_lengthy_secure_hex
JWT_SIGNUP_SECRET=3679e04ca949f914c03332aaaeba805a
JWT_REFRESH_SECRET=5f2f3c8f0159068dc2bbb3a652a716ff
JWT_AUTH_SECRET=4be6ba5602e0fa0ac6ac05c3cd4d247f
JWT_SERVICE_SECRET=f32f716d70a42c5703f4656015e76200
# JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string
# describing a time span (e.g. 60, "2 days", "10h", "7d")
JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME=
JWT_SERVICE_SECRET=
JWT_SIGNUP_LIFETIME=
# Optional lifetimes for OTP expressed in seconds
@ -27,32 +25,34 @@ EMAIL_TOKEN_LIFETIME=
# Required
MONGO_URL=mongodb://root:example@mongo:27017/?authSource=admin
# Optional credentials for MongoDB container instance
# Optional credentials for MongoDB container instance and Mongo-Express
MONGO_USERNAME=root
MONGO_PASSWORD=example
# Mongo-Express vars (needed for development only)
ME_CONFIG_MONGODB_ADMINUSERNAME=root
ME_CONFIG_MONGODB_ADMINPASSWORD=example
ME_CONFIG_MONGODB_URL=mongodb://root:example@mongo:27017/
# Website URL
# Required
NODE_ENV=development
NEXT_PUBLIC_WEBSITE_URL=http://localhost:8080
SITE_URL=http://localhost:8080
# Mail/SMTP
# Required to send emails
# By default, SMTP_HOST is set to smtp.gmail.com
SMTP_HOST=smtp.gmail.com
SMTP_NAME=Team
SMTP_USERNAME=team@infisical.com
SMTP_PASSWORD=
SMTP_HOST= # required
SMTP_USERNAME= # required
SMTP_PASSWORD= # required
SMTP_PORT=587
SMTP_SECURE=false
SMTP_FROM_ADDRESS= # required
SMTP_FROM_NAME=Infisical
# Integration
# Optional only if integration is used
OAUTH_CLIENT_SECRET_HEROKU=
OAUTH_TOKEN_URL_HEROKU=
CLIENT_ID_HEROKU=
CLIENT_ID_VERCEL=
CLIENT_ID_NETLIFY=
CLIENT_ID_GITHUB=
CLIENT_SECRET_HEROKU=
CLIENT_SECRET_VERCEL=
CLIENT_SECRET_NETLIFY=
CLIENT_SECRET_GITHUB=
CLIENT_SLUG_VERCEL=
# Sentry (optional) for monitoring errors
SENTRY_DSN=
@ -64,7 +64,7 @@ POSTHOG_PROJECT_API_KEY=
STRIPE_SECRET_KEY=
STRIPE_PUBLISHABLE_KEY=
STRIPE_WEBHOOK_SECRET=
STRIPE_PRODUCT_CARD_AUTH=
STRIPE_PRODUCT_PRO=
STRIPE_PRODUCT_STARTER=
STRIPE_PRODUCT_TEAM=
STRIPE_PRODUCT_PRO=
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=

4
.eslintignore Normal file
View File

@ -0,0 +1,4 @@
node_modules
built
healthcheck.js
tailwind.config.js

29
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
### Describe the bug
A clear and concise description of what the bug is.
### To Reproduce
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
### Expected behavior
A clear and concise description of what you expected to happen.
### Screenshots
If applicable, add screenshots to help explain your problem.
### Platform you are having the issue on:
### Additional context
Add any other context about the problem here.

View File

@ -0,0 +1,17 @@
---
name: Feature Request
about: Let us now what feature you would want to have in Infisical
title: ''
labels: 'feature request'
assignees: ''
---
### Feature description
A clear and concise description of what the the feature should be.
### Why would it be useful?
Why would this feature be useful for Infisical users?
### Additional context
Add any other context about the problem here.

BIN
.github/images/star-infisical.gif vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

22
.github/pull_request_template.md vendored Normal file
View File

@ -0,0 +1,22 @@
# 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.*
## Type ✨
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation
# Tests 🛠️
*Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. You may want to add screenshots when relevant and possible*
```sh
# Here's some code block to paste some code snippets
```
---
- [ ] 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). 📝

View File

@ -0,0 +1,30 @@
version: '3'
services:
backend:
container_name: infisical-backend-test
restart: unless-stopped
depends_on:
- mongo
image: infisical/backend:test
command: npm run start
environment:
- NODE_ENV=production
- MONGO_URL=mongodb://test:example@mongo:27017/?authSource=admin
- MONGO_USERNAME=test
- MONGO_PASSWORD=example
networks:
- infisical-test
mongo:
container_name: infisical-mongo-test
image: mongo
restart: always
environment:
- MONGO_INITDB_ROOT_USERNAME=test
- MONGO_INITDB_ROOT_PASSWORD=example
networks:
- infisical-test
networks:
infisical-test:

26
.github/resources/healthcheck.sh vendored Executable file
View File

@ -0,0 +1,26 @@
# Name of the target container to check
container_name="$1"
# Timeout in seconds. Default: 60
timeout=$((${2:-60}));
if [ -z $container_name ]; then
echo "No container name specified";
exit 1;
fi
echo "Container: $container_name";
echo "Timeout: $timeout sec";
try=0;
is_healthy="false";
while [ $is_healthy != "true" ];
do
try=$(($try + 1));
printf "■";
is_healthy=$(docker inspect --format='{{json .State.Health}}' $container_name | jq '.Status == "healthy"');
sleep 1;
if [[ $try -eq $timeout ]]; then
echo " Container was not ready within timeout";
exit 1;
fi
done

71
.github/values.yaml vendored Normal file
View File

@ -0,0 +1,71 @@
frontend:
enabled: true
name: frontend
podAnnotations: {}
deploymentAnnotations:
secrets.infisical.com/auto-reload: "true"
replicaCount: 2
image:
repository: infisical/frontend
tag: "latest"
pullPolicy: Always
kubeSecretRef: managed-secret-frontend
service:
annotations: {}
type: ClusterIP
nodePort: ""
frontendEnvironmentVariables: null
backend:
enabled: true
name: backend
podAnnotations: {}
deploymentAnnotations:
secrets.infisical.com/auto-reload: "true"
replicaCount: 2
image:
repository: infisical/backend
tag: "latest"
pullPolicy: Always
kubeSecretRef: managed-backend-secret
service:
annotations: {}
type: ClusterIP
nodePort: ""
backendEnvironmentVariables: null
## Mongo DB persistence
mongodb:
enabled: true
persistence:
enabled: false
## By default the backend will be connected to a Mongo instance within the cluster
## However, it is recommended to add a managed document DB connection string for production-use (DBaaS)
## Learn about connection string type here https://www.mongodb.com/docs/manual/reference/connection-string/
## e.g. "mongodb://<user>:<pass>@<host>:<port>/<database-name>"
mongodbConnection:
externalMongoDBConnectionString: ""
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "nginx"
# cert-manager.io/issuer: letsencrypt-nginx
hostName: gamma.infisical.com ## <- Replace with your own domain
frontend:
path: /
pathType: Prefix
backend:
path: /api
pathType: Prefix
tls:
[]
# - secretName: letsencrypt-nginx
# hosts:
# - infisical.local
mailhog:
enabled: false

41
.github/workflows/be-test-report.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: "Backend Test Report"
on:
workflow_run:
workflows: ["Check Backend Pull Request"]
types:
- completed
jobs:
be-report:
name: Backend test report
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 📁 Download test results
id: download-artifact
uses: dawidd6/action-download-artifact@v2
with:
name: be-test-results
path: backend
workflow: check-be-pull-request.yml
workflow_conclusion: success
- name: 📋 Publish test results
uses: dorny/test-reporter@v1
with:
name: Test Results
path: reports/jest-*.xml
reporter: jest-junit
working-directory: backend
- name: 📋 Publish coverage
uses: ArtiomTr/jest-coverage-report-action@v2
id: coverage
with:
output: comment, report-markdown
coverage-file: coverage/report.json
github-token: ${{ secrets.GITHUB_TOKEN }}
working-directory: backend
- uses: marocchino/sticky-pull-request-comment@v2
with:
message: ${{ steps.coverage.outputs.report }}

View File

@ -0,0 +1,42 @@
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
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,41 @@
name: Check Frontend Pull Request
on:
pull_request:
types: [ opened, synchronize ]
paths:
- 'frontend/**'
- '!frontend/README.md'
- '!frontend/.*'
- 'frontend/.eslintrc.js'
jobs:
check-fe-pr:
name: Check
runs-on: ubuntu-latest
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: frontend/package-lock.json
-
name: 📦 Install dependencies
run: npm ci --only-production --ignore-scripts
working-directory: frontend
# -
# name: 🧪 Run tests
# run: npm run test:ci
# working-directory: frontend
-
name: 🏗️ Run build
run: npm run build
working-directory: frontend

138
.github/workflows/docker-image.yml vendored Normal file
View File

@ -0,0 +1,138 @@
name: Build, Publish and Deploy to Gamma
on: [workflow_dispatch]
jobs:
backend-image:
name: Build backend image
runs-on: ubuntu-latest
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: 📦 Build backend and export to Docker
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
load: true
context: backend
tags: infisical/backend:test
- name: ⏻ Spawn backend container and dependencies
run: |
docker compose -f .github/resources/docker-compose.be-test.yml up --wait --quiet-pull
- name: 🧪 Test backend image
run: |
./.github/resources/healthcheck.sh infisical-backend-test
- name: ⏻ Shut down backend container and dependencies
run: |
docker compose -f .github/resources/docker-compose.be-test.yml down
- name: 🏗️ Build backend and push
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
push: true
context: backend
tags: infisical/backend:${{ steps.commit.outputs.short }},
infisical/backend:latest
platforms: linux/amd64,linux/arm64
frontend-image:
name: Build frontend image
runs-on: ubuntu-latest
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: Save commit hashes for tag
id: commit
uses: pr-mpt/actions-commit-hash@v2
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Set up Depot CLI
uses: depot/setup-action@v1
- name: 📦 Build frontend and export to Docker
uses: depot/build-push-action@v1
with:
load: true
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
project: 64mmf0n610
context: frontend
tags: infisical/frontend:test
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
- name: ⏻ Spawn frontend container
run: |
docker run -d --rm --name infisical-frontend-test infisical/frontend:test
- name: 🧪 Test frontend image
run: |
./.github/resources/healthcheck.sh infisical-frontend-test
- name: ⏻ Shut down frontend container
run: |
docker stop infisical-frontend-test
- name: 🏗️ Build frontend and push
uses: depot/build-push-action@v1
with:
project: 64mmf0n610
push: true
token: ${{ secrets.DEPOT_PROJECT_TOKEN }}
context: frontend
tags: infisical/frontend:${{ steps.commit.outputs.short }},
infisical/frontend:latest
platforms: linux/amd64,linux/arm64
build-args: |
POSTHOG_API_KEY=${{ secrets.PUBLIC_POSTHOG_API_KEY }}
gamma-deployment:
name: Deploy to gamma
runs-on: ubuntu-latest
needs: [frontend-image, backend-image]
steps:
- name: ☁️ Checkout source
uses: actions/checkout@v3
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Install infisical helm chart
run: |
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
helm repo update
- name: Install kubectl
uses: azure/setup-kubectl@v3
- name: Install doctl
uses: digitalocean/action-doctl@v2
with:
token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }}
- name: Save DigitalOcean kubeconfig with short-lived credentials
run: doctl kubernetes cluster kubeconfig save --expiry-seconds 600 k8s-1-25-4-do-0-nyc1-1670645170179
- name: switch to gamma namespace
run: kubectl config set-context --current --namespace=gamma
- name: test kubectl
run: kubectl get ingress
- name: Download helm values to file and upgrade gamma deploy
run: |
wget https://raw.githubusercontent.com/Infisical/infisical/main/.github/values.yaml
helm upgrade infisical infisical-helm-charts/infisical --values values.yaml --recreate-pods
if [[ $(helm status infisical) == *"FAILED"* ]]; then
echo "Helm upgrade failed"
exit 1
else
echo "Helm upgrade was successful"
fi

View File

@ -0,0 +1,22 @@
name: Release Helm Charts
on: [workflow_dispatch]
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: v3.10.0
- name: Install python
uses: actions/setup-python@v4
- name: Install Cloudsmith CLI
run: pip install --upgrade cloudsmith-cli
- name: Build and push helm package to Cloudsmith
run: cd helm-charts && sh upload-to-cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}

View File

@ -1,10 +1,10 @@
name: goreleaser
name: Build and release CLI
on:
push:
# run only against tags
tags:
- 'v*'
- "v*"
permissions:
contents: write
@ -13,29 +13,44 @@ permissions:
jobs:
goreleaser:
runs-on: ubuntu-latest
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- run: git fetch --force --tags
- run: echo "Ref name ${{github.ref_name}}"
- uses: actions/setup-go@v3
with:
go-version: '>=1.19.3'
go-version: ">=1.19.3"
cache: true
cache-dependency-path: cli/go.sum
- uses: goreleaser/goreleaser-action@v2
- name: libssl1.1 => libssl1.0-dev for OSXCross
run: |
echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' | sudo tee -a /etc/apt/sources.list
sudo apt update && apt-cache policy libssl1.0-dev
sudo apt-get install libssl1.0-dev
- name: OSXCross for CGO Support
run: |
mkdir ../../osxcross
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
- uses: goreleaser/goreleaser-action@v4
with:
distribution: goreleaser
version: latest
args: release --rm-dist
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
AUR_KEY: ${{ secrets.AUR_KEY }}
- uses: actions/setup-python@v4
- run: pip install --upgrade cloudsmith-cli
- name: Publish to CloudSmith
run: sh upload_to_cloudsmith.sh
- name: Publish to CloudSmith
run: sh cli/upload_to_cloudsmith.sh
env:
CLOUDSMITH_API_KEY: ${{ secrets.CLOUDSMITH_API_KEY }}

View File

@ -0,0 +1,29 @@
name: Release Docker image for K8 operator
on: [workflow_dispatch]
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 🔧 Set up QEMU
uses: docker/setup-qemu-action@v1
- name: 🔧 Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: 🐋 Login to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
id: docker_build
uses: docker/build-push-action@v2
with:
context: k8-operator
push: true
platforms: linux/amd64,linux/arm64
tags: infisical/kubernetes-operator:latest

9
.gitignore vendored
View File

@ -12,6 +12,8 @@ node_modules
.DS_Store
/dist
/completions/
/manpages/
# frontend
@ -25,7 +27,9 @@ node_modules
.env
# testing
/coverage
coverage
reports
junit.xml
# next.js
/.next/
@ -49,3 +53,6 @@ yarn-error.log*
.env.production.local
.vercel
.env.infisical
# Infisical init
.infisical.json

View File

@ -6,49 +6,82 @@
# - cd cli && go mod tidy
# # you may remove this if you don't need go generate
# - cd cli && go generate ./...
before:
hooks:
- ./cli/scripts/completions.sh
- ./cli/scripts/manpages.sh
builds:
- env:
- CGO_ENABLED=0
- id: darwin-build
binary: infisical
id: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
flags:
- -trimpath
env:
- CGO_ENABLED=1
- CC=/home/runner/work/osxcross/target/bin/o64-clang
- CXX=/home/runner/work/osxcross/target/bin/o64-clang++
goos:
- darwin
ignore:
- goos: darwin
goarch: "386"
dir: ./cli
- id: all-other-builds
env:
- CGO_ENABLED=0
binary: infisical
ldflags: -X github.com/Infisical/infisical-merge/packages/util.CLI_VERSION={{ .Version }}
flags:
- -trimpath
goos:
- freebsd
- linux
- netbsd
- openbsd
- windows
goarch:
- 386
- "386"
- amd64
- arm
- arm64
goarm:
- 6
- 7
- "6"
- "7"
ignore:
- goos: darwin
goarch: "386"
- goos: windows
goarch: "386"
- goos: freebsd
goarch: "386"
dir: ./cli
archives:
- format_overrides:
- goos: windows
format: zip
files:
- README*
- LICENSE*
- manpages/*
- completions/*
release:
replace_existing_draft: true
mode: 'replace'
mode: "replace"
checksum:
name_template: 'checksums.txt'
name_template: "checksums.txt"
snapshot:
name_template: "{{ incpatch .Version }}"
name_template: "{{ incpatch .Version }}-devel"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'
- "^docs:"
- "^test:"
# publishers:
# - name: fury.io
@ -56,6 +89,7 @@ changelog:
# - infisical
# dir: "{{ dir .ArtifactPath }}"
# cmd: curl -F package=@{{ .ArtifactName }} https://{{ .Env.FURY_TOKEN }}@push.fury.io/infisical/
brews:
- name: infisical
tap:
@ -67,21 +101,39 @@ brews:
folder: Formula
homepage: "https://infisical.com"
description: "The official Infisical CLI"
install: |-
bin.install "infisical"
bash_completion.install "completions/infisical.bash" => "infisical"
zsh_completion.install "completions/infisical.zsh" => "_infisical"
fish_completion.install "completions/infisical.fish"
man1.install "manpages/infisical.1.gz"
nfpms:
- id: infisical
package_name: infisical
builds:
- infisical
vendor: Infisical, Inc
homepage: https://infisical.com/
maintainer: Infisical, Inc
description: The offical Infisical CLI
license: Apache 2.0
formats:
- rpm
- deb
- apk
bindir: /usr/bin
- id: infisical
package_name: infisical
builds:
- all-other-builds
vendor: Infisical, Inc
homepage: https://infisical.com/
maintainer: Infisical, Inc
description: The offical Infisical CLI
license: MIT
formats:
- rpm
- deb
- apk
- archlinux
bindir: /usr/bin
contents:
- src: ./completions/infisical.bash
dst: /etc/bash_completion.d/infisical
- src: ./completions/infisical.fish
dst: /usr/share/fish/vendor_completions.d/infisical.fish
- src: ./completions/infisical.zsh
dst: /usr/share/zsh/site-functions/_infisical
- src: ./manpages/infisical.1.gz
dst: /usr/share/man/man1/infisical.1.gz
scoop:
bucket:
owner: Infisical
@ -91,20 +143,38 @@ scoop:
email: ai@infisical.com
homepage: "https://infisical.com"
description: "The official Infisical CLI"
license: Apache-2.0
license: MIT
aurs:
- name: infisical-bin
homepage: "https://infisical.com"
description: "The official Infisical CLI"
maintainers:
- Infisical, Inc <support@infisical.com>
license: MIT
private_key: "{{ .Env.AUR_KEY }}"
git_url: "ssh://aur@aur.archlinux.org/infisical-bin.git"
package: |-
# bin
install -Dm755 "./infisical" "${pkgdir}/usr/bin/infisical"
# license
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/infisical/LICENSE"
# completions
mkdir -p "${pkgdir}/usr/share/bash-completion/completions/"
mkdir -p "${pkgdir}/usr/share/zsh/site-functions/"
mkdir -p "${pkgdir}/usr/share/fish/vendor_completions.d/"
install -Dm644 "./completions/infisical.bash" "${pkgdir}/usr/share/bash-completion/completions/infisical"
install -Dm644 "./completions/infisical.zsh" "${pkgdir}/usr/share/zsh/site-functions/infisical"
install -Dm644 "./completions/infisical.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/infisical.fish"
# man pages
install -Dm644 "./manpages/infisical.1.gz" "${pkgdir}/usr/share/man/man1/infisical.1.gz"
# dockers:
# - dockerfile: goreleaser.dockerfile
# - dockerfile: cli/docker/Dockerfile
# goos: linux
# goarch: amd64
# ids:
# - infisical
# image_templates:
# - "infisical/cli:{{ .Version }}"
# - "infisical/cli:{{ .Major }}.{{ .Minor }}"
# - "infisical/cli:{{ .Major }}"
# - "infisical/cli:{{ .Version }}"
# - "infisical/cli:latest"
# build_flag_templates:
# - "--label=org.label-schema.schema-version=1.0"
# - "--label=org.label-schema.version={{.Version}}"
# - "--label=org.label-schema.name={{.ProjectName}}"
# - "--platform=linux/amd64"

5
.husky/pre-commit Executable file
View File

@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

View File

@ -1,5 +1,7 @@
# Contributing to Infisical
Thanks for taking the time to contribute!
Thanks for taking the time to contribute! 😃 🚀
Please refer to our Contributing Guide for instructions on how to contribute.
Please refer to our [Contributing Guide](https://infisical.com/docs/contributing/overview) for instructions on how to contribute.
We also have some 🔥amazing🔥 merch for our contributors. Please reach out to tony@infisical.com for more info 👀

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2022 Infisical Inc.
Portions of this software are licensed as follows:
- All content that resides under any "ee/" directory of this repository, if such directories exists, are licensed under the license defined in "ee/LICENSE".
- All third party components incorporated into the Infisical Software are licensed under the original license provided by the owner of the applicable component.
- Content outside of the above mentioned directories or restrictions above is available under the "MIT Expat" license as defined below.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,14 +1,17 @@
build:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml build
docker-compose -f docker-compose.yml build
push:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml push
docker-compose -f docker-compose.yml push
up-dev:
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up --build
docker-compose -f docker-compose.dev.yml up --build
i-dev:
infisical run -- docker-compose -f docker-compose.dev.yml up --build
up-prod:
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up --build
docker-compose -f docker-compose.yml up --build
down:
docker-compose down
docker-compose down

372
README.md

File diff suppressed because one or more lines are too long

View File

@ -1,18 +1,12 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"prettier"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"no-console": 2,
"prettier/prettier": 2
}
}
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"rules": {
"no-console": 2
}
}

View File

@ -1,7 +0,0 @@
{
"semi": true,
"trailingComma": "none",
"singleQuote": true,
"printWidth": 80,
"useTabs": true
}

View File

@ -1,12 +1,27 @@
FROM node:16-bullseye-slim
# Build stage
FROM node:16-alpine AS build
WORKDIR /app
COPY package*.json .
RUN npm install
COPY package*.json ./
RUN npm ci --only-production
COPY . .
RUN npm run build
CMD ["npm", "run", "start"]
# Production stage
FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only-production
COPY --from=build /app .
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
CMD node healthcheck.js
EXPOSE 4000
CMD ["npm", "run", "start"]

View File

@ -0,0 +1,19 @@
import { server } from '../src/app';
import { describe, expect, it, beforeAll, afterAll } from '@jest/globals';
import supertest from 'supertest';
import { setUpHealthEndpoint } from '../src/services/health';
const requestWithSupertest = supertest(server);
describe('Healthcheck endpoint', () => {
beforeAll(async () => {
setUpHealthEndpoint(server);
});
afterAll(async () => {
server.close();
});
it('GET /healthcheck should return OK', async () => {
const res = await requestWithSupertest.get('/healthcheck');
expect(res.status).toEqual(200);
});
});

View File

@ -3,8 +3,10 @@ export {};
declare global {
namespace NodeJS {
interface ProcessEnv {
PORT: string;
EMAIL_TOKEN_LIFETIME: string;
ENCRYPTION_KEY: string;
SALT_ROUNDS: string;
JWT_AUTH_LIFETIME: string;
JWT_AUTH_SECRET: string;
JWT_REFRESH_LIFETIME: string;
@ -14,24 +16,36 @@ declare global {
JWT_SIGNUP_SECRET: string;
MONGO_URL: string;
NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
OAUTH_CLIENT_SECRET_HEROKU: string;
OAUTH_TOKEN_URL_HEROKU: string;
VERBOSE_ERROR_OUTPUT: string;
LOKI_HOST: string;
CLIENT_ID_HEROKU: string;
CLIENT_ID_VERCEL: string;
CLIENT_ID_NETLIFY: string;
CLIENT_ID_GITHUB: string;
CLIENT_SECRET_HEROKU: string;
CLIENT_SECRET_VERCEL: string;
CLIENT_SECRET_NETLIFY: string;
CLIENT_SECRET_GITHUB: string;
CLIENT_SLUG_VERCEL: string;
POSTHOG_HOST: string;
POSTHOG_PROJECT_API_KEY: string;
PRIVATE_KEY: string;
PUBLIC_KEY: string;
SENTRY_DSN: string;
SITE_URL: string;
SMTP_HOST: string;
SMTP_NAME: string;
SMTP_PASSWORD: string;
SMTP_SECURE: string;
SMTP_PORT: string;
SMTP_USERNAME: string;
STRIPE_PRODUCT_CARD_AUTH: string;
STRIPE_PRODUCT_PRO: string;
SMTP_PASSWORD: string;
SMTP_FROM_ADDRESS: string;
SMTP_FROM_NAME: string;
STRIPE_PRODUCT_STARTER: string;
STRIPE_PRODUCT_TEAM: string;
STRIPE_PRODUCT_PRO: string;
STRIPE_PUBLISHABLE_KEY: string;
STRIPE_SECRET_KEY: string;
STRIPE_WEBHOOK_SECRET: string;
WEBSITE_URL: string;
TELEMETRY_ENABLED: string;
LICENSE_KEY: string;
}
}
}

24
backend/healthcheck.js Normal file
View File

@ -0,0 +1,24 @@
const http = require('http');
const PORT = process.env.PORT || 4000;
const options = {
host: 'localhost',
port: PORT,
timeout: 2000,
path: '/healthcheck'
};
const healthCheck = http.request(options, (res) => {
console.log(`HEALTHCHECK STATUS: ${res.statusCode}`);
if (res.statusCode == 200) {
process.exit(0);
} else {
process.exit(1);
}
});
healthCheck.on('error', function (err) {
console.error(`HEALTH CHECK ERROR: ${err}`);
process.exit(1);
});
healthCheck.end();

6089
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,41 +1,64 @@
{
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.267.0",
"@godaddy/terminus": "^4.11.2",
"@octokit/rest": "^19.0.5",
"@sentry/node": "^7.14.0",
"@sentry/tracing": "^7.14.0",
"@sentry/tracing": "^7.19.0",
"@types/crypto-js": "^4.1.1",
"@types/libsodium-wrappers": "^0.7.10",
"await-to-js": "^3.0.0",
"aws-sdk": "^2.1311.0",
"axios": "^1.1.3",
"axios-retry": "^3.4.0",
"bcrypt": "^5.1.0",
"bigint-conversion": "^2.2.2",
"builder-pattern": "^2.2.0",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"crypto-js": "^4.1.1",
"dotenv": "^16.0.1",
"express": "^4.18.1",
"express-rate-limit": "^6.5.1",
"express-rate-limit": "^6.7.0",
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
"jsonwebtoken": "^8.5.1",
"js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",
"mongoose": "^6.7.1",
"libsodium-wrappers": "^0.7.10",
"lodash": "^4.17.21",
"mongoose": "^6.7.2",
"nodemailer": "^6.8.0",
"posthog-node": "^2.1.0",
"query-string": "^7.1.1",
"posthog-node": "^2.2.2",
"query-string": "^7.1.3",
"request-ip": "^3.3.0",
"rimraf": "^3.0.2",
"stripe": "^10.7.0",
"swagger-autogen": "^2.22.0",
"swagger-ui-express": "^4.6.0",
"tweetnacl": "^1.0.3",
"tweetnacl-util": "^0.15.1",
"typescript": "^4.8.4"
"typescript": "^4.9.3",
"utility-types": "^3.10.0",
"winston": "^3.8.2",
"winston-loki": "^6.0.6"
},
"name": "infisical-api",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"start": "npm run build && node build/index.js",
"start": "node build/index.js",
"dev": "nodemon",
"build": "rimraf ./build && tsc && cp -R ./src/templates ./src/json ./build",
"swagger-autogen": "node ./swagger/index.ts",
"build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
"lint": "eslint . --ext .ts",
"lint-and-fix": "eslint . --ext .ts --fix",
"prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write"
"lint-staged": "lint-staged",
"pretest": "docker compose -f test-resources/docker-compose.test.yml up -d",
"test": "cross-env NODE_ENV=test jest --testTimeout=10000 --detectOpenHandles",
"test:ci": "npm test -- --watchAll=false --ci --reporters=default --reporters=jest-junit --reporters=github-actions --coverage --testLocationInResults --json --outputFile=coverage/report.json",
"posttest": "docker compose -f test-resources/docker-compose.test.yml down"
},
"repository": {
"type": "git",
@ -49,24 +72,52 @@
"homepage": "https://github.com/Infisical/infisical-api#readme",
"description": "",
"devDependencies": {
"@jest/globals": "^29.3.1",
"@posthog/plugin-scaffold": "^1.3.4",
"@types/bcrypt": "^5.0.0",
"@types/bcryptjs": "^2.4.2",
"@types/cookie-parser": "^1.4.3",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.14",
"@types/jest": "^29.2.4",
"@types/jsonwebtoken": "^8.5.9",
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.3",
"@types/nodemailer": "^6.4.6",
"@types/supertest": "^2.0.12",
"@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3",
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"cross-env": "^7.0.3",
"eslint": "^8.26.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"husky": "^8.0.1",
"install": "^0.13.0",
"jest": "^29.3.1",
"jest-junit": "^15.0.0",
"nodemon": "^2.0.19",
"npm": "^8.19.3",
"prettier": "^2.7.1",
"supertest": "^6.3.3",
"ts-jest": "^29.0.3",
"ts-node": "^10.9.1"
},
"jest": {
"preset": "ts-jest",
"testEnvironment": "node",
"collectCoverageFrom": [
"src/*.{js,ts}",
"!**/node_modules/**"
],
"setupFiles": [
"<rootDir>/test-resources/env-vars.js"
]
},
"jest-junit": {
"outputDirectory": "reports",
"outputName": "jest-junit.xml",
"ancestorSeparator": " ",
"uniqueOutputName": "false",
"suiteNameTemplate": "{filepath}",
"classNameTemplate": "{classname}",
"titleTemplate": "{title}"
}
}

4146
backend/spec.json Normal file

File diff suppressed because it is too large Load Diff

149
backend/src/app.ts Normal file
View File

@ -0,0 +1,149 @@
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { patchRouterParam } = require('./utils/patchAsyncRoutes');
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
import cookieParser from 'cookie-parser';
import dotenv from 'dotenv';
import swaggerUi = require('swagger-ui-express');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const swaggerFile = require('../spec.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const requestIp = require('request-ip');
dotenv.config();
import { PORT, NODE_ENV, SITE_URL } from './config';
import { apiLimiter } from './helpers/rateLimiter';
import {
workspace as eeWorkspaceRouter,
secret as eeSecretRouter,
secretSnapshot as eeSecretSnapshotRouter,
action as eeActionRouter
} from './ee/routes/v1';
import {
signup as v1SignupRouter,
auth as v1AuthRouter,
bot as v1BotRouter,
organization as v1OrganizationRouter,
workspace as v1WorkspaceRouter,
membershipOrg as v1MembershipOrgRouter,
membership as v1MembershipRouter,
key as v1KeyRouter,
inviteOrg as v1InviteOrgRouter,
user as v1UserRouter,
userAction as v1UserActionRouter,
secret as v1SecretRouter,
serviceToken as v1ServiceTokenRouter,
password as v1PasswordRouter,
stripe as v1StripeRouter,
integration as v1IntegrationRouter,
integrationAuth as v1IntegrationAuthRouter,
secretApprovalRequest as v1SecretApprovalRequest
} from './routes/v1';
import {
signup as v2SignupRouter,
auth as v2AuthRouter,
users as v2UsersRouter,
organizations as v2OrganizationsRouter,
workspace as v2WorkspaceRouter,
secret as v2SecretRouter, // begin to phase out
secrets as v2SecretsRouter,
serviceTokenData as v2ServiceTokenDataRouter,
apiKeyData as v2APIKeyDataRouter,
environment as v2EnvironmentRouter,
tags as v2TagsRouter,
} from './routes/v2';
import { healthCheck } from './routes/status';
import { getLogger } from './utils/logger';
import { RouteNotFoundError } from './utils/errors';
import { handleMongoInvalidDataError, requestErrorHandler } from './middleware/requestErrorHandler';
// patch async route params to handle Promise Rejections
patchRouterParam();
export const app = express();
app.enable('trust proxy');
app.use(express.json());
app.use(cookieParser());
app.use(
cors({
credentials: true,
origin: SITE_URL
})
);
app.use(requestIp.mw())
if (NODE_ENV === 'production') {
// enable app-wide rate-limiting + helmet security
// in production
app.disable('x-powered-by');
app.use(apiLimiter);
app.use(helmet());
}
// (EE) routes
app.use('/api/v1/secret', eeSecretRouter);
app.use('/api/v1/secret-snapshot', eeSecretSnapshotRouter);
app.use('/api/v1/workspace', eeWorkspaceRouter);
app.use('/api/v1/action', eeActionRouter);
// v1 routes
app.use('/api/v1/signup', v1SignupRouter);
app.use('/api/v1/auth', v1AuthRouter);
app.use('/api/v1/bot', v1BotRouter);
app.use('/api/v1/user', v1UserRouter);
app.use('/api/v1/user-action', v1UserActionRouter);
app.use('/api/v1/organization', v1OrganizationRouter);
app.use('/api/v1/workspace', v1WorkspaceRouter);
app.use('/api/v1/membership-org', v1MembershipOrgRouter);
app.use('/api/v1/membership', v1MembershipRouter);
app.use('/api/v1/key', v1KeyRouter);
app.use('/api/v1/invite-org', v1InviteOrgRouter);
app.use('/api/v1/secret', v1SecretRouter);
app.use('/api/v1/service-token', v1ServiceTokenRouter); // deprecated
app.use('/api/v1/password', v1PasswordRouter);
app.use('/api/v1/stripe', v1StripeRouter);
app.use('/api/v1/integration', v1IntegrationRouter);
app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
app.use('/api/v1/secrets-approval-request', v1SecretApprovalRequest)
// v2 routes
app.use('/api/v2/signup', v2SignupRouter);
app.use('/api/v2/auth', v2AuthRouter);
app.use('/api/v2/users', v2UsersRouter);
app.use('/api/v2/organizations', v2OrganizationsRouter);
app.use('/api/v2/workspace', v2EnvironmentRouter);
app.use('/api/v2/workspace', v2TagsRouter);
app.use('/api/v2/workspace', v2WorkspaceRouter);
app.use('/api/v2/secret', v2SecretRouter); // deprecated
app.use('/api/v2/secrets', v2SecretsRouter);
app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route
app.use('/api/v2/api-key', v2APIKeyDataRouter);
// api docs
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerFile))
// Server status
app.use('/api', healthCheck)
//* Handle unrouted requests and respond with proper error message as well as status code
app.use((req, res, next) => {
if (res.headersSent) return next();
next(RouteNotFoundError({ message: `The requested source '(${req.method})${req.url}' was not found` }))
})
// handle mongo validation errors
app.use(handleMongoInvalidDataError);
//* Error Handling Middleware (must be after all routing logic)
app.use(requestErrorHandler)
export const server = app.listen(PORT, () => {
getLogger("backend-main").info(`Server started listening at port ${PORT}`)
});

View File

@ -1,8 +1,12 @@
const PORT = process.env.PORT || 4000;
const EMAIL_TOKEN_LIFETIME = process.env.EMAIL_TOKEN_LIFETIME! || '86400'; // investigate
const EMAIL_TOKEN_LIFETIME = parseInt(process.env.EMAIL_TOKEN_LIFETIME! || '86400');
const INVITE_ONLY_SIGNUP = process.env.INVITE_ONLY_SIGNUP == undefined ? false : process.env.INVITE_ONLY_SIGNUP
const ENCRYPTION_KEY = process.env.ENCRYPTION_KEY!;
const SALT_ROUNDS = parseInt(process.env.SALT_ROUNDS!) || 10;
const JWT_AUTH_LIFETIME = process.env.JWT_AUTH_LIFETIME! || '10d';
const JWT_AUTH_SECRET = process.env.JWT_AUTH_SECRET!;
const JWT_MFA_LIFETIME = process.env.JWT_MFA_LIFETIME! || '5m';
const JWT_MFA_SECRET = process.env.JWT_MFA_SECRET!;
const JWT_REFRESH_LIFETIME = process.env.JWT_REFRESH_LIFETIME! || '90d';
const JWT_REFRESH_SECRET = process.env.JWT_REFRESH_SECRET!;
const JWT_SERVICE_SECRET = process.env.JWT_SERVICE_SECRET!;
@ -10,54 +14,90 @@ const JWT_SIGNUP_LIFETIME = process.env.JWT_SIGNUP_LIFETIME! || '15m';
const JWT_SIGNUP_SECRET = process.env.JWT_SIGNUP_SECRET!;
const MONGO_URL = process.env.MONGO_URL!;
const NODE_ENV = process.env.NODE_ENV! || 'production';
const OAUTH_CLIENT_SECRET_HEROKU = process.env.OAUTH_CLIENT_SECRET_HEROKU!;
const OAUTH_TOKEN_URL_HEROKU = process.env.OAUTH_TOKEN_URL_HEROKU!;
const POSTHOG_HOST = process.env.POSTHOG_HOST!;
const POSTHOG_PROJECT_API_KEY = process.env.POSTHOG_PROJECT_API_KEY!;
const PRIVATE_KEY = process.env.PRIVATE_KEY!;
const PUBLIC_KEY = process.env.PUBLIC_KEY!;
const VERBOSE_ERROR_OUTPUT = process.env.VERBOSE_ERROR_OUTPUT! === 'true' && true;
const LOKI_HOST = process.env.LOKI_HOST || undefined;
const CLIENT_ID_AZURE = process.env.CLIENT_ID_AZURE!;
const TENANT_ID_AZURE = process.env.TENANT_ID_AZURE!;
const CLIENT_ID_HEROKU = process.env.CLIENT_ID_HEROKU!;
const CLIENT_ID_VERCEL = process.env.CLIENT_ID_VERCEL!;
const CLIENT_ID_NETLIFY = process.env.CLIENT_ID_NETLIFY!;
const CLIENT_ID_GITHUB = process.env.CLIENT_ID_GITHUB!;
const CLIENT_SECRET_AZURE = process.env.CLIENT_SECRET_AZURE!;
const CLIENT_SECRET_HEROKU = process.env.CLIENT_SECRET_HEROKU!;
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB!;
const CLIENT_SLUG_VERCEL = process.env.CLIENT_SLUG_VERCEL!;
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
const POSTHOG_PROJECT_API_KEY =
process.env.POSTHOG_PROJECT_API_KEY! ||
'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
const SENTRY_DSN = process.env.SENTRY_DSN!;
const SMTP_HOST = process.env.SMTP_HOST! || 'smtp.gmail.com';
const SMTP_NAME = process.env.SMTP_NAME!;
const SITE_URL = process.env.SITE_URL!;
const SMTP_HOST = process.env.SMTP_HOST!;
const SMTP_SECURE = process.env.SMTP_SECURE! === 'true' || false;
const SMTP_PORT = parseInt(process.env.SMTP_PORT!) || 587;
const SMTP_USERNAME = process.env.SMTP_USERNAME!;
const SMTP_PASSWORD = process.env.SMTP_PASSWORD!;
const STRIPE_PRODUCT_CARD_AUTH = process.env.STRIPE_PRODUCT_CARD_AUTH!;
const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!;
const SMTP_FROM_ADDRESS = process.env.SMTP_FROM_ADDRESS!;
const SMTP_FROM_NAME = process.env.SMTP_FROM_NAME! || 'Infisical';
const STRIPE_PRODUCT_STARTER = process.env.STRIPE_PRODUCT_STARTER!;
const STRIPE_PRODUCT_PRO = process.env.STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_TEAM = process.env.STRIPE_PRODUCT_TEAM!;
const STRIPE_PUBLISHABLE_KEY = process.env.STRIPE_PUBLISHABLE_KEY!;
const STRIPE_SECRET_KEY = process.env.STRIPE_SECRET_KEY!;
const STRIPE_WEBHOOK_SECRET = process.env.STRIPE_WEBHOOK_SECRET!;
const WEBSITE_URL = 'http://frontend:3000';
const TELEMETRY_ENABLED = process.env.TELEMETRY_ENABLED! !== 'false' && true;
const LICENSE_KEY = process.env.LICENSE_KEY!;
export {
PORT,
EMAIL_TOKEN_LIFETIME,
ENCRYPTION_KEY,
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_REFRESH_LIFETIME,
JWT_REFRESH_SECRET,
JWT_SERVICE_SECRET,
JWT_SIGNUP_LIFETIME,
JWT_SIGNUP_SECRET,
MONGO_URL,
NODE_ENV,
OAUTH_CLIENT_SECRET_HEROKU,
OAUTH_TOKEN_URL_HEROKU,
POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY,
PRIVATE_KEY,
PUBLIC_KEY,
SENTRY_DSN,
SMTP_HOST,
SMTP_NAME,
SMTP_USERNAME,
SMTP_PASSWORD,
STRIPE_PRODUCT_CARD_AUTH,
STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_STARTER,
STRIPE_PUBLISHABLE_KEY,
STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET,
WEBSITE_URL
PORT,
EMAIL_TOKEN_LIFETIME,
INVITE_ONLY_SIGNUP,
ENCRYPTION_KEY,
SALT_ROUNDS,
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_MFA_LIFETIME,
JWT_MFA_SECRET,
JWT_REFRESH_LIFETIME,
JWT_REFRESH_SECRET,
JWT_SERVICE_SECRET,
JWT_SIGNUP_LIFETIME,
JWT_SIGNUP_SECRET,
MONGO_URL,
NODE_ENV,
VERBOSE_ERROR_OUTPUT,
LOKI_HOST,
CLIENT_ID_AZURE,
TENANT_ID_AZURE,
CLIENT_ID_HEROKU,
CLIENT_ID_VERCEL,
CLIENT_ID_NETLIFY,
CLIENT_ID_GITHUB,
CLIENT_SECRET_AZURE,
CLIENT_SECRET_HEROKU,
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY,
CLIENT_SECRET_GITHUB,
CLIENT_SLUG_VERCEL,
POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY,
SENTRY_DSN,
SITE_URL,
SMTP_HOST,
SMTP_PORT,
SMTP_SECURE,
SMTP_USERNAME,
SMTP_PASSWORD,
SMTP_FROM_ADDRESS,
SMTP_FROM_NAME,
STRIPE_PRODUCT_STARTER,
STRIPE_PRODUCT_TEAM,
STRIPE_PRODUCT_PRO,
STRIPE_PUBLISHABLE_KEY,
STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET,
TELEMETRY_ENABLED,
LICENSE_KEY
};

View File

@ -0,0 +1,16 @@
import axios from 'axios';
import axiosRetry from 'axios-retry';
const axiosInstance = axios.create();
// add retry functionality to the axios instance
axiosRetry(axiosInstance, {
retries: 3,
retryDelay: axiosRetry.exponentialDelay, // exponential back-off delay between retries
retryCondition: (error) => {
// only retry if the error is a network error or a 5xx server error
return axiosRetry.isNetworkError(error) || axiosRetry.isRetryableError(error);
},
});
export default axiosInstance;

View File

@ -1,224 +0,0 @@
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp');
import { User } from '../models';
import { createToken, issueTokens, clearTokens } from '../helpers/auth';
import {
NODE_ENV,
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_REFRESH_SECRET
} from '../config';
declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload {
userId: string;
}
}
const clientPublicKeys: any = {};
/**
* Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req
* @param res
* @returns
*/
export const login1 = async (req: Request, res: Response) => {
try {
const {
email,
clientPublicKey
}: { email: string; clientPublicKey: string } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
},
() => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
clientPublicKeys[email] = {
clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt)
};
return res.status(200).send({
serverPublicKey,
salt: user.salt
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to start authentication process'
});
}
};
/**
* Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted)
* private key
* @param req
* @param res
* @returns
*/
export const login2 = async (req: Request, res: Response) => {
try {
const { email, clientProof } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier +publicKey +encryptedPrivateKey +iv +tag');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: clientPublicKeys[email].serverBInt
},
async () => {
server.setClientPublicKey(clientPublicKeys[email].clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// issue tokens
const tokens = await issueTokens({ userId: user._id.toString() });
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/token',
sameSite: "strict",
secure: NODE_ENV === 'production' ? true : false
});
// return (access) token in response
return res.status(200).send({
token: tokens.token,
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
});
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
};
/**
* Log out user
* @param req
* @param res
* @returns
*/
export const logout = async (req: Request, res: Response) => {
try {
await clearTokens({
userId: req.user._id.toString()
});
// clear httpOnly cookie
res.cookie('jid', '', {
httpOnly: true,
path: '/token',
sameSite: "strict",
secure: NODE_ENV === 'production' ? true : false
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to logout'
});
}
return res.status(200).send({
message: 'Successfully logged out.'
});
};
/**
* Return user is authenticated
* @param req
* @param res
* @returns
*/
export const checkAuth = async (req: Request, res: Response) =>
res.status(200).send({
message: 'Authenticated'
});
/**
* Return new token by redeeming refresh token
* @param req
* @param res
* @returns
*/
export const getNewToken = async (req: Request, res: Response) => {
try {
const refreshToken = req.cookies.jid;
if (!refreshToken) {
throw new Error('Failed to find token in request cookies');
}
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(refreshToken, JWT_REFRESH_SECRET)
);
const user = await User.findOne({
_id: decodedToken.userId
}).select('+publicKey');
if (!user) throw new Error('Failed to authenticate unfound user');
if (!user?.publicKey)
throw new Error('Failed to authenticate not fully set up account');
const token = createToken({
payload: {
userId: decodedToken.userId
},
expiresIn: JWT_AUTH_LIFETIME,
secret: JWT_AUTH_SECRET
});
return res.status(200).send({
token
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Invalid request'
});
}
};

View File

@ -1,153 +0,0 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import axios from 'axios';
import { readFileSync } from 'fs';
import { IntegrationAuth, Integration } from '../models';
import { processOAuthTokenRes } from '../helpers/integrationAuth';
import { INTEGRATION_SET, ENV_DEV } from '../variables';
import { OAUTH_CLIENT_SECRET_HEROKU, OAUTH_TOKEN_URL_HEROKU } from '../config';
/**
* Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId]
* Note: integration [integration] must be set up compatible/designed for OAuth2
* @param req
* @param res
* @returns
*/
export const integrationAuthOauthExchange = async (
req: Request,
res: Response
) => {
try {
let clientSecret;
const { workspaceId, code, integration } = req.body;
if (!INTEGRATION_SET.has(integration))
throw new Error('Failed to validate integration');
// use correct client secret
switch (integration) {
case 'heroku':
clientSecret = OAUTH_CLIENT_SECRET_HEROKU;
}
// TODO: unfinished - make compatible with other integration types
const res = await axios.post(
OAUTH_TOKEN_URL_HEROKU!,
new URLSearchParams({
grant_type: 'authorization_code',
code: code,
client_secret: clientSecret
} as any)
);
const integrationAuth = await processOAuthTokenRes({
workspaceId,
integration,
res
});
// create or replace integration
const integrationObj = await Integration.findOneAndUpdate(
{ workspace: workspaceId, integration },
{
workspace: workspaceId,
environment: ENV_DEV,
isActive: false,
app: null,
integration,
integrationAuth: integrationAuth._id
},
{ upsert: true, new: true }
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get OAuth2 token'
});
}
return res.status(200).send({
message: 'Successfully enabled integration authorization'
});
};
/**
* Return list of applications allowed for integration with id [integrationAuthId]
* @param req
* @param res
* @returns
*/
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
// TODO: unfinished - make compatible with other integration types
let apps;
try {
const res = await axios.get('https://api.heroku.com/apps', {
headers: {
Accept: 'application/vnd.heroku+json; version=3',
Authorization: 'Bearer ' + req.accessToken
}
});
apps = res.data.map((a: any) => ({
name: a.name
}));
} catch (err) {}
return res.status(200).send({
apps
});
};
/**
* Delete integration authorization with id [integrationAuthId]
* @param req
* @param res
* @returns
*/
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
// TODO: unfinished - disable application via Heroku API and make compatible with other integration types
try {
const { integrationAuthId } = req.params;
// TODO: disable application via Heroku API; figure out what authorization id is
const integrations = JSON.parse(
readFileSync('./src/json/integrations.json').toString()
);
let authorizationId;
switch (req.integrationAuth.integration) {
case 'heroku':
authorizationId = integrations.heroku.clientId;
}
// not sure what authorizationId is?
// // revoke authorization
// const res2 = await axios.delete(
// `https://api.heroku.com/oauth/authorizations/${authorizationId}`,
// {
// headers: {
// 'Accept': 'application/vnd.heroku+json; version=3',
// 'Authorization': 'Bearer ' + req.accessToken
// }
// }
// );
const deletedIntegrationAuth = await IntegrationAuth.findOneAndDelete({
_id: integrationAuthId
});
if (deletedIntegrationAuth) {
await Integration.deleteMany({
integrationAuth: deletedIntegrationAuth._id
});
}
} catch (err) {
return res.status(400).send({
message: 'Failed to delete integration authorization'
});
}
};

View File

@ -1,158 +0,0 @@
import { Request, Response } from 'express';
import { readFileSync } from 'fs';
import * as Sentry from '@sentry/node';
import axios from 'axios';
import { Integration } from '../models';
import { decryptAsymmetric } from '../utils/crypto';
import { decryptSecrets } from '../helpers/secret';
import { PRIVATE_KEY } from '../config';
interface Key {
encryptedKey: string;
nonce: string;
}
interface PushSecret {
ciphertextKey: string;
ivKey: string;
tagKey: string;
hashKey: string;
ciphertextValue: string;
ivValue: string;
tagValue: string;
hashValue: string;
type: 'shared' | 'personal';
}
/**
* Return list of all available integrations on Infisical
* @param req
* @param res
* @returns
*/
export const getIntegrations = async (req: Request, res: Response) => {
let integrations;
try {
integrations = JSON.parse(
readFileSync('./src/json/integrations.json').toString()
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get integrations'
});
}
return res.status(200).send({
integrations
});
};
/**
* Sync secrets [secrets] to integration with id [integrationId]
* @param req
* @param res
* @returns
*/
export const syncIntegration = async (req: Request, res: Response) => {
// TODO: unfinished - make more versatile to accomodate for other integrations
try {
const { key, secrets }: { key: Key; secrets: PushSecret[] } = req.body;
const symmetricKey = decryptAsymmetric({
ciphertext: key.encryptedKey,
nonce: key.nonce,
publicKey: req.user.publicKey,
privateKey: PRIVATE_KEY
});
// decrypt secrets with symmetric key
const content = decryptSecrets({
secrets,
key: symmetricKey,
format: 'object'
});
// TODO: make integration work for other integrations as well
const res = await axios.patch(
`https://api.heroku.com/apps/${req.integration.app}/config-vars`,
content,
{
headers: {
Accept: 'application/vnd.heroku+json; version=3',
Authorization: 'Bearer ' + req.accessToken
}
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to sync secrets with integration'
});
}
return res.status(200).send({
message: 'Successfully synced secrets with integration'
});
};
/**
* Change environment or name of integration with id [integrationId]
* @param req
* @param res
* @returns
*/
export const modifyIntegration = async (req: Request, res: Response) => {
let integration;
try {
const { update } = req.body;
integration = await Integration.findOneAndUpdate(
{
_id: req.integration._id
},
update,
{
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to modify integration'
});
}
return res.status(200).send({
integration
});
};
/**
* Delete integration with id [integrationId]
* @param req
* @param res
* @returns
*/
export const deleteIntegration = async (req: Request, res: Response) => {
let deletedIntegration;
try {
const { integrationId } = req.params;
deletedIntegration = await Integration.findOneAndDelete({
_id: integrationId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete integration'
});
}
return res.status(200).send({
deletedIntegration
});
};

View File

@ -1,189 +0,0 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
const jsrp = require('jsrp');
import * as bigintConversion from 'bigint-conversion';
import { User, BackupPrivateKey } from '../models';
const clientPublicKeys: any = {};
/**
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req
* @param res
* @returns
*/
export const srp1 = async (req: Request, res: Response) => {
// return salt, serverPublicKey as part of first step of SRP protocol
try {
const { clientPublicKey } = req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
},
() => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
clientPublicKeys[req.user.email] = {
clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt)
};
return res.status(200).send({
serverPublicKey,
salt: user.salt
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to start change password process'
});
}
};
/**
* Change account SRP authentication information for user
* Requires verifying [clientProof] as part of step 2 of SRP protocol
* as initiated in POST /srp1
* @param req
* @param res
* @returns
*/
export const changePassword = async (req: Request, res: Response) => {
try {
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: clientPublicKeys[req.user.email].serverBInt
},
async () => {
server.setClientPublicKey(
clientPublicKeys[req.user.email].clientPublicKey
);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// change password
await User.findByIdAndUpdate(
req.user._id.toString(),
{
encryptedPrivateKey,
iv,
tag,
salt,
verifier
},
{
new: true
}
);
return res.status(200).send({
message: 'Successfully changed password'
});
}
return res.status(400).send({
error: 'Failed to change password. Try again?'
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to change password. Try again?'
});
}
};
/**
* Create or change backup private key for user
* @param req
* @param res
* @returns
*/
export const createBackupPrivateKey = async (req: Request, res: Response) => {
// create/change backup private key
// requires verifying [clientProof] as part of second step of SRP protocol
// as initiated in /srp1
try {
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: clientPublicKeys[req.user.email].serverBInt
},
async () => {
server.setClientPublicKey(
clientPublicKeys[req.user.email].clientPublicKey
);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// create new or replace backup private key
const backupPrivateKey = await BackupPrivateKey.findOneAndUpdate(
{ user: req.user._id },
{
user: req.user._id,
encryptedPrivateKey,
iv,
tag,
salt,
verifier
},
{ upsert: true, new: true }
).select('+user, encryptedPrivateKey');
// issue tokens
return res.status(200).send({
message: 'Successfully updated backup private key',
backupPrivateKey
});
}
return res.status(400).send({
message: 'Failed to update backup private key'
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update backup private key'
});
}
};

View File

@ -0,0 +1,269 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp');
import { User, LoginSRPDetail } from '../../models';
import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth';
import { checkUserDevice } from '../../helpers/user';
import {
ACTION_LOGIN,
ACTION_LOGOUT
} from '../../variables';
import {
NODE_ENV,
JWT_AUTH_LIFETIME,
JWT_AUTH_SECRET,
JWT_REFRESH_SECRET
} from '../../config';
import { BadRequestError } from '../../utils/errors';
import { EELogService } from '../../ee/services';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload {
userId: string;
}
}
/**
* Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req
* @param res
* @returns
*/
export const login1 = async (req: Request, res: Response) => {
try {
const {
email,
clientPublicKey
}: { email: string; clientPublicKey: string } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({ email: email }, {
email: email,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false })
return res.status(200).send({
serverPublicKey,
salt: user.salt
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to start authentication process'
});
}
};
/**
* Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted)
* private key
* @param req
* @param res
* @returns
*/
export const login2 = async (req: Request, res: Response) => {
try {
const { email, clientProof } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier +publicKey +encryptedPrivateKey +iv +tag');
if (!user) throw new Error('Failed to find user');
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email })
if (!loginSRPDetailFromDB) {
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
}
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetailFromDB.serverBInt
},
async () => {
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// issue tokens
await checkUserDevice({
user,
ip: req.ip,
userAgent: req.headers['user-agent'] ?? ''
});
const tokens = await issueAuthTokens({ userId: user._id.toString() });
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
// return (access) token in response
return res.status(200).send({
token: tokens.token,
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
});
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
};
/**
* Log out user
* @param req
* @param res
* @returns
*/
export const logout = async (req: Request, res: Response) => {
try {
await clearTokens({
userId: req.user._id.toString()
});
// clear httpOnly cookie
res.cookie('jid', '', {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
const logoutAction = await EELogService.createAction({
name: ACTION_LOGOUT,
userId: req.user._id
});
logoutAction && await EELogService.createLog({
userId: req.user._id,
actions: [logoutAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to logout'
});
}
return res.status(200).send({
message: 'Successfully logged out.'
});
};
/**
* Return user is authenticated
* @param req
* @param res
* @returns
*/
export const checkAuth = async (req: Request, res: Response) => {
return res.status(200).send({
message: 'Authenticated'
});
}
/**
* Return new token by redeeming refresh token
* @param req
* @param res
* @returns
*/
export const getNewToken = async (req: Request, res: Response) => {
try {
const refreshToken = req.cookies.jid;
if (!refreshToken) {
throw new Error('Failed to find token in request cookies');
}
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(refreshToken, JWT_REFRESH_SECRET)
);
const user = await User.findOne({
_id: decodedToken.userId
}).select('+publicKey');
if (!user) throw new Error('Failed to authenticate unfound user');
if (!user?.publicKey)
throw new Error('Failed to authenticate not fully set up account');
const token = createToken({
payload: {
userId: decodedToken.userId
},
expiresIn: JWT_AUTH_LIFETIME,
secret: JWT_AUTH_SECRET
});
return res.status(200).send({
token
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Invalid request'
});
}
};

View File

@ -0,0 +1,107 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Bot, BotKey } from '../../models';
import { createBot } from '../../helpers/bot';
interface BotKey {
encryptedKey: string;
nonce: string;
}
/**
* Return bot for workspace with id [workspaceId]. If a workspace bot doesn't exist,
* then create and return a new bot.
* @param req
* @param res
* @returns
*/
export const getBotByWorkspaceId = async (req: Request, res: Response) => {
let bot;
try {
const { workspaceId } = req.params;
bot = await Bot.findOne({
workspace: workspaceId
});
if (!bot) {
// case: bot doesn't exist for workspace with id [workspaceId]
// -> create a new bot and return it
bot = await createBot({
name: 'Infisical Bot',
workspaceId
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get bot for workspace'
});
}
return res.status(200).send({
bot
});
};
/**
* Return bot with id [req.bot._id] with active state set to [isActive].
* @param req
* @param res
* @returns
*/
export const setBotActiveState = async (req: Request, res: Response) => {
let bot;
try {
const { isActive, botKey }: { isActive: boolean, botKey: BotKey } = req.body;
if (isActive) {
// bot state set to active -> share workspace key with bot
if (!botKey?.encryptedKey || !botKey?.nonce) {
return res.status(400).send({
message: 'Failed to set bot state to active - missing bot key'
});
}
await BotKey.findOneAndUpdate({
workspace: req.bot.workspace
}, {
encryptedKey: botKey.encryptedKey,
nonce: botKey.nonce,
sender: req.user._id,
bot: req.bot._id,
workspace: req.bot.workspace
}, {
upsert: true,
new: true
});
} else {
// case: bot state set to inactive -> delete bot's workspace key
await BotKey.deleteOne({
bot: req.bot._id
});
}
bot = await Bot.findOneAndUpdate({
_id: req.bot._id
}, {
isActive
}, {
new: true
});
if (!bot) throw new Error('Failed to update bot active state');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update bot active state'
});
}
return res.status(200).send({
bot
});
};

View File

@ -1,4 +1,5 @@
import * as authController from './authController';
import * as botController from './botController';
import * as integrationAuthController from './integrationAuthController';
import * as integrationController from './integrationController';
import * as keyController from './keyController';
@ -13,9 +14,11 @@ import * as stripeController from './stripeController';
import * as userActionController from './userActionController';
import * as userController from './userController';
import * as workspaceController from './workspaceController';
import * as secretApprovalController from './secretApprovalController';
export {
authController,
botController,
integrationAuthController,
integrationController,
keyController,
@ -29,5 +32,6 @@ export {
stripeController,
userActionController,
userController,
workspaceController
workspaceController,
secretApprovalController
};

View File

@ -0,0 +1,200 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import {
Integration,
IntegrationAuth,
Bot
} from '../../models';
import { INTEGRATION_SET, INTEGRATION_OPTIONS } from '../../variables';
import { IntegrationService } from '../../services';
import { getApps, revokeAccess } from '../../integrations';
/***
* Return integration authorization with id [integrationAuthId]
*/
export const getIntegrationAuth = async (req: Request, res: Response) => {
let integrationAuth;
try {
const { integrationAuthId } = req.params;
integrationAuth = await IntegrationAuth.findById(integrationAuthId);
if (!integrationAuth) return res.status(400).send({
message: 'Failed to find integration authorization'
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get integration authorization'
});
}
return res.status(200).send({
integrationAuth
});
}
export const getIntegrationOptions = async (req: Request, res: Response) => {
return res.status(200).send({
integrationOptions: INTEGRATION_OPTIONS,
});
};
/**
* Perform OAuth2 code-token exchange as part of integration [integration] for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const oAuthExchange = async (
req: Request,
res: Response
) => {
try {
const { workspaceId, code, integration } = req.body;
if (!INTEGRATION_SET.has(integration))
throw new Error('Failed to validate integration');
const environments = req.membership.workspace?.environments || [];
if(environments.length === 0){
throw new Error("Failed to get environments")
}
const integrationAuth = await IntegrationService.handleOAuthExchange({
workspaceId,
integration,
code,
environment: environments[0].slug,
});
return res.status(200).send({
integrationAuth
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get OAuth2 code-token exchange'
});
}
};
/**
* Save integration access token and (optionally) access id as part of integration
* [integration] for workspace with id [workspaceId]
* @param req
* @param res
*/
export const saveIntegrationAccessToken = async (
req: Request,
res: Response
) => {
// TODO: refactor
// TODO: check if access token is valid for each integration
let integrationAuth;
try {
const {
workspaceId,
accessId,
accessToken,
integration
}: {
workspaceId: string;
accessId: string | null;
accessToken: string;
integration: string;
} = req.body;
const bot = await Bot.findOne({
workspace: new Types.ObjectId(workspaceId),
isActive: true
});
if (!bot) throw new Error('Bot must be enabled to save integration access token');
integrationAuth = await IntegrationAuth.findOneAndUpdate({
workspace: new Types.ObjectId(workspaceId),
integration
}, {
workspace: new Types.ObjectId(workspaceId),
integration
}, {
new: true,
upsert: true
});
// encrypt and save integration access details
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
integrationAuthId: integrationAuth._id.toString(),
accessId,
accessToken,
accessExpiresAt: undefined
});
if (!integrationAuth) throw new Error('Failed to save integration access token');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to save access token for integration'
});
}
return res.status(200).send({
integrationAuth
});
}
/**
* Return list of applications allowed for integration with integration authorization id [integrationAuthId]
* @param req
* @param res
* @returns
*/
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
let apps;
try {
apps = await getApps({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get integration authorization applications",
});
}
return res.status(200).send({
apps,
});
};
/**
* Delete integration authorization with id [integrationAuthId]
* @param req
* @param res
* @returns
*/
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
let integrationAuth;
try {
integrationAuth = await revokeAccess({
integrationAuth: req.integrationAuth,
accessToken: req.accessToken,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to delete integration authorization",
});
}
return res.status(200).send({
integrationAuth,
});
};

View File

@ -0,0 +1,161 @@
import { Request, Response } from 'express';
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import {
Integration,
Workspace,
Bot,
BotKey
} from '../../models';
import { EventService } from '../../services';
import { eventPushSecrets } from '../../events';
/**
* Create/initialize an (empty) integration for integration authorization
* @param req
* @param res
* @returns
*/
export const createIntegration = async (req: Request, res: Response) => {
let integration;
try {
const {
integrationAuthId,
app,
appId,
isActive,
sourceEnvironment,
targetEnvironment,
owner,
path,
region
} = req.body;
// TODO: validate [sourceEnvironment] and [targetEnvironment]
// initialize new integration after saving integration access token
integration = await new Integration({
workspace: req.integrationAuth.workspace._id,
environment: sourceEnvironment,
isActive,
app,
appId,
targetEnvironment,
owner,
path,
region,
integration: req.integrationAuth.integration,
integrationAuth: new Types.ObjectId(integrationAuthId)
}).save();
if (integration) {
// trigger event - push secrets
EventService.handleEvent({
event: eventPushSecrets({
workspaceId: integration.workspace.toString()
})
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create integration'
});
}
return res.status(200).send({
integration,
});
};
/**
* Change environment or name of integration with id [integrationId]
* @param req
* @param res
* @returns
*/
export const updateIntegration = async (req: Request, res: Response) => {
let integration;
// TODO: add integration-specific validation to ensure that each
// integration has the correct fields populated in [Integration]
try {
const {
environment,
isActive,
app,
appId,
targetEnvironment,
owner, // github-specific integration param
} = req.body;
integration = await Integration.findOneAndUpdate(
{
_id: req.integration._id,
},
{
environment,
isActive,
app,
appId,
targetEnvironment,
owner,
},
{
new: true,
}
);
if (integration) {
// trigger event - push secrets
EventService.handleEvent({
event: eventPushSecrets({
workspaceId: integration.workspace.toString(),
}),
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to update integration",
});
}
return res.status(200).send({
integration,
});
};
/**
* Delete integration with id [integrationId] and deactivate bot if there are
* no integrations left
* @param req
* @param res
* @returns
*/
export const deleteIntegration = async (req: Request, res: Response) => {
let integration;
try {
const { integrationId } = req.params;
integration = await Integration.findOneAndDelete({
_id: integrationId,
});
if (!integration) throw new Error("Failed to find integration");
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to delete integration",
});
}
return res.status(200).send({
integration,
});
};

View File

@ -1,9 +1,7 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Key } from '../models';
import { findMembership } from '../helpers/membership';
import { PUBLIC_KEY } from '../config';
import { GRANTED } from '../variables';
import { Key } from '../../models';
import { findMembership } from '../../helpers/membership';
/**
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
@ -17,16 +15,6 @@ export const uploadKey = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
const { key } = req.body;
// validate membership of sender
const senderMembership = await findMembership({
user: req.user._id,
workspace: workspaceId
});
if (!senderMembership) {
throw new Error('Failed sender membership validation for workspace');
}
// validate membership of receiver
const receiverMembership = await findMembership({
user: key.userId,
@ -37,9 +25,6 @@ export const uploadKey = async (req: Request, res: Response) => {
throw new Error('Failed receiver membership validation for workspace');
}
receiverMembership.status = GRANTED;
await receiverMembership.save();
await new Key({
encryptedKey: key.encryptedKey,
nonce: key.nonce,
@ -94,16 +79,4 @@ export const getLatestKey = async (req: Request, res: Response) => {
}
return res.status(200).send(resObj);
};
/**
* Return public key of Infisical
* @param req
* @param res
* @returns
*/
export const getPublicKeyInfisical = async (req: Request, res: Response) => {
return res.status(200).send({
publicKey: PUBLIC_KEY
});
};
};

View File

@ -1,13 +1,13 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Membership, MembershipOrg, User, Key } from '../models';
import { Membership, MembershipOrg, User, Key, IMembership, Workspace } from '../../models';
import {
findMembership,
deleteMembership as deleteMember
} from '../helpers/membership';
import { sendMail } from '../helpers/nodemailer';
import { WEBSITE_URL } from '../config';
import { ADMIN, MEMBER, GRANTED, ACCEPTED } from '../variables';
} from '../../helpers/membership';
import { sendMail } from '../../helpers/nodemailer';
import { SITE_URL } from '../../config';
import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
/**
* Check that user is a member of workspace with id [workspaceId]
@ -175,8 +175,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
// already a member of the workspace
const inviteeMembership = await Membership.findOne({
user: invitee._id,
workspace: workspaceId,
status: GRANTED
workspace: workspaceId
});
if (inviteeMembership)
@ -205,8 +204,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
const m = await new Membership({
user: invitee._id,
workspace: workspaceId,
role: MEMBER,
status: GRANTED
role: MEMBER
}).save();
await sendMail({
@ -217,11 +215,10 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
inviterFirstName: req.user.firstName,
inviterEmail: req.user.email,
workspaceName: req.membership.workspace.name,
callback_url: WEBSITE_URL + '/login'
callback_url: SITE_URL + '/login'
}
});
} catch (err) {
console.error(err);
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
@ -233,4 +230,4 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
invitee,
latestKey
});
};
};

View File

@ -1,14 +1,13 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
import { WEBSITE_URL, JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET } from '../config';
import { MembershipOrg, Organization, User, Token } from '../models';
import { deleteMembershipOrg as deleteMemberFromOrg } from '../helpers/membershipOrg';
import { checkEmailVerification } from '../helpers/signup';
import { createToken } from '../helpers/auth';
import { updateSubscriptionOrgQuantity } from '../helpers/organization';
import { sendMail } from '../helpers/nodemailer';
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED } from '../variables';
import { SITE_URL, JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET } from '../../config';
import { MembershipOrg, Organization, User } from '../../models';
import { deleteMembershipOrg as deleteMemberFromOrg } from '../../helpers/membershipOrg';
import { createToken } from '../../helpers/auth';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
/**
* Delete organization membership with id [membershipOrgId] from organization
@ -77,17 +76,15 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
// change role for (target) organization membership with id
// [membershipOrgId]
// TODO
let membershipToChangeRole;
try {
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to change organization membership role'
});
}
// try {
// } catch (err) {
// Sentry.setUser({ email: req.user.email });
// Sentry.captureException(err);
// return res.status(400).send({
// message: 'Failed to change organization membership role'
// });
// }
return res.status(200).send({
membershipOrg: membershipToChangeRole
@ -118,10 +115,11 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
invitee = await User.findOne({
email: inviteeEmail
});
}).select('+publicKey');
if (invitee) {
// case: invitee is an existing user
inviteeMembershipOrg = await MembershipOrg.findOne({
user: invitee._id,
organization: organizationId
@ -164,17 +162,11 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
const organization = await Organization.findOne({ _id: organizationId });
if (organization) {
const token = crypto.randomBytes(16).toString('hex');
await Token.findOneAndUpdate(
{ email: inviteeEmail },
{
email: inviteeEmail,
token,
createdAt: new Date()
},
{ upsert: true, new: true }
);
const token = await TokenService.createToken({
type: TOKEN_EMAIL_ORG_INVITATION,
email: inviteeEmail,
organizationId: organization._id
});
await sendMail({
template: 'organizationInvitation.handlebars',
@ -186,7 +178,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
organizationName: organization.name,
email: inviteeEmail,
token,
callback_url: WEBSITE_URL + '/signupinvite'
callback_url: SITE_URL + '/signupinvite'
}
});
}
@ -217,13 +209,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
try {
const { email, code } = req.body;
user = await User.findOne({ email });
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed email magic link verification for complete account'
});
}
user = await User.findOne({ email }).select('+publicKey');
const membershipOrg = await MembershipOrg.findOne({
inviteEmail: email,
@ -232,12 +218,26 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
if (!membershipOrg)
throw new Error('Failed to find any invitations for email');
await checkEmailVerification({
await TokenService.validateToken({
type: TOKEN_EMAIL_ORG_INVITATION,
email,
code
organizationId: membershipOrg.organization,
token: code
});
if (user && user?.publicKey) {
// case: user has already completed account
// membership can be approved and redirected to login/dashboard
membershipOrg.status = ACCEPTED;
await membershipOrg.save();
return res.status(200).send({
message: 'Successfully verified email',
user,
});
}
if (!user) {
// initialize user account
user = await new User({
@ -257,7 +257,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed email magic link confirmation'
error: 'Failed email magic link verification for organization invitation'
});
}

View File

@ -1,13 +1,11 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import {
STRIPE_SECRET_KEY,
STRIPE_PRODUCT_STARTER,
STRIPE_PRODUCT_PRO,
STRIPE_PRODUCT_CARD_AUTH,
WEBSITE_URL
} from '../config';
SITE_URL,
STRIPE_SECRET_KEY
} from '../../config';
import Stripe from 'stripe';
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01'
});
@ -16,24 +14,14 @@ import {
MembershipOrg,
Organization,
Workspace,
IncidentContactOrg
} from '../models';
import { createOrganization as create } from '../helpers/organization';
import { addMembershipsOrg } from '../helpers/membershipOrg';
import { OWNER, ACCEPTED } from '../variables';
IncidentContactOrg,
IMembershipOrg
} from '../../models';
import { createOrganization as create } from '../../helpers/organization';
import { addMembershipsOrg } from '../../helpers/membershipOrg';
import { OWNER, ACCEPTED } from '../../variables';
import _ from 'lodash';
const productToPriceMap = {
starter: STRIPE_PRODUCT_STARTER,
pro: STRIPE_PRODUCT_PRO,
cardAuth: STRIPE_PRODUCT_CARD_AUTH
};
/**
* Return organizations that user is part of
* @param req
* @param res
* @returns
*/
export const getOrganizations = async (req: Request, res: Response) => {
let organizations;
try {
@ -345,18 +333,17 @@ export const createOrganizationPortalSession = async (
if (paymentMethods.data.length < 1) {
// case: no payment method on file
productToPriceMap['cardAuth'];
session = await stripe.checkout.sessions.create({
customer: req.membershipOrg.organization.customerId,
mode: 'setup',
payment_method_types: ['card'],
success_url: WEBSITE_URL + '/dashboard',
cancel_url: WEBSITE_URL + '/dashboard'
success_url: SITE_URL + '/dashboard',
cancel_url: SITE_URL + '/dashboard'
});
} else {
session = await stripe.billingPortal.sessions.create({
customer: req.membershipOrg.organization.customerId,
return_url: WEBSITE_URL + '/dashboard'
return_url: SITE_URL + '/dashboard'
});
}
@ -397,3 +384,44 @@ export const getOrganizationSubscriptions = async (
subscriptions
});
};
/**
* Given a org id, return the projects each member of the org belongs to
* @param req
* @param res
* @returns
*/
export const getOrganizationMembersAndTheirWorkspaces = async (
req: Request,
res: Response
) => {
const { organizationId } = req.params;
const workspacesSet = (
await Workspace.find(
{
organization: organizationId
},
'_id'
)
).map((w) => w._id.toString());
const memberships = (
await Membership.find({
workspace: { $in: workspacesSet }
}).populate('workspace')
);
const userToWorkspaceIds: any = {};
memberships.forEach(membership => {
const user = membership.user.toString();
if (userToWorkspaceIds[user]) {
userToWorkspaceIds[user].push(membership.workspace);
} else {
userToWorkspaceIds[user] = [membership.workspace];
}
});
return res.json(userToWorkspaceIds);
};

View File

@ -0,0 +1,388 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const jsrp = require('jsrp');
import * as bigintConversion from 'bigint-conversion';
import { User, BackupPrivateKey, LoginSRPDetail } from '../../models';
import { createToken } from '../../helpers/auth';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, SITE_URL } from '../../config';
import { TOKEN_EMAIL_PASSWORD_RESET } from '../../variables';
import { BadRequestError } from '../../utils/errors';
/**
* Password reset step 1: Send email verification link to email [email]
* for account recovery.
* @param req
* @param res
* @returns
*/
export const emailPasswordReset = async (req: Request, res: Response) => {
let email: string;
try {
email = req.body.email;
const user = await User.findOne({ email }).select('+publicKey');
if (!user || !user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed to send email verification for password reset'
});
}
const token = await TokenService.createToken({
type: TOKEN_EMAIL_PASSWORD_RESET,
email
});
await sendMail({
template: 'passwordReset.handlebars',
subjectLine: 'Infisical password reset',
recipients: [email],
substitutions: {
email,
token,
callback_url: SITE_URL + '/password-reset'
}
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to send email for account recovery'
});
}
return res.status(200).send({
message: `Sent an email for account recovery to ${email}`
});
}
/**
* Password reset step 2: Verify email verification link sent to email [email]
* @param req
* @param res
* @returns
*/
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
let user, token;
try {
const { email, code } = req.body;
user = await User.findOne({ email }).select('+publicKey');
if (!user || !user?.publicKey) {
// case: user doesn't exist with email [email] or
// hasn't even completed their account
return res.status(403).send({
error: 'Failed email verification for password reset'
});
}
await TokenService.validateToken({
type: TOKEN_EMAIL_PASSWORD_RESET,
email,
token: code
});
// generate temporary password-reset token
token = createToken({
payload: {
userId: user._id.toString()
},
expiresIn: JWT_SIGNUP_LIFETIME,
secret: JWT_SIGNUP_SECRET
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed email verification for password reset'
});
}
return res.status(200).send({
message: 'Successfully verified email',
user,
token
});
}
/**
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req
* @param res
* @returns
*/
export const srp1 = async (req: Request, res: Response) => {
// return salt, serverPublicKey as part of first step of SRP protocol
try {
const { clientPublicKey } = req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({ email: req.user.email }, {
email: req.user.email,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false })
return res.status(200).send({
serverPublicKey,
salt: user.salt
});
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to start change password process'
});
}
};
/**
* Change account SRP authentication information for user
* Requires verifying [clientProof] as part of step 2 of SRP protocol
* as initiated in POST /srp1
* @param req
* @param res
* @returns
*/
export const changePassword = async (req: Request, res: Response) => {
try {
const {
clientProof,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
} = req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
if (!loginSRPDetailFromDB) {
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
}
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetailFromDB.serverBInt
},
async () => {
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// change password
await User.findByIdAndUpdate(
req.user._id.toString(),
{
encryptionVersion: 2,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag,
salt,
verifier
},
{
new: true
}
);
return res.status(200).send({
message: 'Successfully changed password'
});
}
return res.status(400).send({
error: 'Failed to change password. Try again?'
});
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to change password. Try again?'
});
}
};
/**
* Create or change backup private key for user
* @param req
* @param res
* @returns
*/
export const createBackupPrivateKey = async (req: Request, res: Response) => {
// create/change backup private key
// requires verifying [clientProof] as part of second step of SRP protocol
// as initiated in /srp1
try {
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
req.body;
const user = await User.findOne({
email: req.user.email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
if (!loginSRPDetailFromDB) {
return BadRequestError(Error("It looks like some details from the first login are not found. Please try login one again"))
}
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetailFromDB.serverBInt
},
async () => {
server.setClientPublicKey(
loginSRPDetailFromDB.clientPublicKey
);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
// create new or replace backup private key
const backupPrivateKey = await BackupPrivateKey.findOneAndUpdate(
{ user: req.user._id },
{
user: req.user._id,
encryptedPrivateKey,
iv,
tag,
salt,
verifier
},
{ upsert: true, new: true }
).select('+user, encryptedPrivateKey');
// issue tokens
return res.status(200).send({
message: 'Successfully updated backup private key',
backupPrivateKey
});
}
return res.status(400).send({
message: 'Failed to update backup private key'
});
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update backup private key'
});
}
};
/**
* Return backup private key for user
* @param req
* @param res
* @returns
*/
export const getBackupPrivateKey = async (req: Request, res: Response) => {
let backupPrivateKey;
try {
backupPrivateKey = await BackupPrivateKey.findOne({
user: req.user._id
}).select('+encryptedPrivateKey +iv +tag');
if (!backupPrivateKey) throw new Error('Failed to find backup private key');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get backup private key'
});
}
return res.status(200).send({
backupPrivateKey
});
}
export const resetPassword = async (req: Request, res: Response) => {
try {
const {
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
} = req.body;
await User.findByIdAndUpdate(
req.user._id.toString(),
{
encryptionVersion: 2,
protectedKey,
protectedKeyIV,
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag,
salt,
verifier
},
{
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get backup private key'
});
}
return res.status(200).send({
message: 'Successfully reset password'
});
}

View File

@ -0,0 +1,320 @@
import { Request, Response } from 'express';
import SecretApprovalRequest, { ApprovalStatus, ChangeType, IApprover, IRequestedChange } from '../../models/secretApprovalRequest';
import { Builder, IBuilder } from "builder-pattern"
import { secretObjectHasRequiredFields, validateSecrets } from '../../helpers/secret';
import _ from 'lodash';
import { SECRET_PERSONAL, SECRET_SHARED } from '../../variables';
import { BadRequestError, ResourceNotFound, UnauthorizedRequestError } from '../../utils/errors';
import { ISecret, Membership, Secret, Workspace } from '../../models';
import mongoose from 'mongoose';
export const createApprovalRequest = async (req: Request, res: Response) => {
const { workspaceId, environment, requestedChanges } = req.body;
// validate workspace
const workspaceFromDB = await Workspace.findById(workspaceId)
if (!workspaceFromDB) {
throw ResourceNotFound()
}
const environmentBelongsToWorkspace = _.some(workspaceFromDB.environments, { slug: environment })
if (!environmentBelongsToWorkspace) {
throw ResourceNotFound()
}
// check for secret duplicates
const hasSecretIdDuplicates = requestedChanges.length !== _.uniqBy(requestedChanges, 'modifiedSecretParentId').length;
if (hasSecretIdDuplicates) {
throw BadRequestError({ message: "Request cannot contain changes for duplicate secrets" })
}
// ensure the workspace has approvers set
if (!workspaceFromDB.approvers.length) {
throw BadRequestError({ message: "There are no designated approvers for this project, you must set approvers first before making a request" })
}
const approverIds = _.compact(_.map(workspaceFromDB.approvers, "userId"))
const approversFormatted: IApprover[] = approverIds.map(id => {
return { "userId": id, status: ApprovalStatus.PENDING }
})
const listOfSecretIdsToModify = _.compact(_.map(requestedChanges, "modifiedSecretParentId"))
// Ensure that the user requesting changes for the set of secrets can indeed interact with said secrets
if (listOfSecretIdsToModify.length > 0) {
await validateSecrets({
userId: req.user._id.toString(),
secretIds: listOfSecretIdsToModify
});
}
const sanitizedRequestedChangesList: IRequestedChange[] = []
requestedChanges.forEach((requestedChange: IRequestedChange) => {
const secretDetailsIsValid = secretObjectHasRequiredFields(requestedChange.modifiedSecretDetails)
if (!secretDetailsIsValid) {
throw BadRequestError({ message: "One or more required fields are missing from your modified secret" })
}
if (!requestedChange.modifiedSecretParentId && (requestedChange.type != ChangeType.DELETE.toString() && requestedChange.type != ChangeType.CREATE.toString())) {
throw BadRequestError({ message: "modifiedSecretParentId can only be empty when secret change type is DELETE or CREATE" })
}
sanitizedRequestedChangesList.push(Builder<IRequestedChange>()
.modifiedSecretParentId(requestedChange.modifiedSecretParentId)
.modifiedSecretDetails(requestedChange.modifiedSecretDetails)
.approvers(approversFormatted)
.type(requestedChange.type).build())
});
const newApprovalRequest = await SecretApprovalRequest.create({
workspace: workspaceId,
requestedByUserId: req.user._id.toString(),
environment: environment,
requestedChanges: sanitizedRequestedChangesList
})
const populatedNewApprovalRequest = await newApprovalRequest.populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
return res.send({ approvalRequest: populatedNewApprovalRequest });
};
export const getAllApprovalRequestsForUser = async (req: Request, res: Response) => {
const approvalRequests = await SecretApprovalRequest.find({
requestedByUserId: req.user._id.toString()
}).populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
.sort({ updatedAt: -1 })
res.send({ approvalRequests: approvalRequests })
}
export const getAllApprovalRequestsThatRequireUserApproval = async (req: Request, res: Response) => {
const approvalRequests = await SecretApprovalRequest.find({
'requestedChanges.approvers.userId': req.user._id.toString()
}).populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
.sort({ updatedAt: -1 })
res.send({ approvalRequests: approvalRequests })
}
export const approveApprovalRequest = async (req: Request, res: Response) => {
const { requestedChangeIds } = req.body;
const { reviewId } = req.params
const approvalRequestFromDB = await SecretApprovalRequest.findById(reviewId)
if (!approvalRequestFromDB) {
throw ResourceNotFound()
}
const requestedChangesFromDB: IRequestedChange[] = approvalRequestFromDB.requestedChanges
const filteredChangesByIds = requestedChangesFromDB.filter(change => requestedChangeIds.includes(change._id.toString()))
if (filteredChangesByIds.length != requestedChangeIds.length) {
throw BadRequestError({ message: "All requestedChangeIds should exist in this approval request" })
}
const changesThatRequireUserApproval = _.filter(filteredChangesByIds, change => {
return _.some(change.approvers, approver => {
return approver.userId.toString() == req.user._id.toString();
});
});
if (!changesThatRequireUserApproval.length) {
throw UnauthorizedRequestError({ message: "Your approval is not required for this review" })
}
if (changesThatRequireUserApproval.length != filteredChangesByIds.length) {
throw BadRequestError({ message: "You may only request to approve changes that require your approval" })
}
changesThatRequireUserApproval.forEach((requestedChange) => {
const overallChangeStatus = requestedChange.status
const currentLoggedInUserId = req.user._id.toString()
if (overallChangeStatus == ApprovalStatus.PENDING.toString()) {
requestedChange.approvers.forEach((approver) => {
if (approver.userId.toString() == currentLoggedInUserId && approver.status == ApprovalStatus.PENDING.toString()) {
approver.status = ApprovalStatus.APPROVED
}
})
let updateOverallStatusToApproved = true
requestedChange.approvers.forEach((approver) => {
if (approver.status != ApprovalStatus.APPROVED.toString()) {
updateOverallStatusToApproved = false
}
})
if (updateOverallStatusToApproved) {
requestedChange.status = ApprovalStatus.APPROVED
}
}
})
const updatedApprovalRequest = await SecretApprovalRequest.findByIdAndUpdate(reviewId, {
requestedChanges: requestedChangesFromDB
}, { new: true }).populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
res.send({ approvalRequest: updatedApprovalRequest })
}
export const rejectApprovalRequest = async (req: Request, res: Response) => {
const { requestedChangeIds } = req.body;
const { reviewId } = req.params
const approvalRequestFromDB = await SecretApprovalRequest.findById(reviewId)
if (!approvalRequestFromDB) {
throw ResourceNotFound()
}
const requestedChangesFromDB: IRequestedChange[] = approvalRequestFromDB.requestedChanges
const filteredChangesByIds = requestedChangesFromDB.filter(change => requestedChangeIds.includes(change._id.toString()))
if (filteredChangesByIds.length != requestedChangeIds.length) {
throw BadRequestError({ message: "All requestedChangeIds should exist in this approval request" })
}
const changesThatRequireUserApproval = _.filter(filteredChangesByIds, change => {
return _.some(change.approvers, approver => {
return approver.userId.toString() == req.user._id.toString();
});
});
if (!changesThatRequireUserApproval.length) {
throw UnauthorizedRequestError({ message: "Your approval is not required for this review" })
}
if (changesThatRequireUserApproval.length != filteredChangesByIds.length) {
throw BadRequestError({ message: "You may only request to reject changes that require your approval" })
}
changesThatRequireUserApproval.forEach((requestedChange) => {
const overallChangeStatus = requestedChange.status
const currentLoggedInUserId = req.user._id.toString()
if (overallChangeStatus == ApprovalStatus.PENDING.toString()) {
requestedChange.approvers.forEach((approver) => {
if (approver.userId.toString() == currentLoggedInUserId && approver.status == ApprovalStatus.PENDING.toString()) {
approver.status = ApprovalStatus.REJECTED
requestedChange.status = ApprovalStatus.REJECTED
}
})
}
})
const updatedApprovalRequest = await SecretApprovalRequest.findByIdAndUpdate(reviewId, {
requestedChanges: requestedChangesFromDB
}, { new: true }).populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
res.send({ approvalRequest: updatedApprovalRequest })
};
export const mergeApprovalRequestSecrets = async (req: Request, res: Response) => {
const { requestedChangeIds } = req.body;
const { reviewId } = req.params
// only the user who requested the set of changes can merge it
const approvalRequestFromDB = await SecretApprovalRequest.findOne({ _id: reviewId, requestedByUserId: req.user._id })
if (!approvalRequestFromDB) {
throw ResourceNotFound()
}
// ensure that this user is a member of this workspace
const membershipDetails = await Membership.find({ user: req.user._id, workspace: approvalRequestFromDB.workspace })
if (!membershipDetails) {
throw UnauthorizedRequestError()
}
// filter not merged, approved, and change ids specified in this request
const filteredChangesToMerge: IRequestedChange[] = approvalRequestFromDB.requestedChanges.filter(change => change.merged == false && change.status == ApprovalStatus.APPROVED && requestedChangeIds.includes(change._id.toString()))
if (filteredChangesToMerge.length != requestedChangeIds.length) {
throw BadRequestError({ message: "One or more changes in this approval is either already merged/not approved or do not exist" })
}
const secretsToCreate: ISecret[] = []
const secretsToUpdate: any[] = []
const secretsIdsToDelete: any[] = []
const secretIdsToModify: any[] = []
filteredChangesToMerge.forEach((requestedChange: any) => {
const overallChangeStatus = requestedChange.status
const currentLoggedInUserId = req.user._id.toString()
if (overallChangeStatus == ApprovalStatus.APPROVED.toString()) {
if (ChangeType.CREATE.toString() == requestedChange.type) {
const modifiedSecret = requestedChange.modifiedSecretDetails.toObject()
secretsToCreate.push({
...modifiedSecret,
user: requestedChange.modifiedSecretDetails.type === SECRET_PERSONAL ? currentLoggedInUserId : undefined,
})
}
if (ChangeType.UPDATE.toString() == requestedChange.type) {
const modifiedSecret = requestedChange.modifiedSecretDetails.toObject()
secretIdsToModify.push(requestedChange.modifiedSecretParentId)
secretsToUpdate.push({
filter: { _id: requestedChange.modifiedSecretParentId },
update: {
$set: {
...modifiedSecret,
user: requestedChange.modifiedSecretDetails.type === SECRET_PERSONAL ? currentLoggedInUserId : undefined,
},
$inc: {
version: 1
}
}
})
}
if (ChangeType.DELETE.toString() == requestedChange.type) {
secretsIdsToDelete.push({
_id: requestedChange.modifiedSecretParentId.toString()
})
}
requestedChange.merged = true
}
})
// ensure all secrets that are to be updated exist
const numSecretsFromDBThatRequireUpdate = await Secret.countDocuments({ _id: { $in: secretIdsToModify } });
const numSecretsFromDBThatRequireDelete = await Secret.countDocuments({ _id: { $in: secretsIdsToDelete } });
if (numSecretsFromDBThatRequireUpdate != secretIdsToModify.length || numSecretsFromDBThatRequireDelete != secretsIdsToDelete.length) {
throw BadRequestError({ message: "You cannot merge changes for secrets that no longer exist" })
}
// Add add CRUD operations into a single list of operations
const allOperationsForBulkWrite: any[] = [];
for (const updateStatement of secretsToUpdate) {
allOperationsForBulkWrite.push({ updateOne: updateStatement });
}
for (const secretId of secretsIdsToDelete) {
allOperationsForBulkWrite.push({ deleteOne: { filter: { _id: secretId } } });
}
for (const createStatement of secretsToCreate) {
allOperationsForBulkWrite.push({ insertOne: { document: createStatement } });
}
// start transaction
const session = await mongoose.startSession();
session.startTransaction();
try {
await Secret.bulkWrite(allOperationsForBulkWrite);
await SecretApprovalRequest.updateOne({ _id: reviewId, 'requestedChanges._id': { $in: requestedChangeIds } },
{ $set: { 'requestedChanges.$.merged': true } })
const updatedApproval = await SecretApprovalRequest.findById(reviewId).populate(["requestedChanges.modifiedSecretParentId", { path: 'requestedChanges.approvers.userId', select: 'firstName lastName _id' }])
res.send(updatedApproval)
} catch (error) {
await session.abortTransaction();
throw error
} finally {
session.endSession();
}
};

View File

@ -1,22 +1,15 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Key } from '../models';
import { Key, Secret } from '../../models';
import {
pushSecrets as push,
v1PushSecrets as push,
pullSecrets as pull,
reformatPullSecrets
} from '../helpers/secret';
import { pushKeys } from '../helpers/key';
import { PostHog } from 'posthog-node';
import { ENV_SET } from '../variables';
import { NODE_ENV, POSTHOG_PROJECT_API_KEY, POSTHOG_HOST } from '../config';
let client: any;
if (NODE_ENV === 'production' && POSTHOG_PROJECT_API_KEY && POSTHOG_HOST) {
client = new PostHog(POSTHOG_PROJECT_API_KEY, {
host: POSTHOG_HOST
});
}
} from '../../helpers/secret';
import { pushKeys } from '../../helpers/key';
import { eventPushSecrets } from '../../events';
import { EventService } from '../../services';
import { postHogClient } from '../../services';
interface PushSecret {
ciphertextKey: string;
@ -27,6 +20,10 @@ interface PushSecret {
ivValue: string;
tagValue: string;
hashValue: string;
ciphertextComment: string;
ivComment: string;
tagComment: string;
hashComment: string;
type: 'shared' | 'personal';
}
@ -46,7 +43,8 @@ export const pushSecrets = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
// validate environment
if (!ENV_SET.has(environment)) {
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
@ -67,12 +65,12 @@ export const pushSecrets = async (req: Request, res: Response) => {
workspaceId,
keys
});
if (client) {
// capture secrets pushed event in production
client.capture({
distinctId: req.user.email,
if (postHogClient) {
postHogClient.capture({
event: 'secrets pushed',
distinctId: req.user.email,
properties: {
numberOfSecrets: secrets.length,
environment,
@ -81,6 +79,14 @@ export const pushSecrets = async (req: Request, res: Response) => {
}
});
}
// trigger event - push secrets
EventService.handleEvent({
event: eventPushSecrets({
workspaceId
})
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
@ -110,14 +116,17 @@ export const pullSecrets = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
// validate environment
if (!ENV_SET.has(environment)) {
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
secrets = await pull({
userId: req.user._id.toString(),
workspaceId,
environment
environment,
channel: channel ? channel : 'cli',
ipAddress: req.ip
});
key = await Key.findOne({
@ -131,9 +140,9 @@ export const pullSecrets = async (req: Request, res: Response) => {
secrets = reformatPullSecrets({ secrets });
}
if (client) {
if (postHogClient) {
// capture secrets pushed event in production
client.capture({
postHogClient.capture({
distinctId: req.user.email,
event: 'secrets pulled',
properties: {
@ -167,9 +176,6 @@ export const pullSecrets = async (req: Request, res: Response) => {
* @returns
*/
export const pullSecretsServiceToken = async (req: Request, res: Response) => {
// get (encrypted) secrets from workspace with id [workspaceId]
// service token route
let secrets;
let key;
try {
@ -178,14 +184,17 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
const { workspaceId } = req.params;
// validate environment
if (!ENV_SET.has(environment)) {
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
secrets = await pull({
userId: req.serviceToken.user._id.toString(),
workspaceId,
environment
environment,
channel: 'cli',
ipAddress: req.ip
});
key = {
@ -198,9 +207,9 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
workspace: req.serviceToken.workspace
};
if (client) {
// capture secrets pushed event in production
client.capture({
if (postHogClient) {
// capture secrets pulled event in production
postHogClient.capture({
distinctId: req.serviceToken.user.email,
event: 'secrets pulled',
properties: {
@ -223,4 +232,4 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
secrets: reformatPullSecrets({ secrets }),
key
});
};
};

View File

@ -1,8 +1,7 @@
import { Request, Response } from 'express';
import { ServiceToken } from '../models';
import { createToken } from '../helpers/auth';
import { ENV_SET } from '../variables';
import { JWT_SERVICE_SECRET } from '../config';
import { ServiceToken } from '../../models';
import { createToken } from '../../helpers/auth';
import { JWT_SERVICE_SECRET } from '../../config';
/**
* Return service token on request
@ -11,7 +10,6 @@ import { JWT_SERVICE_SECRET } from '../config';
* @returns
*/
export const getServiceToken = async (req: Request, res: Response) => {
// get service token
return res.status(200).send({
serviceToken: req.serviceToken
});
@ -37,7 +35,8 @@ export const createServiceToken = async (req: Request, res: Response) => {
} = req.body;
// validate environment
if (!ENV_SET.has(environment)) {
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
@ -58,7 +57,8 @@ export const createServiceToken = async (req: Request, res: Response) => {
token = createToken({
payload: {
serviceTokenId: serviceToken._id.toString()
serviceTokenId: serviceToken._id.toString(),
workspaceId
},
expiresIn: expiresIn,
secret: JWT_SERVICE_SECRET
@ -72,4 +72,4 @@ export const createServiceToken = async (req: Request, res: Response) => {
return res.status(200).send({
token
});
};
};

View File

@ -0,0 +1,110 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { User } from '../../models';
import { JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET, INVITE_ONLY_SIGNUP } from '../../config';
import {
sendEmailVerification,
checkEmailVerification,
} from '../../helpers/signup';
import { createToken } from '../../helpers/auth';
import { BadRequestError } from '../../utils/errors';
/**
* Signup step 1: Initialize account for user under email [email] and send a verification code
* to that email
* @param req
* @param res
* @returns
*/
export const beginEmailSignup = async (req: Request, res: Response) => {
let email: string;
try {
email = req.body.email;
if (INVITE_ONLY_SIGNUP) {
// Only one user can create an account without being invited. The rest need to be invited in order to make an account
const userCount = await User.countDocuments({})
if (userCount != 0) {
throw BadRequestError({ message: "New user sign ups are not allowed at this time. You must be invited to sign up." })
}
}
const user = await User.findOne({ email }).select('+publicKey');
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed to send email verification code for complete account'
});
}
// send send verification email
await sendEmailVerification({ email });
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to send email verification code'
});
}
return res.status(200).send({
message: `Sent an email verification code to ${email}`
});
};
/**
* Signup step 2: Verify that code [code] was sent to email [email] and issue
* a temporary signup token for user to complete setting up their account
* @param req
* @param res
* @returns
*/
export const verifyEmailSignup = async (req: Request, res: Response) => {
let user, token;
try {
const { email, code } = req.body;
// initialize user account
user = await User.findOne({ email });
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed email verification for complete user'
});
}
// verify email
await checkEmailVerification({
email,
code
});
if (!user) {
user = await new User({
email
}).save();
}
// generate temporary signup token
token = createToken({
payload: {
userId: user._id.toString()
},
expiresIn: JWT_SIGNUP_LIFETIME,
secret: JWT_SIGNUP_SECRET
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed email verification'
});
}
return res.status(200).send({
message: 'Successfuly verified email',
user,
token
});
};

View File

@ -1,6 +1,6 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { UserAction } from '../models';
import { UserAction } from '../../models';
/**
* Add user action [action]

View File

@ -0,0 +1,442 @@
import { Request, Response } from "express";
import * as Sentry from "@sentry/node";
import {
Workspace,
Membership,
MembershipOrg,
Integration,
IntegrationAuth,
IUser,
ServiceToken,
ServiceTokenData,
} from "../../models";
import {
createWorkspace as create,
deleteWorkspace as deleteWork,
} from "../../helpers/workspace";
import { addMemberships } from "../../helpers/membership";
import { ADMIN } from "../../variables";
import { BadRequestError, ResourceNotFound, UnauthorizedRequestError } from "../../utils/errors";
import _ from "lodash";
/**
* Return public keys of members of workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
let publicKeys;
try {
const { workspaceId } = req.params;
publicKeys = (
await Membership.find({
workspace: workspaceId,
}).populate<{ user: IUser }>("user", "publicKey")
).map((member) => {
return {
publicKey: member.user.publicKey,
userId: member.user._id,
};
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace member public keys",
});
}
return res.status(200).send({
publicKeys,
});
};
/**
* Return memberships for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
let users;
try {
const { workspaceId } = req.params;
users = await Membership.find({
workspace: workspaceId,
}).populate("user", "+publicKey");
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace members",
});
}
return res.status(200).send({
users,
});
};
/**
* Return workspaces that user is part of
* @param req
* @param res
* @returns
*/
export const getWorkspaces = async (req: Request, res: Response) => {
let workspaces;
try {
workspaces = (
await Membership.find({
user: req.user._id,
}).populate("workspace")
).map((m) => m.workspace);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspaces",
});
}
return res.status(200).send({
workspaces,
});
};
/**
* Return workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspace = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceId } = req.params;
workspace = await Workspace.findOne({
_id: workspaceId,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace",
});
}
return res.status(200).send({
workspace,
});
};
/**
* Create new workspace named [workspaceName] under organization with id
* [organizationId] and add user as admin
* @param req
* @param res
* @returns
*/
export const createWorkspace = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceName, organizationId } = req.body;
// validate organization membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId,
});
if (!membershipOrg) {
throw new Error("Failed to validate organization membership");
}
if (workspaceName.length < 1) {
throw new Error("Workspace names must be at least 1-character long");
}
// create workspace and add user as member
workspace = await create({
name: workspaceName,
organizationId,
});
await addMemberships({
userIds: [req.user._id],
workspaceId: workspace._id.toString(),
roles: [ADMIN],
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to create workspace",
});
}
return res.status(200).send({
workspace,
});
};
/**
* Delete workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const deleteWorkspace = async (req: Request, res: Response) => {
try {
const { workspaceId } = req.params;
// delete workspace
await deleteWork({
id: workspaceId,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to delete workspace",
});
}
return res.status(200).send({
message: "Successfully deleted workspace",
});
};
/**
* Change name of workspace with id [workspaceId] to [name]
* @param req
* @param res
* @returns
*/
export const changeWorkspaceName = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceId } = req.params;
const { name } = req.body;
workspace = await Workspace.findOneAndUpdate(
{
_id: workspaceId,
},
{
name,
},
{
new: true,
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to change workspace name",
});
}
return res.status(200).send({
message: "Successfully changed workspace name",
workspace,
});
};
/**
* Return integrations for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceIntegrations = async (req: Request, res: Response) => {
let integrations;
try {
const { workspaceId } = req.params;
integrations = await Integration.find({
workspace: workspaceId,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace integrations",
});
}
return res.status(200).send({
integrations,
});
};
/**
* Return (integration) authorizations for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceIntegrationAuthorizations = async (
req: Request,
res: Response
) => {
let authorizations;
try {
const { workspaceId } = req.params;
authorizations = await IntegrationAuth.find({
workspace: workspaceId,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace integration authorizations",
});
}
return res.status(200).send({
authorizations,
});
};
export const addApproverForWorkspaceAndEnvironment = async (
req: Request,
res: Response
) => {
interface Approver {
environment: string;
userId: string;
}
const { workspaceId } = req.params;
const { approvers }: { approvers: Approver[] } = req.body;
const workspaceFromDB = await Workspace.findById(workspaceId)
if (!workspaceFromDB) {
throw ResourceNotFound()
}
const allAvailableWorkspaceEnvironments = _.map(workspaceFromDB.environments, 'slug');
const environmentsFromApprovers = _.map(approvers, "environment")
const filteredApprovers = environmentsFromApprovers.map(environment => allAvailableWorkspaceEnvironments.includes(environment))
// validate environments
if (filteredApprovers.length != environmentsFromApprovers.length) {
const err = `One or more environments set for approver(s) is invalid`
throw BadRequestError({ message: err })
}
const approverIds = _.map(approvers, "userId")
// validate approvers membership
const approversMemberships = await Membership.find({
workspace: workspaceId,
user: { $in: approverIds }
})
if (!approversMemberships) {
throw ResourceNotFound()
}
if (approversMemberships.length != approverIds.length) {
throw UnauthorizedRequestError({ message: "Approvers must be apart of the workspace they are being added to" })
}
const updatedWorkspace = await Workspace.findByIdAndUpdate(workspaceId,
{
$addToSet: {
approvers: {
$each: approvers,
}
}
}, { new: true })
return res.json(updatedWorkspace)
};
export const removeApproverForWorkspaceAndEnvironment = async (
req: Request,
res: Response
) => {
interface Approver {
environment: string;
userId: string;
}
const { workspaceId } = req.params;
const { approvers }: { approvers: Approver[] } = req.body;
const workspaceFromDB = await Workspace.findById(workspaceId)
if (!workspaceFromDB) {
throw ResourceNotFound()
}
const allAvailableWorkspaceEnvironments = _.map(workspaceFromDB.environments, 'slug');
const environmentsFromApprovers = _.map(approvers, "environment")
const filteredApprovers = environmentsFromApprovers.map(environment => allAvailableWorkspaceEnvironments.includes(environment))
// validate environments
if (filteredApprovers.length != environmentsFromApprovers.length) {
const err = `One or more environments set for approver(s) is invalid`
throw BadRequestError({ message: err })
}
const approverIds = _.map(approvers, "userId")
// validate approvers membership
const approversMemberships = await Membership.find({
workspace: workspaceId,
user: { $in: approverIds }
})
if (!approversMemberships) {
throw ResourceNotFound()
}
if (approversMemberships.length != approverIds.length) {
throw UnauthorizedRequestError({ message: "Approvers must be apart of the workspace they are being added to" })
}
const updatedWorkspace = await Workspace.findByIdAndUpdate(workspaceId, { $pullAll: { approvers: approvers } }, { new: true })
return res.json(updatedWorkspace)
};
/**
* Return service service tokens for workspace [workspaceId] belonging to user
* @param req
* @param res
* @returns
*/
export const getWorkspaceServiceTokens = async (
req: Request,
res: Response
) => {
let serviceTokens;
try {
const { workspaceId } = req.params;
// ?? FIX.
serviceTokens = await ServiceToken.find({
user: req.user._id,
workspace: workspaceId,
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get workspace service tokens",
});
}
return res.status(200).send({
serviceTokens,
});
};

View File

@ -0,0 +1,105 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import {
APIKeyData
} from '../../models';
import {
SALT_ROUNDS
} from '../../config';
/**
* Return API key data for user with id [req.user_id]
* @param req
* @param res
* @returns
*/
export const getAPIKeyData = async (req: Request, res: Response) => {
let apiKeyData;
try {
apiKeyData = await APIKeyData.find({
user: req.user._id
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get API key data'
});
}
return res.status(200).send({
apiKeyData
});
}
/**
* Create new API key data for user with id [req.user._id]
* @param req
* @param res
*/
export const createAPIKeyData = async (req: Request, res: Response) => {
let apiKey, apiKeyData;
try {
const { name, expiresIn } = req.body;
const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
apiKeyData = await new APIKeyData({
name,
expiresAt,
user: req.user._id,
secretHash
}).save();
// return api key data without sensitive data
apiKeyData = await APIKeyData.findById(apiKeyData._id);
if (!apiKeyData) throw new Error('Failed to find API key data');
apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to API key data'
});
}
return res.status(200).send({
apiKey,
apiKeyData
});
}
/**
* Delete API key data with id [apiKeyDataId].
* @param req
* @param res
* @returns
*/
export const deleteAPIKeyData = async (req: Request, res: Response) => {
let apiKeyData;
try {
const { apiKeyDataId } = req.params;
apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete API key data'
});
}
return res.status(200).send({
apiKeyData
});
}

View File

@ -0,0 +1,351 @@
/* eslint-disable @typescript-eslint/no-var-requires */
import { Request, Response } from 'express';
import jwt from 'jsonwebtoken';
import * as Sentry from '@sentry/node';
import * as bigintConversion from 'bigint-conversion';
const jsrp = require('jsrp');
import { User, LoginSRPDetail } from '../../models';
import { issueAuthTokens, createToken } from '../../helpers/auth';
import { checkUserDevice } from '../../helpers/user';
import { sendMail } from '../../helpers/nodemailer';
import { TokenService } from '../../services';
import { EELogService } from '../../ee/services';
import {
NODE_ENV,
JWT_MFA_LIFETIME,
JWT_MFA_SECRET
} from '../../config';
import { BadRequestError, InternalServerError } from '../../utils/errors';
import {
TOKEN_EMAIL_MFA,
ACTION_LOGIN
} from '../../variables';
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
declare module 'jsonwebtoken' {
export interface UserIDJwtPayload extends jwt.JwtPayload {
userId: string;
}
}
const clientPublicKeys: any = {};
/**
* Log in user step 1: Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
* @param req
* @param res
* @returns
*/
export const login1 = async (req: Request, res: Response) => {
try {
const {
email,
clientPublicKey
}: { email: string; clientPublicKey: string } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier');
if (!user) throw new Error('Failed to find user');
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier
},
async () => {
// generate server-side public key
const serverPublicKey = server.getPublicKey();
await LoginSRPDetail.findOneAndReplace({ email: email }, {
email: email,
clientPublicKey: clientPublicKey,
serverBInt: bigintConversion.bigintToBuf(server.bInt),
}, { upsert: true, returnNewDocument: false });
return res.status(200).send({
serverPublicKey,
salt: user.salt
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to start authentication process'
});
}
};
/**
* Log in user step 2: complete step 2 of SRP protocol and return token and their (encrypted)
* private key
* @param req
* @param res
* @returns
*/
export const login2 = async (req: Request, res: Response) => {
try {
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
const { email, clientProof } = req.body;
const user = await User.findOne({
email
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag');
if (!user) throw new Error('Failed to find user');
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
if (!loginSRPDetail) {
return BadRequestError(Error("Failed to find login details for SRP"))
}
const server = new jsrp.server();
server.init(
{
salt: user.salt,
verifier: user.verifier,
b: loginSRPDetail.serverBInt
},
async () => {
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
// compare server and client shared keys
if (server.checkClientProof(clientProof)) {
if (user.isMfaEnabled) {
// case: user has MFA enabled
// generate temporary MFA token
const token = createToken({
payload: {
userId: user._id.toString()
},
expiresIn: JWT_MFA_LIFETIME,
secret: JWT_MFA_SECRET
});
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email
});
// send MFA code [code] to [email]
await sendMail({
template: 'emailMfa.handlebars',
subjectLine: 'Infisical MFA code',
recipients: [email],
substitutions: {
code
}
});
return res.status(200).send({
mfaEnabled: true,
token
});
}
await checkUserDevice({
user,
ip: req.ip,
userAgent: req.headers['user-agent'] ?? ''
});
// issue tokens
const tokens = await issueAuthTokens({ userId: user._id.toString() });
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
// case: user does not have MFA enablgged
// return (access) token in response
interface ResponseData {
mfaEnabled: boolean;
encryptionVersion: any;
protectedKey?: string;
protectedKeyIV?: string;
protectedKeyTag?: string;
token: string;
publicKey?: string;
encryptedPrivateKey?: string;
iv?: string;
tag?: string;
}
const response: ResponseData = {
mfaEnabled: false,
encryptionVersion: user.encryptionVersion,
token: tokens.token,
publicKey: user.publicKey,
encryptedPrivateKey: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag
}
if (
user?.protectedKey &&
user?.protectedKeyIV &&
user?.protectedKeyTag
) {
response.protectedKey = user.protectedKey;
response.protectedKeyIV = user.protectedKeyIV
response.protectedKeyTag = user.protectedKeyTag;
}
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
return res.status(200).send(response);
}
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to authenticate. Try again?'
});
}
};
/**
* Send MFA token to email [email]
* @param req
* @param res
*/
export const sendMfaToken = async (req: Request, res: Response) => {
try {
const { email } = req.body;
const code = await TokenService.createToken({
type: TOKEN_EMAIL_MFA,
email
});
// send MFA code [code] to [email]
await sendMail({
template: 'emailMfa.handlebars',
subjectLine: 'Infisical MFA code',
recipients: [email],
substitutions: {
code
}
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to send MFA code'
});
}
return res.status(200).send({
message: 'Successfully sent new MFA code'
});
}
/**
* Verify MFA token [mfaToken] and issue JWT and refresh tokens if the
* MFA token [mfaToken] is valid
* @param req
* @param res
*/
export const verifyMfaToken = async (req: Request, res: Response) => {
const { email, mfaToken } = req.body;
await TokenService.validateToken({
type: TOKEN_EMAIL_MFA,
email,
token: mfaToken
});
const user = await User.findOne({
email
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag');
if (!user) throw new Error('Failed to find user');
await checkUserDevice({
user,
ip: req.ip,
userAgent: req.headers['user-agent'] ?? ''
});
// issue tokens
const tokens = await issueAuthTokens({ userId: user._id.toString() });
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
interface VerifyMfaTokenRes {
encryptionVersion: number;
protectedKey?: string;
protectedKeyIV?: string;
protectedKeyTag?: string;
token: string;
publicKey: string;
encryptedPrivateKey: string;
iv: string;
tag: string;
}
const resObj: VerifyMfaTokenRes = {
encryptionVersion: user.encryptionVersion,
token: tokens.token,
publicKey: user.publicKey as string,
encryptedPrivateKey: user.encryptedPrivateKey as string,
iv: user.iv as string,
tag: user.tag as string
}
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
resObj.protectedKey = user.protectedKey;
resObj.protectedKeyIV = user.protectedKeyIV;
resObj.protectedKeyTag = user.protectedKeyTag;
}
const loginAction = await EELogService.createAction({
name: ACTION_LOGIN,
userId: user._id
});
loginAction && await EELogService.createLog({
userId: user._id,
actions: [loginAction],
channel: getChannelFromUserAgent(req.headers['user-agent']),
ipAddress: req.ip
});
return res.status(200).send(resObj);
}

View File

@ -0,0 +1,262 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import {
Secret,
ServiceToken,
Workspace,
Integration,
ServiceTokenData,
Membership,
} from '../../models';
import { SecretVersion } from '../../ee/models';
import { BadRequestError } from '../../utils/errors';
import _ from 'lodash';
import { ABILITY_READ, ABILITY_WRITE } from '../../variables/organization';
/**
* Create new workspace environment named [environmentName] under workspace with id
* @param req
* @param res
* @returns
*/
export const createWorkspaceEnvironment = async (
req: Request,
res: Response
) => {
const { workspaceId } = req.params;
const { environmentName, environmentSlug } = req.body;
try {
const workspace = await Workspace.findById(workspaceId).exec();
if (
!workspace ||
workspace?.environments.find(
({ name, slug }) => slug === environmentSlug || environmentName === name
)
) {
throw new Error('Failed to create workspace environment');
}
workspace?.environments.push({
name: environmentName,
slug: environmentSlug.toLowerCase(),
});
await workspace.save();
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create new workspace environment',
});
}
return res.status(200).send({
message: 'Successfully created new environment',
workspace: workspaceId,
environment: {
name: environmentName,
slug: environmentSlug,
},
});
};
/**
* Rename workspace environment with new name and slug of a workspace with [workspaceId]
* Old slug [oldEnvironmentSlug] must be provided
* @param req
* @param res
* @returns
*/
export const renameWorkspaceEnvironment = async (
req: Request,
res: Response
) => {
const { workspaceId } = req.params;
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
try {
// user should pass both new slug and env name
if (!environmentSlug || !environmentName) {
throw new Error('Invalid environment given.');
}
// atomic update the env to avoid conflict
const workspace = await Workspace.findById(workspaceId).exec();
if (!workspace) {
throw new Error('Failed to create workspace environment');
}
const isEnvExist = workspace.environments.some(
({ name, slug }) =>
slug !== oldEnvironmentSlug &&
(name === environmentName || slug === environmentSlug)
);
if (isEnvExist) {
throw new Error('Invalid environment given');
}
const envIndex = workspace?.environments.findIndex(
({ slug }) => slug === oldEnvironmentSlug
);
if (envIndex === -1) {
throw new Error('Invalid environment given');
}
workspace.environments[envIndex].name = environmentName;
workspace.environments[envIndex].slug = environmentSlug.toLowerCase();
await workspace.save();
await Secret.updateMany(
{ workspace: workspaceId, environment: oldEnvironmentSlug },
{ environment: environmentSlug }
);
await SecretVersion.updateMany(
{ workspace: workspaceId, environment: oldEnvironmentSlug },
{ environment: environmentSlug }
);
await ServiceToken.updateMany(
{ workspace: workspaceId, environment: oldEnvironmentSlug },
{ environment: environmentSlug }
);
await ServiceTokenData.updateMany(
{ workspace: workspaceId, environment: oldEnvironmentSlug },
{ environment: environmentSlug }
);
await Integration.updateMany(
{ workspace: workspaceId, environment: oldEnvironmentSlug },
{ environment: environmentSlug }
);
await Membership.updateMany(
{
workspace: workspaceId,
"deniedPermissions.environmentSlug": oldEnvironmentSlug
},
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
)
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update workspace environment',
});
}
return res.status(200).send({
message: 'Successfully update environment',
workspace: workspaceId,
environment: {
name: environmentName,
slug: environmentSlug,
},
});
};
/**
* Delete workspace environment by [environmentSlug] of workspace [workspaceId] and do the clean up
* @param req
* @param res
* @returns
*/
export const deleteWorkspaceEnvironment = async (
req: Request,
res: Response
) => {
const { workspaceId } = req.params;
const { environmentSlug } = req.body;
try {
// atomic update the env to avoid conflict
const workspace = await Workspace.findById(workspaceId).exec();
if (!workspace) {
throw new Error('Failed to create workspace environment');
}
const envIndex = workspace?.environments.findIndex(
({ slug }) => slug === environmentSlug
);
if (envIndex === -1) {
throw new Error('Invalid environment given');
}
workspace.environments.splice(envIndex, 1);
await workspace.save();
// clean up
await Secret.deleteMany({
workspace: workspaceId,
environment: environmentSlug,
});
await SecretVersion.deleteMany({
workspace: workspaceId,
environment: environmentSlug,
});
await ServiceToken.deleteMany({
workspace: workspaceId,
environment: environmentSlug,
});
await ServiceTokenData.deleteMany({
workspace: workspaceId,
environment: environmentSlug,
});
await Integration.deleteMany({
workspace: workspaceId,
environment: environmentSlug,
});
await Membership.updateMany(
{ workspace: workspaceId },
{ $pull: { deniedPermissions: { environmentSlug: environmentSlug } } }
)
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete workspace environment',
});
}
return res.status(200).send({
message: 'Successfully deleted environment',
workspace: workspaceId,
environment: environmentSlug,
});
};
export const getAllAccessibleEnvironmentsOfWorkspace = async (
req: Request,
res: Response
) => {
const { workspaceId } = req.params;
const workspacesUserIsMemberOf = await Membership.findOne({
workspace: workspaceId,
user: req.user
})
if (!workspacesUserIsMemberOf) {
throw BadRequestError()
}
const accessibleEnvironments: any = []
const deniedPermission = workspacesUserIsMemberOf.deniedPermissions
const relatedWorkspace = await Workspace.findById(workspaceId)
if (!relatedWorkspace) {
throw BadRequestError()
}
relatedWorkspace.environments.forEach(environment => {
const isReadBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_READ })
const isWriteBlocked = _.some(deniedPermission, { environmentSlug: environment.slug, ability: ABILITY_WRITE })
if (isReadBlocked && isWriteBlocked) {
return
} else {
accessibleEnvironments.push({
name: environment.name,
slug: environment.slug,
isWriteDenied: isWriteBlocked,
isReadDenied: isReadBlocked
})
}
})
res.json({ accessibleEnvironments })
};

View File

@ -0,0 +1,25 @@
import * as authController from './authController';
import * as signupController from './signupController';
import * as usersController from './usersController';
import * as organizationsController from './organizationsController';
import * as workspaceController from './workspaceController';
import * as serviceTokenDataController from './serviceTokenDataController';
import * as apiKeyDataController from './apiKeyDataController';
import * as secretController from './secretController';
import * as secretsController from './secretsController';
import * as environmentController from './environmentController';
import * as tagController from './tagController';
export {
authController,
signupController,
usersController,
organizationsController,
workspaceController,
serviceTokenDataController,
apiKeyDataController,
secretController,
secretsController,
environmentController,
tagController
}

View File

@ -0,0 +1,296 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import {
MembershipOrg,
Membership,
Workspace
} from '../../models';
import { deleteMembershipOrg } from '../../helpers/membershipOrg';
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
/**
* Return memberships for organization with id [organizationId]
* @param req
* @param res
*/
export const getOrganizationMemberships = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return organization memberships'
#swagger.description = 'Return organization memberships'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['organizationId'] = {
"description": "ID of organization",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"memberships": {
"type": "array",
"items": {
$ref: "#/components/schemas/MembershipOrg"
},
"description": "Memberships of organization"
}
}
}
}
}
}
*/
let memberships;
try {
const { organizationId } = req.params;
memberships = await MembershipOrg.find({
organization: organizationId
}).populate('user', '+publicKey');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get organization memberships'
});
}
return res.status(200).send({
memberships
});
}
/**
* Update role of membership with id [membershipId] to role [role]
* @param req
* @param res
*/
export const updateOrganizationMembership = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Update organization membership'
#swagger.description = 'Update organization membership'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['organizationId'] = {
"description": "ID of organization",
"required": true,
"type": "string"
}
#swagger.parameters['membershipId'] = {
"description": "ID of organization membership to update",
"required": true,
"type": "string"
}
#swagger.requestBody = {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"role": {
"type": "string",
"description": "Role of organization membership - either owner, admin, or member",
}
}
}
}
}
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"membership": {
$ref: "#/components/schemas/MembershipOrg",
"description": "Updated organization membership"
}
}
}
}
}
}
*/
let membership;
try {
const { membershipId } = req.params;
const { role } = req.body;
membership = await MembershipOrg.findByIdAndUpdate(
membershipId,
{
role
}, {
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update organization membership'
});
}
return res.status(200).send({
membership
});
}
/**
* Delete organization membership with id [membershipId]
* @param req
* @param res
* @returns
*/
export const deleteOrganizationMembership = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Delete organization membership'
#swagger.description = 'Delete organization membership'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['organizationId'] = {
"description": "ID of organization",
"required": true,
"type": "string"
}
#swagger.parameters['membershipId'] = {
"description": "ID of organization membership to delete",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"membership": {
$ref: "#/components/schemas/MembershipOrg",
"description": "Deleted organization membership"
}
}
}
}
}
}
*/
let membership;
try {
const { membershipId } = req.params;
// delete organization membership
membership = await deleteMembershipOrg({
membershipOrgId: membershipId
});
await updateSubscriptionOrgQuantity({
organizationId: membership.organization.toString()
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete organization membership'
});
}
return res.status(200).send({
membership
});
}
/**
* Return workspaces for organization with id [organizationId] that user has
* access to
* @param req
* @param res
*/
export const getOrganizationWorkspaces = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return projects in organization that user is part of'
#swagger.description = 'Return projects in organization that user is part of'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['organizationId'] = {
"description": "ID of organization",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"workspaces": {
"type": "array",
"items": {
$ref: "#/components/schemas/Project"
},
"description": "Projects of organization"
}
}
}
}
}
}
*/
let workspaces;
try {
const { organizationId } = req.params;
const workspacesSet = new Set(
(
await Workspace.find(
{
organization: organizationId
},
'_id'
)
).map((w) => w._id.toString())
);
workspaces = (
await Membership.find({
user: req.user._id
}).populate('workspace')
)
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
.map((m) => m.workspace);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get organization workspaces'
});
}
return res.status(200).send({
workspaces
});
}

View File

@ -0,0 +1,401 @@
import to from "await-to-js";
import { Request, Response } from "express";
import mongoose, { Types } from "mongoose";
import Secret, { ISecret } from "../../models/secret";
import { CreateSecretRequestBody, ModifySecretRequestBody, SanitizedSecretForCreate, SanitizedSecretModify } from "../../types/secret";
const { ValidationError } = mongoose.Error;
import { BadRequestError, InternalServerError, UnauthorizedRequestError, ValidationError as RouteValidationError } from '../../utils/errors';
import { AnyBulkWriteOperation } from 'mongodb';
import { SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
import { postHogClient } from '../../services';
/**
* Create secret for workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
*/
export const createSecret = async (req: Request, res: Response) => {
const secretToCreate: CreateSecretRequestBody = req.body.secret;
const { workspaceId, environment } = req.params
const sanitizedSecret: SanitizedSecretForCreate = {
secretKeyCiphertext: secretToCreate.secretKeyCiphertext,
secretKeyIV: secretToCreate.secretKeyIV,
secretKeyTag: secretToCreate.secretKeyTag,
secretKeyHash: secretToCreate.secretKeyHash,
secretValueCiphertext: secretToCreate.secretValueCiphertext,
secretValueIV: secretToCreate.secretValueIV,
secretValueTag: secretToCreate.secretValueTag,
secretValueHash: secretToCreate.secretValueHash,
secretCommentCiphertext: secretToCreate.secretCommentCiphertext,
secretCommentIV: secretToCreate.secretCommentIV,
secretCommentTag: secretToCreate.secretCommentTag,
secretCommentHash: secretToCreate.secretCommentHash,
workspace: new Types.ObjectId(workspaceId),
environment,
type: secretToCreate.type,
user: new Types.ObjectId(req.user._id)
}
const [error, secret] = await to(Secret.create(sanitizedSecret).then())
if (error instanceof ValidationError) {
throw RouteValidationError({ message: error.message, stack: error.stack })
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secret
})
}
/**
* Create many secrets for workspace wiht id [workspaceId] and environment [environment]
* @param req
* @param res
*/
export const createSecrets = async (req: Request, res: Response) => {
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
const { workspaceId, environment } = req.params
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
secretsToCreate.forEach(rawSecret => {
const safeUpdateFields: SanitizedSecretForCreate = {
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
secretKeyIV: rawSecret.secretKeyIV,
secretKeyTag: rawSecret.secretKeyTag,
secretKeyHash: rawSecret.secretKeyHash,
secretValueCiphertext: rawSecret.secretValueCiphertext,
secretValueIV: rawSecret.secretValueIV,
secretValueTag: rawSecret.secretValueTag,
secretValueHash: rawSecret.secretValueHash,
secretCommentCiphertext: rawSecret.secretCommentCiphertext,
secretCommentIV: rawSecret.secretCommentIV,
secretCommentTag: rawSecret.secretCommentTag,
secretCommentHash: rawSecret.secretCommentHash,
workspace: new Types.ObjectId(workspaceId),
environment,
type: rawSecret.type,
user: new Types.ObjectId(req.user._id)
}
sanitizedSecretesToCreate.push(safeUpdateFields)
})
const [bulkCreateError, secrets] = await to(Secret.insertMany(sanitizedSecretesToCreate).then())
if (bulkCreateError) {
if (bulkCreateError instanceof ValidationError) {
throw RouteValidationError({ message: bulkCreateError.message, stack: bulkCreateError.stack })
}
throw InternalServerError({ message: "Unable to process your batch create request. Please try again", stack: bulkCreateError.stack })
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets added',
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsToCreate ?? []).length,
workspaceId,
environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secrets
})
}
/**
* Delete secrets in workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
*/
export const deleteSecrets = async (req: Request, res: Response) => {
const { workspaceId, environmentName } = req.params
const secretIdsToDelete: string[] = req.body.secretIds
const [secretIdsUserCanDeleteError, secretIdsUserCanDelete] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
if (secretIdsUserCanDeleteError) {
throw InternalServerError({ message: `Unable to fetch secrets you own: [error=${secretIdsUserCanDeleteError.message}]` })
}
const secretsUserCanDeleteSet: Set<string> = new Set(secretIdsUserCanDelete.map(objectId => objectId._id.toString()));
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = []
let numSecretsDeleted = 0;
secretIdsToDelete.forEach(secretIdToDelete => {
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
const deleteOperation = { deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } }
deleteOperationsToPerform.push(deleteOperation)
numSecretsDeleted++;
} else {
throw RouteValidationError({ message: "You cannot delete secrets that you do not have access to" })
}
})
const [bulkDeleteError, bulkDelete] = await to(Secret.bulkWrite(deleteOperationsToPerform).then())
if (bulkDeleteError) {
if (bulkDeleteError instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkDeleteError.stack })
}
throw InternalServerError()
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: req.user.email,
properties: {
numberOfSecrets: numSecretsDeleted,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send()
}
/**
* Delete secret with id [secretId]
* @param req
* @param res
*/
export const deleteSecret = async (req: Request, res: Response) => {
await Secret.findByIdAndDelete(req._secret._id)
if (postHogClient) {
postHogClient.capture({
event: 'secrets deleted',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
workspaceId: req._secret.workspace.toString(),
environment: req._secret.environment,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
res.status(200).send({
secret: req._secret
})
}
/**
* Update secrets for workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @returns
*/
export const updateSecrets = async (req: Request, res: Response) => {
const { workspaceId, environmentName } = req.params
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
if (secretIdsUserCanModifyError) {
throw InternalServerError({ message: "Unable to fetch secrets you own" })
}
const secretsUserCanModifySet: Set<string> = new Set(secretIdsUserCanModify.map(objectId => objectId._id.toString()));
const updateOperationsToPerform: any = []
secretsModificationsRequested.forEach(userModifiedSecret => {
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
const sanitizedSecret: SanitizedSecretModify = {
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
secretKeyIV: userModifiedSecret.secretKeyIV,
secretKeyTag: userModifiedSecret.secretKeyTag,
secretKeyHash: userModifiedSecret.secretKeyHash,
secretValueCiphertext: userModifiedSecret.secretValueCiphertext,
secretValueIV: userModifiedSecret.secretValueIV,
secretValueTag: userModifiedSecret.secretValueTag,
secretValueHash: userModifiedSecret.secretValueHash,
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
secretCommentIV: userModifiedSecret.secretCommentIV,
secretCommentTag: userModifiedSecret.secretCommentTag,
secretCommentHash: userModifiedSecret.secretCommentHash,
}
const updateOperation = { updateOne: { filter: { _id: userModifiedSecret._id, workspace: workspaceId }, update: { $inc: { version: 1 }, $set: sanitizedSecret } } }
updateOperationsToPerform.push(updateOperation)
} else {
throw UnauthorizedRequestError({ message: "You do not have permission to modify one or more of the requested secrets" })
}
})
const [bulkModificationInfoError, bulkModificationInfo] = await to(Secret.bulkWrite(updateOperationsToPerform).then())
if (bulkModificationInfoError) {
if (bulkModificationInfoError instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: bulkModificationInfoError.stack })
}
throw InternalServerError()
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: req.user.email,
properties: {
numberOfSecrets: (secretsModificationsRequested ?? []).length,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.status(200).send()
}
/**
* Update a secret within workspace with id [workspaceId] and environment [environment]
* @param req
* @param res
* @returns
*/
export const updateSecret = async (req: Request, res: Response) => {
const { workspaceId, environmentName } = req.params
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
const [secretIdUserCanModifyError, secretIdUserCanModify] = await to(Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
if (secretIdUserCanModifyError && !secretIdUserCanModify) {
throw BadRequestError()
}
const sanitizedSecret: SanitizedSecretModify = {
secretKeyCiphertext: secretModificationsRequested.secretKeyCiphertext,
secretKeyIV: secretModificationsRequested.secretKeyIV,
secretKeyTag: secretModificationsRequested.secretKeyTag,
secretKeyHash: secretModificationsRequested.secretKeyHash,
secretValueCiphertext: secretModificationsRequested.secretValueCiphertext,
secretValueIV: secretModificationsRequested.secretValueIV,
secretValueTag: secretModificationsRequested.secretValueTag,
secretValueHash: secretModificationsRequested.secretValueHash,
secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext,
secretCommentIV: secretModificationsRequested.secretCommentIV,
secretCommentTag: secretModificationsRequested.secretCommentTag,
secretCommentHash: secretModificationsRequested.secretCommentHash,
}
const [error, singleModificationUpdate] = await to(Secret.updateOne({ _id: secretModificationsRequested._id, workspace: workspaceId }, { $inc: { version: 1 }, $set: sanitizedSecret }).then())
if (error instanceof ValidationError) {
throw RouteValidationError({ message: "Unable to apply modifications, please try again", stack: error.stack })
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
distinctId: req.user.email,
properties: {
numberOfSecrets: 1,
environment: environmentName,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.status(200).send(singleModificationUpdate)
}
/**
* Return secrets for workspace with id [workspaceId], environment [environment] and user
* with id [req.user._id]
* @param req
* @param res
* @returns
*/
export const getSecrets = async (req: Request, res: Response) => {
const { environment } = req.query;
const { workspaceId } = req.params;
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
let userEmail: Types.ObjectId | undefined = undefined // used for posthog
if (req.user) {
userId = req.user._id;
userEmail = req.user.email;
}
if (req.serviceTokenData) {
userId = req.serviceTokenData.user._id
userEmail = req.serviceTokenData.user.email;
}
const [err, secrets] = await to(Secret.find(
{
workspace: workspaceId,
environment,
$or: [{ user: userId }, { user: { $exists: false } }],
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
}
).then())
if (err) {
throw RouteValidationError({ message: "Failed to get secrets, please try again", stack: err.stack })
}
if (postHogClient) {
postHogClient.capture({
event: 'secrets pulled',
distinctId: userEmail,
properties: {
numberOfSecrets: (secrets ?? []).length,
environment,
workspaceId,
channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
userAgent: req.headers?.['user-agent']
}
});
}
return res.json(secrets)
}
/**
* Return secret with id [secretId]
* @param req
* @param res
* @returns
*/
export const getSecret = async (req: Request, res: Response) => {
// if (postHogClient) {
// postHogClient.capture({
// event: 'secrets pulled',
// distinctId: req.user.email,
// properties: {
// numberOfSecrets: 1,
// workspaceId: req._secret.workspace.toString(),
// environment: req._secret.environment,
// channel: req.headers?.['user-agent']?.toLowerCase().includes('mozilla') ? 'web' : 'cli',
// userAgent: req.headers?.['user-agent']
// }
// });
// }
return res.status(200).send({
secret: req._secret
});
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,114 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import crypto from 'crypto';
import bcrypt from 'bcrypt';
import {
ServiceTokenData
} from '../../models';
import {
SALT_ROUNDS
} from '../../config';
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
import { ABILITY_READ } from '../../variables/organization';
/**
* Return service token data associated with service token on request
* @param req
* @param res
* @returns
*/
export const getServiceTokenData = async (req: Request, res: Response) => res.status(200).json(req.serviceTokenData);
/**
* Create new service token data for workspace with id [workspaceId] and
* environment [environment].
* @param req
* @param res
* @returns
*/
export const createServiceTokenData = async (req: Request, res: Response) => {
let serviceToken, serviceTokenData;
try {
const {
name,
workspaceId,
environment,
encryptedKey,
iv,
tag,
expiresIn
} = req.body;
const hasAccess = await userHasWorkspaceAccess(req.user, workspaceId, environment, ABILITY_READ)
if (!hasAccess) {
throw UnauthorizedRequestError({ message: "You do not have the necessary permission(s) perform this action" })
}
const secret = crypto.randomBytes(16).toString('hex');
const secretHash = await bcrypt.hash(secret, SALT_ROUNDS);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
serviceTokenData = await new ServiceTokenData({
name,
workspace: workspaceId,
environment,
user: req.user._id,
expiresAt,
secretHash,
encryptedKey,
iv,
tag
}).save();
// return service token data without sensitive data
serviceTokenData = await ServiceTokenData.findById(serviceTokenData._id);
if (!serviceTokenData) throw new Error('Failed to find service token data');
serviceToken = `st.${serviceTokenData._id.toString()}.${secret}`;
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create service token data'
});
}
return res.status(200).send({
serviceToken,
serviceTokenData
});
}
/**
* Delete service token data with id [serviceTokenDataId].
* @param req
* @param res
* @returns
*/
export const deleteServiceTokenData = async (req: Request, res: Response) => {
let serviceTokenData;
try {
const { serviceTokenDataId } = req.params;
serviceTokenData = await ServiceTokenData.findByIdAndDelete(serviceTokenDataId);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete service token data'
});
}
return res.status(200).send({
serviceTokenData
});
}
function UnauthorizedRequestError(arg0: { message: string; }) {
throw new Error('Function not implemented.');
}

View File

@ -1,107 +1,14 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { NODE_ENV, JWT_SIGNUP_LIFETIME, JWT_SIGNUP_SECRET } from '../config';
import { User, MembershipOrg } from '../models';
import { completeAccount } from '../helpers/user';
import { User, MembershipOrg } from '../../models';
import { completeAccount } from '../../helpers/user';
import {
sendEmailVerification,
checkEmailVerification,
initializeDefaultOrg
} from '../helpers/signup';
import { issueTokens, createToken } from '../helpers/auth';
import { INVITED, ACCEPTED } from '../variables';
/**
* Signup step 1: Initialize account for user under email [email] and send a verification code
* to that email
* @param req
* @param res
* @returns
*/
export const beginEmailSignup = async (req: Request, res: Response) => {
let email: string;
try {
email = req.body.email;
const user = await User.findOne({ email }).select('+publicKey');
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed to send email verification code for complete account'
});
}
// send send verification email
await sendEmailVerification({ email });
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed to send email verification code'
});
}
return res.status(200).send({
message: `Sent an email verification code to ${email}`
});
};
/**
* Signup step 2: Verify that code [code] was sent to email [email] and issue
* a temporary signup token for user to complete setting up their account
* @param req
* @param res
* @returns
*/
export const verifyEmailSignup = async (req: Request, res: Response) => {
let user, token;
try {
const { email, code } = req.body;
// initialize user account
user = await User.findOne({ email });
if (user && user?.publicKey) {
// case: user has already completed account
return res.status(403).send({
error: 'Failed email verification for complete user'
});
}
// verify email
await checkEmailVerification({
email,
code
});
if (!user) {
user = await new User({
email
}).save();
}
// generate temporary signup token
token = createToken({
payload: {
userId: user._id.toString()
},
expiresIn: JWT_SIGNUP_LIFETIME,
secret: JWT_SIGNUP_SECRET
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
return res.status(400).send({
error: 'Failed email verification'
});
}
return res.status(200).send({
message: 'Successfuly verified email',
user,
token
});
};
} from '../../helpers/signup';
import { issueAuthTokens } from '../../helpers/auth';
import { INVITED, ACCEPTED } from '../../variables';
import { NODE_ENV } from '../../config';
import request from '../../config/request';
/**
* Complete setting up user by adding their personal and auth information as part of the
@ -117,14 +24,31 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
iv,
tag,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier,
organizationName
} = req.body;
}: {
email: string;
firstName: string;
lastName: string;
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
publicKey: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
encryptedPrivateKeyTag: string;
salt: string;
verifier: string;
organizationName: string;
} = req.body;
// get user
user = await User.findOne({ email });
@ -142,10 +66,14 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
userId: user._id.toString(),
firstName,
lastName,
encryptionVersion: 2,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
iv,
tag,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
});
@ -173,12 +101,34 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
);
// issue tokens
const tokens = await issueTokens({
const tokens = await issueAuthTokens({
userId: user._id.toString()
});
token = tokens.token;
refreshToken = tokens.refreshToken;
// sending a welcome email to new users
if (process.env.LOOPS_API_KEY) {
await request.post("https://app.loops.so/api/v1/events/send", {
"email": email,
"eventName": "Sign Up",
"firstName": firstName,
"lastName": lastName
}, {
headers: {
"Accept": "application/json",
"Authorization": "Bearer " + process.env.LOOPS_API_KEY
},
});
}
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
@ -190,10 +140,10 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
return res.status(200).send({
message: 'Successfully set up account',
user,
token,
refreshToken
token
});
};
/**
* Complete setting up user by adding their personal and auth information as part of the
* invite flow
@ -208,10 +158,13 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
email,
firstName,
lastName,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
iv,
tag,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
} = req.body;
@ -239,10 +192,14 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
userId: user._id.toString(),
firstName,
lastName,
encryptionVersion: 2,
protectedKey,
protectedKeyIV,
protectedKeyTag,
publicKey,
encryptedPrivateKey,
iv,
tag,
encryptedPrivateKeyIV,
encryptedPrivateKeyTag,
salt,
verifier
});
@ -264,12 +221,19 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
);
// issue tokens
const tokens = await issueTokens({
const tokens = await issueAuthTokens({
userId: user._id.toString()
});
token = tokens.token;
refreshToken = tokens.refreshToken;
// store (refresh) token in httpOnly cookie
res.cookie('jid', tokens.refreshToken, {
httpOnly: true,
path: '/',
sameSite: 'strict',
secure: NODE_ENV === 'production' ? true : false
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
@ -277,11 +241,10 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
message: 'Failed to complete account setup'
});
}
return res.status(200).send({
message: 'Successfully set up account',
user,
token,
refreshToken
token
});
};
};

View File

@ -0,0 +1,72 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import {
Membership, Secret,
} from '../../models';
import Tag, { ITag } from '../../models/tag';
import { Builder } from "builder-pattern"
import to from 'await-to-js';
import { BadRequestError, UnauthorizedRequestError } from '../../utils/errors';
import { MongoError } from 'mongodb';
import { userHasWorkspaceAccess } from '../../ee/helpers/checkMembershipPermissions';
export const createWorkspaceTag = async (req: Request, res: Response) => {
const { workspaceId } = req.params
const { name, slug } = req.body
const sanitizedTagToCreate = Builder<ITag>()
.name(name)
.workspace(new Types.ObjectId(workspaceId))
.slug(slug)
.user(new Types.ObjectId(req.user._id))
.build();
const [err, createdTag] = await to(Tag.create(sanitizedTagToCreate))
if (err) {
if ((err as MongoError).code === 11000) {
throw BadRequestError({ message: "Tags must be unique in a workspace" })
}
throw err
}
res.json(createdTag)
}
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
const { tagId } = req.params
const tagFromDB = await Tag.findById(tagId)
if (!tagFromDB) {
throw BadRequestError()
}
// can only delete if the request user is one that belongs to the same workspace as the tag
const membership = await Membership.findOne({
user: req.user,
workspace: tagFromDB.workspace
});
if (!membership) {
UnauthorizedRequestError({ message: 'Failed to validate membership' });
}
const result = await Tag.findByIdAndDelete(tagId);
// remove the tag from secrets
await Secret.updateMany(
{ tags: { $in: [tagId] } },
{ $pull: { tags: tagId } }
);
res.json(result);
}
export const getWorkspaceTags = async (req: Request, res: Response) => {
const { workspaceId } = req.params
const workspaceTags = await Tag.find({ workspace: workspaceId })
return res.json({
workspaceTags
})
}

View File

@ -0,0 +1,147 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import {
User,
MembershipOrg
} from '../../models';
/**
* Return the current user.
* @param req
* @param res
* @returns
*/
export const getMe = async (req: Request, res: Response) => {
/*
#swagger.summary = "Retrieve the current user on the request"
#swagger.description = "Retrieve the current user on the request"
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"user": {
"type": "object",
$ref: "#/components/schemas/CurrentUser",
"description": "Current user on request"
}
}
}
}
}
}
*/
let user;
try {
user = await User
.findById(req.user._id)
.select('+publicKey +encryptedPrivateKey +iv +tag');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get current user'
});
}
return res.status(200).send({
user
});
}
/**
* Update the current user's MFA-enabled status [isMfaEnabled].
* Note: Infisical currently only supports email-based 2FA only; this will expand to
* include SMS and authenticator app modes of authentication in the future.
* @param req
* @param res
* @returns
*/
export const updateMyMfaEnabled = async (req: Request, res: Response) => {
let user;
try {
const { isMfaEnabled }: { isMfaEnabled: boolean } = req.body;
req.user.isMfaEnabled = isMfaEnabled;
if (isMfaEnabled) {
// TODO: adapt this route/controller
// to work for different forms of MFA
req.user.mfaMethods = ['email'];
} else {
req.user.mfaMethods = [];
}
await req.user.save();
user = req.user;
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to update current user's MFA status"
});
}
return res.status(200).send({
user
});
}
/**
* Return organizations that the current user is part of.
* @param req
* @param res
*/
export const getMyOrganizations = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return organizations that current user is part of'
#swagger.description = 'Return organizations that current user is part of'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"organizations": {
"type": "array",
"items": {
$ref: "#/components/schemas/Organization"
},
"description": "Organizations that user is part of"
}
}
}
}
}
}
*/
let organizations;
try {
organizations = (
await MembershipOrg.find({
user: req.user._id
}).populate('organization')
).map((m) => m.organization);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: "Failed to get current user's organizations"
});
}
return res.status(200).send({
organizations
});
}

View File

@ -0,0 +1,508 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import {
Workspace,
Secret,
Membership,
MembershipOrg,
Integration,
IntegrationAuth,
Key,
IUser,
ServiceToken,
ServiceTokenData
} from '../../models';
import {
v2PushSecrets as push,
pullSecrets as pull,
reformatPullSecrets
} from '../../helpers/secret';
import { pushKeys } from '../../helpers/key';
import { postHogClient, EventService } from '../../services';
import { eventPushSecrets } from '../../events';
interface V2PushSecret {
type: string; // personal or shared
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretKeyHash: string;
secretValueCiphertext: string;
secretValueIV: string;
secretValueTag: string;
secretValueHash: string;
secretCommentCiphertext?: string;
secretCommentIV?: string;
secretCommentTag?: string;
secretCommentHash?: string;
}
/**
* Upload (encrypted) secrets to workspace with id [workspaceId]
* for environment [environment]
* @param req
* @param res
* @returns
*/
export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
// upload (encrypted) secrets to workspace with id [workspaceId]
try {
let { secrets }: { secrets: V2PushSecret[] } = req.body;
const { keys, environment, channel } = req.body;
const { workspaceId } = req.params;
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
// sanitize secrets
secrets = secrets.filter(
(s: V2PushSecret) => s.secretKeyCiphertext !== '' && s.secretValueCiphertext !== ''
);
await push({
userId: req.user._id,
workspaceId,
environment,
secrets,
channel: channel ? channel : 'cli',
ipAddress: req.ip
});
await pushKeys({
userId: req.user._id,
workspaceId,
keys
});
if (postHogClient) {
postHogClient.capture({
event: 'secrets pushed',
distinctId: req.user.email,
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
});
}
// trigger event - push secrets
EventService.handleEvent({
event: eventPushSecrets({
workspaceId
})
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to upload workspace secrets'
});
}
return res.status(200).send({
message: 'Successfully uploaded workspace secrets'
});
};
/**
* Return (encrypted) secrets for workspace with id [workspaceId]
* for environment [environment]
* @param req
* @param res
* @returns
*/
export const pullSecrets = async (req: Request, res: Response) => {
let secrets;
try {
const environment: string = req.query.environment as string;
const channel: string = req.query.channel as string;
const { workspaceId } = req.params;
let userId;
if (req.user) {
userId = req.user._id.toString();
} else if (req.serviceTokenData) {
userId = req.serviceTokenData.user._id
}
// validate environment
const workspaceEnvs = req.membership.workspace.environments;
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
throw new Error('Failed to validate environment');
}
secrets = await pull({
userId,
workspaceId,
environment,
channel: channel ? channel : 'cli',
ipAddress: req.ip
});
if (channel !== 'cli') {
secrets = reformatPullSecrets({ secrets });
}
if (postHogClient) {
// capture secrets pushed event in production
postHogClient.capture({
distinctId: req.user.email,
event: 'secrets pulled',
properties: {
numberOfSecrets: secrets.length,
environment,
workspaceId,
channel: channel ? channel : 'cli'
}
});
}
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to pull workspace secrets'
});
}
return res.status(200).send({
secrets
});
};
export const getWorkspaceKey = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return encrypted project key'
#swagger.description = 'Return encrypted project key'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "array",
"items": {
$ref: "#/components/schemas/ProjectKey"
},
"description": "Encrypted project key for the given project"
}
}
}
}
*/
let key;
try {
const { workspaceId } = req.params;
key = await Key.findOne({
workspace: workspaceId,
receiver: req.user._id
}).populate('sender', '+publicKey');
if (!key) throw new Error('Failed to find workspace key');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace key'
});
}
return res.status(200).json(key);
}
export const getWorkspaceServiceTokenData = async (
req: Request,
res: Response
) => {
let serviceTokenData;
try {
const { workspaceId } = req.params;
serviceTokenData = await ServiceTokenData
.find({
workspace: workspaceId
})
.select('+encryptedKey +iv +tag');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace service token data'
});
}
return res.status(200).send({
serviceTokenData
});
}
/**
* Return memberships for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return project memberships'
#swagger.description = 'Return project memberships'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"memberships": {
"type": "array",
"items": {
$ref: "#/components/schemas/Membership"
},
"description": "Memberships of project"
}
}
}
}
}
}
*/
let memberships;
try {
const { workspaceId } = req.params;
memberships = await Membership.find({
workspace: workspaceId
}).populate('user', '+publicKey');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace memberships'
});
}
return res.status(200).send({
memberships
});
}
/**
* Update role of membership with id [membershipId] to role [role]
* @param req
* @param res
* @returns
*/
export const updateWorkspaceMembership = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Update project membership'
#swagger.description = 'Update project membership'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.parameters['membershipId'] = {
"description": "ID of project membership to update",
"required": true,
"type": "string"
}
#swagger.requestBody = {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"role": {
"type": "string",
"description": "Role of membership - either admin or member",
}
}
}
}
}
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"membership": {
$ref: "#/components/schemas/Membership",
"description": "Updated membership"
}
}
}
}
}
}
*/
let membership;
try {
const {
membershipId
} = req.params;
const { role } = req.body;
membership = await Membership.findByIdAndUpdate(
membershipId,
{
role
}, {
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to update workspace membership'
});
}
return res.status(200).send({
membership
});
}
/**
* Delete workspace membership with id [membershipId]
* @param req
* @param res
* @returns
*/
export const deleteWorkspaceMembership = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Delete project membership'
#swagger.description = 'Delete project membership'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.parameters['membershipId'] = {
"description": "ID of project membership to delete",
"required": true,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
"schema": {
"type": "object",
"properties": {
"membership": {
$ref: "#/components/schemas/Membership",
"description": "Deleted membership"
}
}
}
}
}
}
*/
let membership;
try {
const {
membershipId
} = req.params;
membership = await Membership.findByIdAndDelete(membershipId);
if (!membership) throw new Error('Failed to delete workspace membership');
await Key.deleteMany({
receiver: membership.user,
workspace: membership.workspace
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete workspace membership'
});
}
return res.status(200).send({
membership
});
}
/**
* Change autoCapitilzation Rule of workspace
* @param req
* @param res
* @returns
*/
export const toggleAutoCapitalization = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceId } = req.params;
const { autoCapitalization } = req.body;
workspace = await Workspace.findOneAndUpdate(
{
_id: workspaceId
},
{
autoCapitalization
},
{
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to change autoCapitalization setting'
});
}
return res.status(200).send({
message: 'Successfully changed autoCapitalization setting',
workspace
});
};

View File

@ -1,337 +0,0 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import {
Workspace,
Membership,
MembershipOrg,
Integration,
IntegrationAuth,
IUser,
ServiceToken
} from '../models';
import {
createWorkspace as create,
deleteWorkspace as deleteWork
} from '../helpers/workspace';
import { addMemberships } from '../helpers/membership';
import { ADMIN, COMPLETED, GRANTED } from '../variables';
/**
* Return public keys of members of workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspacePublicKeys = async (req: Request, res: Response) => {
let publicKeys;
try {
const { workspaceId } = req.params;
publicKeys = (
await Membership.find({
workspace: workspaceId
}).populate<{ user: IUser }>('user', 'publicKey')
)
.filter((m) => m.status === COMPLETED || m.status === GRANTED)
.map((member) => {
return {
publicKey: member.user.publicKey,
userId: member.user._id
};
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace member public keys'
});
}
return res.status(200).send({
publicKeys
});
};
/**
* Return memberships for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceMemberships = async (req: Request, res: Response) => {
let users;
try {
const { workspaceId } = req.params;
users = await Membership.find({
workspace: workspaceId
}).populate('user', '+publicKey');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace members'
});
}
return res.status(200).send({
users
});
};
/**
* Return workspaces that user is part of
* @param req
* @param res
* @returns
*/
export const getWorkspaces = async (req: Request, res: Response) => {
let workspaces;
try {
workspaces = (
await Membership.find({
user: req.user._id
}).populate('workspace')
).map((m) => m.workspace);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspaces'
});
}
return res.status(200).send({
workspaces
});
};
/**
* Return workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspace = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceId } = req.params;
workspace = await Workspace.findOne({
_id: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace'
});
}
return res.status(200).send({
workspace
});
};
/**
* Create new workspace named [workspaceName] under organization with id
* [organizationId] and add user as admin
* @param req
* @param res
* @returns
*/
export const createWorkspace = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceName, organizationId } = req.body;
// validate organization membership
const membershipOrg = await MembershipOrg.findOne({
user: req.user._id,
organization: organizationId
});
if (!membershipOrg) {
throw new Error('Failed to validate organization membership');
}
if (workspaceName.length < 1) {
throw new Error('Workspace names must be at least 1-character long');
}
// create workspace and add user as member
workspace = await create({
name: workspaceName,
organizationId
});
await addMemberships({
userIds: [req.user._id],
workspaceId: workspace._id.toString(),
roles: [ADMIN],
statuses: [GRANTED]
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to create workspace'
});
}
return res.status(200).send({
workspace
});
};
/**
* Delete workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const deleteWorkspace = async (req: Request, res: Response) => {
try {
const { workspaceId } = req.params;
// delete workspace
await deleteWork({
id: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to delete workspace'
});
}
return res.status(200).send({
message: 'Successfully deleted workspace'
});
};
/**
* Change name of workspace with id [workspaceId] to [name]
* @param req
* @param res
* @returns
*/
export const changeWorkspaceName = async (req: Request, res: Response) => {
let workspace;
try {
const { workspaceId } = req.params;
const { name } = req.body;
workspace = await Workspace.findOneAndUpdate(
{
_id: workspaceId
},
{
name
},
{
new: true
}
);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to change workspace name'
});
}
return res.status(200).send({
message: 'Successfully changed workspace name',
workspace
});
};
/**
* Return integrations for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceIntegrations = async (req: Request, res: Response) => {
let integrations;
try {
const { workspaceId } = req.params;
integrations = await Integration.find({
workspace: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace integrations'
});
}
return res.status(200).send({
integrations
});
};
/**
* Return (integration) authorizations for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceIntegrationAuthorizations = async (
req: Request,
res: Response
) => {
let authorizations;
try {
const { workspaceId } = req.params;
authorizations = await IntegrationAuth.find({
workspace: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace integration authorizations'
});
}
return res.status(200).send({
authorizations
});
};
/**
* Return service service tokens for workspace [workspaceId] belonging to user
* @param req
* @param res
* @returns
*/
export const getWorkspaceServiceTokens = async (
req: Request,
res: Response
) => {
let serviceTokens;
try {
const { workspaceId } = req.params;
serviceTokens = await ServiceToken.find({
user: req.user._id,
workspace: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace service tokens'
});
}
return res.status(200).send({
serviceTokens
});
}

View File

@ -1,5 +0,0 @@
import * as stripeController from './stripeController';
export {
stripeController
}

View File

@ -0,0 +1,31 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Action, SecretVersion } from '../../models';
import { ActionNotFoundError } from '../../../utils/errors';
export const getAction = async (req: Request, res: Response) => {
let action;
try {
const { actionId } = req.params;
action = await Action
.findById(actionId)
.populate([
'payload.secretVersions.oldSecretVersion',
'payload.secretVersions.newSecretVersion'
]);
if (!action) throw ActionNotFoundError({
message: 'Failed to find action'
});
} catch (err) {
throw ActionNotFoundError({
message: 'Failed to find action'
});
}
return res.status(200).send({
action
});
}

View File

@ -0,0 +1,15 @@
import * as stripeController from './stripeController';
import * as secretController from './secretController';
import * as secretSnapshotController from './secretSnapshotController';
import * as workspaceController from './workspaceController';
import * as actionController from './actionController';
import * as membershipController from './membershipController';
export {
stripeController,
secretController,
secretSnapshotController,
workspaceController,
actionController,
membershipController
}

View File

@ -0,0 +1,63 @@
import { Request, Response } from "express";
import { Membership, Workspace } from "../../../models";
import { IMembershipPermission } from "../../../models/membership";
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
import { ABILITY_READ, ABILITY_WRITE, ADMIN, MEMBER } from "../../../variables/organization";
import { Builder } from "builder-pattern"
import _ from "lodash";
export const denyMembershipPermissions = async (req: Request, res: Response) => {
const { membershipId } = req.params;
const { permissions } = req.body;
const sanitizedMembershipPermissions: IMembershipPermission[] = permissions.map((permission: IMembershipPermission) => {
if (!permission.ability || !permission.environmentSlug || ![ABILITY_READ, ABILITY_WRITE].includes(permission.ability)) {
throw BadRequestError({ message: "One or more required fields are missing from the request or have incorrect type" })
}
return Builder<IMembershipPermission>()
.environmentSlug(permission.environmentSlug)
.ability(permission.ability)
.build();
})
const sanitizedMembershipPermissionsUnique = _.uniqWith(sanitizedMembershipPermissions, _.isEqual)
const membershipToModify = await Membership.findById(membershipId)
if (!membershipToModify) {
throw BadRequestError({ message: "Unable to locate resource" })
}
// check if the user making the request is a admin of this project
if (![ADMIN, MEMBER].includes(membershipToModify.role)) {
throw UnauthorizedRequestError()
}
// check if the requested slugs are indeed a part of this related workspace
const relatedWorkspace = await Workspace.findById(membershipToModify.workspace)
if (!relatedWorkspace) {
throw BadRequestError({ message: "Something went wrong when locating the related workspace" })
}
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, 'slug')));
sanitizedMembershipPermissionsUnique.forEach(permission => {
if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) {
throw BadRequestError({ message: "Unknown environment slug reference" })
}
})
// update the permissions
const updatedMembershipWithPermissions = await Membership.findByIdAndUpdate(
{ _id: membershipToModify._id },
{ $set: { deniedPermissions: sanitizedMembershipPermissionsUnique } },
{ new: true }
)
if (!updatedMembershipWithPermissions) {
throw BadRequestError({ message: "The resource has been removed before it can be modified" })
}
res.send({
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions
})
}

View File

@ -0,0 +1,224 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Secret } from '../../../models';
import { SecretVersion } from '../../models';
import { EESecretService } from '../../services';
/**
* Return secret versions for secret with id [secretId]
* @param req
* @param res
*/
export const getSecretVersions = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return secret versions'
#swagger.description = 'Return secret versions'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['secretId'] = {
"description": "ID of secret",
"required": true,
"type": "string"
}
#swagger.parameters['offset'] = {
"description": "Number of versions to skip",
"required": false,
"type": "string"
}
#swagger.parameters['limit'] = {
"description": "Maximum number of versions to return",
"required": false,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
"type": "object",
"properties": {
"secretVersions": {
"type": "array",
"items": {
$ref: "#/components/schemas/SecretVersion"
},
"description": "Secret versions"
}
}
}
}
}
}
*/
let secretVersions;
try {
const { secretId } = req.params;
const offset: number = parseInt(req.query.offset as string);
const limit: number = parseInt(req.query.limit as string);
secretVersions = await SecretVersion.find({
secret: secretId
})
.sort({ createdAt: -1 })
.skip(offset)
.limit(limit);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get secret versions'
});
}
return res.status(200).send({
secretVersions
});
}
/**
* Roll back secret with id [secretId] to version [version]
* @param req
* @param res
* @returns
*/
export const rollbackSecretVersion = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Roll back secret to a version.'
#swagger.description = 'Roll back secret to a version.'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['secretId'] = {
"description": "ID of secret",
"required": true,
"type": "string"
}
#swagger.requestBody = {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"description": "Version of secret to roll back to"
}
}
}
}
}
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
"type": "object",
"properties": {
"secret": {
"type": "object",
$ref: "#/components/schemas/Secret",
"description": "Secret rolled back to"
}
}
}
}
}
}
*/
let secret;
try {
const { secretId } = req.params;
const { version } = req.body;
// validate secret version
const oldSecretVersion = await SecretVersion.findOne({
secret: secretId,
version
});
if (!oldSecretVersion) throw new Error('Failed to find secret version');
const {
workspace,
type,
user,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
} = oldSecretVersion;
// update secret
secret = await Secret.findByIdAndUpdate(
secretId,
{
$inc: {
version: 1
},
workspace,
type,
user,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag,
},
{
new: true
}
);
if (!secret) throw new Error('Failed to find and update secret');
// add new secret version
await new SecretVersion({
secret: secretId,
version: secret.version,
workspace,
type,
user,
environment,
isDeleted: false,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValueCiphertext,
secretValueIV,
secretValueTag
}).save();
// take secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId: secret.workspace.toString()
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to roll back secret version'
});
}
return res.status(200).send({
secret
});
}

View File

@ -0,0 +1,39 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { SecretSnapshot } from '../../models';
/**
* Return secret snapshot with id [secretSnapshotId]
* @param req
* @param res
* @returns
*/
export const getSecretSnapshot = async (req: Request, res: Response) => {
let secretSnapshot;
try {
const { secretSnapshotId } = req.params;
secretSnapshot = await SecretSnapshot
.findById(secretSnapshotId)
.populate({
path: 'secretVersions',
populate: {
path: 'tags',
model: 'Tag',
}
});
if (!secretSnapshot) throw new Error('Failed to find secret snapshot');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get secret snapshot'
});
}
return res.status(200).send({
secretSnapshot
});
}

View File

@ -1,7 +1,7 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import Stripe from 'stripe';
import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../config';
import { STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET } from '../../../config';
const stripe = new Stripe(STRIPE_SECRET_KEY, {
apiVersion: '2022-08-01'
});

View File

@ -0,0 +1,434 @@
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import {
Secret
} from '../../../models';
import {
SecretSnapshot,
Log,
SecretVersion,
ISecretVersion
} from '../../models';
import { EESecretService } from '../../services';
import { getLatestSecretVersionIds } from '../../helpers/secretVersion';
/**
* Return secret snapshots for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceSecretSnapshots = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return project secret snapshot ids'
#swagger.description = 'Return project secret snapshots ids'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.parameters['offset'] = {
"description": "Number of secret snapshots to skip",
"required": false,
"type": "string"
}
#swagger.parameters['limit'] = {
"description": "Maximum number of secret snapshots to return",
"required": false,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
"type": "object",
"properties": {
"secretSnapshots": {
"type": "array",
"items": {
$ref: "#/components/schemas/SecretSnapshot"
},
"description": "Project secret snapshots"
}
}
}
}
}
}
*/
let secretSnapshots;
try {
const { workspaceId } = req.params;
const offset: number = parseInt(req.query.offset as string);
const limit: number = parseInt(req.query.limit as string);
secretSnapshots = await SecretSnapshot.find({
workspace: workspaceId
})
.sort({ createdAt: -1 })
.skip(offset)
.limit(limit);
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get secret snapshots'
});
}
return res.status(200).send({
secretSnapshots
});
}
/**
* Return count of secret snapshots for workspace with id [workspaceId]
* @param req
* @param res
*/
export const getWorkspaceSecretSnapshotsCount = async (req: Request, res: Response) => {
let count;
try {
const { workspaceId } = req.params;
count = await SecretSnapshot.countDocuments({
workspace: workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to count number of secret snapshots'
});
}
return res.status(200).send({
count
});
}
/**
* Rollback secret snapshot with id [secretSnapshotId] to version [version]
* @param req
* @param res
* @returns
*/
export const rollbackWorkspaceSecretSnapshot = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Roll back project secrets to those captured in a secret snapshot version.'
#swagger.description = 'Roll back project secrets to those captured in a secret snapshot version.'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.requestBody = {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"version": {
"type": "integer",
"description": "Version of secret snapshot to roll back to",
}
}
}
}
}
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
"type": "object",
"properties": {
"secrets": {
"type": "array",
"items": {
$ref: "#/components/schemas/Secret"
},
"description": "Secrets rolled back to"
}
}
}
}
}
}
*/
let secrets;
try {
const { workspaceId } = req.params;
const { version } = req.body;
// validate secret snapshot
const secretSnapshot = await SecretSnapshot.findOne({
workspace: workspaceId,
version
}).populate<{ secretVersions: ISecretVersion[]}>('secretVersions');
if (!secretSnapshot) throw new Error('Failed to find secret snapshot');
// TODO: fix any
const oldSecretVersionsObj: any = secretSnapshot.secretVersions
.reduce((accumulator, s) => ({
...accumulator,
[`${s.secret.toString()}`]: s
}), {});
const latestSecretVersionIds = await getLatestSecretVersionIds({
secretIds: secretSnapshot.secretVersions.map((sv) => sv.secret)
});
// TODO: fix any
const latestSecretVersions: any = (await SecretVersion.find({
_id: {
$in: latestSecretVersionIds.map((s) => s.versionId)
}
}, 'secret version'))
.reduce((accumulator, s) => ({
...accumulator,
[`${s.secret.toString()}`]: s
}), {});
// delete existing secrets
await Secret.deleteMany({
workspace: workspaceId
});
// add secrets
secrets = await Secret.insertMany(
secretSnapshot.secretVersions.map((sv) => {
const secretId = sv.secret;
const {
workspace,
type,
user,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretKeyHash,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretValueHash,
createdAt
} = oldSecretVersionsObj[secretId.toString()];
return ({
_id: secretId,
version: latestSecretVersions[secretId.toString()].version + 1,
workspace,
type,
user,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretKeyHash,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretValueHash,
secretCommentCiphertext: '',
secretCommentIV: '',
secretCommentTag: '',
createdAt
});
})
);
// add secret versions
await SecretVersion.insertMany(
secrets.map(({
_id,
version,
workspace,
type,
user,
environment,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretKeyHash,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretValueHash
}) => ({
_id: new Types.ObjectId(),
secret: _id,
version,
workspace,
type,
user,
environment,
isDeleted: false,
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretKeyHash,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretValueHash
}))
);
// update secret versions of restored secrets as not deleted
await SecretVersion.updateMany({
secret: {
$in: secretSnapshot.secretVersions.map((sv) => sv.secret)
}
}, {
isDeleted: false
});
// take secret snapshot
await EESecretService.takeSecretSnapshot({
workspaceId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to roll back secret snapshot'
});
}
return res.status(200).send({
secrets
});
}
/**
* Return (audit) logs for workspace with id [workspaceId]
* @param req
* @param res
* @returns
*/
export const getWorkspaceLogs = async (req: Request, res: Response) => {
/*
#swagger.summary = 'Return project (audit) logs'
#swagger.description = 'Return project (audit) logs'
#swagger.security = [{
"apiKeyAuth": []
}]
#swagger.parameters['workspaceId'] = {
"description": "ID of project",
"required": true,
"type": "string"
}
#swagger.parameters['userId'] = {
"description": "ID of project member",
"required": false,
"type": "string"
}
#swagger.parameters['offset'] = {
"description": "Number of logs to skip",
"required": false,
"type": "string"
}
#swagger.parameters['limit'] = {
"description": "Maximum number of logs to return",
"required": false,
"type": "string"
}
#swagger.parameters['sortBy'] = {
"description": "Order to sort the logs by",
"schema": {
"type": "string",
"@enum": ["oldest", "recent"]
},
"required": false
}
#swagger.parameters['actionNames'] = {
"description": "Names of log actions (comma-separated)",
"required": false,
"type": "string"
}
#swagger.responses[200] = {
content: {
"application/json": {
schema: {
"type": "object",
"properties": {
"logs": {
"type": "array",
"items": {
$ref: "#/components/schemas/Log"
},
"description": "Project logs"
}
}
}
}
}
}
*/
let logs
try {
const { workspaceId } = req.params;
const offset: number = parseInt(req.query.offset as string);
const limit: number = parseInt(req.query.limit as string);
const sortBy: string = req.query.sortBy as string;
const userId: string = req.query.userId as string;
const actionNames: string = req.query.actionNames as string;
logs = await Log.find({
workspace: workspaceId,
...( userId ? { user: userId } : {}),
...(
actionNames
? {
actionNames: {
$in: actionNames.split(',')
}
} : {}
)
})
.sort({ createdAt: sortBy === 'recent' ? -1 : 1 })
.skip(offset)
.limit(limit)
.populate('actions')
.populate('user');
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
return res.status(400).send({
message: 'Failed to get workspace logs'
});
}
return res.status(200).send({
logs
});
}

View File

@ -0,0 +1,202 @@
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { Action } from '../models';
import {
getLatestSecretVersionIds,
getLatestNSecretSecretVersionIds
} from '../helpers/secretVersion';
import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS,
ACTION_UPDATE_SECRETS,
} from '../../variables';
/**
* Create an (audit) action for updating secrets
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
* @returns {Action} action - new action
*/
const createActionUpdateSecret = async ({
name,
userId,
workspaceId,
secretIds
}: {
name: string;
userId: Types.ObjectId;
workspaceId: Types.ObjectId;
secretIds: Types.ObjectId[];
}) => {
let action;
try {
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
secretIds,
n: 2
}))
.map((s) => ({
oldSecretVersion: s.versions[0]._id,
newSecretVersion: s.versions[1]._id
}));
action = await new Action({
name,
user: userId,
workspace: workspaceId,
payload: {
secretVersions: latestSecretVersions
}
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create update secret action');
}
return action;
}
/**
* Create an (audit) action for creating, reading, and deleting
* secrets
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {Types.ObjectId} obj.secretIds - ids of relevant secrets
* @returns {Action} action - new action
*/
const createActionSecret = async ({
name,
userId,
workspaceId,
secretIds
}: {
name: string;
userId: Types.ObjectId;
workspaceId: Types.ObjectId;
secretIds: Types.ObjectId[];
}) => {
let action;
try {
// case: action is adding, deleting, or reading secrets
// -> add new secret versions
const latestSecretVersions = (await getLatestSecretVersionIds({
secretIds
}))
.map((s) => ({
newSecretVersion: s.versionId
}));
action = await new Action({
name,
user: userId,
workspace: workspaceId,
payload: {
secretVersions: latestSecretVersions
}
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create action create/read/delete secret action');
}
return action;
}
/**
* Create an (audit) action for user with id [userId]
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {String} obj.userId - id of user associated with action
* @returns
*/
const createActionUser = ({
name,
userId
}: {
name: string;
userId: Types.ObjectId;
}) => {
let action;
try {
action = new Action({
name,
user: userId
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create user action');
}
return action;
}
/**
* Create an (audit) action.
* @param {Object} obj
* @param {Object} obj.name - name of action
* @param {Types.ObjectId} obj.userId - id of user associated with action
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with action
* @param {Types.ObjectId[]} obj.secretIds - ids of secrets associated with action
*/
const createActionHelper = async ({
name,
userId,
workspaceId,
secretIds,
}: {
name: string;
userId: Types.ObjectId;
workspaceId?: Types.ObjectId;
secretIds?: Types.ObjectId[];
}) => {
let action;
try {
switch (name) {
case ACTION_LOGIN:
case ACTION_LOGOUT:
action = await createActionUser({
name,
userId
});
break;
case ACTION_ADD_SECRETS:
case ACTION_READ_SECRETS:
case ACTION_DELETE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
action = await createActionSecret({
name,
userId,
workspaceId,
secretIds
});
break;
case ACTION_UPDATE_SECRETS:
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
action = await createActionUpdateSecret({
name,
userId,
workspaceId,
secretIds
});
break;
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create action');
}
return action;
}
export {
createActionHelper
};

View File

@ -0,0 +1,54 @@
import _ from "lodash";
import { Membership } from "../../models";
import { ABILITY_READ, ABILITY_WRITE } from "../../variables/organization";
export const userHasWorkspaceAccess = async (userId: any, workspaceId: any, environment: any, action: any) => {
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
if (!membershipForWorkspace) {
return false
}
const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions;
const isDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: action });
if (isDisallowed) {
return false
}
return true
}
export const userHasWriteOnlyAbility = async (userId: any, workspaceId: any, environment: any) => {
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
if (!membershipForWorkspace) {
return false
}
const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions;
const isWriteDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: ABILITY_WRITE });
const isReadDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: ABILITY_READ });
// case: you have write only if read is blocked and write is not
if (isReadDisallowed && !isWriteDisallowed) {
return true
}
return false
}
export const userHasNoAbility = async (userId: any, workspaceId: any, environment: any) => {
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
if (!membershipForWorkspace) {
return true
}
const deniedMembershipPermissions = membershipForWorkspace.deniedPermissions;
const isWriteDisallowed = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: ABILITY_WRITE });
const isReadBlocked = _.some(deniedMembershipPermissions, { environmentSlug: environment, ability: ABILITY_READ });
if (isReadBlocked && isWriteDisallowed) {
return true
}
return false
}

View File

@ -1,21 +0,0 @@
/**
* @param {Object} obj
* @param {Object} obj.licenseKey - Infisical license key
*/
const checkLicenseKey = ({
licenseKey
}: {
licenseKey: string
}) => {
try {
// TODO
} catch (err) {
}
}
export {
checkLicenseKey
}

View File

@ -0,0 +1,51 @@
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import {
Log,
IAction
} from '../models';
/**
* Create an (audit) log
* @param {Object} obj
* @param {Types.ObjectId} obj.userId - id of user associated with the log
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the log
* @param {IAction[]} obj.actions - actions to include in log
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
* @param {String} obj.ipAddress - ip address associated with the log
* @returns {Log} log - new audit log
*/
const createLogHelper = async ({
userId,
workspaceId,
actions,
channel,
ipAddress
}: {
userId: Types.ObjectId;
workspaceId?: Types.ObjectId;
actions: IAction[];
channel: string;
ipAddress: string;
}) => {
let log;
try {
log = await new Log({
user: userId,
workspace: workspaceId ?? undefined,
actionNames: actions.map((a) => a.name),
actions,
channel,
ipAddress
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to create log');
}
return log;
}
export {
createLogHelper
}

View File

@ -0,0 +1,169 @@
import { Types } from 'mongoose';
import * as Sentry from '@sentry/node';
import {
Secret,
ISecret
} from '../../models';
import {
SecretSnapshot,
SecretVersion,
ISecretVersion
} from '../models';
/**
* Save a secret snapshot that is a copy of the current state of secrets in workspace with id
* [workspaceId] under a new snapshot with incremented version under the
* secretsnapshots collection.
* @param {Object} obj
* @param {String} obj.workspaceId
* @returns {SecretSnapshot} secretSnapshot - new secret snapshot
*/
const takeSecretSnapshotHelper = async ({
workspaceId
}: {
workspaceId: string;
}) => {
let secretSnapshot;
try {
const secretIds = (await Secret.find({
workspace: workspaceId
}, '_id')).map((s) => s._id);
const latestSecretVersions = (await SecretVersion.aggregate([
{
$match: {
secret: {
$in: secretIds
}
}
},
{
$group: {
_id: '$secret',
version: { $max: '$version' },
versionId: { $max: '$_id' } // secret version id
}
},
{
$sort: { version: -1 }
}
])
.exec())
.map((s) => s.versionId);
const latestSecretSnapshot = await SecretSnapshot.findOne({
workspace: workspaceId
}).sort({ version: -1 });
secretSnapshot = await new SecretSnapshot({
workspace: workspaceId,
version: latestSecretSnapshot ? latestSecretSnapshot.version + 1 : 1,
secretVersions: latestSecretVersions
}).save();
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to take a secret snapshot');
}
return secretSnapshot;
}
/**
* Add secret versions [secretVersions] to the SecretVersion collection.
* @param {Object} obj
* @param {Object[]} obj.secretVersions
* @returns {SecretVersion[]} newSecretVersions - new secret versions
*/
const addSecretVersionsHelper = async ({
secretVersions
}: {
secretVersions: ISecretVersion[]
}) => {
let newSecretVersions;
try {
newSecretVersions = await SecretVersion.insertMany(secretVersions);
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error(`Failed to add secret versions [err=${err}]`);
}
return newSecretVersions;
}
const markDeletedSecretVersionsHelper = async ({
secretIds
}: {
secretIds: Types.ObjectId[];
}) => {
try {
await SecretVersion.updateMany({
secret: { $in: secretIds }
}, {
isDeleted: true
}, {
new: true
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to mark secret versions as deleted');
}
}
/**
* Initialize secret versioning by setting previously unversioned
* secrets to version 1 and begin populating secret versions.
*/
const initSecretVersioningHelper = async () => {
try {
await Secret.updateMany(
{ version: { $exists: false } },
{ $set: { version: 1 } }
);
const unversionedSecrets: ISecret[] = await Secret.aggregate([
{
$lookup: {
from: 'secretversions',
localField: '_id',
foreignField: 'secret',
as: 'versions',
},
},
{
$match: {
versions: { $size: 0 },
},
},
]);
if (unversionedSecrets.length > 0) {
await addSecretVersionsHelper({
secretVersions: unversionedSecrets.map((s, idx) => ({
...s,
secret: s._id,
version: s.version ? s.version : 1,
isDeleted: false,
workspace: s.workspace,
environment: s.environment
}))
});
}
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to ensure that secrets are versioned');
}
}
export {
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
initSecretVersioningHelper
}

View File

@ -0,0 +1,110 @@
import * as Sentry from '@sentry/node';
import { Types } from 'mongoose';
import { SecretVersion } from '../models';
/**
* Return latest secret versions for secrets with ids [secretIds]
* @param {Object} obj
* @param {Object} obj.secretIds = ids of secrets to get latest versions for
* @returns
*/
const getLatestSecretVersionIds = async ({
secretIds
}: {
secretIds: Types.ObjectId[];
}) => {
interface LatestSecretVersionId {
_id: Types.ObjectId;
version: number;
versionId: Types.ObjectId;
}
let latestSecretVersionIds: LatestSecretVersionId[];
try {
latestSecretVersionIds = (await SecretVersion.aggregate([
{
$match: {
secret: {
$in: secretIds
}
}
},
{
$group: {
_id: '$secret',
version: { $max: '$version' },
versionId: { $max: '$_id' } // id of latest secret version
}
},
{
$sort: { version: -1 }
}
])
.exec());
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get latest secret versions');
}
return latestSecretVersionIds;
}
/**
* Return latest [n] secret versions for secrets with ids [secretIds]
* @param {Object} obj
* @param {Object} obj.secretIds = ids of secrets to get latest versions for
* @param {Number} obj.n - number of latest secret versions to return for each secret
* @returns
*/
const getLatestNSecretSecretVersionIds = async ({
secretIds,
n
}: {
secretIds: Types.ObjectId[];
n: number;
}) => {
// TODO: optimize query
let latestNSecretVersions;
try {
latestNSecretVersions = (await SecretVersion.aggregate([
{
$match: {
secret: {
$in: secretIds,
},
},
},
{
$sort: { version: -1 },
},
{
$group: {
_id: "$secret",
versions: { $push: "$$ROOT" },
},
},
{
$project: {
_id: 0,
secret: "$_id",
versions: { $slice: ["$versions", n] },
},
}
]));
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
throw new Error('Failed to get latest n secret versions');
}
return latestNSecretVersions;
}
export {
getLatestSecretVersionIds,
getLatestNSecretSecretVersionIds
}

View File

@ -0,0 +1,7 @@
import requireLicenseAuth from './requireLicenseAuth';
import requireSecretSnapshotAuth from './requireSecretSnapshotAuth';
export {
requireLicenseAuth,
requireSecretSnapshotAuth
}

View File

@ -0,0 +1,47 @@
import { Request, Response, NextFunction } from 'express';
import { UnauthorizedRequestError, SecretSnapshotNotFoundError } from '../../utils/errors';
import { SecretSnapshot } from '../models';
import {
validateMembership
} from '../../helpers/membership';
/**
* Validate if user on request has proper membership for secret snapshot
* @param {Object} obj
* @param {String[]} obj.acceptedRoles - accepted workspace roles
* @param {String[]} obj.acceptedStatuses - accepted workspace statuses
* @param {String[]} obj.location - location of [workspaceId] on request (e.g. params, body) for parsing
*/
const requireSecretSnapshotAuth = ({
acceptedRoles,
}: {
acceptedRoles: string[];
}) => {
return async (req: Request, res: Response, next: NextFunction) => {
try {
const { secretSnapshotId } = req.params;
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId);
if (!secretSnapshot) {
return next(SecretSnapshotNotFoundError({
message: 'Failed to find secret snapshot'
}));
}
await validateMembership({
userId: req.user._id.toString(),
workspaceId: secretSnapshot.workspace.toString(),
acceptedRoles
});
req.secretSnapshot = secretSnapshot as any;
next();
} catch (err) {
return next(UnauthorizedRequestError({ message: 'Unable to authenticate secret snapshot' }));
}
}
}
export default requireSecretSnapshotAuth;

View File

@ -0,0 +1,62 @@
import { Schema, model, Types } from 'mongoose';
import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
export interface IAction {
name: string;
user?: Types.ObjectId,
workspace?: Types.ObjectId,
payload?: {
secretVersions?: Types.ObjectId[]
}
}
const actionSchema = new Schema<IAction>(
{
name: {
type: String,
required: true,
enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
]
},
user: {
type: Schema.Types.ObjectId,
ref: 'User',
required: true
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace'
},
payload: {
secretVersions: [{
oldSecretVersion: {
type: Schema.Types.ObjectId,
ref: 'SecretVersion'
},
newSecretVersion: {
type: Schema.Types.ObjectId,
ref: 'SecretVersion'
}
}]
}
}, {
timestamps: true
}
);
const Action = model<IAction>('Action', actionSchema);
export default Action;

View File

@ -0,0 +1,15 @@
import SecretSnapshot, { ISecretSnapshot } from './secretSnapshot';
import SecretVersion, { ISecretVersion } from './secretVersion';
import Log, { ILog } from './log';
import Action, { IAction } from './action';
export {
SecretSnapshot,
ISecretSnapshot,
SecretVersion,
ISecretVersion,
Log,
ILog,
Action,
IAction
}

View File

@ -0,0 +1,63 @@
import { Schema, model, Types } from 'mongoose';
import {
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
} from '../../variables';
export interface ILog {
_id: Types.ObjectId;
user?: Types.ObjectId;
workspace?: Types.ObjectId;
actionNames: string[];
actions: Types.ObjectId[];
channel: string;
ipAddress?: string;
}
const logSchema = new Schema<ILog>(
{
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace'
},
actionNames: {
type: [String],
enum: [
ACTION_LOGIN,
ACTION_LOGOUT,
ACTION_ADD_SECRETS,
ACTION_UPDATE_SECRETS,
ACTION_READ_SECRETS,
ACTION_DELETE_SECRETS
],
required: true
},
actions: [{
type: Schema.Types.ObjectId,
ref: 'Action',
required: true
}],
channel: {
type: String,
enum: ['web', 'cli', 'auto', 'k8-operator', 'other'],
required: true
},
ipAddress: {
type: String
}
}, {
timestamps: true
}
);
const Log = model<ILog>('Log', logSchema);
export default Log;

View File

@ -0,0 +1,33 @@
import { Schema, model, Types } from 'mongoose';
export interface ISecretSnapshot {
workspace: Types.ObjectId;
version: number;
secretVersions: Types.ObjectId[];
}
const secretSnapshotSchema = new Schema<ISecretSnapshot>(
{
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace',
required: true
},
version: {
type: Number,
required: true
},
secretVersions: [{
type: Schema.Types.ObjectId,
ref: 'SecretVersion',
required: true
}]
},
{
timestamps: true
}
);
const SecretSnapshot = model<ISecretSnapshot>('SecretSnapshot', secretSnapshotSchema);
export default SecretSnapshot;

View File

@ -0,0 +1,97 @@
import { Schema, model, Types } from 'mongoose';
import {
SECRET_SHARED,
SECRET_PERSONAL,
} from '../../variables';
export interface ISecretVersion {
secret: Types.ObjectId;
version: number;
workspace: Types.ObjectId; // new
type: string; // new
user?: Types.ObjectId; // new
environment: string; // new
isDeleted: boolean;
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretValueCiphertext: string;
secretValueIV: string;
secretValueTag: string;
tags?: string[];
}
const secretVersionSchema = new Schema<ISecretVersion>(
{
secret: { // could be deleted
type: Schema.Types.ObjectId,
ref: 'Secret',
required: true
},
version: {
type: Number,
default: 1,
required: true
},
workspace: {
type: Schema.Types.ObjectId,
ref: 'Workspace',
required: true
},
type: {
type: String,
enum: [SECRET_SHARED, SECRET_PERSONAL],
required: true
},
user: {
// user associated with the personal secret
type: Schema.Types.ObjectId,
ref: 'User'
},
environment: {
type: String,
required: true
},
isDeleted: { // consider removing field
type: Boolean,
default: false,
required: true
},
secretKeyCiphertext: {
type: String,
required: true
},
secretKeyIV: {
type: String, // symmetric
required: true
},
secretKeyTag: {
type: String, // symmetric
required: true
},
secretValueCiphertext: {
type: String,
required: true
},
secretValueIV: {
type: String, // symmetric
required: true
},
secretValueTag: {
type: String, // symmetric
required: true
},
tags: {
ref: 'Tag',
type: [Schema.Types.ObjectId],
default: []
},
},
{
timestamps: true
}
);
const SecretVersion = model<ISecretVersion>('SecretVersion', secretVersionSchema);
export default SecretVersion;

View File

@ -0,0 +1,17 @@
import express from 'express';
const router = express.Router();
import {
validateRequest
} from '../../../middleware';
import { param } from 'express-validator';
import { actionController } from '../../controllers/v1';
// TODO: put into action controller
router.get(
'/:actionId',
param('actionId').exists().trim(),
validateRequest,
actionController.getAction
);
export default router;

View File

@ -0,0 +1,11 @@
import secret from './secret';
import secretSnapshot from './secretSnapshot';
import workspace from './workspace';
import action from './action';
export {
secret,
secretSnapshot,
workspace,
action
}

View File

@ -0,0 +1,40 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
requireSecretAuth,
validateRequest
} from '../../../middleware';
import { query, param, body } from 'express-validator';
import { secretController } from '../../controllers/v1';
import { ADMIN, MEMBER } from '../../../variables';
router.get(
'/:secretId/secret-versions',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('secretId').exists().trim(),
query('offset').exists().isInt(),
query('limit').exists().isInt(),
validateRequest,
secretController.getSecretVersions
);
router.post(
'/:secretId/secret-versions/rollback',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireSecretAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('secretId').exists().trim(),
body('version').exists().isInt(),
secretController.rollbackSecretVersion
);
export default router;

View File

@ -0,0 +1,27 @@
import express from 'express';
const router = express.Router();
import {
requireSecretSnapshotAuth
} from '../../middleware';
import {
requireAuth,
validateRequest
} from '../../../middleware';
import { param, body } from 'express-validator';
import { ADMIN, MEMBER } from '../../../variables';
import { secretSnapshotController } from '../../controllers/v1';
router.get(
'/:secretSnapshotId',
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireSecretSnapshotAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('secretSnapshotId').exists().trim(),
validateRequest,
secretSnapshotController.getSecretSnapshot
);
export default router;

View File

@ -1,6 +1,6 @@
import express from 'express';
const router = express.Router();
import { stripeController } from '../controllers';
import { stripeController } from '../../controllers/v1';
router.post('/webhook', stripeController.handleWebhook);

View File

@ -0,0 +1,72 @@
import express from 'express';
const router = express.Router();
import {
requireAuth,
requireWorkspaceAuth,
validateRequest
} from '../../../middleware';
import { param, query, body } from 'express-validator';
import { ADMIN, MEMBER } from '../../../variables';
import { workspaceController } from '../../controllers/v1';
router.get(
'/:workspaceId/secret-snapshots',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('workspaceId').exists().trim(),
query('offset').exists().isInt(),
query('limit').exists().isInt(),
validateRequest,
workspaceController.getWorkspaceSecretSnapshots
);
router.get(
'/:workspaceId/secret-snapshots/count',
requireAuth({
acceptedAuthModes: ['jwt']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('workspaceId').exists().trim(),
validateRequest,
workspaceController.getWorkspaceSecretSnapshotsCount
);
router.post(
'/:workspaceId/secret-snapshots/rollback',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('workspaceId').exists().trim(),
body('version').exists().isInt(),
validateRequest,
workspaceController.rollbackWorkspaceSecretSnapshot
);
router.get(
'/:workspaceId/logs',
requireAuth({
acceptedAuthModes: ['jwt', 'apiKey']
}),
requireWorkspaceAuth({
acceptedRoles: [ADMIN, MEMBER]
}),
param('workspaceId').exists().trim(),
query('offset').exists().isInt(),
query('limit').exists().isInt(),
query('sortBy'),
query('userId'),
query('actionNames'),
validateRequest,
workspaceController.getWorkspaceLogs
);
export default router;

View File

@ -0,0 +1,19 @@
import { LICENSE_KEY } from '../../config';
/**
* Class to handle Enterprise Edition license actions
*/
class EELicenseService {
private readonly _isLicenseValid: boolean;
constructor(licenseKey: string) {
this._isLicenseValid = true;
}
public get isLicenseValid(): boolean {
return this._isLicenseValid;
}
}
export default new EELicenseService(LICENSE_KEY);

View File

@ -0,0 +1,79 @@
import { Types } from 'mongoose';
import {
IAction
} from '../models';
import {
createLogHelper
} from '../helpers/log';
import {
createActionHelper
} from '../helpers/action';
import EELicenseService from './EELicenseService';
/**
* Class to handle Enterprise Edition log actions
*/
class EELogService {
/**
* Create an (audit) log
* @param {Object} obj
* @param {String} obj.userId - id of user associated with the log
* @param {String} obj.workspaceId - id of workspace associated with the log
* @param {Action} obj.actions - actions to include in log
* @param {String} obj.channel - channel (web/cli/auto) associated with the log
* @param {String} obj.ipAddress - ip address associated with the log
* @returns {Log} log - new audit log
*/
static async createLog({
userId,
workspaceId,
actions,
channel,
ipAddress
}: {
userId: Types.ObjectId;
workspaceId?: Types.ObjectId;
actions: IAction[];
channel: string;
ipAddress: string;
}) {
if (!EELicenseService.isLicenseValid) return null;
return await createLogHelper({
userId,
workspaceId,
actions,
channel,
ipAddress
})
}
/**
* Create an (audit) action
* @param {Object} obj
* @param {String} obj.name - name of action
* @param {Types.ObjectId} obj.userId - id of user associated with the action
* @param {Types.ObjectId} obj.workspaceId - id of workspace associated with the action
* @param {ObjectId[]} obj.secretIds - ids of secrets associated with the action
* @returns {Action} action - new action
*/
static async createAction({
name,
userId,
workspaceId,
secretIds
}: {
name: string;
userId: Types.ObjectId;
workspaceId?: Types.ObjectId;
secretIds?: Types.ObjectId[];
}) {
return await createActionHelper({
name,
userId,
workspaceId,
secretIds
});
}
}
export default EELogService;

View File

@ -0,0 +1,78 @@
import { Types } from 'mongoose';
import { ISecretVersion } from '../models';
import {
takeSecretSnapshotHelper,
addSecretVersionsHelper,
markDeletedSecretVersionsHelper,
initSecretVersioningHelper
} from '../helpers/secret';
import EELicenseService from './EELicenseService';
/**
* Class to handle Enterprise Edition secret actions
*/
class EESecretService {
/**
* Save a secret snapshot that is a copy of the current state of secrets in workspace with id
* [workspaceId] under a new snapshot with incremented version under the
* SecretSnapshot collection.
* Requires a valid license key [licenseKey]
* @param {Object} obj
* @param {String} obj.workspaceId
* @returns {SecretSnapshot} secretSnapshot - new secret snpashot
*/
static async takeSecretSnapshot({
workspaceId
}: {
workspaceId: string;
}) {
if (!EELicenseService.isLicenseValid) return;
return await takeSecretSnapshotHelper({ workspaceId });
}
/**
* Add secret versions [secretVersions] to the SecretVersion collection.
* @param {Object} obj
* @param {Object[]} obj.secretVersions
* @returns {SecretVersion[]} newSecretVersions - new secret versions
*/
static async addSecretVersions({
secretVersions
}: {
secretVersions: ISecretVersion[];
}) {
if (!EELicenseService.isLicenseValid) return;
return await addSecretVersionsHelper({
secretVersions
});
}
/**
* Mark secret versions associated with secrets with ids [secretIds]
* as deleted.
* @param {Object} obj
* @param {ObjectId[]} obj.secretIds - secret ids
*/
static async markDeletedSecretVersions({
secretIds
}: {
secretIds: Types.ObjectId[];
}) {
if (!EELicenseService.isLicenseValid) return;
await markDeletedSecretVersionsHelper({
secretIds
});
}
/**
* Initialize secret versioning by setting previously unversioned
* secrets to version 1 and begin populating secret versions.
*/
static async initSecretVersioning() {
if (!EELicenseService.isLicenseValid) return;
await initSecretVersioningHelper();
}
}
export default EESecretService;

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