From 3817831577684dd9a76ee4b3c423c9666ce7e9dd Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sat, 22 Apr 2023 14:34:05 +0300 Subject: [PATCH 01/13] Update docs for upcoming Node SDK update --- docs/sdks/languages/node.mdx | 159 +++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 46 deletions(-) diff --git a/docs/sdks/languages/node.mdx b/docs/sdks/languages/node.mdx index 58002392f..fe5e969f8 100644 --- a/docs/sdks/languages/node.mdx +++ b/docs/sdks/languages/node.mdx @@ -14,13 +14,13 @@ npm install infisical-node --save ## Initialization -Set up the Infisical client asynchronously as early as possible in your application by importing and initializing the global instance with `infisical.connect(options)`. +Call `connect()` with your Infisical token as early as possible in the main entry module of your application. This initializes the global instance of the SDK, which can be accessed anywhere in your application. -This methods fetches back all the secrets in the project and environment accessible by the token passed in `options`. +For multiple Infisical projects or creating multiple SDK instances, use `createConnection()` instead. This returns a local SDK instance, independent of the global instance. ### 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). +Updates the global instance of the Infisical client with a connection to an Infisical project with the [Infisical Token](/getting-started/dashboard/token). @@ -36,18 +36,18 @@ Updates the global instance of the Infisical client with a connection to an Infi Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`) + + Time-to-live (in seconds) for cached secrets. If set to 0, data is cached indefinitely. + Whether or not debug mode is on - - Whether or not to attach fetched secrets to `process.env` - ### 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). +Returns a local instance of the Infisical client with a connection to an Infisical project 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. @@ -65,6 +65,9 @@ This method is useful if you wish to connect to two or more Infisical projects w Your self-hosted absolute site URL including the protocol (e.g. `https://app.infisical.com`) + + Time-to-live (in seconds) for cached secrets. If set to 0, data is cached indefinitely. + Whether or not debug mode is on @@ -76,15 +79,11 @@ This method is useful if you wish to connect to two or more Infisical projects w ```js import infisical from "infisical-node"; - const main = async () => { - await infisical.connect({ - token: "your_infisical_token", - }); + infisical.connect({ + token: "your_infisical_token", + }); - // your app logic - } - - main(); + // your app logic ``` @@ -93,14 +92,10 @@ This method is useful if you wish to connect to two or more Infisical projects w const infisical = require("infisical-node"); infisical.connect({ - token: "your_infisical_token" - }) - .then(() => { - // your application logic - }) - .catch(err => { - console.error('Error: ', err); - }) + token: "your_infisical_token" + }); + + // your app logic ```` @@ -108,45 +103,117 @@ This method is useful if you wish to connect to two or more Infisical projects w ## Usage -To get the value of a secret, use `infisical.get(key)`. +### infisical.getSecret(secretName, options) -### infisical.get(key) +Retrieve a secret from Infisical. -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 -initialization step or if a value for the secret is not found in the fetched secrets. +By default, `getSecret()` returns a personal secret. If not found, it returns a shared secret, or tries to retrieve the value from `process.env`. - - The key of the secret + + The key of the secret to retrieve + + + + + "personal" (default) or "shared". + + ```js -const value = infisical.get("SOME_KEY"); +const secret = await infisical.getSecret("API_KEY"); +const value = secret.secretValue; // get its value +``` + +### infisical.createSecret(secretName, secretValue, options) + +Create a new secret in Infisical. + + + The key of the secret to create + + + The value of the secret to create + + + + + "shared" (default) or "personal". A personal secret can only be created if a shared secret with the same name exists. + + + + +```js +const newApiKey = await infisical.createSecret("API_KEY", "FOO"); +``` + +### infisical.updateSecret(secretName, secretValue, options) + +Update an existing secret in Infisical. + + + The key of the secret to update + + + The new value of the secret + + + + + "shared" (default) or "personal". + + + + +```js +const updatedApiKey = await infisical.updateSecret("API_KEY", "BAR"); +``` + +### infisical.deleteSecret(secretName, options) + +Delete a secret in Infisical. + + + The key of the secret to delete + + + + + "shared" (default) or "personal". Note that deleting a shared secret also deletes all associated personal secrets. + + + + +```js +const deletedSecret = await infisical.deleteSecret("API_KEY"); ``` ## Example with Express ```js -const express = require("express"); -const port = 3000; -const infisical = require("infisical-node"); +import infisical from "infisical-node"; +import express from "express"; +const app = express(); +const PORT = 3000; -const main = async () => { - await infisical.connect({ - token: "st.xxx.xxx", - }); +infisical.connect({ + token: "YOUR_INFISICAL_TOKEN" +}); - // your application logic +app.get("/", async (req, res) => { + // access value + const name = await infisical.getSecret("NAME"); + res.send(`Hello! My name is: ${name.secretValue}`); +}); - app.get("/", (req, res) => { - res.send(`Howdy, ${infisical.get("NAME")}!`); - }); - - app.listen(port, async () => { - console.log(`App listening on port ${port}`); - }); -}; +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. + We do not recommend hardcoding your [Infisical Token](/getting-started/dashboard/token). Setting it as an environment From e1bf31b3711866bd9e37257e2daf3e99e7ef76dd Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sat, 22 Apr 2023 16:20:33 +0300 Subject: [PATCH 02/13] Update envars to new node SDK format --- backend/src/config/index.ts | 109 +++++++++--------- backend/src/controllers/v1/authController.ts | 10 +- .../v1/integrationAuthController.ts | 2 +- .../controllers/v1/membershipController.ts | 2 +- .../controllers/v1/membershipOrgController.ts | 8 +- .../controllers/v1/organizationController.ts | 10 +- .../src/controllers/v1/passwordController.ts | 6 +- .../src/controllers/v1/secretController.ts | 6 +- .../controllers/v1/serviceTokenController.ts | 2 +- .../src/controllers/v1/signupController.ts | 8 +- .../src/controllers/v1/stripeController.ts | 4 +- .../controllers/v2/apiKeyDataController.ts | 2 +- backend/src/controllers/v2/authController.ts | 8 +- .../src/controllers/v2/secretController.ts | 14 +-- .../src/controllers/v2/secretsController.ts | 10 +- .../v2/serviceAccountsController.ts | 2 +- .../v2/serviceTokenDataController.ts | 2 +- .../src/controllers/v2/signupController.ts | 8 +- .../src/controllers/v2/workspaceController.ts | 4 +- .../src/ee/controllers/v1/stripeController.ts | 4 +- backend/src/helpers/auth.ts | 10 +- backend/src/helpers/bot.ts | 4 +- backend/src/helpers/database.ts | 4 +- backend/src/helpers/nodemailer.ts | 4 +- backend/src/helpers/organization.ts | 14 +-- backend/src/helpers/secrets.ts | 18 +-- backend/src/helpers/token.ts | 2 +- backend/src/helpers/workspace.ts | 1 - backend/src/index.ts | 28 +++-- backend/src/integrations/exchange.ts | 32 ++--- backend/src/integrations/refresh.ts | 12 +- backend/src/middleware/requestErrorHandler.ts | 8 +- backend/src/middleware/requireMfaAuth.ts | 2 +- .../src/middleware/requireServiceTokenAuth.ts | 2 +- backend/src/middleware/requireSignupAuth.ts | 2 +- backend/src/routes/status/status.ts | 4 +- backend/src/services/DatabaseService.ts | 2 - backend/src/services/TelemetryService.ts | 14 +-- backend/src/services/health.ts | 4 +- backend/src/services/smtp.ts | 22 ++-- backend/src/utils/addDevelopmentUser.ts | 2 +- backend/src/utils/logger.ts | 22 ++-- backend/src/utils/requestError.ts | 4 +- backend/src/variables/integration.ts | 14 +-- 44 files changed, 223 insertions(+), 229 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 29f62cdc6..175328caa 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,64 +1,65 @@ import infisical 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'); -export const getEncryptionKey = () => infisical.get('ENCRYPTION_KEY')!; -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 getJwtMfaLifetime = () => infisical.get('JWT_MFA_LIFETIME')! || '5m'; -export const getJwtMfaSecret = () => infisical.get('JWT_MFA_LIFETIME')! || '5m'; -export const getJwtRefreshLifetime = () => infisical.get('JWT_REFRESH_LIFETIME')! || '90d'; -export const getJwtRefreshSecret = () => infisical.get('JWT_REFRESH_SECRET')!; -export const getJwtServiceSecret = () => infisical.get('JWT_SERVICE_SECRET')!; -export const getJwtSignupLifetime = () => infisical.get('JWT_SIGNUP_LIFETIME')! || '15m'; -export const getJwtSignupSecret = () => infisical.get('JWT_SIGNUP_SECRET')!; -export const getMongoURL = () => infisical.get('MONGO_URL')!; -export const getNodeEnv = () => infisical.get('NODE_ENV')! || 'production'; -export const getVerboseErrorOutput = () => infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true; -export const getLokiHost = () => infisical.get('LOKI_HOST')!; -export const getClientIdAzure = () => infisical.get('CLIENT_ID_AZURE')!; -export const getClientIdHeroku = () => infisical.get('CLIENT_ID_HEROKU')!; -export const getClientIdVercel = () => infisical.get('CLIENT_ID_VERCEL')!; -export const getClientIdNetlify = () => infisical.get('CLIENT_ID_NETLIFY')!; -export const getClientIdGitHub = () => infisical.get('CLIENT_ID_GITHUB')!; -export const getClientIdGitLab = () => infisical.get('CLIENT_ID_GITLAB')!; -export const getClientSecretAzure = () => infisical.get('CLIENT_SECRET_AZURE')!; -export const getClientSecretHeroku = () => infisical.get('CLIENT_SECRET_HEROKU')!; -export const getClientSecretVercel = () => infisical.get('CLIENT_SECRET_VERCEL')!; -export const getClientSecretNetlify = () => infisical.get('CLIENT_SECRET_NETLIFY')!; -export const getClientSecretGitHub = () => infisical.get('CLIENT_SECRET_GITHUB')!; -export const getClientSecretGitLab = () => infisical.get('CLIENT_SECRET_GITLAB')!; -export const getClientSlugVercel = () => infisical.get('CLIENT_SLUG_VERCEL')!; -export const getPostHogHost = () => infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com'; -export const getPostHogProjectApiKey = () => infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; -export const getSentryDSN = () => infisical.get('SENTRY_DSN')!; -export const getSiteURL = () => infisical.get('SITE_URL')!; -export const getSmtpHost = () => infisical.get('SMTP_HOST')!; -export const getSmtpSecure = () => infisical.get('SMTP_SECURE')! === 'true' || false; -export const getSmtpPort = () => parseInt(infisical.get('SMTP_PORT')!) || 587; -export const getSmtpUsername = () => infisical.get('SMTP_USERNAME')!; -export const getSmtpPassword = () => infisical.get('SMTP_PASSWORD')!; -export const getSmtpFromAddress = () => infisical.get('SMTP_FROM_ADDRESS')!; -export const getSmtpFromName = () => infisical.get('SMTP_FROM_NAME')! || 'Infisical'; -export const getStripeProductStarter = () => infisical.get('STRIPE_PRODUCT_STARTER')!; -export const getStripeProductPro = () => infisical.get('STRIPE_PRODUCT_PRO')!; -export const getStripeProductTeam = () => infisical.get('STRIPE_PRODUCT_TEAM')!; -export const getStripePublishableKey = () => infisical.get('STRIPE_PUBLISHABLE_KEY')!; -export const getStripeSecretKey = () => infisical.get('STRIPE_SECRET_KEY')!; -export const getStripeWebhookSecret = () => infisical.get('STRIPE_WEBHOOK_SECRET')!; -export const getTelemetryEnabled = () => infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; -export const getLoopsApiKey = () => infisical.get('LOOPS_API_KEY')!; -export const getSmtpConfigured = () => infisical.get('SMTP_HOST') == '' || infisical.get('SMTP_HOST') == undefined ? false : true -export const getHttpsEnabled = () => { - if (getNodeEnv() != "production") { + +export const getPort = async () => await infisical.get('PORT')! || 4000; +export const getInviteOnlySignup = async () => await infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : await infisical.get('INVITE_ONLY_SIGNUP'); +export const getEncryptionKey = async () => await infisical.get('ENCRYPTION_KEY')!; +export const getSaltRounds = async () => parseInt(await infisical.get('SALT_ROUNDS')!) || 10; +export const getJwtAuthLifetime = async () => await infisical.get('JWT_AUTH_LIFETIME')! || '10d'; +export const getJwtAuthSecret = async () => await infisical.get('JWT_AUTH_SECRET')!; +export const getJwtMfaLifetime = async () => await infisical.get('JWT_MFA_LIFETIME')! || '5m'; +export const getJwtMfaSecret = async () => await infisical.get('JWT_MFA_LIFETIME')! || '5m'; +export const getJwtRefreshLifetime = async () => await infisical.get('JWT_REFRESH_LIFETIME')! || '90d'; +export const getJwtRefreshSecret = async () => await infisical.get('JWT_REFRESH_SECRET')!; +export const getJwtServiceSecret = async () => await infisical.get('JWT_SERVICE_SECRET')!; +export const getJwtSignupLifetime = async () => await infisical.get('JWT_SIGNUP_LIFETIME')! || '15m'; +export const getJwtSignupSecret = async () => await infisical.get('JWT_SIGNUP_SECRET')!; +export const getMongoURL = async () => await infisical.get('MONGO_URL')!; +export const getNodeEnv = async () => await infisical.get('NODE_ENV')! || 'production'; +export const getVerboseErrorOutput = async () => await infisical.get('VERBOSE_ERROR_OUTPUT')! === 'true' && true; +export const getLokiHost = async () => await infisical.get('LOKI_HOST')!; +export const getClientIdAzure = async () => await infisical.get('CLIENT_ID_AZURE')!; +export const getClientIdHeroku = async () => await infisical.get('CLIENT_ID_HEROKU')!; +export const getClientIdVercel = async () => await infisical.get('CLIENT_ID_VERCEL')!; +export const getClientIdNetlify = async () => await infisical.get('CLIENT_ID_NETLIFY')!; +export const getClientIdGitHub = async () => await infisical.get('CLIENT_ID_GITHUB')!; +export const getClientIdGitLab = async () => await infisical.get('CLIENT_ID_GITLAB')!; +export const getClientSecretAzure = async () => await infisical.get('CLIENT_SECRET_AZURE')!; +export const getClientSecretHeroku = async () => await infisical.get('CLIENT_SECRET_HEROKU')!; +export const getClientSecretVercel = async () => await infisical.get('CLIENT_SECRET_VERCEL')!; +export const getClientSecretNetlify = async () => await infisical.get('CLIENT_SECRET_NETLIFY')!; +export const getClientSecretGitHub = async () => await infisical.get('CLIENT_SECRET_GITHUB')!; +export const getClientSecretGitLab = async () => await infisical.get('CLIENT_SECRET_GITLAB')!; +export const getClientSlugVercel = async () => await infisical.get('CLIENT_SLUG_VERCEL')!; +export const getPostHogHost = async () => await infisical.get('POSTHOG_HOST')! || 'https://app.posthog.com'; +export const getPostHogProjectApiKey = async () => await infisical.get('POSTHOG_PROJECT_API_KEY')! || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE'; +export const getSentryDSN = async () => await infisical.get('SENTRY_DSN')!; +export const getSiteURL = async () => await infisical.get('SITE_URL')!; +export const getSmtpHost = async () => await infisical.get('SMTP_HOST')!; +export const getSmtpSecure = async () => await infisical.get('SMTP_SECURE')! === 'true' || false; +export const getSmtpPort = async () => parseInt(await infisical.get('SMTP_PORT')!) || 587; +export const getSmtpUsername = async () => await infisical.get('SMTP_USERNAME')!; +export const getSmtpPassword = async () => await infisical.get('SMTP_PASSWORD')!; +export const getSmtpFromAddress = async () => await infisical.get('SMTP_FROM_ADDRESS')!; +export const getSmtpFromName = async () => await infisical.get('SMTP_FROM_NAME')! || 'Infisical'; +export const getStripeProductStarter = async () => await infisical.get('STRIPE_PRODUCT_STARTER')!; +export const getStripeProductPro = async () => await infisical.get('STRIPE_PRODUCT_PRO')!; +export const getStripeProductTeam = async () => await infisical.get('STRIPE_PRODUCT_TEAM')!; +export const getStripePublishableKey = async () => await infisical.get('STRIPE_PUBLISHABLE_KEY')!; +export const getStripeSecretKey = async () => await infisical.get('STRIPE_SECRET_KEY')!; +export const getStripeWebhookSecret = async () => await infisical.get('STRIPE_WEBHOOK_SECRET')!; +export const getTelemetryEnabled = async () => await infisical.get('TELEMETRY_ENABLED')! !== 'false' && true; +export const getLoopsApiKey = async () => await infisical.get('LOOPS_API_KEY')!; +export const getSmtpConfigured = async () => await infisical.get('SMTP_HOST') == '' || await infisical.get('SMTP_HOST') == undefined ? false : true +export const getHttpsEnabled = async () => { + if ((await getNodeEnv()) != "production") { // no https for anything other than prod return false } - if (infisical.get('HTTPS_ENABLED') == undefined || infisical.get('HTTPS_ENABLED') == "") { + if ((await infisical.get('HTTPS_ENABLED')) == undefined || (await infisical.get('HTTPS_ENABLED')) == "") { // default when no value present return true } - return infisical.get('HTTPS_ENABLED') === 'true' && true + return (await infisical.get('HTTPS_ENABLED')) === 'true' && true } \ No newline at end of file diff --git a/backend/src/controllers/v1/authController.ts b/backend/src/controllers/v1/authController.ts index 84404bc74..e60002e9d 100644 --- a/backend/src/controllers/v1/authController.ts +++ b/backend/src/controllers/v1/authController.ts @@ -126,7 +126,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() + secure: await getHttpsEnabled() }); const loginAction = await EELogService.createAction({ @@ -182,7 +182,7 @@ export const logout = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() as boolean + secure: (await getHttpsEnabled()) as boolean }); const logoutAction = await EELogService.createAction({ @@ -237,7 +237,7 @@ export const getNewToken = async (req: Request, res: Response) => { } const decodedToken = ( - jwt.verify(refreshToken, getJwtRefreshSecret()) + jwt.verify(refreshToken, await getJwtRefreshSecret()) ); const user = await User.findOne({ @@ -252,8 +252,8 @@ export const getNewToken = async (req: Request, res: Response) => { payload: { userId: decodedToken.userId }, - expiresIn: getJwtAuthLifetime(), - secret: getJwtAuthSecret() + expiresIn: await getJwtAuthLifetime(), + secret: await getJwtAuthSecret() }); return res.status(200).send({ diff --git a/backend/src/controllers/v1/integrationAuthController.ts b/backend/src/controllers/v1/integrationAuthController.ts index 12a39a389..b21a0cd42 100644 --- a/backend/src/controllers/v1/integrationAuthController.ts +++ b/backend/src/controllers/v1/integrationAuthController.ts @@ -44,7 +44,7 @@ export const getIntegrationAuth = 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({ integrationOptions: INTEGRATION_OPTIONS, diff --git a/backend/src/controllers/v1/membershipController.ts b/backend/src/controllers/v1/membershipController.ts index 436be9dc4..67dd41a0d 100644 --- a/backend/src/controllers/v1/membershipController.ts +++ b/backend/src/controllers/v1/membershipController.ts @@ -215,7 +215,7 @@ export const inviteUserToWorkspace = async (req: Request, res: Response) => { inviterFirstName: req.user.firstName, inviterEmail: req.user.email, workspaceName: req.membership.workspace.name, - callback_url: getSiteURL() + '/login' + callback_url: (await getSiteURL()) + '/login' } }); } catch (err) { diff --git a/backend/src/controllers/v1/membershipOrgController.ts b/backend/src/controllers/v1/membershipOrgController.ts index 5a2b41b2b..b25a9b9a7 100644 --- a/backend/src/controllers/v1/membershipOrgController.ts +++ b/backend/src/controllers/v1/membershipOrgController.ts @@ -180,11 +180,11 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => { organizationName: organization.name, email: inviteeEmail, token, - callback_url: getSiteURL() + '/signupinvite' + callback_url: (await getSiteURL()) + '/signupinvite' } }); - if (!getSmtpConfigured()) { + if (!(await getSmtpConfigured())) { completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}` } } @@ -257,8 +257,8 @@ export const verifyUserToOrganization = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: getJwtSignupLifetime(), - secret: getJwtSignupSecret() + expiresIn: await getJwtSignupLifetime(), + secret: await getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/organizationController.ts b/backend/src/controllers/v1/organizationController.ts index 00ad87b82..6b082dac5 100644 --- a/backend/src/controllers/v1/organizationController.ts +++ b/backend/src/controllers/v1/organizationController.ts @@ -317,7 +317,7 @@ export const createOrganizationPortalSession = async ( ) => { let session; try { - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -333,13 +333,13 @@ export const createOrganizationPortalSession = async ( customer: req.membershipOrg.organization.customerId, mode: 'setup', payment_method_types: ['card'], - success_url: getSiteURL() + '/dashboard', - cancel_url: getSiteURL() + '/dashboard' + success_url: (await getSiteURL()) + '/dashboard', + cancel_url: (await getSiteURL()) + '/dashboard' }); } else { session = await stripe.billingPortal.sessions.create({ customer: req.membershipOrg.organization.customerId, - return_url: getSiteURL() + '/dashboard' + return_url: (await getSiteURL()) + '/dashboard' }); } @@ -365,7 +365,7 @@ export const getOrganizationSubscriptions = async ( ) => { let subscriptions; try { - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); diff --git a/backend/src/controllers/v1/passwordController.ts b/backend/src/controllers/v1/passwordController.ts index fed24419c..85c244505 100644 --- a/backend/src/controllers/v1/passwordController.ts +++ b/backend/src/controllers/v1/passwordController.ts @@ -44,7 +44,7 @@ export const emailPasswordReset = async (req: Request, res: Response) => { substitutions: { email, token, - callback_url: getSiteURL() + '/password-reset' + callback_url: (await getSiteURL()) + '/password-reset' } }); } catch (err) { @@ -91,8 +91,8 @@ export const emailPasswordResetVerify = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: getJwtSignupLifetime(), - secret: getJwtSignupSecret() + expiresIn: await getJwtSignupLifetime(), + secret: await getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/secretController.ts b/backend/src/controllers/v1/secretController.ts index 3ec69122a..316f6cc3e 100644 --- a/backend/src/controllers/v1/secretController.ts +++ b/backend/src/controllers/v1/secretController.ts @@ -39,7 +39,7 @@ export const pushSecrets = async (req: Request, res: Response) => { // upload (encrypted) secrets to workspace with id [workspaceId] try { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); let { secrets }: { secrets: PushSecret[] } = req.body; const { keys, environment, channel } = req.body; const { workspaceId } = req.params; @@ -114,7 +114,7 @@ export const pullSecrets = async (req: Request, res: Response) => { let secrets; let key; try { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; @@ -183,7 +183,7 @@ export const pullSecretsServiceToken = async (req: Request, res: Response) => { let secrets; let key; try { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; diff --git a/backend/src/controllers/v1/serviceTokenController.ts b/backend/src/controllers/v1/serviceTokenController.ts index 9f241349a..86a87f372 100644 --- a/backend/src/controllers/v1/serviceTokenController.ts +++ b/backend/src/controllers/v1/serviceTokenController.ts @@ -61,7 +61,7 @@ export const createServiceToken = async (req: Request, res: Response) => { workspaceId }, expiresIn: expiresIn, - secret: getJwtServiceSecret() + secret: await getJwtServiceSecret() }); } catch (err) { return res.status(400).send({ diff --git a/backend/src/controllers/v1/signupController.ts b/backend/src/controllers/v1/signupController.ts index 6adb0e7a8..193699c15 100644 --- a/backend/src/controllers/v1/signupController.ts +++ b/backend/src/controllers/v1/signupController.ts @@ -21,7 +21,7 @@ export const beginEmailSignup = async (req: Request, res: Response) => { try { 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 const userCount = await User.countDocuments({}) if (userCount != 0) { @@ -75,7 +75,7 @@ export const verifyEmailSignup = async (req: Request, res: Response) => { } // verify email - if (getSmtpConfigured()) { + if (await getSmtpConfigured()) { await checkEmailVerification({ email, code @@ -93,8 +93,8 @@ export const verifyEmailSignup = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: getJwtSignupLifetime(), - secret: getJwtSignupSecret() + expiresIn: await getJwtSignupLifetime(), + secret: await getJwtSignupSecret() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v1/stripeController.ts b/backend/src/controllers/v1/stripeController.ts index 1a981c088..809107ad9 100644 --- a/backend/src/controllers/v1/stripeController.ts +++ b/backend/src/controllers/v1/stripeController.ts @@ -13,7 +13,7 @@ export const handleWebhook = async (req: Request, res: Response) => { let event; try { // check request for valid stripe signature - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => { event = stripe.webhooks.constructEvent( req.body, sig, - getStripeWebhookSecret() + await getStripeWebhookSecret() ); } catch (err) { Sentry.setUser({ email: req.user.email }); diff --git a/backend/src/controllers/v2/apiKeyDataController.ts b/backend/src/controllers/v2/apiKeyDataController.ts index e4450ae90..86533c733 100644 --- a/backend/src/controllers/v2/apiKeyDataController.ts +++ b/backend/src/controllers/v2/apiKeyDataController.ts @@ -43,7 +43,7 @@ export const createAPIKeyData = async (req: Request, res: Response) => { const { name, expiresIn } = req.body; const secret = crypto.randomBytes(16).toString('hex'); - const secretHash = await bcrypt.hash(secret, getSaltRounds()); + const secretHash = await bcrypt.hash(secret, await getSaltRounds()); const expiresAt = new Date(); expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn); diff --git a/backend/src/controllers/v2/authController.ts b/backend/src/controllers/v2/authController.ts index 92b4a159a..15350f6e5 100644 --- a/backend/src/controllers/v2/authController.ts +++ b/backend/src/controllers/v2/authController.ts @@ -124,8 +124,8 @@ export const login2 = async (req: Request, res: Response) => { payload: { userId: user._id.toString() }, - expiresIn: getJwtMfaLifetime(), - secret: getJwtMfaSecret() + expiresIn: await getJwtMfaLifetime(), + secret: await getJwtMfaSecret() }); const code = await TokenService.createToken({ @@ -163,7 +163,7 @@ export const login2 = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() + secure: await getHttpsEnabled() }); // case: user does not have MFA enablgged @@ -302,7 +302,7 @@ export const verifyMfaToken = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() + secure: await getHttpsEnabled() }); interface VerifyMfaTokenRes { diff --git a/backend/src/controllers/v2/secretController.ts b/backend/src/controllers/v2/secretController.ts index d551e8aa4..75eff3127 100644 --- a/backend/src/controllers/v2/secretController.ts +++ b/backend/src/controllers/v2/secretController.ts @@ -17,7 +17,7 @@ import { AccountNotFoundError } from '../../utils/errors'; * @param res */ export const createSecret = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const secretToCreate: CreateSecretRequestBody = req.body.secret; const { workspaceId, environment } = req.params const sanitizedSecret: SanitizedSecretForCreate = { @@ -70,7 +70,7 @@ export const createSecret = async (req: Request, res: Response) => { * @param res */ export const createSecrets = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const secretsToCreate: CreateSecretRequestBody[] = req.body.secrets; const { workspaceId, environment } = req.params const sanitizedSecretesToCreate: SanitizedSecretForCreate[] = [] @@ -132,7 +132,7 @@ export const createSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecrets = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const { workspaceId, environmentName } = req.params const secretIdsToDelete: string[] = req.body.secretIds @@ -186,7 +186,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { * @param res */ export const deleteSecret = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); await Secret.findByIdAndDelete(req._secret._id) if (postHogClient) { @@ -215,7 +215,7 @@ export const deleteSecret = async (req: Request, res: Response) => { * @returns */ export const updateSecrets = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const { workspaceId, environmentName } = req.params const secretsModificationsRequested: ModifySecretRequestBody[] = req.body.secrets; const [secretIdsUserCanModifyError, secretIdsUserCanModify] = await to(Secret.find({ workspace: workspaceId, environment: environmentName }, { _id: 1 }).then()) @@ -283,7 +283,7 @@ export const updateSecrets = async (req: Request, res: Response) => { * @returns */ export const updateSecret = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const { workspaceId, environmentName } = req.params const secretModificationsRequested: ModifySecretRequestBody = req.body.secret; @@ -337,7 +337,7 @@ export const updateSecret = async (req: Request, res: Response) => { * @returns */ export const getSecrets = async (req: Request, res: Response) => { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const { environment } = req.query; const { workspaceId } = req.params; diff --git a/backend/src/controllers/v2/secretsController.ts b/backend/src/controllers/v2/secretsController.ts index f7b8349ff..f7e3cc6b2 100644 --- a/backend/src/controllers/v2/secretsController.ts +++ b/backend/src/controllers/v2/secretsController.ts @@ -35,7 +35,7 @@ import { export const batchSecrets = async (req: Request, res: Response) => { const channel = getChannelFromUserAgent(req.headers['user-agent']); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const { workspaceId, @@ -508,7 +508,7 @@ export const createSecrets = async (req: Request, res: Response) => { workspaceId: new Types.ObjectId(workspaceId) }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ event: 'secrets added', @@ -683,7 +683,7 @@ export const getSecrets = async (req: Request, res: Response) => { ipAddress: req.ip }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ event: 'secrets pulled', @@ -905,7 +905,7 @@ export const updateSecrets = async (req: Request, res: Response) => { workspaceId: new Types.ObjectId(key) }) - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ event: 'secrets modified', @@ -1039,7 +1039,7 @@ export const deleteSecrets = async (req: Request, res: Response) => { workspaceId: new Types.ObjectId(key) }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ event: 'secrets deleted', diff --git a/backend/src/controllers/v2/serviceAccountsController.ts b/backend/src/controllers/v2/serviceAccountsController.ts index 7eaf73cea..d0ec62e1b 100644 --- a/backend/src/controllers/v2/serviceAccountsController.ts +++ b/backend/src/controllers/v2/serviceAccountsController.ts @@ -72,7 +72,7 @@ export const createServiceAccount = async (req: Request, res: Response) => { } 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 const serviceAccount = await new ServiceAccount({ diff --git a/backend/src/controllers/v2/serviceTokenDataController.ts b/backend/src/controllers/v2/serviceTokenDataController.ts index 332e7e34a..597548f2f 100644 --- a/backend/src/controllers/v2/serviceTokenDataController.ts +++ b/backend/src/controllers/v2/serviceTokenDataController.ts @@ -84,7 +84,7 @@ export const createServiceTokenData = async (req: Request, res: Response) => { } = req.body; const secret = crypto.randomBytes(16).toString('hex'); - const secretHash = await bcrypt.hash(secret, getSaltRounds()); + const secretHash = await bcrypt.hash(secret, await getSaltRounds()); let expiresAt; if (expiresIn) { diff --git a/backend/src/controllers/v2/signupController.ts b/backend/src/controllers/v2/signupController.ts index ff41aa017..7cfb3e454 100644 --- a/backend/src/controllers/v2/signupController.ts +++ b/backend/src/controllers/v2/signupController.ts @@ -108,7 +108,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { token = tokens.token; // sending a welcome email to new users - if (getLoopsApiKey()) { + if (await getLoopsApiKey()) { await request.post("https://app.loops.so/api/v1/events/send", { "email": email, "eventName": "Sign Up", @@ -117,7 +117,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { }, { headers: { "Accept": "application/json", - "Authorization": "Bearer " + getLoopsApiKey() + "Authorization": "Bearer " + (await getLoopsApiKey()) }, }); } @@ -127,7 +127,7 @@ export const completeAccountSignup = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() + secure: await getHttpsEnabled() }); } catch (err) { Sentry.setUser(null); @@ -232,7 +232,7 @@ export const completeAccountInvite = async (req: Request, res: Response) => { httpOnly: true, path: '/', sameSite: 'strict', - secure: getHttpsEnabled() + secure: await getHttpsEnabled() }); } catch (err) { Sentry.setUser(null); diff --git a/backend/src/controllers/v2/workspaceController.ts b/backend/src/controllers/v2/workspaceController.ts index dd0efe91f..ea673d428 100644 --- a/backend/src/controllers/v2/workspaceController.ts +++ b/backend/src/controllers/v2/workspaceController.ts @@ -48,7 +48,7 @@ interface V2PushSecret { export const pushWorkspaceSecrets = async (req: Request, res: Response) => { // upload (encrypted) secrets to workspace with id [workspaceId] try { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); let { secrets }: { secrets: V2PushSecret[] } = req.body; const { keys, environment, channel } = req.body; const { workspaceId } = req.params; @@ -123,7 +123,7 @@ export const pushWorkspaceSecrets = async (req: Request, res: Response) => { export const pullSecrets = async (req: Request, res: Response) => { let secrets; try { - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); const environment: string = req.query.environment as string; const channel: string = req.query.channel as string; const { workspaceId } = req.params; diff --git a/backend/src/ee/controllers/v1/stripeController.ts b/backend/src/ee/controllers/v1/stripeController.ts index 3caa0f395..69858c94f 100644 --- a/backend/src/ee/controllers/v1/stripeController.ts +++ b/backend/src/ee/controllers/v1/stripeController.ts @@ -12,7 +12,7 @@ import { getStripeSecretKey, getStripeWebhookSecret } from '../../../config'; export const handleWebhook = async (req: Request, res: Response) => { let event; try { - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); @@ -21,7 +21,7 @@ export const handleWebhook = async (req: Request, res: Response) => { event = stripe.webhooks.constructEvent( req.body, sig, - getStripeWebhookSecret() + await getStripeWebhookSecret() ); } catch (err) { Sentry.setUser({ email: req.user.email }); diff --git a/backend/src/helpers/auth.ts b/backend/src/helpers/auth.ts index 47b1ef9b1..fcc3ba8ff 100644 --- a/backend/src/helpers/auth.ts +++ b/backend/src/helpers/auth.ts @@ -104,7 +104,7 @@ const getAuthUserPayload = async ({ authTokenValue: string; }) => { const decodedToken = ( - jwt.verify(authTokenValue, getJwtAuthSecret()) + jwt.verify(authTokenValue, await getJwtAuthSecret()) ); const user = await User.findOne({ @@ -263,16 +263,16 @@ const issueAuthTokens = async ({ userId }: { userId: string }) => { payload: { userId }, - expiresIn: getJwtAuthLifetime(), - secret: getJwtAuthSecret() + expiresIn: await getJwtAuthLifetime(), + secret: await getJwtAuthSecret() }); const refreshToken = createToken({ payload: { userId }, - expiresIn: getJwtRefreshLifetime(), - secret: getJwtRefreshSecret() + expiresIn: await getJwtRefreshLifetime(), + secret: await getJwtRefreshSecret() }); return { diff --git a/backend/src/helpers/bot.ts b/backend/src/helpers/bot.ts index ff55a5e2c..db022c42f 100644 --- a/backend/src/helpers/bot.ts +++ b/backend/src/helpers/bot.ts @@ -119,7 +119,7 @@ const createBot = async ({ const { publicKey, privateKey } = generateKeyPair(); const { ciphertext, iv, tag } = encryptSymmetric({ plaintext: privateKey, - key: getEncryptionKey() + key: await getEncryptionKey() }); bot = await new Bot({ @@ -216,7 +216,7 @@ const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => { ciphertext: bot.encryptedPrivateKey, iv: bot.iv, tag: bot.tag, - key: getEncryptionKey() + key: await getEncryptionKey() }); key = decryptAsymmetric({ diff --git a/backend/src/helpers/database.ts b/backend/src/helpers/database.ts index aa29f7e46..4bfaf1305 100644 --- a/backend/src/helpers/database.ts +++ b/backend/src/helpers/database.ts @@ -20,12 +20,12 @@ const initDatabaseHelper = async ({ // allow empty strings to pass the required validator 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 SecretService.initSecretBlindIndexDataHelper(); } 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; diff --git a/backend/src/helpers/nodemailer.ts b/backend/src/helpers/nodemailer.ts index fe7c05044..386db2c39 100644 --- a/backend/src/helpers/nodemailer.ts +++ b/backend/src/helpers/nodemailer.ts @@ -25,7 +25,7 @@ const sendMail = async ({ recipients: string[]; substitutions: any; }) => { - if (getSmtpConfigured()) { + if (await getSmtpConfigured()) { try { const html = fs.readFileSync( path.resolve(__dirname, '../templates/' + template), @@ -35,7 +35,7 @@ const sendMail = async ({ const htmlToSend = temp(substitutions); await smtpTransporter.sendMail({ - from: `"${getSmtpFromName()}" <${getSmtpFromAddress()}>`, + from: `"${await getSmtpFromName()}" <${await getSmtpFromAddress()}>`, to: recipients.join(', '), subject: subjectLine, html: htmlToSend diff --git a/backend/src/helpers/organization.ts b/backend/src/helpers/organization.ts index 9840c9075..ee52bac8e 100644 --- a/backend/src/helpers/organization.ts +++ b/backend/src/helpers/organization.ts @@ -123,11 +123,11 @@ const createOrganization = async ({ let organization; try { // register stripe account - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); - if (getStripeSecretKey()) { + if (await getStripeSecretKey()) { const customer = await stripe.customers.create({ email, description: name @@ -177,14 +177,14 @@ const initSubscriptionOrg = async ({ if (organization) { if (organization.customerId) { // initialize starter subscription with quantity of 0 - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); const productToPriceMap = { - starter: getStripeProductStarter(), - team: getStripeProductTeam(), - pro: getStripeProductPro() + starter: await getStripeProductStarter(), + team: await getStripeProductTeam(), + pro: await getStripeProductPro() }; stripeSubscription = await stripe.subscriptions.create({ @@ -239,7 +239,7 @@ const updateSubscriptionOrgQuantity = async ({ status: ACCEPTED }); - const stripe = new Stripe(getStripeSecretKey(), { + const stripe = new Stripe(await getStripeSecretKey(), { apiVersion: '2022-08-01' }); diff --git a/backend/src/helpers/secrets.ts b/backend/src/helpers/secrets.ts index 6e33b187d..87b828659 100644 --- a/backend/src/helpers/secrets.ts +++ b/backend/src/helpers/secrets.ts @@ -242,7 +242,7 @@ const initSecretBlindIndexDataHelper = async () => { tag: saltTag } = encryptSymmetric({ plaintext: salt, - key: getEncryptionKey() + key: await getEncryptionKey() }); const secretBlindIndexData = new SecretBlindIndexData({ @@ -280,7 +280,7 @@ const createSecretBlindIndexDataHelper = async ({ tag: saltTag } = encryptSymmetric({ plaintext: salt, - key: getEncryptionKey() + key: await getEncryptionKey() }); const secretBlindIndexData = await new SecretBlindIndexData({ @@ -316,7 +316,7 @@ const getSecretBlindIndexSaltHelper = async ({ ciphertext: secretBlindIndexData.encryptedSaltCiphertext, iv: secretBlindIndexData.saltIV, tag: secretBlindIndexData.saltTag, - key: getEncryptionKey() + key: await getEncryptionKey() }); return salt; @@ -378,7 +378,7 @@ const generateSecretBlindIndexHelper = async ({ ciphertext: secretBlindIndexData.encryptedSaltCiphertext, iv: secretBlindIndexData.saltIV, tag: secretBlindIndexData.saltTag, - key: getEncryptionKey() + key: await getEncryptionKey() }); const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({ @@ -508,7 +508,7 @@ const createSecretHelper = async ({ workspaceId }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ @@ -578,7 +578,7 @@ const getSecretsHelper = async ({ ipAddress: authData.authIP }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ @@ -660,7 +660,7 @@ const getSecretHelper = async ({ ipAddress: authData.authIP }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ @@ -798,7 +798,7 @@ const updateSecretHelper = async ({ workspaceId }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ @@ -905,7 +905,7 @@ const deleteSecretHelper = async ({ workspaceId }); - const postHogClient = TelemetryService.getPostHogClient(); + const postHogClient = await TelemetryService.getPostHogClient(); if (postHogClient) { postHogClient.capture({ diff --git a/backend/src/helpers/token.ts b/backend/src/helpers/token.ts index 0f6a88cc9..8fcbb1dd6 100644 --- a/backend/src/helpers/token.ts +++ b/backend/src/helpers/token.ts @@ -84,7 +84,7 @@ const createTokenHelper = async ({ const query: TokenDataQuery = { type }; const update: TokenDataUpdate = { type, - tokenHash: await bcrypt.hash(token, getSaltRounds()), + tokenHash: await bcrypt.hash(token, await getSaltRounds()), expiresAt } diff --git a/backend/src/helpers/workspace.ts b/backend/src/helpers/workspace.ts index bb002176b..5047a1967 100644 --- a/backend/src/helpers/workspace.ts +++ b/backend/src/helpers/workspace.ts @@ -28,7 +28,6 @@ import { AUTH_MODE_SERVICE_TOKEN, AUTH_MODE_API_KEY } from '../variables'; -import { getEncryptionKey } from '../config'; import { encryptSymmetric } from '../utils/crypto'; import { SecretService } from '../services'; diff --git a/backend/src/index.ts b/backend/src/index.ts index 0636bb5a3..877a4215d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -79,22 +79,20 @@ import { } from './config'; const main = async () => { - if (process.env.INFISICAL_TOKEN != "" || process.env.INFISICAL_TOKEN != undefined) { - await infisical.connect({ - token: process.env.INFISICAL_TOKEN! - }); - } + infisical.connect({ + token: process.env.INFISICAL_TOKEN! + }); TelemetryService.logTelemetryMessage(); - setTransporter(initSmtp()); + setTransporter(await initSmtp()); - await DatabaseService.initDatabase(getMongoURL()); - if (getNodeEnv() !== 'test') { + await DatabaseService.initDatabase(await getMongoURL()); + if ((await getNodeEnv()) !== 'test') { Sentry.init({ - dsn: getSentryDSN(), + dsn: await getSentryDSN(), tracesSampleRate: 1.0, - debug: getNodeEnv() === 'production' ? false : true, - environment: getNodeEnv() + debug: await getNodeEnv() === 'production' ? false : true, + environment: await getNodeEnv() }); } @@ -106,13 +104,13 @@ const main = async () => { app.use( cors({ credentials: true, - origin: getSiteURL() + origin: await getSiteURL() }) ); app.use(requestIp.mw()); - if (getNodeEnv() === 'production') { + if ((await getNodeEnv()) === 'production') { // enable app-wide rate-limiting + helmet security // in production app.disable('x-powered-by'); @@ -177,8 +175,8 @@ const main = async () => { app.use(requestErrorHandler) - const server = app.listen(getPort(), () => { - getLogger("backend-main").info(`Server started listening at port ${getPort()}`) + const server = app.listen(await getPort(), async () => { + (await getLogger("backend-main")).info(`Server started listening at port ${await getPort()}`) }); await createTestUserForDevelopment(); diff --git a/backend/src/integrations/exchange.ts b/backend/src/integrations/exchange.ts index 1dccb03d0..04dc96ca0 100644 --- a/backend/src/integrations/exchange.ts +++ b/backend/src/integrations/exchange.ts @@ -159,9 +159,9 @@ const exchangeCodeAzure = async ({ grant_type: 'authorization_code', code: code, scope: 'https://vault.azure.net/.default openid offline_access', - client_id: getClientIdAzure(), - client_secret: getClientSecretAzure(), - redirect_uri: `${getSiteURL()}/integrations/azure-key-vault/oauth2/callback` + client_id: await getClientIdAzure(), + client_secret: await getClientSecretAzure(), + redirect_uri: `${await getSiteURL()}/integrations/azure-key-vault/oauth2/callback` } as any) )).data; @@ -204,7 +204,7 @@ const exchangeCodeHeroku = async ({ new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_secret: getClientSecretHeroku() + client_secret: await getClientSecretHeroku() } as any) )).data; @@ -242,9 +242,9 @@ const exchangeCodeVercel = async ({ code }: { code: string }) => { INTEGRATION_VERCEL_TOKEN_URL, new URLSearchParams({ code: code, - client_id: getClientIdVercel(), - client_secret: getClientSecretVercel(), - redirect_uri: `${getSiteURL()}/integrations/vercel/oauth2/callback` + client_id: await getClientIdVercel(), + client_secret: await getClientSecretVercel(), + redirect_uri: `${await getSiteURL()}/integrations/vercel/oauth2/callback` } as any) ) ).data; @@ -282,9 +282,9 @@ const exchangeCodeNetlify = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: getClientIdNetlify(), - client_secret: getClientSecretNetlify(), - redirect_uri: `${getSiteURL()}/integrations/netlify/oauth2/callback` + client_id: await getClientIdNetlify(), + client_secret: await getClientSecretNetlify(), + redirect_uri: `${await getSiteURL()}/integrations/netlify/oauth2/callback` } as any) ) ).data; @@ -333,10 +333,10 @@ const exchangeCodeGithub = async ({ code }: { code: string }) => { res = ( await request.get(INTEGRATION_GITHUB_TOKEN_URL, { params: { - client_id: getClientIdGitHub(), - client_secret: getClientSecretGitHub(), + client_id: await getClientIdGitHub(), + client_secret: await getClientSecretGitHub(), code: code, - redirect_uri: `${getSiteURL()}/integrations/github/oauth2/callback` + redirect_uri: `${await getSiteURL()}/integrations/github/oauth2/callback` }, headers: { 'Accept': 'application/json', @@ -379,9 +379,9 @@ const exchangeCodeGitlab = async ({ code }: { code: string }) => { new URLSearchParams({ grant_type: 'authorization_code', code: code, - client_id: getClientIdGitLab(), - client_secret: getClientSecretGitLab(), - redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback` + client_id: await getClientIdGitLab(), + client_secret: await getClientSecretGitLab(), + redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/integrations/refresh.ts b/backend/src/integrations/refresh.ts index a0aea080e..79b89ecfb 100644 --- a/backend/src/integrations/refresh.ts +++ b/backend/src/integrations/refresh.ts @@ -133,11 +133,11 @@ const exchangeRefreshAzure = async ({ const { data }: { data: RefreshTokenAzureResponse } = await request.post( INTEGRATION_AZURE_TOKEN_URL, new URLSearchParams({ - client_id: getClientIdAzure(), + client_id: await getClientIdAzure(), scope: 'openid offline_access', refresh_token: refreshToken, grant_type: 'refresh_token', - client_secret: getClientSecretAzure() + client_secret: await getClientSecretAzure() } as any) ); @@ -180,7 +180,7 @@ const exchangeRefreshHeroku = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_secret: getClientSecretHeroku() + client_secret: await getClientSecretHeroku() } as any) ); @@ -223,9 +223,9 @@ const exchangeRefreshGitLab = async ({ new URLSearchParams({ grant_type: 'refresh_token', refresh_token: refreshToken, - client_id: getClientIdGitLab, - client_secret: getClientSecretGitLab(), - redirect_uri: `${getSiteURL()}/integrations/gitlab/oauth2/callback` + client_id: await getClientIdGitLab, + client_secret: await getClientSecretGitLab(), + redirect_uri: `${await getSiteURL()}/integrations/gitlab/oauth2/callback` } as any), { headers: { diff --git a/backend/src/middleware/requestErrorHandler.ts b/backend/src/middleware/requestErrorHandler.ts index 202a38e83..4833d991f 100644 --- a/backend/src/middleware/requestErrorHandler.ts +++ b/backend/src/middleware/requestErrorHandler.ts @@ -5,9 +5,9 @@ import { getLogger } from "../utils/logger"; import RequestError, { LogLevel } from "../utils/requestError"; 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 (getNodeEnv() !== "production") { + if ((await getNodeEnv()) !== "production") { /* eslint-disable no-console */ console.log(error) /* 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 if (!(error instanceof RequestError)) { - error = InternalServerError({ context: { exception: error.message }, stack: error.stack }) - getLogger('backend-main').log((error).levelName.toLowerCase(), (error).message) + error = InternalServerError({ context: { exception: error.message }, stack: error.stack }); + (await getLogger('backend-main')).log((error).levelName.toLowerCase(), (error).message) } //* Set Sentry user identification if req.user is populated diff --git a/backend/src/middleware/requireMfaAuth.ts b/backend/src/middleware/requireMfaAuth.ts index 7fb38ca25..ca0b3434e 100644 --- a/backend/src/middleware/requireMfaAuth.ts +++ b/backend/src/middleware/requireMfaAuth.ts @@ -26,7 +26,7 @@ const requireMfaAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, getJwtMfaSecret()) + jwt.verify(AUTH_TOKEN_VALUE, await getJwtMfaSecret()) ); const user = await User.findOne({ diff --git a/backend/src/middleware/requireServiceTokenAuth.ts b/backend/src/middleware/requireServiceTokenAuth.ts index 106ca9bbb..5db0dcda5 100644 --- a/backend/src/middleware/requireServiceTokenAuth.ts +++ b/backend/src/middleware/requireServiceTokenAuth.ts @@ -33,7 +33,7 @@ const requireServiceTokenAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, getJwtServiceSecret()) + jwt.verify(AUTH_TOKEN_VALUE, await getJwtServiceSecret()) ); const serviceToken = await ServiceToken.findOne({ diff --git a/backend/src/middleware/requireSignupAuth.ts b/backend/src/middleware/requireSignupAuth.ts index 19e6b3146..6a0fd0b6e 100644 --- a/backend/src/middleware/requireSignupAuth.ts +++ b/backend/src/middleware/requireSignupAuth.ts @@ -27,7 +27,7 @@ const requireSignupAuth = async ( if(AUTH_TOKEN_VALUE === null) return next(BadRequestError({message: 'Missing Authorization Body in the request header'})) const decodedToken = ( - jwt.verify(AUTH_TOKEN_VALUE, getJwtSignupSecret()) + jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret()) ); const user = await User.findOne({ diff --git a/backend/src/routes/status/status.ts b/backend/src/routes/status/status.ts index f793128be..91d0c9e85 100644 --- a/backend/src/routes/status/status.ts +++ b/backend/src/routes/status/status.ts @@ -5,11 +5,11 @@ const router = express.Router(); router.get( '/status', - (req: Request, res: Response) => { + async (req: Request, res: Response) => { res.status(200).json({ date: new Date(), message: 'Ok', - emailConfigured: getSmtpConfigured() + emailConfigured: await getSmtpConfigured() }) } ); diff --git a/backend/src/services/DatabaseService.ts b/backend/src/services/DatabaseService.ts index fdfd7660a..616f56c47 100644 --- a/backend/src/services/DatabaseService.ts +++ b/backend/src/services/DatabaseService.ts @@ -1,5 +1,3 @@ -import mongoose from 'mongoose'; -import { getLogger } from '../utils/logger'; import { initDatabaseHelper, closeDatabaseHelper diff --git a/backend/src/services/TelemetryService.ts b/backend/src/services/TelemetryService.ts index 578b5292d..da90732eb 100644 --- a/backend/src/services/TelemetryService.ts +++ b/backend/src/services/TelemetryService.ts @@ -24,9 +24,9 @@ class Telemetry { /** * Logs telemetry enable/disable notice. */ - static logTelemetryMessage = () => { - if(!getTelemetryEnabled()){ - getLogger("backend-main").info([ + static logTelemetryMessage = async () => { + if(!(await getTelemetryEnabled())){ + (await getLogger("backend-main")).info([ "", "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.", @@ -39,12 +39,12 @@ class Telemetry { * Return an instance of the PostHog client initialized. * @returns */ - static getPostHogClient = () => { + static getPostHogClient = async () => { let postHogClient: any; - if (getNodeEnv() === 'production' && getTelemetryEnabled()) { + if ((await getNodeEnv()) === 'production' && (await getTelemetryEnabled())) { // case: enable opt-out telemetry in production - postHogClient = new PostHog(getPostHogProjectApiKey(), { - host: getPostHogHost() + postHogClient = new PostHog(await getPostHogProjectApiKey(), { + host: await getPostHogHost() }); } diff --git a/backend/src/services/health.ts b/backend/src/services/health.ts index 9c441ba9d..daf3bf962 100644 --- a/backend/src/services/health.ts +++ b/backend/src/services/health.ts @@ -3,8 +3,8 @@ import { createTerminus } from '@godaddy/terminus'; import { getLogger } from '../utils/logger'; export const setUpHealthEndpoint = (server: T) => { - const onSignal = () => { - getLogger('backend-main').info('Server is starting clean-up'); + const onSignal = async () => { + (await getLogger('backend-main')).info('Server is starting clean-up'); return Promise.all([ new Promise((resolve) => { if (mongoose.connection && mongoose.connection.readyState == 1) { diff --git a/backend/src/services/smtp.ts b/backend/src/services/smtp.ts index b30a43447..6bae626be 100644 --- a/backend/src/services/smtp.ts +++ b/backend/src/services/smtp.ts @@ -15,21 +15,21 @@ import { getSmtpPort } from '../config'; -export const initSmtp = () => { +export const initSmtp = async () => { const mailOpts: SMTPConnection.Options = { - host: getSmtpHost(), - port: getSmtpPort() + host: await getSmtpHost(), + port: await getSmtpPort() }; - if (getSmtpUsername() && getSmtpPassword()) { + if ((await getSmtpUsername()) && (await getSmtpPassword())) { mailOpts.auth = { - user: getSmtpUsername(), - pass: getSmtpPassword() + user: await getSmtpUsername(), + pass: await getSmtpPassword() }; } - if (getSmtpSecure() ? getSmtpSecure() : false) { - switch (getSmtpHost()) { + if ((await getSmtpSecure()) ? (await getSmtpSecure()) : false) { + switch (await getSmtpHost()) { case SMTP_HOST_SENDGRID: mailOpts.requireTLS = true; break; @@ -52,7 +52,7 @@ export const initSmtp = () => { } break; default: - if (getSmtpHost().includes('amazonaws.com')) { + if ((await getSmtpHost()).includes('amazonaws.com')) { mailOpts.tls = { ciphers: 'TLSv1.2' } @@ -70,10 +70,10 @@ export const initSmtp = () => { Sentry.setUser(null); Sentry.captureMessage('SMTP - Successfully connected'); }) - .catch((err) => { + .catch(async (err) => { Sentry.setUser(null); Sentry.captureException( - `SMTP - Failed to connect to ${getSmtpHost()}:${getSmtpPort()} \n\t${err}` + `SMTP - Failed to connect to ${await getSmtpHost()}:${await getSmtpPort()} \n\t${err}` ); }); diff --git a/backend/src/utils/addDevelopmentUser.ts b/backend/src/utils/addDevelopmentUser.ts index 136d91a98..52aedcdd2 100644 --- a/backend/src/utils/addDevelopmentUser.ts +++ b/backend/src/utils/addDevelopmentUser.ts @@ -19,7 +19,7 @@ export const testWorkspaceKeyId = "63cf48f0225e6955acec5eff" export const plainTextWorkspaceKey = "543fef8224813a46230b0a50a46c5fb2" export const createTestUserForDevelopment = async () => { - if (getNodeEnv() === "development" || getNodeEnv() === "test") { + if ((await getNodeEnv()) === "development" || (await getNodeEnv()) === "test") { const testUser = { _id: testUserId, email: testUserEmail, diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts index ed29c97ca..15335a619 100644 --- a/backend/src/utils/logger.ts +++ b/backend/src/utils/logger.ts @@ -12,7 +12,7 @@ const logFormat = (prefix: string) => combine( 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' //* Always add Console output to transports const _transports: any[] = [ @@ -25,10 +25,10 @@ const createLoggerWithLabel = (level: string, label: string) => { }) ] //* Add LokiTransport if it's enabled - if(getLokiHost() !== undefined){ + if((await getLokiHost()) !== undefined){ _transports.push( new LokiTransport({ - host: getLokiHost(), + host: await getLokiHost(), handleExceptions: true, handleRejections: true, batching: true, @@ -40,7 +40,7 @@ const createLoggerWithLabel = (level: string, label: string) => { labels: { app: process.env.npm_package_name, 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) }) @@ -58,12 +58,10 @@ const createLoggerWithLabel = (level: string, label: string) => { }); } -const DEFAULT_LOGGERS = { - "backend-main": createLoggerWithLabel('info', '[IFSC:backend-main]'), - "database": createLoggerWithLabel('info', '[IFSC:database]'), -} -type LoggerNames = keyof typeof DEFAULT_LOGGERS - -export const getLogger = (loggerName: LoggerNames) => { - return DEFAULT_LOGGERS[loggerName] +export const getLogger = async (loggerName: 'backend-main' | 'database') => { + const logger = { + "backend-main": await createLoggerWithLabel('info', '[IFSC:backend-main]'), + "database": await createLoggerWithLabel('info', '[IFSC:database]'), + } + return logger[loggerName] } diff --git a/backend/src/utils/requestError.ts b/backend/src/utils/requestError.ts index 4b5635bac..570ed132e 100644 --- a/backend/src/utils/requestError.ts +++ b/backend/src/utils/requestError.ts @@ -81,13 +81,13 @@ export default class RequestError extends Error{ return obj } - public format(req: Request){ + public async format(req: Request){ let _context = Object.assign({ stacktrace: this.stacktrace }, this.context) //* 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, [ 'stacktrace', 'exception', diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts index bbfd7f107..aac78968d 100644 --- a/backend/src/variables/integration.ts +++ b/backend/src/variables/integration.ts @@ -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_SUPABASE_API_URL = 'https://api.supabase.com'; -const getIntegrationOptions = () => { +const getIntegrationOptions = async () => { const INTEGRATION_OPTIONS = [ { name: 'Heroku', @@ -69,7 +69,7 @@ const getIntegrationOptions = () => { image: 'Heroku.png', isAvailable: true, type: 'oauth', - clientId: getClientIdHeroku(), + clientId: await getClientIdHeroku(), docsLink: '' }, { @@ -79,7 +79,7 @@ const getIntegrationOptions = () => { isAvailable: true, type: 'oauth', clientId: '', - clientSlug: getClientSlugVercel(), + clientSlug: await getClientSlugVercel(), docsLink: '' }, { @@ -88,7 +88,7 @@ const getIntegrationOptions = () => { image: 'Netlify.png', isAvailable: true, type: 'oauth', - clientId: getClientIdNetlify(), + clientId: await getClientIdNetlify(), docsLink: '' }, { @@ -97,7 +97,7 @@ const getIntegrationOptions = () => { image: 'GitHub.png', isAvailable: true, type: 'oauth', - clientId: getClientIdGitHub(), + clientId: await getClientIdGitHub(), docsLink: '' }, { @@ -151,7 +151,7 @@ const getIntegrationOptions = () => { image: 'Microsoft Azure.png', isAvailable: true, type: 'oauth', - clientId: getClientIdAzure(), + clientId: await getClientIdAzure(), docsLink: '' }, { @@ -169,7 +169,7 @@ const getIntegrationOptions = () => { image: 'GitLab.png', isAvailable: true, type: 'custom', - clientId: getClientIdGitLab(), + clientId: await getClientIdGitLab(), docsLink: '' }, { From a7484f8be5b7be0e50da2f541d1682a45af7b77c Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 23 Apr 2023 09:49:21 +0300 Subject: [PATCH 03/13] Update node SDK docs, positioning of examples --- docs/getting-started/quickstart.mdx | 36 ++++++----- docs/sdks/languages/node.mdx | 97 +++++++++++++++-------------- 2 files changed, 69 insertions(+), 64 deletions(-) diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index f19bd1120..8a2ef75f5 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -64,7 +64,9 @@ These examples demonstrate how to store and fetch environment variables from [In ### Initialize the Infisical client ```js - await infisical.connect({ + import infisical from "infisical-node"; + + infisical.connect({ token: "your_infisical_token", }); ``` @@ -72,31 +74,31 @@ These examples demonstrate how to store and fetch environment variables from [In ### Get a value ```js - const value = infisical.get("SOME_KEY"); + const value = await infisical.getSecret("SOME_KEY"); ``` ### Example with Express ```js - const express = require("express"); - const port = 3000; - const infisical = require("infisical-node"); + import infisical from "infisical-node"; + import express from "express"; + const app = express(); + const PORT = 3000; - const main = async () => { - await infisical.connect({ - token: "st.xxx.xxx", - }); + await infisical.connect({ + token: "st.xxx.xxx", + }); - // your application logic + // your application logic - app.get("/", (req, res) => { - res.send(`Howdy, ${infisical.get("NAME")}!`); - }); + app.get("/", async (req, res) => { + const name = await infisical.getSecret("NAME"); + res.send(`Hello! My name is: ${name.secretValue}`); + }); - app.listen(port, async () => { - console.log(`App listening on port ${port}`); - }); - }; + app.listen(PORT, async () => { + console.log(`App listening on port ${port}`); + }); ``` diff --git a/docs/sdks/languages/node.mdx b/docs/sdks/languages/node.mdx index fe5e969f8..28ce8cf00 100644 --- a/docs/sdks/languages/node.mdx +++ b/docs/sdks/languages/node.mdx @@ -2,7 +2,7 @@ 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 ecrets for your application. ## Installation @@ -12,14 +12,42 @@ Run `npm` to add `infisical-node` to your project. npm install infisical-node --save ``` -## Initialization +## Configuration -Call `connect()` with your Infisical token as early as possible in the main entry module of your application. This initializes the global instance of the SDK, which can be accessed anywhere in your application. +Import the SDK and call `connect()` with your Infisical token as early as possible in the main entry module of your application. This initializes the global instance of the SDK, which can be accessed anywhere in your application. For multiple Infisical projects or creating multiple SDK instances, use `createConnection()` instead. This returns a local SDK instance, independent of the global instance. ### infisical.connect(options) + + + + ```js + import infisical from "infisical-node"; + + infisical.connect({ + token: "your_infisical_token", + }); + + // your app logic + ``` + + + + ```js + const infisical = require("infisical-node"); + + infisical.connect({ + token: "your_infisical_token" + }); + + // your app logic + ```` + + + + Updates the global instance of the Infisical client with a connection to an Infisical project with the [Infisical Token](/getting-started/dashboard/token). @@ -37,7 +65,7 @@ Updates the global instance of the Infisical client with a connection to an Infi `https://app.infisical.com`) - Time-to-live (in seconds) for cached secrets. If set to 0, data is cached indefinitely. + Time-to-live (in seconds) for refreshing cached secrets. Default: `300`. Whether or not debug mode is on @@ -74,41 +102,21 @@ This method is useful if you wish to connect to two or more Infisical projects w - - - ```js - import infisical from "infisical-node"; - - infisical.connect({ - token: "your_infisical_token", - }); - - // your app logic - ``` - - - - ```js - const infisical = require("infisical-node"); - - infisical.connect({ - token: "your_infisical_token" - }); - - // your app logic - ```` - - - ## Usage ### infisical.getSecret(secretName, options) +```js +const secret = await infisical.getSecret("API_KEY"); +const value = secret.secretValue; // get its value +``` + Retrieve a secret from Infisical. By default, `getSecret()` returns a personal secret. If not found, it returns a shared secret, or tries to retrieve the value from `process.env`. + The key of the secret to retrieve @@ -120,13 +128,12 @@ By default, `getSecret()` returns a personal secret. If not found, it returns a -```js -const secret = await infisical.getSecret("API_KEY"); -const value = secret.secretValue; // get its value -``` - ### infisical.createSecret(secretName, secretValue, options) +```js +const newApiKey = await infisical.createSecret("API_KEY", "FOO"); +``` + Create a new secret in Infisical. @@ -143,12 +150,12 @@ Create a new secret in Infisical. -```js -const newApiKey = await infisical.createSecret("API_KEY", "FOO"); -``` - ### infisical.updateSecret(secretName, secretValue, options) +```js +const updatedApiKey = await infisical.updateSecret("API_KEY", "BAR"); +``` + Update an existing secret in Infisical. @@ -165,12 +172,12 @@ Update an existing secret in Infisical. -```js -const updatedApiKey = await infisical.updateSecret("API_KEY", "BAR"); -``` - ### infisical.deleteSecret(secretName, options) +```js +const deletedSecret = await infisical.deleteSecret("API_KEY"); +``` + Delete a secret in Infisical. @@ -184,10 +191,6 @@ Delete a secret in Infisical. -```js -const deletedSecret = await infisical.deleteSecret("API_KEY"); -``` - ## Example with Express ```js From aacdaf4556053c74f9d4a750716eb974d7ef54dc Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 23 Apr 2023 12:45:13 +0300 Subject: [PATCH 04/13] Modify Node SDK docs to be inline with new initializer --- docs/sdks/languages/node.mdx | 57 +++++++----------------------------- 1 file changed, 10 insertions(+), 47 deletions(-) diff --git a/docs/sdks/languages/node.mdx b/docs/sdks/languages/node.mdx index 28ce8cf00..5a51afc79 100644 --- a/docs/sdks/languages/node.mdx +++ b/docs/sdks/languages/node.mdx @@ -14,20 +14,15 @@ npm install infisical-node --save ## Configuration -Import the SDK and call `connect()` with your Infisical token as early as possible in the main entry module of your application. This initializes the global instance of the SDK, which can be accessed anywhere in your application. - -For multiple Infisical projects or creating multiple SDK instances, use `createConnection()` instead. This returns a local SDK instance, independent of the global instance. - -### infisical.connect(options) - +Import the SDK and create a client instance with your Infisical token. ```js - import infisical from "infisical-node"; - - infisical.connect({ - token: "your_infisical_token", + import InfisicalClient from "infisical-node"; + + const client = new InfisicalClient({ + token: "your_infisical_token" }); // your app logic @@ -36,9 +31,9 @@ For multiple Infisical projects or creating multiple SDK instances, use `createC ```js - const infisical = require("infisical-node"); + const InfisicalClient = require("infisical-node"); - infisical.connect({ + const client = new InfisicalClient({ token: "your_infisical_token" }); @@ -48,8 +43,6 @@ For multiple Infisical projects or creating multiple SDK instances, use `createC -Updates the global instance of the Infisical client with a connection to an Infisical project with the [Infisical Token](/getting-started/dashboard/token). - @@ -73,36 +66,6 @@ Updates the global instance of the Infisical client with a connection to an Infi -### infisical.createConnection(options) - -Returns a local instance of the Infisical client with a connection to an Infisical project 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. - - - - - An [Infisical Token](/getting-started/dashboard/token) scoped to a project - and environment - - - Your self-hosted absolute site URL including the protocol (e.g. - `https://app.infisical.com`) - - - Time-to-live (in seconds) for cached secrets. If set to 0, data is cached indefinitely. - - - Whether or not debug mode is on - - - - - ## Usage ### infisical.getSecret(secretName, options) @@ -194,18 +157,18 @@ Delete a secret in Infisical. ## Example with Express ```js -import infisical from "infisical-node"; +import InfisicalClient from "infisical-node"; import express from "express"; const app = express(); const PORT = 3000; -infisical.connect({ +const client = new InfisicalClient({ token: "YOUR_INFISICAL_TOKEN" }); app.get("/", async (req, res) => { // access value - const name = await infisical.getSecret("NAME"); + const name = await client.getSecret("NAME"); res.send(`Hello! My name is: ${name.secretValue}`); }); From 34c79b08bc59e5cbb21f9669165dcb5a73bb02bd Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 23 Apr 2023 13:38:36 +0300 Subject: [PATCH 05/13] Update InfisicalClient initialization --- backend/src/config/index.ts | 6 +++++- backend/src/index.ts | 4 ---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 175328caa..e2b52eab3 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,4 +1,8 @@ -import infisical from 'infisical-node'; +import InfisicalClient from 'infisical-node'; + +const infisical = new InfisicalClient({ + token: process.env.INFISICAL_TOKEN! +}); export const getPort = async () => await infisical.get('PORT')! || 4000; export const getInviteOnlySignup = async () => await infisical.get('INVITE_ONLY_SIGNUP')! == undefined ? false : await infisical.get('INVITE_ONLY_SIGNUP'); diff --git a/backend/src/index.ts b/backend/src/index.ts index 877a4215d..7aecafe9e 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -79,10 +79,6 @@ import { } from './config'; const main = async () => { - infisical.connect({ - token: process.env.INFISICAL_TOKEN! - }); - TelemetryService.logTelemetryMessage(); setTransporter(await initSmtp()); From 9e42a7a33e10638822ed34d846099acb7d2ec908 Mon Sep 17 00:00:00 2001 From: Tuan Dang Date: Sun, 23 Apr 2023 15:51:42 +0300 Subject: [PATCH 06/13] Update quickstart example --- docs/getting-started/quickstart.mdx | 12 ++++++------ docs/sdks/languages/node.mdx | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/getting-started/quickstart.mdx b/docs/getting-started/quickstart.mdx index 8a2ef75f5..7fca86d4d 100644 --- a/docs/getting-started/quickstart.mdx +++ b/docs/getting-started/quickstart.mdx @@ -64,9 +64,9 @@ These examples demonstrate how to store and fetch environment variables from [In ### Initialize the Infisical client ```js - import infisical from "infisical-node"; + import InfisicalClient from "infisical-node"; - infisical.connect({ + const client = new InfisicalClient({ token: "your_infisical_token", }); ``` @@ -74,25 +74,25 @@ These examples demonstrate how to store and fetch environment variables from [In ### Get a value ```js - const value = await infisical.getSecret("SOME_KEY"); + const value = await client.getSecret("SOME_KEY"); ``` ### Example with Express ```js - import infisical from "infisical-node"; + import InfisicalClient from "infisical-node"; import express from "express"; const app = express(); const PORT = 3000; - await infisical.connect({ + const client = InfisicalClient({ token: "st.xxx.xxx", }); // your application logic app.get("/", async (req, res) => { - const name = await infisical.getSecret("NAME"); + const name = await client.getSecret("NAME"); res.send(`Hello! My name is: ${name.secretValue}`); }); diff --git a/docs/sdks/languages/node.mdx b/docs/sdks/languages/node.mdx index 5a51afc79..cb56f50d4 100644 --- a/docs/sdks/languages/node.mdx +++ b/docs/sdks/languages/node.mdx @@ -2,7 +2,7 @@ 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 and work with ecrets 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. ## Installation From f0075e8d0956fcdad7109a37092088a20d9ee29a Mon Sep 17 00:00:00 2001 From: Maidul Islam Date: Tue, 25 Apr 2023 16:15:18 -0400 Subject: [PATCH 07/13] add folder controller --- .../controllers/v1/secretsFolderController.ts | 89 +++++++++++++++++++ backend/src/models/folder.ts | 36 ++++++++ backend/src/utils/folder.ts | 87 ++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 backend/src/controllers/v1/secretsFolderController.ts create mode 100644 backend/src/models/folder.ts create mode 100644 backend/src/utils/folder.ts diff --git a/backend/src/controllers/v1/secretsFolderController.ts b/backend/src/controllers/v1/secretsFolderController.ts new file mode 100644 index 000000000..2e856c2a4 --- /dev/null +++ b/backend/src/controllers/v1/secretsFolderController.ts @@ -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() +} \ No newline at end of file diff --git a/backend/src/models/folder.ts b/backend/src/models/folder.ts new file mode 100644 index 000000000..885e320a8 --- /dev/null +++ b/backend/src/models/folder.ts @@ -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; \ No newline at end of file diff --git a/backend/src/utils/folder.ts b/backend/src/utils/folder.ts new file mode 100644 index 000000000..f12845339 --- /dev/null +++ b/backend/src/utils/folder.ts @@ -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); +} \ No newline at end of file From a946031d6f673b291de2b5e76b5754b031d77778 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Tue, 25 Apr 2023 17:14:39 -0700 Subject: [PATCH 08/13] fix loading animation --- frontend/src/pages/dashboard/[id].tsx | 2 -- frontend/src/views/DashboardPage/DashboardEnvOverview.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/pages/dashboard/[id].tsx b/frontend/src/pages/dashboard/[id].tsx index 418c038f5..449182ed8 100644 --- a/frontend/src/pages/dashboard/[id].tsx +++ b/frontend/src/pages/dashboard/[id].tsx @@ -803,8 +803,6 @@ export default function Dashboard() { isReadDenied: false }; - console.log(124, envSlug, selectedWorkspaceEnv) - if (selectedWorkspaceEnv) { if (snapshotData) setSelectedSnapshotEnv(selectedWorkspaceEnv); else setSelectedEnv(selectedWorkspaceEnv); diff --git a/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx b/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx index b71e0118f..a2a2ea624 100644 --- a/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx +++ b/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx @@ -125,7 +125,7 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => { if (isSecretsLoading || isEnvListLoading) { return ( -
+
loading animation
); From 7a3456ca1dc804ab2dbff88c089cf2e2e46c0c47 Mon Sep 17 00:00:00 2001 From: Vladyslav Matsiiako Date: Tue, 25 Apr 2023 19:25:31 -0700 Subject: [PATCH 09/13] scrolling fix --- frontend/src/views/DashboardPage/DashboardEnvOverview.tsx | 4 ++-- .../components/EnvComparisonRow/EnvComparisonRow.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx b/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx index a2a2ea624..f4efa8583 100644 --- a/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx +++ b/frontend/src/views/DashboardPage/DashboardEnvOverview.tsx @@ -234,14 +234,14 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
*/} -
+
0
0
{userAvailableEnvs?.map(env => { - return
+ return