mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 08:27:22 +00:00
Merge branch 'main' into feature/google-signin-signup-integration
This commit is contained in:
Generated
+43
-7
@@ -34,7 +34,7 @@
|
|||||||
"express-validator": "^6.14.2",
|
"express-validator": "^6.14.2",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"helmet": "^5.1.1",
|
"helmet": "^5.1.1",
|
||||||
"infisical-node": "^1.0.37",
|
"infisical-node": "^1.1.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
@@ -5345,6 +5345,14 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/co": {
|
"node_modules/co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -6941,11 +6949,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/infisical-node": {
|
"node_modules/infisical-node": {
|
||||||
"version": "1.0.37",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz",
|
"resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.1.3.tgz",
|
||||||
"integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==",
|
"integrity": "sha512-MLcZQ/zdpCYFRbj50Tn4Qm58wSKPQfKc3xX4I0c3NnFZvMGd50wnoG1jkkNKjKiYU5h7QDpOg0XZSvlU7yuG6g==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"node-cache": "^5.1.2",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"tweetnacl-util": "^0.15.1"
|
"tweetnacl-util": "^0.15.1"
|
||||||
}
|
}
|
||||||
@@ -8439,6 +8449,17 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||||
},
|
},
|
||||||
|
"node_modules/node-cache": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"clone": "2.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||||
@@ -17402,6 +17423,11 @@
|
|||||||
"wrap-ansi": "^7.0.0"
|
"wrap-ansi": "^7.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"clone": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="
|
||||||
|
},
|
||||||
"co": {
|
"co": {
|
||||||
"version": "4.6.0",
|
"version": "4.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||||
@@ -18622,11 +18648,13 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"infisical-node": {
|
"infisical-node": {
|
||||||
"version": "1.0.37",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.0.37.tgz",
|
"resolved": "https://registry.npmjs.org/infisical-node/-/infisical-node-1.1.3.tgz",
|
||||||
"integrity": "sha512-9ZswN5UovZq46a7Qv/4KmfaAu9pO/TmxxdcEY2PosDIAlXbpfC651hcKr7T8q1iMlRnHLA/gpYkcZMeS0es0rg==",
|
"integrity": "sha512-MLcZQ/zdpCYFRbj50Tn4Qm58wSKPQfKc3xX4I0c3NnFZvMGd50wnoG1jkkNKjKiYU5h7QDpOg0XZSvlU7yuG6g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"axios": "^1.3.3",
|
"axios": "^1.3.3",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"node-cache": "^5.1.2",
|
||||||
"tweetnacl": "^1.0.3",
|
"tweetnacl": "^1.0.3",
|
||||||
"tweetnacl-util": "^0.15.1"
|
"tweetnacl-util": "^0.15.1"
|
||||||
}
|
}
|
||||||
@@ -19774,6 +19802,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
|
||||||
},
|
},
|
||||||
|
"node-cache": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
|
||||||
|
"requires": {
|
||||||
|
"clone": "2.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"@aws-sdk/client-secrets-manager": "^3.303.0",
|
"@aws-sdk/client-secrets-manager": "^3.303.0",
|
||||||
"@godaddy/terminus": "^4.11.2",
|
"@godaddy/terminus": "^4.11.2",
|
||||||
"@octokit/rest": "^19.0.5",
|
"@octokit/rest": "^19.0.5",
|
||||||
"@sentry/node": "^7.45.0",
|
"@sentry/node": "^7.41.0",
|
||||||
"@sentry/tracing": "^7.46.0",
|
"@sentry/tracing": "^7.46.0",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
"@types/libsodium-wrappers": "^0.7.10",
|
"@types/libsodium-wrappers": "^0.7.10",
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"express-validator": "^6.14.2",
|
"express-validator": "^6.14.2",
|
||||||
"handlebars": "^4.7.7",
|
"handlebars": "^4.7.7",
|
||||||
"helmet": "^5.1.1",
|
"helmet": "^5.1.1",
|
||||||
"infisical-node": "^1.0.37",
|
"infisical-node": "^1.1.3",
|
||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"jsonwebtoken": "^9.0.0",
|
"jsonwebtoken": "^9.0.0",
|
||||||
"jsrp": "^0.2.4",
|
"jsrp": "^0.2.4",
|
||||||
|
|||||||
+63
-58
@@ -1,67 +1,72 @@
|
|||||||
import infisical from 'infisical-node';
|
import InfisicalClient from 'infisical-node';
|
||||||
export const getPort = () => infisical.get('PORT')! || 4000;
|
|
||||||
export const getInviteOnlySignup = () => infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : infisical.get('INVITE_ONLY_SIGNUP');
|
const client = new InfisicalClient({
|
||||||
export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!;
|
token: process.env.INFISICAL_TOKEN!
|
||||||
export const getSaltRounds = () => parseInt(infisical.get('SALT_ROUNDS')!) || 10;
|
});
|
||||||
export const getJwtAuthLifetime = () => infisical.get('JWT_AUTH_LIFETIME')! || '10d';
|
|
||||||
export const getJwtAuthSecret = () => infisical.get('JWT_AUTH_SECRET')!;
|
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
|
||||||
export const getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
|
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue == undefined ? false : await client.getSecret('INVITE_ONLY_SIGNUP');
|
||||||
export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m';
|
export const getEncryptionKey = async () => (await client.getSecret('ENCRYPTION_KEY')).secretValue;
|
||||||
export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d';
|
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
|
||||||
export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!;
|
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
|
||||||
export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!;
|
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;
|
||||||
export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')! || '15m';
|
export const getJwtMfaLifetime = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
||||||
export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!;
|
export const getJwtMfaSecret = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
|
||||||
export const getJwtProviderAuthSecret = () => infisical.get('JWT_PROVIDER_AUTH_SECRET')!;
|
export const getJwtRefreshLifetime = async () => (await client.getSecret('JWT_REFRESH_LIFETIME')).secretValue || '90d';
|
||||||
export const getJwtProviderAuthLifetime = () => infisical.get('JWT_PROVIDER_AUTH_LIFETIME')! || '15m';
|
export const getJwtRefreshSecret = async () => (await client.getSecret('JWT_REFRESH_SECRET')).secretValue;
|
||||||
export const getMongoURL = () => infisical.get('MONGO_URL')!;
|
export const getJwtServiceSecret = async () => (await client.getSecret('JWT_SERVICE_SECRET')).secretValue;
|
||||||
export const getNodeEnv = () => infisical.get('NODE_ENV')! || 'production';
|
export const getJwtSignupLifetime = async () => (await client.getSecret('JWT_SIGNUP_LIFETIME')).secretValue || '15m';
|
||||||
export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true;
|
export const getJwtProviderAuthSecret = async () => (await client.getSecret('JWT_PROVIDER_AUTH_SECRET')).secretValue;
|
||||||
export const getLokiHost = () => infisical.get('LOKI_HOST')!;
|
export const getJwtProviderAuthLifetime = async () => (await client.getSecret('JWT_PROVIDER_AUTH_LIFETIME')).secretValue || '15m';
|
||||||
export const getClientIdAzure = () => infisical.get('CLIENT_ID_AZURE')!;
|
export const getJwtSignupSecret = async () => (await client.getSecret('JWT_SIGNUP_SECRET')).secretValue;
|
||||||
export const getClientIdHeroku = () => infisical.get('CLIENT_ID_HEROKU')!;
|
export const getMongoURL = async () => (await client.getSecret('MONGO_URL')).secretValue;
|
||||||
export const getClientIdVercel = () => infisical.get('CLIENT_ID_VERCEL')!;
|
export const getNodeEnv = async () => (await client.getSecret('NODE_ENV')).secretValue || 'production';
|
||||||
export const getClientIdNetlify = () => infisical.get('CLIENT_ID_NETLIFY')!;
|
export const getVerboseErrorOutput = async () => (await client.getSecret('VERBOSE_ERROR_OUTPUT')).secretValue === 'true' && true;
|
||||||
export const getClientIdGitHub = () => infisical.get('CLIENT_ID_GITHUB')!;
|
export const getLokiHost = async () => (await client.getSecret('LOKI_HOST')).secretValue;
|
||||||
export const getClientIdGitLab = () => infisical.get('CLIENT_ID_GITLAB')!;
|
export const getClientIdAzure = async () => (await client.getSecret('CLIENT_ID_AZURE')).secretValue;
|
||||||
export const getClientSecretAzure = () => infisical.get('CLIENT_SECRET_AZURE')!;
|
export const getClientIdHeroku = async () => (await client.getSecret('CLIENT_ID_HEROKU')).secretValue;
|
||||||
export const getClientSecretHeroku = () => infisical.get('CLIENT_SECRET_HEROKU')!;
|
export const getClientIdVercel = async () => (await client.getSecret('CLIENT_ID_VERCEL')).secretValue;
|
||||||
export const getClientSecretVercel = () => infisical.get('CLIENT_SECRET_VERCEL')!;
|
export const getClientIdNetlify = async () => (await client.getSecret('CLIENT_ID_NETLIFY')).secretValue;
|
||||||
export const getClientSecretNetlify = () => infisical.get('CLIENT_SECRET_NETLIFY')!;
|
export const getClientIdGitHub = async () => (await client.getSecret('CLIENT_ID_GITHUB')).secretValue;
|
||||||
export const getClientSecretGitHub = () => infisical.get('CLIENT_SECRET_GITHUB')!;
|
export const getClientIdGitLab = async () => (await client.getSecret('CLIENT_ID_GITLAB')).secretValue;
|
||||||
export const getClientSecretGitLab = () => infisical.get('CLIENT_SECRET_GITLAB')!;
|
export const getClientSecretAzure = async () => (await client.getSecret('CLIENT_SECRET_AZURE')).secretValue;
|
||||||
export const getClientSlugVercel = () => infisical.get('CLIENT_SLUG_VERCEL')!;
|
export const getClientSecretHeroku = async () => (await client.getSecret('CLIENT_SECRET_HEROKU')).secretValue;
|
||||||
export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com';
|
export const getClientSecretVercel = async () => (await client.getSecret('CLIENT_SECRET_VERCEL')).secretValue;
|
||||||
export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
export const getClientSecretNetlify = async () => (await client.getSecret('CLIENT_SECRET_NETLIFY')).secretValue;
|
||||||
export const getSentryDSN = () => infisical.get('SENTRY_DSN')!;
|
export const getClientSecretGitHub = async () => (await client.getSecret('CLIENT_SECRET_GITHUB')).secretValue;
|
||||||
export const getSessionSecret = () => infisical.get('SESSION_SECRET')!;
|
export const getClientSecretGitLab = async () => (await client.getSecret('CLIENT_SECRET_GITLAB')).secretValue;
|
||||||
export const getSiteURL = () => infisical.get('SITE_URL')!;
|
export const getClientSlugVercel = async () => (await client.getSecret('CLIENT_SLUG_VERCEL')).secretValue;
|
||||||
export const getSmtpHost = () => infisical.get('SMTP_HOST')!;
|
export const getPostHogHost = async () => (await client.getSecret('POSTHOG_HOST')).secretValue || 'https://app.posthog.com';
|
||||||
export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false;
|
export const getPostHogProjectApiKey = async () => (await client.getSecret('POSTHOG_PROJECT_API_KEY')).secretValue || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
|
||||||
export const getSmtpPort = () => parseInt(infisical.get('SMTP_PORT')!) || 587;
|
export const getSentryDSN = async () => (await client.getSecret('SENTRY_DSN')).secretValue;
|
||||||
export const getSmtpUsername = () => infisical.get('SMTP_USERNAME')!;
|
export const getSessionSecret = async () => (await client.getSecret('SESSION_SECRET')).secretValue;
|
||||||
export const getSmtpPassword = () => infisical.get('SMTP_PASSWORD')!;
|
export const getSiteURL = async () => (await client.getSecret('SITE_URL')).secretValue;
|
||||||
export const getSmtpFromAddress = () => infisical.get('SMTP_FROM_ADDRESS')!;
|
export const getSmtpHost = async () => (await client.getSecret('SMTP_HOST')).secretValue;
|
||||||
export const getSmtpFromName = () => infisical.get('SMTP_FROM_NAME')! || 'Infisical';
|
export const getSmtpSecure = async () => (await client.getSecret('SMTP_SECURE')).secretValue === 'true' || false;
|
||||||
export const getStripeProductStarter = () => infisical.get('STRIPE_PRODUCT_STARTER')!;
|
export const getSmtpPort = async () => parseInt((await client.getSecret('SMTP_PORT')).secretValue) || 587;
|
||||||
export const getStripeProductPro = () => infisical.get('STRIPE_PRODUCT_PRO')!;
|
export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAME')).secretValue;
|
||||||
export const getStripeProductTeam = () => infisical.get('STRIPE_PRODUCT_TEAM')!;
|
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
|
||||||
export const getStripePublishableKey = () => infisical.get('STRIPE_PUBLISHABLE_KEY')!;
|
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
|
||||||
export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!;
|
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
|
||||||
export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!;
|
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
|
||||||
export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true;
|
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
|
||||||
export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!;
|
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
|
||||||
export const getSmtpConfigured = () => infisical.get('SMTP_HOST') == '' || infisical.get('SMTP_HOST') == undefined ? false : true
|
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
|
||||||
export const getHttpsEnabled = () => {
|
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
|
||||||
if (getNodeEnv() != "production") {
|
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 getHttpsEnabled = async () => {
|
||||||
|
if ((await getNodeEnv()) != "production") {
|
||||||
// no https for anything other than prod
|
// no https for anything other than prod
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (infisical.get('HTTPS_ENABLED') == undefined || infisical.get('HTTPS_ENABLED') == "") {
|
if ((await client.getSecret('HTTPS_ENABLED')).secretValue == undefined || (await client.getSecret('HTTPS_ENABLED')).secretValue == "") {
|
||||||
// default when no value present
|
// default when no value present
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
return infisical.get('HTTPS_ENABLED') === 'true' && true
|
return (await client.getSecret('HTTPS_ENABLED')).secretValue === 'true' && true
|
||||||
}
|
}
|
||||||
@@ -126,7 +126,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled()
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
const loginAction = await EELogService.createAction({
|
const loginAction = await EELogService.createAction({
|
||||||
@@ -182,7 +182,7 @@ export const logout = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled() as boolean
|
secure: (await getHttpsEnabled()) as boolean
|
||||||
});
|
});
|
||||||
|
|
||||||
const logoutAction = await EELogService.createAction({
|
const logoutAction = await EELogService.createAction({
|
||||||
@@ -237,7 +237,7 @@ export const getNewToken = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(refreshToken, getJwtRefreshSecret())
|
jwt.verify(refreshToken, await getJwtRefreshSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
@@ -252,8 +252,8 @@ export const getNewToken = async (req: Request, res: Response) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId: decodedToken.userId
|
userId: decodedToken.userId
|
||||||
},
|
},
|
||||||
expiresIn: getJwtAuthLifetime(),
|
expiresIn: await getJwtAuthLifetime(),
|
||||||
secret: getJwtAuthSecret()
|
secret: await getJwtAuthSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const getIntegrationAuth = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const getIntegrationOptions = async (req: Request, res: Response) => {
|
export const getIntegrationOptions = async (req: Request, res: Response) => {
|
||||||
const INTEGRATION_OPTIONS = getIntegrationOptionsFunc();
|
const INTEGRATION_OPTIONS = await getIntegrationOptionsFunc();
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
integrationOptions: INTEGRATION_OPTIONS,
|
integrationOptions: INTEGRATION_OPTIONS,
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => {
|
|||||||
inviterFirstName: req.user.firstName,
|
inviterFirstName: req.user.firstName,
|
||||||
inviterEmail: req.user.email,
|
inviterEmail: req.user.email,
|
||||||
workspaceName: req.membership.workspace.name,
|
workspaceName: req.membership.workspace.name,
|
||||||
callback_url: getSiteURL() + '/login'
|
callback_url: (await getSiteURL()) + '/login'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -180,11 +180,11 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
organizationName: organization.name,
|
organizationName: organization.name,
|
||||||
email: inviteeEmail,
|
email: inviteeEmail,
|
||||||
token,
|
token,
|
||||||
callback_url: getSiteURL() + '/signupinvite'
|
callback_url: (await getSiteURL()) + '/signupinvite'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!getSmtpConfigured()) {
|
if (!(await getSmtpConfigured())) {
|
||||||
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}`
|
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,8 +257,8 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn: getJwtSignupLifetime(),
|
expiresIn: await getJwtSignupLifetime(),
|
||||||
secret: getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ export const createOrganizationPortalSession = async (
|
|||||||
) => {
|
) => {
|
||||||
let session;
|
let session;
|
||||||
try {
|
try {
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -333,13 +333,13 @@ export const createOrganizationPortalSession = async (
|
|||||||
customer: req.membershipOrg.organization.customerId,
|
customer: req.membershipOrg.organization.customerId,
|
||||||
mode: 'setup',
|
mode: 'setup',
|
||||||
payment_method_types: ['card'],
|
payment_method_types: ['card'],
|
||||||
success_url: getSiteURL() + '/dashboard',
|
success_url: (await getSiteURL()) + '/dashboard',
|
||||||
cancel_url: getSiteURL() + '/dashboard'
|
cancel_url: (await getSiteURL()) + '/dashboard'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
session = await stripe.billingPortal.sessions.create({
|
session = await stripe.billingPortal.sessions.create({
|
||||||
customer: req.membershipOrg.organization.customerId,
|
customer: req.membershipOrg.organization.customerId,
|
||||||
return_url: getSiteURL() + '/dashboard'
|
return_url: (await getSiteURL()) + '/dashboard'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ export const getOrganizationSubscriptions = async (
|
|||||||
) => {
|
) => {
|
||||||
let subscriptions;
|
let subscriptions;
|
||||||
try {
|
try {
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => {
|
|||||||
substitutions: {
|
substitutions: {
|
||||||
email,
|
email,
|
||||||
token,
|
token,
|
||||||
callback_url: getSiteURL() + '/password-reset'
|
callback_url: (await getSiteURL()) + '/password-reset'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -91,8 +91,8 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn: getJwtSignupLifetime(),
|
expiresIn: await getJwtSignupLifetime(),
|
||||||
secret: getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const pushSecrets = async (req: Request, res: Response) => {
|
|||||||
// upload (encrypted) secrets to workspace with id [workspaceId]
|
// upload (encrypted) secrets to workspace with id [workspaceId]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
let { secrets }: { secrets: PushSecret[] } = req.body;
|
let { secrets }: { secrets: PushSecret[] } = req.body;
|
||||||
const { keys, environment, channel } = req.body;
|
const { keys, environment, channel } = req.body;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
@@ -114,7 +114,7 @@ export const pullSecrets = async (req: Request, res: Response) => {
|
|||||||
let secrets;
|
let secrets;
|
||||||
let key;
|
let key;
|
||||||
try {
|
try {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const environment: string = req.query.environment as string;
|
const environment: string = req.query.environment as string;
|
||||||
const channel: string = req.query.channel as string;
|
const channel: string = req.query.channel as string;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
@@ -183,7 +183,7 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => {
|
|||||||
let secrets;
|
let secrets;
|
||||||
let key;
|
let key;
|
||||||
try {
|
try {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const environment: string = req.query.environment as string;
|
const environment: string = req.query.environment as string;
|
||||||
const channel: string = req.query.channel as string;
|
const channel: string = req.query.channel as string;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { Request, Response } from 'express';
|
||||||
|
import { Secret } from '../../models';
|
||||||
|
import Folder from '../../models/folder';
|
||||||
|
import { BadRequestError } from '../../utils/errors';
|
||||||
|
import { ROOT_FOLDER_PATH, getFolderPath, getParentPath, normalizePath, validateFolderName } from '../../utils/folder';
|
||||||
|
import { ADMIN, MEMBER } from '../../variables';
|
||||||
|
import { validateMembership } from '../../helpers/membership';
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// verify workspace id/environment
|
||||||
|
export const createFolder = async (req: Request, res: Response) => {
|
||||||
|
const { workspaceId, environment, folderName, parentFolderId } = req.body
|
||||||
|
if (!validateFolderName(folderName)) {
|
||||||
|
throw BadRequestError({ message: "Folder name cannot contain spaces. Only underscore and dashes" })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parentFolderId) {
|
||||||
|
const parentFolder = await Folder.find({ environment: environment, workspace: workspaceId, id: parentFolderId });
|
||||||
|
if (!parentFolder) {
|
||||||
|
throw BadRequestError({ message: "The parent folder doesn't exist" })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let completePath = await getFolderPath(parentFolderId)
|
||||||
|
if (completePath == ROOT_FOLDER_PATH) {
|
||||||
|
completePath = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentFolderPath = completePath + "/" + folderName // construct new path with current folder to be created
|
||||||
|
const normalizedCurrentPath = normalizePath(currentFolderPath)
|
||||||
|
const normalizedParentPath = getParentPath(normalizedCurrentPath)
|
||||||
|
|
||||||
|
const existingFolder = await Folder.findOne({
|
||||||
|
name: folderName,
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parent: parentFolderId,
|
||||||
|
path: normalizedCurrentPath
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingFolder) {
|
||||||
|
return res.json(existingFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
const newFolder = new Folder({
|
||||||
|
name: folderName,
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parent: parentFolderId,
|
||||||
|
path: normalizedCurrentPath,
|
||||||
|
parentPath: normalizedParentPath
|
||||||
|
});
|
||||||
|
|
||||||
|
await newFolder.save();
|
||||||
|
|
||||||
|
return res.json(newFolder)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteFolder = async (req: Request, res: Response) => {
|
||||||
|
const { folderId } = req.params
|
||||||
|
const queue: any[] = [folderId];
|
||||||
|
|
||||||
|
const folder = await Folder.findById(folderId);
|
||||||
|
if (!folder) {
|
||||||
|
throw BadRequestError({ message: "The folder doesn't exist" })
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that user is a member of the workspace
|
||||||
|
await validateMembership({
|
||||||
|
userId: req.user._id.toString(),
|
||||||
|
workspaceId: folder.workspace as any,
|
||||||
|
acceptedRoles: [ADMIN, MEMBER]
|
||||||
|
});
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
const currentFolderId = queue.shift();
|
||||||
|
|
||||||
|
const childFolders = await Folder.find({ parent: currentFolderId });
|
||||||
|
for (const childFolder of childFolders) {
|
||||||
|
queue.push(childFolder._id);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Secret.deleteMany({ folder: currentFolderId });
|
||||||
|
|
||||||
|
await Folder.deleteOne({ _id: currentFolderId });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send()
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ export const createServiceToken = async (req: Request, res: Response) => {
|
|||||||
workspaceId
|
workspaceId
|
||||||
},
|
},
|
||||||
expiresIn: expiresIn,
|
expiresIn: expiresIn,
|
||||||
secret: getJwtServiceSecret()
|
secret: await getJwtServiceSecret()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return res.status(400).send({
|
return res.status(400).send({
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export const beginEmailSignup = async (req: Request, res: Response) => {
|
|||||||
try {
|
try {
|
||||||
email = req.body.email;
|
email = req.body.email;
|
||||||
|
|
||||||
if (getInviteOnlySignup()) {
|
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
|
// 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) {
|
if (userCount != 0) {
|
||||||
@@ -75,7 +75,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify email
|
// verify email
|
||||||
if (getSmtpConfigured()) {
|
if (await getSmtpConfigured()) {
|
||||||
await checkEmailVerification({
|
await checkEmailVerification({
|
||||||
email,
|
email,
|
||||||
code
|
code
|
||||||
@@ -93,8 +93,8 @@ export const verifyEmailSignup = async (req: Request, res: Response) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn: getJwtSignupLifetime(),
|
expiresIn: await getJwtSignupLifetime(),
|
||||||
secret: getJwtSignupSecret()
|
secret: await getJwtSignupSecret()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
|
|||||||
let event;
|
let event;
|
||||||
try {
|
try {
|
||||||
// check request for valid stripe signature
|
// check request for valid stripe signature
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
|
|||||||
event = stripe.webhooks.constructEvent(
|
event = stripe.webhooks.constructEvent(
|
||||||
req.body,
|
req.body,
|
||||||
sig,
|
sig,
|
||||||
getStripeWebhookSecret()
|
await getStripeWebhookSecret()
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser({ email: req.user.email });
|
Sentry.setUser({ email: req.user.email });
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const createAPIKeyData = async (req: Request, res: Response) => {
|
|||||||
const { name, expiresIn } = req.body;
|
const { name, expiresIn } = req.body;
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('hex');
|
const secret = crypto.randomBytes(16).toString('hex');
|
||||||
const secretHash = await bcrypt.hash(secret, getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
const expiresAt = new Date();
|
const expiresAt = new Date();
|
||||||
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId: user._id.toString()
|
userId: user._id.toString()
|
||||||
},
|
},
|
||||||
expiresIn: getJwtMfaLifetime(),
|
expiresIn: await getJwtMfaLifetime(),
|
||||||
secret: getJwtMfaSecret()
|
secret: await getJwtMfaSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
const code = await TokenService.createToken({
|
const code = await TokenService.createToken({
|
||||||
@@ -163,7 +163,7 @@ export const login2 = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled()
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
// case: user does not have MFA enablgged
|
// case: user does not have MFA enablgged
|
||||||
@@ -302,7 +302,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled()
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
|
|
||||||
interface VerifyMfaTokenRes {
|
interface VerifyMfaTokenRes {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { AccountNotFoundError } from '../../utils/errors';
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecret = async (req: Request, res: Response) => {
|
export const createSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const secretToCreate: CreateSecretRequestBody = req.body.secret;
|
const secretToCreate: CreateSecretRequestBody = req.body.secret;
|
||||||
const { workspaceId, environment } = req.params
|
const { workspaceId, environment } = req.params
|
||||||
const sanitizedSecret: SanitizedSecretForCreate = {
|
const sanitizedSecret: SanitizedSecretForCreate = {
|
||||||
@@ -70,7 +70,7 @@ export const createSecret = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const createSecrets = async (req: Request, res: Response) => {
|
export const createSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets;
|
||||||
const { workspaceId, environment } = req.params
|
const { workspaceId, environment } = req.params
|
||||||
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
|
const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = []
|
||||||
@@ -132,7 +132,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecrets = async (req: Request, res: Response) => {
|
export const deleteSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params
|
||||||
const secretIdsToDelete: string[] = req.body.secretIds
|
const secretIdsToDelete: string[] = req.body.secretIds
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
* @param res
|
* @param res
|
||||||
*/
|
*/
|
||||||
export const deleteSecret = async (req: Request, res: Response) => {
|
export const deleteSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
await Secret.findByIdAndDelete(req._secret._id)
|
await Secret.findByIdAndDelete(req._secret._id)
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
@@ -215,7 +215,7 @@ export const deleteSecret = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateSecrets = async (req: Request, res: Response) => {
|
export const updateSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params
|
||||||
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
|
const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets;
|
||||||
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then())
|
||||||
@@ -283,7 +283,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const updateSecret = async (req: Request, res: Response) => {
|
export const updateSecret = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { workspaceId, environmentName } = req.params
|
const { workspaceId, environmentName } = req.params
|
||||||
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
|
const secretModificationsRequested: ModifySecretRequestBody = req.body.secret;
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ export const updateSecret = async (req: Request, res: Response) => {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export const getSecrets = async (req: Request, res: Response) => {
|
export const getSecrets = async (req: Request, res: Response) => {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const { environment } = req.query;
|
const { environment } = req.query;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import {
|
|||||||
BatchSecretRequest,
|
BatchSecretRequest,
|
||||||
BatchSecret
|
BatchSecret
|
||||||
} from '../../types/secret';
|
} from '../../types/secret';
|
||||||
|
import { getFolderPath, getFoldersInDirectory, normalizePath } from '../../utils/folder';
|
||||||
|
import { ROOT_FOLDER_PATH } from '../../utils/folder';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Peform a batch of any specified CUD secret operations
|
* Peform a batch of any specified CUD secret operations
|
||||||
@@ -35,7 +37,7 @@ import {
|
|||||||
export const batchSecrets = async (req: Request, res: Response) => {
|
export const batchSecrets = async (req: Request, res: Response) => {
|
||||||
|
|
||||||
const channel = getChannelFromUserAgent(req.headers['user-agent']);
|
const channel = getChannelFromUserAgent(req.headers['user-agent']);
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
workspaceId,
|
workspaceId,
|
||||||
@@ -51,13 +53,18 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
const updateSecrets: BatchSecret[] = [];
|
const updateSecrets: BatchSecret[] = [];
|
||||||
const deleteSecrets: Types.ObjectId[] = [];
|
const deleteSecrets: Types.ObjectId[] = [];
|
||||||
const actions: IAction[] = [];
|
const actions: IAction[] = [];
|
||||||
|
|
||||||
// get secret blind index salt
|
// get secret blind index salt
|
||||||
const salt = await SecretService.getSecretBlindIndexSalt({
|
const salt = await SecretService.getSecretBlindIndexSalt({
|
||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
for await (const request of requests) {
|
for await (const request of requests) {
|
||||||
|
const folderId = request.secret.folderId
|
||||||
|
|
||||||
|
// TODO: need to auth folder
|
||||||
|
const fullFolderPath = await getFolderPath(folderId)
|
||||||
|
|
||||||
let secretBlindIndex = '';
|
let secretBlindIndex = '';
|
||||||
switch (request.method) {
|
switch (request.method) {
|
||||||
case 'POST':
|
case 'POST':
|
||||||
@@ -72,19 +79,23 @@ export const batchSecrets = async (req: Request, res: Response) => {
|
|||||||
user: request.secret.type === SECRET_PERSONAL ? req.user : undefined,
|
user: request.secret.type === SECRET_PERSONAL ? req.user : undefined,
|
||||||
environment,
|
environment,
|
||||||
workspace: new Types.ObjectId(workspaceId),
|
workspace: new Types.ObjectId(workspaceId),
|
||||||
|
path: fullFolderPath,
|
||||||
|
folder: folderId,
|
||||||
secretBlindIndex
|
secretBlindIndex
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'PATCH':
|
case 'PATCH':
|
||||||
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
|
||||||
secretName: request.secret.secretName,
|
secretName: request.secret.secretName,
|
||||||
salt
|
salt,
|
||||||
});
|
});
|
||||||
|
|
||||||
updateSecrets.push({
|
updateSecrets.push({
|
||||||
...request.secret,
|
...request.secret,
|
||||||
_id: new Types.ObjectId(request.secret._id),
|
_id: new Types.ObjectId(request.secret._id),
|
||||||
secretBlindIndex
|
secretBlindIndex,
|
||||||
|
folder: folderId,
|
||||||
|
path: fullFolderPath,
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
@@ -437,9 +448,9 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map((insertedSecret) => insertedSecret.toObject());
|
const newlyCreatedSecrets: ISecret[] = (await Secret.insertMany(secretsToInsert)).map((insertedSecret) => insertedSecret.toObject());
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
// trigger event - push secrets
|
// trigger event - push secrets
|
||||||
await EventService.handleEvent({
|
await EventService.handleEvent({
|
||||||
@@ -508,7 +519,7 @@ export const createSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(workspaceId)
|
workspaceId: new Types.ObjectId(workspaceId)
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets added',
|
event: 'secrets added',
|
||||||
@@ -578,9 +589,11 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { tagSlugs } = req.query;
|
const { tagSlugs, secretsPath } = req.query;
|
||||||
const workspaceId = req.query.workspaceId as string;
|
const workspaceId = req.query.workspaceId as string;
|
||||||
const environment = req.query.environment as string;
|
const environment = req.query.environment as string;
|
||||||
|
const normalizedPath = normalizePath(secretsPath as string)
|
||||||
|
const folders = await getFoldersInDirectory(workspaceId as string, environment as string, normalizedPath)
|
||||||
|
|
||||||
// secrets to return
|
// secrets to return
|
||||||
let secrets: ISecret[] = [];
|
let secrets: ISecret[] = [];
|
||||||
@@ -613,6 +626,12 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalizedPath == ROOT_FOLDER_PATH) {
|
||||||
|
secretQuery.path = { $in: [ROOT_FOLDER_PATH, null, undefined] }
|
||||||
|
} else if (normalizedPath) {
|
||||||
|
secretQuery.path = normalizedPath
|
||||||
|
}
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
secretQuery.tags = { $in: tagIds };
|
secretQuery.tags = { $in: tagIds };
|
||||||
}
|
}
|
||||||
@@ -638,6 +657,13 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: check if user can query for given path
|
||||||
|
if (normalizedPath == ROOT_FOLDER_PATH) {
|
||||||
|
secretQuery.path = { $in: [ROOT_FOLDER_PATH, null, undefined] }
|
||||||
|
} else if (normalizedPath) {
|
||||||
|
secretQuery.path = normalizedPath
|
||||||
|
}
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
secretQuery.tags = { $in: tagIds };
|
secretQuery.tags = { $in: tagIds };
|
||||||
}
|
}
|
||||||
@@ -655,6 +681,12 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
user: { $exists: false } // shared secrets only from workspace
|
user: { $exists: false } // shared secrets only from workspace
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (normalizedPath == ROOT_FOLDER_PATH) {
|
||||||
|
secretQuery.path = { $in: [ROOT_FOLDER_PATH, null, undefined] }
|
||||||
|
} else if (normalizedPath) {
|
||||||
|
secretQuery.path = normalizedPath
|
||||||
|
}
|
||||||
|
|
||||||
if (tagIds.length > 0) {
|
if (tagIds.length > 0) {
|
||||||
secretQuery.tags = { $in: tagIds };
|
secretQuery.tags = { $in: tagIds };
|
||||||
}
|
}
|
||||||
@@ -683,7 +715,7 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
ipAddress: req.ip
|
ipAddress: req.ip
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets pulled',
|
event: 'secrets pulled',
|
||||||
@@ -701,7 +733,8 @@ export const getSecrets = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).send({
|
return res.status(200).send({
|
||||||
secrets
|
secrets,
|
||||||
|
folders
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -905,7 +938,7 @@ export const updateSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(key)
|
workspaceId: new Types.ObjectId(key)
|
||||||
})
|
})
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets modified',
|
event: 'secrets modified',
|
||||||
@@ -1039,7 +1072,7 @@ export const deleteSecrets = async (req: Request, res: Response) => {
|
|||||||
workspaceId: new Types.ObjectId(key)
|
workspaceId: new Types.ObjectId(key)
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
event: 'secrets deleted',
|
event: 'secrets deleted',
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const createServiceAccount = async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('base64');
|
const secret = crypto.randomBytes(16).toString('base64');
|
||||||
const secretHash = await bcrypt.hash(secret, getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
// create service account
|
// create service account
|
||||||
const serviceAccount = await new ServiceAccount({
|
const serviceAccount = await new ServiceAccount({
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => {
|
|||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
const secret = crypto.randomBytes(16).toString('hex');
|
const secret = crypto.randomBytes(16).toString('hex');
|
||||||
const secretHash = await bcrypt.hash(secret, getSaltRounds());
|
const secretHash = await bcrypt.hash(secret, await getSaltRounds());
|
||||||
|
|
||||||
let expiresAt;
|
let expiresAt;
|
||||||
if (expiresIn) {
|
if (expiresIn) {
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
token = tokens.token;
|
token = tokens.token;
|
||||||
|
|
||||||
// sending a welcome email to new users
|
// sending a welcome email to new users
|
||||||
if (getLoopsApiKey()) {
|
if (await getLoopsApiKey()) {
|
||||||
await request.post("https://app.loops.so/api/v1/events/send", {
|
await request.post("https://app.loops.so/api/v1/events/send", {
|
||||||
"email": email,
|
"email": email,
|
||||||
"eventName": "Sign Up",
|
"eventName": "Sign Up",
|
||||||
@@ -117,7 +117,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
}, {
|
}, {
|
||||||
headers: {
|
headers: {
|
||||||
"Accept": "application/json",
|
"Accept": "application/json",
|
||||||
"Authorization": "Bearer " + getLoopsApiKey()
|
"Authorization": "Bearer " + (await getLoopsApiKey())
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -127,7 +127,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled()
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
@@ -232,7 +232,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => {
|
|||||||
httpOnly: true,
|
httpOnly: true,
|
||||||
path: '/',
|
path: '/',
|
||||||
sameSite: 'strict',
|
sameSite: 'strict',
|
||||||
secure: getHttpsEnabled()
|
secure: await getHttpsEnabled()
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ interface V2PushSecret {
|
|||||||
export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
||||||
// upload (encrypted) secrets to workspace with id [workspaceId]
|
// upload (encrypted) secrets to workspace with id [workspaceId]
|
||||||
try {
|
try {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
let { secrets }: { secrets: V2PushSecret[] } = req.body;
|
let { secrets }: { secrets: V2PushSecret[] } = req.body;
|
||||||
const { keys, environment, channel } = req.body;
|
const { keys, environment, channel } = req.body;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
@@ -123,7 +123,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => {
|
|||||||
export const pullSecrets = async (req: Request, res: Response) => {
|
export const pullSecrets = async (req: Request, res: Response) => {
|
||||||
let secrets;
|
let secrets;
|
||||||
try {
|
try {
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
const environment: string = req.query.environment as string;
|
const environment: string = req.query.environment as string;
|
||||||
const channel: string = req.query.channel as string;
|
const channel: string = req.query.channel as string;
|
||||||
const { workspaceId } = req.params;
|
const { workspaceId } = req.params;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config';
|
|||||||
export const handleWebhook = async (req: Request, res: Response) => {
|
export const handleWebhook = async (req: Request, res: Response) => {
|
||||||
let event;
|
let event;
|
||||||
try {
|
try {
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => {
|
|||||||
event = stripe.webhooks.constructEvent(
|
event = stripe.webhooks.constructEvent(
|
||||||
req.body,
|
req.body,
|
||||||
sig,
|
sig,
|
||||||
getStripeWebhookSecret()
|
await getStripeWebhookSecret()
|
||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.setUser({ email: req.user.email });
|
Sentry.setUser({ email: req.user.email });
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ const getAuthUserPayload = async ({
|
|||||||
authTokenValue: string;
|
authTokenValue: string;
|
||||||
}) => {
|
}) => {
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(authTokenValue, getJwtAuthSecret())
|
jwt.verify(authTokenValue, await getJwtAuthSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
@@ -263,16 +263,16 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => {
|
|||||||
payload: {
|
payload: {
|
||||||
userId
|
userId
|
||||||
},
|
},
|
||||||
expiresIn: getJwtAuthLifetime(),
|
expiresIn: await getJwtAuthLifetime(),
|
||||||
secret: getJwtAuthSecret()
|
secret: await getJwtAuthSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
const refreshToken = createToken({
|
const refreshToken = createToken({
|
||||||
payload: {
|
payload: {
|
||||||
userId
|
userId
|
||||||
},
|
},
|
||||||
expiresIn: getJwtRefreshLifetime(),
|
expiresIn: await getJwtRefreshLifetime(),
|
||||||
secret: getJwtRefreshSecret()
|
secret: await getJwtRefreshSecret()
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ const createBot = async ({
|
|||||||
const { publicKey, privateKey } = generateKeyPair();
|
const { publicKey, privateKey } = generateKeyPair();
|
||||||
const { ciphertext, iv, tag } = encryptSymmetric({
|
const { ciphertext, iv, tag } = encryptSymmetric({
|
||||||
plaintext: privateKey,
|
plaintext: privateKey,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
bot = await new Bot({
|
bot = await new Bot({
|
||||||
@@ -216,7 +216,7 @@ const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => {
|
|||||||
ciphertext: bot.encryptedPrivateKey,
|
ciphertext: bot.encryptedPrivateKey,
|
||||||
iv: bot.iv,
|
iv: bot.iv,
|
||||||
tag: bot.tag,
|
tag: bot.tag,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
key = decryptAsymmetric({
|
key = decryptAsymmetric({
|
||||||
|
|||||||
@@ -20,12 +20,12 @@ const initDatabaseHelper = async ({
|
|||||||
// allow empty strings to pass the required validator
|
// 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');
|
||||||
|
|
||||||
getLogger("database").info("Database connection established");
|
(await getLogger("database")).info("Database connection established");
|
||||||
|
|
||||||
await EESecretService.initSecretVersioning();
|
await EESecretService.initSecretVersioning();
|
||||||
await SecretService.initSecretBlindIndexDataHelper();
|
await SecretService.initSecretBlindIndexDataHelper();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
getLogger("database").error(`Unable to establish Database connection due to the error.\n${err}`);
|
(await getLogger("database")).error(`Unable to establish Database connection due to the error.\n${err}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mongoose.connection;
|
return mongoose.connection;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ const sendMail = async ({
|
|||||||
recipients: string[];
|
recipients: string[];
|
||||||
substitutions: any;
|
substitutions: any;
|
||||||
}) => {
|
}) => {
|
||||||
if (getSmtpConfigured()) {
|
if (await getSmtpConfigured()) {
|
||||||
try {
|
try {
|
||||||
const html = fs.readFileSync(
|
const html = fs.readFileSync(
|
||||||
path.resolve(__dirname, '../templates/' + template),
|
path.resolve(__dirname, '../templates/' + template),
|
||||||
@@ -35,7 +35,7 @@ const sendMail = async ({
|
|||||||
const htmlToSend = temp(substitutions);
|
const htmlToSend = temp(substitutions);
|
||||||
|
|
||||||
await smtpTransporter.sendMail({
|
await smtpTransporter.sendMail({
|
||||||
from: `"${getSmtpFromName()}" <${getSmtpFromAddress()}>`,
|
from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`,
|
||||||
to: recipients.join(', '),
|
to: recipients.join(', '),
|
||||||
subject: subjectLine,
|
subject: subjectLine,
|
||||||
html: htmlToSend
|
html: htmlToSend
|
||||||
|
|||||||
@@ -123,11 +123,11 @@ const createOrganization = async ({
|
|||||||
let organization;
|
let organization;
|
||||||
try {
|
try {
|
||||||
// register stripe account
|
// register stripe account
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (getStripeSecretKey()) {
|
if (await getStripeSecretKey()) {
|
||||||
const customer = await stripe.customers.create({
|
const customer = await stripe.customers.create({
|
||||||
email,
|
email,
|
||||||
description: name
|
description: name
|
||||||
@@ -177,14 +177,14 @@ const initSubscriptionOrg = async ({
|
|||||||
if (organization) {
|
if (organization) {
|
||||||
if (organization.customerId) {
|
if (organization.customerId) {
|
||||||
// initialize starter subscription with quantity of 0
|
// initialize starter subscription with quantity of 0
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
const productToPriceMap = {
|
const productToPriceMap = {
|
||||||
starter: getStripeProductStarter(),
|
starter: await getStripeProductStarter(),
|
||||||
team: getStripeProductTeam(),
|
team: await getStripeProductTeam(),
|
||||||
pro: getStripeProductPro()
|
pro: await getStripeProductPro()
|
||||||
};
|
};
|
||||||
|
|
||||||
stripeSubscription = await stripe.subscriptions.create({
|
stripeSubscription = await stripe.subscriptions.create({
|
||||||
@@ -239,7 +239,7 @@ const updateSubscriptionOrgQuantity = async ({
|
|||||||
status: ACCEPTED
|
status: ACCEPTED
|
||||||
});
|
});
|
||||||
|
|
||||||
const stripe = new Stripe(getStripeSecretKey(), {
|
const stripe = new Stripe(await getStripeSecretKey(), {
|
||||||
apiVersion: '2022-08-01'
|
apiVersion: '2022-08-01'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -233,27 +233,29 @@ const initSecretBlindIndexDataHelper = async () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretBlindIndexDataToInsert = workspaceIdsToBlindIndex.map((workspaceToBlindIndex) => {
|
const secretBlindIndexDataToInsert = await Promise.all(
|
||||||
const salt = crypto.randomBytes(16).toString('base64');
|
workspaceIdsToBlindIndex.map(async (workspaceToBlindIndex) => {
|
||||||
|
const salt = crypto.randomBytes(16).toString('base64');
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ciphertext: encryptedSaltCiphertext,
|
ciphertext: encryptedSaltCiphertext,
|
||||||
iv: saltIV,
|
iv: saltIV,
|
||||||
tag: saltTag
|
tag: saltTag
|
||||||
} = encryptSymmetric({
|
} = encryptSymmetric({
|
||||||
plaintext: salt,
|
plaintext: salt,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretBlindIndexData = new SecretBlindIndexData({
|
const secretBlindIndexData = new SecretBlindIndexData({
|
||||||
workspace: workspaceToBlindIndex,
|
workspace: workspaceToBlindIndex,
|
||||||
encryptedSaltCiphertext,
|
encryptedSaltCiphertext,
|
||||||
saltIV,
|
saltIV,
|
||||||
saltTag
|
saltTag
|
||||||
|
})
|
||||||
|
|
||||||
|
return secretBlindIndexData;
|
||||||
})
|
})
|
||||||
|
);
|
||||||
return secretBlindIndexData;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (secretBlindIndexDataToInsert.length > 0) {
|
if (secretBlindIndexDataToInsert.length > 0) {
|
||||||
await SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert);
|
await SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert);
|
||||||
@@ -280,7 +282,7 @@ const createSecretBlindIndexDataHelper = async ({
|
|||||||
tag: saltTag
|
tag: saltTag
|
||||||
} = encryptSymmetric({
|
} = encryptSymmetric({
|
||||||
plaintext: salt,
|
plaintext: salt,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretBlindIndexData = await new SecretBlindIndexData({
|
const secretBlindIndexData = await new SecretBlindIndexData({
|
||||||
@@ -316,7 +318,7 @@ const getSecretBlindIndexSaltHelper = async ({
|
|||||||
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
iv: secretBlindIndexData.saltIV,
|
iv: secretBlindIndexData.saltIV,
|
||||||
tag: secretBlindIndexData.saltTag,
|
tag: secretBlindIndexData.saltTag,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
return salt;
|
return salt;
|
||||||
@@ -378,7 +380,7 @@ const generateSecretBlindIndexHelper = async ({
|
|||||||
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
|
||||||
iv: secretBlindIndexData.saltIV,
|
iv: secretBlindIndexData.saltIV,
|
||||||
tag: secretBlindIndexData.saltTag,
|
tag: secretBlindIndexData.saltTag,
|
||||||
key: getEncryptionKey()
|
key: await getEncryptionKey()
|
||||||
});
|
});
|
||||||
|
|
||||||
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
|
||||||
@@ -508,7 +510,7 @@ const createSecretHelper = async ({
|
|||||||
workspaceId
|
workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
@@ -578,7 +580,7 @@ const getSecretsHelper = async ({
|
|||||||
ipAddress: authData.authIP
|
ipAddress: authData.authIP
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
@@ -660,7 +662,7 @@ const getSecretHelper = async ({
|
|||||||
ipAddress: authData.authIP
|
ipAddress: authData.authIP
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
@@ -798,7 +800,7 @@ const updateSecretHelper = async ({
|
|||||||
workspaceId
|
workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
@@ -905,7 +907,7 @@ const deleteSecretHelper = async ({
|
|||||||
workspaceId
|
workspaceId
|
||||||
});
|
});
|
||||||
|
|
||||||
const postHogClient = TelemetryService.getPostHogClient();
|
const postHogClient = await TelemetryService.getPostHogClient();
|
||||||
|
|
||||||
if (postHogClient) {
|
if (postHogClient) {
|
||||||
postHogClient.capture({
|
postHogClient.capture({
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ const createTokenHelper = async ({
|
|||||||
const query: TokenDataQuery = { type };
|
const query: TokenDataQuery = { type };
|
||||||
const update: TokenDataUpdate = {
|
const update: TokenDataUpdate = {
|
||||||
type,
|
type,
|
||||||
tokenHash: await bcrypt.hash(token, getSaltRounds()),
|
tokenHash: await bcrypt.hash(token, await getSaltRounds()),
|
||||||
expiresAt
|
expiresAt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import {
|
|||||||
AUTH_MODE_SERVICE_TOKEN,
|
AUTH_MODE_SERVICE_TOKEN,
|
||||||
AUTH_MODE_API_KEY
|
AUTH_MODE_API_KEY
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
import { getEncryptionKey } from '../config';
|
|
||||||
import { encryptSymmetric } from '../utils/crypto';
|
import { encryptSymmetric } from '../utils/crypto';
|
||||||
import { SecretService } from '../services';
|
import { SecretService } from '../services';
|
||||||
|
|
||||||
|
|||||||
+13
-18
@@ -1,6 +1,5 @@
|
|||||||
import dotenv from 'dotenv';
|
import dotenv from 'dotenv';
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
import infisical from 'infisical-node';
|
|
||||||
import express from 'express';
|
import express from 'express';
|
||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
@@ -45,7 +44,8 @@ import {
|
|||||||
password as v1PasswordRouter,
|
password as v1PasswordRouter,
|
||||||
stripe as v1StripeRouter,
|
stripe as v1StripeRouter,
|
||||||
integration as v1IntegrationRouter,
|
integration as v1IntegrationRouter,
|
||||||
integrationAuth as v1IntegrationAuthRouter
|
integrationAuth as v1IntegrationAuthRouter,
|
||||||
|
secretsFolder as v1SecretsFolder
|
||||||
} from './routes/v1';
|
} from './routes/v1';
|
||||||
import {
|
import {
|
||||||
signup as v2SignupRouter,
|
signup as v2SignupRouter,
|
||||||
@@ -80,22 +80,16 @@ import {
|
|||||||
} from './config';
|
} from './config';
|
||||||
|
|
||||||
const main = async () => {
|
const main = async () => {
|
||||||
if (process.env.INFISICAL_TOKEN != "" || process.env.INFISICAL_TOKEN != undefined) {
|
|
||||||
await infisical.connect({
|
|
||||||
token: process.env.INFISICAL_TOKEN!
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TelemetryService.logTelemetryMessage();
|
TelemetryService.logTelemetryMessage();
|
||||||
setTransporter(initSmtp());
|
setTransporter(await initSmtp());
|
||||||
|
|
||||||
await DatabaseService.initDatabase(getMongoURL());
|
await DatabaseService.initDatabase(await getMongoURL());
|
||||||
if (getNodeEnv() !== 'test') {
|
if ((await getNodeEnv()) !== 'test') {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
dsn: getSentryDSN(),
|
dsn: await getSentryDSN(),
|
||||||
tracesSampleRate: 1.0,
|
tracesSampleRate: 1.0,
|
||||||
debug: getNodeEnv() === 'production' ? false : true,
|
debug: await getNodeEnv() === 'production' ? false : true,
|
||||||
environment: getNodeEnv()
|
environment: await getNodeEnv()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +101,7 @@ const main = async () => {
|
|||||||
app.use(
|
app.use(
|
||||||
cors({
|
cors({
|
||||||
credentials: true,
|
credentials: true,
|
||||||
origin: getSiteURL()
|
origin: await getSiteURL()
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -118,7 +112,7 @@ const main = async () => {
|
|||||||
saveUninitialized: false, // don't create session until something stored
|
saveUninitialized: false, // don't create session until something stored
|
||||||
}));
|
}));
|
||||||
|
|
||||||
if (getNodeEnv() === 'production') {
|
if ((await getNodeEnv()) === 'production') {
|
||||||
// enable app-wide rate-limiting + helmet security
|
// enable app-wide rate-limiting + helmet security
|
||||||
// in production
|
// in production
|
||||||
app.disable('x-powered-by');
|
app.disable('x-powered-by');
|
||||||
@@ -150,6 +144,7 @@ const main = async () => {
|
|||||||
app.use('/api/v1/stripe', v1StripeRouter);
|
app.use('/api/v1/stripe', v1StripeRouter);
|
||||||
app.use('/api/v1/integration', v1IntegrationRouter);
|
app.use('/api/v1/integration', v1IntegrationRouter);
|
||||||
app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
|
app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
|
||||||
|
app.use('/api/v1/folder', v1SecretsFolder)
|
||||||
|
|
||||||
// v2 routes (improvements)
|
// v2 routes (improvements)
|
||||||
app.use('/api/v2/signup', v2SignupRouter);
|
app.use('/api/v2/signup', v2SignupRouter);
|
||||||
@@ -184,8 +179,8 @@ const main = async () => {
|
|||||||
|
|
||||||
app.use(requestErrorHandler)
|
app.use(requestErrorHandler)
|
||||||
|
|
||||||
const server = app.listen(getPort(), () => {
|
const server = app.listen(await getPort(), async () => {
|
||||||
getLogger("backend-main").info(`Server started listening at port ${getPort()}`)
|
(await getLogger("backend-main")).info(`Server started listening at port ${await getPort()}`)
|
||||||
});
|
});
|
||||||
|
|
||||||
await createTestUserForDevelopment();
|
await createTestUserForDevelopment();
|
||||||
|
|||||||
@@ -159,9 +159,9 @@ const exchangeCodeAzure = async ({
|
|||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
code: code,
|
code: code,
|
||||||
scope: 'https://vault.azure.net/.default openid offline_access',
|
scope: 'https://vault.azure.net/.default openid offline_access',
|
||||||
client_id: getClientIdAzure(),
|
client_id: await getClientIdAzure(),
|
||||||
client_secret: getClientSecretAzure(),
|
client_secret: await getClientSecretAzure(),
|
||||||
redirect_uri: `${getSiteURL()}/integrations/azure-key-vault/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/azure-key-vault/oauth2/callback`
|
||||||
} as any)
|
} as any)
|
||||||
)).data;
|
)).data;
|
||||||
|
|
||||||
@@ -204,7 +204,7 @@ const exchangeCodeHeroku = async ({
|
|||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
code: code,
|
code: code,
|
||||||
client_secret: getClientSecretHeroku()
|
client_secret: await getClientSecretHeroku()
|
||||||
} as any)
|
} as any)
|
||||||
)).data;
|
)).data;
|
||||||
|
|
||||||
@@ -242,9 +242,9 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => {
|
|||||||
INTEGRATION_VERCEL_TOKEN_URL,
|
INTEGRATION_VERCEL_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
code: code,
|
code: code,
|
||||||
client_id: getClientIdVercel(),
|
client_id: await getClientIdVercel(),
|
||||||
client_secret: getClientSecretVercel(),
|
client_secret: await getClientSecretVercel(),
|
||||||
redirect_uri: `${getSiteURL()}/integrations/vercel/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/vercel/oauth2/callback`
|
||||||
} as any)
|
} as any)
|
||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
@@ -282,9 +282,9 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => {
|
|||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
code: code,
|
code: code,
|
||||||
client_id: getClientIdNetlify(),
|
client_id: await getClientIdNetlify(),
|
||||||
client_secret: getClientSecretNetlify(),
|
client_secret: await getClientSecretNetlify(),
|
||||||
redirect_uri: `${getSiteURL()}/integrations/netlify/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/netlify/oauth2/callback`
|
||||||
} as any)
|
} as any)
|
||||||
)
|
)
|
||||||
).data;
|
).data;
|
||||||
@@ -333,10 +333,10 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => {
|
|||||||
res = (
|
res = (
|
||||||
await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
|
await request.get(INTEGRATION_GITHUB_TOKEN_URL, {
|
||||||
params: {
|
params: {
|
||||||
client_id: getClientIdGitHub(),
|
client_id: await getClientIdGitHub(),
|
||||||
client_secret: getClientSecretGitHub(),
|
client_secret: await getClientSecretGitHub(),
|
||||||
code: code,
|
code: code,
|
||||||
redirect_uri: `${getSiteURL()}/integrations/github/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/github/oauth2/callback`
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
@@ -379,9 +379,9 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => {
|
|||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'authorization_code',
|
grant_type: 'authorization_code',
|
||||||
code: code,
|
code: code,
|
||||||
client_id: getClientIdGitLab(),
|
client_id: await getClientIdGitLab(),
|
||||||
client_secret: getClientSecretGitLab(),
|
client_secret: await getClientSecretGitLab(),
|
||||||
redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`
|
||||||
} as any),
|
} as any),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -133,11 +133,11 @@ const exchangeRefreshAzure = async ({
|
|||||||
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
|
const { data }: { data: RefreshTokenAzureResponse } = await request.post(
|
||||||
INTEGRATION_AZURE_TOKEN_URL,
|
INTEGRATION_AZURE_TOKEN_URL,
|
||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
client_id: getClientIdAzure(),
|
client_id: await getClientIdAzure(),
|
||||||
scope: 'openid offline_access',
|
scope: 'openid offline_access',
|
||||||
refresh_token: refreshToken,
|
refresh_token: refreshToken,
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
client_secret: getClientSecretAzure()
|
client_secret: await getClientSecretAzure()
|
||||||
} as any)
|
} as any)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ const exchangeRefreshHeroku = async ({
|
|||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
refresh_token: refreshToken,
|
refresh_token: refreshToken,
|
||||||
client_secret: getClientSecretHeroku()
|
client_secret: await getClientSecretHeroku()
|
||||||
} as any)
|
} as any)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -223,9 +223,9 @@ const exchangeRefreshGitLab = async ({
|
|||||||
new URLSearchParams({
|
new URLSearchParams({
|
||||||
grant_type: 'refresh_token',
|
grant_type: 'refresh_token',
|
||||||
refresh_token: refreshToken,
|
refresh_token: refreshToken,
|
||||||
client_id: getClientIdGitLab,
|
client_id: await getClientIdGitLab,
|
||||||
client_secret: getClientSecretGitLab(),
|
client_secret: await getClientSecretGitLab(),
|
||||||
redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback`
|
redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback`
|
||||||
} as any),
|
} as any),
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import { getLogger } from "../utils/logger";
|
|||||||
import RequestError, { LogLevel } from "../utils/requestError";
|
import RequestError, { LogLevel } from "../utils/requestError";
|
||||||
import { getNodeEnv } from '../config';
|
import { getNodeEnv } from '../config';
|
||||||
|
|
||||||
export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | Error, req, res, next) => {
|
export const requestErrorHandler: ErrorRequestHandler = async (error: RequestError | Error, req, res, next) => {
|
||||||
if (res.headersSent) return next();
|
if (res.headersSent) return next();
|
||||||
if (getNodeEnv() !== "production") {
|
if ((await getNodeEnv()) !== "production") {
|
||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
console.log(error)
|
console.log(error)
|
||||||
/* eslint-enable no-console */
|
/* eslint-enable no-console */
|
||||||
@@ -15,8 +15,8 @@ export const requestErrorHandler: ErrorRequestHandler = (error: RequestError | E
|
|||||||
|
|
||||||
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
||||||
if (!(error instanceof RequestError)) {
|
if (!(error instanceof RequestError)) {
|
||||||
error = InternalServerError({ context: { exception: error.message }, stack: error.stack })
|
error = InternalServerError({ context: { exception: error.message }, stack: error.stack });
|
||||||
getLogger('backend-main').log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
|
(await getLogger('backend-main')).log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Set Sentry user identification if req.user is populated
|
//* Set Sentry user identification if req.user is populated
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ const requireMfaAuth = async (
|
|||||||
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(AUTH_TOKEN_VALUE, getJwtMfaSecret())
|
jwt.verify(AUTH_TOKEN_VALUE, await getJwtMfaSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const requireServiceTokenAuth = async (
|
|||||||
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(AUTH_TOKEN_VALUE, getJwtServiceSecret())
|
jwt.verify(AUTH_TOKEN_VALUE, await getJwtServiceSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
const serviceToken = await ServiceToken.findOne({
|
const serviceToken = await ServiceToken.findOne({
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const requireSignupAuth = async (
|
|||||||
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'}))
|
||||||
|
|
||||||
const decodedToken = <jwt.UserIDJwtPayload>(
|
const decodedToken = <jwt.UserIDJwtPayload>(
|
||||||
jwt.verify(AUTH_TOKEN_VALUE, getJwtSignupSecret())
|
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret())
|
||||||
);
|
);
|
||||||
|
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { Schema, Types, model } from 'mongoose';
|
||||||
|
|
||||||
|
const folderSchema = new Schema({
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
workspace: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Workspace',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
environment: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
parent: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Folder',
|
||||||
|
required: false, // optional for root folders
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
parentPath: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
timestamps: true
|
||||||
|
});
|
||||||
|
|
||||||
|
const Folder = model('Folder', folderSchema);
|
||||||
|
|
||||||
|
export default Folder;
|
||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
SECRET_SHARED,
|
SECRET_SHARED,
|
||||||
SECRET_PERSONAL,
|
SECRET_PERSONAL,
|
||||||
} from '../variables';
|
} from '../variables';
|
||||||
|
import { ROOT_FOLDER_PATH } from '../utils/folder';
|
||||||
|
|
||||||
export interface ISecret {
|
export interface ISecret {
|
||||||
_id: Types.ObjectId;
|
_id: Types.ObjectId;
|
||||||
@@ -25,6 +26,8 @@ export interface ISecret {
|
|||||||
secretCommentTag?: string;
|
secretCommentTag?: string;
|
||||||
secretCommentHash?: string;
|
secretCommentHash?: string;
|
||||||
tags?: string[];
|
tags?: string[];
|
||||||
|
path?: string;
|
||||||
|
folder?: Types.ObjectId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const secretSchema = new Schema<ISecret>(
|
const secretSchema = new Schema<ISecret>(
|
||||||
@@ -107,7 +110,18 @@ const secretSchema = new Schema<ISecret>(
|
|||||||
secretCommentHash: {
|
secretCommentHash: {
|
||||||
type: String,
|
type: String,
|
||||||
required: false
|
required: false
|
||||||
}
|
},
|
||||||
|
// the full path to the secret in relation to folders
|
||||||
|
path: {
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
default: ROOT_FOLDER_PATH
|
||||||
|
},
|
||||||
|
folder: {
|
||||||
|
type: Schema.Types.ObjectId,
|
||||||
|
ref: 'Folder',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
timestamps: true
|
timestamps: true
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ const router = express.Router();
|
|||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/status',
|
'/status',
|
||||||
(req: Request, res: Response) => {
|
async (req: Request, res: Response) => {
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
message: 'Ok',
|
message: 'Ok',
|
||||||
emailConfigured: getSmtpConfigured()
|
emailConfigured: await getSmtpConfigured()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import password from './password';
|
|||||||
import stripe from './stripe';
|
import stripe from './stripe';
|
||||||
import integration from './integration';
|
import integration from './integration';
|
||||||
import integrationAuth from './integrationAuth';
|
import integrationAuth from './integrationAuth';
|
||||||
|
import secretsFolder from './secretsFolder'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
signup,
|
signup,
|
||||||
@@ -33,5 +34,6 @@ export {
|
|||||||
password,
|
password,
|
||||||
stripe,
|
stripe,
|
||||||
integration,
|
integration,
|
||||||
integrationAuth
|
integrationAuth,
|
||||||
|
secretsFolder
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import express, { Request, Response } from 'express';
|
||||||
|
const router = express.Router();
|
||||||
|
import {
|
||||||
|
requireAuth,
|
||||||
|
requireWorkspaceAuth,
|
||||||
|
validateRequest
|
||||||
|
} from '../../middleware';
|
||||||
|
import { body, param } from 'express-validator';
|
||||||
|
import { createFolder, deleteFolder } from '../../controllers/v1/secretsFolderController';
|
||||||
|
import { ADMIN, MEMBER } from '../../variables';
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/',
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: ['jwt']
|
||||||
|
}),
|
||||||
|
requireWorkspaceAuth({
|
||||||
|
acceptedRoles: [ADMIN, MEMBER],
|
||||||
|
locationWorkspaceId: 'body'
|
||||||
|
}),
|
||||||
|
body('workspaceId').exists(),
|
||||||
|
body('environment').exists(),
|
||||||
|
body('folderName').exists(),
|
||||||
|
body('parentFolderId'),
|
||||||
|
validateRequest,
|
||||||
|
createFolder
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/:folderId',
|
||||||
|
requireAuth({
|
||||||
|
acceptedAuthModes: ['jwt']
|
||||||
|
}),
|
||||||
|
param('folderId').exists(),
|
||||||
|
validateRequest,
|
||||||
|
deleteFolder
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
export default router;
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import mongoose from 'mongoose';
|
|
||||||
import { getLogger } from '../utils/logger';
|
|
||||||
import {
|
import {
|
||||||
initDatabaseHelper,
|
initDatabaseHelper,
|
||||||
closeDatabaseHelper
|
closeDatabaseHelper
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ class Telemetry {
|
|||||||
/**
|
/**
|
||||||
* Logs telemetry enable/disable notice.
|
* Logs telemetry enable/disable notice.
|
||||||
*/
|
*/
|
||||||
static logTelemetryMessage = () => {
|
static logTelemetryMessage = async () => {
|
||||||
if(!getTelemetryEnabled()){
|
if(!(await getTelemetryEnabled())){
|
||||||
getLogger("backend-main").info([
|
(await getLogger("backend-main")).info([
|
||||||
"",
|
"",
|
||||||
"To improve, Infisical collects telemetry data about general usage.",
|
"To improve, Infisical collects telemetry data about general usage.",
|
||||||
"This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.",
|
"This helps us understand how the product is doing and guide our product development to create the best possible platform; it also helps us demonstrate growth as we support Infisical as open-source software.",
|
||||||
@@ -39,12 +39,12 @@ class Telemetry {
|
|||||||
* Return an instance of the PostHog client initialized.
|
* Return an instance of the PostHog client initialized.
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
static getPostHogClient = () => {
|
static getPostHogClient = async () => {
|
||||||
let postHogClient: any;
|
let postHogClient: any;
|
||||||
if (getNodeEnv() === 'production' && getTelemetryEnabled()) {
|
if ((await getNodeEnv()) === 'production' && (await getTelemetryEnabled())) {
|
||||||
// case: enable opt-out telemetry in production
|
// case: enable opt-out telemetry in production
|
||||||
postHogClient = new PostHog(getPostHogProjectApiKey(), {
|
postHogClient = new PostHog(await getPostHogProjectApiKey(), {
|
||||||
host: getPostHogHost()
|
host: await getPostHogHost()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import { createTerminus } from '@godaddy/terminus';
|
|||||||
import { getLogger } from '../utils/logger';
|
import { getLogger } from '../utils/logger';
|
||||||
|
|
||||||
export const setUpHealthEndpoint = <T>(server: T) => {
|
export const setUpHealthEndpoint = <T>(server: T) => {
|
||||||
const onSignal = () => {
|
const onSignal = async () => {
|
||||||
getLogger('backend-main').info('Server is starting clean-up');
|
(await getLogger('backend-main')).info('Server is starting clean-up');
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
if (mongoose.connection && mongoose.connection.readyState == 1) {
|
if (mongoose.connection && mongoose.connection.readyState == 1) {
|
||||||
|
|||||||
@@ -15,21 +15,21 @@ import {
|
|||||||
getSmtpPort
|
getSmtpPort
|
||||||
} from '../config';
|
} from '../config';
|
||||||
|
|
||||||
export const initSmtp = () => {
|
export const initSmtp = async () => {
|
||||||
const mailOpts: SMTPConnection.Options = {
|
const mailOpts: SMTPConnection.Options = {
|
||||||
host: getSmtpHost(),
|
host: await getSmtpHost(),
|
||||||
port: getSmtpPort()
|
port: await getSmtpPort()
|
||||||
};
|
};
|
||||||
|
|
||||||
if (getSmtpUsername() && getSmtpPassword()) {
|
if ((await getSmtpUsername()) && (await getSmtpPassword())) {
|
||||||
mailOpts.auth = {
|
mailOpts.auth = {
|
||||||
user: getSmtpUsername(),
|
user: await getSmtpUsername(),
|
||||||
pass: getSmtpPassword()
|
pass: await getSmtpPassword()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getSmtpSecure() ? getSmtpSecure() : false) {
|
if ((await getSmtpSecure()) ? (await getSmtpSecure()) : false) {
|
||||||
switch (getSmtpHost()) {
|
switch (await getSmtpHost()) {
|
||||||
case SMTP_HOST_SENDGRID:
|
case SMTP_HOST_SENDGRID:
|
||||||
mailOpts.requireTLS = true;
|
mailOpts.requireTLS = true;
|
||||||
break;
|
break;
|
||||||
@@ -52,7 +52,7 @@ export const initSmtp = () => {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (getSmtpHost().includes('amazonaws.com')) {
|
if ((await getSmtpHost()).includes('amazonaws.com')) {
|
||||||
mailOpts.tls = {
|
mailOpts.tls = {
|
||||||
ciphers: 'TLSv1.2'
|
ciphers: 'TLSv1.2'
|
||||||
}
|
}
|
||||||
@@ -70,10 +70,10 @@ export const initSmtp = () => {
|
|||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureMessage('SMTP - Successfully connected');
|
Sentry.captureMessage('SMTP - Successfully connected');
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch(async (err) => {
|
||||||
Sentry.setUser(null);
|
Sentry.setUser(null);
|
||||||
Sentry.captureException(
|
Sentry.captureException(
|
||||||
`SMTP - Failed to connect to ${getSmtpHost()}:${getSmtpPort()} \n\t${err}`
|
`SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()} \n\t${err}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const testWorkspaceKeyId = "63cf48f0225e6955acec5eff"
|
|||||||
export const plainTextWorkspaceKey = "543fef8224813a46230b0a50a46c5fb2"
|
export const plainTextWorkspaceKey = "543fef8224813a46230b0a50a46c5fb2"
|
||||||
|
|
||||||
export const createTestUserForDevelopment = async () => {
|
export const createTestUserForDevelopment = async () => {
|
||||||
if (getNodeEnv() === "development" || getNodeEnv() === "test") {
|
if ((await getNodeEnv()) === "development" || (await getNodeEnv()) === "test") {
|
||||||
const testUser = {
|
const testUser = {
|
||||||
_id: testUserId,
|
_id: testUserId,
|
||||||
email: testUserEmail,
|
email: testUserEmail,
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import Folder from "../models/folder";
|
||||||
|
|
||||||
|
export const ROOT_FOLDER_PATH = "/"
|
||||||
|
|
||||||
|
export const getFolderPath = async (folderId: string) => {
|
||||||
|
let currentFolder = await Folder.findById(folderId);
|
||||||
|
const pathSegments = [];
|
||||||
|
|
||||||
|
while (currentFolder) {
|
||||||
|
pathSegments.unshift(currentFolder.name);
|
||||||
|
currentFolder = currentFolder.parent ? await Folder.findById(currentFolder.parent) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return '/' + pathSegments.join('/');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the folder ID associated with the specified secret path in the given workspace and environment.
|
||||||
|
@param workspaceId - The ID of the workspace to search in.
|
||||||
|
@param environment - The environment to search in.
|
||||||
|
@param secretPath - The secret path to search for.
|
||||||
|
@returns The folder ID associated with the specified secret path, or undefined if the path is at the root folder level.
|
||||||
|
@throws Error if the specified secret path is not found.
|
||||||
|
*/
|
||||||
|
export const getFolderIdFromPath = async (workspaceId: string, environment: string, secretPath: string) => {
|
||||||
|
const secretPathParts = secretPath.split("/").filter(path => path != "")
|
||||||
|
if (secretPathParts.length <= 1) {
|
||||||
|
return undefined // root folder, so no folder id
|
||||||
|
}
|
||||||
|
|
||||||
|
const folderId = await Folder.find({ path: secretPath, workspace: workspaceId, environment: environment })
|
||||||
|
if (!folderId) {
|
||||||
|
throw Error("Secret path not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up a path by removing empty parts, duplicate slashes,
|
||||||
|
* and ensuring it starts with ROOT_FOLDER_PATH.
|
||||||
|
* @param path - The input path to clean up.
|
||||||
|
* @returns The cleaned-up path string.
|
||||||
|
*/
|
||||||
|
export const normalizePath = (path: string) => {
|
||||||
|
if (path == undefined || path == "" || path == ROOT_FOLDER_PATH) {
|
||||||
|
return ROOT_FOLDER_PATH
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathParts = path.split("/").filter(part => part != "")
|
||||||
|
const cleanPathString = ROOT_FOLDER_PATH + pathParts.join("/")
|
||||||
|
|
||||||
|
return cleanPathString
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getFoldersInDirectory = async (workspaceId: string, environment: string, pathString: string) => {
|
||||||
|
const normalizedPath = normalizePath(pathString)
|
||||||
|
const foldersInDirectory = await Folder.find({
|
||||||
|
workspace: workspaceId,
|
||||||
|
environment: environment,
|
||||||
|
parentPath: normalizedPath,
|
||||||
|
});
|
||||||
|
|
||||||
|
return foldersInDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the parent path of the given path.
|
||||||
|
* @param path - The input path.
|
||||||
|
* @returns The parent path string.
|
||||||
|
*/
|
||||||
|
export const getParentPath = (path: string) => {
|
||||||
|
const normalizedPath = normalizePath(path);
|
||||||
|
const folderParts = normalizedPath.split('/').filter(part => part !== '');
|
||||||
|
|
||||||
|
let folderParent = ROOT_FOLDER_PATH;
|
||||||
|
if (folderParts.length > 1) {
|
||||||
|
folderParent = ROOT_FOLDER_PATH + folderParts.slice(0, folderParts.length - 1).join('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return folderParent;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const validateFolderName = (folderName: string) => {
|
||||||
|
const validNameRegex = /^[a-zA-Z0-9-_]+$/;
|
||||||
|
return validNameRegex.test(folderName);
|
||||||
|
}
|
||||||
+10
-12
@@ -12,7 +12,7 @@ const logFormat = (prefix: string) => combine(
|
|||||||
printf((info) => `${info.timestamp} ${info.label} ${info.level}: ${info.message}`)
|
printf((info) => `${info.timestamp} ${info.label} ${info.level}: ${info.message}`)
|
||||||
);
|
);
|
||||||
|
|
||||||
const createLoggerWithLabel = (level: string, label: string) => {
|
const createLoggerWithLabel = async (level: string, label: string) => {
|
||||||
const _level = level.toLowerCase() || 'info'
|
const _level = level.toLowerCase() || 'info'
|
||||||
//* Always add Console output to transports
|
//* Always add Console output to transports
|
||||||
const _transports: any[] = [
|
const _transports: any[] = [
|
||||||
@@ -25,10 +25,10 @@ const createLoggerWithLabel = (level: string, label: string) => {
|
|||||||
})
|
})
|
||||||
]
|
]
|
||||||
//* Add LokiTransport if it's enabled
|
//* Add LokiTransport if it's enabled
|
||||||
if(getLokiHost() !== undefined){
|
if((await getLokiHost()) !== undefined){
|
||||||
_transports.push(
|
_transports.push(
|
||||||
new LokiTransport({
|
new LokiTransport({
|
||||||
host: getLokiHost(),
|
host: await getLokiHost(),
|
||||||
handleExceptions: true,
|
handleExceptions: true,
|
||||||
handleRejections: true,
|
handleRejections: true,
|
||||||
batching: true,
|
batching: true,
|
||||||
@@ -40,7 +40,7 @@ const createLoggerWithLabel = (level: string, label: string) => {
|
|||||||
labels: {
|
labels: {
|
||||||
app: process.env.npm_package_name,
|
app: process.env.npm_package_name,
|
||||||
version: process.env.npm_package_version,
|
version: process.env.npm_package_version,
|
||||||
environment: getNodeEnv()
|
environment: await getNodeEnv()
|
||||||
},
|
},
|
||||||
onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err)
|
onConnectionError: (err: Error)=> console.error('Connection error while connecting to Loki Server.\n', err)
|
||||||
})
|
})
|
||||||
@@ -58,12 +58,10 @@ const createLoggerWithLabel = (level: string, label: string) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_LOGGERS = {
|
export const getLogger = async (loggerName: 'backend-main' | 'database') => {
|
||||||
"backend-main": createLoggerWithLabel('info', '[IFSC:backend-main]'),
|
const logger = {
|
||||||
"database": createLoggerWithLabel('info', '[IFSC:database]'),
|
"backend-main": await createLoggerWithLabel('info', '[IFSC:backend-main]'),
|
||||||
}
|
"database": await createLoggerWithLabel('info', '[IFSC:database]'),
|
||||||
type LoggerNames = keyof typeof DEFAULT_LOGGERS
|
}
|
||||||
|
return logger[loggerName]
|
||||||
export const getLogger = (loggerName: LoggerNames) => {
|
|
||||||
return DEFAULT_LOGGERS[loggerName]
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,13 +81,13 @@ export default class RequestError extends Error{
|
|||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|
||||||
public format(req: Request){
|
public async format(req: Request){
|
||||||
let _context = Object.assign({
|
let _context = Object.assign({
|
||||||
stacktrace: this.stacktrace
|
stacktrace: this.stacktrace
|
||||||
}, this.context)
|
}, this.context)
|
||||||
|
|
||||||
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer
|
//* Omit sensitive information from context that can leak internal workings of this program if user is not developer
|
||||||
if(!getVerboseErrorOutput()){
|
if(!(await getVerboseErrorOutput())){
|
||||||
_context = this._omit(_context, [
|
_context = this._omit(_context, [
|
||||||
'stacktrace',
|
'stacktrace',
|
||||||
'exception',
|
'exception',
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const INTEGRATION_CIRCLECI_API_URL = "https://circleci.com/api";
|
|||||||
const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
|
const INTEGRATION_TRAVISCI_API_URL = "https://api.travis-ci.com";
|
||||||
const INTEGRATION_SUPABASE_API_URL = 'https://api.supabase.com';
|
const INTEGRATION_SUPABASE_API_URL = 'https://api.supabase.com';
|
||||||
|
|
||||||
const getIntegrationOptions = () => {
|
const getIntegrationOptions = async () => {
|
||||||
const INTEGRATION_OPTIONS = [
|
const INTEGRATION_OPTIONS = [
|
||||||
{
|
{
|
||||||
name: 'Heroku',
|
name: 'Heroku',
|
||||||
@@ -69,7 +69,7 @@ const getIntegrationOptions = () => {
|
|||||||
image: 'Heroku.png',
|
image: 'Heroku.png',
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
clientId: getClientIdHeroku(),
|
clientId: await getClientIdHeroku(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -79,7 +79,7 @@ const getIntegrationOptions = () => {
|
|||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
clientId: '',
|
clientId: '',
|
||||||
clientSlug: getClientSlugVercel(),
|
clientSlug: await getClientSlugVercel(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -88,7 +88,7 @@ const getIntegrationOptions = () => {
|
|||||||
image: 'Netlify.png',
|
image: 'Netlify.png',
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
clientId: getClientIdNetlify(),
|
clientId: await getClientIdNetlify(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -97,7 +97,7 @@ const getIntegrationOptions = () => {
|
|||||||
image: 'GitHub.png',
|
image: 'GitHub.png',
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
clientId: getClientIdGitHub(),
|
clientId: await getClientIdGitHub(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -151,7 +151,7 @@ const getIntegrationOptions = () => {
|
|||||||
image: 'Microsoft Azure.png',
|
image: 'Microsoft Azure.png',
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
clientId: getClientIdAzure(),
|
clientId: await getClientIdAzure(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -169,7 +169,7 @@ const getIntegrationOptions = () => {
|
|||||||
image: 'GitLab.png',
|
image: 'GitLab.png',
|
||||||
isAvailable: true,
|
isAvailable: true,
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
clientId: getClientIdGitLab(),
|
clientId: await getClientIdGitLab(),
|
||||||
docsLink: ''
|
docsLink: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ These examples demonstrate how to store and fetch environment variables from [In
|
|||||||
### Initialize the Infisical client
|
### Initialize the Infisical client
|
||||||
|
|
||||||
```js
|
```js
|
||||||
await infisical.connect({
|
import InfisicalClient from "infisical-node";
|
||||||
|
|
||||||
|
const client = new InfisicalClient({
|
||||||
token: "your_infisical_token",
|
token: "your_infisical_token",
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@@ -72,31 +74,31 @@ These examples demonstrate how to store and fetch environment variables from [In
|
|||||||
### Get a value
|
### Get a value
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const value = infisical.get("SOME_KEY");
|
const value = await client.getSecret("SOME_KEY");
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example with Express
|
### Example with Express
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const express = require("express");
|
import InfisicalClient from "infisical-node";
|
||||||
const port = 3000;
|
import express from "express";
|
||||||
const infisical = require("infisical-node");
|
const app = express();
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
const main = async () => {
|
const client = InfisicalClient({
|
||||||
await infisical.connect({
|
token: "st.xxx.xxx",
|
||||||
token: "st.xxx.xxx",
|
});
|
||||||
});
|
|
||||||
|
|
||||||
// your application logic
|
// your application logic
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
app.get("/", async (req, res) => {
|
||||||
res.send(`Howdy, ${infisical.get("NAME")}!`);
|
const name = await client.getSecret("NAME");
|
||||||
});
|
res.send(`Hello! My name is: ${name.secretValue}`);
|
||||||
|
});
|
||||||
|
|
||||||
app.listen(port, async () => {
|
app.listen(PORT, async () => {
|
||||||
console.log(`App listening on port ${port}`);
|
console.log(`App listening on port ${port}`);
|
||||||
});
|
});
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<Warning>
|
<Warning>
|
||||||
|
|||||||
+156
-117
@@ -2,7 +2,39 @@
|
|||||||
title: "Node"
|
title: "Node"
|
||||||
---
|
---
|
||||||
|
|
||||||
If you're working with Node.js, the official [infisical-node](https://github.com/Infisical/infisical-node) package is the easiest way to fetch secrets for your application.
|
If you're working with Node.js, the official [infisical-node](https://github.com/Infisical/infisical-node) package is the easiest way to fetch and work with secrets for your application.
|
||||||
|
|
||||||
|
## Basic Usage
|
||||||
|
|
||||||
|
```js
|
||||||
|
import InfisicalClient from "infisical-node";
|
||||||
|
import express from "express";
|
||||||
|
const app = express();
|
||||||
|
const PORT = 3000;
|
||||||
|
|
||||||
|
const client = new InfisicalClient({
|
||||||
|
token: "YOUR_INFISICAL_TOKEN"
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get("/", async (req, res) => {
|
||||||
|
// access value
|
||||||
|
const name = await client.getSecret("NAME");
|
||||||
|
res.send(`Hello! My name is: ${name.secretValue}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(PORT, async () => {
|
||||||
|
// initialize client
|
||||||
|
console.log(`App listening on port ${port}`);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
This example demonstrates how to use the Infisical SDK with an Express application. The application retrieves a secret named "NAME" and responds to requests with a greeting that includes the secret value.
|
||||||
|
|
||||||
|
<Warning>
|
||||||
|
We do not recommend hardcoding your [Infisical
|
||||||
|
Token](/getting-started/dashboard/token). Setting it as an environment
|
||||||
|
variable would be best.
|
||||||
|
</Warning>
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -12,143 +44,150 @@ Run `npm` to add `infisical-node` to your project.
|
|||||||
npm install infisical-node --save
|
npm install infisical-node --save
|
||||||
```
|
```
|
||||||
|
|
||||||
## Initialization
|
## Configuration
|
||||||
|
|
||||||
Set up the Infisical client asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect(options)`.
|
Import the SDK and create a client instance with your Infisical token.
|
||||||
|
|
||||||
This methods fetches back all the secrets in the project and environment accessible by the token passed in `options`.
|
|
||||||
|
|
||||||
### infisical.connect(options)
|
|
||||||
|
|
||||||
Updates the global instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token).
|
|
||||||
|
|
||||||
<ResponseField name="options" type="object">
|
|
||||||
<Expandable title="properties">
|
|
||||||
<ResponseField name="token" type="string">
|
|
||||||
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
|
||||||
and environment
|
|
||||||
</ResponseField>
|
|
||||||
<ResponseField
|
|
||||||
name="siteURL"
|
|
||||||
type="string"
|
|
||||||
default="https://app.infisical.com"
|
|
||||||
>
|
|
||||||
Your self-hosted absolute site URL including the protocol (e.g.
|
|
||||||
`https://app.infisical.com`)
|
|
||||||
</ResponseField>
|
|
||||||
<ResponseField name="debug" type="boolean" default="false">
|
|
||||||
Whether or not debug mode is on
|
|
||||||
</ResponseField>
|
|
||||||
<ResponseField name="attachToProcessEnv" type="boolean" default="false">
|
|
||||||
Whether or not to attach fetched secrets to `process.env`
|
|
||||||
</ResponseField>
|
|
||||||
</Expandable>
|
|
||||||
</ResponseField>
|
|
||||||
|
|
||||||
### infisical.createConnection(options)
|
|
||||||
|
|
||||||
Returns a local instance of the Infisical client with a connection to an Infisical project and fetches back secrets if supplied with an [Infisical Token](/getting-started/dashboard/token).
|
|
||||||
|
|
||||||
This method is useful if you wish to connect to two or more Infisical projects within your app.
|
|
||||||
|
|
||||||
<ResponseField name="options" type="object">
|
|
||||||
<Expandable title="properties">
|
|
||||||
<ResponseField name="token" type="string">
|
|
||||||
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
|
||||||
and environment
|
|
||||||
</ResponseField>
|
|
||||||
<ResponseField
|
|
||||||
name="siteURL"
|
|
||||||
type="string"
|
|
||||||
default="https://app.infisical.com"
|
|
||||||
>
|
|
||||||
Your self-hosted absolute site URL including the protocol (e.g.
|
|
||||||
`https://app.infisical.com`)
|
|
||||||
</ResponseField>
|
|
||||||
<ResponseField name="debug" type="boolean" default="false">
|
|
||||||
Whether or not debug mode is on
|
|
||||||
</ResponseField>
|
|
||||||
</Expandable>
|
|
||||||
</ResponseField>
|
|
||||||
|
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab title="ES6">
|
<Tab title="ES6">
|
||||||
```js
|
```js
|
||||||
import infisical from "infisical-node";
|
import InfisicalClient from "infisical-node";
|
||||||
|
|
||||||
|
const client = new InfisicalClient({
|
||||||
|
token: "your_infisical_token"
|
||||||
|
});
|
||||||
|
|
||||||
const main = async () => {
|
// your app logic
|
||||||
await infisical.connect({
|
|
||||||
token: "your_infisical_token",
|
|
||||||
});
|
|
||||||
|
|
||||||
// your app logic
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
||||||
```
|
```
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="ES5">
|
<Tab title="ES5">
|
||||||
```js
|
```js
|
||||||
const infisical = require("infisical-node");
|
const InfisicalClient = require("infisical-node");
|
||||||
|
|
||||||
infisical.connect({
|
const client = new InfisicalClient({
|
||||||
token: "your_infisical_token"
|
token: "your_infisical_token"
|
||||||
})
|
});
|
||||||
.then(() => {
|
|
||||||
// your application logic
|
// your app logic
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
console.error('Error: ', err);
|
|
||||||
})
|
|
||||||
````
|
````
|
||||||
</Tab>
|
</Tab>
|
||||||
|
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
## Usage
|
<ResponseField name="options" type="object">
|
||||||
|
<Expandable title="properties">
|
||||||
To get the value of a secret, use `infisical.get(key)`.
|
<ResponseField name="token" type="string">
|
||||||
|
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
|
||||||
### infisical.get(key)
|
and environment
|
||||||
|
</ResponseField>
|
||||||
Return the value of the secret with the specified `key`. Note that the Infisical client falls back to `process.env` if `token` is `undefined` during the
|
<ResponseField
|
||||||
initialization step or if a value for the secret is not found in the fetched secrets.
|
name="siteURL"
|
||||||
|
type="string"
|
||||||
<ResponseField name="key" type="string" required>
|
default="https://app.infisical.com"
|
||||||
The key of the secret
|
>
|
||||||
|
Your self-hosted absolute site URL including the protocol (e.g.
|
||||||
|
`https://app.infisical.com`)
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="cacheTTL" type="number" default="300">
|
||||||
|
Time-to-live (in seconds) for refreshing cached secrets. Default: `300`.
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="debug" type="boolean" default="false">
|
||||||
|
Whether or not debug mode is on
|
||||||
|
</ResponseField>
|
||||||
|
</Expandable>
|
||||||
</ResponseField>
|
</ResponseField>
|
||||||
|
|
||||||
```js
|
## Caching
|
||||||
const value = infisical.get("SOME_KEY");
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example with Express
|
The SDK caches every secret and updates it periodically based on the provided `cacheTTL`. For example, if `cacheTTL` of `300` is provided, then a secret will be refetched 5 minutes after the first fetch; if the fetch fails, the cached secret is returned.
|
||||||
|
|
||||||
|
## Working with Secrets
|
||||||
|
|
||||||
|
### infisical.getSecret(secretName, options)
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const express = require("express");
|
const secret = await infisical.getSecret("API_KEY");
|
||||||
const port = 3000;
|
const value = secret.secretValue; // get its value
|
||||||
const infisical = require("infisical-node");
|
|
||||||
|
|
||||||
const main = async () => {
|
|
||||||
await infisical.connect({
|
|
||||||
token: "st.xxx.xxx",
|
|
||||||
});
|
|
||||||
|
|
||||||
// your application logic
|
|
||||||
|
|
||||||
app.get("/", (req, res) => {
|
|
||||||
res.send(`Howdy, ${infisical.get("NAME")}!`);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(port, async () => {
|
|
||||||
console.log(`App listening on port ${port}`);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
```
|
```
|
||||||
|
|
||||||
<Warning>
|
Retrieve a secret from Infisical.
|
||||||
We do not recommend hardcoding your [Infisical
|
|
||||||
Token](/getting-started/dashboard/token). Setting it as an environment
|
By default, `getSecret()` fetches and returns a personal secret. If not found, it returns a shared secret, or tries to retrieve the value from `process.env`. If a secret is fetched, `getSecret()` caches it to reduce excessive calls and re-fetches periodically based on the `cacheTTL` option (default is `300` seconds) when initializing the client — for more information, see the caching section.
|
||||||
variable would be best.
|
|
||||||
</Warning>
|
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The key of the secret to retrieve
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="options" type="object">
|
||||||
|
<Expandable title="properties">
|
||||||
|
<ResponseField name="type" type="string">
|
||||||
|
"personal" (default) or "shared".
|
||||||
|
</ResponseField>
|
||||||
|
</Expandable>
|
||||||
|
</ResponseField>
|
||||||
|
|
||||||
|
### infisical.createSecret(secretName, secretValue, options)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const newApiKey = await infisical.createSecret("API_KEY", "FOO");
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new secret in Infisical.
|
||||||
|
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The key of the secret to create
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The value of the secret to create
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="options" type="object">
|
||||||
|
<Expandable title="properties">
|
||||||
|
<ResponseField name="type" type="string">
|
||||||
|
"shared" (default) or "personal". A personal secret can only be created if a shared secret with the same name exists.
|
||||||
|
</ResponseField>
|
||||||
|
</Expandable>
|
||||||
|
</ResponseField>
|
||||||
|
|
||||||
|
### infisical.updateSecret(secretName, secretValue, options)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const updatedApiKey = await infisical.updateSecret("API_KEY", "BAR");
|
||||||
|
```
|
||||||
|
|
||||||
|
Update an existing secret in Infisical.
|
||||||
|
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The key of the secret to update
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The new value of the secret
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="options" type="object">
|
||||||
|
<Expandable title="properties">
|
||||||
|
<ResponseField name="type" type="string">
|
||||||
|
"shared" (default) or "personal".
|
||||||
|
</ResponseField>
|
||||||
|
</Expandable>
|
||||||
|
</ResponseField>
|
||||||
|
|
||||||
|
### infisical.deleteSecret(secretName, options)
|
||||||
|
|
||||||
|
```js
|
||||||
|
const deletedSecret = await infisical.deleteSecret("API_KEY");
|
||||||
|
```
|
||||||
|
|
||||||
|
Delete a secret in Infisical.
|
||||||
|
|
||||||
|
<ResponseField name="secretName" type="string" required>
|
||||||
|
The key of the secret to delete
|
||||||
|
</ResponseField>
|
||||||
|
<ResponseField name="options" type="object">
|
||||||
|
<Expandable title="properties">
|
||||||
|
<ResponseField name="type" type="string">
|
||||||
|
"shared" (default) or "personal". Note that deleting a shared secret also deletes all associated personal secrets.
|
||||||
|
</ResponseField>
|
||||||
|
</Expandable>
|
||||||
|
</ResponseField>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -803,8 +803,6 @@ export default function Dashboard() {
|
|||||||
isReadDenied: false
|
isReadDenied: false
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(124, envSlug, selectedWorkspaceEnv)
|
|
||||||
|
|
||||||
if (selectedWorkspaceEnv) {
|
if (selectedWorkspaceEnv) {
|
||||||
if (snapshotData) setSelectedSnapshotEnv(selectedWorkspaceEnv);
|
if (snapshotData) setSelectedSnapshotEnv(selectedWorkspaceEnv);
|
||||||
else setSelectedEnv(selectedWorkspaceEnv);
|
else setSelectedEnv(selectedWorkspaceEnv);
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
|
|||||||
|
|
||||||
if (isSecretsLoading || isEnvListLoading) {
|
if (isSecretsLoading || isEnvListLoading) {
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto flex h-full w-full items-center justify-center px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
<div className="container mx-auto flex h-screen w-full items-center justify-center px-8 text-mineshaft-50 dark:[color-scheme:dark]">
|
||||||
<img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" />
|
<img src="/images/loading/loading.gif" height={70} width={120} alt="loading animation" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -234,14 +234,14 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</div> */}
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
<div className="group min-w-full flex flex-row items-center mt-4">
|
<div className="group flex flex-row items-center mt-4 min-w-[60.3rem]">
|
||||||
<div className="w-10 h-10 px-4 flex items-center justify-center border-none"><div className='text-center w-10 text-xs text-transparent'>0</div></div>
|
<div className="w-10 h-10 px-4 flex items-center justify-center border-none"><div className='text-center w-10 text-xs text-transparent'>0</div></div>
|
||||||
<div className="flex flex-row justify-between items-center min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
|
<div className="flex flex-row justify-between items-center min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
|
||||||
<span className="text-transparent">0</span>
|
<span className="text-transparent">0</span>
|
||||||
<button type="button" className='mr-2 text-transparent'>1</button>
|
<button type="button" className='mr-2 text-transparent'>1</button>
|
||||||
</div>
|
</div>
|
||||||
{userAvailableEnvs?.map(env => {
|
{userAvailableEnvs?.map(env => {
|
||||||
return <div key={`button-${env.slug}`} className="flex flex-row w-full justify-center h-10 items-center border-none mb-1 mx-2 min-w-[10rem]">
|
return <div key={`button-${env.slug}`} className="flex flex-row w-full justify-center h-10 items-center border-none mb-1 mx-2 min-w-[11rem]">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => onEnvChange(env.slug)}
|
onClick={() => onEnvChange(env.slug)}
|
||||||
// router.push(`${router.asPath }?env=${env.slug}`)
|
// router.push(`${router.asPath }?env=${env.slug}`)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ const DashboardInput = ({ isOverridden, isSecretValueHidden, isReadOnly, secret,
|
|||||||
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
ref.current.scrollLeft = e.currentTarget.scrollLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
return <td key={`row-${secret?.key || ''}--`} className={`flex cursor-default flex-row w-full min-w-[11rem] justify-center h-10 items-center ${!(secret?.value || secret?.value === '') ? "bg-red-400/10" : "bg-mineshaft-900/30"}`}>
|
return <td key={`row-${secret?.key || ''}--`} className={`flex cursor-default flex-row w-full justify-center h-10 items-center ${!(secret?.value || secret?.value === '') ? "bg-red-400/10" : "bg-mineshaft-900/30"}`}>
|
||||||
<div className="group relative whitespace-pre flex flex-col justify-center w-full cursor-default">
|
<div className="group relative whitespace-pre flex flex-col justify-center w-full cursor-default">
|
||||||
<input
|
<input
|
||||||
// {...register(`secrets.${index}.valueOverride`)}
|
// {...register(`secrets.${index}.valueOverride`)}
|
||||||
|
|||||||
Reference in New Issue
Block a user