1
0
mirror of https://github.com/Infisical/infisical.git synced 2025-03-29 13:26:20 +00:00

Compare commits

..

49 Commits

Author SHA1 Message Date
6de25174aa fix: upgrade sharp from 0.31.3 to 0.32.0
Snyk has created this PR to upgrade sharp from 0.31.3 to 0.32.0.

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

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/53d4ecb6-6cc1-4918-aa73-bf9cae4ffd13?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-28 20:42:03 +00:00
2aa79d4ad6 Merge pull request from seonggwonyoon/main
Add namespace option for using helm
2023-04-28 14:18:38 -04:00
44b4de754a remove test check in workflow 2023-04-28 13:21:38 -04:00
db0f0d0d9c disable secrets integ tests temp 2023-04-28 13:18:25 -04:00
3471e387ae Merge branch 'main' of https://github.com/Infisical/infisical 2023-04-28 10:11:36 -07:00
aadd964409 Fix the deployment issue 2023-04-28 10:11:25 -07:00
102e45891c Update getAppsGitHub to include pagination 2023-04-28 20:10:29 +03:00
b9ae224aef Patch organization invitation emails expiring for existing users and billing logic affected by missing organization populate call 2023-04-28 17:57:50 +03:00
330968c7af added gradient to the menu 2023-04-27 19:46:01 -07:00
68e8e727cd Merge branch 'main' of https://github.com/Infisical/infisical 2023-04-27 18:44:46 -07:00
3b94ee42e9 Animated menu icons 2023-04-27 18:44:23 -07:00
09286b4421 Merge pull request from PylotLight/update-k8s-doc
Update k8s doc to add backend service info
2023-04-27 11:24:31 -04:00
04a9604ba9 add advanced use cases for hostAPI 2023-04-27 11:14:47 -04:00
d86f88db92 Merge pull request from Infisical/snyk-upgrade-9829915033f54fef09ffef896e2c5908
[Snyk] Upgrade @sentry/tracing from 7.46.0 to 7.47.0
2023-04-27 09:57:55 -04:00
fc53c094b7 Merge branch 'main' into snyk-upgrade-9829915033f54fef09ffef896e2c5908 2023-04-27 09:57:49 -04:00
6726ca1882 Merge pull request from Infisical/snyk-upgrade-521a72e06b59b78e721ff564679159b3
[Snyk] Upgrade @aws-sdk/client-secrets-manager from 3.303.0 to 3.306.0
2023-04-27 09:57:05 -04:00
ddbe4d7040 Merge pull request from Infisical/snyk-upgrade-714666653eb4091158908b7ca4704cbb
[Snyk] Upgrade @sentry/node from 7.46.0 to 7.47.0
2023-04-27 09:56:53 -04:00
3f6b0a9e66 Merge pull request from Infisical/snyk-upgrade-8b1f2b028bcdff3d60cbaa239abb732d
[Snyk] Upgrade axios from 1.3.4 to 1.3.5
2023-04-27 09:56:43 -04:00
c3a47597b6 fix formatting 2023-04-27 23:31:33 +10:00
a696a99232 add backend service inof to doc 2023-04-27 23:28:19 +10:00
8b1e64f75e Merge pull request from Infisical/python-sdk-docs
Finish Python SDK docs
2023-04-27 15:57:19 +03:00
f137087ef1 Finish Python SDK docs 2023-04-27 15:53:23 +03:00
2157fab181 fix: upgrade axios from 1.3.4 to 1.3.5
Snyk has created this PR to upgrade axios from 1.3.4 to 1.3.5.

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

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-27 04:43:44 +00:00
d2acab57e0 fix: upgrade @sentry/node from 7.46.0 to 7.47.0
Snyk has created this PR to upgrade @sentry/node from 7.46.0 to 7.47.0.

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

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-27 04:43:39 +00:00
811929987b fix: upgrade @sentry/tracing from 7.46.0 to 7.47.0
Snyk has created this PR to upgrade @sentry/tracing from 7.46.0 to 7.47.0.

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

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-27 04:43:36 +00:00
4ac13f61e0 Update README.md 2023-04-26 12:05:13 -07:00
3d2b0fa3fc Update docker-image.yml 2023-04-26 15:03:31 -04:00
242809ce26 add folders to batch and get secrets api 2023-04-26 12:53:14 -04:00
492bf39243 Clarify getSecret and caching behavior in docs 2023-04-26 12:11:46 +03:00
dbfa4f5277 Merge pull request from Infisical/update-node-sdk
Update Infisical to use new Infisical Node SDK 1.1.3.
2023-04-26 11:58:07 +03:00
3fd2e22cbd Move Express example for Node SDK to top of that docs page 2023-04-26 11:53:46 +03:00
150eb1f5ee Merge remote-tracking branch 'origin' into update-node-sdk 2023-04-26 11:51:21 +03:00
6314a949f8 Update Infisical to use Infisical Node SDK 1.1.3 2023-04-26 11:50:51 +03:00
660c5806e3 Merge pull request from Infisical/revise-node-sdk-docs
Revise docs for Node SDK
2023-04-26 09:31:54 +03:00
c6d2828262 Merge remote-tracking branch 'origin' into revise-node-sdk-docs 2023-04-26 09:30:03 +03:00
8dedfad22d fix: upgrade @aws-sdk/client-secrets-manager from 3.303.0 to 3.306.0
Snyk has created this PR to upgrade @aws-sdk/client-secrets-manager from 3.303.0 to 3.306.0.

See this package in npm:
https://www.npmjs.com/package/@aws-sdk/client-secrets-manager

See this project in Snyk:
https://app.snyk.io/org/maidul98/project/35057e82-ed7d-4e19-ba4d-719a42135cd6?utm_source=github&utm_medium=referral&page=upgrade-pr
2023-04-26 04:29:05 +00:00
7a3456ca1d scrolling fix 2023-04-25 19:25:31 -07:00
a946031d6f fix loading animation 2023-04-25 17:14:39 -07:00
f0075e8d09 add folder controller 2023-04-25 16:15:18 -04:00
3b00df6662 Updated readme 2023-04-25 08:12:12 -07:00
a263d7481b Added truncation for secret names on the comparison screen 2023-04-25 08:11:31 -07:00
6f91331549 Merge pull request from Infisical/snyk-fix-c89a9aceb5e7741daf73a9a657eb1ead
[Snyk] Security upgrade yaml from 2.2.1 to 2.2.2
2023-04-25 10:14:55 -04:00
a5c5ec1f4d Print helm with namespace 2023-04-25 10:55:27 +09:00
9e42a7a33e Update quickstart example 2023-04-23 15:51:42 +03:00
34c79b08bc Update InfisicalClient initialization 2023-04-23 13:38:36 +03:00
aacdaf4556 Modify Node SDK docs to be inline with new initializer 2023-04-23 12:45:13 +03:00
a7484f8be5 Update node SDK docs, positioning of examples 2023-04-23 09:49:21 +03:00
e1bf31b371 Update envars to new node SDK format 2023-04-22 16:20:33 +03:00
3817831577 Update docs for upcoming Node SDK update 2023-04-22 14:34:05 +03:00
82 changed files with 11361 additions and 11325 deletions
README.md
backend
docs
frontend
helm-charts/infisical/templates

