mirror of
https://github.com/Infisical/infisical.git
synced 2025-04-15 15:36:18 +00:00
Compare commits
88 Commits
v0.4.2
...
infisical-
Author | SHA1 | Date | |
---|---|---|---|
efd13e6b19 | |||
4ac74e6e9a | |||
1d422fa82c | |||
8ba3f8d1f7 | |||
6b83393952 | |||
da07d71e15 | |||
82d3971d9e | |||
3dd21374e7 | |||
c5fe41ae57 | |||
9f0313f50b | |||
a6e670e93a | |||
ec97e1a930 | |||
55ca6938db | |||
1401c7f6bc | |||
bb6d0fd7c6 | |||
689a20dca2 | |||
e4b4126971 | |||
04b04cba5c | |||
89e5f644a4 | |||
c5619d27d7 | |||
12a1d8e822 | |||
a85a7d1b00 | |||
fc2846534f | |||
2b605856a3 | |||
191582ef26 | |||
213b5d465b | |||
75f550caf2 | |||
daabf5ab70 | |||
7b11976a60 | |||
39be52c6b2 | |||
bced5d0151 | |||
939d7eb433 | |||
6de25174aa | |||
2aa79d4ad6 | |||
44b4de754a | |||
db0f0d0d9c | |||
3471e387ae | |||
aadd964409 | |||
102e45891c | |||
b9ae224aef | |||
e5cb0cbca3 | |||
330968c7af | |||
68e8e727cd | |||
3b94ee42e9 | |||
09286b4421 | |||
04a9604ba9 | |||
d86f88db92 | |||
fc53c094b7 | |||
6726ca1882 | |||
ddbe4d7040 | |||
3f6b0a9e66 | |||
c3a47597b6 | |||
a696a99232 | |||
8b1e64f75e | |||
f137087ef1 | |||
2157fab181 | |||
d2acab57e0 | |||
811929987b | |||
4ac13f61e0 | |||
3d2b0fa3fc | |||
242809ce26 | |||
492bf39243 | |||
dbfa4f5277 | |||
3fd2e22cbd | |||
150eb1f5ee | |||
6314a949f8 | |||
660c5806e3 | |||
c6d2828262 | |||
8dedfad22d | |||
7a3456ca1d | |||
a946031d6f | |||
f0075e8d09 | |||
3b00df6662 | |||
a263d7481b | |||
6f91331549 | |||
13ecc22159 | |||
a5c5ec1f4d | |||
cbb28dc373 | |||
e00aad4159 | |||
fb8aaa9d9f | |||
e5c5e4cca2 | |||
803a97fdfc | |||
9e42a7a33e | |||
34c79b08bc | |||
aacdaf4556 | |||
a7484f8be5 | |||
e1bf31b371 | |||
3817831577 |
5
.github/workflows/release_build.yml
vendored
5
.github/workflows/release_build.yml
vendored
@ -4,7 +4,7 @@ on:
|
||||
push:
|
||||
# run only against tags
|
||||
tags:
|
||||
- "v*"
|
||||
- "infisical-cli/v*.*.*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@ -41,13 +41,14 @@ jobs:
|
||||
git clone https://github.com/plentico/osxcross-target.git ../../osxcross/target
|
||||
- uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
distribution: goreleaser
|
||||
distribution: goreleaser-pro
|
||||
version: latest
|
||||
args: release --clean
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GO_RELEASER_GITHUB_TOKEN }}
|
||||
FURY_TOKEN: ${{ secrets.FURYPUSHTOKEN }}
|
||||
AUR_KEY: ${{ secrets.AUR_KEY }}
|
||||
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
|
||||
- uses: actions/setup-python@v4
|
||||
- run: pip install --upgrade cloudsmith-cli
|
||||
- name: Publish to CloudSmith
|
||||
|
@ -11,6 +11,10 @@ before:
|
||||
- ./cli/scripts/completions.sh
|
||||
- ./cli/scripts/manpages.sh
|
||||
|
||||
monorepo:
|
||||
tag_prefix: infisical-cli/
|
||||
dir: cli
|
||||
|
||||
builds:
|
||||
- id: darwin-build
|
||||
binary: infisical
|
||||
@ -74,14 +78,7 @@ checksum:
|
||||
name_template: "checksums.txt"
|
||||
|
||||
snapshot:
|
||||
name_template: "{{ incpatch .Version }}-devel"
|
||||
|
||||
changelog:
|
||||
sort: asc
|
||||
filters:
|
||||
exclude:
|
||||
- "^docs:"
|
||||
- "^test:"
|
||||
name_template: "{{ .Version }}-devel"
|
||||
|
||||
# publishers:
|
||||
# - name: fury.io
|
||||
@ -101,12 +98,12 @@ brews:
|
||||
folder: Formula
|
||||
homepage: "https://infisical.com"
|
||||
description: "The official Infisical CLI"
|
||||
install: |-
|
||||
bin.install "infisical"
|
||||
bash_completion.install "completions/infisical.bash" => "infisical"
|
||||
zsh_completion.install "completions/infisical.zsh" => "_infisical"
|
||||
fish_completion.install "completions/infisical.fish"
|
||||
man1.install "manpages/infisical.1.gz"
|
||||
# install: |-
|
||||
# bin.install "infisical"
|
||||
# bash_completion.install "completions/infisical.bash" => "infisical"
|
||||
# zsh_completion.install "completions/infisical.zsh" => "_infisical"
|
||||
# fish_completion.install "completions/infisical.fish"
|
||||
# man1.install "manpages/infisical.1.gz"
|
||||
|
||||
nfpms:
|
||||
- id: infisical
|
||||
@ -124,15 +121,15 @@ nfpms:
|
||||
- apk
|
||||
- archlinux
|
||||
bindir: /usr/bin
|
||||
contents:
|
||||
- src: ./completions/infisical.bash
|
||||
dst: /etc/bash_completion.d/infisical
|
||||
- src: ./completions/infisical.fish
|
||||
dst: /usr/share/fish/vendor_completions.d/infisical.fish
|
||||
- src: ./completions/infisical.zsh
|
||||
dst: /usr/share/zsh/site-functions/_infisical
|
||||
- src: ./manpages/infisical.1.gz
|
||||
dst: /usr/share/man/man1/infisical.1.gz
|
||||
# contents:
|
||||
# - src: ./completions/infisical.bash
|
||||
# dst: /etc/bash_completion.d/infisical
|
||||
# - src: ./completions/infisical.fish
|
||||
# dst: /usr/share/fish/vendor_completions.d/infisical.fish
|
||||
# - src: ./completions/infisical.zsh
|
||||
# dst: /usr/share/zsh/site-functions/_infisical
|
||||
# - src: ./manpages/infisical.1.gz
|
||||
# dst: /usr/share/man/man1/infisical.1.gz
|
||||
|
||||
scoop:
|
||||
bucket:
|
||||
@ -159,15 +156,6 @@ aurs:
|
||||
install -Dm755 "./infisical" "${pkgdir}/usr/bin/infisical"
|
||||
# license
|
||||
install -Dm644 "./LICENSE" "${pkgdir}/usr/share/licenses/infisical/LICENSE"
|
||||
# completions
|
||||
mkdir -p "${pkgdir}/usr/share/bash-completion/completions/"
|
||||
mkdir -p "${pkgdir}/usr/share/zsh/site-functions/"
|
||||
mkdir -p "${pkgdir}/usr/share/fish/vendor_completions.d/"
|
||||
install -Dm644 "./completions/infisical.bash" "${pkgdir}/usr/share/bash-completion/completions/infisical"
|
||||
install -Dm644 "./completions/infisical.zsh" "${pkgdir}/usr/share/zsh/site-functions/infisical"
|
||||
install -Dm644 "./completions/infisical.fish" "${pkgdir}/usr/share/fish/vendor_completions.d/infisical.fish"
|
||||
# man pages
|
||||
install -Dm644 "./manpages/infisical.1.gz" "${pkgdir}/usr/share/man/man1/infisical.1.gz"
|
||||
|
||||
# dockers:
|
||||
# - dockerfile: cli/docker/Dockerfile
|
||||
|
10
SECURITY.md
10
SECURITY.md
@ -1,9 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
## Supported versions
|
||||
|
||||
We always recommend using the latest version of Infisical to ensure you get all security updates.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
## Reporting vulnerabilities
|
||||
|
||||
Please report security vulnerabilities or concerns to team@infisical.com.
|
||||
Please do not file GitHub issues or post on our public forum for security vulnerabilities, as they are public!
|
||||
|
||||
Infisical takes security issues very seriously. If you have any concerns about Infisical or believe you have uncovered a vulnerability, please get in touch via the e-mail address security@infisical.com. In the message, try to provide a description of the issue and ideally a way of reproducing it. The security team will get back to you as soon as possible.
|
||||
|
||||
Note that this security address should be used only for undisclosed vulnerabilities. Please report any security problems to us before disclosing it publicly.
|
1736
backend/package-lock.json
generated
1736
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.309.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')).secretValue;
|
||||
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;
|
||||
|
89
backend/src/controllers/v1/secretsFolderController.ts
Normal file
89
backend/src/controllers/v1/secretsFolderController.ts
Normal file
@ -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({
|
||||
|
36
backend/src/models/folder.ts
Normal file
36
backend/src/models/folder.ts
Normal file
@ -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
|
||||
|
40
backend/src/routes/v1/secretsFolder.ts
Normal file
40
backend/src/routes/v1/secretsFolder.ts
Normal file
@ -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,
|
||||
|
87
backend/src/utils/folder.ts
Normal file
87
backend/src/utils/folder.ts
Normal file
@ -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)
|
||||
// })
|
||||
// })
|
||||
// })
|
@ -4,12 +4,14 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
@ -73,7 +75,23 @@ func getLatestTag(repoOwner string, repoName string) (string, error) {
|
||||
return "", fmt.Errorf("failed to unmarshal github response: %w", err)
|
||||
}
|
||||
|
||||
return tags[0].Name[1:], nil
|
||||
// Filter tags with prefix "infisical-cli/"
|
||||
prefix := "infisical-cli/v"
|
||||
var validTags []string
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(tag.Name, prefix) {
|
||||
validTags = append(validTags, tag.Name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(validTags) == 0 {
|
||||
return "", errors.New("no tags for the CLI is found")
|
||||
}
|
||||
|
||||
// Extract the version from the first valid tag
|
||||
version := strings.TrimPrefix(validTags[0], prefix)
|
||||
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func GetUpdateInstructions() string {
|
||||
|
@ -5,11 +5,11 @@ description: "How to authenticate with the Infisical Public API"
|
||||
|
||||
## Essentials
|
||||
|
||||
The Public API accepts multiple modes of authentication being via API Key, Service Account credentials, or [Infisical Token](../../getting-started/dashboard/token).
|
||||
The Public API accepts multiple modes of authentication being via API Key, Service Account credentials, or [Infisical Token](/documentation/platform/token).
|
||||
|
||||
- API Key: Provides full access to all endpoints representing the user.
|
||||
- [Service Account](): Provides scoped access to an organization and select projects representing a machine such as a VM or application client.
|
||||
- [Infisical Token](../../getting-started/dashboard/token): Provides short-lived, scoped CRUD access to the secrets of a specific project and environment.
|
||||
- Service Account: Provides scoped access to an organization and select projects representing a machine such as a VM or application client.
|
||||
- [Infisical Token](/documentation/platform/token): Provides short-lived, scoped CRUD access to the secrets of a specific project and environment.
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="API Key">
|
||||
|
@ -6,7 +6,7 @@ description: "How to add a secret using an Infisical Token scoped to a project a
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment with write access enabled.
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
|
@ -6,7 +6,7 @@ description: "How to delete a secret using an Infisical Token scoped to a projec
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create either an [API Key](/api-reference/overview/authentication) or [Infisical Token](../../../getting-started/dashboard/token) for your project and environment with write access enabled.
|
||||
- Create either an [API Key](/api-reference/overview/authentication) or [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
|
@ -6,7 +6,7 @@ description: "How to get a secret using an Infisical Token scoped to a project a
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment.
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
|
@ -6,7 +6,7 @@ description: "How to get all secrets using an Infisical Token scoped to a projec
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment.
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
|
@ -6,7 +6,7 @@ description: "How to update a secret using an Infisical Token scoped to a projec
|
||||
Prerequisites:
|
||||
|
||||
- Set up and add envars to [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](../../../getting-started/dashboard/token) for your project and environment with write access enabled.
|
||||
- Create an [Infisical Token](/documentation/platform/token) for your project and environment with write access enabled.
|
||||
- Grasp a basic understanding of the system and its underlying cryptography [here](/api-reference/overview/introduction).
|
||||
- [Ensure that your project is blind-indexed](../blind-indices).
|
||||
|
||||
|
@ -37,7 +37,7 @@ Export environment variables from the platform into a file format.
|
||||
|
||||
### Environment variables
|
||||
<Accordion title="INFISICAL_TOKEN">
|
||||
Used to fetch secrets via a [service token](/getting-started/dashboard/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
Used to fetch secrets via a [service token](/documentation/platform/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
|
@ -42,7 +42,7 @@ Inject secrets from Infisical into your application process.
|
||||
|
||||
### Environment variables
|
||||
<Accordion title="INFISICAL_TOKEN">
|
||||
Used to fetch secrets via a [service token](/getting-started/dashboard/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
Used to fetch secrets via a [service token](/documentation/platform/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
@ -72,7 +72,7 @@ Inject secrets from Infisical into your application process.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="--token">
|
||||
If you are using a [service token](../../getting-started/dashboard/token) to authenticate, you can pass the token as a flag
|
||||
If you are using a [service token](/documentation/platform/token) to authenticate, you can pass the token as a flag
|
||||
|
||||
```bash
|
||||
# Example
|
||||
|
@ -20,7 +20,7 @@ This command enables you to perform CRUD (create, read, update, delete) operatio
|
||||
|
||||
### Environment variables
|
||||
<Accordion title="INFISICAL_TOKEN">
|
||||
Used to fetch secrets via a [service token](/getting-started/dashboard/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
Used to fetch secrets via a [service token](/documentation/platform/token) apposed to logged in credentials. Simply, export this variable in the terminal before running this command.
|
||||
|
||||
```bash
|
||||
# Example
|
||||
|
@ -3,7 +3,7 @@ title: "Infisical Token"
|
||||
description: "How to use Infical service token within the CLI."
|
||||
---
|
||||
|
||||
Prerequisite: [Infisical Token and How to Generate One](../../getting-started/dashboard/token).
|
||||
Prerequisite: [Infisical Token and How to Generate One](/documentation/platform/token).
|
||||
|
||||
It's possible to use the CLI to sync environment varialbes without manually entering login credentials by using a service token in the prerequisite link above.
|
||||
|
||||
|
@ -18,7 +18,7 @@ Prerequisite: [Install the CLI](/cli/overview)
|
||||
|
||||
<Tab title="Infisical Token">
|
||||
To use Infisical CLI in environments where you cannot run the `infisical login` command, you can authenticate via a
|
||||
Infisical Token instead. Learn more about [Infisical Token](../getting-started/dashboard/token).
|
||||
Infisical Token instead. Learn more about [Infisical Token](/documentation/platform/token).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
128
docs/documentation/getting-started/cli.mdx
Normal file
128
docs/documentation/getting-started/cli.mdx
Normal file
@ -0,0 +1,128 @@
|
||||
---
|
||||
title: "CLI"
|
||||
---
|
||||
|
||||
The Infisical CLI can be used to inject secrets into any framework like Next.js, Express, Django and more in local development.
|
||||
|
||||
It can also be used to expose secrets from Infisical as environment variables in CI/CD pipelines and [Docker containers](/documentation/getting-started/docker)
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
|
||||
## Installation
|
||||
|
||||
Follow the instructions for your operating system to install the Infisical CLI.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="MacOS">
|
||||
Use [brew](https://brew.sh/) package manager
|
||||
|
||||
```console
|
||||
$ brew install infisical/get-cli/infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Windows">
|
||||
Use [Scoop](https://scoop.sh/) package manager
|
||||
|
||||
```console
|
||||
$ scoop bucket add org https://github.com/Infisical/scoop-infisical.git
|
||||
```
|
||||
|
||||
```console
|
||||
$ scoop install infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Alpine">
|
||||
Install prerequisite
|
||||
```console
|
||||
$ apk add --no-cache bash sudo
|
||||
```
|
||||
|
||||
Add Infisical repository
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
||||
| bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ apk update && sudo apk add infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="RedHat/CentOs/Amazon">
|
||||
Add Infisical repository
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ sudo yum install infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Debian/Ubuntu">
|
||||
Add Infisical repository
|
||||
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ sudo apt-get update && sudo apt-get install -y infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Arch Linux">
|
||||
Use the `yay` package manager to install from the [Arch User Repository](https://aur.archlinux.org/packages/infisical-bin)
|
||||
|
||||
```console
|
||||
$ yay -S infisical-bin
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Login
|
||||
|
||||
Authenticate the CLI with the Infisical platform using your email and password.
|
||||
|
||||
```console
|
||||
$ infisical login
|
||||
```
|
||||
|
||||
## Initialization
|
||||
|
||||
Navigate to the root of your project directory and run the `init` command. This step connects your local project to the project on the Infisical platform and creates a `infisical.json` file containing a reference to that latter project.
|
||||
|
||||
```console
|
||||
$ infisical init
|
||||
```
|
||||
|
||||
## Start your app with environment variables injected
|
||||
|
||||
```console
|
||||
$ infisical run -- <your_application_start_command>
|
||||
```
|
||||
|
||||
## Example Start Commands
|
||||
|
||||
```console
|
||||
$ infisical run -- npm run dev
|
||||
$ infisical run -- flask run
|
||||
$ infisical run -- ./your_bash_script.sh
|
||||
```
|
||||
|
||||
Your app should now be running with the secrets from Infisical injected as environment variables.
|
||||
|
||||
See also:
|
||||
|
||||
- [Documentation for the Infisical CLI](/cli/overview)
|
185
docs/documentation/getting-started/docker.mdx
Normal file
185
docs/documentation/getting-started/docker.mdx
Normal file
@ -0,0 +1,185 @@
|
||||
---
|
||||
title: "Docker"
|
||||
---
|
||||
|
||||
The [Infisical CLI](/cli/overview) can be added to Dockerfiles to fetch secrets from Infisical and make them available as environment variables within containers at runtime.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical.
|
||||
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Docker">
|
||||
|
||||
## Dockerfile Modification
|
||||
|
||||
Follow the instruction for your specific Linux distrubtion to add the Infisical CLI to your Dockerfile.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Alpine">
|
||||
```dockerfile
|
||||
RUN apk add --no-cache bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \
|
||||
&& apk add infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="RedHat/CentOs/Amazon-linux">
|
||||
```dockerfile
|
||||
RUN curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' | sh \
|
||||
&& yum install -y infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Debian/Ubuntu">
|
||||
```dockerfile
|
||||
RUN apt-get update && apt-get install -y bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash \
|
||||
&& apt-get update && apt-get install -y infisical
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
Next, modify the start command of your Dockerfile:
|
||||
|
||||
```dockerfile
|
||||
CMD ["infisical", "run", "--", "[your service start command]"]
|
||||
```
|
||||
|
||||
## Launch
|
||||
|
||||
Spin up your container with the `docker run` command and feed in your Infisical Token.
|
||||
|
||||
```console
|
||||
docker run --env INFISICAL_TOKEN=<your_infisical_token> <DOCKER-IMAGE>
|
||||
```
|
||||
|
||||
Your containerized application should now be up and running with secrets from Infisical exposed as environment variables within your application's process.
|
||||
|
||||
## Example Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Select your base image (based on your Linux distribution, e.g., Alpine, Debian, Ubuntu, etc.)
|
||||
FROM alpine
|
||||
|
||||
# Add the Infisical CLI to your Dockerfile (choose the appropriate block based on your base image)
|
||||
RUN apk add --no-cache bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \
|
||||
&& apk add infisical
|
||||
|
||||
# Install any additional dependencies or packages your service requires
|
||||
# RUN <additional commands for your service>
|
||||
|
||||
# Copy your service files to the container
|
||||
COPY . /app
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Modify the start command of your Dockerfile
|
||||
CMD ["infisical", "run", "--", "npm run start"]
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Docker Compose">
|
||||
|
||||
## Dockerfile Modifications
|
||||
|
||||
Follow the instruction for your specific Linux distributions to add the Infisical CLI to your Dockerfiles.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Alpine">
|
||||
```dockerfile
|
||||
RUN apk add --no-cache bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \
|
||||
&& apk add infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="RedHat/CentOs/Amazon-linux">
|
||||
```dockerfile
|
||||
RUN curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' | sh \
|
||||
&& yum install -y infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Debian/Ubuntu">
|
||||
```dockerfile
|
||||
RUN apt-get update && apt-get install -y bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' | bash \
|
||||
&& apt-get update && apt-get install -y infisical
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
Next, modify the start commands of your Dockerfiles:
|
||||
|
||||
```dockerfile
|
||||
CMD ["infisical", "run", "--", "[your service start command]"]
|
||||
```
|
||||
|
||||
## Example Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Select your base image (based on your Linux distribution, e.g., Alpine, Debian, Ubuntu, etc.)
|
||||
FROM alpine
|
||||
|
||||
# Add the Infisical CLI to your Dockerfile (choose the appropriate block based on your base image)
|
||||
RUN apk add --no-cache bash curl && curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' | bash \
|
||||
&& apk add infisical
|
||||
|
||||
# Install any additional dependencies or packages your service requires
|
||||
# RUN <additional commands for your service>
|
||||
|
||||
# Copy your service files to the container
|
||||
COPY . /app
|
||||
|
||||
# Set the working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Modify the start command of your Dockerfile
|
||||
CMD ["infisical", "run", "--", "[your service start command]"]
|
||||
```
|
||||
|
||||
## Docker Compose File Modification
|
||||
|
||||
For each service you want to inject secrets into, set an environment variable called `INFISICAL_TOKEN` equal to a unique identifier variable. For example:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
api:
|
||||
build: .
|
||||
image: example-service-2
|
||||
environment:
|
||||
- INFISICAL_TOKEN=${INFISICAL_TOKEN_FOR_API}
|
||||
...
|
||||
```
|
||||
|
||||
## Export shell variables
|
||||
|
||||
Next, set the shell variables you defined in your compose file. Continuing from the previous example:
|
||||
|
||||
```console
|
||||
export INFISICAL_TOKEN_FOR_API=<your_infisical_token>
|
||||
```
|
||||
|
||||
## Launch
|
||||
|
||||
Spin up your containers with the `docker-compose up` command.
|
||||
|
||||
```console
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
Your containers should now be running with the secrets from Infisical available inside as environment variables.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
See also:
|
||||
|
||||
- [Documentation for Docker](/integrations/platforms/docker)
|
||||
- [Documentation for Docker Compose](/integrations/platforms/docker-compose)
|
90
docs/documentation/getting-started/introduction.mdx
Normal file
90
docs/documentation/getting-started/introduction.mdx
Normal file
@ -0,0 +1,90 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
---
|
||||
|
||||
Infisical is an [open-source](https://opensource.com/resources/what-open-source), [end-to-end encrypted](https://en.wikipedia.org/wiki/End-to-end_encryption) secret management platform that enables teams to easily manage and sync their environment variables.
|
||||
|
||||
Start syncing environment variables with [Infisical Cloud](https://app.infisical.com) or learn how to [host Infisical](/self-hosting/overview) yourself.
|
||||
|
||||
## Learn about Infisical
|
||||
|
||||
<Card
|
||||
href="/documentation/getting-started/platform"
|
||||
title="Platform"
|
||||
icon="laptop"
|
||||
color="#dc2626"
|
||||
>
|
||||
Store secrets like API keys, database credentials, environment variables with Infisical
|
||||
</Card>
|
||||
|
||||
## Integrate with Infisical
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="SDKs"
|
||||
href="/documentation/getting-started/sdks"
|
||||
icon="boxes-stacked"
|
||||
color="#3c8639"
|
||||
>
|
||||
Fetch secrets with any programming language on demand
|
||||
</Card>
|
||||
<Card href="/documentation/getting-started/sdks" title="Command Line Interface" icon="square-terminal" color="#3775a9">
|
||||
Inject secrets into any application process/environment
|
||||
</Card>
|
||||
<Card href="/documentation/getting-started/docker" title="Docker" icon="docker" color="#0078d3">
|
||||
Inject secrets into Docker containers
|
||||
</Card>
|
||||
<Card
|
||||
href="/documentation/getting-started/kubernetes"
|
||||
title="Kubernetes"
|
||||
icon="server"
|
||||
color="#3775a9"
|
||||
>
|
||||
Fetch and save secrets as native Kubernetes secrets
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
href="/self-hosting/overview"
|
||||
title="Self-hosting"
|
||||
icon="server"
|
||||
color="#0285c7"
|
||||
>
|
||||
Learn how to configure and deploy Infisical
|
||||
</Card>
|
||||
<Card
|
||||
href="/documentation/guides/introduction"
|
||||
title="Guide"
|
||||
icon="book-open"
|
||||
color="#dc2626"
|
||||
>
|
||||
Explore guides for every language and stack
|
||||
</Card>
|
||||
<Card
|
||||
href="/integrations/overview"
|
||||
title="Native Integrations"
|
||||
icon="clouds"
|
||||
color="#dc2626"
|
||||
>
|
||||
Explore integrations for GitHub, Vercel, Netlify, and more
|
||||
</Card>
|
||||
<Card
|
||||
href="/integrations/overview"
|
||||
title="Frameworks"
|
||||
icon="plug"
|
||||
color="#dc2626"
|
||||
>
|
||||
Explore integrations for Next.js, Express, Django, and more
|
||||
</Card>
|
||||
<Card
|
||||
href="https://calendly.com/team-infisical/infisical-demo"
|
||||
title="Contact Us"
|
||||
icon="user-headset"
|
||||
color="#0285c7"
|
||||
>
|
||||
Questions? Need help setting up? Book a 1x1 meeting with us
|
||||
</Card>
|
||||
</CardGroup>
|
83
docs/documentation/getting-started/kubernetes.mdx
Normal file
83
docs/documentation/getting-started/kubernetes.mdx
Normal file
@ -0,0 +1,83 @@
|
||||
---
|
||||
title: "Kubernetes"
|
||||
---
|
||||
|
||||
The Infisical Secrets Operator fetches secrets from Infisical and saves them as Kubernetes secrets using the custom `InfisicalSecret` resource to define authentication and storage methods.
|
||||
The operator updates secrets continuously and can reload dependent deployments automatically on secret changes.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Connected to your cluster via kubectl
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical.
|
||||
|
||||
## Installation
|
||||
|
||||
Follow the instructions for either [Helm](https://helm.sh/) or [kubectl](https://github.com/kubernetes/kubectl) to install the Infisical Secrets Operator.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Helm">
|
||||
Install the Infisical Helm repository
|
||||
|
||||
```console
|
||||
helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
|
||||
|
||||
helm repo update
|
||||
```
|
||||
|
||||
Install the Helm chart
|
||||
```console
|
||||
helm install --generate-name infisical-helm-charts/secrets-operator
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Kubectl">
|
||||
The operator will be installed in `infisical-operator-system` namespace
|
||||
```
|
||||
kubectl apply -f https://raw.githubusercontent.com/Infisical/infisical/main/k8-operator/kubectl-install/install-secrets-operator.yaml
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
**Step 1: Create Kubernetes secret containing service token**
|
||||
|
||||
Once you have generated the service token, create a Kubernetes secret containing the service token you generated by running the command below.
|
||||
|
||||
``` bash
|
||||
kubectl create secret generic service-token --from-literal=infisicalToken=<your-service-token-here>
|
||||
```
|
||||
|
||||
**Step 2: Fill out the InfisicalSecrets CRD and apply it to your cluster**
|
||||
|
||||
```yaml infisical-secrets-config.yaml
|
||||
apiVersion: secrets.infisical.com/v1alpha1
|
||||
kind: InfisicalSecret
|
||||
metadata:
|
||||
# Name of of this InfisicalSecret resource
|
||||
name: infisicalsecret-sample
|
||||
spec:
|
||||
# The host that should be used to pull secrets from. If left empty, the value specified in Global configuration will be used
|
||||
hostAPI: https://app.infisical.com/api
|
||||
authentication:
|
||||
serviceToken:
|
||||
serviceTokenSecretReference: # <-- The secret's namespaced name that holds the project token for authentication in step 1
|
||||
secretName: service-token
|
||||
secretNamespace: option
|
||||
managedSecretReference:
|
||||
secretName: managed-secret # <-- the name of kubernetes secret that will be created
|
||||
secretNamespace: default # <-- in what namespace it will be created in
|
||||
```
|
||||
|
||||
```
|
||||
kubectl apply -f infisical-secrets-config.yaml
|
||||
```
|
||||
|
||||
You should now see a new kubernetes secret automatically created in the namespace you defined in the `managedSecretReference` property above.
|
||||
|
||||
See also:
|
||||
|
||||
- [Documentation for the Infisical Kubernetes Operator](../../integrations/platforms/kubernetes)
|
||||
|
57
docs/documentation/getting-started/platform.mdx
Normal file
57
docs/documentation/getting-started/platform.mdx
Normal file
@ -0,0 +1,57 @@
|
||||
---
|
||||
title: "Platform"
|
||||
---
|
||||
|
||||
Infisical is an [open-source](https://opensource.com/resources/what-open-source), [end-to-end encrypted](https://en.wikipedia.org/wiki/End-to-end_encryption) secret management platform that enables teams to easily store, manage, and sync secrets like API keys, database credentials, and environment variables across their apps and infrastructure.
|
||||
|
||||
This quickstart provides an overview of the functionalities offered by Infisical.
|
||||
|
||||
## Projects
|
||||
|
||||
Projects hold secrets for applications, which are further organized into environments such as development, testing and production.
|
||||
|
||||
### Secrets Overview
|
||||
|
||||
The secrets overview provides a bird's-eye view of all the secrets in a project and is particularly useful for identifying missing secrets across environments.
|
||||
|
||||

|
||||
|
||||
### Secrets Dashboard
|
||||
|
||||
The secrets dashboard lets you manage secrets for a specific environment in a project.
|
||||
Here, developers can [override secrets](//project#personal-overrides), [version secrets](/documentation/platform/secret-versioning), [rollback projects to any point in time](/documentation/platform/pit-recovery), and much more.
|
||||
|
||||

|
||||
|
||||
### Integrations
|
||||
|
||||
The integrations page provides native integrations to sync secrets from a project environment to a [host of ever-expanding integrations](/integrations/overview).
|
||||
|
||||
<Tip>
|
||||
Depending on your infrastructure setup and compliance requirements, you may or may not prefer to use these native integrations since they break end-to-end encryption (E2EE).
|
||||
|
||||
You will learn about various ways to integrate with Infisical and maintain E2EE in subsequent quickstart sections.
|
||||
</Tip>
|
||||
|
||||

|
||||
|
||||
### Access Control
|
||||
|
||||
The members page lets you add/remove members for a project and provision them access to environments (access levels include `No Access`, `Read Only`, and `Read and Write`).
|
||||
|
||||

|
||||
|
||||
## Organizations
|
||||
|
||||
Organizations house projects and members.
|
||||
|
||||
### Organization Settings
|
||||
|
||||
At the organization-level, you can add/remove members and manage their access to projects.
|
||||
|
||||

|
||||

|
||||
|
||||
That's it for the platform quickstart! — We encourage you to continue exploring the documentation to gain a deeper understanding of the extensive features and functionalities that Infisical has to offer.
|
||||
|
||||
Next, head back to [Getting Started > Introduction](/documentation/getting-started/overview) to explore ways to fetch secrets from Infisical to your apps and infrastructure.
|
145
docs/documentation/getting-started/sdks.mdx
Normal file
145
docs/documentation/getting-started/sdks.mdx
Normal file
@ -0,0 +1,145 @@
|
||||
---
|
||||
title: "SDKs"
|
||||
---
|
||||
|
||||
From local development to production, Infisical's language-specific SDKs provide the easiest way for your app to fetch back secrets on demand.
|
||||
|
||||
Prerequisites:
|
||||
|
||||
- Have a project with secrets ready in [Infisical Cloud](https://app.infisical.com).
|
||||
- Create an [Infisical Token](/documentation/platform/token) scoped to an environment in your project in Infisical.
|
||||
|
||||
## Installation
|
||||
|
||||
Follow the instructions for your language to install the SDK for it.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Node">
|
||||
|
||||
Run `npm` to add [infisical-node](https://github.com/Infisical/infisical-node) to your project.
|
||||
|
||||
```console
|
||||
$ npm install infisical-node --save
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Import the SDK and create a client instance with your [Infisical Token](/documentation/platform/token).
|
||||
|
||||
<Tabs>
|
||||
<Tab title="ES6">
|
||||
```js
|
||||
import InfisicalClient from "infisical-node";
|
||||
|
||||
const client = new InfisicalClient({
|
||||
token: "your_infisical_token"
|
||||
});
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="ES5">
|
||||
```js
|
||||
const InfisicalClient = require("infisical-node");
|
||||
|
||||
const client = new InfisicalClient({
|
||||
token: "your_infisical_token"
|
||||
});
|
||||
````
|
||||
</Tab>
|
||||
</Tabs>
|
||||
## Get a Secret
|
||||
|
||||
```js
|
||||
const secret = await client.getSecret("API_KEY");
|
||||
const value = secret.secretValue; // get its value
|
||||
```
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```js
|
||||
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 () => {
|
||||
console.log(`App listening on port ${port}`);
|
||||
});
|
||||
```
|
||||
|
||||
This example demonstrates how to use the Infisical Node SDK with an Express application. The application retrieves a secret named "NAME" and responds to requests with a greeting that includes the secret value.
|
||||
</Tab>
|
||||
<Tab title="Python">
|
||||
|
||||
## Installation
|
||||
|
||||
Run `pip` to add [infisical-python](https://github.com/Astropilot/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](/documentation/platform/token).
|
||||
|
||||
```py
|
||||
from infisical import InfisicalClient
|
||||
|
||||
client = InfisicalClient(token="your_infisical_token")
|
||||
```
|
||||
|
||||
## Get a Secret
|
||||
|
||||
```py
|
||||
secret = client.get_secret("API_KEY")
|
||||
value = secret.secret_value # get its value
|
||||
```
|
||||
|
||||
## 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.
|
||||
</Tab>
|
||||
<Tab title="Other">
|
||||
We're currently working on SDKs for other languages. Follow the GitHub issue for your needed language below:
|
||||
- [Java](https://github.com/Infisical/infisical/issues/434)
|
||||
- [Ruby](https://github.com/Infisical/infisical/issues/435)
|
||||
- [Go](https://github.com/Infisical/infisical/issues/436)
|
||||
- [Rust](https://github.com/Infisical/infisical/issues/437)
|
||||
- [PHP](https://github.com/Infisical/infisical/issues/531)
|
||||
|
||||
Missing a language? [Throw in a request](https://github.com/Infisical/infisical/issues).
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
See also:
|
||||
|
||||
- Explore the [Node SDK](https://github.com/Infisical/infisical-node)
|
||||
- Explore the [Python SDK](https://github.com/Infisical/infisical-python)
|
41
docs/documentation/guides/introduction.mdx
Normal file
41
docs/documentation/guides/introduction.mdx
Normal file
@ -0,0 +1,41 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
---
|
||||
|
||||
Whether you're running a Node application on Heroku, Next.js application with Vercel, or Kubernetes on AWS, Infisical has a secret management strategy from local development to production ready for you.
|
||||
|
||||
## Guides by Language
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Node"
|
||||
href="/documentation/guides/node"
|
||||
icon="node"
|
||||
color="#3c8639"
|
||||
>
|
||||
Manage secrets across your Node stack
|
||||
</Card>
|
||||
<Card
|
||||
href="/documentation/guides/python"
|
||||
title="Python"
|
||||
icon="python"
|
||||
color="#3775a9"
|
||||
>
|
||||
Manage secrets across your Python stack
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Guides by Stack
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Next.js + Vercel"
|
||||
href="/documentation/guides/nextjs-vercel"
|
||||
icon="cloud"
|
||||
color="#3c8639"
|
||||
>
|
||||
Manage secrets for your Next.js + Vercel stack
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
Want a guide? [Throw in a request](https://github.com/Infisical/infisical/issues).
|
254
docs/documentation/guides/nextjs-vercel.mdx
Normal file
254
docs/documentation/guides/nextjs-vercel.mdx
Normal file
@ -0,0 +1,254 @@
|
||||
---
|
||||
title: "Next.js + Vercel"
|
||||
---
|
||||
|
||||
|
||||
This guide demonstrates how to use Infisical to manage secrets for your Next.js + Vercel stack from local development to production. It uses:
|
||||
|
||||
- Infisical (you can use [Infisical Cloud](https://app.infisical.com) or a [self-hosted instance of Infisical](https://infisical.com/docs/self-hosting/overview)) to store your secrets.
|
||||
|
||||
## Project Setup
|
||||
|
||||
To begin, we need to set up a project in Infisical and add secrets to an environment in it.
|
||||
|
||||
### Create a project
|
||||
|
||||
1. Create a new project in [Infisical](https://app.infisical.com/).
|
||||
|
||||
2. Add a secret to the development environment of this project so we can pull it back for local development. In the **Secrets Overview** page, press **Explore Development** and add a secret with the key `NEXT_PUBLIC_NAME` and value `YOUR_NAME`.
|
||||
|
||||
3. Add a secret to the production environment of this project so we can sync it to Vercel. Switch to the **Production** environment and add a secret with the key `NEXT_PUBLIC_NAME` and value `ANOTHER_NAME`.
|
||||
|
||||
## Create a Next.js app
|
||||
|
||||
Initialize a new Node.js app.
|
||||
|
||||
We can use `create-next-app` to initialize an app called `infisical-nextjs`.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="JavaScript">
|
||||
```console
|
||||
npx create-next-app@latest --use-npm infisical-nextjs
|
||||
cd infisical-nextjs
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
```console
|
||||
npx create-next-app@latest --ts --use-npm infisical-nextjs
|
||||
cd infisical-nextjs
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
Next, inside `pages/_app.js`, lets add a `console.log()` to print out the environment variable in the browser console.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="JavaScript">
|
||||
```js
|
||||
import '@/styles/globals.css'
|
||||
|
||||
export default function App({ Component, pageProps }) {
|
||||
console.log('Hello, ', process.env.NEXT_PUBLIC_NAME);
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="TypeScript">
|
||||
```tsx
|
||||
import '@/styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
export default function App({ Component, pageProps }: AppProps) {
|
||||
console.log('Hello, ', process.env.NEXT_PUBLIC_NAME);
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
## Infisical CLI for local development environment variables
|
||||
|
||||
We'll now use the Infisical CLI to fetch secrets from Infisical into your Next.js app for local development.
|
||||
|
||||
### CLI Installation
|
||||
|
||||
Follow the instructions for your operating system to install the Infisical CLI.
|
||||
|
||||
<Tabs>
|
||||
<Tab title="MacOS">
|
||||
Use [brew](https://brew.sh/) package manager
|
||||
|
||||
```console
|
||||
$ brew install infisical/get-cli/infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Windows">
|
||||
Use [Scoop](https://scoop.sh/) package manager
|
||||
|
||||
```console
|
||||
$ scoop bucket add org https://github.com/Infisical/scoop-infisical.git
|
||||
```
|
||||
|
||||
```console
|
||||
$ scoop install infisical
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Alpine">
|
||||
Install prerequisite
|
||||
```console
|
||||
$ apk add --no-cache bash sudo
|
||||
```
|
||||
|
||||
Add Infisical repository
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.alpine.sh' \
|
||||
| bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ apk update && sudo apk add infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="RedHat/CentOs/Amazon">
|
||||
Add Infisical repository
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.rpm.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ sudo yum install infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Debian/Ubuntu">
|
||||
Add Infisical repository
|
||||
|
||||
```console
|
||||
$ curl -1sLf \
|
||||
'https://dl.cloudsmith.io/public/infisical/infisical-cli/setup.deb.sh' \
|
||||
| sudo -E bash
|
||||
```
|
||||
|
||||
Then install CLI
|
||||
```console
|
||||
$ sudo apt-get update && sudo apt-get install -y infisical
|
||||
```
|
||||
|
||||
</Tab>
|
||||
<Tab title="Arch Linux">
|
||||
Use the `yay` package manager to install from the [Arch User Repository](https://aur.archlinux.org/packages/infisical-bin)
|
||||
|
||||
```console
|
||||
$ yay -S infisical-bin
|
||||
```
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
### Login
|
||||
|
||||
Authenticate the CLI with the Infisical platform using your email and password.
|
||||
|
||||
```console
|
||||
$ infisical login
|
||||
```
|
||||
|
||||
### Initialization
|
||||
|
||||
Run the `init` command at the root of the Next.js app. This step connects your local project to the project on the Infisical platform and creates a `infisical.json` file containing a reference to that latter project.
|
||||
|
||||
```console
|
||||
$ infisical init
|
||||
```
|
||||
|
||||
### Start the Next.js app with secrets injected as environment variables
|
||||
|
||||
```console
|
||||
$ infisical run -- npm run dev
|
||||
```
|
||||
|
||||
If you open your browser console, **Hello, YOUR_NAME** should be printed out.
|
||||
|
||||
Here, the CLI fetched the secret from Infisical and injected it into the Next.js app upon starting up. By default,
|
||||
the CLI fetches secrets from the development environment which has the slug `dev`; you can inject secrets from different
|
||||
environments by modifying the `env` flag as per the [CLI documentation](/cli/usage).
|
||||
|
||||
At this stage, you know how to use the Infisical CLI to inject secrets into your Next.js app for local development.
|
||||
|
||||
## Infisical-Vercel integration for production environment variables
|
||||
|
||||
We'll now use the Infisical-Vercel integration send secrets from Infisical to Vercel as production environment variables.
|
||||
|
||||
### Infisical-Vercel integration
|
||||
|
||||
To begin we have to import the Next.js app into Vercel as a project. [Follow these instructions](https://nextjs.org/learn/basics/deploying-nextjs-app/deploy) to deploy the Next.js app to Vercel.
|
||||
|
||||
Next, navigate to your project's integrations tab in Infisical and press on the Vercel tile to grant Infisical access to your Vercel account.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
<Note>
|
||||
Opting in for the Infisical-Vercel integration will break end-to-end encryption since Infisical will be able to read
|
||||
your secrets. This is, however, necessary for Infisical to sync the secrets to Vercel.
|
||||
|
||||
Your secrets remain encrypted at rest following our [security guide mechanics](/security/mechanics).
|
||||
</Note>
|
||||
|
||||
Now select **Production** for (the source) **Environment** and sync it to the **Production Environment** of the (target) application in Vercel.
|
||||
Lastly, press create integration to start syncing secrets to Vercel.
|
||||
|
||||

|
||||

|
||||
|
||||
You should now see your secret from Infisical appear as production environment variables in your Vercel project.
|
||||
|
||||
At this stage, you know how to use the Infisical-Vercel integration to sync production secrets from Infisical to Vercel.
|
||||
|
||||
<Warning>
|
||||
The following environment variable names are reserved by Vercel and cannot be
|
||||
synced: `AWS_SECRET_KEY`, `AWS_EXECUTION_ENV`, `AWS_LAMBDA_LOG_GROUP_NAME`,
|
||||
`AWS_LAMBDA_LOG_STREAM_NAME`, `AWS_LAMBDA_FUNCTION_NAME`,
|
||||
`AWS_LAMBDA_FUNCTION_MEMORY_SIZE`, `AWS_LAMBDA_FUNCTION_VERSION`,
|
||||
`NOW_REGION`, `TZ`, `LAMBDA_TASK_ROOT`, `LAMBDA_RUNTIME_DIR`,
|
||||
`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_SESSION_TOKEN`,
|
||||
`AWS_REGION`, and `AWS_DEFAULT_REGION`.
|
||||
</Warning>
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Why should I use Infisical if I can centralize all my Next.js + Vercel environment variables across all environments directly in Vercel?">
|
||||
Vercel does not specialize in secret management which means it lacks many useful features for effectively managing environment variables.
|
||||
Here are some features that teams benefit from by using Infisical together with Vercel:
|
||||
|
||||
- Audit logs: See which team members are creating, reading, updating, and deleting environment variables across all environments.
|
||||
- Versioning and point in time recovery: Rolling back secrets and an entire project state.
|
||||
- Overriding secrets that should be unique amongst team members.
|
||||
|
||||
And much more.
|
||||
</Accordion>
|
||||
<Accordion title="Is opting out of end-to-end encryption for the Infisical-Vercel integration safe?">
|
||||
Yes. Your secrets are still encrypted at rest. To note, most secret managers actually don't support end-to-end encryption.
|
||||
|
||||
Check out the [security guide](/security/overview).
|
||||
</Accordion>
|
||||
<Accordion title="Is there way to retain end-to-end encryption for syncing production secrets to Vercel?">
|
||||
Yes. You can also use the Infisical [Node SDK](https://github.com/Infisical/infisical-node) to fetch secrets back to your Next.js app
|
||||
in both development and production.
|
||||
|
||||
Depending on how you use it, however, it may require certain pages to be server-side rendered.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
|
||||
See also:
|
||||
|
||||
- [Documentation for the Infisical CLI](/cli/overview)
|
||||
- [Documentation for the Vercel integration](/integrations/cloud/vercel)
|
121
docs/documentation/guides/node.mdx
Normal file
121
docs/documentation/guides/node.mdx
Normal file
@ -0,0 +1,121 @@
|
||||
---
|
||||
title: "Node"
|
||||
---
|
||||
|
||||
This guide demonstrates how to use Infisical to manage secrets for your Node stack from local development to production. It uses:
|
||||
|
||||
- Infisical (you can use [Infisical Cloud](https://app.infisical.com) or a [self-hosted instance of Infisical](https://infisical.com/docs/self-hosting/overview)) to store your secrets.
|
||||
- The [infisical-node](https://github.com/Infisical/infisical-node) client SDK to fetch secrets back to your Node application on demand.
|
||||
|
||||
## Project Setup
|
||||
|
||||
To begin, we need to set up a project in Infisical and add secrets to an environment in it.
|
||||
|
||||
### Create a project
|
||||
|
||||
1. Create a new project in [Infisical](https://app.infisical.com/).
|
||||
|
||||
2. Add a secret to the development environment of this project so we can pull it back for local development. In the **Secrets Overview** page, press **Explore Development** and add a secret with the key `NAME` and value `YOUR_NAME`.
|
||||
|
||||
### Create an Infisical Token
|
||||
|
||||
Now that we've created a project and added a secret to its development environment, we need to provision an Infisical Token that our Node application can use to access the secret.
|
||||
|
||||
1. Head to the **Project Settings > Service Tokens** and press **Add New Token**.
|
||||
2. Call the token anything like **My App Token** and select **Development** under **Environment**.
|
||||
3. Copy the token and keep it handy.
|
||||
|
||||
|
||||
## Create a Node app
|
||||
|
||||
For this demonstration, we use a minimal Express application. However, the same principles will apply for any Node application such as those built on Koa or Fastify.
|
||||
|
||||
### Create an Express app
|
||||
|
||||
Initialize a new Node.js project with a default `package.json` file.
|
||||
|
||||
```console
|
||||
npm init -y
|
||||
```
|
||||
|
||||
Install `express` and [infisical-node](https://github.com/Infisical/infisical-node), the client Node SDK for Infisical.
|
||||
|
||||
```console
|
||||
npm install express infisical-node
|
||||
```
|
||||
|
||||
Finally, create an index.js file containing the application code.
|
||||
|
||||
```js
|
||||
const express = require("express");
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
|
||||
const client = new InfisicalClient({
|
||||
token: "YOUR_INFISICAL_TOKEN"
|
||||
});
|
||||
|
||||
app.get("/", async (req, res) => {
|
||||
const name = (await client.getSecret("NAME")).secretValue;
|
||||
res.send(`Hello, ${name}!`);
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Example app listening on port ${PORT}`);
|
||||
});
|
||||
```
|
||||
|
||||
Here, we initialized a `client` instance of the Infisical Node SDK with the Infisical Token
|
||||
that we created earlier, giving access to the secrets in the development environment of the
|
||||
project in Infisical that we created earlier.
|
||||
|
||||
Finally, start the app and head to `http://localhost:3000` to see the message **Hello, Your Name**.
|
||||
|
||||
```console
|
||||
node index.js
|
||||
```
|
||||
|
||||
The client fetched the secret with the key `NAME` from Infisical that we returned in the response of the endpoint.
|
||||
|
||||
At this stage, you know how to fetch secrets from Infisical back to your Node application. By using Infisical Tokens scoped to different environments, you can easily manage secrets across various stages of your project in Infisical, from local development to production.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Are my secrets exposed in transit every time the SDK fetches them?">
|
||||
No. Infisical uses end-to-end encryption which ensures that secrets are always encrypted in transit
|
||||
and decrypted on the client side. In fact, not even the server can decrypt your secrets (unless
|
||||
that permission is explicitly granted from within the platform).
|
||||
|
||||
Check out the [security guide](/security/overview).
|
||||
</Accordion>
|
||||
<Accordion title="Isn't it inefficient if my app makes a request every time it needs a secret?">
|
||||
The client SDK caches every secret and implements a 5-minute waiting period before
|
||||
re-requesting it. The waiting period can be controlled by setting the `cacheTTL` parameter at
|
||||
the time of initializing the client.
|
||||
</Accordion>
|
||||
<Accordion title="What if a request for a secret fails?">
|
||||
The SDK caches every secret and falls back to the cached value if a request fails. If no cached
|
||||
value ever-existed, the SDK falls back to whatever value is on `process.env`.
|
||||
</Accordion>
|
||||
<Accordion title="Can I still use process.env with the SDK?">
|
||||
Yes. If no `token` parameter is passed in at the time of initializing the client or nothing is found when requesting for a secret,
|
||||
then the SDK falls back to whatever value is on `process.env`.
|
||||
</Accordion>
|
||||
<Accordion title="What's the point if I still have to manage a token for the SDK?">
|
||||
The token enables the SDK to authenticate with Infisical to fetch back your secrets.
|
||||
Although the SDK requires you to pass in a token, it enables greater efficiency and security
|
||||
than if you managed dozens of secrets yourself without it. Here're some benefits:
|
||||
|
||||
- You always pull in the right secrets because they're fetched on demand from a centralize source that is Infisical.
|
||||
- You can use the Infisical which comes with tons of benefits like secret versioning, access controls, audit logs, etc.
|
||||
- You now risk leaking one token that can be revoked instead of dozens of raw secrets.
|
||||
|
||||
And much more.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
See also:
|
||||
|
||||
- Explore the [Node SDK](https://github.com/Infisical/infisical-node)
|
119
docs/documentation/guides/python.mdx
Normal file
119
docs/documentation/guides/python.mdx
Normal file
@ -0,0 +1,119 @@
|
||||
---
|
||||
title: "Python"
|
||||
---
|
||||
|
||||
This guide demonstrates how to use Infisical to manage secrets for your Python stack from local development to production. It uses:
|
||||
|
||||
- Infisical (you can use [Infisical Cloud](https://app.infisical.com) or a [self-hosted instance of Infisical](https://infisical.com/docs/self-hosting/overview)) to store your secrets.
|
||||
- The [infisical-python](https://github.com/Infisical/infisical-python) client SDK to fetch secrets back to your Python application on demand.
|
||||
|
||||
## Project Setup
|
||||
|
||||
To begin, we need to set up a project in Infisical and add secrets to an environment in it.
|
||||
|
||||
### Create a project
|
||||
|
||||
1. Create a new project in [Infisical](https://app.infisical.com/).
|
||||
|
||||
2. Add a secret to the development environment of this project so we can pull it back for local development. In the **Secrets Overview** page, press **Explore Development** and add a secret with the key `NAME` and value `YOUR_NAME`.
|
||||
|
||||
### Create an Infisical Token
|
||||
|
||||
Now that we've created a project and added a secret to its development environment, we need to provision an Infisical Token that our Node application can use to access the secret.
|
||||
|
||||
1. Head to the **Project Settings > Service Tokens** and press **Add New Token**.
|
||||
2. Call the token anything like **My App Token** and select **Development** under **Environment**.
|
||||
3. Copy the token and keep it handy.
|
||||
|
||||
## Create a Python app
|
||||
|
||||
For this demonstration, we use a minimal Flask application. However, the same principles will apply for any Python application such as those built with Django.
|
||||
|
||||
### Create a Flask app
|
||||
|
||||
First, create a virtual environment and activate it.
|
||||
|
||||
```console
|
||||
python3 -m venv env
|
||||
source env/bin/activate
|
||||
```
|
||||
|
||||
Install Flask and [infisical-python](https://github.com/Infisical/infisical-python), the client Python SDK for Infisical.
|
||||
|
||||
```console
|
||||
pip install Flask infisical
|
||||
```
|
||||
|
||||
Finally, create an `app.py` file containing the application code.
|
||||
|
||||
```python
|
||||
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, {name.secret_value}!"
|
||||
```
|
||||
|
||||
Here, we initialized a `client` instance of the Infisical Python SDK with the Infisical Token
|
||||
that we created earlier, giving access to the secrets in the development environment of the
|
||||
project in Infisical that we created earlier.
|
||||
|
||||
Finally, start the app and head to `http://localhost:5000` to see the message **Hello, Your Name**.
|
||||
|
||||
```console
|
||||
flask run
|
||||
```
|
||||
|
||||
The client fetched the secret with the key `NAME` from Infisical that we returned in the response of the endpoint.
|
||||
|
||||
At this stage, you know how to fetch secrets from Infisical back to your Python application. By using Infisical Tokens scoped to different environments, you can easily manage secrets across various stages of your project in Infisical, from local development to production.
|
||||
|
||||
## FAQ
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Are my secrets exposed in transit every time the SDK fetches them?">
|
||||
No. Infisical uses end-to-end encryption which ensures that secrets are always encrypted in transit
|
||||
and decrypted on the client side. In fact, not even the server can decrypt your secrets (unless
|
||||
that permission is explicitly granted from within the platform).
|
||||
|
||||
Check out the [security guide](/security/overview).
|
||||
</Accordion>
|
||||
<Accordion title="Isn't it inefficient if my app makes a request every time it needs a secret?">
|
||||
The client SDK caches every secret and implements a 5-minute waiting period before
|
||||
re-requesting it. The waiting period can be controlled by setting the `cacheTTL` parameter at
|
||||
the time of initializing the client.
|
||||
</Accordion>
|
||||
<Accordion title="What if a request for a secret fails?">
|
||||
The SDK caches every secret and falls back to the cached value if a request fails. If no cached
|
||||
value ever-existed, the SDK falls back to whatever value is on `process.env`.
|
||||
</Accordion>
|
||||
<Accordion title="Can I still use process.env with the SDK?">
|
||||
Yes. If no `token` parameter is passed in at the time of initializing the client or nothing is found when requesting for a secret,
|
||||
then the SDK falls back to whatever value is on `process.env`.
|
||||
</Accordion>
|
||||
<Accordion title="What's the point if I still have to manage a token for the SDK?">
|
||||
The token enables the SDK to authenticate with Infisical to fetch back your secrets.
|
||||
Although the SDK requires you to pass in a token, it enables greater efficiency and security
|
||||
than if you managed dozens of secrets yourself without it. Here're some benefits:
|
||||
|
||||
- You always pull in the right secrets because they're fetched on demand from a centralize source that is Infisical.
|
||||
- You can use the Infisical which comes with tons of benefits like secret versioning, access controls, audit logs, etc.
|
||||
- You now risk leaking one token that can be revoked instead of dozens of raw secrets.
|
||||
|
||||
And much more.
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
|
||||
See also:
|
||||
|
||||
- Explore the [Python SDK](https://github.com/Infisical/infisical-python)
|
||||
|
||||
|
@ -1,30 +0,0 @@
|
||||
---
|
||||
title: "Features"
|
||||
description: "A non-exhaustive list of features that Infisical has to offer."
|
||||
---
|
||||
|
||||
## Platform
|
||||
|
||||
- Provision members access to organizations and projects.
|
||||
- Manage secrets by adding, deleting, updating them across environments; search, sort, hide/un-hide, export/import them.
|
||||
- Sync secrets to platforms via integrations to platforms like GitHub, Vercel, and Netlify.
|
||||
- Rollback secrets to any point in time.
|
||||
- Rollback each secrets to any version.
|
||||
- Track actions through audit logs.
|
||||
|
||||
## CLI
|
||||
|
||||
The [CLI](/cli/overview) is used to inject environment variables into applications and infrastructure.
|
||||
|
||||
- Inject environment variables.
|
||||
- Inject environment variables into containers via service tokens for Docker.
|
||||
|
||||
## SDKs
|
||||
|
||||
[SDKs](/sdks/overview) enable apps to fetch back secrets using an [Infisical Token](/getting-started/dashboard/token) scoped to a project and environment.
|
||||
|
||||
## Roadmap
|
||||
|
||||
We're building the future of secret management, one that's comprehensive and accessible to all. Check out our [roadmap](https://www.notion.so/infisical/be2d2585a6694e40889b03aef96ea36b?v=5b19a8127d1a4060b54769567a8785fa).
|
||||
|
||||
Interested in contributing? Check out the [guide](/contributing/overview).
|
@ -1,42 +0,0 @@
|
||||
---
|
||||
title: "Introduction"
|
||||
description: "What is Infisical?"
|
||||
---
|
||||
|
||||
Infisical is an [open-source](https://opensource.com/resources/what-open-source), [end-to-end encrypted](https://en.wikipedia.org/wiki/End-to-end_encryption) secret management platform that enables teams to easily manage and sync their environment variables.
|
||||
|
||||
Start syncing environment variables with [Infisical Cloud](https://app.infisical.com) or learn how to [host Infisical](/self-hosting/overview) yourself.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card
|
||||
title="Quickstart"
|
||||
href="/getting-started/quickstart"
|
||||
icon="timer"
|
||||
color="#ea5a0c"
|
||||
>
|
||||
Tour Infisical in a few minutes.
|
||||
</Card>
|
||||
<Card href="/cli/overview" title="CLI" icon="square-terminal" color="#16a34a">
|
||||
Install the CLI to inject secrets into apps and infra.
|
||||
</Card>
|
||||
<Card href="/sdks/overview" title="SDKs" icon="puzzle-piece" color="#16a34a">
|
||||
Install an SDK into your app to fetch secrets.
|
||||
</Card>
|
||||
<Card
|
||||
href="/self-hosting/overview"
|
||||
title="Self-hosting"
|
||||
icon="server"
|
||||
color="#0285c7"
|
||||
>
|
||||
Learn how to configure and deploy Infisical.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
<Card
|
||||
href="/integrations/overview"
|
||||
title="Integrations"
|
||||
icon="plug"
|
||||
color="#dc2626"
|
||||
>
|
||||
Explore integrations for Docker, AWS, Heroku, etc.
|
||||
</Card>
|
@ -1,112 +0,0 @@
|
||||
---
|
||||
title: "Quickstart"
|
||||
description: "Start managing developer secrets and configs with Infisical in minutes."
|
||||
---
|
||||
|
||||
These examples demonstrate how to store and fetch environment variables from [Infisical Cloud](https://app.infisical.com) into your application.
|
||||
|
||||
## Set up Infisical Cloud
|
||||
|
||||
1. Login or create an account at `app.infisical.com`.
|
||||
2. Create a new project.
|
||||
3. Keep the default environment variables or populate them as in the image below.
|
||||
|
||||

|
||||
|
||||
## Fetch Secrets for Your App
|
||||
|
||||
<Tabs>
|
||||
<Tab title="CLI">
|
||||
|
||||
The Infisical CLI is platform-agnostic and enables you to inject environment variables into your app across many tech stacks and frameworks.
|
||||
|
||||
### Set up the CLI
|
||||
|
||||
1. Follow the instructions to [install our platform-agnostic CLI](/cli/overview).
|
||||
|
||||
2. Initialize Infisical for your project.
|
||||
|
||||
```bash
|
||||
# move to your project
|
||||
cd /path/to/project
|
||||
|
||||
# initialize infisical
|
||||
infisical init
|
||||
```
|
||||
|
||||
### Start your app with environment variables injected
|
||||
|
||||
```bash
|
||||
# inject environment variables into app
|
||||
infisical run -- [your application start command]
|
||||
```
|
||||
|
||||
Your app should now be running with the environment variables injected.
|
||||
|
||||
Check out our [integrations](/integrations/overview) for injecting environment variables into frameworks and platforms like Docker.
|
||||
|
||||
</Tab>
|
||||
<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).
|
||||
|
||||
### 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.
|
||||
|
||||

|
||||
|
||||
### Install the SDK
|
||||
|
||||
```bash
|
||||
npm install infisical-node --save
|
||||
```
|
||||
|
||||
### Initialize the Infisical client
|
||||
|
||||
```js
|
||||
await infisical.connect({
|
||||
token: "your_infisical_token",
|
||||
});
|
||||
```
|
||||
|
||||
### Get a value
|
||||
|
||||
```js
|
||||
const value = infisical.get("SOME_KEY");
|
||||
```
|
||||
|
||||
### 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}`);
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
<Warning>
|
||||
We do not recommend hardcoding your [Infisical
|
||||
Token](/getting-started/dashboard/token). Setting it as an environment
|
||||
variable would be best.
|
||||
|
||||
</Warning>
|
||||
|
||||
Check out our [SDKs](/sdks/overview) for other language SDKs.
|
||||
|
||||
</Tab>
|
||||
</Tabs>
|
BIN
docs/images/dashboard-secrets-overview.png
Normal file
BIN
docs/images/dashboard-secrets-overview.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 502 KiB |
BIN
docs/images/project-members.png
Normal file
BIN
docs/images/project-members.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 MiB |
@ -37,7 +37,7 @@ Select which Infisical environment secrets you want to sync to which GitLab repo
|
||||
|
||||
## Generate service token
|
||||
|
||||
Generate an [Infisical Token](../../getting-started/dashboard/token) for the specific project and environment in Infisical.
|
||||
Generate an [Infisical Token](/documentation/platform/token) for the specific project and environment in Infisical.
|
||||
|
||||
## Set the Infisical Token in Gitlab
|
||||
|
||||
|
@ -5,7 +5,7 @@ description: "How to use Infisical to inject secrets and configs into various 3-
|
||||
|
||||
Integrations allow environment variables to be synced from Infisical into your local development workflow, CI/CD pipelines, and production infrastructure.
|
||||
|
||||
Missing an integration? Throw in a [request](https://github.com/Infisical/infisical/issues).
|
||||
Missing an integration? [Throw in a request](https://github.com/Infisical/infisical/issues).
|
||||
|
||||
| Integration | Type | Status |
|
||||
| -------------------------------------------------------------- | --------- | ----------- |
|
||||
|
@ -13,7 +13,7 @@ Follow this [guide](./docker) to configure the Infisical CLI for each service th
|
||||
|
||||
## Generate Infisical Tokens
|
||||
|
||||
Generate a unique [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token) for each service.
|
||||
Generate a unique [Infisical Token](/documentation/platform/token) for each service.
|
||||
|
||||
## Add Infisical Tokens to your Docker Compose file
|
||||
|
||||
|
@ -50,7 +50,7 @@ CMD ["infisical", "run", "--command", "npm run start && ..."]
|
||||
|
||||
## Generate an Infisical Token
|
||||
|
||||
Head to your project settings in Infisical Cloud to generate an [Infisical Token](https://infisical.com/docs/getting-started/dashboard/token).
|
||||
Head to your project settings in Infisical Cloud to generate an [Infisical Token](/documentation/platform/token).
|
||||
|
||||
## Feed Docker your Infisical Token
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user