mirror of
https://github.com/Infisical/infisical.git
synced 2025-03-20 19:38:58 +00:00
Compare commits
97 Commits
infisical-
...
infisical/
Author | SHA1 | Date | |
---|---|---|---|
b2ee15a4ff | |||
42de0fbe73 | |||
553c986aa8 | |||
c30ec8cb5f | |||
104c752f9a | |||
b66bea5671 | |||
f9313204a7 | |||
cb5c371a4f | |||
a32df58f46 | |||
e2658cc8dd | |||
1fbec20c6f | |||
ddff8be53c | |||
114d488345 | |||
c4da5a6ead | |||
056f5a4555 | |||
5612a01039 | |||
f1d609cf40 | |||
0e9c71ae9f | |||
d1af399489 | |||
f445bac42f | |||
798f091ff2 | |||
8381944bb2 | |||
f9d0e0d971 | |||
29d50f850b | |||
81c69d92b3 | |||
5cd9f37fdf | |||
1cf65aca1b | |||
470c429bd9 | |||
c8d081e818 | |||
492c6a6f97 | |||
1dfd18e779 | |||
caed17152d | |||
825143f17c | |||
da144b4d02 | |||
f4c4545099 | |||
924a969307 | |||
072f6c737c | |||
5f683dd389 | |||
2526cbe6ca | |||
6959fc52ac | |||
68c8dad829 | |||
ca3f7bac6c | |||
a127d452bd | |||
7c77cc4ea4 | |||
9c0e32a790 | |||
611fae785a | |||
0ef4ac1cdc | |||
c04ea7e731 | |||
9bdecaf02f | |||
6b222bad01 | |||
12d0916625 | |||
e0976d6bd6 | |||
a31f364361 | |||
8efa17928c | |||
48bfdd500d | |||
4621122cfb | |||
62fb048cce | |||
d4d0fe60b3 | |||
0a6e8e009b | |||
9f319d7ce3 | |||
7b3bd54386 | |||
8d82e2d0fc | |||
ffd4655e2f | |||
8f119fbdd3 | |||
b22a179a17 | |||
1cbab58d29 | |||
28943f3b6f | |||
b1f4e17aaf | |||
afd0c6de08 | |||
cf114b0d3c | |||
f785d62315 | |||
7aeda9e245 | |||
8a5e655122 | |||
9b447a4ab0 | |||
f3e84dc6eb | |||
a18a86770e | |||
6300f86cc4 | |||
df662b1058 | |||
db019178b7 | |||
dcec2dfcb0 | |||
e6ad153e83 | |||
9d33e4756b | |||
c267aee20f | |||
381e40f9a3 | |||
1760b319d3 | |||
59737f89c1 | |||
17097965d9 | |||
1a54bf34ef | |||
7e8ba077ae | |||
6ca010e2ba | |||
5a4a36a06a | |||
dd0fdea19f | |||
381806d84b | |||
9e9129dd02 | |||
c64cf39b69 | |||
dffcee52d7 | |||
db28536ea8 |
.env.example.gitignorepackage.json
.vscode
README.mdbackend
.eslintrc.prettierrcenvironment.d.tsjest.config.tspackage-lock.jsonpackage.json
src
config
controllers
v1
authController.tsbotController.tsindex.tsintegrationAuthController.tskeyController.tsmembershipController.tsmembershipOrgController.tsorganizationController.tspasswordController.tssecretController.tssecretsFolderController.tsserviceTokenController.tssignupController.tsstripeController.tsuserActionController.tsuserController.tsworkspaceController.ts
v2
apiKeyDataController.tsauthController.tsenvironmentController.tsindex.tsorganizationsController.tssecretController.tssecretsController.tsserviceAccountsController.tsserviceTokenDataController.tssignupController.tstagController.tsusersController.tsworkspaceController.ts
v3
ee
controllers/v1
actionController.tscloudProductsController.tsindex.tsmembershipController.tsorganizationsController.tssecretController.tssecretSnapshotController.tsstripeController.tsworkspaceController.ts
helpers
middleware
models
routes/v1
services
events
helpers
auth.tsbot.tsdatabase.tsevent.tsindex.tsintegration.tskey.tsmembership.tsmembershipOrg.tsnodemailer.tsorganization.tsrateLimiter.tssecret.tssecrets.tssignup.tsuser.tsworkspace.ts
index.tsintegrations
interfaces
middleware
index.tsrequestErrorHandler.tsrequireAuth.tsrequireBotAuth.tsrequireIntegrationAuth.tsrequireIntegrationAuthorizationAuth.tsrequireMembershipAuth.tsrequireMembershipOrgAuth.tsrequireMfaAuth.tsrequireOrganizationAuth.tsrequireSecretAuth.tsrequireSecretsAuth.tsrequireServiceAccountAuth.tsrequireServiceAccountWorkspacePermissionAuth.tsrequireServiceTokenAuth.tsrequireServiceTokenDataAuth.tsrequireSignupAuth.tsrequireWorkspaceAuth.tsvalidateRequest.ts
models
apiKeyData.tsbackupPrivateKey.tsbot.tsbotKey.tsfolder.tsincidentContactOrg.tsindex.tsintegration.tsintegrationAuth.tskey.tsloginSRPDetail.tsmembership.tsmembershipOrg.tsorganization.tssecret.tssecretApprovalRequest.tssecretBlindIndexData.tsserviceAccount.tsserviceAccountKey.tsserviceAccountOrganizationPermission.tsserviceAccountWorkspacePermission.tsserviceToken.tsserviceTokenData.tstag.tstoken.tstokenData.tstokenVersion.tsuser.tsuserAction.tsworkspace.ts
routes
status
v1
auth.tsbot.tsindex.tsintegration.tsintegrationAuth.tsinviteOrg.tskey.tsmembership.tsmembershipOrg.tsorganization.tspassword.tssecret.tsserviceToken.tssignup.tsstripe.tsuser.tsuserAction.tsworkspace.ts
v2
apiKeyData.tsauth.tsenvironment.tsindex.tsorganizations.tssecret.tssecrets.tsserviceAccounts.tsserviceTokenData.tssignup.tstags.tsusers.tsworkspace.ts
v3
services
BotService.tsDatabaseService.tsEventService.tsIntegrationService.tsSecretService.tsTelemetryService.tsTokenService.tshealth.tsindex.tssmtp.ts
types
utils
validation
bot.tsindex.tsintegration.tsintegrationAuth.tsmembership.tsmembershipOrg.tsorganization.tssecrets.tsserviceAccount.tsserviceTokenData.tsuser.tsworkspace.ts
variables
swagger
tests
cli
docker-compose.dev.ymldocker-compose.ymldocs
documentation/platform
images
dashboard-add-folder.pngdashboard-folder-overview.pngdashboard-folders.pngintegration-folders.pngintegrations-cloudflare-auth.pngintegrations-cloudflare-create.pngintegrations-cloudflare-credentials-1.pngintegrations-cloudflare-credentials-2.pngintegrations-cloudflare-credentials-3.pngintegrations-cloudflare-credentials-4.pngintegrations-cloudflare.pngproject-folder-token.pngproject-token-add.pngproject-token-added.png
integrations
mint.jsonfrontend
.eslintrc.js.prettierrcindex.tsuseLeaveConfirm.tsxusePersistentState.tsusePopUp.tsxuseProviderAuth.tsuseSyntaxHighlight.tsxuseToggle.tsxi18n.ts
.storybook
Dockerfilenext.config.jspackage-lock.jsonpackage.jsonpublic
src
components
RouteGuard.tsx
analytics
basic
Error.tsxEventFilter.tsxInputField.tsxLayout.tsxListbox.tsxToggle.tsx
buttons
dialog
ActivateBotDialog.tsxAddApiKeyDialog.tsxAddIncidentContactDialog.tsxAddProjectMemberDialog.tsxAddServiceTokenDialog.tsxAddUpdateEnvironmentDialog.tsxAddUserDialog.tsxDeleteActionModal.tsxDeleteEnvVar.tsxDeleteUserDialog.tsxUpgradePlan.tsx
popups
table
billing
context/Notifications
dashboard
AddTagsMenu.tsxCommentField.tsxCompareSecretsModal.tsxConfirmEnvOverwriteModal.tsxDashboardInputField.tsxDeleteActionButton.tsxDownloadSecretsMenu.tsxDropZone.tsxGenerateSecretMenu.tsxKeyPair.tsxSideBar.tsx
integrations
CloudIntegration.tsxCloudIntegrationSection.tsxFrameworkIntegration.tsxFrameworkIntegrationSection.tsxIntegration.tsxIntegrationSection.tsx
login
navigation
signup
CodeInputStep.tsxDonwloadBackupPDFStep.tsxEnterEmailStep.tsxInitialSignupStep.tsxTeamInviteStep.tsxUserInfoStep.tsx
utilities
SecurityClient.tsattemptChangePassword.tsattemptCliLogin.tsattemptCliLoginMfa.tsattemptLogin.tsattemptLoginMfa.ts
checks
config
cryptography
generateBackupPDF.tsintercom
parseDotEnv.tssecrets
v2
Button
Card
Checkbox
DeleteActionModal
Drawer
Dropdown
EmailServiceSetupModal
EmptyState
FormControl
HoverCard
HoverCardv2
IconButton
Input
Menu
Modal
Popover
Popoverv2
Select
Skeleton
Spinner
Switch
Table
Tabs
Tag
TextArea
Tooltip
UpgradePlanModal
index.tsxconfig
const.tscontext
ee
api
memberships
secrets
components
helpers
hooks
api
auth
bots
incidentContacts
index.tsxintegrationAuth
integrations
keys
organization
secretFolders
secretSnapshots
secrets
serverDetails
serviceAccounts
serviceTokens
subscriptions
tags
types.tsusers
workspace
layouts
pages
404.tsx_app.tsxcli-redirect.tsxdashboard.tsxsignup.tsxsignupinvite.tsx
reactQuery.tsactivity
api
apiKey
auth
ChangePassword2.tsCheckAuth.tsCheckEmailVerificationCode.tsCompleteAccountInformationSignup.tsCompleteAccountInformationSignupInvite.tsEmailVerifyOnPasswordReset.tsIssueBackupPrivateKey.tsLogin2.tsLogout.tsSRP1.tsSendEmailOnPasswordReset.tsSendVerificationEmail.tsToken.tsVerifySignupInvite.tsgetBackupEncryptedPrivateKey.tspublicKeyInfisical.tsresetPasswordOnAccountRecovery.tsverifyMfaToken.ts
bot
environments
files
integrations
ChangeHerokuConfigVars.tsDeleteIntegration.tsDeleteIntegrationAuth.tsGetIntegrationApps.tsGetIntegrationOptions.tsStartIntegration.tsauthorizeIntegration.tscreateIntegration.tsgetWorkspaceAuthorizations.tsgetWorkspaceIntegrations.tssaveIntegrationAccessToken.tsupdateIntegration.ts
organization
GetOrg.tsGetOrgProjectMemberships.tsGetOrgProjects.tsGetOrgSubscription.tsGetOrgUserProjects.tsGetOrgUsers.tsStripeRedirect.tsaddIncidentContact.tsaddUserToOrg.tschangeUserRoleInOrganization.tsdeleteIncidentContact.tsdeleteUserFromOrganization.tsgetIncidentContacts.tsgetOrgs.tsrenameOrg.ts
serviceToken
user
userActions
workspace
dashboard
email-not-verified.tsxhome
integrations
[id].tsx
login.tsxaws-parameter-store
aws-secret-manager
azure-key-vault
checkly
circleci
cloudflare-pages
flyio
github
gitlab
hashicorp-vault
heroku
netlify
railway
render
supabase
travisci
vercel
login/provider
noprojects.tsxpassword-reset.tsxrequestnewinvite.tsxsaml-sso.tsxsettings
billing
org/[id]
personal
project
users
verify-email.tsxservices
views
DashboardPage
DashboardEnvOverview.tsxDashboardPage.tsxDashboardPage.utils.tsindex.tsx
components
CompareSecret
CreateTagModal
EnvComparisonRow
FolderSection
PitDrawer
SecretDetailDrawer
SecretDropzone
SecretInputRow
SecretTableHeader
IntegrationsPage
IntegrationPage.utils.tsxIntegrationsPage.tsxindex.tsx
components
CloudIntegrationSection
FrameworkIntegrationSection
IntegrationsSection
Settings
BillingSettingsPage
BillingSettingsPage.tsxindex.tsx
components
BillingCloudTab
BillingCloudTab.tsxCurrentPlanSection.tsxManagePlansModal.tsxManagePlansTable.tsxPreviewSection.tsxindex.tsx
BillingDetailsTab
BillingDetailsTab.tsxCompanyNameSection.tsxInvoiceEmailSection.tsxPmtMethodsSection.tsxPmtMethodsTable.tsxTaxIDModal.tsxTaxIDSection.tsxTaxIDTable.tsxindex.tsx
BillingReceiptsTab
BillingTabGroup
index.tsxCreateServiceAccountPage
CreateServiceAccountPage.tsxindex.tsx
components
CopyServiceAccountIDSection
CopyServiceAccountPublicKeySection
SAProjectLevelPermissionsTable
ServiceAccountNameChangeSection
index.tsxOrgSettingsPage
OrgSettingsPage.tsxindex.tsx
components
OrgIncidentContactsSection
OrgIncidentContactsTable
OrgMembersSection
OrgMembersTable
OrgNameChangeSection
OrgServiceAccountsTable
index.tsxPersonalSettingsPage
APIKeySection
ChangeLanguageSection
ChangePasswordSection
EmergencyKitSection
PersonalAPIKeyTab
PersonalSecurityTab
PersonalSettingsPage.tsxPersonalTabGroup
SecuritySection
SessionsSection
index.tsxProjectSettingsPage
ProjectSettingsPage.tsxindex.tsx
components
AutoCapitalizationSection
CopyProjectIDSection
DeleteProjectSection
E2EESection
EnvironmentSection
AddEnvironmentModal.tsxEnvironmentSection.tsxEnvironmentTable.tsxUpdateEnvironmentModal.tsxindex.tsx
ProjectGeneralTab
ProjectIndexSecretsSection
ProjectNameChangeSection
ProjectServiceTokensTab
ProjectTabGroup
SecretTagsSection
ServiceTokenSection
index.tsxhelm-charts/secrets-operator
k8-operator
api/v1alpha1
config
controllers
kubectl-install
packages
@ -61,13 +61,6 @@ SENTRY_DSN=
|
||||
# Ignore - Not applicable for self-hosted version
|
||||
POSTHOG_HOST=
|
||||
POSTHOG_PROJECT_API_KEY=
|
||||
STRIPE_SECRET_KEY=
|
||||
STRIPE_PUBLISHABLE_KEY=
|
||||
STRIPE_WEBHOOK_SECRET=
|
||||
STRIPE_PRODUCT_STARTER=
|
||||
STRIPE_PRODUCT_TEAM=
|
||||
STRIPE_PRODUCT_PRO=
|
||||
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=
|
||||
|
||||
CLIENT_ID_GOOGLE=
|
||||
CLIENT_SECRET_GOOGLE=
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
node_modules
|
||||
.env
|
||||
.env.dev
|
||||
.env.gamma
|
||||
.env.prod
|
||||
.env.infisical
|
||||
|
||||
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"workbench.editor.wrapTabs": true
|
||||
}
|
@ -136,7 +136,7 @@ Not sure where to get started? You can:
|
||||
- [Slack](https://join.slack.com/t/infisical-users/shared_invite/zt-1wehzfnzn-1aMo5JcGENJiNAC2SD8Jlg) for discussion with the community and Infisical team.
|
||||
- [GitHub](https://github.com/Infisical/infisical) for code, issues, and pull requests
|
||||
- [Twitter](https://twitter.com/infisical) for fast news
|
||||
- [YouTube](https://www.youtube.com/@infisical5306) for videos on secret management
|
||||
- [YouTube](https://www.youtube.com/@infisical_od) for videos on secret management
|
||||
- [Blog](https://infisical.com/blog) for secret management insights, articles, tutorials, and updates
|
||||
- [Roadmap](https://www.notion.so/infisical/be2d2585a6694e40889b03aef96ea36b?v=5b19a8127d1a4060b54769567a8785fa) for planned features
|
||||
|
||||
|
@ -1,12 +1,21 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"plugins": ["@typescript-eslint", "unused-imports"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": 2
|
||||
"no-console": 2,
|
||||
"quotes": ["error", "double", { "avoidEscape": true }],
|
||||
"comma-dangle": ["error", "only-multiline"],
|
||||
"@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{ "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" }
|
||||
],
|
||||
"sort-imports": ["error", { "ignoreDeclarationSort": true }]
|
||||
}
|
||||
}
|
||||
|
7
backend/.prettierrc
Normal file
7
backend/.prettierrc
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"singleQuote": false,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none",
|
||||
"tabWidth": 2,
|
||||
"semi": true
|
||||
}
|
8
backend/environment.d.ts
vendored
8
backend/environment.d.ts
vendored
@ -14,7 +14,7 @@ declare global {
|
||||
JWT_SIGNUP_LIFETIME: string;
|
||||
JWT_SIGNUP_SECRET: string;
|
||||
MONGO_URL: string;
|
||||
NODE_ENV: 'development' | 'staging' | 'testing' | 'production';
|
||||
NODE_ENV: "development" | "staging" | "testing" | "production";
|
||||
VERBOSE_ERROR_OUTPUT: string;
|
||||
LOKI_HOST: string;
|
||||
CLIENT_ID_HEROKU: string;
|
||||
@ -39,12 +39,6 @@ declare global {
|
||||
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;
|
||||
TELEMETRY_ENABLED: string;
|
||||
LICENSE_KEY: string;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
collectCoverageFrom: ['src/*.{js,ts}', '!**/node_modules/**'],
|
||||
modulePaths: ['<rootDir>/src'],
|
||||
testMatch: ['<rootDir>/tests/**/*.test.ts'],
|
||||
setupFiles: ['<rootDir>/test-resources/env-vars.js'],
|
||||
setupFilesAfterEnv: ['<rootDir>/tests/setupTests.ts']
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
collectCoverageFrom: ["src/*.{js,ts}", "!**/node_modules/**"],
|
||||
modulePaths: ["<rootDir>/src"],
|
||||
testMatch: ["<rootDir>/tests/**/*.test.ts"],
|
||||
setupFiles: ["<rootDir>/test-resources/env-vars.js"],
|
||||
setupFilesAfterEnv: ["<rootDir>/tests/setupTests.ts"],
|
||||
};
|
||||
|
94
backend/package-lock.json
generated
94
backend/package-lock.json
generated
@ -17,13 +17,11 @@
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/libsodium-wrappers": "^0.7.10",
|
||||
"argon2": "^0.30.3",
|
||||
"await-to-js": "^3.0.0",
|
||||
"aws-sdk": "^2.1364.0",
|
||||
"axios": "^1.3.5",
|
||||
"axios-retry": "^3.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"bigint-conversion": "^2.4.0",
|
||||
"builder-pattern": "^2.2.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
@ -50,7 +48,6 @@
|
||||
"query-string": "^7.1.3",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"stripe": "^10.7.0",
|
||||
"swagger-autogen": "^2.22.0",
|
||||
"swagger-ui-express": "^4.6.2",
|
||||
"tweetnacl": "^1.0.3",
|
||||
@ -81,6 +78,7 @@
|
||||
"@typescript-eslint/parser": "^5.40.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"install": "^0.13.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-junit": "^15.0.0",
|
||||
@ -2959,6 +2957,7 @@
|
||||
"version": "10.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.2.0.tgz",
|
||||
"integrity": "sha512-OPwQlEdg40HAj5KNF8WW6q2KG4Z+cBCZb3m4ninfTZKaBmbIJodviQsDBoYMPHkOyJJMHnOJo5j2+LKDOhOACg==",
|
||||
"deprecated": "Use version 10.1.0. Version 10.2.0 has potential breaking issues",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
@ -3786,14 +3785,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/await-to-js": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz",
|
||||
"integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-sdk": {
|
||||
"version": "2.1386.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1386.0.tgz",
|
||||
@ -4216,11 +4207,6 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/builder-pattern": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-pattern/-/builder-pattern-2.2.0.tgz",
|
||||
"integrity": "sha512-cES3qdeBzA4QyJi7rV/l/kAhIFX6AKo3vK66ZPXLNpjcQWCS8sjLKscly8imlfW2YPTo/hquMRMnaWpZ80Kj+g=="
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@ -4972,6 +4958,36 @@
|
||||
"url": "https://opencollective.com/eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-unused-imports": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz",
|
||||
"integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"eslint-rule-composer": "^0.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"eslint": "^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-rule-composer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
|
||||
"integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
@ -11556,18 +11572,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/stripe": {
|
||||
"version": "10.17.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-10.17.0.tgz",
|
||||
"integrity": "sha512-JHV2KoL+nMQRXu3m9ervCZZvi4DDCJfzHUE6CmtJxR9TmizyYfrVuhGvnsZLLnheby9Qrnf4Hq6iOEcejGwnGQ==",
|
||||
"dependencies": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^8.1 || >=10.*"
|
||||
}
|
||||
},
|
||||
"node_modules/strnum": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||
@ -15374,11 +15378,6 @@
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
|
||||
"integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw=="
|
||||
},
|
||||
"await-to-js": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/await-to-js/-/await-to-js-3.0.0.tgz",
|
||||
"integrity": "sha512-zJAaP9zxTcvTHRlejau3ZOY4V7SRpiByf3/dxx2uyKxxor19tpmpV2QRsTKikckwhaPmr2dVpxxMr7jOCYVp5g=="
|
||||
},
|
||||
"aws-sdk": {
|
||||
"version": "2.1386.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1386.0.tgz",
|
||||
@ -15705,11 +15704,6 @@
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"builder-pattern": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/builder-pattern/-/builder-pattern-2.2.0.tgz",
|
||||
"integrity": "sha512-cES3qdeBzA4QyJi7rV/l/kAhIFX6AKo3vK66ZPXLNpjcQWCS8sjLKscly8imlfW2YPTo/hquMRMnaWpZ80Kj+g=="
|
||||
},
|
||||
"bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
@ -16291,6 +16285,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslint-plugin-unused-imports": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-2.0.0.tgz",
|
||||
"integrity": "sha512-3APeS/tQlTrFa167ThtP0Zm0vctjr4M44HMpeg1P4bK6wItarumq0Ma82xorMKdFsWpphQBlRPzw/pxiVELX1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eslint-rule-composer": "^0.3.0"
|
||||
}
|
||||
},
|
||||
"eslint-rule-composer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz",
|
||||
"integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==",
|
||||
"dev": true
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
|
||||
@ -21049,15 +21058,6 @@
|
||||
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
|
||||
"dev": true
|
||||
},
|
||||
"stripe": {
|
||||
"version": "10.17.0",
|
||||
"resolved": "https://registry.npmjs.org/stripe/-/stripe-10.17.0.tgz",
|
||||
"integrity": "sha512-JHV2KoL+nMQRXu3m9ervCZZvi4DDCJfzHUE6CmtJxR9TmizyYfrVuhGvnsZLLnheby9Qrnf4Hq6iOEcejGwnGQ==",
|
||||
"requires": {
|
||||
"@types/node": ">=8.1.0",
|
||||
"qs": "^6.11.0"
|
||||
}
|
||||
},
|
||||
"strnum": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
|
||||
|
@ -8,13 +8,11 @@
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/libsodium-wrappers": "^0.7.10",
|
||||
"argon2": "^0.30.3",
|
||||
"await-to-js": "^3.0.0",
|
||||
"aws-sdk": "^2.1364.0",
|
||||
"axios": "^1.3.5",
|
||||
"axios-retry": "^3.4.0",
|
||||
"bcrypt": "^5.1.0",
|
||||
"bigint-conversion": "^2.4.0",
|
||||
"builder-pattern": "^2.2.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"cors": "^2.8.5",
|
||||
"crypto-js": "^4.1.1",
|
||||
@ -41,7 +39,6 @@
|
||||
"query-string": "^7.1.3",
|
||||
"rate-limit-mongo": "^2.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"stripe": "^10.7.0",
|
||||
"swagger-autogen": "^2.22.0",
|
||||
"swagger-ui-express": "^4.6.2",
|
||||
"tweetnacl": "^1.0.3",
|
||||
@ -99,6 +96,7 @@
|
||||
"@typescript-eslint/parser": "^5.40.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^8.26.0",
|
||||
"eslint-plugin-unused-imports": "^2.0.0",
|
||||
"install": "^0.13.0",
|
||||
"jest": "^29.3.1",
|
||||
"jest-junit": "^15.0.0",
|
||||
|
@ -1,93 +1,85 @@
|
||||
import InfisicalClient from 'infisical-node';
|
||||
import InfisicalClient from "infisical-node";
|
||||
|
||||
export const client = new InfisicalClient({
|
||||
token: process.env.INFISICAL_TOKEN!
|
||||
token: process.env.INFISICAL_TOKEN!,
|
||||
});
|
||||
|
||||
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
|
||||
export const getPort = async () => (await client.getSecret("PORT")).secretValue || 4000;
|
||||
export const getEncryptionKey = async () => {
|
||||
const secretValue = (await client.getSecret('ENCRYPTION_KEY')).secretValue;
|
||||
return secretValue === '' ? undefined : secretValue;
|
||||
const secretValue = (await client.getSecret("ENCRYPTION_KEY")).secretValue;
|
||||
return secretValue === "" ? undefined : secretValue;
|
||||
}
|
||||
export const getRootEncryptionKey = async () => {
|
||||
const secretValue = (await client.getSecret('ROOT_ENCRYPTION_KEY')).secretValue;
|
||||
return secretValue === '' ? undefined : secretValue;
|
||||
const secretValue = (await client.getSecret("ROOT_ENCRYPTION_KEY")).secretValue;
|
||||
return secretValue === "" ? undefined : secretValue;
|
||||
}
|
||||
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue === 'true'
|
||||
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
|
||||
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
|
||||
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;
|
||||
export const getJwtMfaLifetime = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
||||
export const getJwtMfaSecret = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
||||
export const getJwtRefreshLifetime = async () => (await client.getSecret('JWT_REFRESH_LIFETIME')).secretValue || '90d';
|
||||
export const getJwtRefreshSecret = async () => (await client.getSecret('JWT_REFRESH_SECRET')).secretValue;
|
||||
export const getJwtServiceSecret = async () => (await client.getSecret('JWT_SERVICE_SECRET')).secretValue;
|
||||
export const getJwtSignupLifetime = async () => (await client.getSecret('JWT_SIGNUP_LIFETIME')).secretValue || '15m';
|
||||
export const getJwtProviderAuthSecret = async () => (await client.getSecret('JWT_PROVIDER_AUTH_SECRET')).secretValue;
|
||||
export const getJwtProviderAuthLifetime = async () => (await client.getSecret('JWT_PROVIDER_AUTH_LIFETIME')).secretValue || '15m';
|
||||
export const getJwtSignupSecret = async () => (await client.getSecret('JWT_SIGNUP_SECRET')).secretValue;
|
||||
export const getMongoURL = async () => (await client.getSecret('MONGO_URL')).secretValue;
|
||||
export const getNodeEnv = async () => (await client.getSecret('NODE_ENV')).secretValue || 'production';
|
||||
export const getVerboseErrorOutput = async () => (await client.getSecret('VERBOSE_ERROR_OUTPUT')).secretValue === 'true' && true;
|
||||
export const getLokiHost = async () => (await client.getSecret('LOKI_HOST')).secretValue;
|
||||
export const getClientIdAzure = async () => (await client.getSecret('CLIENT_ID_AZURE')).secretValue;
|
||||
export const getClientIdHeroku = async () => (await client.getSecret('CLIENT_ID_HEROKU')).secretValue;
|
||||
export const getClientIdVercel = async () => (await client.getSecret('CLIENT_ID_VERCEL')).secretValue;
|
||||
export const getClientIdNetlify = async () => (await client.getSecret('CLIENT_ID_NETLIFY')).secretValue;
|
||||
export const getClientIdGitHub = async () => (await client.getSecret('CLIENT_ID_GITHUB')).secretValue;
|
||||
export const getClientIdGitLab = async () => (await client.getSecret('CLIENT_ID_GITLAB')).secretValue;
|
||||
export const getClientIdGoogle = async () => (await client.getSecret('CLIENT_ID_GOOGLE')).secretValue;
|
||||
export const getClientSecretAzure = async () => (await client.getSecret('CLIENT_SECRET_AZURE')).secretValue;
|
||||
export const getClientSecretHeroku = async () => (await client.getSecret('CLIENT_SECRET_HEROKU')).secretValue;
|
||||
export const getClientSecretVercel = async () => (await client.getSecret('CLIENT_SECRET_VERCEL')).secretValue;
|
||||
export const getClientSecretNetlify = async () => (await client.getSecret('CLIENT_SECRET_NETLIFY')).secretValue;
|
||||
export const getClientSecretGitHub = async () => (await client.getSecret('CLIENT_SECRET_GITHUB')).secretValue;
|
||||
export const getClientSecretGitLab = async () => (await client.getSecret('CLIENT_SECRET_GITLAB')).secretValue;
|
||||
export const getClientSecretGoogle = async () => (await client.getSecret('CLIENT_SECRET_GOOGLE')).secretValue;
|
||||
export const getClientSlugVercel = async () => (await client.getSecret('CLIENT_SLUG_VERCEL')).secretValue;
|
||||
export const getPostHogHost = async () => (await client.getSecret('POSTHOG_HOST')).secretValue || 'https://app.posthog.com';
|
||||
export const getPostHogProjectApiKey = async () => (await client.getSecret('POSTHOG_PROJECT_API_KEY')).secretValue || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
||||
export const getSentryDSN = async () => (await client.getSecret('SENTRY_DSN')).secretValue;
|
||||
export const getSiteURL = async () => (await client.getSecret('SITE_URL')).secretValue;
|
||||
export const getSmtpHost = async () => (await client.getSecret('SMTP_HOST')).secretValue;
|
||||
export const getSmtpSecure = async () => (await client.getSecret('SMTP_SECURE')).secretValue === 'true' || false;
|
||||
export const getSmtpPort = async () => parseInt((await client.getSecret('SMTP_PORT')).secretValue) || 587;
|
||||
export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAME')).secretValue;
|
||||
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
|
||||
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
|
||||
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
|
||||
export const getInviteOnlySignup = async () => (await client.getSecret("INVITE_ONLY_SIGNUP")).secretValue === "true"
|
||||
export const getSaltRounds = async () => parseInt((await client.getSecret("SALT_ROUNDS")).secretValue) || 10;
|
||||
export const getJwtAuthLifetime = async () => (await client.getSecret("JWT_AUTH_LIFETIME")).secretValue || "10d";
|
||||
export const getJwtAuthSecret = async () => (await client.getSecret("JWT_AUTH_SECRET")).secretValue;
|
||||
export const getJwtMfaLifetime = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
|
||||
export const getJwtMfaSecret = async () => (await client.getSecret("JWT_MFA_LIFETIME")).secretValue || "5m";
|
||||
export const getJwtRefreshLifetime = async () => (await client.getSecret("JWT_REFRESH_LIFETIME")).secretValue || "90d";
|
||||
export const getJwtRefreshSecret = async () => (await client.getSecret("JWT_REFRESH_SECRET")).secretValue;
|
||||
export const getJwtServiceSecret = async () => (await client.getSecret("JWT_SERVICE_SECRET")).secretValue;
|
||||
export const getJwtSignupLifetime = async () => (await client.getSecret("JWT_SIGNUP_LIFETIME")).secretValue || "15m";
|
||||
export const getJwtProviderAuthSecret = async () => (await client.getSecret("JWT_PROVIDER_AUTH_SECRET")).secretValue;
|
||||
export const getJwtProviderAuthLifetime = async () => (await client.getSecret("JWT_PROVIDER_AUTH_LIFETIME")).secretValue || "15m";
|
||||
export const getJwtSignupSecret = async () => (await client.getSecret("JWT_SIGNUP_SECRET")).secretValue;
|
||||
export const getMongoURL = async () => (await client.getSecret("MONGO_URL")).secretValue;
|
||||
export const getNodeEnv = async () => (await client.getSecret("NODE_ENV")).secretValue || "production";
|
||||
export const getVerboseErrorOutput = async () => (await client.getSecret("VERBOSE_ERROR_OUTPUT")).secretValue === "true" && true;
|
||||
export const getLokiHost = async () => (await client.getSecret("LOKI_HOST")).secretValue;
|
||||
export const getClientIdAzure = async () => (await client.getSecret("CLIENT_ID_AZURE")).secretValue;
|
||||
export const getClientIdHeroku = async () => (await client.getSecret("CLIENT_ID_HEROKU")).secretValue;
|
||||
export const getClientIdVercel = async () => (await client.getSecret("CLIENT_ID_VERCEL")).secretValue;
|
||||
export const getClientIdNetlify = async () => (await client.getSecret("CLIENT_ID_NETLIFY")).secretValue;
|
||||
export const getClientIdGitHub = async () => (await client.getSecret("CLIENT_ID_GITHUB")).secretValue;
|
||||
export const getClientIdGitLab = async () => (await client.getSecret("CLIENT_ID_GITLAB")).secretValue;
|
||||
export const getClientIdGoogle = async () => (await client.getSecret("CLIENT_ID_GOOGLE")).secretValue;
|
||||
export const getClientSecretAzure = async () => (await client.getSecret("CLIENT_SECRET_AZURE")).secretValue;
|
||||
export const getClientSecretHeroku = async () => (await client.getSecret("CLIENT_SECRET_HEROKU")).secretValue;
|
||||
export const getClientSecretVercel = async () => (await client.getSecret("CLIENT_SECRET_VERCEL")).secretValue;
|
||||
export const getClientSecretNetlify = async () => (await client.getSecret("CLIENT_SECRET_NETLIFY")).secretValue;
|
||||
export const getClientSecretGitHub = async () => (await client.getSecret("CLIENT_SECRET_GITHUB")).secretValue;
|
||||
export const getClientSecretGitLab = async () => (await client.getSecret("CLIENT_SECRET_GITLAB")).secretValue;
|
||||
export const getClientSecretGoogle = async () => (await client.getSecret("CLIENT_SECRET_GOOGLE")).secretValue;
|
||||
export const getClientSlugVercel = async () => (await client.getSecret("CLIENT_SLUG_VERCEL")).secretValue;
|
||||
export const getPostHogHost = async () => (await client.getSecret("POSTHOG_HOST")).secretValue || "https://app.posthog.com";
|
||||
export const getPostHogProjectApiKey = async () => (await client.getSecret("POSTHOG_PROJECT_API_KEY")).secretValue || "phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE";
|
||||
export const getSentryDSN = async () => (await client.getSecret("SENTRY_DSN")).secretValue;
|
||||
export const getSiteURL = async () => (await client.getSecret("SITE_URL")).secretValue;
|
||||
export const getSmtpHost = async () => (await client.getSecret("SMTP_HOST")).secretValue;
|
||||
export const getSmtpSecure = async () => (await client.getSecret("SMTP_SECURE")).secretValue === "true" || false;
|
||||
export const getSmtpPort = async () => parseInt((await client.getSecret("SMTP_PORT")).secretValue) || 587;
|
||||
export const getSmtpUsername = async () => (await client.getSecret("SMTP_USERNAME")).secretValue;
|
||||
export const getSmtpPassword = async () => (await client.getSecret("SMTP_PASSWORD")).secretValue;
|
||||
export const getSmtpFromAddress = async () => (await client.getSecret("SMTP_FROM_ADDRESS")).secretValue;
|
||||
export const getSmtpFromName = async () => (await client.getSecret("SMTP_FROM_NAME")).secretValue || "Infisical";
|
||||
|
||||
export const getLicenseKey = async () => {
|
||||
const secretValue = (await client.getSecret('LICENSE_KEY')).secretValue;
|
||||
return secretValue === '' ? undefined : secretValue;
|
||||
const secretValue = (await client.getSecret("LICENSE_KEY")).secretValue;
|
||||
return secretValue === "" ? undefined : secretValue;
|
||||
}
|
||||
export const getLicenseServerKey = async () => {
|
||||
const secretValue = (await client.getSecret('LICENSE_SERVER_KEY')).secretValue;
|
||||
return secretValue === '' ? undefined : secretValue;
|
||||
const secretValue = (await client.getSecret("LICENSE_SERVER_KEY")).secretValue;
|
||||
return secretValue === "" ? undefined : secretValue;
|
||||
}
|
||||
export const getLicenseServerUrl = async () => (await client.getSecret('LICENSE_SERVER_URL')).secretValue || 'https://portal.infisical.com';
|
||||
export const getLicenseServerUrl = async () => (await client.getSecret("LICENSE_SERVER_URL")).secretValue || "https://portal.infisical.com";
|
||||
|
||||
// TODO: deprecate from here
|
||||
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
|
||||
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
|
||||
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
|
||||
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
|
||||
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
|
||||
export const getStripeWebhookSecret = async () => (await client.getSecret('STRIPE_WEBHOOK_SECRET')).secretValue;
|
||||
|
||||
export const getTelemetryEnabled = async () => (await client.getSecret('TELEMETRY_ENABLED')).secretValue !== 'false' && true;
|
||||
export const getLoopsApiKey = async () => (await client.getSecret('LOOPS_API_KEY')).secretValue;
|
||||
export const getSmtpConfigured = async () => (await client.getSecret('SMTP_HOST')).secretValue == '' || (await client.getSecret('SMTP_HOST')).secretValue == undefined ? false : true
|
||||
export const getTelemetryEnabled = async () => (await client.getSecret("TELEMETRY_ENABLED")).secretValue !== "false" && true;
|
||||
export const getLoopsApiKey = async () => (await client.getSecret("LOOPS_API_KEY")).secretValue;
|
||||
export const getSmtpConfigured = async () => (await client.getSecret("SMTP_HOST")).secretValue == "" || (await client.getSecret("SMTP_HOST")).secretValue == undefined ? false : true
|
||||
export const getHttpsEnabled = async () => {
|
||||
if ((await getNodeEnv()) != "production") {
|
||||
// no https for anything other than prod
|
||||
return false
|
||||
}
|
||||
|
||||
if ((await client.getSecret('HTTPS_ENABLED')).secretValue == undefined || (await client.getSecret('HTTPS_ENABLED')).secretValue == "") {
|
||||
if ((await client.getSecret("HTTPS_ENABLED")).secretValue == undefined || (await client.getSecret("HTTPS_ENABLED")).secretValue == "") {
|
||||
// default when no value present
|
||||
return true
|
||||
}
|
||||
|
||||
return (await client.getSecret('HTTPS_ENABLED')).secretValue === 'true' && true
|
||||
return (await client.getSecret("HTTPS_ENABLED")).secretValue === "true" && true
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
import axios from 'axios';
|
||||
import axiosRetry from 'axios-retry';
|
||||
import axios from "axios";
|
||||
import axiosRetry from "axios-retry";
|
||||
import {
|
||||
getLicenseServerKeyAuthToken,
|
||||
setLicenseServerKeyAuthToken,
|
||||
getLicenseKeyAuthToken,
|
||||
setLicenseKeyAuthToken
|
||||
} from './storage';
|
||||
getLicenseServerKeyAuthToken,
|
||||
setLicenseKeyAuthToken,
|
||||
setLicenseServerKeyAuthToken,
|
||||
} from "./storage";
|
||||
import {
|
||||
getLicenseKey,
|
||||
getLicenseServerKey,
|
||||
getLicenseServerUrl
|
||||
} from './index';
|
||||
getLicenseServerUrl,
|
||||
} from "./index";
|
||||
|
||||
// should have JWT to interact with the license server
|
||||
export const licenseServerKeyRequest = axios.create();
|
||||
@ -35,8 +35,8 @@ export const refreshLicenseServerKeyToken = async () => {
|
||||
`${licenseServerUrl}/api/auth/v1/license-server-login`, {},
|
||||
{
|
||||
headers: {
|
||||
'X-API-KEY': licenseServerKey
|
||||
}
|
||||
"X-API-KEY": licenseServerKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@ -53,8 +53,8 @@ export const refreshLicenseKeyToken = async () => {
|
||||
`${licenseServerUrl}/api/auth/v1/license-login`, {},
|
||||
{
|
||||
headers: {
|
||||
'X-API-KEY': licenseKey
|
||||
}
|
||||
"X-API-KEY": licenseKey,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
@ -86,7 +86,7 @@ licenseServerKeyRequest.interceptors.response.use((response) => {
|
||||
// refresh
|
||||
const token = await refreshLicenseServerKeyToken();
|
||||
|
||||
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
||||
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
|
||||
return licenseServerKeyRequest(originalRequest);
|
||||
}
|
||||
|
||||
@ -116,7 +116,7 @@ licenseKeyRequest.interceptors.response.use((response) => {
|
||||
// refresh
|
||||
const token = await refreshLicenseKeyToken();
|
||||
|
||||
axios.defaults.headers.common['Authorization'] = 'Bearer ' + token;
|
||||
axios.defaults.headers.common["Authorization"] = "Bearer " + token;
|
||||
return licenseKeyRequest(originalRequest);
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ const MemoryLicenseServerKeyTokenStorage = () => {
|
||||
setToken: (token: string) => {
|
||||
authToken = token;
|
||||
},
|
||||
getToken: () => authToken
|
||||
getToken: () => authToken,
|
||||
};
|
||||
};
|
||||
|
||||
@ -16,7 +16,7 @@ const MemoryLicenseKeyTokenStorage = () => {
|
||||
setToken: (token: string) => {
|
||||
authToken = token;
|
||||
},
|
||||
getToken: () => authToken
|
||||
getToken: () => authToken,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -1,36 +1,36 @@
|
||||
import { Request, Response } from 'express';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import * as bigintConversion from 'bigint-conversion';
|
||||
import { Request, Response } from "express";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import jwt from "jsonwebtoken";
|
||||
import * as bigintConversion from "bigint-conversion";
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const jsrp = require('jsrp');
|
||||
const jsrp = require("jsrp");
|
||||
import {
|
||||
User,
|
||||
LoginSRPDetail,
|
||||
TokenVersion
|
||||
} from '../../models';
|
||||
import { createToken, issueAuthTokens, clearTokens } from '../../helpers/auth';
|
||||
import { checkUserDevice } from '../../helpers/user';
|
||||
LoginSRPDetail,
|
||||
TokenVersion,
|
||||
User,
|
||||
} from "../../models";
|
||||
import { clearTokens, createToken, issueAuthTokens } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
AUTH_MODE_JWT
|
||||
} from '../../variables';
|
||||
AUTH_MODE_JWT,
|
||||
} from "../../variables";
|
||||
import {
|
||||
BadRequestError,
|
||||
UnauthorizedRequestError
|
||||
} from '../../utils/errors';
|
||||
import { EELogService } from '../../ee/services';
|
||||
import { getChannelFromUserAgent } from '../../utils/posthog';
|
||||
UnauthorizedRequestError,
|
||||
} from "../../utils/errors";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { getChannelFromUserAgent } from "../../utils/posthog";
|
||||
import {
|
||||
getJwtRefreshSecret,
|
||||
getHttpsEnabled,
|
||||
getJwtAuthLifetime,
|
||||
getJwtAuthSecret,
|
||||
getHttpsEnabled
|
||||
} from '../../config';
|
||||
getJwtRefreshSecret,
|
||||
} from "../../config";
|
||||
|
||||
declare module 'jsonwebtoken' {
|
||||
declare module "jsonwebtoken" {
|
||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||
userId: string;
|
||||
refreshVersion?: number;
|
||||
@ -46,20 +46,20 @@ declare module 'jsonwebtoken' {
|
||||
export const login1 = async (req: Request, res: Response) => {
|
||||
const {
|
||||
email,
|
||||
clientPublicKey
|
||||
clientPublicKey,
|
||||
}: { email: string; clientPublicKey: string } = req.body;
|
||||
|
||||
const user = await User.findOne({
|
||||
email
|
||||
}).select('+salt +verifier');
|
||||
email,
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const server = new jsrp.server();
|
||||
server.init(
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier
|
||||
verifier: user.verifier,
|
||||
},
|
||||
async () => {
|
||||
// generate server-side public key
|
||||
@ -73,7 +73,7 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
|
||||
return res.status(200).send({
|
||||
serverPublicKey,
|
||||
salt: user.salt
|
||||
salt: user.salt,
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -89,10 +89,10 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
export const login2 = async (req: Request, res: Response) => {
|
||||
const { email, clientProof } = req.body;
|
||||
const user = await User.findOne({
|
||||
email
|
||||
}).select('+salt +verifier +publicKey +encryptedPrivateKey +iv +tag');
|
||||
email,
|
||||
}).select("+salt +verifier +publicKey +encryptedPrivateKey +iv +tag");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: email })
|
||||
|
||||
@ -105,7 +105,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier,
|
||||
b: loginSRPDetailFromDB.serverBInt
|
||||
b: loginSRPDetailFromDB.serverBInt,
|
||||
},
|
||||
async () => {
|
||||
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
|
||||
@ -117,33 +117,33 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
await checkUserDevice({
|
||||
user,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
loginAction && await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.realIP
|
||||
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
// return (access) token in response
|
||||
@ -152,12 +152,12 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
publicKey: user.publicKey,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||
iv: user.iv,
|
||||
tag: user.tag
|
||||
tag: user.tag,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(400).send({
|
||||
message: 'Failed to authenticate. Try again?'
|
||||
message: "Failed to authenticate. Try again?",
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -175,51 +175,51 @@ export const logout = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
// clear httpOnly cookie
|
||||
res.cookie('jid', '', {
|
||||
res.cookie("jid", "", {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: (await getHttpsEnabled()) as boolean
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: (await getHttpsEnabled()) as boolean,
|
||||
});
|
||||
|
||||
const logoutAction = await EELogService.createAction({
|
||||
name: ACTION_LOGOUT,
|
||||
userId: req.user._id
|
||||
userId: req.user._id,
|
||||
});
|
||||
|
||||
logoutAction && await EELogService.createLog({
|
||||
userId: req.user._id,
|
||||
actions: [logoutAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.realIP
|
||||
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully logged out.'
|
||||
message: "Successfully logged out.",
|
||||
});
|
||||
};
|
||||
|
||||
export const getCommonPasswords = async (req: Request, res: Response) => {
|
||||
const commonPasswords = fs.readFileSync(
|
||||
path.resolve(__dirname, '../../data/' + 'common_passwords.txt'),
|
||||
'utf8'
|
||||
).split('\n');
|
||||
path.resolve(__dirname, "../../data/" + "common_passwords.txt"),
|
||||
"utf8"
|
||||
).split("\n");
|
||||
|
||||
return res.status(200).send(commonPasswords);
|
||||
}
|
||||
|
||||
export const revokeAllSessions = async (req: Request, res: Response) => {
|
||||
await TokenVersion.updateMany({
|
||||
user: req.user._id
|
||||
user: req.user._id,
|
||||
}, {
|
||||
$inc: {
|
||||
refreshVersion: 1,
|
||||
accessVersion: 1
|
||||
}
|
||||
accessVersion: 1,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully revoked all sessions.'
|
||||
message: "Successfully revoked all sessions.",
|
||||
});
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ export const revokeAllSessions = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const checkAuth = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
message: 'Authenticated'
|
||||
message: "Authenticated",
|
||||
});
|
||||
}
|
||||
|
||||
@ -244,44 +244,44 @@ export const checkAuth = async (req: Request, res: Response) => {
|
||||
export const getNewToken = async (req: Request, res: Response) => {
|
||||
const refreshToken = req.cookies.jid;
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new Error('Failed to find refresh token in request cookies');
|
||||
}
|
||||
if (!refreshToken) throw BadRequestError({
|
||||
message: "Failed to find refresh token in request cookies"
|
||||
});
|
||||
|
||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||
jwt.verify(refreshToken, await getJwtRefreshSecret())
|
||||
);
|
||||
|
||||
const user = await User.findOne({
|
||||
_id: decodedToken.userId
|
||||
}).select('+publicKey +refreshVersion +accessVersion');
|
||||
_id: decodedToken.userId,
|
||||
}).select("+publicKey +refreshVersion +accessVersion");
|
||||
|
||||
if (!user) throw new Error('Failed to authenticate unfound user');
|
||||
if (!user) throw new Error("Failed to authenticate unfound user");
|
||||
if (!user?.publicKey)
|
||||
throw new Error('Failed to authenticate not fully set up account');
|
||||
throw new Error("Failed to authenticate not fully set up account");
|
||||
|
||||
const tokenVersion = await TokenVersion.findById(decodedToken.tokenVersionId);
|
||||
|
||||
if (!tokenVersion) throw UnauthorizedRequestError({
|
||||
message: 'Failed to validate refresh token'
|
||||
message: "Failed to validate refresh token",
|
||||
});
|
||||
|
||||
if (decodedToken.refreshVersion !== tokenVersion.refreshVersion) throw BadRequestError({
|
||||
message: 'Failed to validate refresh token'
|
||||
message: "Failed to validate refresh token",
|
||||
});
|
||||
|
||||
const token = createToken({
|
||||
payload: {
|
||||
userId: decodedToken.userId,
|
||||
tokenVersionId: tokenVersion._id.toString(),
|
||||
accessVersion: tokenVersion.refreshVersion
|
||||
accessVersion: tokenVersion.refreshVersion,
|
||||
},
|
||||
expiresIn: await getJwtAuthLifetime(),
|
||||
secret: await getJwtAuthSecret()
|
||||
secret: await getJwtAuthSecret(),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
token
|
||||
token,
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Bot, BotKey } from '../../models';
|
||||
import { createBot } from '../../helpers/bot';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { Bot, BotKey } from "../../models";
|
||||
import { createBot } from "../../helpers/bot";
|
||||
|
||||
interface BotKey {
|
||||
encryptedKey: string;
|
||||
@ -19,20 +19,20 @@ export const getBotByWorkspaceId = async (req: Request, res: Response) => {
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
let bot = await Bot.findOne({
|
||||
workspace: workspaceId
|
||||
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: new Types.ObjectId(workspaceId)
|
||||
name: "Infisical Bot",
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
bot
|
||||
bot,
|
||||
});
|
||||
};
|
||||
|
||||
@ -49,40 +49,40 @@ export const setBotActiveState = async (req: Request, res: Response) => {
|
||||
// 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'
|
||||
message: "Failed to set bot state to active - missing bot key",
|
||||
});
|
||||
}
|
||||
|
||||
await BotKey.findOneAndUpdate({
|
||||
workspace: req.bot.workspace
|
||||
workspace: req.bot.workspace,
|
||||
}, {
|
||||
encryptedKey: botKey.encryptedKey,
|
||||
nonce: botKey.nonce,
|
||||
sender: req.user._id,
|
||||
bot: req.bot._id,
|
||||
workspace: req.bot.workspace
|
||||
workspace: req.bot.workspace,
|
||||
}, {
|
||||
upsert: true,
|
||||
new: true
|
||||
new: true,
|
||||
});
|
||||
} else {
|
||||
// case: bot state set to inactive -> delete bot's workspace key
|
||||
await BotKey.deleteOne({
|
||||
bot: req.bot._id
|
||||
bot: req.bot._id,
|
||||
});
|
||||
}
|
||||
|
||||
let bot = await Bot.findOneAndUpdate({
|
||||
_id: req.bot._id
|
||||
const bot = await Bot.findOneAndUpdate({
|
||||
_id: req.bot._id,
|
||||
}, {
|
||||
isActive
|
||||
isActive,
|
||||
}, {
|
||||
new: true
|
||||
new: true,
|
||||
});
|
||||
|
||||
if (!bot) throw new Error('Failed to update bot active state');
|
||||
if (!bot) throw new Error("Failed to update bot active state");
|
||||
|
||||
return res.status(200).send({
|
||||
bot
|
||||
bot,
|
||||
});
|
||||
};
|
||||
|
@ -1,19 +1,18 @@
|
||||
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';
|
||||
import * as membershipController from './membershipController';
|
||||
import * as membershipOrgController from './membershipOrgController';
|
||||
import * as organizationController from './organizationController';
|
||||
import * as passwordController from './passwordController';
|
||||
import * as secretController from './secretController';
|
||||
import * as serviceTokenController from './serviceTokenController';
|
||||
import * as signupController from './signupController';
|
||||
import * as stripeController from './stripeController';
|
||||
import * as userActionController from './userActionController';
|
||||
import * as userController from './userController';
|
||||
import * as workspaceController from './workspaceController';
|
||||
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";
|
||||
import * as membershipController from "./membershipController";
|
||||
import * as membershipOrgController from "./membershipOrgController";
|
||||
import * as organizationController from "./organizationController";
|
||||
import * as passwordController from "./passwordController";
|
||||
import * as secretController from "./secretController";
|
||||
import * as serviceTokenController from "./serviceTokenController";
|
||||
import * as signupController from "./signupController";
|
||||
import * as userActionController from "./userActionController";
|
||||
import * as userController from "./userController";
|
||||
import * as workspaceController from "./workspaceController";
|
||||
|
||||
export {
|
||||
authController,
|
||||
@ -28,8 +27,7 @@ export {
|
||||
secretController,
|
||||
serviceTokenController,
|
||||
signupController,
|
||||
stripeController,
|
||||
userActionController,
|
||||
userController,
|
||||
workspaceController
|
||||
workspaceController,
|
||||
};
|
||||
|
@ -1,21 +1,17 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { standardRequest } from "../../config/request";
|
||||
import { getApps, getTeams, revokeAccess } from "../../integrations";
|
||||
import { Bot, IntegrationAuth } from "../../models";
|
||||
import { IntegrationService } from "../../services";
|
||||
import {
|
||||
IntegrationAuth,
|
||||
Bot
|
||||
} from '../../models';
|
||||
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, INTEGRATION_SET, getIntegrationOptions as getIntegrationOptionsFunc } from '../../variables';
|
||||
import { IntegrationService } from '../../services';
|
||||
import {
|
||||
getApps,
|
||||
getTeams,
|
||||
revokeAccess
|
||||
} from '../../integrations';
|
||||
import {
|
||||
INTEGRATION_VERCEL_API_URL,
|
||||
INTEGRATION_RAILWAY_API_URL
|
||||
} from '../../variables';
|
||||
import { standardRequest } from '../../config/request';
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
INTEGRATION_RAILWAY_API_URL,
|
||||
INTEGRATION_SET,
|
||||
INTEGRATION_VERCEL_API_URL,
|
||||
getIntegrationOptions as getIntegrationOptionsFunc
|
||||
} from "../../variables";
|
||||
|
||||
/***
|
||||
* Return integration authorization with id [integrationAuthId]
|
||||
@ -23,22 +19,23 @@ import { standardRequest } from '../../config/request';
|
||||
export const getIntegrationAuth = async (req: Request, res: Response) => {
|
||||
const { integrationAuthId } = req.params;
|
||||
const integrationAuth = await IntegrationAuth.findById(integrationAuthId);
|
||||
|
||||
if (!integrationAuth) return res.status(400).send({
|
||||
message: 'Failed to find integration authorization'
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
});
|
||||
}
|
||||
if (!integrationAuth)
|
||||
return res.status(400).send({
|
||||
message: "Failed to find integration authorization"
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
});
|
||||
};
|
||||
|
||||
export const getIntegrationOptions = async (req: Request, res: Response) => {
|
||||
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
|
||||
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
|
||||
|
||||
return res.status(200).send({
|
||||
integrationOptions: INTEGRATION_OPTIONS,
|
||||
});
|
||||
return res.status(200).send({
|
||||
integrationOptions: INTEGRATION_OPTIONS
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -47,26 +44,22 @@ export const getIntegrationOptions = async (req: Request, res: Response) => {
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const oAuthExchange = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => {
|
||||
export const oAuthExchange = async (req: Request, res: Response) => {
|
||||
const { workspaceId, code, integration } = req.body;
|
||||
if (!INTEGRATION_SET.has(integration))
|
||||
throw new Error('Failed to validate integration');
|
||||
|
||||
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")
|
||||
if (environments.length === 0) {
|
||||
throw new Error("Failed to get environments");
|
||||
}
|
||||
|
||||
const integrationAuth = await IntegrationService.handleOAuthExchange({
|
||||
workspaceId,
|
||||
integration,
|
||||
code,
|
||||
environment: environments[0].slug,
|
||||
environment: environments[0].slug
|
||||
});
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
});
|
||||
@ -75,69 +68,70 @@ export const oAuthExchange = async (
|
||||
/**
|
||||
* Save integration access token and (optionally) access id as part of integration
|
||||
* [integration] for workspace with id [workspaceId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const saveIntegrationAccessToken = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => {
|
||||
// TODO: refactor
|
||||
// TODO: check if access token is valid for each integration
|
||||
export const saveIntegrationAccessToken = async (req: Request, res: Response) => {
|
||||
// TODO: refactor
|
||||
// TODO: check if access token is valid for each integration
|
||||
|
||||
let integrationAuth;
|
||||
const {
|
||||
workspaceId,
|
||||
accessId,
|
||||
accessToken,
|
||||
url,
|
||||
namespace,
|
||||
integration
|
||||
}: {
|
||||
workspaceId: string;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
url: string;
|
||||
namespace: string;
|
||||
integration: string;
|
||||
} = req.body;
|
||||
let integrationAuth;
|
||||
const {
|
||||
workspaceId,
|
||||
accessId,
|
||||
accessToken,
|
||||
url,
|
||||
namespace,
|
||||
integration
|
||||
}: {
|
||||
workspaceId: string;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
url: string;
|
||||
namespace: 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');
|
||||
const bot = await Bot.findOne({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
isActive: true
|
||||
});
|
||||
|
||||
integrationAuth = await IntegrationAuth.findOneAndUpdate({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration
|
||||
}, {
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration,
|
||||
url,
|
||||
namespace,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
}, {
|
||||
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');
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
});
|
||||
}
|
||||
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,
|
||||
url,
|
||||
namespace,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
},
|
||||
{
|
||||
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");
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of applications allowed for integration with integration authorization id [integrationAuthId]
|
||||
@ -147,108 +141,108 @@ export const saveIntegrationAccessToken = async (
|
||||
*/
|
||||
export const getIntegrationAuthApps = async (req: Request, res: Response) => {
|
||||
const teamId = req.query.teamId as string;
|
||||
|
||||
|
||||
const apps = await getApps({
|
||||
integrationAuth: req.integrationAuth,
|
||||
accessToken: req.accessToken,
|
||||
accessId: req.accessId,
|
||||
...teamId && { teamId }
|
||||
accessId: req.accessId,
|
||||
...(teamId && { teamId })
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
apps
|
||||
});
|
||||
return res.status(200).send({
|
||||
apps
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of teams allowed for integration with integration authorization id [integrationAuthId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getIntegrationAuthTeams = async (req: Request, res: Response) => {
|
||||
const teams = await getTeams({
|
||||
integrationAuth: req.integrationAuth,
|
||||
accessToken: req.accessToken
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
teams
|
||||
});
|
||||
}
|
||||
const teams = await getTeams({
|
||||
integrationAuth: req.integrationAuth,
|
||||
accessToken: req.accessToken
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
teams
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of available Vercel (preview) branches for Vercel project with
|
||||
* id [appId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getIntegrationAuthVercelBranches = async (req: Request, res: Response) => {
|
||||
const { integrationAuthId } = req.params;
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
interface VercelBranch {
|
||||
ref: string;
|
||||
lastCommit: string;
|
||||
isProtected: boolean;
|
||||
}
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
const params = new URLSearchParams({
|
||||
projectId: appId,
|
||||
...(req.integrationAuth.teamId ? {
|
||||
teamId: req.integrationAuth.teamId
|
||||
} : {})
|
||||
});
|
||||
interface VercelBranch {
|
||||
ref: string;
|
||||
lastCommit: string;
|
||||
isProtected: boolean;
|
||||
}
|
||||
|
||||
let branches: string[] = [];
|
||||
|
||||
if (appId && appId !== '') {
|
||||
const { data }: { data: VercelBranch[] } = await standardRequest.get(
|
||||
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${req.accessToken}`,
|
||||
'Accept-Encoding': 'application/json'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
branches = data.map((b) => b.ref);
|
||||
}
|
||||
const params = new URLSearchParams({
|
||||
projectId: appId,
|
||||
...(req.integrationAuth.teamId
|
||||
? {
|
||||
teamId: req.integrationAuth.teamId
|
||||
}
|
||||
: {})
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
branches
|
||||
});
|
||||
}
|
||||
let branches: string[] = [];
|
||||
|
||||
if (appId && appId !== "") {
|
||||
const { data }: { data: VercelBranch[] } = await standardRequest.get(
|
||||
`${INTEGRATION_VERCEL_API_URL}/v1/integrations/git-branches`,
|
||||
{
|
||||
params,
|
||||
headers: {
|
||||
Authorization: `Bearer ${req.accessToken}`,
|
||||
"Accept-Encoding": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
branches = data.map((b) => b.ref);
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
branches
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of Railway environments for Railway project with
|
||||
* id [appId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: Response) => {
|
||||
const { integrationAuthId } = req.params;
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
interface RailwayEnvironment {
|
||||
node: {
|
||||
id: string;
|
||||
name: string;
|
||||
isEphemeral: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
interface Environment {
|
||||
environmentId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
let environments: Environment[] = [];
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
if (appId && appId !== '') {
|
||||
const query = `
|
||||
interface RailwayEnvironment {
|
||||
node: {
|
||||
id: string;
|
||||
name: string;
|
||||
isEphemeral: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
interface Environment {
|
||||
environmentId: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
let environments: Environment[] = [];
|
||||
|
||||
if (appId && appId !== "") {
|
||||
const query = `
|
||||
query GetEnvironments($projectId: String!, $after: String, $before: String, $first: Int, $isEphemeral: Boolean, $last: Int) {
|
||||
environments(projectId: $projectId, after: $after, before: $before, first: $first, isEphemeral: $isEphemeral, last: $last) {
|
||||
edges {
|
||||
@ -261,59 +255,68 @@ export const getIntegrationAuthRailwayEnvironments = async (req: Request, res: R
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const variables = {
|
||||
projectId: appId
|
||||
}
|
||||
|
||||
const { data: { data: { environments: { edges } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
|
||||
query,
|
||||
variables,
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${req.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
environments = edges.map((e: RailwayEnvironment) => {
|
||||
return ({
|
||||
name: e.node.name,
|
||||
environmentId: e.node.id
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
environments
|
||||
});
|
||||
}
|
||||
|
||||
const variables = {
|
||||
projectId: appId
|
||||
};
|
||||
|
||||
const {
|
||||
data: {
|
||||
data: {
|
||||
environments: { edges }
|
||||
}
|
||||
}
|
||||
} = await standardRequest.post(
|
||||
INTEGRATION_RAILWAY_API_URL,
|
||||
{
|
||||
query,
|
||||
variables
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${req.accessToken}`,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
environments = edges.map((e: RailwayEnvironment) => {
|
||||
return {
|
||||
name: e.node.name,
|
||||
environmentId: e.node.id
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
environments
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return list of Railway services for Railway project with id
|
||||
* [appId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const getIntegrationAuthRailwayServices = async (req: Request, res: Response) => {
|
||||
const { integrationAuthId } = req.params;
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
interface RailwayService {
|
||||
node: {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
}
|
||||
|
||||
interface Service {
|
||||
name: string;
|
||||
serviceId: string;
|
||||
}
|
||||
|
||||
let services: Service[] = [];
|
||||
|
||||
const query = `
|
||||
const appId = req.query.appId as string;
|
||||
|
||||
interface RailwayService {
|
||||
node: {
|
||||
id: string;
|
||||
name: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Service {
|
||||
name: string;
|
||||
serviceId: string;
|
||||
}
|
||||
|
||||
let services: Service[] = [];
|
||||
|
||||
const query = `
|
||||
query project($id: String!) {
|
||||
project(id: $id) {
|
||||
createdAt
|
||||
@ -341,31 +344,43 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
||||
}
|
||||
`;
|
||||
|
||||
if (appId && appId !== '') {
|
||||
const variables = {
|
||||
id: appId
|
||||
}
|
||||
|
||||
const { data: { data: { project: { services: { edges } } } } } = await standardRequest.post(INTEGRATION_RAILWAY_API_URL, {
|
||||
query,
|
||||
variables
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${req.accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
services = edges.map((e: RailwayService) => ({
|
||||
name: e.node.name,
|
||||
serviceId: e.node.id
|
||||
}));
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
services
|
||||
});
|
||||
}
|
||||
if (appId && appId !== "") {
|
||||
const variables = {
|
||||
id: appId
|
||||
};
|
||||
|
||||
const {
|
||||
data: {
|
||||
data: {
|
||||
project: {
|
||||
services: { edges }
|
||||
}
|
||||
}
|
||||
}
|
||||
} = await standardRequest.post(
|
||||
INTEGRATION_RAILWAY_API_URL,
|
||||
{
|
||||
query,
|
||||
variables
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${req.accessToken}`,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
services = edges.map((e: RailwayService) => ({
|
||||
name: e.node.name,
|
||||
serviceId: e.node.id
|
||||
}));
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
services
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete integration authorization with id [integrationAuthId]
|
||||
@ -376,10 +391,10 @@ export const getIntegrationAuthRailwayServices = async (req: Request, res: Respo
|
||||
export const deleteIntegrationAuth = async (req: Request, res: Response) => {
|
||||
const integrationAuth = await revokeAccess({
|
||||
integrationAuth: req.integrationAuth,
|
||||
accessToken: req.accessToken,
|
||||
accessToken: req.accessToken
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth,
|
||||
integrationAuth
|
||||
});
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Key } from '../../models';
|
||||
import { findMembership } from '../../helpers/membership';
|
||||
import { Request, Response } from "express";
|
||||
import { Key } from "../../models";
|
||||
import { findMembership } from "../../helpers/membership";
|
||||
|
||||
/**
|
||||
* Add (encrypted) copy of workspace key for workspace with id [workspaceId] for user with
|
||||
@ -16,11 +16,11 @@ export const uploadKey = async (req: Request, res: Response) => {
|
||||
// validate membership of receiver
|
||||
const receiverMembership = await findMembership({
|
||||
user: key.userId,
|
||||
workspace: workspaceId
|
||||
workspace: workspaceId,
|
||||
});
|
||||
|
||||
if (!receiverMembership) {
|
||||
throw new Error('Failed receiver membership validation for workspace');
|
||||
throw new Error("Failed receiver membership validation for workspace");
|
||||
}
|
||||
|
||||
await new Key({
|
||||
@ -28,11 +28,11 @@ export const uploadKey = async (req: Request, res: Response) => {
|
||||
nonce: key.nonce,
|
||||
sender: req.user._id,
|
||||
receiver: key.userId,
|
||||
workspace: workspaceId
|
||||
workspace: workspaceId,
|
||||
}).save();
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully uploaded key to workspace'
|
||||
message: "Successfully uploaded key to workspace",
|
||||
});
|
||||
};
|
||||
|
||||
@ -48,16 +48,16 @@ export const getLatestKey = async (req: Request, res: Response) => {
|
||||
// get latest key
|
||||
const latestKey = await Key.find({
|
||||
workspace: workspaceId,
|
||||
receiver: req.user._id
|
||||
receiver: req.user._id,
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.limit(1)
|
||||
.populate('sender', '+publicKey');
|
||||
.populate("sender", "+publicKey");
|
||||
|
||||
const resObj: any = {};
|
||||
|
||||
if (latestKey.length > 0) {
|
||||
resObj['latestKey'] = latestKey[0];
|
||||
resObj["latestKey"] = latestKey[0];
|
||||
}
|
||||
|
||||
return res.status(200).send(resObj);
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Membership, MembershipOrg, User, Key } from '../../models';
|
||||
import {
|
||||
findMembership,
|
||||
deleteMembership as deleteMember
|
||||
} from '../../helpers/membership';
|
||||
import { sendMail } from '../../helpers/nodemailer';
|
||||
import { ADMIN, MEMBER, ACCEPTED } from '../../variables';
|
||||
import { getSiteURL } from '../../config';
|
||||
import { Request, Response } from "express";
|
||||
import { Key, Membership, MembershipOrg, User } from "../../models";
|
||||
import { deleteMembership as deleteMember, findMembership } from "../../helpers/membership";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { ACCEPTED, ADMIN, MEMBER } from "../../variables";
|
||||
import { getSiteURL } from "../../config";
|
||||
|
||||
/**
|
||||
* Check that user is a member of workspace with id [workspaceId]
|
||||
@ -23,12 +20,12 @@ export const validateMembership = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membership) {
|
||||
throw new Error('Failed to validate membership');
|
||||
throw new Error("Failed to validate membership");
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Workspace membership confirmed'
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: "Workspace membership confirmed"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -43,12 +40,10 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
||||
// check if membership to delete exists
|
||||
const membershipToDelete = await Membership.findOne({
|
||||
_id: membershipId
|
||||
}).populate('user');
|
||||
}).populate("user");
|
||||
|
||||
if (!membershipToDelete) {
|
||||
throw new Error(
|
||||
"Failed to delete workspace membership that doesn't exist"
|
||||
);
|
||||
throw new Error("Failed to delete workspace membership that doesn't exist");
|
||||
}
|
||||
|
||||
// check if user is a member and admin of the workspace
|
||||
@ -59,12 +54,12 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membership) {
|
||||
throw new Error('Failed to validate workspace membership');
|
||||
throw new Error("Failed to validate workspace membership");
|
||||
}
|
||||
|
||||
if (membership.role !== ADMIN) {
|
||||
// user is not an admin member of the workspace
|
||||
throw new Error('Insufficient role for deleting workspace membership');
|
||||
throw new Error("Insufficient role for deleting workspace membership");
|
||||
}
|
||||
|
||||
// delete workspace membership
|
||||
@ -72,9 +67,9 @@ export const deleteMembership = async (req: Request, res: Response) => {
|
||||
membershipId: membershipToDelete._id.toString()
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
deletedMembership
|
||||
});
|
||||
return res.status(200).send({
|
||||
deletedMembership
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -88,7 +83,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||
const { role } = req.body;
|
||||
|
||||
if (![ADMIN, MEMBER].includes(role)) {
|
||||
throw new Error('Failed to validate role');
|
||||
throw new Error("Failed to validate role");
|
||||
}
|
||||
|
||||
// validate target membership
|
||||
@ -97,7 +92,7 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membershipToChangeRole) {
|
||||
throw new Error('Failed to find membership to change role');
|
||||
throw new Error("Failed to find membership to change role");
|
||||
}
|
||||
|
||||
// check if user is a member and admin of target membership's
|
||||
@ -108,20 +103,20 @@ export const changeMembershipRole = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membership) {
|
||||
throw new Error('Failed to validate membership');
|
||||
throw new Error("Failed to validate membership");
|
||||
}
|
||||
|
||||
if (membership.role !== ADMIN) {
|
||||
// user is not an admin member of the workspace
|
||||
throw new Error('Insufficient role for changing member roles');
|
||||
throw new Error("Insufficient role for changing member roles");
|
||||
}
|
||||
|
||||
membershipToChangeRole.role = role;
|
||||
await membershipToChangeRole.save();
|
||||
|
||||
return res.status(200).send({
|
||||
membership: membershipToChangeRole
|
||||
});
|
||||
return res.status(200).send({
|
||||
membership: membershipToChangeRole
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -136,10 +131,9 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
||||
|
||||
const invitee = await User.findOne({
|
||||
email
|
||||
}).select('+publicKey');
|
||||
}).select("+publicKey");
|
||||
|
||||
if (!invitee || !invitee?.publicKey)
|
||||
throw new Error('Failed to validate invitee');
|
||||
if (!invitee || !invitee?.publicKey) throw new Error("Failed to validate invitee");
|
||||
|
||||
// validate invitee's workspace membership - ensure member isn't
|
||||
// already a member of the workspace
|
||||
@ -148,8 +142,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
||||
workspace: workspaceId
|
||||
});
|
||||
|
||||
if (inviteeMembership)
|
||||
throw new Error('Failed to add existing member of workspace');
|
||||
if (inviteeMembership) throw new Error("Failed to add existing member of workspace");
|
||||
|
||||
// validate invitee's organization membership - ensure that only
|
||||
// (accepted) organization members can be added to the workspace
|
||||
@ -159,8 +152,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
||||
status: ACCEPTED
|
||||
});
|
||||
|
||||
if (!membershipOrg)
|
||||
throw new Error("Failed to validate invitee's organization membership");
|
||||
if (!membershipOrg) throw new Error("Failed to validate invitee's organization membership");
|
||||
|
||||
// get latest key
|
||||
const latestKey = await Key.findOne({
|
||||
@ -168,29 +160,29 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
||||
receiver: req.user._id
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.populate('sender', '+publicKey');
|
||||
.populate("sender", "+publicKey");
|
||||
|
||||
// create new workspace membership
|
||||
const m = await new Membership({
|
||||
await new Membership({
|
||||
user: invitee._id,
|
||||
workspace: workspaceId,
|
||||
role: MEMBER
|
||||
}).save();
|
||||
|
||||
await sendMail({
|
||||
template: 'workspaceInvitation.handlebars',
|
||||
subjectLine: 'Infisical workspace invitation',
|
||||
template: "workspaceInvitation.handlebars",
|
||||
subjectLine: "Infisical workspace invitation",
|
||||
recipients: [invitee.email],
|
||||
substitutions: {
|
||||
inviterFirstName: req.user.firstName,
|
||||
inviterEmail: req.user.email,
|
||||
workspaceName: req.membership.workspace.name,
|
||||
callback_url: (await getSiteURL()) + '/login'
|
||||
callback_url: (await getSiteURL()) + "/login"
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
invitee,
|
||||
latestKey
|
||||
});
|
||||
return res.status(200).send({
|
||||
invitee,
|
||||
latestKey
|
||||
});
|
||||
};
|
||||
|
@ -1,15 +1,27 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Request, Response } from 'express';
|
||||
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 { EELicenseService } from '../../ee/services';
|
||||
import { OWNER, ADMIN, MEMBER, ACCEPTED, INVITED, TOKEN_EMAIL_ORG_INVITATION } from '../../variables';
|
||||
import { getSiteURL, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
||||
import { validateUserEmail } from '../../validation';
|
||||
import { Types } from "mongoose";
|
||||
import { Request, Response } from "express";
|
||||
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 { EELicenseService } from "../../ee/services";
|
||||
import {
|
||||
ACCEPTED,
|
||||
ADMIN,
|
||||
INVITED,
|
||||
MEMBER,
|
||||
OWNER,
|
||||
TOKEN_EMAIL_ORG_INVITATION
|
||||
} from "../../variables";
|
||||
import {
|
||||
getJwtSignupLifetime,
|
||||
getJwtSignupSecret,
|
||||
getSiteURL,
|
||||
getSmtpConfigured
|
||||
} from "../../config";
|
||||
import { validateUserEmail } from "../../validation";
|
||||
|
||||
/**
|
||||
* Delete organization membership with id [membershipOrgId] from organization
|
||||
@ -17,18 +29,16 @@ import { validateUserEmail } from '../../validation';
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
||||
export const deleteMembershipOrg = async (req: Request, _res: Response) => {
|
||||
const { membershipOrgId } = req.params;
|
||||
|
||||
// check if organization membership to delete exists
|
||||
const membershipOrgToDelete = await MembershipOrg.findOne({
|
||||
_id: membershipOrgId
|
||||
}).populate('user');
|
||||
}).populate("user");
|
||||
|
||||
if (!membershipOrgToDelete) {
|
||||
throw new Error(
|
||||
"Failed to delete organization membership that doesn't exist"
|
||||
);
|
||||
throw new Error("Failed to delete organization membership that doesn't exist");
|
||||
}
|
||||
|
||||
// check if user is a member and admin of the organization
|
||||
@ -39,16 +49,16 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membershipOrg) {
|
||||
throw new Error('Failed to validate organization membership');
|
||||
throw new Error("Failed to validate organization membership");
|
||||
}
|
||||
|
||||
if (membershipOrg.role !== OWNER && membershipOrg.role !== ADMIN) {
|
||||
// user is not an admin member of the organization
|
||||
throw new Error('Insufficient role for deleting organization membership');
|
||||
throw new Error("Insufficient role for deleting organization membership");
|
||||
}
|
||||
|
||||
// delete organization membership
|
||||
const deletedMembershipOrg = await deleteMemberFromOrg({
|
||||
await deleteMemberFromOrg({
|
||||
membershipOrgId: membershipOrgToDelete._id.toString()
|
||||
});
|
||||
|
||||
@ -56,7 +66,7 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
||||
organizationId: membershipOrg.organization.toString()
|
||||
});
|
||||
|
||||
return membershipOrgToDelete;
|
||||
return membershipOrgToDelete;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -66,14 +76,14 @@ export const deleteMembershipOrg = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
||||
// change role for (target) organization membership with id
|
||||
// [membershipOrgId]
|
||||
// change role for (target) organization membership with id
|
||||
// [membershipOrgId]
|
||||
|
||||
let membershipToChangeRole;
|
||||
let membershipToChangeRole;
|
||||
|
||||
return res.status(200).send({
|
||||
membershipOrg: membershipToChangeRole
|
||||
});
|
||||
return res.status(200).send({
|
||||
membershipOrg: membershipToChangeRole
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -84,7 +94,7 @@ export const changeMembershipOrgRole = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
let invitee, inviteeMembershipOrg, completeInviteLink;
|
||||
let inviteeMembershipOrg, completeInviteLink;
|
||||
const { organizationId, inviteeEmail } = req.body;
|
||||
const host = req.headers.host;
|
||||
const siteUrl = `${req.protocol}://${host}`;
|
||||
@ -96,25 +106,26 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (!membershipOrg) {
|
||||
throw new Error('Failed to validate organization membership');
|
||||
throw new Error("Failed to validate organization membership");
|
||||
}
|
||||
|
||||
|
||||
const plan = await EELicenseService.getPlan(organizationId);
|
||||
|
||||
|
||||
if (plan.memberLimit !== null) {
|
||||
// case: limit imposed on number of members allowed
|
||||
|
||||
|
||||
if (plan.membersUsed >= plan.memberLimit) {
|
||||
// case: number of members used exceeds the number of members allowed
|
||||
return res.status(400).send({
|
||||
message: 'Failed to invite member due to member limit reached. Upgrade plan to invite more members.'
|
||||
message:
|
||||
"Failed to invite member due to member limit reached. Upgrade plan to invite more members."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
invitee = await User.findOne({
|
||||
const invitee = await User.findOne({
|
||||
email: inviteeEmail
|
||||
}).select('+publicKey');
|
||||
}).select("+publicKey");
|
||||
|
||||
if (invitee) {
|
||||
// case: invitee is an existing user
|
||||
@ -125,13 +136,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
if (inviteeMembershipOrg && inviteeMembershipOrg.status === ACCEPTED) {
|
||||
throw new Error(
|
||||
'Failed to invite an existing member of the organization'
|
||||
);
|
||||
throw new Error("Failed to invite an existing member of the organization");
|
||||
}
|
||||
|
||||
if (!inviteeMembershipOrg) {
|
||||
|
||||
await new MembershipOrg({
|
||||
user: invitee,
|
||||
inviteEmail: inviteeEmail,
|
||||
@ -149,7 +157,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
|
||||
if (!inviteeMembershipOrg) {
|
||||
// case: invitee has never been invited before
|
||||
|
||||
|
||||
// validate that email is not disposable
|
||||
validateUserEmail(inviteeEmail);
|
||||
|
||||
@ -165,7 +173,6 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
const organization = await Organization.findOne({ _id: organizationId });
|
||||
|
||||
if (organization) {
|
||||
|
||||
const token = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_ORG_INVITATION,
|
||||
email: inviteeEmail,
|
||||
@ -173,8 +180,8 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
await sendMail({
|
||||
template: 'organizationInvitation.handlebars',
|
||||
subjectLine: 'Infisical organization invitation',
|
||||
template: "organizationInvitation.handlebars",
|
||||
subjectLine: "Infisical organization invitation",
|
||||
recipients: [inviteeEmail],
|
||||
substitutions: {
|
||||
inviterFirstName: req.user.firstName,
|
||||
@ -183,21 +190,23 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
email: inviteeEmail,
|
||||
organizationId: organization._id.toString(),
|
||||
token,
|
||||
callback_url: (await getSiteURL()) + '/signupinvite'
|
||||
callback_url: (await getSiteURL()) + "/signupinvite"
|
||||
}
|
||||
});
|
||||
|
||||
if (!(await getSmtpConfigured())) {
|
||||
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`
|
||||
completeInviteLink = `${
|
||||
siteUrl + "/signupinvite"
|
||||
}?token=${token}&to=${inviteeEmail}&organization_id=${organization._id}`;
|
||||
}
|
||||
}
|
||||
|
||||
await updateSubscriptionOrgQuantity({ organizationId });
|
||||
|
||||
return res.status(200).send({
|
||||
message: `Sent an invite link to ${req.body.inviteeEmail}`,
|
||||
completeInviteLink
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: `Sent an invite link to ${req.body.inviteeEmail}`,
|
||||
completeInviteLink
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -208,14 +217,10 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
||||
let user;
|
||||
const {
|
||||
email,
|
||||
organizationId,
|
||||
code
|
||||
} = req.body;
|
||||
let user;
|
||||
const { email, organizationId, code } = req.body;
|
||||
|
||||
user = await User.findOne({ email }).select('+publicKey');
|
||||
user = await User.findOne({ email }).select("+publicKey");
|
||||
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
inviteEmail: email,
|
||||
@ -223,8 +228,7 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
});
|
||||
|
||||
if (!membershipOrg)
|
||||
throw new Error('Failed to find any invitations for email');
|
||||
if (!membershipOrg) throw new Error("Failed to find any invitations for email");
|
||||
|
||||
await TokenService.validateToken({
|
||||
type: TOKEN_EMAIL_ORG_INVITATION,
|
||||
@ -238,14 +242,14 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
||||
// membership can be approved and redirected to login/dashboard
|
||||
membershipOrg.status = ACCEPTED;
|
||||
await membershipOrg.save();
|
||||
|
||||
|
||||
await updateSubscriptionOrgQuantity({
|
||||
organizationId
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully verified email',
|
||||
user,
|
||||
message: "Successfully verified email",
|
||||
user
|
||||
});
|
||||
}
|
||||
|
||||
@ -265,9 +269,9 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
||||
secret: await getJwtSignupSecret()
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully verified email',
|
||||
user,
|
||||
token
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: "Successfully verified email",
|
||||
user,
|
||||
token
|
||||
});
|
||||
};
|
||||
|
@ -1,28 +1,27 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
import { Request, Response } from "express";
|
||||
import {
|
||||
IncidentContactOrg,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
Organization,
|
||||
Workspace,
|
||||
IncidentContactOrg
|
||||
} from '../../models';
|
||||
import { createOrganization as create } from '../../helpers/organization';
|
||||
import { addMembershipsOrg } from '../../helpers/membershipOrg';
|
||||
import { OWNER, ACCEPTED } from '../../variables';
|
||||
import _ from 'lodash';
|
||||
import { getStripeSecretKey, getSiteURL } from '../../config';
|
||||
} from "../../models";
|
||||
import { createOrganization as create } from "../../helpers/organization";
|
||||
import { addMembershipsOrg } from "../../helpers/membershipOrg";
|
||||
import { ACCEPTED, OWNER } from "../../variables";
|
||||
import { getSiteURL, getLicenseServerUrl } from "../../config";
|
||||
import { licenseServerKeyRequest } from "../../config/request";
|
||||
|
||||
export const getOrganizations = async (req: Request, res: Response) => {
|
||||
const organizations = (
|
||||
await MembershipOrg.find({
|
||||
user: req.user._id,
|
||||
status: ACCEPTED
|
||||
}).populate('organization')
|
||||
status: ACCEPTED,
|
||||
}).populate("organization")
|
||||
).map((m) => m.organization);
|
||||
|
||||
return res.status(200).send({
|
||||
organizations
|
||||
organizations,
|
||||
});
|
||||
};
|
||||
|
||||
@ -37,24 +36,24 @@ export const createOrganization = async (req: Request, res: Response) => {
|
||||
const { organizationName } = req.body;
|
||||
|
||||
if (organizationName.length < 1) {
|
||||
throw new Error('Organization names must be at least 1-character long');
|
||||
throw new Error("Organization names must be at least 1-character long");
|
||||
}
|
||||
|
||||
// create organization and add user as member
|
||||
const organization = await create({
|
||||
email: req.user.email,
|
||||
name: organizationName
|
||||
name: organizationName,
|
||||
});
|
||||
|
||||
await addMembershipsOrg({
|
||||
userIds: [req.user._id.toString()],
|
||||
organizationId: organization._id.toString(),
|
||||
roles: [OWNER],
|
||||
statuses: [ACCEPTED]
|
||||
statuses: [ACCEPTED],
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
organization
|
||||
organization,
|
||||
});
|
||||
};
|
||||
|
||||
@ -67,7 +66,7 @@ export const createOrganization = async (req: Request, res: Response) => {
|
||||
export const getOrganization = async (req: Request, res: Response) => {
|
||||
const organization = req.organization
|
||||
return res.status(200).send({
|
||||
organization
|
||||
organization,
|
||||
});
|
||||
};
|
||||
|
||||
@ -81,11 +80,11 @@ export const getOrganizationMembers = async (req: Request, res: Response) => {
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const users = await MembershipOrg.find({
|
||||
organization: organizationId
|
||||
}).populate('user', '+publicKey');
|
||||
organization: organizationId,
|
||||
}).populate("user", "+publicKey");
|
||||
|
||||
return res.status(200).send({
|
||||
users
|
||||
users,
|
||||
});
|
||||
};
|
||||
|
||||
@ -105,23 +104,23 @@ export const getOrganizationWorkspaces = async (
|
||||
(
|
||||
await Workspace.find(
|
||||
{
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
},
|
||||
'_id'
|
||||
"_id"
|
||||
)
|
||||
).map((w) => w._id.toString())
|
||||
);
|
||||
|
||||
const workspaces = (
|
||||
await Membership.find({
|
||||
user: req.user._id
|
||||
}).populate('workspace')
|
||||
user: req.user._id,
|
||||
}).populate("workspace")
|
||||
)
|
||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||
.map((m) => m.workspace);
|
||||
|
||||
return res.status(200).send({
|
||||
workspaces
|
||||
workspaces,
|
||||
});
|
||||
};
|
||||
|
||||
@ -137,19 +136,19 @@ export const changeOrganizationName = async (req: Request, res: Response) => {
|
||||
|
||||
const organization = await Organization.findOneAndUpdate(
|
||||
{
|
||||
_id: organizationId
|
||||
_id: organizationId,
|
||||
},
|
||||
{
|
||||
name
|
||||
name,
|
||||
},
|
||||
{
|
||||
new: true
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully changed organization name',
|
||||
organization
|
||||
message: "Successfully changed organization name",
|
||||
organization,
|
||||
});
|
||||
};
|
||||
|
||||
@ -166,11 +165,11 @@ export const getOrganizationIncidentContacts = async (
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const incidentContactsOrg = await IncidentContactOrg.find({
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
incidentContactsOrg
|
||||
incidentContactsOrg,
|
||||
});
|
||||
};
|
||||
|
||||
@ -194,7 +193,7 @@ export const addOrganizationIncidentContact = async (
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
incidentContactOrg
|
||||
incidentContactOrg,
|
||||
});
|
||||
};
|
||||
|
||||
@ -213,17 +212,17 @@ export const deleteOrganizationIncidentContact = async (
|
||||
|
||||
const incidentContactOrg = await IncidentContactOrg.findOneAndDelete({
|
||||
email,
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully deleted organization incident contact',
|
||||
incidentContactOrg
|
||||
message: "Successfully deleted organization incident contact",
|
||||
incidentContactOrg,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect user to (stripe) billing portal or add card page depending on
|
||||
* Redirect user to billing portal or add card page depending on
|
||||
* if there is a card on file
|
||||
* @param req
|
||||
* @param res
|
||||
@ -233,34 +232,32 @@ export const createOrganizationPortalSession = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => {
|
||||
let session;
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: '2022-08-01'
|
||||
});
|
||||
|
||||
// check if there is a payment method on file
|
||||
const paymentMethods = await stripe.paymentMethods.list({
|
||||
customer: req.organization.customerId,
|
||||
type: 'card'
|
||||
});
|
||||
const { data: { pmtMethods } } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
|
||||
);
|
||||
|
||||
if (paymentMethods.data.length < 1) {
|
||||
// case: no payment method on file
|
||||
session = await stripe.checkout.sessions.create({
|
||||
customer: req.organization.customerId,
|
||||
mode: 'setup',
|
||||
payment_method_types: ['card'],
|
||||
success_url: (await getSiteURL()) + '/dashboard',
|
||||
cancel_url: (await getSiteURL()) + '/dashboard'
|
||||
});
|
||||
if (pmtMethods.length < 1) {
|
||||
// case: organization has no payment method on file
|
||||
// -> redirect to add payment method portal
|
||||
const { data: { url } } = await licenseServerKeyRequest.post(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
|
||||
{
|
||||
success_url: (await getSiteURL()) + "/dashboard",
|
||||
cancel_url: (await getSiteURL()) + "/dashboard"
|
||||
}
|
||||
);
|
||||
return res.status(200).send({ url });
|
||||
} else {
|
||||
session = await stripe.billingPortal.sessions.create({
|
||||
customer: req.organization.customerId,
|
||||
return_url: (await getSiteURL()) + '/dashboard'
|
||||
});
|
||||
// case: organization has payment method on file
|
||||
// -> redirect to billing portal
|
||||
const { data: { url } } = await licenseServerKeyRequest.post(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/billing-portal`,
|
||||
{
|
||||
return_url: (await getSiteURL()) + "/dashboard"
|
||||
}
|
||||
);
|
||||
return res.status(200).send({ url });
|
||||
}
|
||||
|
||||
return res.status(200).send({ url: session.url });
|
||||
};
|
||||
|
||||
/**
|
||||
@ -273,16 +270,8 @@ export const getOrganizationSubscriptions = async (
|
||||
req: Request,
|
||||
res: Response
|
||||
) => {
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: '2022-08-01'
|
||||
});
|
||||
|
||||
const subscriptions = await stripe.subscriptions.list({
|
||||
customer: req.organization.customerId
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
subscriptions
|
||||
subscriptions: []
|
||||
});
|
||||
};
|
||||
|
||||
@ -302,16 +291,16 @@ export const getOrganizationMembersAndTheirWorkspaces = async (
|
||||
const workspacesSet = (
|
||||
await Workspace.find(
|
||||
{
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
},
|
||||
'_id'
|
||||
"_id"
|
||||
)
|
||||
).map((w) => w._id.toString());
|
||||
|
||||
const memberships = (
|
||||
await Membership.find({
|
||||
workspace: { $in: workspacesSet }
|
||||
}).populate('workspace')
|
||||
workspace: { $in: workspacesSet },
|
||||
}).populate("workspace")
|
||||
);
|
||||
const userToWorkspaceIds: any = {};
|
||||
|
||||
|
@ -1,85 +1,77 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Request, Response } from "express";
|
||||
// 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';
|
||||
const jsrp = require("jsrp");
|
||||
import * as bigintConversion from "bigint-conversion";
|
||||
import { BackupPrivateKey, LoginSRPDetail, User } from "../../models";
|
||||
import { clearTokens, createToken, sendMail } from "../../helpers";
|
||||
import { TokenService } from "../../services";
|
||||
import { AUTH_MODE_JWT, TOKEN_EMAIL_PASSWORD_RESET } from "../../variables";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
import {
|
||||
createToken,
|
||||
sendMail,
|
||||
clearTokens
|
||||
} from '../../helpers';
|
||||
import { TokenService } from '../../services';
|
||||
import {
|
||||
TOKEN_EMAIL_PASSWORD_RESET,
|
||||
AUTH_MODE_JWT
|
||||
} from '../../variables';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import {
|
||||
getSiteURL,
|
||||
getJwtSignupLifetime,
|
||||
getJwtSignupSecret,
|
||||
getHttpsEnabled
|
||||
} from '../../config';
|
||||
getHttpsEnabled,
|
||||
getJwtSignupLifetime,
|
||||
getJwtSignupSecret,
|
||||
getSiteURL
|
||||
} from "../../config";
|
||||
|
||||
/**
|
||||
* Password reset step 1: Send email verification link to email [email]
|
||||
* 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;
|
||||
email = req.body.email;
|
||||
const email: string = req.body.email;
|
||||
|
||||
const user = await User.findOne({ email }).select('+publicKey');
|
||||
const user = await User.findOne({ email }).select("+publicKey");
|
||||
if (!user || !user?.publicKey) {
|
||||
// case: user has already completed account
|
||||
|
||||
return res.status(200).send({
|
||||
message:"If an account exists with this email, a password reset link has been sent"
|
||||
message: "If an account exists with this email, a password reset link has been sent"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const token = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_PASSWORD_RESET,
|
||||
email
|
||||
});
|
||||
|
||||
|
||||
await sendMail({
|
||||
template: 'passwordReset.handlebars',
|
||||
subjectLine: 'Infisical password reset',
|
||||
template: "passwordReset.handlebars",
|
||||
subjectLine: "Infisical password reset",
|
||||
recipients: [email],
|
||||
substitutions: {
|
||||
email,
|
||||
token,
|
||||
callback_url: (await getSiteURL()) + '/password-reset'
|
||||
callback_url: (await getSiteURL()) + "/password-reset"
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message:"If an account exists with this email, a password reset link has been sent"
|
||||
});
|
||||
}
|
||||
return res.status(200).send({
|
||||
message: "If an account exists with this email, a password reset link has been sent"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Password reset step 2: Verify email verification link sent to email [email]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||
const { email, code } = req.body;
|
||||
|
||||
const user = await User.findOne({ email }).select('+publicKey');
|
||||
const user = await User.findOne({ email }).select("+publicKey");
|
||||
if (!user || !user?.publicKey) {
|
||||
// case: user doesn't exist with email [email] or
|
||||
// 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'
|
||||
error: "Failed email verification for password reset"
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
await TokenService.validateToken({
|
||||
type: TOKEN_EMAIL_PASSWORD_RESET,
|
||||
email,
|
||||
@ -95,12 +87,12 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||
secret: await getJwtSignupSecret()
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully verified email',
|
||||
user,
|
||||
token
|
||||
});
|
||||
}
|
||||
return res.status(200).send({
|
||||
message: "Successfully verified email",
|
||||
user,
|
||||
token
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return [salt] and [serverPublicKey] as part of step 1 of SRP protocol
|
||||
@ -109,14 +101,14 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const srp1 = async (req: Request, res: Response) => {
|
||||
// return salt, serverPublicKey as part of first step of SRP protocol
|
||||
|
||||
// return salt, serverPublicKey as part of first step of SRP protocol
|
||||
|
||||
const { clientPublicKey } = req.body;
|
||||
const user = await User.findOne({
|
||||
email: req.user.email
|
||||
}).select('+salt +verifier');
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const server = new jsrp.server();
|
||||
server.init(
|
||||
@ -128,11 +120,15 @@ export const srp1 = async (req: Request, res: Response) => {
|
||||
// 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 })
|
||||
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,
|
||||
@ -140,8 +136,7 @@ export const srp1 = async (req: Request, res: Response) => {
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Change account SRP authentication information for user
|
||||
@ -152,8 +147,8 @@ export const srp1 = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const changePassword = async (req: Request, res: Response) => {
|
||||
const {
|
||||
clientProof,
|
||||
const {
|
||||
clientProof,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
@ -166,14 +161,18 @@ export const changePassword = async (req: Request, res: Response) => {
|
||||
|
||||
const user = await User.findOne({
|
||||
email: req.user.email
|
||||
}).select('+salt +verifier');
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
|
||||
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"))
|
||||
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();
|
||||
@ -207,27 +206,31 @@ export const changePassword = async (req: Request, res: Response) => {
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
if (req.authData.authMode === AUTH_MODE_JWT && req.authData.authPayload instanceof User && req.authData.tokenVersionId) {
|
||||
await clearTokens(req.authData.tokenVersionId)
|
||||
|
||||
if (
|
||||
req.authData.authMode === AUTH_MODE_JWT &&
|
||||
req.authData.authPayload instanceof User &&
|
||||
req.authData.tokenVersionId
|
||||
) {
|
||||
await clearTokens(req.authData.tokenVersionId);
|
||||
}
|
||||
|
||||
// clear httpOnly cookie
|
||||
|
||||
res.cookie('jid', '', {
|
||||
|
||||
res.cookie("jid", "", {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: (await getHttpsEnabled()) as boolean
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully changed password'
|
||||
message: "Successfully changed password"
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(400).send({
|
||||
error: 'Failed to change password. Try again?'
|
||||
error: "Failed to change password. Try again?"
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -240,22 +243,25 @@ export const changePassword = async (req: Request, res: Response) => {
|
||||
* @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
|
||||
// create/change backup private key
|
||||
// requires verifying [clientProof] as part of second step of SRP protocol
|
||||
// as initiated in /srp1
|
||||
|
||||
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } =
|
||||
req.body;
|
||||
const { clientProof, encryptedPrivateKey, iv, tag, salt, verifier } = req.body;
|
||||
const user = await User.findOne({
|
||||
email: req.user.email
|
||||
}).select('+salt +verifier');
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const loginSRPDetailFromDB = await LoginSRPDetail.findOneAndDelete({ email: req.user.email })
|
||||
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"))
|
||||
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();
|
||||
@ -266,9 +272,7 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
||||
b: loginSRPDetailFromDB.serverBInt
|
||||
},
|
||||
async () => {
|
||||
server.setClientPublicKey(
|
||||
loginSRPDetailFromDB.clientPublicKey
|
||||
);
|
||||
server.setClientPublicKey(loginSRPDetailFromDB.clientPublicKey);
|
||||
|
||||
// compare server and client shared keys
|
||||
if (server.checkClientProof(clientProof)) {
|
||||
@ -285,17 +289,17 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
||||
verifier
|
||||
},
|
||||
{ upsert: true, new: true }
|
||||
).select('+user, encryptedPrivateKey');
|
||||
).select("+user, encryptedPrivateKey");
|
||||
|
||||
// issue tokens
|
||||
return res.status(200).send({
|
||||
message: 'Successfully updated backup private key',
|
||||
message: "Successfully updated backup private key",
|
||||
backupPrivateKey
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(400).send({
|
||||
message: 'Failed to update backup private key'
|
||||
message: "Failed to update backup private key"
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -303,21 +307,21 @@ export const createBackupPrivateKey = async (req: Request, res: Response) => {
|
||||
|
||||
/**
|
||||
* Return backup private key for user
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getBackupPrivateKey = async (req: Request, res: Response) => {
|
||||
const backupPrivateKey = await BackupPrivateKey.findOne({
|
||||
user: req.user._id
|
||||
}).select('+encryptedPrivateKey +iv +tag');
|
||||
}).select("+encryptedPrivateKey +iv +tag");
|
||||
|
||||
if (!backupPrivateKey) throw new Error('Failed to find backup private key');
|
||||
if (!backupPrivateKey) throw new Error("Failed to find backup private key");
|
||||
|
||||
return res.status(200).send({
|
||||
backupPrivateKey
|
||||
});
|
||||
}
|
||||
return res.status(200).send({
|
||||
backupPrivateKey
|
||||
});
|
||||
};
|
||||
|
||||
export const resetPassword = async (req: Request, res: Response) => {
|
||||
const {
|
||||
@ -328,7 +332,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
verifier
|
||||
} = req.body;
|
||||
|
||||
await User.findByIdAndUpdate(
|
||||
@ -337,7 +341,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
||||
encryptionVersion: 2,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
protectedKeyTag,
|
||||
encryptedPrivateKey,
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
@ -349,7 +353,7 @@ export const resetPassword = async (req: Request, res: Response) => {
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully reset password'
|
||||
});
|
||||
}
|
||||
return res.status(200).send({
|
||||
message: "Successfully reset password"
|
||||
});
|
||||
};
|
||||
|
@ -1,30 +1,30 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Key, Secret } from '../../models';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { Key } from "../../models";
|
||||
import {
|
||||
v1PushSecrets as push,
|
||||
pullSecrets as pull,
|
||||
reformatPullSecrets
|
||||
} from '../../helpers/secret';
|
||||
import { pushKeys } from '../../helpers/key';
|
||||
import { eventPushSecrets } from '../../events';
|
||||
import { EventService } from '../../services';
|
||||
import { TelemetryService } from '../../services';
|
||||
pullSecrets as pull,
|
||||
v1PushSecrets as push,
|
||||
reformatPullSecrets
|
||||
} from "../../helpers/secret";
|
||||
import { pushKeys } from "../../helpers/key";
|
||||
import { eventPushSecrets } from "../../events";
|
||||
import { EventService } from "../../services";
|
||||
import { TelemetryService } from "../../services";
|
||||
|
||||
interface PushSecret {
|
||||
ciphertextKey: string;
|
||||
ivKey: string;
|
||||
tagKey: string;
|
||||
hashKey: string;
|
||||
ciphertextValue: string;
|
||||
ivValue: string;
|
||||
tagValue: string;
|
||||
hashValue: string;
|
||||
ciphertextComment: string;
|
||||
ivComment: string;
|
||||
tagComment: string;
|
||||
hashComment: string;
|
||||
type: 'shared' | 'personal';
|
||||
ciphertextKey: string;
|
||||
ivKey: string;
|
||||
tagKey: string;
|
||||
hashKey: string;
|
||||
ciphertextValue: string;
|
||||
ivValue: string;
|
||||
tagValue: string;
|
||||
hashValue: string;
|
||||
ciphertextComment: string;
|
||||
ivComment: string;
|
||||
tagComment: string;
|
||||
hashComment: string;
|
||||
type: "shared" | "personal";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +35,7 @@ interface PushSecret {
|
||||
* @returns
|
||||
*/
|
||||
export const pushSecrets = async (req: Request, res: Response) => {
|
||||
// upload (encrypted) secrets to workspace with id [workspaceId]
|
||||
// upload (encrypted) secrets to workspace with id [workspaceId]
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
let { secrets }: { secrets: PushSecret[] } = req.body;
|
||||
const { keys, environment, channel } = req.body;
|
||||
@ -44,13 +44,11 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
||||
// validate environment
|
||||
const workspaceEnvs = req.membership.workspace.environments;
|
||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||
throw new Error('Failed to validate environment');
|
||||
throw new Error("Failed to validate environment");
|
||||
}
|
||||
|
||||
// sanitize secrets
|
||||
secrets = secrets.filter(
|
||||
(s: PushSecret) => s.ciphertextKey !== '' && s.ciphertextValue !== ''
|
||||
);
|
||||
secrets = secrets.filter((s: PushSecret) => s.ciphertextKey !== "" && s.ciphertextValue !== "");
|
||||
|
||||
await push({
|
||||
userId: req.user._id,
|
||||
@ -64,17 +62,16 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
keys
|
||||
});
|
||||
|
||||
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets pushed',
|
||||
event: "secrets pushed",
|
||||
distinctId: req.user.email,
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
channel: channel ? channel : 'cli'
|
||||
channel: channel ? channel : "cli"
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -87,9 +84,9 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
||||
})
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully uploaded workspace secrets'
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: "Successfully uploaded workspace secrets"
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -100,57 +97,56 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const pullSecrets = async (req: Request, res: Response) => {
|
||||
let secrets;
|
||||
let key;
|
||||
let secrets;
|
||||
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const environment: string = req.query.environment as string;
|
||||
const channel: string = req.query.channel as string;
|
||||
const { workspaceId } = req.params;
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const environment: string = req.query.environment as string;
|
||||
const channel: string = req.query.channel as string;
|
||||
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');
|
||||
}
|
||||
// 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: req.user._id.toString(),
|
||||
workspaceId,
|
||||
environment,
|
||||
channel: channel ? channel : 'cli',
|
||||
ipAddress: req.realIP
|
||||
});
|
||||
secrets = await pull({
|
||||
userId: req.user._id.toString(),
|
||||
workspaceId,
|
||||
environment,
|
||||
channel: channel ? channel : "cli",
|
||||
ipAddress: req.realIP
|
||||
});
|
||||
|
||||
key = await Key.findOne({
|
||||
workspace: workspaceId,
|
||||
receiver: req.user._id
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.populate('sender', '+publicKey');
|
||||
|
||||
if (channel !== 'cli') {
|
||||
secrets = reformatPullSecrets({ secrets });
|
||||
}
|
||||
const key = await Key.findOne({
|
||||
workspace: workspaceId,
|
||||
receiver: req.user._id
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.populate("sender", "+publicKey");
|
||||
|
||||
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'
|
||||
}
|
||||
});
|
||||
}
|
||||
if (channel !== "cli") {
|
||||
secrets = reformatPullSecrets({ secrets });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
secrets,
|
||||
key
|
||||
});
|
||||
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"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
secrets,
|
||||
key
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -162,54 +158,51 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const pullSecretsServiceToken = async (req: Request, res: Response) => {
|
||||
let secrets;
|
||||
let key;
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const environment: string = req.query.environment as string;
|
||||
const channel: string = req.query.channel as string;
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const environment: string = req.query.environment as string;
|
||||
const channel: string = req.query.channel as string;
|
||||
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");
|
||||
}
|
||||
|
||||
// validate environment
|
||||
const workspaceEnvs = req.membership.workspace.environments;
|
||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||
throw new Error('Failed to validate environment');
|
||||
}
|
||||
const secrets = await pull({
|
||||
userId: req.serviceToken.user._id.toString(),
|
||||
workspaceId,
|
||||
environment,
|
||||
channel: "cli",
|
||||
ipAddress: req.realIP
|
||||
});
|
||||
|
||||
secrets = await pull({
|
||||
userId: req.serviceToken.user._id.toString(),
|
||||
workspaceId,
|
||||
environment,
|
||||
channel: 'cli',
|
||||
ipAddress: req.realIP
|
||||
});
|
||||
const key = {
|
||||
encryptedKey: req.serviceToken.encryptedKey,
|
||||
nonce: req.serviceToken.nonce,
|
||||
sender: {
|
||||
publicKey: req.serviceToken.publicKey
|
||||
},
|
||||
receiver: req.serviceToken.user,
|
||||
workspace: req.serviceToken.workspace
|
||||
};
|
||||
|
||||
key = {
|
||||
encryptedKey: req.serviceToken.encryptedKey,
|
||||
nonce: req.serviceToken.nonce,
|
||||
sender: {
|
||||
publicKey: req.serviceToken.publicKey
|
||||
},
|
||||
receiver: req.serviceToken.user,
|
||||
workspace: req.serviceToken.workspace
|
||||
};
|
||||
if (postHogClient) {
|
||||
// capture secrets pulled event in production
|
||||
postHogClient.capture({
|
||||
distinctId: req.serviceToken.user.email,
|
||||
event: "secrets pulled",
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
channel: channel ? channel : "cli"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (postHogClient) {
|
||||
// capture secrets pulled event in production
|
||||
postHogClient.capture({
|
||||
distinctId: req.serviceToken.user.email,
|
||||
event: 'secrets pulled',
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
channel: channel ? channel : 'cli'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
secrets: reformatPullSecrets({ secrets }),
|
||||
key
|
||||
});
|
||||
return res.status(200).send({
|
||||
secrets: reformatPullSecrets({ secrets }),
|
||||
key
|
||||
});
|
||||
};
|
||||
|
@ -5,13 +5,13 @@ import { BadRequestError } from "../../utils/errors";
|
||||
import {
|
||||
appendFolder,
|
||||
deleteFolderById,
|
||||
getAllFolderIds,
|
||||
searchByFolderIdWithDir,
|
||||
searchByFolderId,
|
||||
validateFolderName,
|
||||
generateFolderId,
|
||||
getParentFromFolderId,
|
||||
getAllFolderIds,
|
||||
getFolderByPath,
|
||||
getParentFromFolderId,
|
||||
searchByFolderId,
|
||||
searchByFolderIdWithDir,
|
||||
validateFolderName,
|
||||
} from "../../services/FolderService";
|
||||
import { ADMIN, MEMBER } from "../../variables";
|
||||
import { validateMembership } from "../../helpers/membership";
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { ServiceToken } from '../../models';
|
||||
import { createToken } from '../../helpers/auth';
|
||||
import { getJwtServiceSecret } from '../../config';
|
||||
import { Request, Response } from "express";
|
||||
import { ServiceToken } from "../../models";
|
||||
import { createToken } from "../../helpers/auth";
|
||||
import { getJwtServiceSecret } from "../../config";
|
||||
|
||||
/**
|
||||
* Return service token on request
|
||||
@ -11,7 +11,7 @@ import { getJwtServiceSecret } from '../../config';
|
||||
*/
|
||||
export const getServiceToken = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
serviceToken: req.serviceToken
|
||||
serviceToken: req.serviceToken,
|
||||
});
|
||||
};
|
||||
|
||||
@ -31,13 +31,13 @@ export const createServiceToken = async (req: Request, res: Response) => {
|
||||
expiresIn,
|
||||
publicKey,
|
||||
encryptedKey,
|
||||
nonce
|
||||
nonce,
|
||||
} = req.body;
|
||||
|
||||
// validate environment
|
||||
const workspaceEnvs = req.membership.workspace.environments;
|
||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||
throw new Error('Failed to validate environment');
|
||||
throw new Error("Failed to validate environment");
|
||||
}
|
||||
|
||||
// compute access token expiration date
|
||||
@ -52,24 +52,24 @@ export const createServiceToken = async (req: Request, res: Response) => {
|
||||
expiresAt,
|
||||
publicKey,
|
||||
encryptedKey,
|
||||
nonce
|
||||
nonce,
|
||||
}).save();
|
||||
|
||||
token = createToken({
|
||||
payload: {
|
||||
serviceTokenId: serviceToken._id.toString(),
|
||||
workspaceId
|
||||
workspaceId,
|
||||
},
|
||||
expiresIn: expiresIn,
|
||||
secret: await getJwtServiceSecret()
|
||||
secret: await getJwtServiceSecret(),
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(400).send({
|
||||
message: 'Failed to create service token'
|
||||
message: "Failed to create service token",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
token
|
||||
token,
|
||||
});
|
||||
};
|
@ -1,13 +1,15 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { User } from '../../models';
|
||||
import { Request, Response } from "express";
|
||||
import { User } from "../../models";
|
||||
import { checkEmailVerification, sendEmailVerification } from "../../helpers/signup";
|
||||
import { createToken } from "../../helpers/auth";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
import {
|
||||
sendEmailVerification,
|
||||
checkEmailVerification,
|
||||
} from '../../helpers/signup';
|
||||
import { createToken } from '../../helpers/auth';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import { getInviteOnlySignup, getJwtSignupLifetime, getJwtSignupSecret, getSmtpConfigured } from '../../config';
|
||||
import { validateUserEmail } from '../../validation';
|
||||
getInviteOnlySignup,
|
||||
getJwtSignupLifetime,
|
||||
getJwtSignupSecret,
|
||||
getSmtpConfigured
|
||||
} from "../../config";
|
||||
import { validateUserEmail } from "../../validation";
|
||||
|
||||
/**
|
||||
* Signup step 1: Initialize account for user under email [email] and send a verification code
|
||||
@ -17,27 +19,26 @@ import { validateUserEmail } from '../../validation';
|
||||
* @returns
|
||||
*/
|
||||
export const beginEmailSignup = async (req: Request, res: Response) => {
|
||||
let email: string;
|
||||
email = req.body.email;
|
||||
|
||||
const email: string = req.body.email;
|
||||
|
||||
// validate that email is not disposable
|
||||
validateUserEmail(email);
|
||||
|
||||
const user = await User.findOne({ email }).select('+publicKey');
|
||||
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'
|
||||
error: "Failed to send email verification code for complete account"
|
||||
});
|
||||
}
|
||||
|
||||
// send send verification email
|
||||
await sendEmailVerification({ email });
|
||||
|
||||
return res.status(200).send({
|
||||
message: `Sent an email verification code to ${email}`
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: `Sent an email verification code to ${email}`
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -48,23 +49,25 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const verifyEmailSignup = async (req: Request, res: Response) => {
|
||||
let user, token;
|
||||
let user;
|
||||
const { email, code } = req.body;
|
||||
|
||||
// initialize user account
|
||||
user = await User.findOne({ email }).select('+publicKey');
|
||||
user = await User.findOne({ email }).select("+publicKey");
|
||||
if (user && user?.publicKey) {
|
||||
// case: user has already completed account
|
||||
return res.status(403).send({
|
||||
error: 'Failed email verification for complete user'
|
||||
error: "Failed email verification for complete user"
|
||||
});
|
||||
}
|
||||
|
||||
if (await getInviteOnlySignup()) {
|
||||
// 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({})
|
||||
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." })
|
||||
throw BadRequestError({
|
||||
message: "New user sign ups are not allowed at this time. You must be invited to sign up."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +86,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
// generate temporary signup token
|
||||
token = createToken({
|
||||
const token = createToken({
|
||||
payload: {
|
||||
userId: user._id.toString()
|
||||
},
|
||||
@ -91,9 +94,9 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
||||
secret: await getJwtSignupSecret()
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfuly verified email',
|
||||
user,
|
||||
token
|
||||
});
|
||||
return res.status(200).send({
|
||||
message: "Successfuly verified email",
|
||||
user,
|
||||
token
|
||||
});
|
||||
};
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
import { getStripeSecretKey, getStripeWebhookSecret } from '../../config';
|
||||
|
||||
/**
|
||||
* Handle service provisioning/un-provisioning via Stripe
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const handleWebhook = async (req: Request, res: Response) => {
|
||||
// check request for valid stripe signature
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: '2022-08-01'
|
||||
});
|
||||
|
||||
const sig = req.headers['stripe-signature'] as string;
|
||||
const event = stripe.webhooks.constructEvent(
|
||||
req.body,
|
||||
sig,
|
||||
await getStripeWebhookSecret()
|
||||
);
|
||||
|
||||
switch (event.type) {
|
||||
case '':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return res.json({ received: true });
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { UserAction } from '../../models';
|
||||
import { Request, Response } from "express";
|
||||
import { UserAction } from "../../models";
|
||||
|
||||
/**
|
||||
* Add user action [action]
|
||||
@ -15,18 +15,18 @@ export const addUserAction = async (req: Request, res: Response) => {
|
||||
const userAction = await UserAction.findOneAndUpdate(
|
||||
{
|
||||
user: req.user._id,
|
||||
action
|
||||
action,
|
||||
},
|
||||
{ user: req.user._id, action },
|
||||
{
|
||||
new: true,
|
||||
upsert: true
|
||||
upsert: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully recorded user action',
|
||||
userAction
|
||||
message: "Successfully recorded user action",
|
||||
userAction,
|
||||
});
|
||||
};
|
||||
|
||||
@ -42,10 +42,10 @@ export const getUserAction = async (req: Request, res: Response) => {
|
||||
|
||||
const userAction = await UserAction.findOne({
|
||||
user: req.user._id,
|
||||
action
|
||||
action,
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
userAction
|
||||
userAction,
|
||||
});
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Request, Response } from "express";
|
||||
|
||||
/**
|
||||
* Return user on request
|
||||
@ -8,6 +8,6 @@ import { Request, Response } from 'express';
|
||||
*/
|
||||
export const getUser = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
user: req.user
|
||||
user: req.user,
|
||||
});
|
||||
};
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { Request, Response } from "express";
|
||||
import {
|
||||
Workspace,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
IUser,
|
||||
Integration,
|
||||
IntegrationAuth,
|
||||
IUser,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
ServiceToken,
|
||||
ServiceTokenData,
|
||||
Workspace,
|
||||
} from "../../models";
|
||||
import {
|
||||
createWorkspace as create,
|
||||
deleteWorkspace as deleteWork,
|
||||
} from "../../helpers/workspace";
|
||||
import { EELicenseService } from '../../ee/services';
|
||||
import { EELicenseService } from "../../ee/services";
|
||||
import { addMemberships } from "../../helpers/membership";
|
||||
import { ADMIN } from "../../variables";
|
||||
|
||||
@ -123,7 +122,7 @@ export const createWorkspace = async (req: Request, res: Response) => {
|
||||
if (plan.workspacesUsed >= plan.workspaceLimit) {
|
||||
// case: number of workspaces used exceeds the number of workspaces allowed
|
||||
return res.status(400).send({
|
||||
message: 'Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.'
|
||||
message: "Failed to create workspace due to plan limit reached. Upgrade plan to add more workspaces.",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
import {
|
||||
APIKeyData
|
||||
} from '../../models';
|
||||
import { getSaltRounds } 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) => {
|
||||
const apiKeyData = await APIKeyData.find({
|
||||
user: req.user._id
|
||||
});
|
||||
|
||||
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) => {
|
||||
const { name, expiresIn } = req.body;
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('hex');
|
||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
|
||||
let apiKeyData = await new APIKeyData({
|
||||
name,
|
||||
lastUsed: new Date(),
|
||||
expiresAt,
|
||||
user: req.user._id,
|
||||
secretHash
|
||||
}).save();
|
||||
|
||||
// return api key data without sensitive data
|
||||
// FIX: fix this any
|
||||
apiKeyData = await APIKeyData.findById(apiKeyData._id) as any
|
||||
|
||||
if (!apiKeyData) throw new Error('Failed to find API key data');
|
||||
|
||||
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
||||
|
||||
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) => {
|
||||
const { apiKeyDataId } = req.params;
|
||||
const apiKeyData = await APIKeyData.findByIdAndDelete(apiKeyDataId);
|
||||
|
||||
return res.status(200).send({
|
||||
apiKeyData
|
||||
});
|
||||
}
|
@ -1,27 +1,27 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import { Request, Response } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
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 { BadRequestError, InternalServerError } from '../../utils/errors';
|
||||
import { Request, Response } from "express";
|
||||
import jwt from "jsonwebtoken";
|
||||
import * as bigintConversion from "bigint-conversion";
|
||||
const jsrp = require("jsrp");
|
||||
import { LoginSRPDetail, User } from "../../models";
|
||||
import { createToken, issueAuthTokens } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { TokenService } from "../../services";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
TOKEN_EMAIL_MFA,
|
||||
ACTION_LOGIN
|
||||
} from '../../variables';
|
||||
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
|
||||
} from "../../variables";
|
||||
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
|
||||
import {
|
||||
getHttpsEnabled,
|
||||
getJwtMfaLifetime,
|
||||
getJwtMfaSecret,
|
||||
getHttpsEnabled
|
||||
} from '../../config';
|
||||
} from "../../config";
|
||||
|
||||
declare module 'jsonwebtoken' {
|
||||
declare module "jsonwebtoken" {
|
||||
export interface UserIDJwtPayload extends jwt.JwtPayload {
|
||||
userId: string;
|
||||
}
|
||||
@ -36,20 +36,20 @@ declare module 'jsonwebtoken' {
|
||||
export const login1 = async (req: Request, res: Response) => {
|
||||
const {
|
||||
email,
|
||||
clientPublicKey
|
||||
clientPublicKey,
|
||||
}: { email: string; clientPublicKey: string } = req.body;
|
||||
|
||||
const user = await User.findOne({
|
||||
email
|
||||
}).select('+salt +verifier');
|
||||
email,
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const server = new jsrp.server();
|
||||
server.init(
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier
|
||||
verifier: user.verifier,
|
||||
},
|
||||
async () => {
|
||||
// generate server-side public key
|
||||
@ -63,7 +63,7 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
|
||||
return res.status(200).send({
|
||||
serverPublicKey,
|
||||
salt: user.salt
|
||||
salt: user.salt,
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -78,14 +78,14 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
* @returns
|
||||
*/
|
||||
export const login2 = async (req: Request, res: Response) => {
|
||||
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
|
||||
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 +devices');
|
||||
email,
|
||||
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
const loginSRPDetail = await LoginSRPDetail.findOneAndDelete({ email: email })
|
||||
|
||||
@ -98,7 +98,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier,
|
||||
b: loginSRPDetail.serverBInt
|
||||
b: loginSRPDetail.serverBInt,
|
||||
},
|
||||
async () => {
|
||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||
@ -111,52 +111,52 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
// generate temporary MFA token
|
||||
const token = createToken({
|
||||
payload: {
|
||||
userId: user._id.toString()
|
||||
userId: user._id.toString(),
|
||||
},
|
||||
expiresIn: await getJwtMfaLifetime(),
|
||||
secret: await getJwtMfaSecret()
|
||||
secret: await getJwtMfaSecret(),
|
||||
});
|
||||
|
||||
const code = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email
|
||||
email,
|
||||
});
|
||||
|
||||
// send MFA code [code] to [email]
|
||||
await sendMail({
|
||||
template: 'emailMfa.handlebars',
|
||||
subjectLine: 'Infisical MFA code',
|
||||
template: "emailMfa.handlebars",
|
||||
subjectLine: "Infisical MFA code",
|
||||
recipients: [email],
|
||||
substitutions: {
|
||||
code
|
||||
}
|
||||
code,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
mfaEnabled: true,
|
||||
token
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
await checkUserDevice({
|
||||
user,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// issue tokens
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
// case: user does not have MFA enabled
|
||||
@ -182,7 +182,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
publicKey: user.publicKey,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||
iv: user.iv,
|
||||
tag: user.tag
|
||||
tag: user.tag,
|
||||
}
|
||||
|
||||
if (
|
||||
@ -197,21 +197,21 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
loginAction && await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.ip
|
||||
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?'
|
||||
message: "Failed to authenticate. Try again?",
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -227,21 +227,21 @@ export const sendMfaToken = async (req: Request, res: Response) => {
|
||||
|
||||
const code = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email
|
||||
email,
|
||||
});
|
||||
|
||||
// send MFA code [code] to [email]
|
||||
await sendMail({
|
||||
template: 'emailMfa.handlebars',
|
||||
subjectLine: 'Infisical MFA code',
|
||||
template: "emailMfa.handlebars",
|
||||
subjectLine: "Infisical MFA code",
|
||||
recipients: [email],
|
||||
substitutions: {
|
||||
code
|
||||
}
|
||||
code,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully sent new MFA code'
|
||||
message: "Successfully sent new MFA code",
|
||||
});
|
||||
}
|
||||
|
||||
@ -257,36 +257,36 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
await TokenService.validateToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email,
|
||||
token: mfaToken
|
||||
token: mfaToken,
|
||||
});
|
||||
|
||||
const user = await User.findOne({
|
||||
email
|
||||
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
|
||||
email,
|
||||
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
await LoginSRPDetail.deleteOne({ userId: user.id })
|
||||
|
||||
await checkUserDevice({
|
||||
user,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// issue tokens
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
interface VerifyMfaTokenRes {
|
||||
@ -319,7 +319,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
publicKey: user.publicKey as string,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey as string,
|
||||
iv: user.iv as string,
|
||||
tag: user.tag as string
|
||||
tag: user.tag as string,
|
||||
}
|
||||
|
||||
if (user?.protectedKey && user?.protectedKeyIV && user?.protectedKeyTag) {
|
||||
@ -330,14 +330,14 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
loginAction && await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.realIP
|
||||
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
return res.status(200).send(resObj);
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Request, Response } from "express";
|
||||
import {
|
||||
Integration,
|
||||
Membership,
|
||||
Secret,
|
||||
ServiceToken,
|
||||
Workspace,
|
||||
Integration,
|
||||
ServiceTokenData,
|
||||
Membership,
|
||||
} from '../../models';
|
||||
import { SecretVersion } from '../../ee/models';
|
||||
import { EELicenseService } from '../../ee/services';
|
||||
import { BadRequestError, WorkspaceNotFoundError } from '../../utils/errors';
|
||||
import _ from 'lodash';
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
|
||||
Workspace,
|
||||
} from "../../models";
|
||||
import { SecretVersion } from "../../ee/models";
|
||||
import { EELicenseService } from "../../ee/services";
|
||||
import { BadRequestError, WorkspaceNotFoundError } from "../../utils/errors";
|
||||
import _ from "lodash";
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||
|
||||
/**
|
||||
* Create new workspace environment named [environmentName] under workspace with id
|
||||
@ -38,7 +38,7 @@ export const createWorkspaceEnvironment = async (
|
||||
// case: number of environments used exceeds the number of environments allowed
|
||||
|
||||
return res.status(400).send({
|
||||
message: 'Failed to create environment due to environment limit reached. Upgrade plan to create more environments.'
|
||||
message: "Failed to create environment due to environment limit reached. Upgrade plan to create more environments.",
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -49,7 +49,7 @@ export const createWorkspaceEnvironment = async (
|
||||
({ name, slug }) => slug === environmentSlug || environmentName === name
|
||||
)
|
||||
) {
|
||||
throw new Error('Failed to create workspace environment');
|
||||
throw new Error("Failed to create workspace environment");
|
||||
}
|
||||
|
||||
workspace?.environments.push({
|
||||
@ -61,7 +61,7 @@ export const createWorkspaceEnvironment = async (
|
||||
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully created new environment',
|
||||
message: "Successfully created new environment",
|
||||
workspace: workspaceId,
|
||||
environment: {
|
||||
name: environmentName,
|
||||
@ -85,13 +85,13 @@ export const renameWorkspaceEnvironment = async (
|
||||
const { environmentName, environmentSlug, oldEnvironmentSlug } = req.body;
|
||||
// user should pass both new slug and env name
|
||||
if (!environmentSlug || !environmentName) {
|
||||
throw new Error('Invalid environment given.');
|
||||
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');
|
||||
throw new Error("Failed to create workspace environment");
|
||||
}
|
||||
|
||||
const isEnvExist = workspace.environments.some(
|
||||
@ -100,14 +100,14 @@ export const renameWorkspaceEnvironment = async (
|
||||
(name === environmentName || slug === environmentSlug)
|
||||
);
|
||||
if (isEnvExist) {
|
||||
throw new Error('Invalid environment given');
|
||||
throw new Error("Invalid environment given");
|
||||
}
|
||||
|
||||
const envIndex = workspace?.environments.findIndex(
|
||||
({ slug }) => slug === oldEnvironmentSlug
|
||||
);
|
||||
if (envIndex === -1) {
|
||||
throw new Error('Invalid environment given');
|
||||
throw new Error("Invalid environment given");
|
||||
}
|
||||
|
||||
workspace.environments[envIndex].name = environmentName;
|
||||
@ -137,7 +137,7 @@ export const renameWorkspaceEnvironment = async (
|
||||
await Membership.updateMany(
|
||||
{
|
||||
workspace: workspaceId,
|
||||
"deniedPermissions.environmentSlug": oldEnvironmentSlug
|
||||
"deniedPermissions.environmentSlug": oldEnvironmentSlug,
|
||||
},
|
||||
{ $set: { "deniedPermissions.$[element].environmentSlug": environmentSlug } },
|
||||
{ arrayFilters: [{ "element.environmentSlug": oldEnvironmentSlug }] }
|
||||
@ -145,7 +145,7 @@ export const renameWorkspaceEnvironment = async (
|
||||
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully update environment',
|
||||
message: "Successfully update environment",
|
||||
workspace: workspaceId,
|
||||
environment: {
|
||||
name: environmentName,
|
||||
@ -169,14 +169,14 @@ export const deleteWorkspaceEnvironment = async (
|
||||
// atomic update the env to avoid conflict
|
||||
const workspace = await Workspace.findById(workspaceId).exec();
|
||||
if (!workspace) {
|
||||
throw new Error('Failed to create workspace environment');
|
||||
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');
|
||||
throw new Error("Invalid environment given");
|
||||
}
|
||||
|
||||
workspace.environments.splice(envIndex, 1);
|
||||
@ -211,7 +211,7 @@ export const deleteWorkspaceEnvironment = async (
|
||||
await EELicenseService.refreshPlan(workspace.organization.toString(), workspaceId);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully deleted environment',
|
||||
message: "Successfully deleted environment",
|
||||
workspace: workspaceId,
|
||||
environment: environmentSlug,
|
||||
});
|
||||
@ -225,7 +225,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
||||
const { workspaceId } = req.params;
|
||||
const workspacesUserIsMemberOf = await Membership.findOne({
|
||||
workspace: workspaceId,
|
||||
user: req.user
|
||||
user: req.user,
|
||||
})
|
||||
|
||||
if (!workspacesUserIsMemberOf) {
|
||||
@ -249,7 +249,7 @@ export const getAllAccessibleEnvironmentsOfWorkspace = async (
|
||||
name: environment.name,
|
||||
slug: environment.slug,
|
||||
isWriteDenied: isWriteBlocked,
|
||||
isReadDenied: isReadBlocked
|
||||
isReadDenied: isReadBlocked,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
@ -1,15 +1,14 @@
|
||||
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 serviceAccountsController from './serviceAccountsController';
|
||||
import * as environmentController from './environmentController';
|
||||
import * as tagController from './tagController';
|
||||
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 secretController from "./secretController";
|
||||
import * as secretsController from "./secretsController";
|
||||
import * as serviceAccountsController from "./serviceAccountsController";
|
||||
import * as environmentController from "./environmentController";
|
||||
import * as tagController from "./tagController";
|
||||
|
||||
export {
|
||||
authController,
|
||||
@ -18,10 +17,9 @@ export {
|
||||
organizationsController,
|
||||
workspaceController,
|
||||
serviceTokenDataController,
|
||||
apiKeyDataController,
|
||||
secretController,
|
||||
secretsController,
|
||||
serviceAccountsController,
|
||||
environmentController,
|
||||
tagController
|
||||
tagController,
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
MembershipOrg,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
ServiceAccount,
|
||||
Workspace,
|
||||
ServiceAccount
|
||||
} from '../../models';
|
||||
import { deleteMembershipOrg } from '../../helpers/membershipOrg';
|
||||
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
||||
} from "../../models";
|
||||
import { deleteMembershipOrg } from "../../helpers/membershipOrg";
|
||||
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
||||
|
||||
/**
|
||||
* Return memberships for organization with id [organizationId]
|
||||
@ -51,11 +51,11 @@ export const getOrganizationMemberships = async (req: Request, res: Response) =>
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const memberships = await MembershipOrg.find({
|
||||
organization: organizationId
|
||||
}).populate('user', '+publicKey');
|
||||
organization: organizationId,
|
||||
}).populate("user", "+publicKey");
|
||||
|
||||
return res.status(200).send({
|
||||
memberships
|
||||
memberships,
|
||||
});
|
||||
}
|
||||
|
||||
@ -124,14 +124,14 @@ export const updateOrganizationMembership = async (req: Request, res: Response)
|
||||
const membership = await MembershipOrg.findByIdAndUpdate(
|
||||
membershipId,
|
||||
{
|
||||
role
|
||||
role,
|
||||
}, {
|
||||
new: true
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
membership
|
||||
membership,
|
||||
});
|
||||
}
|
||||
|
||||
@ -182,15 +182,15 @@ export const deleteOrganizationMembership = async (req: Request, res: Response)
|
||||
|
||||
// delete organization membership
|
||||
const membership = await deleteMembershipOrg({
|
||||
membershipOrgId: membershipId
|
||||
membershipOrgId: membershipId,
|
||||
});
|
||||
|
||||
await updateSubscriptionOrgQuantity({
|
||||
organizationId: membership.organization.toString()
|
||||
organizationId: membership.organization.toString(),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
membership
|
||||
membership,
|
||||
});
|
||||
}
|
||||
|
||||
@ -240,23 +240,23 @@ export const getOrganizationWorkspaces = async (req: Request, res: Response) =>
|
||||
(
|
||||
await Workspace.find(
|
||||
{
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
},
|
||||
'_id'
|
||||
"_id"
|
||||
)
|
||||
).map((w) => w._id.toString())
|
||||
);
|
||||
|
||||
const workspaces = (
|
||||
await Membership.find({
|
||||
user: req.user._id
|
||||
}).populate('workspace')
|
||||
user: req.user._id,
|
||||
}).populate("workspace")
|
||||
)
|
||||
.filter((m) => workspacesSet.has(m.workspace._id.toString()))
|
||||
.map((m) => m.workspace);
|
||||
|
||||
return res.status(200).send({
|
||||
workspaces
|
||||
workspaces,
|
||||
});
|
||||
}
|
||||
|
||||
@ -269,10 +269,10 @@ export const getOrganizationServiceAccounts = async (req: Request, res: Response
|
||||
const { organizationId } = req.params;
|
||||
|
||||
const serviceAccounts = await ServiceAccount.find({
|
||||
organization: new Types.ObjectId(organizationId)
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccounts
|
||||
serviceAccounts,
|
||||
});
|
||||
}
|
||||
|
@ -1,25 +1,39 @@
|
||||
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";
|
||||
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 { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_UTF8, SECRET_PERSONAL, SECRET_SHARED } from "../../variables";
|
||||
import { TelemetryService } from '../../services';
|
||||
import {
|
||||
BadRequestError,
|
||||
InternalServerError,
|
||||
ValidationError as RouteValidationError,
|
||||
UnauthorizedRequestError
|
||||
} from "../../utils/errors";
|
||||
import { AnyBulkWriteOperation } from "mongodb";
|
||||
import {
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
SECRET_SHARED
|
||||
} from "../../variables";
|
||||
import { TelemetryService } from "../../services";
|
||||
import { User } from "../../models";
|
||||
import { AccountNotFoundError } from '../../utils/errors';
|
||||
import { AccountNotFoundError } from "../../utils/errors";
|
||||
|
||||
/**
|
||||
* Create secret for workspace with id [workspaceId] and environment [environment]
|
||||
* @param req
|
||||
* @param res
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const createSecret = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const secretToCreate: CreateSecretRequestBody = req.body.secret;
|
||||
const { workspaceId, environment } = req.params
|
||||
const { workspaceId, environment } = req.params;
|
||||
const sanitizedSecret: SanitizedSecretForCreate = {
|
||||
secretKeyCiphertext: secretToCreate.secretKeyCiphertext,
|
||||
secretKeyIV: secretToCreate.secretKeyIV,
|
||||
@ -39,45 +53,41 @@ export const createSecret = async (req: Request, res: Response) => {
|
||||
user: new Types.ObjectId(req.user._id),
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const [error, secret] = await to(Secret.create(sanitizedSecret).then())
|
||||
if (error instanceof ValidationError) {
|
||||
throw RouteValidationError({ message: error.message, stack: error.stack })
|
||||
}
|
||||
const secret = await new Secret(sanitizedSecret).save();
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets added',
|
||||
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']
|
||||
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
|
||||
* Create many secrets for workspace with id [workspaceId] and environment [environment]
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const createSecrets = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
||||
const { workspaceId, environment } = req.params
|
||||
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
|
||||
const { workspaceId, environment } = req.params;
|
||||
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [];
|
||||
|
||||
secretsToCreate.forEach(rawSecret => {
|
||||
secretsToCreate.forEach((rawSecret) => {
|
||||
const safeUpdateFields: SanitizedSecretForCreate = {
|
||||
secretKeyCiphertext: rawSecret.secretKeyCiphertext,
|
||||
secretKeyIV: rawSecret.secretKeyIV,
|
||||
@ -97,140 +107,129 @@ export const createSecrets = async (req: Request, res: Response) => {
|
||||
user: new Types.ObjectId(req.user._id),
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
}
|
||||
};
|
||||
|
||||
sanitizedSecretesToCreate.push(safeUpdateFields)
|
||||
})
|
||||
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 })
|
||||
}
|
||||
const secrets = await Secret.insertMany(sanitizedSecretesToCreate);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets added',
|
||||
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']
|
||||
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
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteSecrets = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const { workspaceId, environmentName } = req.params
|
||||
const secretIdsToDelete: string[] = req.body.secretIds
|
||||
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 secretIdsUserCanDelete = await Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 });
|
||||
|
||||
const secretsUserCanDeleteSet: Set<string> = new Set(secretIdsUserCanDelete.map(objectId => objectId._id.toString()));
|
||||
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = []
|
||||
const secretsUserCanDeleteSet: Set<string> = new Set(
|
||||
secretIdsUserCanDelete.map((objectId) => objectId._id.toString())
|
||||
);
|
||||
const deleteOperationsToPerform: AnyBulkWriteOperation<ISecret>[] = [];
|
||||
|
||||
let numSecretsDeleted = 0;
|
||||
secretIdsToDelete.forEach(secretIdToDelete => {
|
||||
secretIdsToDelete.forEach((secretIdToDelete) => {
|
||||
if (secretsUserCanDeleteSet.has(secretIdToDelete)) {
|
||||
const deleteOperation = { deleteOne: { filter: { _id: new Types.ObjectId(secretIdToDelete) } } }
|
||||
deleteOperationsToPerform.push(deleteOperation)
|
||||
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" })
|
||||
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()
|
||||
}
|
||||
await Secret.bulkWrite(deleteOperationsToPerform);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets deleted',
|
||||
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']
|
||||
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||
userAgent: req.headers?.["user-agent"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
res.status(200).send()
|
||||
}
|
||||
res.status(200).send();
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete secret with id [secretId]
|
||||
* @param req
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteSecret = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
await Secret.findByIdAndDelete(req._secret._id)
|
||||
await Secret.findByIdAndDelete(req._secret._id);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets deleted',
|
||||
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']
|
||||
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
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateSecrets = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const { workspaceId, environmentName } = req.params
|
||||
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 secretIdsUserCanModify = await Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 });
|
||||
|
||||
const secretsUserCanModifySet: Set<string> = new Set(secretIdsUserCanModify.map(objectId => objectId._id.toString()));
|
||||
const updateOperationsToPerform: any = []
|
||||
const secretsUserCanModifySet: Set<string> = new Set(
|
||||
secretIdsUserCanModify.map((objectId) => objectId._id.toString())
|
||||
);
|
||||
const updateOperationsToPerform: any = [];
|
||||
|
||||
secretsModificationsRequested.forEach(userModifiedSecret => {
|
||||
secretsModificationsRequested.forEach((userModifiedSecret) => {
|
||||
if (secretsUserCanModifySet.has(userModifiedSecret._id.toString())) {
|
||||
const sanitizedSecret: SanitizedSecretModify = {
|
||||
secretKeyCiphertext: userModifiedSecret.secretKeyCiphertext,
|
||||
@ -244,57 +243,54 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
||||
secretCommentCiphertext: userModifiedSecret.secretCommentCiphertext,
|
||||
secretCommentIV: userModifiedSecret.secretCommentIV,
|
||||
secretCommentTag: userModifiedSecret.secretCommentTag,
|
||||
secretCommentHash: userModifiedSecret.secretCommentHash,
|
||||
}
|
||||
secretCommentHash: userModifiedSecret.secretCommentHash
|
||||
};
|
||||
|
||||
const updateOperation = { updateOne: { filter: { _id: userModifiedSecret._id, workspace: workspaceId }, update: { $inc: { version: 1 }, $set: sanitizedSecret } } }
|
||||
updateOperationsToPerform.push(updateOperation)
|
||||
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" })
|
||||
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()
|
||||
}
|
||||
await Secret.bulkWrite(updateOperationsToPerform);
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets modified',
|
||||
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']
|
||||
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||
userAgent: req.headers?.["user-agent"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send()
|
||||
}
|
||||
return res.status(200).send();
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a secret within workspace with id [workspaceId] and environment [environment]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateSecret = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const { workspaceId, environmentName } = req.params
|
||||
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 secretIdUserCanModify = await Secret.findOne({ workspace: workspaceId, environment: environmentName }, { _id: 1 });
|
||||
|
||||
const sanitizedSecret: SanitizedSecretModify = {
|
||||
secretKeyCiphertext: secretModificationsRequested.secretKeyCiphertext,
|
||||
@ -308,45 +304,55 @@ export const updateSecret = async (req: Request, res: Response) => {
|
||||
secretCommentCiphertext: secretModificationsRequested.secretCommentCiphertext,
|
||||
secretCommentIV: secretModificationsRequested.secretCommentIV,
|
||||
secretCommentTag: secretModificationsRequested.secretCommentTag,
|
||||
secretCommentHash: secretModificationsRequested.secretCommentHash,
|
||||
}
|
||||
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 })
|
||||
}
|
||||
const singleModificationUpdate = await Secret.updateOne(
|
||||
{ _id: secretModificationsRequested._id, workspace: workspaceId },
|
||||
{ $inc: { version: 1 }, $set: sanitizedSecret }
|
||||
)
|
||||
.catch((error) => {
|
||||
if (error instanceof ValidationError) {
|
||||
throw RouteValidationError({
|
||||
message: "Unable to apply modifications, please try again",
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
});
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets modified',
|
||||
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']
|
||||
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||
userAgent: req.headers?.["user-agent"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send(singleModificationUpdate)
|
||||
}
|
||||
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
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getSecrets = async (req: Request, res: Response) => {
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
const { environment } = req.query;
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
let userId: Types.ObjectId | undefined = undefined // used for getting personal secrets for user
|
||||
let userEmail: string | undefined = undefined // used for posthog
|
||||
let userId: Types.ObjectId | undefined = undefined; // used for getting personal secrets for user
|
||||
let userEmail: string | undefined = undefined; // used for posthog
|
||||
if (req.user) {
|
||||
userId = req.user._id;
|
||||
userEmail = req.user.email;
|
||||
@ -354,47 +360,47 @@ export const getSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
if (req.serviceTokenData) {
|
||||
userId = req.serviceTokenData.user;
|
||||
|
||||
const user = await User.findById(req.serviceTokenData.user, 'email');
|
||||
|
||||
const user = await User.findById(req.serviceTokenData.user, "email");
|
||||
if (!user) throw AccountNotFoundError();
|
||||
userEmail = 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 })
|
||||
}
|
||||
const secrets = await Secret.find({
|
||||
workspace: workspaceId,
|
||||
environment,
|
||||
$or: [{ user: userId }, { user: { $exists: false } }],
|
||||
type: { $in: [SECRET_SHARED, SECRET_PERSONAL] }
|
||||
})
|
||||
.catch((err) => {
|
||||
throw RouteValidationError({
|
||||
message: "Failed to get secrets, please try again",
|
||||
stack: err.stack
|
||||
});
|
||||
})
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets pulled',
|
||||
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']
|
||||
channel: req.headers?.["user-agent"]?.toLowerCase().includes("mozilla") ? "web" : "cli",
|
||||
userAgent: req.headers?.["user-agent"]
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return res.json(secrets)
|
||||
}
|
||||
return res.json(secrets);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return secret with id [secretId]
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getSecret = async (req: Request, res: Response) => {
|
||||
// if (postHogClient) {
|
||||
@ -414,4 +420,4 @@ export const getSecret = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
secret: req._secret
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -3,19 +3,19 @@ import { Request, Response } from "express";
|
||||
import { ISecret, Secret, ServiceTokenData } from "../../models";
|
||||
import { IAction, SecretVersion } from "../../ee/models";
|
||||
import {
|
||||
SECRET_PERSONAL,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
} from "../../variables";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { EventService } from "../../services";
|
||||
import { eventPushSecrets } from "../../events";
|
||||
import { EESecretService, EELogService } from "../../ee/services";
|
||||
import { TelemetryService, SecretService } from "../../services";
|
||||
import { EELogService, EESecretService } from "../../ee/services";
|
||||
import { SecretService, TelemetryService } from "../../services";
|
||||
import { getChannelFromUserAgent } from "../../utils/posthog";
|
||||
import { PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||
import {
|
||||
@ -25,7 +25,7 @@ import {
|
||||
} from "../../ee/helpers/checkMembershipPermissions";
|
||||
import Tag from "../../models/tag";
|
||||
import _ from "lodash";
|
||||
import { BatchSecretRequest, BatchSecret } from "../../types/secret";
|
||||
import { BatchSecret, BatchSecretRequest } from "../../types/secret";
|
||||
import Folder from "../../models/folder";
|
||||
import {
|
||||
getFolderByPath,
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import crypto from 'crypto';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcrypt";
|
||||
import {
|
||||
ServiceAccount,
|
||||
ServiceAccountKey,
|
||||
ServiceAccountOrganizationPermission,
|
||||
ServiceAccountWorkspacePermission
|
||||
} from '../../models';
|
||||
ServiceAccountWorkspacePermission,
|
||||
} from "../../models";
|
||||
import {
|
||||
CreateServiceAccountDto
|
||||
} from '../../interfaces/serviceAccounts/dto';
|
||||
import { BadRequestError, ServiceAccountNotFoundError } from '../../utils/errors';
|
||||
import { getSaltRounds } from '../../config';
|
||||
CreateServiceAccountDto,
|
||||
} from "../../interfaces/serviceAccounts/dto";
|
||||
import { BadRequestError, ServiceAccountNotFoundError } from "../../utils/errors";
|
||||
import { getSaltRounds } from "../../config";
|
||||
|
||||
/**
|
||||
* Return service account tied to the request (service account) client
|
||||
@ -23,11 +23,11 @@ export const getCurrentServiceAccount = async (req: Request, res: Response) => {
|
||||
const serviceAccount = await ServiceAccount.findById(req.serviceAccount._id);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
||||
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
@ -42,11 +42,11 @@ export const getServiceAccountById = async (req: Request, res: Response) => {
|
||||
const serviceAccount = await ServiceAccount.findById(serviceAccountId);
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
||||
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
}
|
||||
|
||||
const secret = crypto.randomBytes(16).toString('base64');
|
||||
const secret = crypto.randomBytes(16).toString("base64");
|
||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||
|
||||
// create service account
|
||||
@ -82,7 +82,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
publicKey,
|
||||
lastUsed: new Date(),
|
||||
expiresAt,
|
||||
secretHash
|
||||
secretHash,
|
||||
}).save();
|
||||
|
||||
const serviceAccountObj = serviceAccount.toObject();
|
||||
@ -91,14 +91,14 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
||||
|
||||
// provision default org-level permission for service account
|
||||
await new ServiceAccountOrganizationPermission({
|
||||
serviceAccount: serviceAccount._id
|
||||
serviceAccount: serviceAccount._id,
|
||||
}).save();
|
||||
|
||||
const secretId = Buffer.from(serviceAccount._id.toString(), 'hex').toString('base64');
|
||||
const secretId = Buffer.from(serviceAccount._id.toString(), "hex").toString("base64");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountAccessKey: `sa.${secretId}.${secret}`,
|
||||
serviceAccount: serviceAccountObj
|
||||
serviceAccount: serviceAccountObj,
|
||||
});
|
||||
}
|
||||
|
||||
@ -114,18 +114,18 @@ export const changeServiceAccountName = async (req: Request, res: Response) => {
|
||||
|
||||
const serviceAccount = await ServiceAccount.findOneAndUpdate(
|
||||
{
|
||||
_id: new Types.ObjectId(serviceAccountId)
|
||||
_id: new Types.ObjectId(serviceAccountId),
|
||||
},
|
||||
{
|
||||
name
|
||||
name,
|
||||
},
|
||||
{
|
||||
new: true
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
||||
const {
|
||||
workspaceId,
|
||||
encryptedKey,
|
||||
nonce
|
||||
nonce,
|
||||
} = req.body;
|
||||
|
||||
const serviceAccountKey = await new ServiceAccountKey({
|
||||
@ -148,7 +148,7 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
||||
nonce,
|
||||
sender: req.user._id,
|
||||
serviceAccount: req.serviceAccount._d,
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
}).save();
|
||||
|
||||
return serviceAccountKey;
|
||||
@ -161,11 +161,11 @@ export const addServiceAccountKey = async (req: Request, res: Response) => {
|
||||
*/
|
||||
export const getServiceAccountWorkspacePermissions = async (req: Request, res: Response) => {
|
||||
const serviceAccountWorkspacePermissions = await ServiceAccountWorkspacePermission.find({
|
||||
serviceAccount: req.serviceAccount._id
|
||||
}).populate('workspace');
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
}).populate("workspace");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermissions
|
||||
serviceAccountWorkspacePermissions,
|
||||
});
|
||||
}
|
||||
|
||||
@ -182,34 +182,34 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
||||
read = false,
|
||||
write = false,
|
||||
encryptedKey,
|
||||
nonce
|
||||
nonce,
|
||||
} = req.body;
|
||||
|
||||
if (!req.membership.workspace.environments.some((e: { name: string; slug: string }) => e.slug === environment)) {
|
||||
return res.status(400).send({
|
||||
message: 'Failed to validate workspace environment'
|
||||
message: "Failed to validate workspace environment",
|
||||
});
|
||||
}
|
||||
|
||||
const existingPermission = await ServiceAccountWorkspacePermission.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment
|
||||
environment,
|
||||
});
|
||||
|
||||
if (existingPermission) throw BadRequestError({ message: 'Failed to add workspace permission to service account due to already-existing ' });
|
||||
if (existingPermission) throw BadRequestError({ message: "Failed to add workspace permission to service account due to already-existing " });
|
||||
|
||||
const serviceAccountWorkspacePermission = await new ServiceAccountWorkspacePermission({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
environment,
|
||||
read,
|
||||
write
|
||||
write,
|
||||
}).save();
|
||||
|
||||
const existingServiceAccountKey = await ServiceAccountKey.findOne({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
if (!existingServiceAccountKey) {
|
||||
@ -218,12 +218,12 @@ export const addServiceAccountWorkspacePermission = async (req: Request, res: Re
|
||||
nonce,
|
||||
sender: req.user._id,
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
}).save();
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermission
|
||||
serviceAccountWorkspacePermission,
|
||||
});
|
||||
}
|
||||
|
||||
@ -240,19 +240,19 @@ export const deleteServiceAccountWorkspacePermission = async (req: Request, res:
|
||||
const { serviceAccount, workspace } = serviceAccountWorkspacePermission;
|
||||
const count = await ServiceAccountWorkspacePermission.countDocuments({
|
||||
serviceAccount,
|
||||
workspace
|
||||
workspace,
|
||||
});
|
||||
|
||||
if (count === 0) {
|
||||
await ServiceAccountKey.findOneAndDelete({
|
||||
serviceAccount,
|
||||
workspace
|
||||
workspace,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountWorkspacePermission
|
||||
serviceAccountWorkspacePermission,
|
||||
});
|
||||
}
|
||||
|
||||
@ -269,20 +269,20 @@ export const deleteServiceAccount = async (req: Request, res: Response) => {
|
||||
|
||||
if (serviceAccount) {
|
||||
await ServiceAccountKey.deleteMany({
|
||||
serviceAccount: serviceAccount._id
|
||||
serviceAccount: serviceAccount._id,
|
||||
});
|
||||
|
||||
await ServiceAccountOrganizationPermission.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
});
|
||||
|
||||
await ServiceAccountWorkspacePermission.deleteMany({
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId)
|
||||
serviceAccount: new Types.ObjectId(serviceAccountId),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccount
|
||||
serviceAccount,
|
||||
});
|
||||
}
|
||||
|
||||
@ -297,10 +297,10 @@ export const getServiceAccountKeys = async (req: Request, res: Response) => {
|
||||
|
||||
const serviceAccountKeys = await ServiceAccountKey.find({
|
||||
serviceAccount: req.serviceAccount._id,
|
||||
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {})
|
||||
...(workspaceId ? { workspace: new Types.ObjectId(workspaceId) } : {}),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
serviceAccountKeys
|
||||
serviceAccountKeys,
|
||||
});
|
||||
}
|
@ -1,13 +1,10 @@
|
||||
import { Request, Response } from "express";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcrypt";
|
||||
import { User, ServiceAccount, ServiceTokenData } from "../../models";
|
||||
import { userHasWorkspaceAccess } from "../../ee/helpers/checkMembershipPermissions";
|
||||
import { ServiceAccount, ServiceTokenData, User } from "../../models";
|
||||
import {
|
||||
PERMISSION_READ_SECRETS,
|
||||
AUTH_MODE_JWT,
|
||||
AUTH_MODE_SERVICE_ACCOUNT,
|
||||
AUTH_MODE_SERVICE_TOKEN,
|
||||
} from "../../variables";
|
||||
import { getSaltRounds } from "../../config";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
@ -56,7 +53,7 @@ export const getServiceTokenData = async (req: Request, res: Response) => {
|
||||
req.authData.authPayload._id
|
||||
)
|
||||
.select("+encryptedKey +iv +tag")
|
||||
.populate("user");
|
||||
.populate("user").lean();
|
||||
|
||||
return res.status(200).json(serviceTokenData);
|
||||
};
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { User, MembershipOrg } from '../../models';
|
||||
import { completeAccount } from '../../helpers/user';
|
||||
import { Request, Response } from "express";
|
||||
import { MembershipOrg, User } from "../../models";
|
||||
import { completeAccount } from "../../helpers/user";
|
||||
import {
|
||||
initializeDefaultOrg
|
||||
} from '../../helpers/signup';
|
||||
import { issueAuthTokens } from '../../helpers/auth';
|
||||
import { INVITED, ACCEPTED } from '../../variables';
|
||||
import { standardRequest } from '../../config/request';
|
||||
import { getLoopsApiKey, getHttpsEnabled } from '../../config';
|
||||
import { updateSubscriptionOrgQuantity } from '../../helpers/organization';
|
||||
initializeDefaultOrg,
|
||||
} from "../../helpers/signup";
|
||||
import { issueAuthTokens } from "../../helpers/auth";
|
||||
import { ACCEPTED, INVITED } from "../../variables";
|
||||
import { standardRequest } from "../../config/request";
|
||||
import { getHttpsEnabled, getLoopsApiKey } from "../../config";
|
||||
import { updateSubscriptionOrgQuantity } from "../../helpers/organization";
|
||||
|
||||
/**
|
||||
* Complete setting up user by adding their personal and auth information as part of the
|
||||
@ -32,7 +32,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier,
|
||||
organizationName
|
||||
organizationName,
|
||||
}: {
|
||||
email: string;
|
||||
firstName: string;
|
||||
@ -56,7 +56,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
// case 1: user doesn't exist.
|
||||
// case 2: user has already completed account
|
||||
return res.status(403).send({
|
||||
error: 'Failed to complete account for complete user'
|
||||
error: "Failed to complete account for complete user",
|
||||
});
|
||||
}
|
||||
|
||||
@ -74,28 +74,28 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
|
||||
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
|
||||
|
||||
// initialize default organization and workspace
|
||||
await initializeDefaultOrg({
|
||||
organizationName,
|
||||
user
|
||||
user,
|
||||
});
|
||||
|
||||
// update organization membership statuses that are
|
||||
// invited to completed with user attached
|
||||
const membershipsToUpdate = await MembershipOrg.find({
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
});
|
||||
|
||||
membershipsToUpdate.forEach(async (membership) => {
|
||||
await updateSubscriptionOrgQuantity({
|
||||
organizationId: membership.organization.toString()
|
||||
organizationId: membership.organization.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
@ -104,11 +104,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
await MembershipOrg.updateMany(
|
||||
{
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
},
|
||||
{
|
||||
user,
|
||||
status: ACCEPTED
|
||||
status: ACCEPTED,
|
||||
}
|
||||
);
|
||||
|
||||
@ -116,7 +116,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
token = tokens.token;
|
||||
@ -127,27 +127,27 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
"email": email,
|
||||
"eventName": "Sign Up",
|
||||
"firstName": firstName,
|
||||
"lastName": lastName
|
||||
"lastName": lastName,
|
||||
}, {
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer " + (await getLoopsApiKey())
|
||||
"Authorization": "Bearer " + (await getLoopsApiKey()),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully set up account',
|
||||
message: "Successfully set up account",
|
||||
user,
|
||||
token
|
||||
token,
|
||||
});
|
||||
};
|
||||
|
||||
@ -172,7 +172,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
} = req.body;
|
||||
|
||||
// get user
|
||||
@ -182,16 +182,16 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
// case 1: user doesn't exist.
|
||||
// case 2: user has already completed account
|
||||
return res.status(403).send({
|
||||
error: 'Failed to complete account for complete user'
|
||||
error: "Failed to complete account for complete user",
|
||||
});
|
||||
}
|
||||
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
});
|
||||
|
||||
if (!membershipOrg) throw new Error('Failed to find invitations for email');
|
||||
if (!membershipOrg) throw new Error("Failed to find invitations for email");
|
||||
|
||||
// complete setting up user's account
|
||||
user = await completeAccount({
|
||||
@ -207,33 +207,33 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new Error('Failed to complete account for non-existent user');
|
||||
throw new Error("Failed to complete account for non-existent user");
|
||||
|
||||
// update organization membership statuses that are
|
||||
// invited to completed with user attached
|
||||
const membershipsToUpdate = await MembershipOrg.find({
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
});
|
||||
|
||||
membershipsToUpdate.forEach(async (membership) => {
|
||||
await updateSubscriptionOrgQuantity({
|
||||
organizationId: membership.organization.toString()
|
||||
organizationId: membership.organization.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
await MembershipOrg.updateMany(
|
||||
{
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
},
|
||||
{
|
||||
user,
|
||||
status: ACCEPTED
|
||||
status: ACCEPTED,
|
||||
}
|
||||
);
|
||||
|
||||
@ -241,22 +241,22 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
token = tokens.token;
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully set up account',
|
||||
message: "Successfully set up account",
|
||||
user,
|
||||
token
|
||||
token,
|
||||
});
|
||||
};
|
||||
|
@ -1,71 +1,60 @@
|
||||
import { Request, Response } from 'express';
|
||||
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';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { Membership, Secret } from "../../models";
|
||||
import Tag from "../../models/tag";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../utils/errors";
|
||||
import { MongoError } from "mongodb";
|
||||
|
||||
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)
|
||||
}
|
||||
const { workspaceId } = req.params;
|
||||
const { name, slug } = req.body;
|
||||
|
||||
const tagToCreate = {
|
||||
name,
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
slug,
|
||||
user: new Types.ObjectId(req.user._id),
|
||||
};
|
||||
|
||||
const createdTag = await new Tag(tagToCreate).save();
|
||||
|
||||
res.json(createdTag);
|
||||
};
|
||||
|
||||
export const deleteWorkspaceTag = async (req: Request, res: Response) => {
|
||||
const { tagId } = req.params
|
||||
const { tagId } = req.params;
|
||||
|
||||
const tagFromDB = await Tag.findById(tagId)
|
||||
if (!tagFromDB) {
|
||||
throw BadRequestError()
|
||||
}
|
||||
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
|
||||
});
|
||||
// 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' });
|
||||
}
|
||||
if (!membership) {
|
||||
UnauthorizedRequestError({ message: "Failed to validate membership" });
|
||||
}
|
||||
|
||||
const result = await Tag.findByIdAndDelete(tagId);
|
||||
const result = await Tag.findByIdAndDelete(tagId);
|
||||
|
||||
// remove the tag from secrets
|
||||
await Secret.updateMany(
|
||||
{ tags: { $in: [tagId] } },
|
||||
{ $pull: { tags: tagId } }
|
||||
);
|
||||
// remove the tag from secrets
|
||||
await Secret.updateMany({ tags: { $in: [tagId] } }, { $pull: { tags: tagId } });
|
||||
|
||||
res.json(result);
|
||||
}
|
||||
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
|
||||
})
|
||||
}
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
const workspaceTags = await Tag.find({
|
||||
workspace: new Types.ObjectId(workspaceId)
|
||||
});
|
||||
|
||||
return res.json({
|
||||
workspaceTags
|
||||
});
|
||||
};
|
||||
|
@ -1,8 +1,14 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import crypto from "crypto";
|
||||
import bcrypt from "bcrypt";
|
||||
import {
|
||||
MembershipOrg,
|
||||
User,
|
||||
MembershipOrg
|
||||
} from '../../models';
|
||||
APIKeyData,
|
||||
TokenVersion
|
||||
} from "../../models";
|
||||
import { getSaltRounds } from "../../config";
|
||||
|
||||
/**
|
||||
* Return the current user.
|
||||
@ -38,10 +44,10 @@ export const getMe = async (req: Request, res: Response) => {
|
||||
*/
|
||||
const user = await User
|
||||
.findById(req.user._id)
|
||||
.select('+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag');
|
||||
.select("+salt +publicKey +encryptedPrivateKey +iv +tag +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag");
|
||||
|
||||
return res.status(200).send({
|
||||
user
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
@ -60,7 +66,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
||||
if (isMfaEnabled) {
|
||||
// TODO: adapt this route/controller
|
||||
// to work for different forms of MFA
|
||||
req.user.mfaMethods = ['email'];
|
||||
req.user.mfaMethods = ["email"];
|
||||
} else {
|
||||
req.user.mfaMethods = [];
|
||||
}
|
||||
@ -70,7 +76,7 @@ export const updateMyMfaEnabled = async (req: Request, res: Response) => {
|
||||
const user = req.user;
|
||||
|
||||
return res.status(200).send({
|
||||
user
|
||||
user,
|
||||
});
|
||||
}
|
||||
|
||||
@ -109,11 +115,114 @@ export const getMyOrganizations = async (req: Request, res: Response) => {
|
||||
*/
|
||||
const organizations = (
|
||||
await MembershipOrg.find({
|
||||
user: req.user._id
|
||||
}).populate('organization')
|
||||
user: req.user._id,
|
||||
}).populate("organization")
|
||||
).map((m) => m.organization);
|
||||
|
||||
return res.status(200).send({
|
||||
organizations
|
||||
organizations,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return API keys belonging to current user.
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getMyAPIKeys = async (req: Request, res: Response) => {
|
||||
const apiKeyData = await APIKeyData.find({
|
||||
user: req.user._id,
|
||||
});
|
||||
|
||||
return res.status(200).send(apiKeyData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new API key for current user.
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const createAPIKey = async (req: Request, res: Response) => {
|
||||
const { name, expiresIn } = req.body;
|
||||
|
||||
const secret = crypto.randomBytes(16).toString("hex");
|
||||
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||
|
||||
const expiresAt = new Date();
|
||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||
|
||||
let apiKeyData = await new APIKeyData({
|
||||
name,
|
||||
lastUsed: new Date(),
|
||||
expiresAt,
|
||||
user: req.user._id,
|
||||
secretHash,
|
||||
}).save();
|
||||
|
||||
// return api key data without sensitive data
|
||||
apiKeyData = (await APIKeyData.findById(apiKeyData._id)) as any;
|
||||
|
||||
if (!apiKeyData) throw new Error("Failed to find API key data");
|
||||
|
||||
const apiKey = `ak.${apiKeyData._id.toString()}.${secret}`;
|
||||
|
||||
return res.status(200).send({
|
||||
apiKey,
|
||||
apiKeyData,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete API key with id [apiKeyDataId] belonging to current user
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export const deleteAPIKey = async (req: Request, res: Response) => {
|
||||
const { apiKeyDataId } = req.params;
|
||||
|
||||
const apiKeyData = await APIKeyData.findOneAndDelete({
|
||||
_id: new Types.ObjectId(apiKeyDataId),
|
||||
user: req.user._id
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
apiKeyData
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return active sessions (TokenVersion) belonging to user
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getMySessions = async (req: Request, res: Response) => {
|
||||
const tokenVersions = await TokenVersion.find({
|
||||
user: req.user._id
|
||||
});
|
||||
|
||||
return res.status(200).send(tokenVersions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke all active sessions belong to user
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const deleteMySessions = async (req: Request, res: Response) => {
|
||||
await TokenVersion.updateMany({
|
||||
user: req.user._id,
|
||||
}, {
|
||||
$inc: {
|
||||
refreshVersion: 1,
|
||||
accessVersion: 1,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: "Successfully revoked all sessions"
|
||||
});
|
||||
}
|
@ -1,25 +1,19 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
Workspace,
|
||||
Secret,
|
||||
Membership,
|
||||
MembershipOrg,
|
||||
Integration,
|
||||
IntegrationAuth,
|
||||
Key,
|
||||
IUser,
|
||||
ServiceToken,
|
||||
ServiceTokenData
|
||||
} from '../../models';
|
||||
Membership,
|
||||
ServiceTokenData,
|
||||
Workspace,
|
||||
} from "../../models";
|
||||
import {
|
||||
v2PushSecrets as push,
|
||||
pullSecrets as pull,
|
||||
reformatPullSecrets
|
||||
} from '../../helpers/secret';
|
||||
import { pushKeys } from '../../helpers/key';
|
||||
import { TelemetryService, EventService } from '../../services';
|
||||
import { eventPushSecrets } from '../../events';
|
||||
v2PushSecrets as push,
|
||||
reformatPullSecrets,
|
||||
} from "../../helpers/secret";
|
||||
import { pushKeys } from "../../helpers/key";
|
||||
import { EventService, TelemetryService } from "../../services";
|
||||
import { eventPushSecrets } from "../../events";
|
||||
|
||||
interface V2PushSecret {
|
||||
type: string; // personal or shared
|
||||
@ -54,12 +48,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
// validate environment
|
||||
const workspaceEnvs = req.membership.workspace.environments;
|
||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||
throw new Error('Failed to validate environment');
|
||||
throw new Error("Failed to validate environment");
|
||||
}
|
||||
|
||||
// sanitize secrets
|
||||
secrets = secrets.filter(
|
||||
(s: V2PushSecret) => s.secretKeyCiphertext !== '' && s.secretValueCiphertext !== ''
|
||||
(s: V2PushSecret) => s.secretKeyCiphertext !== "" && s.secretValueCiphertext !== ""
|
||||
);
|
||||
|
||||
await push({
|
||||
@ -67,26 +61,26 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
environment,
|
||||
secrets,
|
||||
channel: channel ? channel : 'cli',
|
||||
ipAddress: req.realIP
|
||||
channel: channel ? channel : "cli",
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
await pushKeys({
|
||||
userId: req.user._id,
|
||||
workspaceId,
|
||||
keys
|
||||
keys,
|
||||
});
|
||||
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'secrets pushed',
|
||||
event: "secrets pushed",
|
||||
distinctId: req.user.email,
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
channel: channel ? channel : 'cli'
|
||||
}
|
||||
channel: channel ? channel : "cli",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -94,12 +88,12 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
EventService.handleEvent({
|
||||
event: eventPushSecrets({
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
environment
|
||||
})
|
||||
environment,
|
||||
}),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully uploaded workspace secrets'
|
||||
message: "Successfully uploaded workspace secrets",
|
||||
});
|
||||
};
|
||||
|
||||
@ -126,18 +120,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
||||
// validate environment
|
||||
const workspaceEnvs = req.membership.workspace.environments;
|
||||
if (!workspaceEnvs.find(({ slug }: { slug: string }) => slug === environment)) {
|
||||
throw new Error('Failed to validate environment');
|
||||
throw new Error("Failed to validate environment");
|
||||
}
|
||||
|
||||
secrets = await pull({
|
||||
userId,
|
||||
workspaceId,
|
||||
environment,
|
||||
channel: channel ? channel : 'cli',
|
||||
ipAddress: req.realIP
|
||||
channel: channel ? channel : "cli",
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
if (channel !== 'cli') {
|
||||
if (channel !== "cli") {
|
||||
secrets = reformatPullSecrets({ secrets });
|
||||
}
|
||||
|
||||
@ -145,18 +139,18 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
||||
// capture secrets pushed event in production
|
||||
postHogClient.capture({
|
||||
distinctId: req.user.email,
|
||||
event: 'secrets pulled',
|
||||
event: "secrets pulled",
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
environment,
|
||||
workspaceId,
|
||||
channel: channel ? channel : 'cli'
|
||||
}
|
||||
channel: channel ? channel : "cli",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
secrets
|
||||
secrets,
|
||||
});
|
||||
};
|
||||
|
||||
@ -194,10 +188,10 @@ export const getWorkspaceKey = async (req: Request, res: Response) => {
|
||||
|
||||
key = await Key.findOne({
|
||||
workspace: workspaceId,
|
||||
receiver: req.user._id
|
||||
}).populate('sender', '+publicKey');
|
||||
receiver: req.user._id,
|
||||
}).populate("sender", "+publicKey");
|
||||
|
||||
if (!key) throw new Error('Failed to find workspace key');
|
||||
if (!key) throw new Error("Failed to find workspace key");
|
||||
|
||||
return res.status(200).json(key);
|
||||
}
|
||||
@ -209,12 +203,12 @@ export const getWorkspaceServiceTokenData = async (
|
||||
|
||||
const serviceTokenData = await ServiceTokenData
|
||||
.find({
|
||||
workspace: workspaceId
|
||||
workspace: workspaceId,
|
||||
})
|
||||
.select('+encryptedKey +iv +tag');
|
||||
.select("+encryptedKey +iv +tag");
|
||||
|
||||
return res.status(200).send({
|
||||
serviceTokenData
|
||||
serviceTokenData,
|
||||
});
|
||||
}
|
||||
|
||||
@ -261,11 +255,11 @@ export const getWorkspaceMemberships = async (req: Request, res: Response) => {
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
const memberships = await Membership.find({
|
||||
workspace: workspaceId
|
||||
}).populate('user', '+publicKey');
|
||||
workspace: workspaceId,
|
||||
}).populate("user", "+publicKey");
|
||||
|
||||
return res.status(200).send({
|
||||
memberships
|
||||
memberships,
|
||||
});
|
||||
}
|
||||
|
||||
@ -330,21 +324,21 @@ export const updateWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
}
|
||||
*/
|
||||
const {
|
||||
membershipId
|
||||
membershipId,
|
||||
} = req.params;
|
||||
const { role } = req.body;
|
||||
|
||||
const membership = await Membership.findByIdAndUpdate(
|
||||
membershipId,
|
||||
{
|
||||
role
|
||||
role,
|
||||
}, {
|
||||
new: true
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
membership
|
||||
membership,
|
||||
});
|
||||
}
|
||||
|
||||
@ -392,20 +386,20 @@ export const deleteWorkspaceMembership = async (req: Request, res: Response) =>
|
||||
}
|
||||
*/
|
||||
const {
|
||||
membershipId
|
||||
membershipId,
|
||||
} = req.params;
|
||||
|
||||
const membership = await Membership.findByIdAndDelete(membershipId);
|
||||
|
||||
if (!membership) throw new Error('Failed to delete workspace membership');
|
||||
if (!membership) throw new Error("Failed to delete workspace membership");
|
||||
|
||||
await Key.deleteMany({
|
||||
receiver: membership.user,
|
||||
workspace: membership.workspace
|
||||
workspace: membership.workspace,
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
membership
|
||||
membership,
|
||||
});
|
||||
}
|
||||
|
||||
@ -421,18 +415,18 @@ export const toggleAutoCapitalization = async (req: Request, res: Response) => {
|
||||
|
||||
const workspace = await Workspace.findOneAndUpdate(
|
||||
{
|
||||
_id: workspaceId
|
||||
_id: workspaceId,
|
||||
},
|
||||
{
|
||||
autoCapitalization
|
||||
autoCapitalization,
|
||||
},
|
||||
{
|
||||
new: true
|
||||
new: true,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully changed autoCapitalization setting',
|
||||
workspace
|
||||
message: "Successfully changed autoCapitalization setting",
|
||||
workspace,
|
||||
});
|
||||
};
|
||||
|
@ -1,29 +1,29 @@
|
||||
/* 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, validateProviderAuthToken } from '../../helpers/auth';
|
||||
import { checkUserDevice } from '../../helpers/user';
|
||||
import { sendMail } from '../../helpers/nodemailer';
|
||||
import { TokenService } from '../../services';
|
||||
import { EELogService } from '../../ee/services';
|
||||
import { BadRequestError, InternalServerError } from '../../utils/errors';
|
||||
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 { LoginSRPDetail, User } from "../../models";
|
||||
import { createToken, issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
||||
import { checkUserDevice } from "../../helpers/user";
|
||||
import { sendMail } from "../../helpers/nodemailer";
|
||||
import { TokenService } from "../../services";
|
||||
import { EELogService } from "../../ee/services";
|
||||
import { BadRequestError, InternalServerError } from "../../utils/errors";
|
||||
import {
|
||||
ACTION_LOGIN,
|
||||
TOKEN_EMAIL_MFA,
|
||||
ACTION_LOGIN
|
||||
} from '../../variables';
|
||||
import { getChannelFromUserAgent } from '../../utils/posthog'; // TODO: move this
|
||||
} from "../../variables";
|
||||
import { getChannelFromUserAgent } from "../../utils/posthog"; // TODO: move this
|
||||
import {
|
||||
getHttpsEnabled,
|
||||
getJwtMfaLifetime,
|
||||
getJwtMfaSecret,
|
||||
getHttpsEnabled,
|
||||
} from '../../config';
|
||||
import { AuthProvider } from '../../models/user';
|
||||
} from "../../config";
|
||||
import { AuthProvider } from "../../models/user";
|
||||
|
||||
declare module 'jsonwebtoken' {
|
||||
declare module "jsonwebtoken" {
|
||||
export interface ProviderAuthJwtPayload extends jwt.JwtPayload {
|
||||
userId: string;
|
||||
email: string;
|
||||
@ -43,7 +43,7 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
const {
|
||||
email,
|
||||
providerAuthToken,
|
||||
clientPublicKey
|
||||
clientPublicKey,
|
||||
}: {
|
||||
email: string;
|
||||
clientPublicKey: string,
|
||||
@ -52,9 +52,9 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
|
||||
const user = await User.findOne({
|
||||
email,
|
||||
}).select('+salt +verifier');
|
||||
}).select("+salt +verifier");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
if (user.authProvider) {
|
||||
await validateProviderAuthToken({
|
||||
@ -68,13 +68,13 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
server.init(
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier
|
||||
verifier: user.verifier,
|
||||
},
|
||||
async () => {
|
||||
// generate server-side public key
|
||||
const serverPublicKey = server.getPublicKey();
|
||||
await LoginSRPDetail.findOneAndReplace({
|
||||
email: email
|
||||
email: email,
|
||||
}, {
|
||||
email,
|
||||
userId: user.id,
|
||||
@ -84,7 +84,7 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
|
||||
return res.status(200).send({
|
||||
serverPublicKey,
|
||||
salt: user.salt
|
||||
salt: user.salt,
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -92,7 +92,7 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
return res.status(400).send({
|
||||
message: 'Failed to start authentication process'
|
||||
message: "Failed to start authentication process",
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -107,15 +107,15 @@ export const login1 = async (req: Request, res: Response) => {
|
||||
export const login2 = async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
||||
if (!req.headers['user-agent']) throw InternalServerError({ message: 'User-Agent header is required' });
|
||||
if (!req.headers["user-agent"]) throw InternalServerError({ message: "User-Agent header is required" });
|
||||
|
||||
const { email, clientProof, providerAuthToken } = req.body;
|
||||
|
||||
const user = await User.findOne({
|
||||
email,
|
||||
}).select('+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices');
|
||||
}).select("+salt +verifier +encryptionVersion +protectedKey +protectedKeyIV +protectedKeyTag +publicKey +encryptedPrivateKey +iv +tag +devices");
|
||||
|
||||
if (!user) throw new Error('Failed to find user');
|
||||
if (!user) throw new Error("Failed to find user");
|
||||
|
||||
if (user.authProvider) {
|
||||
await validateProviderAuthToken({
|
||||
@ -136,7 +136,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
{
|
||||
salt: user.salt,
|
||||
verifier: user.verifier,
|
||||
b: loginSRPDetail.serverBInt
|
||||
b: loginSRPDetail.serverBInt,
|
||||
},
|
||||
async () => {
|
||||
server.setClientPublicKey(loginSRPDetail.clientPublicKey);
|
||||
@ -150,52 +150,52 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
// generate temporary MFA token
|
||||
const token = createToken({
|
||||
payload: {
|
||||
userId: user._id.toString()
|
||||
userId: user._id.toString(),
|
||||
},
|
||||
expiresIn: await getJwtMfaLifetime(),
|
||||
secret: await getJwtMfaSecret()
|
||||
secret: await getJwtMfaSecret(),
|
||||
});
|
||||
|
||||
const code = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_MFA,
|
||||
email
|
||||
email,
|
||||
});
|
||||
|
||||
// send MFA code [code] to [email]
|
||||
await sendMail({
|
||||
template: 'emailMfa.handlebars',
|
||||
subjectLine: 'Infisical MFA code',
|
||||
template: "emailMfa.handlebars",
|
||||
subjectLine: "Infisical MFA code",
|
||||
recipients: [user.email],
|
||||
substitutions: {
|
||||
code
|
||||
}
|
||||
code,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
mfaEnabled: true,
|
||||
token
|
||||
token,
|
||||
});
|
||||
}
|
||||
|
||||
await checkUserDevice({
|
||||
user,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// issue tokens
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
// case: user does not have MFA enablgged
|
||||
@ -221,7 +221,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
publicKey: user.publicKey,
|
||||
encryptedPrivateKey: user.encryptedPrivateKey,
|
||||
iv: user.iv,
|
||||
tag: user.tag
|
||||
tag: user.tag,
|
||||
}
|
||||
|
||||
if (
|
||||
@ -236,21 +236,21 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
|
||||
const loginAction = await EELogService.createAction({
|
||||
name: ACTION_LOGIN,
|
||||
userId: user._id
|
||||
userId: user._id,
|
||||
});
|
||||
|
||||
loginAction && await EELogService.createLog({
|
||||
userId: user._id,
|
||||
actions: [loginAction],
|
||||
channel: getChannelFromUserAgent(req.headers['user-agent']),
|
||||
ipAddress: req.realIP
|
||||
channel: getChannelFromUserAgent(req.headers["user-agent"]),
|
||||
ipAddress: req.realIP,
|
||||
});
|
||||
|
||||
return res.status(200).send(response);
|
||||
}
|
||||
|
||||
return res.status(400).send({
|
||||
message: 'Failed to authenticate. Try again?'
|
||||
message: "Failed to authenticate. Try again?",
|
||||
});
|
||||
}
|
||||
);
|
||||
@ -258,7 +258,7 @@ export const login2 = async (req: Request, res: Response) => {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
return res.status(400).send({
|
||||
message: 'Failed to authenticate. Try again?'
|
||||
message: "Failed to authenticate. Try again?",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as secretsController from './secretsController';
|
||||
import * as workspacesController from './workspacesController';
|
||||
import * as authController from './authController';
|
||||
import * as signupController from './signupController';
|
||||
import * as secretsController from "./secretsController";
|
||||
import * as workspacesController from "./workspacesController";
|
||||
import * as authController from "./authController";
|
||||
import * as signupController from "./signupController";
|
||||
|
||||
export {
|
||||
authController,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { SecretService, EventService } from "../../services";
|
||||
import { EventService, SecretService } from "../../services";
|
||||
import { eventPushSecrets } from "../../events";
|
||||
import { BotService } from "../../services";
|
||||
import { repackageSecretToRaw } from "../../helpers/secrets";
|
||||
@ -25,18 +25,18 @@ export const getSecretsRaw = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
secrets: secrets.map((secret) => {
|
||||
const rep = repackageSecretToRaw({
|
||||
secret,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
return rep;
|
||||
})
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -62,14 +62,14 @@ export const getSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
secret: repackageSecretToRaw({
|
||||
secret,
|
||||
key
|
||||
})
|
||||
key,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -86,26 +86,26 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
||||
type,
|
||||
secretValue,
|
||||
secretComment,
|
||||
secretPath = "/"
|
||||
secretPath = "/",
|
||||
} = req.body;
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||
plaintext: secretName,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||
plaintext: secretValue,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||
plaintext: secretComment,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
const secret = await SecretService.createSecret({
|
||||
@ -123,7 +123,7 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
||||
secretPath,
|
||||
secretCommentCiphertext: secretCommentEncrypted.ciphertext,
|
||||
secretCommentIV: secretCommentEncrypted.iv,
|
||||
secretCommentTag: secretCommentEncrypted.tag
|
||||
secretCommentTag: secretCommentEncrypted.tag,
|
||||
});
|
||||
|
||||
await EventService.handleEvent({
|
||||
@ -139,8 +139,8 @@ export const createSecretRaw = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
secret: repackageSecretToRaw({
|
||||
secret: secretWithoutBlindIndex,
|
||||
key
|
||||
})
|
||||
key,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -160,12 +160,12 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
} = req.body;
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8({
|
||||
plaintext: secretValue,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
const secret = await SecretService.updateSecret({
|
||||
@ -190,8 +190,8 @@ export const updateSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
return res.status(200).send({
|
||||
secret: repackageSecretToRaw({
|
||||
secret,
|
||||
key
|
||||
})
|
||||
key,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -206,7 +206,7 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
environment,
|
||||
type,
|
||||
secretPath = "/"
|
||||
secretPath = "/",
|
||||
} = req.body;
|
||||
|
||||
const { secret } = await SecretService.deleteSecret({
|
||||
@ -226,14 +226,14 @@ export const deleteSecretByNameRaw = async (req: Request, res: Response) => {
|
||||
});
|
||||
|
||||
const key = await BotService.getWorkspaceKeyWithBot({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
secret: repackageSecretToRaw({
|
||||
secret,
|
||||
key
|
||||
})
|
||||
key,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
@ -324,7 +324,7 @@ export const createSecret = async (req: Request, res: Response) => {
|
||||
secretPath,
|
||||
secretCommentCiphertext,
|
||||
secretCommentIV,
|
||||
secretCommentTag
|
||||
secretCommentTag,
|
||||
});
|
||||
|
||||
await EventService.handleEvent({
|
||||
@ -395,7 +395,7 @@ export const deleteSecretByName = async (req: Request, res: Response) => {
|
||||
workspaceId,
|
||||
environment,
|
||||
type,
|
||||
secretPath = "/"
|
||||
secretPath = "/",
|
||||
} = req.body;
|
||||
|
||||
const { secret } = await SecretService.deleteSecret({
|
||||
|
@ -1,17 +1,17 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Request, Response } from 'express';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import { User, MembershipOrg } from '../../models';
|
||||
import { completeAccount } from '../../helpers/user';
|
||||
import jwt from "jsonwebtoken";
|
||||
import { Request, Response } from "express";
|
||||
import * as Sentry from "@sentry/node";
|
||||
import { MembershipOrg, User } from "../../models";
|
||||
import { completeAccount } from "../../helpers/user";
|
||||
import {
|
||||
initializeDefaultOrg
|
||||
} from '../../helpers/signup';
|
||||
import { issueAuthTokens, validateProviderAuthToken } from '../../helpers/auth';
|
||||
import { INVITED, ACCEPTED } from '../../variables';
|
||||
import { standardRequest } from '../../config/request';
|
||||
import { getLoopsApiKey, getHttpsEnabled, getJwtSignupSecret } from '../../config';
|
||||
import { BadRequestError } from '../../utils/errors';
|
||||
import { TelemetryService } from '../../services';
|
||||
initializeDefaultOrg,
|
||||
} from "../../helpers/signup";
|
||||
import { issueAuthTokens, validateProviderAuthToken } from "../../helpers/auth";
|
||||
import { ACCEPTED, INVITED } from "../../variables";
|
||||
import { standardRequest } from "../../config/request";
|
||||
import { getHttpsEnabled, getJwtSignupSecret, getLoopsApiKey } from "../../config";
|
||||
import { BadRequestError } from "../../utils/errors";
|
||||
import { TelemetryService } from "../../services";
|
||||
|
||||
/**
|
||||
* Complete setting up user by adding their personal and auth information as part of the
|
||||
@ -63,7 +63,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
// case 1: user doesn't exist.
|
||||
// case 2: user has already completed account
|
||||
return res.status(403).send({
|
||||
error: 'Failed to complete account for complete user'
|
||||
error: "Failed to complete account for complete user",
|
||||
});
|
||||
}
|
||||
|
||||
@ -74,16 +74,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
user,
|
||||
});
|
||||
} else {
|
||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers['authorization']?.split(' ', 2) ?? [null, null]
|
||||
const [AUTH_TOKEN_TYPE, AUTH_TOKEN_VALUE] = <[string, string]>req.headers["authorization"]?.split(" ", 2) ?? [null, null]
|
||||
if (AUTH_TOKEN_TYPE === null) {
|
||||
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
|
||||
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||
}
|
||||
if (AUTH_TOKEN_TYPE.toLowerCase() !== 'bearer') {
|
||||
if (AUTH_TOKEN_TYPE.toLowerCase() !== "bearer") {
|
||||
throw BadRequestError({ message: `The provided authentication type '${AUTH_TOKEN_TYPE}' is not supported.` })
|
||||
}
|
||||
if (AUTH_TOKEN_VALUE === null) {
|
||||
throw BadRequestError({
|
||||
message: 'Missing Authorization Body in the request header',
|
||||
message: "Missing Authorization Body in the request header",
|
||||
})
|
||||
}
|
||||
|
||||
@ -110,16 +110,16 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
});
|
||||
|
||||
if (!user)
|
||||
throw new Error('Failed to complete account for non-existent user'); // ensure user is non-null
|
||||
throw new Error("Failed to complete account for non-existent user"); // ensure user is non-null
|
||||
|
||||
// initialize default organization and workspace
|
||||
await initializeDefaultOrg({
|
||||
organizationName,
|
||||
user
|
||||
user,
|
||||
});
|
||||
|
||||
// update organization membership statuses that are
|
||||
@ -127,11 +127,11 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
await MembershipOrg.updateMany(
|
||||
{
|
||||
inviteEmail: email,
|
||||
status: INVITED
|
||||
status: INVITED,
|
||||
},
|
||||
{
|
||||
user,
|
||||
status: ACCEPTED
|
||||
status: ACCEPTED,
|
||||
}
|
||||
);
|
||||
|
||||
@ -139,7 +139,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
const tokens = await issueAuthTokens({
|
||||
userId: user._id,
|
||||
ip: req.realIP,
|
||||
userAgent: req.headers['user-agent'] ?? ''
|
||||
userAgent: req.headers["user-agent"] ?? "",
|
||||
});
|
||||
|
||||
token = tokens.token;
|
||||
@ -150,45 +150,45 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
||||
"email": email,
|
||||
"eventName": "Sign Up",
|
||||
"firstName": firstName,
|
||||
"lastName": lastName
|
||||
"lastName": lastName,
|
||||
}, {
|
||||
headers: {
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer " + (await getLoopsApiKey())
|
||||
"Authorization": "Bearer " + (await getLoopsApiKey()),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// store (refresh) token in httpOnly cookie
|
||||
res.cookie('jid', tokens.refreshToken, {
|
||||
res.cookie("jid", tokens.refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
sameSite: 'strict',
|
||||
secure: await getHttpsEnabled()
|
||||
path: "/",
|
||||
sameSite: "strict",
|
||||
secure: await getHttpsEnabled(),
|
||||
});
|
||||
|
||||
const postHogClient = await TelemetryService.getPostHogClient();
|
||||
if (postHogClient) {
|
||||
postHogClient.capture({
|
||||
event: 'User Signed Up',
|
||||
event: "User Signed Up",
|
||||
distinctId: email,
|
||||
properties: {
|
||||
email,
|
||||
attributionSource
|
||||
}
|
||||
attributionSource,
|
||||
},
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
Sentry.setUser(null);
|
||||
Sentry.captureException(err);
|
||||
return res.status(400).send({
|
||||
message: 'Failed to complete account setup'
|
||||
message: "Failed to complete account setup",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully set up account',
|
||||
message: "Successfully set up account",
|
||||
user,
|
||||
token
|
||||
token,
|
||||
});
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Types } from 'mongoose';
|
||||
import { Secret } from '../../models';
|
||||
import { SecretService } from'../../services';
|
||||
import { Request, Response } from "express";
|
||||
import { Types } from "mongoose";
|
||||
import { Secret } from "../../models";
|
||||
import { SecretService } from"../../services";
|
||||
|
||||
/**
|
||||
* Return whether or not all secrets in workspace with id [workspaceId]
|
||||
@ -16,8 +16,8 @@ export const getWorkspaceBlindIndexStatus = async (req: Request, res: Response)
|
||||
const secretsWithoutBlindIndex = await Secret.countDocuments({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
secretBlindIndex: {
|
||||
$exists: false
|
||||
}
|
||||
$exists: false,
|
||||
},
|
||||
});
|
||||
|
||||
return res.status(200).send(secretsWithoutBlindIndex === 0);
|
||||
@ -30,11 +30,11 @@ export const getWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
const { workspaceId } = req.params;
|
||||
|
||||
const secrets = await Secret.find({
|
||||
workspace: new Types.ObjectId (workspaceId)
|
||||
workspace: new Types.ObjectId (workspaceId),
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
secrets
|
||||
secrets,
|
||||
});
|
||||
}
|
||||
|
||||
@ -51,14 +51,14 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
|
||||
const { workspaceId } = req.params;
|
||||
const {
|
||||
secretsToUpdate
|
||||
secretsToUpdate,
|
||||
}: {
|
||||
secretsToUpdate: SecretToUpdate[];
|
||||
} = req.body;
|
||||
|
||||
// get secret blind index salt
|
||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||
workspaceId: new Types.ObjectId(workspaceId)
|
||||
workspaceId: new Types.ObjectId(workspaceId),
|
||||
});
|
||||
|
||||
// update secret blind indices
|
||||
@ -66,18 +66,18 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
secretsToUpdate.map(async (secretToUpdate: SecretToUpdate) => {
|
||||
const secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||
secretName: secretToUpdate.secretName,
|
||||
salt
|
||||
salt,
|
||||
});
|
||||
|
||||
return ({
|
||||
updateOne: {
|
||||
filter: {
|
||||
_id: new Types.ObjectId(secretToUpdate._id)
|
||||
_id: new Types.ObjectId(secretToUpdate._id),
|
||||
},
|
||||
update: {
|
||||
secretBlindIndex
|
||||
}
|
||||
}
|
||||
secretBlindIndex,
|
||||
},
|
||||
},
|
||||
});
|
||||
})
|
||||
);
|
||||
@ -85,6 +85,6 @@ export const nameWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||
await Secret.bulkWrite(operations);
|
||||
|
||||
return res.status(200).send({
|
||||
message: 'Successfully named workspace secrets'
|
||||
message: "Successfully named workspace secrets",
|
||||
});
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { Action, SecretVersion } from '../../models';
|
||||
import { ActionNotFoundError } from '../../../utils/errors';
|
||||
import { Request, Response } from "express";
|
||||
import { Action } from "../../models";
|
||||
import { ActionNotFoundError } from "../../../utils/errors";
|
||||
|
||||
export const getAction = async (req: Request, res: Response) => {
|
||||
let action;
|
||||
@ -10,21 +10,21 @@ export const getAction = async (req: Request, res: Response) => {
|
||||
action = await Action
|
||||
.findById(actionId)
|
||||
.populate([
|
||||
'payload.secretVersions.oldSecretVersion',
|
||||
'payload.secretVersions.newSecretVersion'
|
||||
"payload.secretVersions.oldSecretVersion",
|
||||
"payload.secretVersions.newSecretVersion",
|
||||
]);
|
||||
|
||||
if (!action) throw ActionNotFoundError({
|
||||
message: 'Failed to find action'
|
||||
message: "Failed to find action",
|
||||
});
|
||||
|
||||
} catch (err) {
|
||||
throw ActionNotFoundError({
|
||||
message: 'Failed to find action'
|
||||
message: "Failed to find action",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
action
|
||||
action,
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { EELicenseService } from '../../services';
|
||||
import { getLicenseServerUrl } from '../../../config';
|
||||
import { licenseServerKeyRequest } from '../../../config/request';
|
||||
import { Request, Response } from "express";
|
||||
import { EELicenseService } from "../../services";
|
||||
import { getLicenseServerUrl } from "../../../config";
|
||||
import { licenseServerKeyRequest } from "../../../config/request";
|
||||
|
||||
/**
|
||||
* Return available cloud product information.
|
||||
@ -11,9 +11,9 @@ import { licenseServerKeyRequest } from '../../../config/request';
|
||||
* @returns
|
||||
*/
|
||||
export const getCloudProducts = async (req: Request, res: Response) => {
|
||||
const billingCycle = req.query['billing-cycle'] as string;
|
||||
const billingCycle = req.query["billing-cycle"] as string;
|
||||
|
||||
if (EELicenseService.instanceType === 'cloud') {
|
||||
if (EELicenseService.instanceType === "cloud") {
|
||||
const { data } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||
);
|
||||
@ -23,6 +23,6 @@ export const getCloudProducts = async (req: Request, res: Response) => {
|
||||
|
||||
return res.status(200).send({
|
||||
head: [],
|
||||
rows: []
|
||||
rows: [],
|
||||
});
|
||||
}
|
||||
|
@ -1,19 +1,17 @@
|
||||
import * as stripeController from './stripeController';
|
||||
import * as secretController from './secretController';
|
||||
import * as secretSnapshotController from './secretSnapshotController';
|
||||
import * as organizationsController from './organizationsController';
|
||||
import * as workspaceController from './workspaceController';
|
||||
import * as actionController from './actionController';
|
||||
import * as membershipController from './membershipController';
|
||||
import * as cloudProductsController from './cloudProductsController';
|
||||
import * as secretController from "./secretController";
|
||||
import * as secretSnapshotController from "./secretSnapshotController";
|
||||
import * as organizationsController from "./organizationsController";
|
||||
import * as workspaceController from "./workspaceController";
|
||||
import * as actionController from "./actionController";
|
||||
import * as membershipController from "./membershipController";
|
||||
import * as cloudProductsController from "./cloudProductsController";
|
||||
|
||||
export {
|
||||
stripeController,
|
||||
secretController,
|
||||
secretSnapshotController,
|
||||
organizationsController,
|
||||
workspaceController,
|
||||
actionController,
|
||||
membershipController,
|
||||
cloudProductsController
|
||||
cloudProductsController,
|
||||
}
|
@ -3,8 +3,7 @@ import { Membership, Workspace } from "../../../models";
|
||||
import { IMembershipPermission } from "../../../models/membership";
|
||||
import { BadRequestError, UnauthorizedRequestError } from "../../../utils/errors";
|
||||
import { ADMIN, MEMBER } from "../../../variables/organization";
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../../variables';
|
||||
import { Builder } from "builder-pattern"
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../../variables";
|
||||
import _ from "lodash";
|
||||
|
||||
export const denyMembershipPermissions = async (req: Request, res: Response) => {
|
||||
@ -15,10 +14,10 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
|
||||
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();
|
||||
return {
|
||||
environmentSlug: permission.environmentSlug,
|
||||
ability: permission.ability
|
||||
}
|
||||
})
|
||||
|
||||
const sanitizedMembershipPermissionsUnique = _.uniqWith(sanitizedMembershipPermissions, _.isEqual)
|
||||
@ -39,7 +38,7 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
|
||||
throw BadRequestError({ message: "Something went wrong when locating the related workspace" })
|
||||
}
|
||||
|
||||
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, 'slug')));
|
||||
const uniqueEnvironmentSlugs = new Set(_.uniq(_.map(relatedWorkspace.environments, "slug")));
|
||||
|
||||
sanitizedMembershipPermissionsUnique.forEach(permission => {
|
||||
if (!uniqueEnvironmentSlugs.has(permission.environmentSlug)) {
|
||||
@ -59,6 +58,6 @@ export const denyMembershipPermissions = async (req: Request, res: Response) =>
|
||||
}
|
||||
|
||||
res.send({
|
||||
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions
|
||||
permissionsDenied: updatedMembershipWithPermissions.deniedPermissions,
|
||||
})
|
||||
}
|
||||
|
@ -1,10 +1,20 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { getLicenseServerUrl } from '../../../config';
|
||||
import { licenseServerKeyRequest } from '../../../config/request';
|
||||
import { EELicenseService } from '../../services';
|
||||
import { Request, Response } from "express";
|
||||
import { getLicenseServerUrl } from "../../../config";
|
||||
import { licenseServerKeyRequest } from "../../../config/request";
|
||||
import { EELicenseService } from "../../services";
|
||||
|
||||
export const getOrganizationPlansTable = async (req: Request, res: Response) => {
|
||||
const billingCycle = req.query.billingCycle as string;
|
||||
|
||||
const { data } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/cloud-products?billing-cycle=${billingCycle}`
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the organization's current plan and allowed feature set
|
||||
* Return the organization current plan's feature set
|
||||
*/
|
||||
export const getOrganizationPlan = async (req: Request, res: Response) => {
|
||||
const { organizationId } = req.params;
|
||||
@ -18,26 +28,58 @@ export const getOrganizationPlan = async (req: Request, res: Response) => {
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the organization plan to product with id [productId]
|
||||
* Return the organization's current plan's billing info
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const updateOrganizationPlan = async (req: Request, res: Response) => {
|
||||
const {
|
||||
productId
|
||||
} = req.body;
|
||||
|
||||
const { data } = await licenseServerKeyRequest.patch(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan`,
|
||||
{
|
||||
productId
|
||||
}
|
||||
export const getOrganizationPlanBillingInfo = async (req: Request, res: Response) => {
|
||||
const { data } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan/billing`
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the organization's current plan's feature table
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const getOrganizationPlanTable = async (req: Request, res: Response) => {
|
||||
const { data } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/cloud-plan/table`
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
export const getOrganizationBillingDetails = async (req: Request, res: Response) => {
|
||||
const { data } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details`
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
export const updateOrganizationBillingDetails = async (req: Request, res: Response) => {
|
||||
const {
|
||||
name,
|
||||
email
|
||||
} = req.body;
|
||||
|
||||
const { data } = await licenseServerKeyRequest.patch(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details`,
|
||||
{
|
||||
...(name ? { name } : {}),
|
||||
...(email ? { email } : {})
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the organization's payment methods on file
|
||||
*/
|
||||
@ -46,30 +88,28 @@ export const getOrganizationPmtMethods = async (req: Request, res: Response) =>
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
pmtMethods
|
||||
});
|
||||
return res.status(200).send(pmtMethods);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Stripe session URL to add payment method for organization
|
||||
* Return URL to add payment method for organization
|
||||
*/
|
||||
export const addOrganizationPmtMethod = async (req: Request, res: Response) => {
|
||||
const {
|
||||
success_url,
|
||||
cancel_url
|
||||
cancel_url,
|
||||
} = req.body;
|
||||
|
||||
const { data: { url } } = await licenseServerKeyRequest.post(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/payment-methods`,
|
||||
{
|
||||
success_url,
|
||||
cancel_url
|
||||
cancel_url,
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send({
|
||||
url
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
@ -81,4 +121,53 @@ export const deleteOrganizationPmtMethod = async (req: Request, res: Response) =
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the organization's tax ids on file
|
||||
*/
|
||||
export const getOrganizationTaxIds = async (req: Request, res: Response) => {
|
||||
const { data: { tax_ids } } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids`
|
||||
);
|
||||
|
||||
return res.status(200).send(tax_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add tax id to organization
|
||||
*/
|
||||
export const addOrganizationTaxId = async (req: Request, res: Response) => {
|
||||
const {
|
||||
type,
|
||||
value
|
||||
} = req.body;
|
||||
|
||||
const { data } = await licenseServerKeyRequest.post(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids`,
|
||||
{
|
||||
type,
|
||||
value
|
||||
}
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
export const deleteOrganizationTaxId = async (req: Request, res: Response) => {
|
||||
const { taxId } = req.params;
|
||||
|
||||
const { data } = await licenseServerKeyRequest.delete(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/billing-details/tax-ids/${taxId}`,
|
||||
);
|
||||
|
||||
return res.status(200).send(data);
|
||||
}
|
||||
|
||||
export const getOrganizationInvoices = async (req: Request, res: Response) => {
|
||||
const { data: { invoices } } = await licenseServerKeyRequest.get(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${req.organization.customerId}/invoices`
|
||||
);
|
||||
|
||||
return res.status(200).send(invoices);
|
||||
}
|
@ -54,23 +54,20 @@ export const getSecretVersions = async (req: Request, res: Response) => {
|
||||
}
|
||||
}
|
||||
*/
|
||||
const { secretId, workspaceId, environment, folderId } = req.params;
|
||||
const { secretId } = req.params;
|
||||
|
||||
const offset: number = parseInt(req.query.offset as string);
|
||||
const limit: number = parseInt(req.query.limit as string);
|
||||
|
||||
const secretVersions = await SecretVersion.find({
|
||||
secret: secretId,
|
||||
workspace: workspaceId,
|
||||
environment,
|
||||
folder: folderId,
|
||||
secret: secretId
|
||||
})
|
||||
.sort({ createdAt: -1 })
|
||||
.skip(offset)
|
||||
.limit(limit);
|
||||
|
||||
return res.status(200).send({
|
||||
secretVersions,
|
||||
secretVersions
|
||||
});
|
||||
};
|
||||
|
||||
@ -135,7 +132,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
||||
// validate secret version
|
||||
const oldSecretVersion = await SecretVersion.findOne({
|
||||
secret: secretId,
|
||||
version,
|
||||
version
|
||||
}).select("+secretBlindIndex");
|
||||
|
||||
if (!oldSecretVersion) throw new Error("Failed to find secret version");
|
||||
@ -154,7 +151,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
||||
secretValueTag,
|
||||
algorithm,
|
||||
folder,
|
||||
keyEncoding,
|
||||
keyEncoding
|
||||
} = oldSecretVersion;
|
||||
|
||||
// update secret
|
||||
@ -162,7 +159,7 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
||||
secretId,
|
||||
{
|
||||
$inc: {
|
||||
version: 1,
|
||||
version: 1
|
||||
},
|
||||
workspace,
|
||||
type,
|
||||
@ -177,10 +174,10 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
||||
secretValueTag,
|
||||
folderId: folder,
|
||||
algorithm,
|
||||
keyEncoding,
|
||||
keyEncoding
|
||||
},
|
||||
{
|
||||
new: true,
|
||||
new: true
|
||||
}
|
||||
);
|
||||
|
||||
@ -204,17 +201,17 @@ export const rollbackSecretVersion = async (req: Request, res: Response) => {
|
||||
secretValueTag,
|
||||
folder,
|
||||
algorithm,
|
||||
keyEncoding,
|
||||
keyEncoding
|
||||
}).save();
|
||||
|
||||
// take secret snapshot
|
||||
await EESecretService.takeSecretSnapshot({
|
||||
workspaceId: secret.workspace,
|
||||
environment,
|
||||
folderId: folder,
|
||||
folderId: folder
|
||||
});
|
||||
|
||||
return res.status(200).send({
|
||||
secret,
|
||||
secret
|
||||
});
|
||||
};
|
||||
|
@ -17,11 +17,11 @@ export const getSecretSnapshot = async (req: Request, res: Response) => {
|
||||
const secretSnapshot = await SecretSnapshot.findById(secretSnapshotId)
|
||||
.lean()
|
||||
.populate<{ secretVersions: ISecretVersion[] }>({
|
||||
path: 'secretVersions',
|
||||
path: "secretVersions",
|
||||
populate: {
|
||||
path: 'tags',
|
||||
model: 'Tag'
|
||||
}
|
||||
path: "tags",
|
||||
model: "Tag",
|
||||
},
|
||||
})
|
||||
.populate<{ folderVersion: TFolderRootVersionSchema }>("folderVersion");
|
||||
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { Request, Response } from 'express';
|
||||
import Stripe from 'stripe';
|
||||
import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
|
||||
|
||||
/**
|
||||
* Handle service provisioning/un-provisioning via Stripe
|
||||
* @param req
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
export const handleWebhook = async (req: Request, res: Response) => {
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: '2022-08-01'
|
||||
});
|
||||
|
||||
// check request for valid stripe signature
|
||||
const sig = req.headers['stripe-signature'] as string;
|
||||
const event = stripe.webhooks.constructEvent(
|
||||
req.body,
|
||||
sig,
|
||||
await getStripeWebhookSecret()
|
||||
);
|
||||
|
||||
switch (event.type) {
|
||||
case '':
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
return res.json({ received: true });
|
||||
};
|
@ -2,11 +2,11 @@ import { Request, Response } from "express";
|
||||
import { PipelineStage, Types } from "mongoose";
|
||||
import { Secret } from "../../../models";
|
||||
import {
|
||||
SecretSnapshot,
|
||||
Log,
|
||||
SecretVersion,
|
||||
ISecretVersion,
|
||||
FolderVersion,
|
||||
ISecretVersion,
|
||||
Log,
|
||||
SecretSnapshot,
|
||||
SecretVersion,
|
||||
TFolderRootVersionSchema,
|
||||
} from "../../models";
|
||||
import { EESecretService } from "../../services";
|
||||
|
@ -1,17 +1,17 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Action } from '../models';
|
||||
import { Types } from "mongoose";
|
||||
import { Action } from "../models";
|
||||
import {
|
||||
getLatestNSecretSecretVersionIds,
|
||||
getLatestSecretVersionIds,
|
||||
getLatestNSecretSecretVersionIds
|
||||
} from '../helpers/secretVersion';
|
||||
} from "../helpers/secretVersion";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from '../../variables';
|
||||
} from "../../variables";
|
||||
|
||||
/**
|
||||
* Create an (audit) action for updating secrets
|
||||
@ -26,7 +26,7 @@ const createActionUpdateSecret = async ({
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
@ -37,11 +37,11 @@ const createActionUpdateSecret = async ({
|
||||
}) => {
|
||||
const latestSecretVersions = (await getLatestNSecretSecretVersionIds({
|
||||
secretIds,
|
||||
n: 2
|
||||
n: 2,
|
||||
}))
|
||||
.map((s) => ({
|
||||
oldSecretVersion: s.versions[0]._id,
|
||||
newSecretVersion: s.versions[1]._id
|
||||
newSecretVersion: s.versions[1]._id,
|
||||
}));
|
||||
|
||||
const action = await new Action({
|
||||
@ -51,8 +51,8 @@ const createActionUpdateSecret = async ({
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
workspace: workspaceId,
|
||||
payload: {
|
||||
secretVersions: latestSecretVersions
|
||||
}
|
||||
secretVersions: latestSecretVersions,
|
||||
},
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
@ -72,7 +72,7 @@ const createActionSecret = async ({
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
@ -84,10 +84,10 @@ const createActionSecret = async ({
|
||||
// case: action is adding, deleting, or reading secrets
|
||||
// -> add new secret versions
|
||||
const latestSecretVersions = (await getLatestSecretVersionIds({
|
||||
secretIds
|
||||
secretIds,
|
||||
}))
|
||||
.map((s) => ({
|
||||
newSecretVersion: s.versionId
|
||||
newSecretVersion: s.versionId,
|
||||
}));
|
||||
|
||||
const action = await new Action({
|
||||
@ -97,8 +97,8 @@ const createActionSecret = async ({
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
workspace: workspaceId,
|
||||
payload: {
|
||||
secretVersions: latestSecretVersions
|
||||
}
|
||||
secretVersions: latestSecretVersions,
|
||||
},
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
@ -116,7 +116,7 @@ const createActionClient = ({
|
||||
name,
|
||||
userId,
|
||||
serviceAccountId,
|
||||
serviceTokenDataId
|
||||
serviceTokenDataId,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
@ -127,7 +127,7 @@ const createActionClient = ({
|
||||
name,
|
||||
user: userId,
|
||||
serviceAccount: serviceAccountId,
|
||||
serviceTokenData: serviceTokenDataId
|
||||
serviceTokenData: serviceTokenDataId,
|
||||
}).save();
|
||||
|
||||
return action;
|
||||
@ -162,27 +162,27 @@ const createActionHelper = async ({
|
||||
case ACTION_LOGOUT:
|
||||
action = await createActionClient({
|
||||
name,
|
||||
userId
|
||||
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');
|
||||
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
|
||||
secretIds,
|
||||
});
|
||||
break;
|
||||
case ACTION_UPDATE_SECRETS:
|
||||
if (!workspaceId || !secretIds) throw new Error('Missing required params workspace id or secret ids to create action secret');
|
||||
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
|
||||
secretIds,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@ -191,5 +191,5 @@ const createActionHelper = async ({
|
||||
}
|
||||
|
||||
export {
|
||||
createActionHelper
|
||||
createActionHelper,
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Types } from "mongoose";
|
||||
import _ from "lodash";
|
||||
import { Membership } from "../../models";
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from '../../variables';
|
||||
import { PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS } from "../../variables";
|
||||
|
||||
export const userHasWorkspaceAccess = async (userId: Types.ObjectId, workspaceId: Types.ObjectId, environment: string, action: any) => {
|
||||
const membershipForWorkspace = await Membership.findOne({ workspace: workspaceId, user: userId })
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IAction,
|
||||
Log,
|
||||
IAction
|
||||
} from '../models';
|
||||
} from "../models";
|
||||
|
||||
/**
|
||||
* Create an (audit) log
|
||||
@ -21,7 +21,7 @@ const createLogHelper = async ({
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress
|
||||
ipAddress,
|
||||
}: {
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
@ -39,12 +39,12 @@ const createLogHelper = async ({
|
||||
actionNames: actions.map((a) => a.name),
|
||||
actions,
|
||||
channel,
|
||||
ipAddress
|
||||
ipAddress,
|
||||
}).save();
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
export {
|
||||
createLogHelper
|
||||
createLogHelper,
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Secret, ISecret } from "../../models";
|
||||
import { Secret } from "../../models";
|
||||
import {
|
||||
FolderVersion,
|
||||
ISecretVersion,
|
||||
SecretSnapshot,
|
||||
SecretVersion,
|
||||
ISecretVersion,
|
||||
FolderVersion,
|
||||
} from "../models";
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,7 @@
|
||||
import requireLicenseAuth from './requireLicenseAuth';
|
||||
import requireSecretSnapshotAuth from './requireSecretSnapshotAuth';
|
||||
import requireLicenseAuth from "./requireLicenseAuth";
|
||||
import requireSecretSnapshotAuth from "./requireSecretSnapshotAuth";
|
||||
|
||||
export {
|
||||
requireLicenseAuth,
|
||||
requireSecretSnapshotAuth
|
||||
requireSecretSnapshotAuth,
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
|
||||
/**
|
||||
* Validate if organization hosting meets license requirements to
|
||||
@ -7,7 +7,7 @@ import { Request, Response, NextFunction } from 'express';
|
||||
* @param {String[]} obj.acceptedTiers
|
||||
*/
|
||||
const requireLicenseAuth = ({
|
||||
acceptedTiers
|
||||
acceptedTiers,
|
||||
}: {
|
||||
acceptedTiers: string[];
|
||||
}) => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { UnauthorizedRequestError, SecretSnapshotNotFoundError } from '../../utils/errors';
|
||||
import { SecretSnapshot } from '../models';
|
||||
import { NextFunction, Request, Response } from "express";
|
||||
import { SecretSnapshotNotFoundError } from "../../utils/errors";
|
||||
import { SecretSnapshot } from "../models";
|
||||
import {
|
||||
validateMembership
|
||||
} from '../../helpers/membership';
|
||||
validateMembership,
|
||||
} from "../../helpers/membership";
|
||||
|
||||
/**
|
||||
* Validate if user on request has proper membership for secret snapshot
|
||||
@ -15,7 +15,7 @@ import {
|
||||
const requireSecretSnapshotAuth = ({
|
||||
acceptedRoles,
|
||||
}: {
|
||||
acceptedRoles: Array<'admin' | 'member'>;
|
||||
acceptedRoles: Array<"admin" | "member">;
|
||||
}) => {
|
||||
return async (req: Request, res: Response, next: NextFunction) => {
|
||||
const { secretSnapshotId } = req.params;
|
||||
@ -24,14 +24,14 @@ const requireSecretSnapshotAuth = ({
|
||||
|
||||
if (!secretSnapshot) {
|
||||
return next(SecretSnapshotNotFoundError({
|
||||
message: 'Failed to find secret snapshot'
|
||||
message: "Failed to find secret snapshot",
|
||||
}));
|
||||
}
|
||||
|
||||
await validateMembership({
|
||||
userId: req.user._id,
|
||||
workspaceId: secretSnapshot.workspace,
|
||||
acceptedRoles
|
||||
acceptedRoles,
|
||||
});
|
||||
|
||||
req.secretSnapshot = secretSnapshot as any;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Schema, model, Types } from 'mongoose';
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
} from '../../variables';
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from "../../variables";
|
||||
|
||||
export interface IAction {
|
||||
name: string;
|
||||
@ -30,42 +30,42 @@ const actionSchema = new Schema<IAction>(
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
]
|
||||
ACTION_DELETE_SECRETS,
|
||||
],
|
||||
},
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User'
|
||||
ref: "User",
|
||||
},
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceAccount'
|
||||
ref: "ServiceAccount",
|
||||
},
|
||||
serviceTokenData: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceTokenData'
|
||||
ref: "ServiceTokenData",
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace'
|
||||
ref: "Workspace",
|
||||
},
|
||||
payload: {
|
||||
secretVersions: [{
|
||||
oldSecretVersion: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'SecretVersion'
|
||||
ref: "SecretVersion",
|
||||
},
|
||||
newSecretVersion: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'SecretVersion'
|
||||
}
|
||||
}]
|
||||
}
|
||||
ref: "SecretVersion",
|
||||
},
|
||||
}],
|
||||
},
|
||||
}, {
|
||||
timestamps: true
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
const Action = model<IAction>('Action', actionSchema);
|
||||
const Action = model<IAction>("Action", actionSchema);
|
||||
|
||||
export default Action;
|
@ -1,4 +1,4 @@
|
||||
import { model, Schema, Types } from "mongoose";
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
|
||||
export type TFolderRootVersionSchema = {
|
||||
_id: Types.ObjectId;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { Schema, model, Types } from 'mongoose';
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import {
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_LOGIN,
|
||||
ACTION_LOGOUT,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
} from '../../variables';
|
||||
ACTION_UPDATE_SECRETS,
|
||||
} from "../../variables";
|
||||
|
||||
export interface ILog {
|
||||
_id: Types.ObjectId;
|
||||
@ -24,19 +24,19 @@ const logSchema = new Schema<ILog>(
|
||||
{
|
||||
user: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'User'
|
||||
ref: "User",
|
||||
},
|
||||
serviceAccount: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceAccount'
|
||||
ref: "ServiceAccount",
|
||||
},
|
||||
serviceTokenData: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'ServiceTokenData'
|
||||
ref: "ServiceTokenData",
|
||||
},
|
||||
workspace: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Workspace'
|
||||
ref: "Workspace",
|
||||
},
|
||||
actionNames: {
|
||||
type: [String],
|
||||
@ -46,28 +46,28 @@ const logSchema = new Schema<ILog>(
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_DELETE_SECRETS
|
||||
ACTION_DELETE_SECRETS,
|
||||
],
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
actions: [{
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'Action',
|
||||
required: true
|
||||
ref: "Action",
|
||||
required: true,
|
||||
}],
|
||||
channel: {
|
||||
type: String,
|
||||
enum: ['web', 'cli', 'auto', 'k8-operator', 'other'],
|
||||
required: true
|
||||
enum: ["web", "cli", "auto", "k8-operator", "other"],
|
||||
required: true,
|
||||
},
|
||||
ipAddress: {
|
||||
type: String
|
||||
}
|
||||
type: String,
|
||||
},
|
||||
}, {
|
||||
timestamps: true
|
||||
timestamps: true,
|
||||
}
|
||||
);
|
||||
|
||||
const Log = model<ILog>('Log', logSchema);
|
||||
const Log = model<ILog>("Log", logSchema);
|
||||
|
||||
export default Log;
|
@ -1,4 +1,4 @@
|
||||
import { Schema, model, Types } from "mongoose";
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
|
||||
export interface ISecretSnapshot {
|
||||
workspace: Types.ObjectId;
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { Schema, model, Types } from "mongoose";
|
||||
import { Schema, Types, model } from "mongoose";
|
||||
import {
|
||||
SECRET_SHARED,
|
||||
SECRET_PERSONAL,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
ENCODING_SCHEME_BASE64,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
SECRET_SHARED,
|
||||
} from "../../variables";
|
||||
|
||||
export interface ISecretVersion {
|
||||
@ -114,9 +114,9 @@ const secretVersionSchema = new Schema<ISecretVersion>(
|
||||
required: true,
|
||||
},
|
||||
tags: {
|
||||
ref: 'Tag',
|
||||
ref: "Tag",
|
||||
type: [Schema.Types.ObjectId],
|
||||
default: []
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1,15 +1,15 @@
|
||||
import express from 'express';
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import {
|
||||
validateRequest
|
||||
} from '../../../middleware';
|
||||
import { param } from 'express-validator';
|
||||
import { actionController } from '../../controllers/v1';
|
||||
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(),
|
||||
"/:actionId",
|
||||
param("actionId").exists().trim(),
|
||||
validateRequest,
|
||||
actionController.getAction
|
||||
);
|
||||
|
@ -1,18 +1,18 @@
|
||||
import express from 'express';
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireAuth,
|
||||
validateRequest
|
||||
} from '../../../middleware';
|
||||
import { query } from 'express-validator';
|
||||
import { cloudProductsController } from '../../controllers/v1';
|
||||
validateRequest,
|
||||
} from "../../../middleware";
|
||||
import { query } from "express-validator";
|
||||
import { cloudProductsController } from "../../controllers/v1";
|
||||
|
||||
router.get(
|
||||
'/',
|
||||
"/",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt", "apiKey"],
|
||||
}),
|
||||
query('billing-cycle').exists().isIn(['monthly', 'yearly']),
|
||||
query("billing-cycle").exists().isIn(["monthly", "yearly"]),
|
||||
validateRequest,
|
||||
cloudProductsController.getCloudProducts
|
||||
);
|
||||
|
@ -1,9 +1,9 @@
|
||||
import secret from './secret';
|
||||
import secretSnapshot from './secretSnapshot';
|
||||
import organizations from './organizations';
|
||||
import workspace from './workspace';
|
||||
import action from './action';
|
||||
import cloudProducts from './cloudProducts';
|
||||
import secret from "./secret";
|
||||
import secretSnapshot from "./secretSnapshot";
|
||||
import organizations from "./organizations";
|
||||
import workspace from "./workspace";
|
||||
import action from "./action";
|
||||
import cloudProducts from "./cloudProducts";
|
||||
|
||||
export {
|
||||
secret,
|
||||
@ -11,5 +11,5 @@ export {
|
||||
organizations,
|
||||
workspace,
|
||||
action,
|
||||
cloudProducts
|
||||
cloudProducts,
|
||||
}
|
@ -1,88 +1,208 @@
|
||||
import express from 'express';
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireAuth,
|
||||
requireOrganizationAuth,
|
||||
validateRequest
|
||||
} from '../../../middleware';
|
||||
import { param, body, query } from 'express-validator';
|
||||
import { organizationsController } from '../../controllers/v1';
|
||||
validateRequest,
|
||||
} from "../../../middleware";
|
||||
import { body, param, query } from "express-validator";
|
||||
import { organizationsController } from "../../controllers/v1";
|
||||
import {
|
||||
OWNER, ADMIN, MEMBER, ACCEPTED
|
||||
} from '../../../variables';
|
||||
ACCEPTED, ADMIN, MEMBER, OWNER,
|
||||
} from "../../../variables";
|
||||
|
||||
router.get(
|
||||
'/:organizationId/plan',
|
||||
"/:organizationId/plans/table",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param('organizationId').exists().trim(),
|
||||
query('workspaceId').optional().isString(),
|
||||
param("organizationId").exists().trim(),
|
||||
query("billingCycle").exists().isString().isIn(["monthly", "yearly"]),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationPlansTable
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/plan",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
query("workspaceId").optional().isString(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationPlan
|
||||
);
|
||||
|
||||
router.patch(
|
||||
'/:organizationId/plan',
|
||||
router.get(
|
||||
"/:organizationId/plan/billing",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param('organizationId').exists().trim(),
|
||||
body('productId').exists().isString(),
|
||||
param("organizationId").exists().trim(),
|
||||
query("workspaceId").optional().isString(),
|
||||
validateRequest,
|
||||
organizationsController.updateOrganizationPlan
|
||||
organizationsController.getOrganizationPlanBillingInfo
|
||||
);
|
||||
|
||||
router.get(
|
||||
'/:organizationId/billing-details/payment-methods',
|
||||
"/:organizationId/plan/table",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param('organizationId').exists().trim(),
|
||||
param("organizationId").exists().trim(),
|
||||
query("workspaceId").optional().isString(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationPlanTable
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/billing-details",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationBillingDetails
|
||||
);
|
||||
|
||||
router.patch(
|
||||
"/:organizationId/billing-details",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
body("email").optional().isString().trim(),
|
||||
body("name").optional().isString().trim(),
|
||||
validateRequest,
|
||||
organizationsController.updateOrganizationBillingDetails
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/billing-details/payment-methods",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationPmtMethods
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:organizationId/billing-details/payment-methods',
|
||||
"/:organizationId/billing-details/payment-methods",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param('organizationId').exists().trim(),
|
||||
body('success_url').exists().isString(),
|
||||
body('cancel_url').exists().isString(),
|
||||
param("organizationId").exists().trim(),
|
||||
body("success_url").exists().isString(),
|
||||
body("cancel_url").exists().isString(),
|
||||
validateRequest,
|
||||
organizationsController.addOrganizationPmtMethod
|
||||
);
|
||||
|
||||
router.delete(
|
||||
'/:organizationId/billing-details/payment-methods/:pmtMethodId',
|
||||
"/:organizationId/billing-details/payment-methods/:pmtMethodId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED]
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param('organizationId').exists().trim(),
|
||||
param("organizationId").exists().trim(),
|
||||
param("pmtMethodId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.deleteOrganizationPmtMethod
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/billing-details/tax-ids",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationTaxIds
|
||||
);
|
||||
|
||||
router.post(
|
||||
"/:organizationId/billing-details/tax-ids",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
body("type").exists().isString(),
|
||||
body("value").exists().isString(),
|
||||
validateRequest,
|
||||
organizationsController.addOrganizationTaxId
|
||||
);
|
||||
|
||||
router.delete(
|
||||
"/:organizationId/billing-details/tax-ids/:taxId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
param("taxId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.deleteOrganizationTaxId
|
||||
);
|
||||
|
||||
router.get(
|
||||
"/:organizationId/invoices",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireOrganizationAuth({
|
||||
acceptedRoles: [OWNER, ADMIN, MEMBER],
|
||||
acceptedStatuses: [ACCEPTED],
|
||||
}),
|
||||
param("organizationId").exists().trim(),
|
||||
validateRequest,
|
||||
organizationsController.getOrganizationInvoices
|
||||
);
|
||||
|
||||
export default router;
|
@ -1,46 +1,46 @@
|
||||
import express from 'express';
|
||||
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';
|
||||
validateRequest,
|
||||
} from "../../../middleware";
|
||||
import { body, param, query } from "express-validator";
|
||||
import { secretController } from "../../controllers/v1";
|
||||
import {
|
||||
ADMIN,
|
||||
MEMBER,
|
||||
PERMISSION_READ_SECRETS,
|
||||
PERMISSION_WRITE_SECRETS
|
||||
} from '../../../variables';
|
||||
PERMISSION_WRITE_SECRETS,
|
||||
} from "../../../variables";
|
||||
|
||||
router.get(
|
||||
'/:secretId/secret-versions',
|
||||
"/:secretId/secret-versions",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt", "apiKey"],
|
||||
}),
|
||||
requireSecretAuth({
|
||||
acceptedRoles: [ADMIN, MEMBER],
|
||||
requiredPermissions: [PERMISSION_READ_SECRETS]
|
||||
requiredPermissions: [PERMISSION_READ_SECRETS],
|
||||
}),
|
||||
param('secretId').exists().trim(),
|
||||
query('offset').exists().isInt(),
|
||||
query('limit').exists().isInt(),
|
||||
param("secretId").exists().trim(),
|
||||
query("offset").exists().isInt(),
|
||||
query("limit").exists().isInt(),
|
||||
validateRequest,
|
||||
secretController.getSecretVersions
|
||||
);
|
||||
|
||||
router.post(
|
||||
'/:secretId/secret-versions/rollback',
|
||||
"/:secretId/secret-versions/rollback",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt', 'apiKey']
|
||||
acceptedAuthModes: ["jwt", "apiKey"],
|
||||
}),
|
||||
requireSecretAuth({
|
||||
acceptedRoles: [ADMIN, MEMBER],
|
||||
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS]
|
||||
requiredPermissions: [PERMISSION_READ_SECRETS, PERMISSION_WRITE_SECRETS],
|
||||
}),
|
||||
param('secretId').exists().trim(),
|
||||
body('version').exists().isInt(),
|
||||
param("secretId").exists().trim(),
|
||||
body("version").exists().isInt(),
|
||||
secretController.rollbackSecretVersion
|
||||
);
|
||||
|
||||
|
@ -1,25 +1,25 @@
|
||||
import express from 'express';
|
||||
import express from "express";
|
||||
const router = express.Router();
|
||||
import {
|
||||
requireSecretSnapshotAuth
|
||||
} from '../../middleware';
|
||||
requireSecretSnapshotAuth,
|
||||
} from "../../middleware";
|
||||
import {
|
||||
requireAuth,
|
||||
validateRequest
|
||||
} from '../../../middleware';
|
||||
import { param } from 'express-validator';
|
||||
import { ADMIN, MEMBER } from '../../../variables';
|
||||
import { secretSnapshotController } from '../../controllers/v1';
|
||||
validateRequest,
|
||||
} from "../../../middleware";
|
||||
import { param } from "express-validator";
|
||||
import { ADMIN, MEMBER } from "../../../variables";
|
||||
import { secretSnapshotController } from "../../controllers/v1";
|
||||
|
||||
router.get(
|
||||
'/:secretSnapshotId',
|
||||
"/:secretSnapshotId",
|
||||
requireAuth({
|
||||
acceptedAuthModes: ['jwt']
|
||||
acceptedAuthModes: ["jwt"],
|
||||
}),
|
||||
requireSecretSnapshotAuth({
|
||||
acceptedRoles: [ADMIN, MEMBER]
|
||||
acceptedRoles: [ADMIN, MEMBER],
|
||||
}),
|
||||
param('secretSnapshotId').exists().trim(),
|
||||
param("secretSnapshotId").exists().trim(),
|
||||
validateRequest,
|
||||
secretSnapshotController.getSecretSnapshot
|
||||
);
|
||||
|
@ -1,7 +0,0 @@
|
||||
import express from 'express';
|
||||
const router = express.Router();
|
||||
import { stripeController } from '../../controllers/v1';
|
||||
|
||||
router.post('/webhook', stripeController.handleWebhook);
|
||||
|
||||
export default router;
|
@ -5,7 +5,7 @@ import {
|
||||
requireWorkspaceAuth,
|
||||
validateRequest,
|
||||
} from "../../../middleware";
|
||||
import { param, query, body } from "express-validator";
|
||||
import { body, param, query } from "express-validator";
|
||||
import { ADMIN, MEMBER } from "../../../variables";
|
||||
import { workspaceController } from "../../controllers/v1";
|
||||
|
||||
|
@ -1,22 +1,22 @@
|
||||
import * as Sentry from '@sentry/node';
|
||||
import NodeCache from 'node-cache';
|
||||
import * as Sentry from "@sentry/node";
|
||||
import NodeCache from "node-cache";
|
||||
import {
|
||||
getLicenseKey,
|
||||
getLicenseServerKey,
|
||||
getLicenseServerUrl
|
||||
} from '../../config';
|
||||
getLicenseServerUrl,
|
||||
} from "../../config";
|
||||
import {
|
||||
licenseKeyRequest,
|
||||
licenseServerKeyRequest,
|
||||
refreshLicenseKeyToken,
|
||||
refreshLicenseServerKeyToken,
|
||||
refreshLicenseKeyToken
|
||||
} from '../../config/request';
|
||||
import { Organization } from '../../models';
|
||||
import { OrganizationNotFoundError } from '../../utils/errors';
|
||||
} from "../../config/request";
|
||||
import { Organization } from "../../models";
|
||||
import { OrganizationNotFoundError } from "../../utils/errors";
|
||||
|
||||
interface FeatureSet {
|
||||
_id: string | null;
|
||||
slug: 'starter' | 'team' | 'pro' | 'enterprise' | null;
|
||||
slug: "starter" | "team" | "pro" | "enterprise" | null;
|
||||
tier: number;
|
||||
workspaceLimit: number | null;
|
||||
workspacesUsed: number;
|
||||
@ -30,6 +30,8 @@ interface FeatureSet {
|
||||
customRateLimits: boolean;
|
||||
customAlerts: boolean;
|
||||
auditLogs: boolean;
|
||||
status: 'incomplete' | 'incomplete_expired' | 'trialing' | 'active' | 'past_due' | 'canceled' | 'unpaid' | null;
|
||||
trial_end: number | null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,7 +44,7 @@ class EELicenseService {
|
||||
|
||||
private readonly _isLicenseValid: boolean; // TODO: deprecate
|
||||
|
||||
public instanceType: 'self-hosted' | 'enterprise-self-hosted' | 'cloud' = 'self-hosted';
|
||||
public instanceType: "self-hosted" | "enterprise-self-hosted" | "cloud" = "self-hosted";
|
||||
|
||||
public globalFeatureSet: FeatureSet = {
|
||||
_id: null,
|
||||
@ -55,11 +57,13 @@ class EELicenseService {
|
||||
environmentLimit: null,
|
||||
environmentsUsed: 0,
|
||||
secretVersioning: true,
|
||||
pitRecovery: true,
|
||||
pitRecovery: false,
|
||||
rbac: true,
|
||||
customRateLimits: true,
|
||||
customAlerts: true,
|
||||
auditLogs: false
|
||||
auditLogs: false,
|
||||
status: null,
|
||||
trial_end: null
|
||||
}
|
||||
|
||||
public localFeatureSet: NodeCache;
|
||||
@ -67,14 +71,14 @@ class EELicenseService {
|
||||
constructor() {
|
||||
this._isLicenseValid = true;
|
||||
this.localFeatureSet = new NodeCache({
|
||||
stdTTL: 300
|
||||
stdTTL: 300,
|
||||
});
|
||||
}
|
||||
|
||||
public async getPlan(organizationId: string, workspaceId?: string): Promise<FeatureSet> {
|
||||
try {
|
||||
if (this.instanceType === 'cloud') {
|
||||
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ''}`);
|
||||
if (this.instanceType === "cloud") {
|
||||
const cachedPlan = this.localFeatureSet.get<FeatureSet>(`${organizationId}-${workspaceId ?? ""}`);
|
||||
if (cachedPlan) {
|
||||
return cachedPlan;
|
||||
}
|
||||
@ -83,7 +87,7 @@ class EELicenseService {
|
||||
if (!organization) throw OrganizationNotFoundError();
|
||||
|
||||
let url = `${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`;
|
||||
|
||||
|
||||
if (workspaceId) {
|
||||
url += `?workspaceId=${workspaceId}`;
|
||||
}
|
||||
@ -91,7 +95,7 @@ class EELicenseService {
|
||||
const { data: { currentPlan } } = await licenseServerKeyRequest.get(url);
|
||||
|
||||
// cache fetched plan for organization
|
||||
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ''}`, currentPlan);
|
||||
this.localFeatureSet.set(`${organizationId}-${workspaceId ?? ""}`, currentPlan);
|
||||
|
||||
return currentPlan;
|
||||
}
|
||||
@ -103,8 +107,8 @@ class EELicenseService {
|
||||
}
|
||||
|
||||
public async refreshPlan(organizationId: string, workspaceId?: string) {
|
||||
if (this.instanceType === 'cloud') {
|
||||
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ''}`);
|
||||
if (this.instanceType === "cloud") {
|
||||
this.localFeatureSet.del(`${organizationId}-${workspaceId ?? ""}`);
|
||||
await this.getPlan(organizationId, workspaceId);
|
||||
}
|
||||
}
|
||||
@ -119,7 +123,7 @@ class EELicenseService {
|
||||
const token = await refreshLicenseServerKeyToken()
|
||||
|
||||
if (token) {
|
||||
this.instanceType = 'cloud';
|
||||
this.instanceType = "cloud";
|
||||
}
|
||||
|
||||
return;
|
||||
@ -135,7 +139,7 @@ class EELicenseService {
|
||||
);
|
||||
|
||||
this.globalFeatureSet = currentPlan;
|
||||
this.instanceType = 'enterprise-self-hosted';
|
||||
this.instanceType = "enterprise-self-hosted";
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
IAction
|
||||
} from '../models';
|
||||
IAction,
|
||||
} from "../models";
|
||||
import {
|
||||
createLogHelper
|
||||
} from '../helpers/log';
|
||||
createLogHelper,
|
||||
} from "../helpers/log";
|
||||
import {
|
||||
createActionHelper
|
||||
} from '../helpers/action';
|
||||
import EELicenseService from './EELicenseService';
|
||||
createActionHelper,
|
||||
} from "../helpers/action";
|
||||
import EELicenseService from "./EELicenseService";
|
||||
|
||||
/**
|
||||
* Class to handle Enterprise Edition log actions
|
||||
@ -31,7 +31,7 @@ class EELogService {
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress
|
||||
ipAddress,
|
||||
}: {
|
||||
userId?: Types.ObjectId;
|
||||
serviceAccountId?: Types.ObjectId;
|
||||
@ -49,7 +49,7 @@ class EELogService {
|
||||
workspaceId,
|
||||
actions,
|
||||
channel,
|
||||
ipAddress
|
||||
ipAddress,
|
||||
})
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ class EELogService {
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
secretIds,
|
||||
}: {
|
||||
name: string;
|
||||
userId?: Types.ObjectId;
|
||||
@ -83,7 +83,7 @@ class EELogService {
|
||||
serviceAccountId,
|
||||
serviceTokenDataId,
|
||||
workspaceId,
|
||||
secretIds
|
||||
secretIds,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { ISecretVersion } from '../models';
|
||||
import { Types } from "mongoose";
|
||||
import { ISecretVersion } from "../models";
|
||||
import {
|
||||
takeSecretSnapshotHelper,
|
||||
addSecretVersionsHelper,
|
||||
markDeletedSecretVersionsHelper,
|
||||
} from '../helpers/secret';
|
||||
import EELicenseService from './EELicenseService';
|
||||
takeSecretSnapshotHelper,
|
||||
} from "../helpers/secret";
|
||||
import EELicenseService from "./EELicenseService";
|
||||
|
||||
/**
|
||||
* Class to handle Enterprise Edition secret actions
|
||||
|
@ -5,5 +5,5 @@ import EELogService from "./EELogService";
|
||||
export {
|
||||
EELicenseService,
|
||||
EESecretService,
|
||||
EELogService
|
||||
EELogService,
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { eventPushSecrets } from "./secret"
|
||||
|
||||
export {
|
||||
eventPushSecrets
|
||||
eventPushSecrets,
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
EVENT_PULL_SECRETS,
|
||||
EVENT_PUSH_SECRETS,
|
||||
EVENT_PULL_SECRETS
|
||||
} from '../variables';
|
||||
} from "../variables";
|
||||
|
||||
interface PushSecret {
|
||||
ciphertextKey: string;
|
||||
@ -13,7 +13,7 @@ interface PushSecret {
|
||||
ivValue: string;
|
||||
tagValue: string;
|
||||
hashValue: string;
|
||||
type: 'shared' | 'personal';
|
||||
type: "shared" | "personal";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,7 +24,7 @@ interface PushSecret {
|
||||
*/
|
||||
const eventPushSecrets = ({
|
||||
workspaceId,
|
||||
environment
|
||||
environment,
|
||||
}: {
|
||||
workspaceId: Types.ObjectId;
|
||||
environment?: string;
|
||||
@ -35,7 +35,7 @@ const eventPushSecrets = ({
|
||||
environment,
|
||||
payload: {
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@ -55,10 +55,10 @@ const eventPullSecrets = ({
|
||||
workspaceId,
|
||||
payload: {
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
eventPushSecrets
|
||||
eventPushSecrets,
|
||||
}
|
||||
|
@ -1,36 +1,36 @@
|
||||
import { Types } from 'mongoose';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import bcrypt from 'bcrypt';
|
||||
import { Types } from "mongoose";
|
||||
import jwt from "jsonwebtoken";
|
||||
import bcrypt from "bcrypt";
|
||||
import {
|
||||
IUser,
|
||||
User,
|
||||
ServiceTokenData,
|
||||
ServiceAccount,
|
||||
APIKeyData,
|
||||
ITokenVersion,
|
||||
IUser,
|
||||
ServiceAccount,
|
||||
ServiceTokenData,
|
||||
TokenVersion,
|
||||
ITokenVersion
|
||||
} from '../models';
|
||||
User,
|
||||
} from "../models";
|
||||
import {
|
||||
AccountNotFoundError,
|
||||
ServiceTokenDataNotFoundError,
|
||||
ServiceAccountNotFoundError,
|
||||
APIKeyDataNotFoundError,
|
||||
AccountNotFoundError,
|
||||
BadRequestError,
|
||||
ServiceAccountNotFoundError,
|
||||
ServiceTokenDataNotFoundError,
|
||||
UnauthorizedRequestError,
|
||||
BadRequestError
|
||||
} from '../utils/errors';
|
||||
} from "../utils/errors";
|
||||
import {
|
||||
getJwtAuthLifetime,
|
||||
getJwtAuthSecret,
|
||||
getJwtProviderAuthSecret,
|
||||
getJwtRefreshLifetime,
|
||||
getJwtRefreshSecret
|
||||
} from '../config';
|
||||
getJwtRefreshSecret,
|
||||
} from "../config";
|
||||
import {
|
||||
AUTH_MODE_API_KEY,
|
||||
AUTH_MODE_JWT,
|
||||
AUTH_MODE_SERVICE_ACCOUNT,
|
||||
AUTH_MODE_SERVICE_TOKEN,
|
||||
AUTH_MODE_API_KEY
|
||||
} from '../variables';
|
||||
} from "../variables";
|
||||
|
||||
/**
|
||||
*
|
||||
@ -39,41 +39,41 @@ import {
|
||||
*/
|
||||
export const validateAuthMode = ({
|
||||
headers,
|
||||
acceptedAuthModes
|
||||
acceptedAuthModes,
|
||||
}: {
|
||||
headers: { [key: string]: string | string[] | undefined },
|
||||
acceptedAuthModes: string[]
|
||||
}) => {
|
||||
const apiKey = headers['x-api-key'];
|
||||
const authHeader = headers['authorization'];
|
||||
const apiKey = headers["x-api-key"];
|
||||
const authHeader = headers["authorization"];
|
||||
|
||||
let authMode, authTokenValue;
|
||||
if (apiKey === undefined && authHeader === undefined) {
|
||||
// case: no auth or X-API-KEY header present
|
||||
throw BadRequestError({ message: 'Missing Authorization or X-API-KEY in request header.' });
|
||||
throw BadRequestError({ message: "Missing Authorization or X-API-KEY in request header." });
|
||||
}
|
||||
|
||||
if (typeof apiKey === 'string') {
|
||||
if (typeof apiKey === "string") {
|
||||
// case: treat request authentication type as via X-API-KEY (i.e. API Key)
|
||||
authMode = AUTH_MODE_API_KEY;
|
||||
authTokenValue = apiKey;
|
||||
}
|
||||
|
||||
if (typeof authHeader === 'string') {
|
||||
if (typeof authHeader === "string") {
|
||||
// case: treat request authentication type as via Authorization header (i.e. either JWT or service token)
|
||||
const [tokenType, tokenValue] = <[string, string]>authHeader.split(' ', 2) ?? [null, null]
|
||||
const [tokenType, tokenValue] = <[string, string]>authHeader.split(" ", 2) ?? [null, null]
|
||||
if (tokenType === null)
|
||||
throw BadRequestError({ message: `Missing Authorization Header in the request header.` });
|
||||
if (tokenType.toLowerCase() !== 'bearer')
|
||||
throw BadRequestError({ message: "Missing Authorization Header in the request header." });
|
||||
if (tokenType.toLowerCase() !== "bearer")
|
||||
throw BadRequestError({ message: `The provided authentication type '${tokenType}' is not supported.` });
|
||||
if (tokenValue === null)
|
||||
throw BadRequestError({ message: 'Missing Authorization Body in the request header.' });
|
||||
throw BadRequestError({ message: "Missing Authorization Body in the request header." });
|
||||
|
||||
switch (tokenValue.split('.', 1)[0]) {
|
||||
case 'st':
|
||||
switch (tokenValue.split(".", 1)[0]) {
|
||||
case "st":
|
||||
authMode = AUTH_MODE_SERVICE_TOKEN;
|
||||
break;
|
||||
case 'sa':
|
||||
case "sa":
|
||||
authMode = AUTH_MODE_SERVICE_ACCOUNT;
|
||||
break;
|
||||
default:
|
||||
@ -83,13 +83,13 @@ export const validateAuthMode = ({
|
||||
authTokenValue = tokenValue;
|
||||
}
|
||||
|
||||
if (!authMode || !authTokenValue) throw BadRequestError({ message: 'Missing valid Authorization or X-API-KEY in request header.' });
|
||||
if (!authMode || !authTokenValue) throw BadRequestError({ message: "Missing valid Authorization or X-API-KEY in request header." });
|
||||
|
||||
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: 'The provided authentication type is not supported.' });
|
||||
if (!acceptedAuthModes.includes(authMode)) throw BadRequestError({ message: "The provided authentication type is not supported." });
|
||||
|
||||
return ({
|
||||
authMode,
|
||||
authTokenValue
|
||||
authTokenValue,
|
||||
});
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export const validateAuthMode = ({
|
||||
* @returns {User} user - user corresponding to JWT token
|
||||
*/
|
||||
export const getAuthUserPayload = async ({
|
||||
authTokenValue
|
||||
authTokenValue,
|
||||
}: {
|
||||
authTokenValue: string;
|
||||
}) => {
|
||||
@ -109,31 +109,31 @@ export const getAuthUserPayload = async ({
|
||||
);
|
||||
|
||||
const user = await User.findOne({
|
||||
_id: new Types.ObjectId(decodedToken.userId)
|
||||
}).select('+publicKey +accessVersion');
|
||||
_id: new Types.ObjectId(decodedToken.userId),
|
||||
}).select("+publicKey +accessVersion");
|
||||
|
||||
if (!user) throw AccountNotFoundError({ message: 'Failed to find user' });
|
||||
if (!user) throw AccountNotFoundError({ message: "Failed to find user" });
|
||||
|
||||
if (!user?.publicKey) throw UnauthorizedRequestError({ message: 'Failed to authenticate user with partially set up account' });
|
||||
if (!user?.publicKey) throw UnauthorizedRequestError({ message: "Failed to authenticate user with partially set up account" });
|
||||
|
||||
const tokenVersion = await TokenVersion.findOneAndUpdate({
|
||||
_id: new Types.ObjectId(decodedToken.tokenVersionId),
|
||||
user: user._id
|
||||
user: user._id,
|
||||
}, {
|
||||
lastUsed: new Date()
|
||||
lastUsed: new Date(),
|
||||
});
|
||||
|
||||
if (!tokenVersion) throw UnauthorizedRequestError({
|
||||
message: 'Failed to validate access token'
|
||||
message: "Failed to validate access token",
|
||||
});
|
||||
|
||||
if (decodedToken.accessVersion !== tokenVersion.accessVersion) throw UnauthorizedRequestError({
|
||||
message: 'Failed to validate access token'
|
||||
message: "Failed to validate access token",
|
||||
});
|
||||
|
||||
return ({
|
||||
user,
|
||||
tokenVersionId: tokenVersion._id
|
||||
tokenVersionId: tokenVersion._id,
|
||||
});
|
||||
}
|
||||
|
||||
@ -144,41 +144,41 @@ export const getAuthUserPayload = async ({
|
||||
* @returns {ServiceTokenData} serviceTokenData - service token data
|
||||
*/
|
||||
export const getAuthSTDPayload = async ({
|
||||
authTokenValue
|
||||
authTokenValue,
|
||||
}: {
|
||||
authTokenValue: string;
|
||||
}) => {
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||
|
||||
let serviceTokenData = await ServiceTokenData
|
||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt');
|
||||
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt");
|
||||
|
||||
if (!serviceTokenData) {
|
||||
throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||
throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
|
||||
} else if (serviceTokenData?.expiresAt && new Date(serviceTokenData.expiresAt) < new Date()) {
|
||||
// case: service token expired
|
||||
await ServiceTokenData.findByIdAndDelete(serviceTokenData._id);
|
||||
throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate expired service token'
|
||||
message: "Failed to authenticate expired service token",
|
||||
});
|
||||
}
|
||||
|
||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceTokenData.secretHash);
|
||||
if (!isMatch) throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate service token'
|
||||
message: "Failed to authenticate service token",
|
||||
});
|
||||
|
||||
serviceTokenData = await ServiceTokenData
|
||||
.findOneAndUpdate({
|
||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
|
||||
}, {
|
||||
lastUsed: new Date()
|
||||
lastUsed: new Date(),
|
||||
}, {
|
||||
new: true
|
||||
new: true,
|
||||
})
|
||||
.select('+encryptedKey +iv +tag');
|
||||
.select("+encryptedKey +iv +tag");
|
||||
|
||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: 'Failed to find service token data' });
|
||||
if (!serviceTokenData) throw ServiceTokenDataNotFoundError({ message: "Failed to find service token data" });
|
||||
|
||||
return serviceTokenData;
|
||||
}
|
||||
@ -190,23 +190,23 @@ export const getAuthSTDPayload = async ({
|
||||
* @returns {ServiceAccount} serviceAccount
|
||||
*/
|
||||
export const getAuthSAAKPayload = async ({
|
||||
authTokenValue
|
||||
authTokenValue,
|
||||
}: {
|
||||
authTokenValue: string;
|
||||
}) => {
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||
|
||||
const serviceAccount = await ServiceAccount.findById(
|
||||
Buffer.from(TOKEN_IDENTIFIER, 'base64').toString('hex')
|
||||
).select('+secretHash');
|
||||
Buffer.from(TOKEN_IDENTIFIER, "base64").toString("hex")
|
||||
).select("+secretHash");
|
||||
|
||||
if (!serviceAccount) {
|
||||
throw ServiceAccountNotFoundError({ message: 'Failed to find service account' });
|
||||
throw ServiceAccountNotFoundError({ message: "Failed to find service account" });
|
||||
}
|
||||
|
||||
const result = await bcrypt.compare(TOKEN_SECRET, serviceAccount.secretHash);
|
||||
if (!result) throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate service account access key'
|
||||
message: "Failed to authenticate service account access key",
|
||||
});
|
||||
|
||||
return serviceAccount;
|
||||
@ -219,48 +219,48 @@ export const getAuthSAAKPayload = async ({
|
||||
* @returns {APIKeyData} apiKeyData - API key data
|
||||
*/
|
||||
export const getAuthAPIKeyPayload = async ({
|
||||
authTokenValue
|
||||
authTokenValue,
|
||||
}: {
|
||||
authTokenValue: string;
|
||||
}) => {
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split('.', 3);
|
||||
const [_, TOKEN_IDENTIFIER, TOKEN_SECRET] = <[string, string, string]>authTokenValue.split(".", 3);
|
||||
|
||||
let apiKeyData = await APIKeyData
|
||||
.findById(TOKEN_IDENTIFIER, '+secretHash +expiresAt')
|
||||
.populate<{ user: IUser }>('user', '+publicKey');
|
||||
.findById(TOKEN_IDENTIFIER, "+secretHash +expiresAt")
|
||||
.populate<{ user: IUser }>("user", "+publicKey");
|
||||
|
||||
if (!apiKeyData) {
|
||||
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
||||
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
|
||||
} else if (apiKeyData?.expiresAt && new Date(apiKeyData.expiresAt) < new Date()) {
|
||||
// case: API key expired
|
||||
await APIKeyData.findByIdAndDelete(apiKeyData._id);
|
||||
throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate expired API key'
|
||||
message: "Failed to authenticate expired API key",
|
||||
});
|
||||
}
|
||||
|
||||
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKeyData.secretHash);
|
||||
if (!isMatch) throw UnauthorizedRequestError({
|
||||
message: 'Failed to authenticate API key'
|
||||
message: "Failed to authenticate API key",
|
||||
});
|
||||
|
||||
apiKeyData = await APIKeyData.findOneAndUpdate({
|
||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER)
|
||||
_id: new Types.ObjectId(TOKEN_IDENTIFIER),
|
||||
}, {
|
||||
lastUsed: new Date()
|
||||
lastUsed: new Date(),
|
||||
}, {
|
||||
new: true
|
||||
new: true,
|
||||
});
|
||||
|
||||
if (!apiKeyData) {
|
||||
throw APIKeyDataNotFoundError({ message: 'Failed to find API key data' });
|
||||
throw APIKeyDataNotFoundError({ message: "Failed to find API key data" });
|
||||
}
|
||||
|
||||
const user = await User.findById(apiKeyData.user).select('+publicKey');
|
||||
const user = await User.findById(apiKeyData.user).select("+publicKey");
|
||||
|
||||
if (!user) {
|
||||
throw AccountNotFoundError({
|
||||
message: 'Failed to find user'
|
||||
message: "Failed to find user",
|
||||
});
|
||||
}
|
||||
|
||||
@ -278,7 +278,7 @@ export const getAuthAPIKeyPayload = async ({
|
||||
export const issueAuthTokens = async ({
|
||||
userId,
|
||||
ip,
|
||||
userAgent
|
||||
userAgent,
|
||||
}: {
|
||||
userId: Types.ObjectId;
|
||||
ip: string;
|
||||
@ -290,7 +290,7 @@ export const issueAuthTokens = async ({
|
||||
tokenVersion = await TokenVersion.findOne({
|
||||
user: userId,
|
||||
ip,
|
||||
userAgent
|
||||
userAgent,
|
||||
});
|
||||
|
||||
if (!tokenVersion) {
|
||||
@ -302,7 +302,7 @@ export const issueAuthTokens = async ({
|
||||
accessVersion: 0,
|
||||
ip,
|
||||
userAgent,
|
||||
lastUsed: new Date()
|
||||
lastUsed: new Date(),
|
||||
}).save();
|
||||
}
|
||||
|
||||
@ -311,25 +311,25 @@ export const issueAuthTokens = async ({
|
||||
payload: {
|
||||
userId,
|
||||
tokenVersionId: tokenVersion._id.toString(),
|
||||
accessVersion: tokenVersion.accessVersion
|
||||
accessVersion: tokenVersion.accessVersion,
|
||||
},
|
||||
expiresIn: await getJwtAuthLifetime(),
|
||||
secret: await getJwtAuthSecret()
|
||||
secret: await getJwtAuthSecret(),
|
||||
});
|
||||
|
||||
const refreshToken = createToken({
|
||||
payload: {
|
||||
userId,
|
||||
tokenVersionId: tokenVersion._id.toString(),
|
||||
refreshVersion: tokenVersion.refreshVersion
|
||||
refreshVersion: tokenVersion.refreshVersion,
|
||||
},
|
||||
expiresIn: await getJwtRefreshLifetime(),
|
||||
secret: await getJwtRefreshSecret()
|
||||
secret: await getJwtRefreshSecret(),
|
||||
});
|
||||
|
||||
return {
|
||||
token,
|
||||
refreshToken
|
||||
refreshToken,
|
||||
};
|
||||
};
|
||||
|
||||
@ -342,12 +342,12 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
|
||||
// increment refreshVersion on user by 1
|
||||
|
||||
await TokenVersion.findOneAndUpdate({
|
||||
_id: tokenVersionId
|
||||
_id: tokenVersionId,
|
||||
}, {
|
||||
$inc: {
|
||||
refreshVersion: 1,
|
||||
accessVersion: 1
|
||||
}
|
||||
accessVersion: 1,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -362,14 +362,14 @@ export const clearTokens = async (tokenVersionId: Types.ObjectId): Promise<void>
|
||||
export const createToken = ({
|
||||
payload,
|
||||
expiresIn,
|
||||
secret
|
||||
secret,
|
||||
}: {
|
||||
payload: any;
|
||||
expiresIn: string | number;
|
||||
secret: string;
|
||||
}) => {
|
||||
return jwt.sign(payload, secret, {
|
||||
expiresIn
|
||||
expiresIn,
|
||||
});
|
||||
};
|
||||
|
||||
@ -383,7 +383,7 @@ export const validateProviderAuthToken = async ({
|
||||
providerAuthToken?: string;
|
||||
}) => {
|
||||
if (!providerAuthToken) {
|
||||
throw new Error('Invalid authentication request.');
|
||||
throw new Error("Invalid authentication request.");
|
||||
}
|
||||
|
||||
const decodedToken = <jwt.ProviderAuthJwtPayload>(
|
||||
@ -394,6 +394,6 @@ export const validateProviderAuthToken = async ({
|
||||
decodedToken.authProvider !== user.authProvider ||
|
||||
decodedToken.email !== email
|
||||
) {
|
||||
throw new Error('Invalid authentication credentials.')
|
||||
throw new Error("Invalid authentication credentials.")
|
||||
}
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Bot, BotKey, Secret, ISecret, IUser } from "../models";
|
||||
import { Bot, BotKey, ISecret, IUser, Secret } from "../models";
|
||||
import {
|
||||
generateKeyPair,
|
||||
encryptSymmetric128BitHexKeyUTF8,
|
||||
decryptSymmetric128BitHexKeyUTF8,
|
||||
decryptAsymmetric,
|
||||
decryptSymmetric128BitHexKeyUTF8,
|
||||
encryptSymmetric128BitHexKeyUTF8,
|
||||
generateKeyPair,
|
||||
} from "../utils/crypto";
|
||||
import {
|
||||
SECRET_SHARED,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
ENCODING_SCHEME_BASE64,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_SHARED,
|
||||
} from "../variables";
|
||||
import { getEncryptionKey, getRootEncryptionKey, client } from "../config";
|
||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||
import { InternalServerError } from "../utils/errors";
|
||||
import Folder from "../models/folder";
|
||||
import { getFolderByPath } from "../services/FolderService";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { getLogger } from '../utils/logger';
|
||||
import mongoose from "mongoose";
|
||||
import { getLogger } from "../utils/logger";
|
||||
|
||||
/**
|
||||
* Initialize database connection
|
||||
@ -8,7 +8,7 @@ import { getLogger } from '../utils/logger';
|
||||
* @returns
|
||||
*/
|
||||
export const initDatabaseHelper = async ({
|
||||
mongoURL
|
||||
mongoURL,
|
||||
}: {
|
||||
mongoURL: string;
|
||||
}) => {
|
||||
@ -16,7 +16,7 @@ export const initDatabaseHelper = async ({
|
||||
await mongoose.connect(mongoURL);
|
||||
|
||||
// allow empty strings to pass the required validator
|
||||
mongoose.Schema.Types.String.checkRequired(v => typeof v === 'string');
|
||||
mongoose.Schema.Types.String.checkRequired(v => typeof v === "string");
|
||||
|
||||
(await getLogger("database")).info("Database connection established");
|
||||
|
||||
@ -35,10 +35,10 @@ export const closeDatabaseHelper = async () => {
|
||||
new Promise((resolve) => {
|
||||
if (mongoose.connection && mongoose.connection.readyState == 1) {
|
||||
mongoose.connection.close()
|
||||
.then(() => resolve('Database connection closed'));
|
||||
.then(() => resolve("Database connection closed"));
|
||||
} else {
|
||||
resolve('Database connection already closed');
|
||||
resolve("Database connection already closed");
|
||||
}
|
||||
})
|
||||
}),
|
||||
]);
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Bot, IBot } from "../models";
|
||||
import { Bot } from "../models";
|
||||
import { EVENT_PUSH_SECRETS } from "../variables";
|
||||
import { IntegrationService } from "../services";
|
||||
|
||||
|
@ -1,17 +1,17 @@
|
||||
export * from './auth';
|
||||
export * from './bot';
|
||||
export * from './database';
|
||||
export * from './event';
|
||||
export * from './integration';
|
||||
export * from './key';
|
||||
export * from './membership';
|
||||
export * from './membershipOrg';
|
||||
export * from './nodemailer';
|
||||
export * from './organization';
|
||||
export * from './rateLimiter';
|
||||
export * from './secret';
|
||||
export * from './secrets';
|
||||
export * from './signup';
|
||||
export * from './token';
|
||||
export * from './user';
|
||||
export * from './workspace';
|
||||
export * from "./auth";
|
||||
export * from "./bot";
|
||||
export * from "./database";
|
||||
export * from "./event";
|
||||
export * from "./integration";
|
||||
export * from "./key";
|
||||
export * from "./membership";
|
||||
export * from "./membershipOrg";
|
||||
export * from "./nodemailer";
|
||||
export * from "./organization";
|
||||
export * from "./rateLimiter";
|
||||
export * from "./secret";
|
||||
export * from "./secrets";
|
||||
export * from "./signup";
|
||||
export * from "./token";
|
||||
export * from "./user";
|
||||
export * from "./workspace";
|
@ -3,10 +3,10 @@ import { Bot, Integration, IntegrationAuth } from "../models";
|
||||
import { exchangeCode, exchangeRefresh, syncSecrets } from "../integrations";
|
||||
import { BotService } from "../services";
|
||||
import {
|
||||
INTEGRATION_VERCEL,
|
||||
INTEGRATION_NETLIFY,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
INTEGRATION_NETLIFY,
|
||||
INTEGRATION_VERCEL,
|
||||
} from "../variables";
|
||||
import { UnauthorizedRequestError } from "../utils/errors";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Key, IKey } from '../models';
|
||||
import { IKey, Key } from "../models";
|
||||
|
||||
interface Key {
|
||||
encryptedKey: string;
|
||||
@ -20,7 +20,7 @@ interface Key {
|
||||
export const pushKeys = async ({
|
||||
userId,
|
||||
workspaceId,
|
||||
keys
|
||||
keys,
|
||||
}: {
|
||||
userId: string;
|
||||
workspaceId: string;
|
||||
@ -31,9 +31,9 @@ export const pushKeys = async ({
|
||||
(
|
||||
await Key.find(
|
||||
{
|
||||
workspace: workspaceId
|
||||
workspace: workspaceId,
|
||||
},
|
||||
'receiver'
|
||||
"receiver"
|
||||
)
|
||||
).map((k: IKey) => k.receiver.toString())
|
||||
);
|
||||
@ -47,7 +47,7 @@ export const pushKeys = async ({
|
||||
nonce: k.nonce,
|
||||
sender: userId,
|
||||
receiver: k.userId,
|
||||
workspace: workspaceId
|
||||
workspace: workspaceId,
|
||||
}))
|
||||
);
|
||||
};
|
@ -1,6 +1,6 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Membership, Key } from "../models";
|
||||
import { MembershipNotFoundError, BadRequestError } from "../utils/errors";
|
||||
import { Key, Membership } from "../models";
|
||||
import { BadRequestError, MembershipNotFoundError } from "../utils/errors";
|
||||
|
||||
/**
|
||||
* Validate that user with id [userId] is a member of workspace with id [workspaceId]
|
||||
@ -62,7 +62,7 @@ export const findMembership = async (queryObj: any) => {
|
||||
export const addMemberships = async ({
|
||||
userIds,
|
||||
workspaceId,
|
||||
roles
|
||||
roles,
|
||||
}: {
|
||||
userIds: string[];
|
||||
workspaceId: string;
|
||||
@ -95,7 +95,7 @@ export const addMemberships = async ({
|
||||
*/
|
||||
export const deleteMembership = async ({ membershipId }: { membershipId: string }) => {
|
||||
const deletedMembership = await Membership.findOneAndDelete({
|
||||
_id: membershipId
|
||||
_id: membershipId,
|
||||
});
|
||||
|
||||
// delete keys associated with the membership
|
||||
|
@ -1,14 +1,14 @@
|
||||
import { Types } from 'mongoose';
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
MembershipOrg,
|
||||
Workspace,
|
||||
Key,
|
||||
Membership,
|
||||
Key
|
||||
} from '../models';
|
||||
MembershipOrg,
|
||||
Workspace,
|
||||
} from "../models";
|
||||
import {
|
||||
MembershipOrgNotFoundError,
|
||||
UnauthorizedRequestError
|
||||
} from '../utils/errors';
|
||||
UnauthorizedRequestError,
|
||||
} from "../utils/errors";
|
||||
|
||||
/**
|
||||
* Validate that user with id [userId] is a member of organization with id [organizationId]
|
||||
@ -22,31 +22,31 @@ export const validateMembershipOrg = async ({
|
||||
userId,
|
||||
organizationId,
|
||||
acceptedRoles,
|
||||
acceptedStatuses
|
||||
acceptedStatuses,
|
||||
}: {
|
||||
userId: Types.ObjectId;
|
||||
organizationId: Types.ObjectId;
|
||||
acceptedRoles?: Array<'owner' | 'admin' | 'member'>;
|
||||
acceptedStatuses?: Array<'invited' | 'accepted'>;
|
||||
acceptedRoles?: Array<"owner" | "admin" | "member">;
|
||||
acceptedStatuses?: Array<"invited" | "accepted">;
|
||||
}) => {
|
||||
const membershipOrg = await MembershipOrg.findOne({
|
||||
user: userId,
|
||||
organization: organizationId
|
||||
organization: organizationId,
|
||||
});
|
||||
|
||||
if (!membershipOrg) {
|
||||
throw MembershipOrgNotFoundError({ message: 'Failed to find organization membership' });
|
||||
throw MembershipOrgNotFoundError({ message: "Failed to find organization membership" });
|
||||
}
|
||||
|
||||
if (acceptedRoles) {
|
||||
if (!acceptedRoles.includes(membershipOrg.role)) {
|
||||
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership role' });
|
||||
throw UnauthorizedRequestError({ message: "Failed to validate organization membership role" });
|
||||
}
|
||||
}
|
||||
|
||||
if (acceptedStatuses) {
|
||||
if (!acceptedStatuses.includes(membershipOrg.status)) {
|
||||
throw UnauthorizedRequestError({ message: 'Failed to validate organization membership status' });
|
||||
throw UnauthorizedRequestError({ message: "Failed to validate organization membership status" });
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ export const addMembershipsOrg = async ({
|
||||
userIds,
|
||||
organizationId,
|
||||
roles,
|
||||
statuses
|
||||
statuses,
|
||||
}: {
|
||||
userIds: string[];
|
||||
organizationId: string;
|
||||
@ -90,16 +90,16 @@ export const addMembershipsOrg = async ({
|
||||
user: userId,
|
||||
organization: organizationId,
|
||||
role: roles[idx],
|
||||
status: statuses[idx]
|
||||
status: statuses[idx],
|
||||
},
|
||||
update: {
|
||||
user: userId,
|
||||
organization: organizationId,
|
||||
role: roles[idx],
|
||||
status: statuses[idx]
|
||||
status: statuses[idx],
|
||||
},
|
||||
upsert: true
|
||||
}
|
||||
upsert: true,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@ -112,15 +112,15 @@ export const addMembershipsOrg = async ({
|
||||
* @param {String} obj.membershipOrgId - id of organization membership to delete
|
||||
*/
|
||||
export const deleteMembershipOrg = async ({
|
||||
membershipOrgId
|
||||
membershipOrgId,
|
||||
}: {
|
||||
membershipOrgId: string;
|
||||
}) => {
|
||||
const deletedMembershipOrg = await MembershipOrg.findOneAndDelete({
|
||||
_id: membershipOrgId
|
||||
_id: membershipOrgId,
|
||||
});
|
||||
|
||||
if (!deletedMembershipOrg) throw new Error('Failed to delete organization membership');
|
||||
if (!deletedMembershipOrg) throw new Error("Failed to delete organization membership");
|
||||
|
||||
// delete keys associated with organization membership
|
||||
if (deletedMembershipOrg?.user) {
|
||||
@ -128,22 +128,22 @@ export const deleteMembershipOrg = async ({
|
||||
|
||||
const workspaces = (
|
||||
await Workspace.find({
|
||||
organization: deletedMembershipOrg.organization
|
||||
organization: deletedMembershipOrg.organization,
|
||||
})
|
||||
).map((w) => w._id.toString());
|
||||
|
||||
await Membership.deleteMany({
|
||||
user: deletedMembershipOrg.user,
|
||||
workspace: {
|
||||
$in: workspaces
|
||||
}
|
||||
$in: workspaces,
|
||||
},
|
||||
});
|
||||
|
||||
await Key.deleteMany({
|
||||
receiver: deletedMembershipOrg.user,
|
||||
workspace: {
|
||||
$in: workspaces
|
||||
}
|
||||
$in: workspaces,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import handlebars from 'handlebars';
|
||||
import nodemailer from 'nodemailer';
|
||||
import { getSmtpFromName, getSmtpFromAddress, getSmtpConfigured } from '../config';
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import handlebars from "handlebars";
|
||||
import nodemailer from "nodemailer";
|
||||
import { getSmtpConfigured, getSmtpFromAddress, getSmtpFromName } from "../config";
|
||||
|
||||
let smtpTransporter: nodemailer.Transporter;
|
||||
|
||||
@ -17,7 +17,7 @@ export const sendMail = async ({
|
||||
template,
|
||||
subjectLine,
|
||||
recipients,
|
||||
substitutions
|
||||
substitutions,
|
||||
}: {
|
||||
template: string;
|
||||
subjectLine: string;
|
||||
@ -26,17 +26,17 @@ export const sendMail = async ({
|
||||
}) => {
|
||||
if (await getSmtpConfigured()) {
|
||||
const html = fs.readFileSync(
|
||||
path.resolve(__dirname, '../templates/' + template),
|
||||
'utf8'
|
||||
path.resolve(__dirname, "../templates/" + template),
|
||||
"utf8"
|
||||
);
|
||||
const temp = handlebars.compile(html);
|
||||
const htmlToSend = temp(substitutions);
|
||||
|
||||
await smtpTransporter.sendMail({
|
||||
from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`,
|
||||
to: recipients.join(', '),
|
||||
to: recipients.join(", "),
|
||||
subject: subjectLine,
|
||||
html: htmlToSend
|
||||
html: htmlToSend,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -1,25 +1,19 @@
|
||||
import Stripe from "stripe";
|
||||
import { Types } from "mongoose";
|
||||
import { Organization, MembershipOrg } from "../models";
|
||||
import { MembershipOrg, Organization } from "../models";
|
||||
import {
|
||||
ACCEPTED
|
||||
ACCEPTED,
|
||||
} from "../variables";
|
||||
import {
|
||||
getStripeSecretKey,
|
||||
getStripeProductPro,
|
||||
getStripeProductTeam,
|
||||
getStripeProductStarter,
|
||||
EELicenseService,
|
||||
} from "../ee/services";
|
||||
import {
|
||||
getLicenseServerKey,
|
||||
getLicenseServerUrl,
|
||||
} from "../config";
|
||||
import {
|
||||
EELicenseService
|
||||
} from '../ee/services';
|
||||
import {
|
||||
getLicenseServerUrl
|
||||
} from '../config';
|
||||
import {
|
||||
licenseKeyRequest,
|
||||
licenseServerKeyRequest,
|
||||
licenseKeyRequest
|
||||
} from '../config/request';
|
||||
} from "../config/request";
|
||||
|
||||
/**
|
||||
* Create an organization with name [name]
|
||||
@ -35,90 +29,32 @@ export const createOrganization = async ({
|
||||
name: string;
|
||||
email: string;
|
||||
}) => {
|
||||
const licenseServerKey = await getLicenseServerKey();
|
||||
let organization;
|
||||
// register stripe account
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: "2022-08-01",
|
||||
});
|
||||
|
||||
if (await getStripeSecretKey()) {
|
||||
const customer = await stripe.customers.create({
|
||||
email,
|
||||
description: name,
|
||||
});
|
||||
|
||||
|
||||
if (licenseServerKey) {
|
||||
const { data: { customerId } } = await licenseServerKeyRequest.post(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers`,
|
||||
{
|
||||
email,
|
||||
name
|
||||
}
|
||||
);
|
||||
|
||||
organization = await new Organization({
|
||||
name,
|
||||
customerId: customer.id,
|
||||
customerId
|
||||
}).save();
|
||||
|
||||
} else {
|
||||
organization = await new Organization({
|
||||
name,
|
||||
}).save();
|
||||
}
|
||||
|
||||
await initSubscriptionOrg({ organizationId: organization._id });
|
||||
|
||||
return organization;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize free-tier subscription for new organization
|
||||
* @param {Object} obj
|
||||
* @param {String} obj.organizationId - id of associated organization for subscription
|
||||
* @return {Object} obj
|
||||
* @return {Object} obj.stripeSubscription - new stripe subscription
|
||||
* @return {Subscription} obj.subscription - new subscription
|
||||
*/
|
||||
export const initSubscriptionOrg = async ({
|
||||
organizationId,
|
||||
}: {
|
||||
organizationId: Types.ObjectId;
|
||||
}) => {
|
||||
let stripeSubscription;
|
||||
let subscription;
|
||||
|
||||
// find organization
|
||||
const organization = await Organization.findOne({
|
||||
_id: organizationId,
|
||||
});
|
||||
|
||||
if (organization) {
|
||||
if (organization.customerId) {
|
||||
// initialize starter subscription with quantity of 0
|
||||
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||
apiVersion: "2022-08-01",
|
||||
});
|
||||
|
||||
const productToPriceMap = {
|
||||
starter: await getStripeProductStarter(),
|
||||
team: await getStripeProductTeam(),
|
||||
pro: await getStripeProductPro(),
|
||||
};
|
||||
|
||||
stripeSubscription = await stripe.subscriptions.create({
|
||||
customer: organization.customerId,
|
||||
items: [
|
||||
{
|
||||
price: productToPriceMap["starter"],
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
payment_behavior: "default_incomplete",
|
||||
proration_behavior: "none",
|
||||
expand: ["latest_invoice.payment_intent"],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
throw new Error("Failed to initialize free organization subscription");
|
||||
}
|
||||
|
||||
return {
|
||||
stripeSubscription,
|
||||
subscription,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update organization subscription quantity to reflect number of members in
|
||||
* the organization.
|
||||
@ -130,14 +66,13 @@ export const updateSubscriptionOrgQuantity = async ({
|
||||
}: {
|
||||
organizationId: string;
|
||||
}) => {
|
||||
let stripeSubscription;
|
||||
// find organization
|
||||
const organization = await Organization.findOne({
|
||||
_id: organizationId,
|
||||
});
|
||||
|
||||
if (organization && organization.customerId) {
|
||||
if (EELicenseService.instanceType === 'cloud') {
|
||||
if (EELicenseService.instanceType === "cloud") {
|
||||
// instance of Infisical is a cloud instance
|
||||
const quantity = await MembershipOrg.countDocuments({
|
||||
organization: new Types.ObjectId(organizationId),
|
||||
@ -147,7 +82,7 @@ export const updateSubscriptionOrgQuantity = async ({
|
||||
await licenseServerKeyRequest.patch(
|
||||
`${await getLicenseServerUrl()}/api/license-server/v1/customers/${organization.customerId}/cloud-plan`,
|
||||
{
|
||||
quantity
|
||||
quantity,
|
||||
}
|
||||
);
|
||||
|
||||
@ -155,22 +90,20 @@ export const updateSubscriptionOrgQuantity = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (EELicenseService.instanceType === 'enterprise-self-hosted') {
|
||||
if (EELicenseService.instanceType === "enterprise-self-hosted") {
|
||||
// instance of Infisical is an enterprise self-hosted instance
|
||||
|
||||
const usedSeats = await MembershipOrg.countDocuments({
|
||||
status: ACCEPTED
|
||||
status: ACCEPTED,
|
||||
});
|
||||
|
||||
await licenseKeyRequest.patch(
|
||||
`${await getLicenseServerUrl()}/api/license/v1/license`,
|
||||
{
|
||||
usedSeats
|
||||
usedSeats,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
await EELicenseService.refreshPlan(organizationId);
|
||||
|
||||
return stripeSubscription;
|
||||
};
|
@ -1,4 +1,4 @@
|
||||
import rateLimit from 'express-rate-limit';
|
||||
import rateLimit from "express-rate-limit";
|
||||
// const MongoStore = require('rate-limit-mongo');
|
||||
|
||||
// 200 per minute
|
||||
@ -10,15 +10,15 @@ export const apiLimiter = rateLimit({
|
||||
// errorHandler: console.error.bind(null, 'rate-limit-mongo')
|
||||
// }),
|
||||
windowMs: 60 * 1000,
|
||||
max: 240,
|
||||
max: 350,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
skip: (request) => {
|
||||
return request.path === '/healthcheck' || request.path === '/api/status'
|
||||
return request.path === "/healthcheck" || request.path === "/api/status"
|
||||
},
|
||||
keyGenerator: (req, res) => {
|
||||
return req.realIP
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 50 requests per 1 hours
|
||||
@ -30,12 +30,12 @@ const authLimit = rateLimit({
|
||||
// collectionName: "expressRateRecords-authLimit",
|
||||
// }),
|
||||
windowMs: 60 * 1000,
|
||||
max: 10,
|
||||
max: 100,
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req, res) => {
|
||||
return req.realIP
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 5 requests per 1 hour
|
||||
@ -52,11 +52,11 @@ export const passwordLimiter = rateLimit({
|
||||
legacyHeaders: false,
|
||||
keyGenerator: (req, res) => {
|
||||
return req.realIP
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export const authLimiter = (req: any, res: any, next: any) => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
authLimit(req, res, next);
|
||||
} else {
|
||||
next();
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Types } from "mongoose";
|
||||
import { Secret, ISecret } from "../models";
|
||||
import { EESecretService, EELogService } from "../ee/services";
|
||||
import { ISecret, Secret } from "../models";
|
||||
import { EELogService, EESecretService } from "../ee/services";
|
||||
import { IAction, SecretVersion } from "../ee/models";
|
||||
import {
|
||||
SECRET_SHARED,
|
||||
SECRET_PERSONAL,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
SECRET_SHARED,
|
||||
} from "../variables";
|
||||
|
||||
interface V1PushSecret {
|
||||
|
@ -1,45 +1,45 @@
|
||||
import { Types } from "mongoose";
|
||||
import {
|
||||
CreateSecretParams,
|
||||
GetSecretsParams,
|
||||
GetSecretParams,
|
||||
UpdateSecretParams,
|
||||
DeleteSecretParams,
|
||||
} from '../interfaces/services/SecretService';
|
||||
GetSecretParams,
|
||||
GetSecretsParams,
|
||||
UpdateSecretParams,
|
||||
} from "../interfaces/services/SecretService";
|
||||
import {
|
||||
Secret,
|
||||
ISecret,
|
||||
Secret,
|
||||
SecretBlindIndexData,
|
||||
ServiceTokenData,
|
||||
} from "../models";
|
||||
import { SecretVersion } from "../ee/models";
|
||||
import {
|
||||
BadRequestError,
|
||||
SecretNotFoundError,
|
||||
SecretBlindIndexDataNotFoundError,
|
||||
InternalServerError,
|
||||
SecretBlindIndexDataNotFoundError,
|
||||
SecretNotFoundError,
|
||||
UnauthorizedRequestError,
|
||||
} from "../utils/errors";
|
||||
import {
|
||||
SECRET_PERSONAL,
|
||||
SECRET_SHARED,
|
||||
ACTION_ADD_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ACTION_READ_SECRETS,
|
||||
ACTION_UPDATE_SECRETS,
|
||||
ACTION_DELETE_SECRETS,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
ENCODING_SCHEME_BASE64,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
SECRET_PERSONAL,
|
||||
SECRET_SHARED,
|
||||
} from "../variables";
|
||||
import crypto from "crypto";
|
||||
import * as argon2 from "argon2";
|
||||
import {
|
||||
encryptSymmetric128BitHexKeyUTF8,
|
||||
decryptSymmetric128BitHexKeyUTF8,
|
||||
} from '../utils/crypto';
|
||||
import { TelemetryService } from '../services';
|
||||
import { getEncryptionKey, client, getRootEncryptionKey } from "../config";
|
||||
import { EESecretService, EELogService } from "../ee/services";
|
||||
encryptSymmetric128BitHexKeyUTF8,
|
||||
} from "../utils/crypto";
|
||||
import { TelemetryService } from "../services";
|
||||
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
|
||||
import { EELogService, EESecretService } from "../ee/services";
|
||||
import {
|
||||
getAuthDataPayloadIdObj,
|
||||
getAuthDataPayloadUserObj,
|
||||
@ -56,7 +56,7 @@ import { getFolderIdFromServiceToken } from "../services/FolderService";
|
||||
*/
|
||||
export const repackageSecretToRaw = ({
|
||||
secret,
|
||||
key
|
||||
key,
|
||||
}: {
|
||||
secret: ISecret;
|
||||
key: string;
|
||||
@ -66,24 +66,24 @@ export const repackageSecretToRaw = ({
|
||||
ciphertext: secret.secretKeyCiphertext,
|
||||
iv: secret.secretKeyIV,
|
||||
tag: secret.secretKeyTag,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
const secretValue = decryptSymmetric128BitHexKeyUTF8({
|
||||
ciphertext: secret.secretValueCiphertext,
|
||||
iv: secret.secretValueIV,
|
||||
tag: secret.secretValueTag,
|
||||
key
|
||||
key,
|
||||
});
|
||||
|
||||
let secretComment: string = '';
|
||||
let secretComment = "";
|
||||
|
||||
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
|
||||
secretComment = decryptSymmetric128BitHexKeyUTF8({
|
||||
ciphertext: secret.secretCommentCiphertext,
|
||||
iv: secret.secretCommentIV,
|
||||
tag: secret.secretCommentTag,
|
||||
key
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
@ -96,7 +96,7 @@ export const repackageSecretToRaw = ({
|
||||
user: secret.user,
|
||||
secretKey,
|
||||
secretValue,
|
||||
secretComment
|
||||
secretComment,
|
||||
});
|
||||
}
|
||||
|
||||
@ -944,6 +944,6 @@ export const deleteSecretHelper = async ({
|
||||
|
||||
return ({
|
||||
secrets,
|
||||
secret
|
||||
secret,
|
||||
});
|
||||
};
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { IUser } from '../models';
|
||||
import { createOrganization } from './organization';
|
||||
import { addMembershipsOrg } from './membershipOrg';
|
||||
import { OWNER, ACCEPTED } from '../variables';
|
||||
import { sendMail } from '../helpers/nodemailer';
|
||||
import { TokenService } from '../services';
|
||||
import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
|
||||
import { IUser } from "../models";
|
||||
import { createOrganization } from "./organization";
|
||||
import { addMembershipsOrg } from "./membershipOrg";
|
||||
import { ACCEPTED, OWNER } from "../variables";
|
||||
import { sendMail } from "../helpers/nodemailer";
|
||||
import { TokenService } from "../services";
|
||||
import { TOKEN_EMAIL_CONFIRMATION } from "../variables";
|
||||
|
||||
/**
|
||||
* Send magic link to verify email to [email]
|
||||
@ -16,17 +16,17 @@ import { TOKEN_EMAIL_CONFIRMATION } from '../variables';
|
||||
export const sendEmailVerification = async ({ email }: { email: string }) => {
|
||||
const token = await TokenService.createToken({
|
||||
type: TOKEN_EMAIL_CONFIRMATION,
|
||||
email
|
||||
email,
|
||||
});
|
||||
|
||||
// send mail
|
||||
await sendMail({
|
||||
template: 'emailVerification.handlebars',
|
||||
subjectLine: 'Infisical confirmation code',
|
||||
template: "emailVerification.handlebars",
|
||||
subjectLine: "Infisical confirmation code",
|
||||
recipients: [email],
|
||||
substitutions: {
|
||||
code: token
|
||||
}
|
||||
code: token,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@ -38,7 +38,7 @@ export const sendEmailVerification = async ({ email }: { email: string }) => {
|
||||
*/
|
||||
export const checkEmailVerification = async ({
|
||||
email,
|
||||
code
|
||||
code,
|
||||
}: {
|
||||
email: string;
|
||||
code: string;
|
||||
@ -46,7 +46,7 @@ export const checkEmailVerification = async ({
|
||||
await TokenService.validateToken({
|
||||
type: TOKEN_EMAIL_CONFIRMATION,
|
||||
email,
|
||||
token: code
|
||||
token: code,
|
||||
});
|
||||
};
|
||||
|
||||
@ -59,7 +59,7 @@ export const checkEmailVerification = async ({
|
||||
*/
|
||||
export const initializeDefaultOrg = async ({
|
||||
organizationName,
|
||||
user
|
||||
user,
|
||||
}: {
|
||||
organizationName: string;
|
||||
user: IUser;
|
||||
@ -69,14 +69,14 @@ export const initializeDefaultOrg = async ({
|
||||
// subscription
|
||||
const organization = await createOrganization({
|
||||
email: user.email,
|
||||
name: organizationName
|
||||
name: organizationName,
|
||||
});
|
||||
|
||||
await addMembershipsOrg({
|
||||
userIds: [user._id.toString()],
|
||||
organizationId: organization._id.toString(),
|
||||
roles: [OWNER],
|
||||
statuses: [ACCEPTED]
|
||||
statuses: [ACCEPTED],
|
||||
});
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to initialize default organization and workspace [err=${err}]`);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {
|
||||
IUser,
|
||||
User,
|
||||
} from '../models';
|
||||
import { sendMail } from './nodemailer';
|
||||
} from "../models";
|
||||
import { sendMail } from "./nodemailer";
|
||||
|
||||
/**
|
||||
* Initialize a user under email [email]
|
||||
@ -12,7 +12,7 @@ import { sendMail } from './nodemailer';
|
||||
*/
|
||||
export const setupAccount = async ({ email }: { email: string }) => {
|
||||
const user = await new User({
|
||||
email
|
||||
email,
|
||||
}).save();
|
||||
|
||||
return user;
|
||||
@ -49,7 +49,7 @@ export const completeAccount = async ({
|
||||
encryptedPrivateKeyIV,
|
||||
encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
}: {
|
||||
userId: string;
|
||||
firstName: string;
|
||||
@ -66,7 +66,7 @@ export const completeAccount = async ({
|
||||
verifier: string;
|
||||
}) => {
|
||||
const options = {
|
||||
new: true
|
||||
new: true,
|
||||
};
|
||||
const user = await User.findByIdAndUpdate(
|
||||
userId,
|
||||
@ -82,7 +82,7 @@ export const completeAccount = async ({
|
||||
iv: encryptedPrivateKeyIV,
|
||||
tag: encryptedPrivateKeyTag,
|
||||
salt,
|
||||
verifier
|
||||
verifier,
|
||||
},
|
||||
options
|
||||
);
|
||||
@ -100,7 +100,7 @@ export const completeAccount = async ({
|
||||
export const checkUserDevice = async ({
|
||||
user,
|
||||
ip,
|
||||
userAgent
|
||||
userAgent,
|
||||
}: {
|
||||
user: IUser;
|
||||
ip: string;
|
||||
@ -114,22 +114,22 @@ export const checkUserDevice = async ({
|
||||
|
||||
user.devices = user.devices.concat([{
|
||||
ip: String(ip),
|
||||
userAgent
|
||||
userAgent,
|
||||
}]);
|
||||
|
||||
await user.save();
|
||||
|
||||
// send MFA code [code] to [email]
|
||||
await sendMail({
|
||||
template: 'newDevice.handlebars',
|
||||
subjectLine: `Successful login from new device`,
|
||||
template: "newDevice.handlebars",
|
||||
subjectLine: "Successful login from new device",
|
||||
recipients: [user.email],
|
||||
substitutions: {
|
||||
email: user.email,
|
||||
timestamp: new Date().toString(),
|
||||
ip,
|
||||
userAgent
|
||||
}
|
||||
userAgent,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import {
|
||||
Workspace,
|
||||
Bot,
|
||||
Membership,
|
||||
Key,
|
||||
Secret
|
||||
} from '../models';
|
||||
import { createBot } from '../helpers/bot';
|
||||
import { EELicenseService } from '../ee/services';
|
||||
import { SecretService } from '../services';
|
||||
Membership,
|
||||
Secret,
|
||||
Workspace,
|
||||
} from "../models";
|
||||
import { createBot } from "../helpers/bot";
|
||||
import { EELicenseService } from "../ee/services";
|
||||
import { SecretService } from "../services";
|
||||
|
||||
/**
|
||||
* Create a workspace with name [name] in organization with id [organizationId]
|
||||
@ -18,7 +18,7 @@ import { SecretService } from '../services';
|
||||
*/
|
||||
export const createWorkspace = async ({
|
||||
name,
|
||||
organizationId
|
||||
organizationId,
|
||||
}: {
|
||||
name: string;
|
||||
organizationId: string;
|
||||
@ -27,18 +27,18 @@ export const createWorkspace = async ({
|
||||
const workspace = await new Workspace({
|
||||
name,
|
||||
organization: organizationId,
|
||||
autoCapitalization: true
|
||||
autoCapitalization: true,
|
||||
}).save();
|
||||
|
||||
// initialize bot for workspace
|
||||
await createBot({
|
||||
name: 'Infisical Bot',
|
||||
workspaceId: workspace._id
|
||||
name: "Infisical Bot",
|
||||
workspaceId: workspace._id,
|
||||
});
|
||||
|
||||
// initialize blind index salt for workspace
|
||||
await SecretService.createSecretBlindIndexData({
|
||||
workspaceId: workspace._id
|
||||
workspaceId: workspace._id,
|
||||
});
|
||||
|
||||
await EELicenseService.refreshPlan(organizationId);
|
||||
@ -55,15 +55,15 @@ export const createWorkspace = async ({
|
||||
export const deleteWorkspace = async ({ id }: { id: string }) => {
|
||||
await Workspace.deleteOne({ _id: id });
|
||||
await Bot.deleteOne({
|
||||
workspace: id
|
||||
workspace: id,
|
||||
});
|
||||
await Membership.deleteMany({
|
||||
workspace: id
|
||||
workspace: id,
|
||||
});
|
||||
await Secret.deleteMany({
|
||||
workspace: id
|
||||
workspace: id,
|
||||
});
|
||||
await Key.deleteMany({
|
||||
workspace: id
|
||||
workspace: id,
|
||||
});
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user