@ -25,7 +25,7 @@
<img src="https://img.shields.io/github/commit-activity/m/infisical/infisical" alt="git commit activity" />
</a>
<a href="https://cloudsmith.io/~infisical/repos/">
<img src="https://img.shields.io/badge/Downloads-128.2k-orange" alt="Cloudsmith downloads" />
<img src="https://img.shields.io/badge/Downloads-150.8k-orange" alt="Cloudsmith downloads" />
</a>
<a href="https://join.slack.com/t/infisical-users/shared_invite/zt-1kdbk07ro-RtoyEt_9E~fyzGo_xQYP6g">
<img src="https://img.shields.io/badge/chat-on%20Slack-blueviolet" alt="Slack community channel" />
@ -63,7 +63,7 @@
- **Role-based Access Controls** per environment
- **2FA** (more options coming soon)
- **Smart Security Alerts**
- 🔜 **1-Click Deploy** to AWS
- [**1-Click Deploy** to AWS and Digital Ocean](https://infisical.com/docs/self-hosting/overview)
- 🔜 **Automatic Secret Rotation**
- 🔜 **Slack & MS Teams** integrations

1699
backend/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -1,17 +1,16 @@
{
"dependencies": {
"@aws-sdk/client-secrets-manager": "^3.303.0",
"@aws-sdk/client-secrets-manager": "^3.306.0",
"@godaddy/terminus": "^4.11.2",
"@octokit/rest": "^19.0.5",
"@sentry/node": "^7.45.0",
"@sentry/tracing": "^7.46.0",
"@sentry/node": "^7.41.0",
"@sentry/tracing": "^7.47.0",
"@types/crypto-js": "^4.1.1",
"@types/libsodium-wrappers": "^0.7.10",
"argon2": "^0.30.3",
"await-to-js": "^3.0.0",
"aws-sdk": "^2.1338.0",
"axios": "^1.1.3",
"axios": "^1.3.5",
"axios-retry": "^3.4.0",
"bcrypt": "^5.1.0",
"bigint-conversion": "^2.2.2",
@ -25,7 +24,7 @@
"express-validator": "^6.14.2",
"handlebars": "^4.7.7",
"helmet": "^5.1.1",
"infisical-node": "^1.0.37",
"infisical-node": "^1.1.3",
"js-yaml": "^4.1.0",
"jsonwebtoken": "^9.0.0",
"jsrp": "^0.2.4",

@ -1,64 +1,69 @@
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") {
import InfisicalClient from 'infisical-node';
const client = new InfisicalClient({
token: process.env.INFISICAL_TOKEN!
});
export const getPort = async () => (await client.getSecret('PORT')).secretValue || 4000;
export const getInviteOnlySignup = async () => (await client.getSecret('INVITE_ONLY_SIGNUP')).secretValue == undefined ? false : await client.getSecret('INVITE_ONLY_SIGNUP');
export const getEncryptionKey = async () => (await client.getSecret('ENCRYPTION_KEY')).secretValue;
export const getSaltRounds = async () => parseInt((await client.getSecret('SALT_ROUNDS')).secretValue) || 10;
export const getJwtAuthLifetime = async () => (await client.getSecret('JWT_AUTH_LIFETIME')).secretValue || '10d';
export const getJwtAuthSecret = async () => (await client.getSecret('JWT_AUTH_SECRET')).secretValue;
export const getJwtMfaLifetime = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
export const getJwtMfaSecret = async () => (await client.getSecret('JWT_MFA_LIFETIME')).secretValue || '5m';
export const getJwtRefreshLifetime = async () => (await client.getSecret('JWT_REFRESH_LIFETIME')).secretValue || '90d';
export const getJwtRefreshSecret = async () => (await client.getSecret('JWT_REFRESH_SECRET')).secretValue;
export const getJwtServiceSecret = async () => (await client.getSecret('JWT_SERVICE_SECRET')).secretValue;
export const getJwtSignupLifetime = async () => (await client.getSecret('JWT_SIGNUP_LIFETIME')).secretValue || '15m';
export const getJwtSignupSecret = async () => (await client.getSecret('JWT_SIGNUP_SECRET')).secretValue;
export const getMongoURL = async () => (await client.getSecret('MONGO_URL')).secretValue;
export const getNodeEnv = async () => (await client.getSecret('NODE_ENV')).secretValue || 'production';
export const getVerboseErrorOutput = async () => (await client.getSecret('VERBOSE_ERROR_OUTPUT')).secretValue === 'true' && true;
export const getLokiHost = async () => (await client.getSecret('LOKI_HOST')).secretValue;
export const getClientIdAzure = async () => (await client.getSecret('CLIENT_ID_AZURE')).secretValue;
export const getClientIdHeroku = async () => (await client.getSecret('CLIENT_ID_HEROKU')).secretValue;
export const getClientIdVercel = async () => (await client.getSecret('CLIENT_ID_VERCEL')).secretValue;
export const getClientIdNetlify = async () => (await client.getSecret('CLIENT_ID_NETLIFY')).secretValue;
export const getClientIdGitHub = async () => (await client.getSecret('CLIENT_ID_GITHUB')).secretValue;
export const getClientIdGitLab = async () => (await client.getSecret('CLIENT_ID_GITLAB')).secretValue;
export const getClientSecretAzure = async () => (await client.getSecret('CLIENT_SECRET_AZURE')).secretValue;
export const getClientSecretHeroku = async () => (await client.getSecret('CLIENT_SECRET_HEROKU')).secretValue;
export const getClientSecretVercel = async () => (await client.getSecret('CLIENT_SECRET_VERCEL')).secretValue;
export const getClientSecretNetlify = async () => (await client.getSecret('CLIENT_SECRET_NETLIFY')).secretValue;
export const getClientSecretGitHub = async () => (await client.getSecret('CLIENT_SECRET_GITHUB')).secretValue;
export const getClientSecretGitLab = async () => (await client.getSecret('CLIENT_SECRET_GITLAB')).secretValue;
export const getClientSlugVercel = async () => (await client.getSecret('CLIENT_SLUG_VERCEL')).secretValue;
export const getPostHogHost = async () => (await client.getSecret('POSTHOG_HOST')).secretValue || 'https://app.posthog.com';
export const getPostHogProjectApiKey = async () => (await client.getSecret('POSTHOG_PROJECT_API_KEY')).secretValue || 'phc_nSin8j5q2zdhpFDI1ETmFNUIuTG4DwKVyIigrY10XiE';
export const getSentryDSN = async () => (await client.getSecret('SENTRY_DSN')).secretValue;
export const getSiteURL = async () => (await client.getSecret('SITE_URL')).secretValue;
export const getSmtpHost = async () => (await client.getSecret('SMTP_HOST')).secretValue;
export const getSmtpSecure = async () => (await client.getSecret('SMTP_SECURE')).secretValue === 'true' || false;
export const getSmtpPort = async () => parseInt((await client.getSecret('SMTP_PORT')).secretValue) || 587;
export const getSmtpUsername = async () => (await client.getSecret('SMTP_USERNAME')).secretValue;
export const getSmtpPassword = async () => (await client.getSecret('SMTP_PASSWORD')).secretValue;
export const getSmtpFromAddress = async () => (await client.getSecret('SMTP_FROM_ADDRESS')).secretValue;
export const getSmtpFromName = async () => (await client.getSecret('SMTP_FROM_NAME')).secretValue || 'Infisical';
export const getStripeProductStarter = async () => (await client.getSecret('STRIPE_PRODUCT_STARTER')).secretValue;
export const getStripeProductPro = async () => (await client.getSecret('STRIPE_PRODUCT_PRO')).secretValue;
export const getStripeProductTeam = async () => (await client.getSecret('STRIPE_PRODUCT_TEAM')).secretValue;
export const getStripePublishableKey = async () => (await client.getSecret('STRIPE_PUBLISHABLE_KEY')).secretValue;
export const getStripeSecretKey = async () => (await client.getSecret('STRIPE_SECRET_KEY')).secretValue;
export const getStripeWebhookSecret = async () => (await client.getSecret('STRIPE_WEBHOOK_SECRET')).secretValue;
export const getTelemetryEnabled = async () => (await client.getSecret('TELEMETRY_ENABLED')).secretValue !== 'false' && true;
export const getLoopsApiKey = async () => (await client.getSecret('LOOPS_API_KEY')).secretValue;
export const getSmtpConfigured = async () => (await client.getSecret('SMTP_HOST')).secretValue == '' || (await client.getSecret('SMTP_HOST')).secretValue == undefined ? false : true
export const 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 client.getSecret('HTTPS_ENABLED')).secretValue == undefined || (await client.getSecret('HTTPS_ENABLED')).secretValue == "") {
// default when no value present
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,
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.UserIDJwtPayload>(
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({

@ -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,

@ -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) {

@ -1,3 +1,4 @@
import { Types } from 'mongoose';
import { Request, Response } from 'express';
import * as Sentry from '@sentry/node';
import { MembershipOrg, Organization, User } from '../../models';
@ -139,7 +140,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
inviteEmail: inviteeEmail,
organization: organizationId,
role: MEMBER,
status: invitee?.publicKey ? ACCEPTED : INVITED
status: INVITED
}).save();
}
} else {
@ -164,6 +165,7 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
const organization = await Organization.findOne({ _id: organizationId });
if (organization) {
const token = await TokenService.createToken({
type: TOKEN_EMAIL_ORG_INVITATION,
email: inviteeEmail,
@ -179,12 +181,13 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
inviterEmail: req.user.email,
organizationName: organization.name,
email: inviteeEmail,
organizationId: organization._id.toString(),
token,
callback_url: getSiteURL() + '/signupinvite'
callback_url: (await getSiteURL()) + '/signupinvite'
}
});
if (!getSmtpConfigured()) {
if (!(await getSmtpConfigured())) {
completeInviteLink = `${siteUrl + '/signupinvite'}?token=${token}&to=${inviteeEmail}`
}
}
@ -214,13 +217,18 @@ export const inviteUserToOrganization = async (req: Request, res: Response) => {
export const verifyUserToOrganization = async (req: Request, res: Response) => {
let user, token;
try {
const { email, code } = req.body;
const {
email,
organizationId,
code
} = req.body;
user = await User.findOne({ email }).select('+publicKey');
const membershipOrg = await MembershipOrg.findOne({
inviteEmail: email,
status: INVITED
status: INVITED,
organization: new Types.ObjectId(organizationId)
});
if (!membershipOrg)
@ -257,8 +265,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);

@ -85,7 +85,7 @@ export const createOrganization = async (req: Request, res: Response) => {
export const getOrganization = async (req: Request, res: Response) => {
let organization;
try {
organization = req.membershipOrg.organization;
organization = req.organization
} catch (err) {
Sentry.setUser({ email: req.user.email });
Sentry.captureException(err);
@ -317,29 +317,29 @@ export const createOrganizationPortalSession = async (
) => {
let session;
try {
const stripe = new Stripe(getStripeSecretKey(), {
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
// check if there is a payment method on file
const paymentMethods = await stripe.paymentMethods.list({
customer: req.membershipOrg.organization.customerId,
customer: req.organization.customerId,
type: 'card'
});
if (paymentMethods.data.length < 1) {
// case: no payment method on file
session = await stripe.checkout.sessions.create({
customer: req.membershipOrg.organization.customerId,
customer: req.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'
customer: req.organization.customerId,
return_url: (await getSiteURL()) + '/dashboard'
});
}
@ -365,12 +365,12 @@ export const getOrganizationSubscriptions = async (
) => {
let subscriptions;
try {
const stripe = new Stripe(getStripeSecretKey(), {
const stripe = new Stripe(await getStripeSecretKey(), {
apiVersion: '2022-08-01'
});
subscriptions = await stripe.subscriptions.list({
customer: req.membershipOrg.organization.customerId
customer: req.organization.customerId
});
} catch (err) {
Sentry.setUser({ email: req.user.email });

@ -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);

@ -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;

@ -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
},
expiresIn: expiresIn,
secret: getJwtServiceSecret()
secret: await getJwtServiceSecret()
});
} catch (err) {
return res.status(400).send({

@ -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);

@ -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 });

@ -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);

@ -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 {

@ -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;

@ -25,6 +25,8 @@ import {
BatchSecretRequest,
BatchSecret
} 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
@ -35,7 +37,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,
@ -51,13 +53,18 @@ export const batchSecrets = async (req: Request, res: Response) => {
const updateSecrets: BatchSecret[] = [];
const deleteSecrets: Types.ObjectId[] = [];
const actions: IAction[] = [];
// get secret blind index salt
const salt = await SecretService.getSecretBlindIndexSalt({
workspaceId: new Types.ObjectId(workspaceId)
});
for await (const request of requests) {
const folderId = request.secret.folderId
// TODO: need to auth folder
const fullFolderPath = await getFolderPath(folderId)
let secretBlindIndex = '';
switch (request.method) {
case 'POST':
@ -72,19 +79,23 @@ export const batchSecrets = async (req: Request, res: Response) => {
user: request.secret.type === SECRET_PERSONAL ? req.user : undefined,
environment,
workspace: new Types.ObjectId(workspaceId),
path: fullFolderPath,
folder: folderId,
secretBlindIndex
});
break;
case 'PATCH':
secretBlindIndex = await SecretService.generateSecretBlindIndexWithSalt({
secretName: request.secret.secretName,
salt
salt,
});
updateSecrets.push({
...request.secret,
_id: new Types.ObjectId(request.secret._id),
secretBlindIndex
secretBlindIndex,
folder: folderId,
path: fullFolderPath,
});
break;
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());
setTimeout(async () => {
// trigger event - push secrets
await EventService.handleEvent({
@ -508,7 +519,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',
@ -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 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
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) {
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) {
secretQuery.tags = { $in: tagIds };
}
@ -655,6 +681,12 @@ export const getSecrets = async (req: Request, res: Response) => {
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) {
secretQuery.tags = { $in: tagIds };
}
@ -683,7 +715,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',
@ -701,7 +733,8 @@ export const getSecrets = async (req: Request, res: Response) => {
}
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)
})
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
event: 'secrets modified',
@ -1039,7 +1072,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',

@ -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({

@ -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) {

@ -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);

@ -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;

@ -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 });

@ -104,7 +104,7 @@ const getAuthUserPayload = async ({
authTokenValue: string;
}) => {
const decodedToken = <jwt.UserIDJwtPayload>(
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 {

@ -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({

@ -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;

@ -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

@ -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'
});

@ -233,27 +233,29 @@ const initSecretBlindIndexDataHelper = async () => {
}
});
const secretBlindIndexDataToInsert = workspaceIdsToBlindIndex.map((workspaceToBlindIndex) => {
const salt = crypto.randomBytes(16).toString('base64');
const secretBlindIndexDataToInsert = await Promise.all(
workspaceIdsToBlindIndex.map(async (workspaceToBlindIndex) => {
const salt = crypto.randomBytes(16).toString('base64');
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: getEncryptionKey()
});
const {
ciphertext: encryptedSaltCiphertext,
iv: saltIV,
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: await getEncryptionKey()
});
const secretBlindIndexData = new SecretBlindIndexData({
workspace: workspaceToBlindIndex,
encryptedSaltCiphertext,
saltIV,
saltTag
const secretBlindIndexData = new SecretBlindIndexData({
workspace: workspaceToBlindIndex,
encryptedSaltCiphertext,
saltIV,
saltTag
})
return secretBlindIndexData;
})
return secretBlindIndexData;
});
);
if (secretBlindIndexDataToInsert.length > 0) {
await SecretBlindIndexData.insertMany(secretBlindIndexDataToInsert);
@ -280,7 +282,7 @@ const createSecretBlindIndexDataHelper = async ({
tag: saltTag
} = encryptSymmetric({
plaintext: salt,
key: getEncryptionKey()
key: await getEncryptionKey()
});
const secretBlindIndexData = await new SecretBlindIndexData({
@ -316,7 +318,7 @@ const getSecretBlindIndexSaltHelper = async ({
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
iv: secretBlindIndexData.saltIV,
tag: secretBlindIndexData.saltTag,
key: getEncryptionKey()
key: await getEncryptionKey()
});
return salt;
@ -378,7 +380,7 @@ const generateSecretBlindIndexHelper = async ({
ciphertext: secretBlindIndexData.encryptedSaltCiphertext,
iv: secretBlindIndexData.saltIV,
tag: secretBlindIndexData.saltTag,
key: getEncryptionKey()
key: await getEncryptionKey()
});
const secretBlindIndex = await generateSecretBlindIndexWithSaltHelper({
@ -508,7 +510,7 @@ const createSecretHelper = async ({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -578,7 +580,7 @@ const getSecretsHelper = async ({
ipAddress: authData.authIP
});
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -660,7 +662,7 @@ const getSecretHelper = async ({
ipAddress: authData.authIP
});
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -798,7 +800,7 @@ const updateSecretHelper = async ({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({
@ -905,7 +907,7 @@ const deleteSecretHelper = async ({
workspaceId
});
const postHogClient = TelemetryService.getPostHogClient();
const postHogClient = await TelemetryService.getPostHogClient();
if (postHogClient) {
postHogClient.capture({

@ -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
}

@ -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';

@ -1,7 +1,6 @@
import mongoose from 'mongoose';
import dotenv from 'dotenv';
dotenv.config();
import infisical from 'infisical-node';
import express from 'express';
import helmet from 'helmet';
import cors from 'cors';
@ -45,7 +44,8 @@ import {
password as v1PasswordRouter,
stripe as v1StripeRouter,
integration as v1IntegrationRouter,
integrationAuth as v1IntegrationAuthRouter
integrationAuth as v1IntegrationAuthRouter,
secretsFolder as v1SecretsFolder
} from './routes/v1';
import {
signup as v2SignupRouter,
@ -79,22 +79,16 @@ import {
} from './config';
const main = async () => {
if (process.env.INFISICAL_TOKEN != "" || process.env.INFISICAL_TOKEN != undefined) {
await 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 +100,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');
@ -144,6 +138,7 @@ const main = async () => {
app.use('/api/v1/stripe', v1StripeRouter);
app.use('/api/v1/integration', v1IntegrationRouter);
app.use('/api/v1/integration-auth', v1IntegrationAuthRouter);
app.use('/api/v1/folder', v1SecretsFolder)
// v2 routes (improvements)
app.use('/api/v2/signup', v2SignupRouter);
@ -158,7 +153,7 @@ const main = async () => {
app.use('/api/v2/service-token', v2ServiceTokenDataRouter); // TODO: turn into plural route
app.use('/api/v2/service-accounts', v2ServiceAccountsRouter); // new
app.use('/api/v2/api-key', v2APIKeyDataRouter);
// v3 routes (experimental)
app.use('/api/v3/secrets', v3SecretsRouter);
app.use('/api/v3/workspaces', v3WorkspacesRouter);
@ -177,8 +172,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();

@ -270,28 +270,59 @@ const getAppsNetlify = async ({ accessToken }: { accessToken: string }) => {
const getAppsGithub = async ({ accessToken }: { accessToken: string }) => {
let apps;
try {
interface GitHubApp {
id: string;
name: string;
permissions: {
admin: boolean;
};
owner: {
login: string;
}
}
const octokit = new Octokit({
auth: accessToken,
});
const repos = (
await octokit.request(
"GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}",
{
per_page: 100,
const getAllRepos = async () => {
let repos: GitHubApp[] = [];
let page = 1;
const per_page = 100;
let hasMore = true;
while (hasMore) {
const response = await octokit.request(
"GET /user/repos{?visibility,affiliation,type,sort,direction,per_page,page,since,before}",
{
per_page,
page,
}
);
if (response.data.length > 0) {
repos = repos.concat(response.data);
page++;
} else {
hasMore = false;
}
)
).data;
}
return repos;
};
const repos = await getAllRepos();
apps = repos
.filter((a: any) => a.permissions.admin === true)
.map((a: any) => {
return ({
.filter((a: GitHubApp) => a.permissions.admin === true)
.map((a: GitHubApp) => {
return {
appId: a.id,
name: a.name,
owner: a.owner.login,
});
};
});
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);

@ -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: {

@ -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: {

@ -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((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
error = InternalServerError({ context: { exception: error.message }, stack: error.stack });
(await getLogger('backend-main')).log((<RequestError>error).levelName.toLowerCase(), (<RequestError>error).message)
}
//* 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'}))
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, getJwtMfaSecret())
jwt.verify(AUTH_TOKEN_VALUE, await getJwtMfaSecret())
);
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'}))
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, getJwtServiceSecret())
jwt.verify(AUTH_TOKEN_VALUE, await getJwtServiceSecret())
);
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'}))
const decodedToken = <jwt.UserIDJwtPayload>(
jwt.verify(AUTH_TOKEN_VALUE, getJwtSignupSecret())
jwt.verify(AUTH_TOKEN_VALUE, await getJwtSignupSecret())
);
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_PERSONAL,
} from '../variables';
import { ROOT_FOLDER_PATH } from '../utils/folder';
export interface ISecret {
_id: Types.ObjectId;
@ -25,6 +26,8 @@ export interface ISecret {
secretCommentTag?: string;
secretCommentHash?: string;
tags?: string[];
path?: string;
folder?: Types.ObjectId;
}
const secretSchema = new Schema<ISecret>(
@ -107,7 +110,18 @@ const secretSchema = new Schema<ISecret>(
secretCommentHash: {
type: String,
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

@ -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()
})
}
);

@ -15,6 +15,7 @@ import password from './password';
import stripe from './stripe';
import integration from './integration';
import integrationAuth from './integrationAuth';
import secretsFolder from './secretsFolder'
export {
signup,
@ -33,5 +34,6 @@ export {
password,
stripe,
integration,
integrationAuth
integrationAuth,
secretsFolder
};

@ -19,6 +19,7 @@ router.post(
router.post(
'/verify',
body('email').exists().trim().notEmpty(),
body('organizationId').exists().trim().notEmpty(),
body('code').exists().trim().notEmpty(),
validateRequest,
membershipOrgController.verifyUserToOrganization

@ -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 {
initDatabaseHelper,
closeDatabaseHelper

@ -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()
});
}

@ -3,8 +3,8 @@ import { createTerminus } from '@godaddy/terminus';
import { getLogger } from '../utils/logger';
export const setUpHealthEndpoint = <T>(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) {

@ -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}`
);
});

@ -9,7 +9,7 @@
<body>
<h2>Join your organization on Infisical</h2>
<p>{{inviterFirstName}} ({{inviterEmail}}) has invited you to their Infisical organization — {{organizationName}}</p>
<a href="{{callback_url}}?token={{token}}&to={{email}}">Join now</a>
<a href="{{callback_url}}?token={{token}}&to={{email}}&organization_id={{organizationId}}">Join now</a>
<h3>What is Infisical?</h3>
<p>Infisical is an easy-to-use end-to-end encrypted tool that enables developers to sync and manage their secrets and configs.</p>
</body>

@ -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,

@ -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);
}

@ -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]
}

@ -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',

@ -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: ''
},
{

@ -1,408 +1,408 @@
import request from 'supertest'
import main from '../../../../src/index'
import { testWorkspaceId } from '../../../../src/utils/addDevelopmentUser';
import { deleteAllSecrets, getAllSecrets, getJWTFromTestUser, getServiceTokenFromTestUser } from '../../../helper/helper';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const batchSecretRequestWithNoOverride = require('../../../data/batch-secrets-no-override.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const batchSecretRequestWithOverrides = require('../../../data/batch-secrets-with-overrides.json');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const batchSecretRequestWithBadRequest = require('../../../data/batch-create-secrets-with-some-missing-params.json');
let server: any;
beforeAll(async () => {
server = await main;
});
afterAll(async () => {
server.close();
});
describe("GET /api/v2/secrets", () => {
describe("Get secrets via JTW", () => {
test("should create secrets and read secrets via jwt", async () => {
try {
// get login details
const loginResponse = await getJWTFromTestUser()
// create creates
const createSecretsResponse = await request(server)
.post("/api/v2/secrets/batch")
.set('Authorization', `Bearer ${loginResponse.token}`)
.send({
workspaceId: testWorkspaceId,
environment: "dev",
requests: batchSecretRequestWithNoOverride
})
expect(createSecretsResponse.statusCode).toBe(200)
const getSecrets = await request(server)
.get("/api/v2/secrets")
.set('Authorization', `Bearer ${loginResponse.token}`)
.query({
workspaceId: testWorkspaceId,
environment: "dev"
})
expect(getSecrets.statusCode).toBe(200)
expect(getSecrets.body).toHaveProperty("secrets")
expect(getSecrets.body.secrets).toHaveLength(3)
expect(getSecrets.body.secrets).toBeInstanceOf(Array);
getSecrets.body.secrets.forEach((secret: any) => {
expect(secret).toHaveProperty('_id');
expect(secret._id).toBeTruthy();
expect(secret).toHaveProperty('version');
expect(secret.version).toBeTruthy();
expect(secret).toHaveProperty('workspace');
expect(secret.workspace).toBeTruthy();
expect(secret).toHaveProperty('type');
expect(secret.type).toBeTruthy();
expect(secret).toHaveProperty('tags');
expect(secret.tags).toHaveLength(0);
expect(secret).toHaveProperty('environment');
expect(secret.environment).toEqual("dev");
expect(secret).toHaveProperty('secretKeyCiphertext');
expect(secret.secretKeyCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretKeyIV');
expect(secret.secretKeyIV).toBeTruthy();
// import request from 'supertest'
// import main from '../../../../src/index'
// import { testWorkspaceId } from '../../../../src/utils/addDevelopmentUser';
// import { deleteAllSecrets, getAllSecrets, getJWTFromTestUser, getServiceTokenFromTestUser } from '../../../helper/helper';
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// const batchSecretRequestWithNoOverride = require('../../../data/batch-secrets-no-override.json');
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// const batchSecretRequestWithOverrides = require('../../../data/batch-secrets-with-overrides.json');
// // eslint-disable-next-line @typescript-eslint/no-var-requires
// const batchSecretRequestWithBadRequest = require('../../../data/batch-create-secrets-with-some-missing-params.json');
// let server: any;
// beforeAll(async () => {
// server = await main;
// });
// afterAll(async () => {
// server.close();
// });
// describe("GET /api/v2/secrets", () => {
// describe("Get secrets via JTW", () => {
// test("should create secrets and read secrets via jwt", async () => {
// try {
// // get login details
// const loginResponse = await getJWTFromTestUser()
// // create creates
// const createSecretsResponse = await request(server)
// .post("/api/v2/secrets/batch")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .send({
// workspaceId: testWorkspaceId,
// environment: "dev",
// requests: batchSecretRequestWithNoOverride
// })
// expect(createSecretsResponse.statusCode).toBe(200)
// const getSecrets = await request(server)
// .get("/api/v2/secrets")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .query({
// workspaceId: testWorkspaceId,
// environment: "dev"
// })
// expect(getSecrets.statusCode).toBe(200)
// expect(getSecrets.body).toHaveProperty("secrets")
// expect(getSecrets.body.secrets).toHaveLength(3)
// expect(getSecrets.body.secrets).toBeInstanceOf(Array);
// getSecrets.body.secrets.forEach((secret: any) => {
// expect(secret).toHaveProperty('_id');
// expect(secret._id).toBeTruthy();
// expect(secret).toHaveProperty('version');
// expect(secret.version).toBeTruthy();
// expect(secret).toHaveProperty('workspace');
// expect(secret.workspace).toBeTruthy();
// expect(secret).toHaveProperty('type');
// expect(secret.type).toBeTruthy();
// expect(secret).toHaveProperty('tags');
// expect(secret.tags).toHaveLength(0);
// expect(secret).toHaveProperty('environment');
// expect(secret.environment).toEqual("dev");
// expect(secret).toHaveProperty('secretKeyCiphertext');
// expect(secret.secretKeyCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyIV');
// expect(secret.secretKeyIV).toBeTruthy();
expect(secret).toHaveProperty('secretKeyTag');
expect(secret.secretKeyTag).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyTag');
// expect(secret.secretKeyTag).toBeTruthy();
expect(secret).toHaveProperty('secretValueCiphertext');
expect(secret.secretValueCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretValueCiphertext');
// expect(secret.secretValueCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretValueIV');
expect(secret.secretValueIV).toBeTruthy();
// expect(secret).toHaveProperty('secretValueIV');
// expect(secret.secretValueIV).toBeTruthy();
expect(secret).toHaveProperty('secretValueTag');
expect(secret.secretValueTag).toBeTruthy();
// expect(secret).toHaveProperty('secretValueTag');
// expect(secret.secretValueTag).toBeTruthy();
expect(secret).toHaveProperty('secretCommentCiphertext');
expect(secret.secretCommentCiphertext).toBeFalsy();
// expect(secret).toHaveProperty('secretCommentCiphertext');
// expect(secret.secretCommentCiphertext).toBeFalsy();
expect(secret).toHaveProperty('secretCommentIV');
expect(secret.secretCommentIV).toBeTruthy();
expect(secret).toHaveProperty('secretCommentTag');
expect(secret.secretCommentTag).toBeTruthy();
expect(secret).toHaveProperty('createdAt');
expect(secret.createdAt).toBeTruthy();
expect(secret).toHaveProperty('updatedAt');
expect(secret.updatedAt).toBeTruthy();
});
} finally {
// clean up
await deleteAllSecrets()
}
})
test("Get secrets via jwt when personal overrides exist", async () => {
try {
// get login details
const loginResponse = await getJWTFromTestUser()
// create creates
const createSecretsResponse = await request(server)
.post("/api/v2/secrets/batch")
.set('Authorization', `Bearer ${loginResponse.token}`)
.send({
workspaceId: testWorkspaceId,
environment: "dev",
requests: batchSecretRequestWithOverrides
})
expect(createSecretsResponse.statusCode).toBe(200)
const getSecrets = await request(server)
.get("/api/v2/secrets")
.set('Authorization', `Bearer ${loginResponse.token}`)
.query({
workspaceId: testWorkspaceId,
environment: "dev"
})
// expect(secret).toHaveProperty('secretCommentIV');
// expect(secret.secretCommentIV).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentTag');
// expect(secret.secretCommentTag).toBeTruthy();
// expect(secret).toHaveProperty('createdAt');
// expect(secret.createdAt).toBeTruthy();
// expect(secret).toHaveProperty('updatedAt');
// expect(secret.updatedAt).toBeTruthy();
// });
// } finally {
// // clean up
// await deleteAllSecrets()
// }
// })
// test("Get secrets via jwt when personal overrides exist", async () => {
// try {
// // get login details
// const loginResponse = await getJWTFromTestUser()
// // create creates
// const createSecretsResponse = await request(server)
// .post("/api/v2/secrets/batch")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .send({
// workspaceId: testWorkspaceId,
// environment: "dev",
// requests: batchSecretRequestWithOverrides
// })
// expect(createSecretsResponse.statusCode).toBe(200)
// const getSecrets = await request(server)
// .get("/api/v2/secrets")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .query({
// workspaceId: testWorkspaceId,
// environment: "dev"
// })
expect(getSecrets.statusCode).toBe(200)
expect(getSecrets.body).toHaveProperty("secrets")
expect(getSecrets.body.secrets).toHaveLength(2)
expect(getSecrets.body.secrets).toBeInstanceOf(Array);
getSecrets.body.secrets.forEach((secret: any) => {
expect(secret).toHaveProperty('_id');
expect(secret._id).toBeTruthy();
// expect(getSecrets.statusCode).toBe(200)
// expect(getSecrets.body).toHaveProperty("secrets")
// expect(getSecrets.body.secrets).toHaveLength(2)
// expect(getSecrets.body.secrets).toBeInstanceOf(Array);
// getSecrets.body.secrets.forEach((secret: any) => {
// expect(secret).toHaveProperty('_id');
// expect(secret._id).toBeTruthy();
expect(secret).toHaveProperty('version');
expect(secret.version).toBeTruthy();
// expect(secret).toHaveProperty('version');
// expect(secret.version).toBeTruthy();
expect(secret).toHaveProperty('workspace');
expect(secret.workspace).toBeTruthy();
// expect(secret).toHaveProperty('workspace');
// expect(secret.workspace).toBeTruthy();
expect(secret).toHaveProperty('type');
expect(secret.type).toBeTruthy();
// expect(secret).toHaveProperty('type');
// expect(secret.type).toBeTruthy();
expect(secret).toHaveProperty('tags');
expect(secret.tags).toHaveLength(0);
// expect(secret).toHaveProperty('tags');
// expect(secret.tags).toHaveLength(0);
expect(secret).toHaveProperty('environment');
expect(secret.environment).toEqual("dev");
// expect(secret).toHaveProperty('environment');
// expect(secret.environment).toEqual("dev");
expect(secret).toHaveProperty('secretKeyCiphertext');
expect(secret.secretKeyCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyCiphertext');
// expect(secret.secretKeyCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretKeyIV');
expect(secret.secretKeyIV).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyIV');
// expect(secret.secretKeyIV).toBeTruthy();
expect(secret).toHaveProperty('secretKeyTag');
expect(secret.secretKeyTag).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyTag');
// expect(secret.secretKeyTag).toBeTruthy();
expect(secret).toHaveProperty('secretValueCiphertext');
expect(secret.secretValueCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretValueCiphertext');
// expect(secret.secretValueCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretValueIV');
expect(secret.secretValueIV).toBeTruthy();
// expect(secret).toHaveProperty('secretValueIV');
// expect(secret.secretValueIV).toBeTruthy();
expect(secret).toHaveProperty('secretValueTag');
expect(secret.secretValueTag).toBeTruthy();
// expect(secret).toHaveProperty('secretValueTag');
// expect(secret.secretValueTag).toBeTruthy();
expect(secret).toHaveProperty('secretCommentCiphertext');
expect(secret.secretCommentCiphertext).toBeFalsy();
// expect(secret).toHaveProperty('secretCommentCiphertext');
// expect(secret.secretCommentCiphertext).toBeFalsy();
expect(secret).toHaveProperty('secretCommentIV');
expect(secret.secretCommentIV).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentIV');
// expect(secret.secretCommentIV).toBeTruthy();
expect(secret).toHaveProperty('secretCommentTag');
expect(secret.secretCommentTag).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentTag');
// expect(secret.secretCommentTag).toBeTruthy();
expect(secret).toHaveProperty('createdAt');
expect(secret.createdAt).toBeTruthy();
// expect(secret).toHaveProperty('createdAt');
// expect(secret.createdAt).toBeTruthy();
expect(secret).toHaveProperty('updatedAt');
expect(secret.updatedAt).toBeTruthy();
});
} finally {
// clean up
await deleteAllSecrets()
}
})
})
describe("fetch secrets via service token", () => {
test("Get secrets via jwt when personal overrides exist", async () => {
try {
// get login details
const loginResponse = await getJWTFromTestUser()
// create creates
const createSecretsResponse = await request(server)
.post("/api/v2/secrets/batch")
.set('Authorization', `Bearer ${loginResponse.token}`)
.send({
workspaceId: testWorkspaceId,
environment: "dev",
requests: batchSecretRequestWithOverrides
})
// expect(secret).toHaveProperty('updatedAt');
// expect(secret.updatedAt).toBeTruthy();
// });
// } finally {
// // clean up
// await deleteAllSecrets()
// }
// })
// })
// describe("fetch secrets via service token", () => {
// test("Get secrets via jwt when personal overrides exist", async () => {
// try {
// // get login details
// const loginResponse = await getJWTFromTestUser()
// // create creates
// const createSecretsResponse = await request(server)
// .post("/api/v2/secrets/batch")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .send({
// workspaceId: testWorkspaceId,
// environment: "dev",
// requests: batchSecretRequestWithOverrides
// })
expect(createSecretsResponse.statusCode).toBe(200)
// now use the service token to fetch secrets
const serviceToken = await getServiceTokenFromTestUser()
// expect(createSecretsResponse.statusCode).toBe(200)
// // now use the service token to fetch secrets
// const serviceToken = await getServiceTokenFromTestUser()
const getSecrets = await request(server)
.get("/api/v2/secrets")
.set('Authorization', `Bearer ${serviceToken}`)
.query({
workspaceId: testWorkspaceId,
environment: "dev"
})
expect(getSecrets.statusCode).toBe(200)
expect(getSecrets.body).toHaveProperty("secrets")
expect(getSecrets.body.secrets).toHaveLength(2)
expect(getSecrets.body.secrets).toBeInstanceOf(Array);
// const getSecrets = await request(server)
// .get("/api/v2/secrets")
// .set('Authorization', `Bearer ${serviceToken}`)
// .query({
// workspaceId: testWorkspaceId,
// environment: "dev"
// })
// expect(getSecrets.statusCode).toBe(200)
// expect(getSecrets.body).toHaveProperty("secrets")
// expect(getSecrets.body.secrets).toHaveLength(2)
// expect(getSecrets.body.secrets).toBeInstanceOf(Array);
getSecrets.body.secrets.forEach((secret: any) => {
expect(secret).toHaveProperty('_id');
expect(secret._id).toBeTruthy();
// getSecrets.body.secrets.forEach((secret: any) => {
// expect(secret).toHaveProperty('_id');
// expect(secret._id).toBeTruthy();
expect(secret).toHaveProperty('version');
expect(secret.version).toBeTruthy();
// expect(secret).toHaveProperty('version');
// expect(secret.version).toBeTruthy();
expect(secret).toHaveProperty('workspace');
expect(secret.workspace).toBeTruthy();
// expect(secret).toHaveProperty('workspace');
// expect(secret.workspace).toBeTruthy();
expect(secret).toHaveProperty('type');
expect(secret.type).toBeTruthy();
// expect(secret).toHaveProperty('type');
// expect(secret.type).toBeTruthy();
expect(secret).toHaveProperty('tags');
expect(secret.tags).toHaveLength(0);
// expect(secret).toHaveProperty('tags');
// expect(secret.tags).toHaveLength(0);
expect(secret).toHaveProperty('environment');
expect(secret.environment).toEqual("dev");
// expect(secret).toHaveProperty('environment');
// expect(secret.environment).toEqual("dev");
expect(secret).toHaveProperty('secretKeyCiphertext');
expect(secret.secretKeyCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyCiphertext');
// expect(secret.secretKeyCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretKeyIV');
expect(secret.secretKeyIV).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyIV');
// expect(secret.secretKeyIV).toBeTruthy();
expect(secret).toHaveProperty('secretKeyTag');
expect(secret.secretKeyTag).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyTag');
// expect(secret.secretKeyTag).toBeTruthy();
expect(secret).toHaveProperty('secretValueCiphertext');
expect(secret.secretValueCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretValueCiphertext');
// expect(secret.secretValueCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretValueIV');
expect(secret.secretValueIV).toBeTruthy();
// expect(secret).toHaveProperty('secretValueIV');
// expect(secret.secretValueIV).toBeTruthy();
expect(secret).toHaveProperty('secretValueTag');
expect(secret.secretValueTag).toBeTruthy();
// expect(secret).toHaveProperty('secretValueTag');
// expect(secret.secretValueTag).toBeTruthy();
expect(secret).toHaveProperty('secretCommentCiphertext');
expect(secret.secretCommentCiphertext).toBeFalsy();
// expect(secret).toHaveProperty('secretCommentCiphertext');
// expect(secret.secretCommentCiphertext).toBeFalsy();
expect(secret).toHaveProperty('secretCommentIV');
expect(secret.secretCommentIV).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentIV');
// expect(secret.secretCommentIV).toBeTruthy();
expect(secret).toHaveProperty('secretCommentTag');
expect(secret.secretCommentTag).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentTag');
// expect(secret.secretCommentTag).toBeTruthy();
expect(secret).toHaveProperty('createdAt');
expect(secret.createdAt).toBeTruthy();
// expect(secret).toHaveProperty('createdAt');
// expect(secret.createdAt).toBeTruthy();
expect(secret).toHaveProperty('updatedAt');
expect(secret.updatedAt).toBeTruthy();
});
} finally {
// clean up
await deleteAllSecrets()
}
})
test("should create secrets and read secrets via service token when no overrides", async () => {
try {
// get login details
const loginResponse = await getJWTFromTestUser()
// create secrets
const createSecretsResponse = await request(server)
.post("/api/v2/secrets/batch")
.set('Authorization', `Bearer ${loginResponse.token}`)
.send({
workspaceId: testWorkspaceId,
environment: "dev",
requests: batchSecretRequestWithNoOverride
})
expect(createSecretsResponse.statusCode).toBe(200)
// expect(secret).toHaveProperty('updatedAt');
// expect(secret.updatedAt).toBeTruthy();
// });
// } finally {
// // clean up
// await deleteAllSecrets()
// }
// })
// test("should create secrets and read secrets via service token when no overrides", async () => {
// try {
// // get login details
// const loginResponse = await getJWTFromTestUser()
// // create secrets
// const createSecretsResponse = await request(server)
// .post("/api/v2/secrets/batch")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .send({
// workspaceId: testWorkspaceId,
// environment: "dev",
// requests: batchSecretRequestWithNoOverride
// })
// expect(createSecretsResponse.statusCode).toBe(200)
// now use the service token to fetch secrets
const serviceToken = await getServiceTokenFromTestUser()
// // now use the service token to fetch secrets
// const serviceToken = await getServiceTokenFromTestUser()
const getSecrets = await request(server)
.get("/api/v2/secrets")
.set('Authorization', `Bearer ${serviceToken}`)
.query({
workspaceId: testWorkspaceId,
environment: "dev"
})
// const getSecrets = await request(server)
// .get("/api/v2/secrets")
// .set('Authorization', `Bearer ${serviceToken}`)
// .query({
// workspaceId: testWorkspaceId,
// environment: "dev"
// })
expect(getSecrets.statusCode).toBe(200)
expect(getSecrets.body).toHaveProperty("secrets")
expect(getSecrets.body.secrets).toHaveLength(3)
expect(getSecrets.body.secrets).toBeInstanceOf(Array);
// expect(getSecrets.statusCode).toBe(200)
// expect(getSecrets.body).toHaveProperty("secrets")
// expect(getSecrets.body.secrets).toHaveLength(3)
// expect(getSecrets.body.secrets).toBeInstanceOf(Array);
getSecrets.body.secrets.forEach((secret: any) => {
expect(secret).toHaveProperty('_id');
expect(secret._id).toBeTruthy();
// getSecrets.body.secrets.forEach((secret: any) => {
// expect(secret).toHaveProperty('_id');
// expect(secret._id).toBeTruthy();
expect(secret).toHaveProperty('version');
expect(secret.version).toBeTruthy();
// expect(secret).toHaveProperty('version');
// expect(secret.version).toBeTruthy();
expect(secret).toHaveProperty('workspace');
expect(secret.workspace).toBeTruthy();
// expect(secret).toHaveProperty('workspace');
// expect(secret.workspace).toBeTruthy();
expect(secret).toHaveProperty('type');
expect(secret.type).toBeTruthy();
// expect(secret).toHaveProperty('type');
// expect(secret.type).toBeTruthy();
expect(secret).toHaveProperty('tags');
expect(secret.tags).toHaveLength(0);
// expect(secret).toHaveProperty('tags');
// expect(secret.tags).toHaveLength(0);
expect(secret).toHaveProperty('environment');
expect(secret.environment).toEqual("dev");
// expect(secret).toHaveProperty('environment');
// expect(secret.environment).toEqual("dev");
expect(secret).toHaveProperty('secretKeyCiphertext');
expect(secret.secretKeyCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyCiphertext');
// expect(secret.secretKeyCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretKeyIV');
expect(secret.secretKeyIV).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyIV');
// expect(secret.secretKeyIV).toBeTruthy();
expect(secret).toHaveProperty('secretKeyTag');
expect(secret.secretKeyTag).toBeTruthy();
// expect(secret).toHaveProperty('secretKeyTag');
// expect(secret.secretKeyTag).toBeTruthy();
expect(secret).toHaveProperty('secretValueCiphertext');
expect(secret.secretValueCiphertext).toBeTruthy();
// expect(secret).toHaveProperty('secretValueCiphertext');
// expect(secret.secretValueCiphertext).toBeTruthy();
expect(secret).toHaveProperty('secretValueIV');
expect(secret.secretValueIV).toBeTruthy();
expect(secret).toHaveProperty('secretValueTag');
expect(secret.secretValueTag).toBeTruthy();
expect(secret).toHaveProperty('secretCommentCiphertext');
expect(secret.secretCommentCiphertext).toBeFalsy();
expect(secret).toHaveProperty('secretCommentIV');
expect(secret.secretCommentIV).toBeTruthy();
expect(secret).toHaveProperty('secretCommentTag');
expect(secret.secretCommentTag).toBeTruthy();
expect(secret).toHaveProperty('createdAt');
expect(secret.createdAt).toBeTruthy();
expect(secret).toHaveProperty('updatedAt');
expect(secret.updatedAt).toBeTruthy();
});
} finally {
// clean up
await deleteAllSecrets()
}
})
})
describe("create secrets via JWT", () => {
test("Create secrets via jwt when some requests have missing required parameters", async () => {
// get login details
const loginResponse = await getJWTFromTestUser()
// create creates
const createSecretsResponse = await request(server)
.post("/api/v2/secrets/batch")
.set('Authorization', `Bearer ${loginResponse.token}`)
.send({
workspaceId: testWorkspaceId,
environment: "dev",
requests: batchSecretRequestWithBadRequest
})
const allSecretsInDB = await getAllSecrets()
// expect(secret).toHaveProperty('secretValueIV');
// expect(secret.secretValueIV).toBeTruthy();
// expect(secret).toHaveProperty('secretValueTag');
// expect(secret.secretValueTag).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentCiphertext');
// expect(secret.secretCommentCiphertext).toBeFalsy();
// expect(secret).toHaveProperty('secretCommentIV');
// expect(secret.secretCommentIV).toBeTruthy();
// expect(secret).toHaveProperty('secretCommentTag');
// expect(secret.secretCommentTag).toBeTruthy();
// expect(secret).toHaveProperty('createdAt');
// expect(secret.createdAt).toBeTruthy();
// expect(secret).toHaveProperty('updatedAt');
// expect(secret.updatedAt).toBeTruthy();
// });
// } finally {
// // clean up
// await deleteAllSecrets()
// }
// })
// })
// describe("create secrets via JWT", () => {
// test("Create secrets via jwt when some requests have missing required parameters", async () => {
// // get login details
// const loginResponse = await getJWTFromTestUser()
// // create creates
// const createSecretsResponse = await request(server)
// .post("/api/v2/secrets/batch")
// .set('Authorization', `Bearer ${loginResponse.token}`)
// .send({
// workspaceId: testWorkspaceId,
// environment: "dev",
// requests: batchSecretRequestWithBadRequest
// })
// const allSecretsInDB = await getAllSecrets()
expect(createSecretsResponse.statusCode).toBe(500) // TODO should be set to 400
expect(allSecretsInDB).toHaveLength(0)
})
})
})
// expect(createSecretsResponse.statusCode).toBe(500) // TODO should be set to 400
// expect(allSecretsInDB).toHaveLength(0)
// })
// })
// })

@ -49,6 +49,8 @@ These examples demonstrate how to store and fetch environment variables from [In
<Tab title="SDK">
[Infisical SDKs](/sdks/overview) let your app fetch back secrets using an [Infisical Token](/getting-started/dashboard/token) that is scoped to a project and environment in Infisical. In this example, we demonstrate how to use the [Node SDK](/sdks/languages/node).
Using Python? We have a [Python SDK](/sdks/languages/python) as well.
### Obtain an [Infisical Token](/getting-started/dashboard/token)
Head to your project settings to create a token scoped to the project and environment you wish to fetch secrets from.
@ -64,7 +66,9 @@ These examples demonstrate how to store and fetch environment variables from [In
### Initialize the Infisical client
```js
await infisical.connect({
import InfisicalClient from "infisical-node";
const client = new InfisicalClient({
token: "your_infisical_token",
});
```
@ -72,31 +76,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 client.getSecret("SOME_KEY");
```
### Example with Express
```js
const express = require("express");
const port = 3000;
const infisical = require("infisical-node");
import InfisicalClient from "infisical-node";
import express from "express";
const app = express();
const PORT = 3000;
const main = async () => {
await infisical.connect({
token: "st.xxx.xxx",
});
const client = InfisicalClient({
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 client.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}`);
});
```
<Warning>

@ -72,6 +72,17 @@ spec:
` https://your-self-hosted-instace.com/api`
When `hostAPI` is not defined the operator fetches secrets from Infisical Cloud.
<Accordion title="Advanced use case">
If you have installed your Infisical instance within the same cluster as the Infisical operator, you can optionally access the Infisical backend's service directly without having to route through the public internet.
To achieve this, use the following address for the hostAPI field:
``` bash
http://<backend-svc-name>.<namespace>.svc.cluster.local:4000/api
```
Make sure to replace `<backend-svc-name>` and `<namespace>` with the appropriate values for your backend service and namespace.
</Accordion>
</Accordion>
<Accordion title="authentication">

@ -1,5 +1,6 @@
---
title: "Go"
icon: "golang"
---
Coming soon.

@ -1,5 +1,6 @@
---
title: "Java"
icon: "java"
---
Coming soon.

@ -1,154 +1,212 @@
---
title: "Node"
icon: "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.
## Installation
Run `npm` to add `infisical-node` to your project.
```bash
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)`.
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>
<Tab title="ES6">
```js
import infisical from "infisical-node";
const main = async () => {
await infisical.connect({
token: "your_infisical_token",
});
// your app logic
}
main();
```
</Tab>
<Tab title="ES5">
```js
const infisical = require("infisical-node");
infisical.connect({
token: "your_infisical_token"
})
.then(() => {
// your application logic
})
.catch(err => {
console.error('Error: ', err);
})
````
</Tab>
</Tabs>
## Usage
To get the value of a secret, use `infisical.get(key)`.
### infisical.get(key)
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.
<ResponseField name="key" type="string" required>
The key of the secret
</ResponseField>
## Basic Usage
```js
const value = infisical.get("SOME_KEY");
import express from "express";
import InfisicalClient from "infisical-node";
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}`);
});
```
## Example with Express
```js
const express = require("express");
const port = 3000;
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}`);
});
};
```
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
Run `npm` to add `infisical-node` to your project.
```console
$ npm install infisical-node --save
```
## Configuration
Import the SDK and create a client instance with your Infisical token.
<Tabs>
<Tab title="ES6">
```js
import InfisicalClient from "infisical-node";
const client = new InfisicalClient({
token: "your_infisical_token"
});
// your app logic
```
</Tab>
<Tab title="ES5">
```js
const InfisicalClient = require("infisical-node");
const client = new InfisicalClient({
token: "your_infisical_token"
});
// your app logic
````
</Tab>
</Tabs>
### Parameters
<ParamField query="options" type="object">
<Expandable title="properties">
<ParamField query="token" type="string" optional>
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
and environment
</ParamField>
<ParamField
query="siteURL"
type="string"
default="https://app.infisical.com"
optional
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ParamField>
<ParamField query="cacheTTL" type="number" default="300" optional>
Time-to-live (in seconds) for refreshing cached secrets. Default: `300`.
</ParamField>
<ParamField query="debug" type="boolean" default="false" optional>
Whether or not debug mode is on
</ParamField>
</Expandable>
</ParamField>
## Caching
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.
<Tip>
For optimal performance, we recommend creating a single instance of the Infisical client and exporting it to be used across your entire app to take advantage of caching benefits.
</Tip>
## Working with Secrets
### client.getAllSecrets()
```js
const secrets = await client.getAllSecrets();
```
Retrieve all secrets within the Infisical project and environment that client is connected to
### client.getSecret(secretName, options)
```js
const secret = await client.getSecret("API_KEY");
const value = secret.secretValue; // get its value
```
Retrieve a secret from Infisical.
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.
### Parameters
<ParamField query="secretName" type="string" required>
The key of the secret to retrieve
</ParamField>
<ParamField query="options" type="object" optional>
<Expandable title="properties">
<ParamField query="type" type="string" default="personal" optional>
The type of the secret. Valid options are "shared" or "personal"
</ParamField>
</Expandable>
</ParamField>
### client.createSecret(secretName, secretValue, options)
```js
const newApiKey = await client.createSecret("API_KEY", "FOO");
```
Create a new secret in Infisical.
<ParamField query="secretName" type="string" required>
The key of the secret to create
</ParamField>
<ParamField query="secretName" type="string" required>
The value of the secret to create
</ParamField>
<ParamField query="options" type="object" default="object" optional>
<Expandable title="properties">
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal". A personal secret can only be created if a shared secret with the same name exists.
</ParamField>
</Expandable>
</ParamField>
### client.updateSecret(secretName, secretValue, options)
```js
const updatedApiKey = await client.updateSecret("API_KEY", "BAR");
```
Update an existing secret in Infisical.
### Parameters
<ParamField query="secretName" type="string" required>
The key of the secret to update
</ParamField>
<ParamField query="secretName" type="string" required>
The new value of the secret
</ParamField>
<ParamField query="options" type="object" default="object" optional>
<Expandable title="properties">
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal"
</ParamField>
</Expandable>
</ParamField>
### client.deleteSecret(secretName, options)
```js
const deletedSecret = await client.deleteSecret("API_KEY");
```
Delete a secret in Infisical.
<ParamField query="secretName" type="string" required>
The key of the secret to delete
</ParamField>
<ParamField query="options" type="object" default="object" optional>
<Expandable title="properties">
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal". Note that deleting a shared secret also deletes all associated personal secrets.
</ParamField>
</Expandable>
</ParamField>

@ -1,8 +1,169 @@
---
title: "Python"
icon: "python"
---
Coming soon.
If you're working with Python, the official [infisical-python](https://github.com/Infisical/infisical-python) package is the easiest way to fetch and work with secrets for your application.
## Basic Usage
```py
from flask import Flask
from infisical import InfisicalClient
app = Flask(__name__)
client = InfisicalClient(token="your_infisical_token")
@app.route("/")
def hello_world():
# access value
name = client.get_secret("NAME")
return f"Hello! My name is: {name.secret_value}"
```
This example demonstrates how to use the Infisical Python SDK with a Flask 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
Run `pip` to add `infisical-python` to your project
```console
$ pip install infisical
```
Note: You need Python 3.7+.
## Configuration
Import the SDK and create a client instance with your Infisical token.
```py
from infisical import InfisicalClient
client = InfisicalClient(token="your_infisical_token")
```
### Parameters
<ParamField query="token" type="string" optional>
An [Infisical Token](/getting-started/dashboard/token) scoped to a project
and environment
</ParamField>
<ParamField
query="site_url"
type="string"
default="https://app.infisical.com"
optional
>
Your self-hosted absolute site URL including the protocol (e.g.
`https://app.infisical.com`)
</ParamField>
<ParamField query="cache_ttl" type="number" default="300" optional>
Time-to-live (in seconds) for refreshing cached secrets. Default: `300`.
</ParamField>
<ParamField query="debug" type="boolean" default="false" optional>
Whether or not debug mode is on
</ParamField>
## Caching
The SDK caches every secret and updates it periodically based on the provided `cache_ttl`. For example, if `cache_ttl` 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.
<Tip>
For optimal performance, we recommend creating a single instance of the Infisical client and exporting it to be used across your entire app to take advantage of caching benefits.
</Tip>
## Working with Secrets
### client.get_all_secrets()
```py
secrets = client.get_all_secrets()
```
Retrieve all secrets within the Infisical project and environment that client is connected to
### client.get_secret(secret_name, options)
```py
secret = client.get_secret("API_KEY")
value = secret.secret_value # get its value
```
By default, `get_secret()` fetches and returns a personal secret. If not found, it returns a shared secret, or tries to retrieve the value from `os.environ`. If a secret is fetched, `get_secret()` 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.
### Parameters
<ParamField query="secret_name" type="string" required>
The key of the secret to retrieve
</ParamField>
<ParamField query="type" type="string" default="personal" optional>
The type of the secret. Valid options are "shared" or "personal"
</ParamField>
### client.create_secret(secret_name, secret_value, options)
```py
new_api_key = client.create_secret("API_KEY", "FOO");
```
Create a new secret in Infisical.
### Parameters
<ParamField query="secret_name" type="string" required>
The key of the secret to create
</ParamField>
<ParamField query="secret_value" type="string" required>
The value of the secret to create
</ParamField>
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal". A personal secret can only be created if a shared secret with the same name exists.
</ParamField>
### client.update_secret(secret_name, secret_value, options)
```py
updated_api_key = client.update_secret("API_KEY", "BAR");
```
Update an existing secret in Infisical.
### Parameters
<ParamField query="secret_name" type="string" required>
The key of the secret to update
</ParamField>
<ParamField query="secret_value" type="string" required>
The new value of the secret
</ParamField>
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal"
</ParamField>
### client.delete_secret(secret_name, options)
```py
deleted_secret = client.delete_secret("API_KEY");
```
Delete a secret in Infisical.
### Parameters
<ParamField query="secret_name" type="string" required>
The key of the secret to delete
</ParamField>
<ParamField query="type" type="string" default="shared" optional>
The type of the secret. Valid options are "shared" or "personal"
</ParamField>
Follow this GitHub
[issue](https://github.com/Infisical/infisical/issues/433) to stay updated.

@ -1,5 +1,6 @@
---
title: "Ruby"
icon: "gem"
---
Coming soon.

@ -1,5 +1,6 @@
---
title: "Rust"
icon: "rust"
---
Coming soon.

@ -3,12 +3,9 @@ title: "Overview"
description: "How to use Infisical SDKs to fetch back secrets for your app"
---
Infisical SDKs provide the easiest way for your app to fetch back secrets using an [Infisical Token](/getting-started/dashboard/token) and has a few benefits:
Whether it be for local development or production, Infisical SDKs provide the easiest way for your app to fetch back secrets using an [Infisical Token](/getting-started/dashboard/token).
- Local development: Replace 10s of environment variables in your `.env` file with 1 environment variable (the [Infisical Token](/getting-started/dashboard/token)).
- Production: Fetch secrets back to any cloud regardless of if an integration exists between Infisical and the cloud platform.
We currently only have the [Node SDK](/sdks/languages/node) available but more language SDKs are coming out soon:
We currently have the [Node SDK](/sdks/languages/node) and [Python SDK](/sdks/languages/python) available with more language SDKs coming out soon:
- [Node](/sdks/languages/node)
- [Python](/sdks/languages/python)

18677
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -51,6 +51,7 @@
"classnames": "^2.3.1",
"cookies": "^0.8.0",
"cva": "npm:class-variance-authority@^0.4.0",
"framer-motion": "^6.2.3",
"fs": "^0.0.1-security",
"gray-matter": "^4.0.3",
"http-proxy": "^1.18.1",
@ -58,6 +59,7 @@
"infisical-node": "^1.0.37",
"jspdf": "^2.5.1",
"jsrp": "^0.2.4",
"lottie-react": "^2.4.0",
"markdown-it": "^13.0.1",
"next": "^12.3.4",
"next-i18next": "^13.0.2",
@ -75,7 +77,7 @@
"react-redux": "^8.0.2",
"react-table": "^7.8.0",
"set-cookie-parser": "^2.5.1",
"sharp": "^0.31.2",
"sharp": "^0.32.0",
"styled-components": "^5.3.7",
"tailwind-merge": "^1.8.1",
"tweetnacl": "^1.0.3",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1,7 +1,4 @@
// eslint-disable-next-line import/no-extraneous-dependencies
import { faKey, faUser } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// eslint-disable-next-line import/no-extraneous-dependencies
import { Meta, StoryObj } from '@storybook/react';
import { Menu, MenuGroup, MenuItem } from './Menu';
@ -74,14 +71,14 @@ export const WithIcons: Story = {
render: (args) => (
<Menu {...args}>
<MenuGroup title="Group 1">
<MenuItem isDisabled icon={<FontAwesomeIcon icon={faKey} />}>
<MenuItem isDisabled icon="system-outline-90-lock-closed">
Secrets
</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faUser} />}>Members</MenuItem>
<MenuItem icon="system-outline-96-groups">Members</MenuItem>
</MenuGroup>
<MenuGroup title="Group 2">
<MenuItem icon={<FontAwesomeIcon icon={faKey} />}>Secrets</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faKey} />}>Members</MenuItem>
<MenuItem icon="system-outline-90-lock-closed">Secrets</MenuItem>
<MenuItem icon="system-outline-96-groups">Members</MenuItem>
</MenuGroup>
</Menu>
),
@ -93,12 +90,12 @@ export const WithDescription: Story = {
<Menu {...args}>
<MenuItem
isDisabled
icon={<FontAwesomeIcon icon={faKey} />}
icon="system-outline-90-lock-closed"
description="Some random description"
>
Secrets
</MenuItem>
<MenuItem icon={<FontAwesomeIcon icon={faUser} />} description="Some random description">
<MenuItem icon="system-outline-96-groups" description="Some random description">
Members
</MenuItem>
</Menu>

@ -1,4 +1,9 @@
import { ComponentPropsWithRef, ElementType, ReactNode, Ref } from 'react';
// @ts-nocheck
/* eslint-disable import/no-extraneous-dependencies */
/* eslint-disable global-require */
import { ComponentPropsWithRef, ElementType, ReactNode, Ref, useRef } from 'react';
import { motion } from 'framer-motion'
import Lottie from 'lottie-react'
import { twMerge } from 'tailwind-merge';
export type MenuProps = {
@ -14,7 +19,7 @@ export type MenuItemProps<T extends ElementType> = {
// Kudos to https://itnext.io/react-polymorphic-components-with-typescript-f7ce72ea7af2
as?: T;
children: ReactNode;
icon?: ReactNode;
icon?: string;
description?: ReactNode;
isDisabled?: boolean;
isSelected?: boolean;
@ -33,23 +38,43 @@ export const MenuItem = <T extends ElementType = 'button'>({
// wrapping in forward ref with generic component causes the loss of ts definitions on props
inputRef,
...props
}: MenuItemProps<T> & ComponentPropsWithRef<T>): JSX.Element => (
<li
className={twMerge(
'px-1 py-2.5 mt-0.5 font-inter flex flex-col text-sm text-bunker-100 transition-all rounded cursor-pointer hover:bg-mineshaft-600 duration-50',
isSelected && 'bg-mineshaft-500',
isDisabled && 'hover:bg-transparent cursor-not-allowed',
className
)}
>
<Item type="button" role="menuitem" className="flex items-center relative" ref={inputRef} {...props}>
<div className={`${isSelected ? "visisble" : "invisible"} absolute w-[0.25rem] rounded-md h-8 bg-primary`}/>
{icon && <span className="mr-3 ml-4 w-5">{icon}</span>}
<span className="flex-grow text-left">{children}</span>
</Item>
{description && <span className="mt-2 text-xs">{description}</span>}
</li>
);
}: MenuItemProps<T> & ComponentPropsWithRef<T>): JSX.Element => {
const iconRef = useRef()
return(
<a
onMouseEnter={() => iconRef.current?.play()}
onMouseLeave={() => iconRef.current?.stop()}
>
<li
className={twMerge(
'group px-1 py-2.5 mt-0.5 font-inter flex flex-col text-sm text-bunker-100 transition-all rounded cursor-pointer hover:bg-mineshaft-700 duration-50',
isSelected && 'bg-mineshaft-600 hover:bg-mineshaft-600',
isDisabled && 'hover:bg-transparent cursor-not-allowed',
className
)}
>
<motion.span className="w-full flex flex-row items-center justify-start rounded-sm">
<Item type="button" role="menuitem" className="flex items-center relative" ref={inputRef} {...props}>
<div className={`${isSelected ? "visisble" : "invisible"} absolute w-[0.25rem] rounded-md h-8 bg-primary`}/>
{/* {icon && <span className="mr-3 ml-4 w-5 block group-hover:hidden">{icon}</span>} */}
<Lottie
lottieRef={iconRef}
style={{ width: 24, height: 24 }}
// eslint-disable-next-line import/no-dynamic-require
animationData={require(`../../../../public/lotties/${icon}.json`)}
loop={false}
autoplay={false}
className="my-auto ml-3 mr-3"
/>
<span className="flex-grow text-left">{children}</span>
</Item>
{description && <span className="mt-2 text-xs">{description}</span>}
</motion.span>
</li>
</a>
)
};
MenuItem.displayName = 'MenuItem';

@ -10,13 +10,8 @@ import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import {
faBookOpen,
faFileLines,
faGear,
faKey,
faMobile,
faPlug,
faPlus,
faUser
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { yupResolver } from '@hookform/resolvers/yup';
@ -248,7 +243,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
<div className="hidden h-screen w-full flex-col overflow-x-hidden md:flex dark">
<Navbar />
<div className="flex flex-grow flex-col overflow-y-hidden md:flex-row">
<aside className="w-full border-r border-mineshaft-500 bg-mineshaft-900 md:w-60">
<aside className="w-full border-r border-mineshaft-600 bg-gradient-to-tr from-mineshaft-700 via-mineshaft-800 to-mineshaft-900 md:w-60">
<nav className="items-between flex h-full flex-col justify-between">
<div>
{currentWorkspace ? (
@ -309,7 +304,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
<a>
<MenuItem
isSelected={router.asPath.includes(`/dashboard/${currentWorkspace?._id}`)}
icon={<FontAwesomeIcon icon={faKey} size="lg" />}
icon="system-outline-90-lock-closed"
>
{t('nav:menu.secrets')}
</MenuItem>
@ -319,7 +314,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
<a>
<MenuItem
isSelected={router.asPath === `/users/${currentWorkspace?._id}`}
icon={<FontAwesomeIcon icon={faUser} size="lg" />}
icon="system-outline-96-groups"
>
{t('nav:menu.members')}
</MenuItem>
@ -329,21 +324,20 @@ export const AppLayout = ({ children }: LayoutProps) => {
<a>
<MenuItem
isSelected={router.asPath === `/integrations/${currentWorkspace?._id}`}
icon={<FontAwesomeIcon icon={faPlug} size="lg" />}
icon="system-outline-82-extension"
>
{t('nav:menu.integrations')}
</MenuItem>
</a>
</Link>
<Link href={`/activity/${currentWorkspace?._id}`} passHref>
<a>
<MenuItem
isSelected={router.asPath === `/activity/${currentWorkspace?._id}`}
icon={<FontAwesomeIcon icon={faFileLines} size="lg" />}
// icon={<FontAwesomeIcon icon={faFileLines} size="lg" />}
icon="system-outline-168-view-headline"
>
Audit Logs
</MenuItem>
</a>
</Link>
<Link href={`/settings/project/${currentWorkspace?._id}`} passHref>
<a>
@ -351,7 +345,7 @@ export const AppLayout = ({ children }: LayoutProps) => {
isSelected={
router.asPath === `/settings/project/${currentWorkspace?._id}`
}
icon={<FontAwesomeIcon icon={faGear} size="lg" />}
icon="system-outline-109-slider-toggle-settings"
>
{t('nav:menu.project-settings')}
</MenuItem>

@ -1,22 +1,25 @@
interface Props {
email: string;
code: string;
organizationId: string;
}
/**
* This route verifies the signup invite link
* @param {object} obj
* @param {string} obj.email - email that a user is trying to verify
* @param {string} obj.organizationId - id of organization that a user is trying to verify for
* @param {string} obj.code - code that a user received to the abovementioned email
* @returns
*/
const verifySignupInvite = ({ email, code }: Props) => fetch('/api/v1/invite-org/verify', {
const verifySignupInvite = ({ email, organizationId, code }: Props) => fetch('/api/v1/invite-org/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
organizationId,
code
})
});

@ -803,8 +803,6 @@ export default function Dashboard() {
isReadDenied: false
};
console.log(124, envSlug, selectedWorkspaceEnv)
if (selectedWorkspaceEnv) {
if (snapshotData) setSelectedSnapshotEnv(selectedWorkspaceEnv);
else setSelectedEnv(selectedWorkspaceEnv);

@ -51,6 +51,7 @@ export default function SignupInvite() {
const router = useRouter();
const parsedUrl = queryString.parse(router.asPath.split('?')[1]);
const token = parsedUrl.token as string;
const organizationId = parsedUrl.organization_id as string;
const email = (parsedUrl.to as string)?.replace(' ', '+').trim();
// Verifies if the information that the users entered (name, workspace) is there, and if the password matched the criteria.
@ -190,7 +191,8 @@ export default function SignupInvite() {
onButtonPressed={async () => {
const response = await verifySignupInvite({
email,
code: token
code: token,
organizationId
});
if (response.status === 200) {
const res = await response.json();

@ -125,7 +125,7 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
if (isSecretsLoading || isEnvListLoading) {
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" />
</div>
);
@ -234,14 +234,14 @@ export const DashboardEnvOverview = ({onEnvChange}: {onEnvChange: any;}) => {
</Button>
</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="flex flex-row justify-between items-center min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
<span className="text-transparent">0</span>
<button type="button" className='mr-2 text-transparent'>1</button>
</div>
{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
onClick={() => onEnvChange(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;
};
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">
<input
// {...register(`secrets.${index}.valueOverride`)}
@ -118,8 +118,8 @@ export const EnvComparisonRow = ({
<tr className="group min-w-full flex flex-row items-center hover:bg-bunker-700">
<td className="w-10 h-10 px-4 flex items-center justify-center border-none"><div className='text-center w-10 text-xs text-bunker-400'>{index + 1}</div></td>
<td className="flex flex-row justify-between items-center h-full min-w-[200px] lg:min-w-[220px] xl:min-w-[250px]">
<div className="flex flex-row items-center h-8 cursor-default">{secrets![0].key || ''}</div>
<button type="button" className='mr-2 text-bunker-400 hover:text-bunker-300 invisible group-hover:visible' onClick={() => setAreValuesHiddenThisRow(!areValuesHiddenThisRow)}>
<div className="flex truncate flex-row items-center h-8 cursor-default">{secrets![0].key || ''}</div>
<button type="button" className='mr-1 ml-2 text-bunker-400 hover:text-bunker-300 invisible group-hover:visible' onClick={() => setAreValuesHiddenThisRow(!areValuesHiddenThisRow)}>
<FontAwesomeIcon icon={areValuesHiddenThisRow ? faEye : faEyeSlash} />
</button>
</td>

@ -60,13 +60,13 @@
$ kubectl get all -n {{ .Release.Namespace }}
→ Get your release status
$ helm status {{ .Release.Namespace }} {{ .Release.Name }}
$ helm status -n {{ .Release.Namespace }} {{ .Release.Name }}
→ Get your release resources
$ helm get all {{ .Release.Namespace }} {{ .Release.Name }}
$ helm get all -n {{ .Release.Namespace }} {{ .Release.Name }}
→ Uninstall your release
$ helm uninstall {{ .Release.Namespace }} {{ .Release.Name }}
$ helm uninstall -n {{ .Release.Namespace }} {{ .Release.Name }}
→ Get MongoDB root password
$ kubectl get secret -n {{ .Release.Namespace }} mongodb
@ -82,4 +82,4 @@ $ kubectl get secrets/<your-secret-name> -n {{ .Release.Namespace }} \
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――┤
##
##