Merge remote-tracking branch 'origin' into view-licenses

This commit is contained in:
Tuan Dang
2023-08-03 11:51:12 +07:00
28 changed files with 1194 additions and 1341 deletions

View File

@@ -4,18 +4,20 @@ import {
decryptAsymmetric,
decryptSymmetric128BitHexKeyUTF8,
encryptSymmetric128BitHexKeyUTF8,
generateKeyPair,
generateKeyPair
} from "../utils/crypto";
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_BASE64,
ENCODING_SCHEME_UTF8,
SECRET_SHARED,
SECRET_SHARED
} from "../variables";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { InternalServerError } from "../utils/errors";
import Folder from "../models/folder";
import { getFolderByPath } from "../services/FolderService";
import { getAllImportedSecrets } from "../services/SecretImportService";
import { expandSecrets } from "./secrets";
/**
* Create an inactive bot with name [name] for workspace with id [workspaceId]
@@ -25,7 +27,7 @@ import { getFolderByPath } from "../services/FolderService";
*/
export const createBot = async ({
name,
workspaceId,
workspaceId
}: {
name: string;
workspaceId: Types.ObjectId;
@@ -36,10 +38,7 @@ export const createBot = async ({
const { publicKey, privateKey } = generateKeyPair();
if (rootEncryptionKey) {
const { ciphertext, iv, tag } = client.encryptSymmetric(
privateKey,
rootEncryptionKey
);
const { ciphertext, iv, tag } = client.encryptSymmetric(privateKey, rootEncryptionKey);
return await new Bot({
name,
@@ -50,12 +49,12 @@ export const createBot = async ({
iv,
tag,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_BASE64,
keyEncoding: ENCODING_SCHEME_BASE64
}).save();
} else if (encryptionKey) {
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext: privateKey,
key: await getEncryptionKey(),
key: await getEncryptionKey()
});
return await new Bot({
@@ -67,12 +66,12 @@ export const createBot = async ({
iv,
tag,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8,
keyEncoding: ENCODING_SCHEME_UTF8
}).save();
}
throw InternalServerError({
message: "Failed to create new bot due to missing encryption key",
message: "Failed to create new bot due to missing encryption key"
});
};
@@ -82,7 +81,7 @@ export const createBot = async ({
*/
export const getIsWorkspaceE2EEHelper = async (workspaceId: Types.ObjectId) => {
const botKey = await BotKey.exists({
workspace: workspaceId,
workspace: workspaceId
});
return botKey ? false : true;
@@ -98,19 +97,19 @@ export const getIsWorkspaceE2EEHelper = async (workspaceId: Types.ObjectId) => {
export const getSecretsBotHelper = async ({
workspaceId,
environment,
secretPath,
secretPath
}: {
workspaceId: Types.ObjectId;
environment: string;
secretPath: string;
}) => {
const content = {} as any;
const content: Record<string, { value: string; comment?: string }> = {};
const key = await getKey({ workspaceId: workspaceId });
let folderId = "root";
const folders = await Folder.findOne({
workspace: workspaceId,
environment,
environment
});
if (!folders && secretPath !== "/") {
@@ -129,7 +128,43 @@ export const getSecretsBotHelper = async ({
workspace: workspaceId,
environment,
type: SECRET_SHARED,
folder: folderId,
folder: folderId
});
const importedSecrets = await getAllImportedSecrets(
workspaceId.toString(),
environment,
folderId
);
importedSecrets.forEach(({ secrets }) => {
secrets.forEach((secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
});
content[secretKey] = { value: secretValue };
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const commentValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key
});
content[secretKey].comment = commentValue;
}
});
});
secrets.forEach((secret: ISecret) => {
@@ -137,19 +172,31 @@ export const getSecretsBotHelper = async ({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key,
key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key,
key
});
content[secretKey] = secretValue;
content[secretKey] = { value: secretValue };
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const commentValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key
});
content[secretKey].comment = commentValue;
}
});
await expandSecrets(workspaceId.toString(), key, content);
return content;
};
@@ -160,22 +207,18 @@ export const getSecretsBotHelper = async ({
* @param {String} obj.workspaceId - id of workspace
* @returns {String} key - decrypted workspace key
*/
export const getKey = async ({
workspaceId,
}: {
workspaceId: Types.ObjectId;
}) => {
export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => {
const encryptionKey = await getEncryptionKey();
const rootEncryptionKey = await getRootEncryptionKey();
const botKey = await BotKey.findOne({
workspace: workspaceId,
workspace: workspaceId
}).populate<{ sender: IUser }>("sender", "publicKey");
if (!botKey) throw new Error("Failed to find bot key");
const bot = await Bot.findOne({
workspace: workspaceId,
workspace: workspaceId
}).select("+encryptedPrivateKey +iv +tag +algorithm +keyEncoding");
if (!bot) throw new Error("Failed to find bot");
@@ -194,7 +237,7 @@ export const getKey = async ({
ciphertext: botKey.encryptedKey,
nonce: botKey.nonce,
publicKey: botKey.sender.publicKey as string,
privateKey: privateKeyBot,
privateKey: privateKeyBot
});
} else if (encryptionKey && bot.keyEncoding === ENCODING_SCHEME_UTF8) {
// case: encoding scheme is utf8
@@ -202,20 +245,19 @@ export const getKey = async ({
ciphertext: bot.encryptedPrivateKey,
iv: bot.iv,
tag: bot.tag,
key: encryptionKey,
key: encryptionKey
});
return decryptAsymmetric({
ciphertext: botKey.encryptedKey,
nonce: botKey.nonce,
publicKey: botKey.sender.publicKey as string,
privateKey: privateKeyBot,
privateKey: privateKeyBot
});
}
throw InternalServerError({
message:
"Failed to obtain bot's copy of workspace key needed for bot operations",
message: "Failed to obtain bot's copy of workspace key needed for bot operations"
});
};
@@ -228,7 +270,7 @@ export const getKey = async ({
*/
export const encryptSymmetricHelper = async ({
workspaceId,
plaintext,
plaintext
}: {
workspaceId: Types.ObjectId;
plaintext: string;
@@ -236,13 +278,13 @@ export const encryptSymmetricHelper = async ({
const key = await getKey({ workspaceId: workspaceId });
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext,
key,
key
});
return {
ciphertext,
iv,
tag,
tag
};
};
/**
@@ -258,7 +300,7 @@ export const decryptSymmetricHelper = async ({
workspaceId,
ciphertext,
iv,
tag,
tag
}: {
workspaceId: Types.ObjectId;
ciphertext: string;
@@ -270,7 +312,7 @@ export const decryptSymmetricHelper = async ({
ciphertext,
iv,
tag,
key,
key
});
return plaintext;
@@ -281,24 +323,24 @@ export const decryptSymmetricHelper = async ({
* and [envionment] using bot
* @param {Object} obj
* @param {String} obj.workspaceId - id of workspace
* @param {String} obj.environment - environment
* @param {String} obj.environment - environment
*/
export const getSecretsCommentBotHelper = async ({
workspaceId,
environment,
secretPath
} : {
}: {
workspaceId: Types.ObjectId;
environment: string;
secretPath: string;
}) => {
const content = {} as any;
const key = await getKey({ workspaceId: workspaceId });
let folderId = "root";
const folders = await Folder.findOne({
workspace: workspaceId,
environment,
environment
});
if (!folders && secretPath !== "/") {
@@ -317,23 +359,23 @@ export const getSecretsCommentBotHelper = async ({
workspace: workspaceId,
environment,
type: SECRET_SHARED,
folder: folderId,
folder: folderId
});
secrets.forEach((secret: ISecret) => {
if(secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key,
key
});
const commentValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key,
key
});
content[secretKey] = commentValue;
@@ -341,4 +383,4 @@ export const getSecretsCommentBotHelper = async ({
});
return content;
}
};

View File

@@ -6,7 +6,7 @@ import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_UTF8,
INTEGRATION_NETLIFY,
INTEGRATION_VERCEL,
INTEGRATION_VERCEL
} from "../variables";
import { UnauthorizedRequestError } from "../utils/errors";
import * as Sentry from "@sentry/node";
@@ -34,7 +34,7 @@ export const handleOAuthExchangeHelper = async ({
workspaceId,
integration,
code,
environment,
environment
}: {
workspaceId: string;
integration: string;
@@ -43,21 +43,20 @@ export const handleOAuthExchangeHelper = async ({
}) => {
const bot = await Bot.findOne({
workspace: workspaceId,
isActive: true,
isActive: true
});
if (!bot)
throw new Error("Bot must be enabled for OAuth2 code-token exchange");
if (!bot) throw new Error("Bot must be enabled for OAuth2 code-token exchange");
// exchange code for access and refresh tokens
const res = await exchangeCode({
integration,
code,
code
});
const update: Update = {
workspace: workspaceId,
integration,
integration
};
switch (integration) {
@@ -72,12 +71,12 @@ export const handleOAuthExchangeHelper = async ({
const integrationAuth = await IntegrationAuth.findOneAndUpdate(
{
workspace: workspaceId,
integration,
integration
},
update,
{
new: true,
upsert: true,
upsert: true
}
);
@@ -86,7 +85,7 @@ export const handleOAuthExchangeHelper = async ({
// set integration auth refresh token
await setIntegrationAuthRefreshHelper({
integrationAuthId: integrationAuth._id.toString(),
refreshToken: res.refreshToken,
refreshToken: res.refreshToken
});
}
@@ -97,7 +96,7 @@ export const handleOAuthExchangeHelper = async ({
integrationAuthId: integrationAuth._id.toString(),
accessId: null,
accessToken: res.accessToken,
accessExpiresAt: res.accessExpiresAt,
accessExpiresAt: res.accessExpiresAt
});
}
@@ -111,7 +110,7 @@ export const handleOAuthExchangeHelper = async ({
*/
export const syncIntegrationsHelper = async ({
workspaceId,
environment,
environment
}: {
workspaceId: Types.ObjectId;
environment?: string;
@@ -121,11 +120,11 @@ export const syncIntegrationsHelper = async ({
workspace: workspaceId,
...(environment
? {
environment,
}
: {}),
environment
}
: {}),
isActive: true,
app: { $ne: null },
app: { $ne: null }
});
// for each workspace integration, sync/push secrets
@@ -135,25 +134,16 @@ export const syncIntegrationsHelper = async ({
const secrets = await BotService.getSecrets({
workspaceId: integration.workspace,
environment: integration.environment,
secretPath: integration.secretPath,
secretPath: integration.secretPath
});
// get workspace, environment (shared) secrets comments
const secretComments = await BotService.getSecretComments({
workspaceId: integration.workspace,
environment: integration.environment,
secretPath: integration.secretPath,
})
const integrationAuth = await IntegrationAuth.findById(
integration.integrationAuth
);
const integrationAuth = await IntegrationAuth.findById(integration.integrationAuth);
if (!integrationAuth) throw new Error("Failed to find integration auth");
// get integration auth access token
const access = await getIntegrationAuthAccessHelper({
integrationAuthId: integration.integrationAuth,
integrationAuthId: integration.integrationAuth
});
// sync secrets to integration
@@ -162,14 +152,17 @@ export const syncIntegrationsHelper = async ({
integrationAuth,
secrets,
accessId: access.accessId === undefined ? null : access.accessId,
accessToken: access.accessToken,
secretComments
accessToken: access.accessToken
});
}
} catch (err) {
Sentry.captureException(err);
console.log(`syncIntegrationsHelper: failed with [workspaceId=${workspaceId}] [environment=${environment}]`, err) // eslint-disable-line no-use-before-define
throw err
// eslint-disable-next-line
console.log(
`syncIntegrationsHelper: failed with [workspaceId=${workspaceId}] [environment=${environment}]`,
err
); // eslint-disable-line no-use-before-define
throw err;
}
};
@@ -182,24 +175,24 @@ export const syncIntegrationsHelper = async ({
* @param {String} refreshToken - decrypted refresh token
*/
export const getIntegrationAuthRefreshHelper = async ({
integrationAuthId,
integrationAuthId
}: {
integrationAuthId: Types.ObjectId;
}) => {
const integrationAuth = await IntegrationAuth.findById(
integrationAuthId
).select("+refreshCiphertext +refreshIV +refreshTag");
const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select(
"+refreshCiphertext +refreshIV +refreshTag"
);
if (!integrationAuth)
throw UnauthorizedRequestError({
message: "Failed to locate Integration Authentication credentials",
message: "Failed to locate Integration Authentication credentials"
});
const refreshToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.refreshCiphertext as string,
iv: integrationAuth.refreshIV as string,
tag: integrationAuth.refreshTag as string,
tag: integrationAuth.refreshTag as string
});
return refreshToken;
@@ -214,28 +207,26 @@ export const getIntegrationAuthRefreshHelper = async ({
* @returns {String} accessToken - decrypted access token
*/
export const getIntegrationAuthAccessHelper = async ({
integrationAuthId,
integrationAuthId
}: {
integrationAuthId: Types.ObjectId;
}) => {
let accessId;
let accessToken;
const integrationAuth = await IntegrationAuth.findById(
integrationAuthId
).select(
const integrationAuth = await IntegrationAuth.findById(integrationAuthId).select(
"workspace integration +accessCiphertext +accessIV +accessTag +accessExpiresAt + refreshCiphertext +accessIdCiphertext +accessIdIV +accessIdTag"
);
if (!integrationAuth)
throw UnauthorizedRequestError({
message: "Failed to locate Integration Authentication credentials",
message: "Failed to locate Integration Authentication credentials"
});
accessToken = await BotService.decryptSymmetric({
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.accessCiphertext as string,
iv: integrationAuth.accessIV as string,
tag: integrationAuth.accessTag as string,
tag: integrationAuth.accessTag as string
});
if (integrationAuth?.accessExpiresAt && integrationAuth?.refreshCiphertext) {
@@ -245,11 +236,11 @@ export const getIntegrationAuthAccessHelper = async ({
if (integrationAuth.accessExpiresAt < new Date()) {
// access token is expired
const refreshToken = await getIntegrationAuthRefreshHelper({
integrationAuthId,
integrationAuthId
});
accessToken = await exchangeRefresh({
integrationAuth,
refreshToken,
refreshToken
});
}
}
@@ -263,13 +254,13 @@ export const getIntegrationAuthAccessHelper = async ({
workspaceId: integrationAuth.workspace,
ciphertext: integrationAuth.accessIdCiphertext as string,
iv: integrationAuth.accessIdIV as string,
tag: integrationAuth.accessIdTag as string,
tag: integrationAuth.accessIdTag as string
});
}
return {
accessId,
accessToken,
accessToken
};
};
@@ -283,7 +274,7 @@ export const getIntegrationAuthAccessHelper = async ({
*/
export const setIntegrationAuthRefreshHelper = async ({
integrationAuthId,
refreshToken,
refreshToken
}: {
integrationAuthId: string;
refreshToken: string;
@@ -294,22 +285,22 @@ export const setIntegrationAuthRefreshHelper = async ({
const obj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace,
plaintext: refreshToken,
plaintext: refreshToken
});
integrationAuth = await IntegrationAuth.findOneAndUpdate(
{
_id: integrationAuthId,
_id: integrationAuthId
},
{
refreshCiphertext: obj.ciphertext,
refreshIV: obj.iv,
refreshTag: obj.tag,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8,
keyEncoding: ENCODING_SCHEME_UTF8
},
{
new: true,
new: true
}
);
@@ -329,7 +320,7 @@ export const setIntegrationAuthAccessHelper = async ({
integrationAuthId,
accessId,
accessToken,
accessExpiresAt,
accessExpiresAt
}: {
integrationAuthId: string;
accessId: string | null;
@@ -342,20 +333,20 @@ export const setIntegrationAuthAccessHelper = async ({
const encryptedAccessTokenObj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace,
plaintext: accessToken,
plaintext: accessToken
});
let encryptedAccessIdObj;
if (accessId) {
encryptedAccessIdObj = await BotService.encryptSymmetric({
workspaceId: integrationAuth.workspace,
plaintext: accessId,
plaintext: accessId
});
}
integrationAuth = await IntegrationAuth.findOneAndUpdate(
{
_id: integrationAuthId,
_id: integrationAuthId
},
{
accessIdCiphertext: encryptedAccessIdObj?.ciphertext ?? undefined,
@@ -366,10 +357,10 @@ export const setIntegrationAuthAccessHelper = async ({
accessTag: encryptedAccessTokenObj.tag,
accessExpiresAt,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_UTF8,
keyEncoding: ENCODING_SCHEME_UTF8
},
{
new: true,
new: true
}
);

View File

@@ -42,9 +42,10 @@ import { TelemetryService } from "../services";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { EELogService, EESecretService } from "../ee/services";
import { getAuthDataPayloadIdObj, getAuthDataPayloadUserObj } from "../utils/auth";
import { getFolderIdFromServiceToken } from "../services/FolderService";
import { getFolderByPath, getFolderIdFromServiceToken } from "../services/FolderService";
import picomatch from "picomatch";
import path from "path";
import Folder, { TFolderRootSchema } from "../models/folder";
export const isValidScope = (
authPayload: IServiceTokenData,
@@ -64,10 +65,9 @@ export const isValidScope = (
export function containsGlobPatterns(secretPath: string) {
const globChars = ["*", "?", "[", "]", "{", "}", "**"];
const normalizedPath = path.normalize(secretPath);
return globChars.some(char => normalizedPath.includes(char));
return globChars.some((char) => normalizedPath.includes(char));
}
/**
* Returns an object containing secret [secret] but with its value, key, comment decrypted.
*
@@ -929,3 +929,164 @@ export const deleteSecretHelper = async ({
secret
};
};
const fetchSecretsCrossEnv = (workspaceId: string, folders: TFolderRootSchema[], key: string) => {
const fetchCache: Record<string, Record<string, string>> = {};
return async (secRefEnv: string, secRefPath: string[], secRefKey: string) => {
const secRefPathUrl = path.join("/", ...secRefPath);
const uniqKey = `${secRefEnv}-${secRefPathUrl}`;
if (fetchCache?.[uniqKey]) {
return fetchCache[uniqKey][secRefKey];
}
let folderId = "root";
const folder = folders.find(({ environment }) => environment === secRefEnv);
if (!folder && secRefPathUrl !== "/") {
throw BadRequestError({ message: "Folder not found" });
}
if (folder) {
const selectedFolder = getFolderByPath(folder.nodes, secRefPathUrl);
if (!selectedFolder) {
throw BadRequestError({ message: "Folder not found" });
}
folderId = selectedFolder.id;
}
const secrets = await Secret.find({
workspace: workspaceId,
environment: secRefEnv,
type: SECRET_SHARED,
folder: folderId
});
const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
});
prev[secretKey] = secretValue;
return prev;
}, {});
fetchCache[uniqKey] = decryptedSec;
return fetchCache[uniqKey][secRefKey];
};
};
const INTERPOLATION_SYNTAX_REG = new RegExp(/\${([^}]+)}/g);
const recursivelyExpandSecret = async (
expandedSec: Record<string, string>,
interpolatedSec: Record<string, string>,
fetchCrossEnv: (env: string, secPath: string[], secKey: string) => Promise<string>,
recursionChainBreaker: Record<string, boolean>,
key: string
) => {
if (expandedSec?.[key]) {
return expandedSec[key];
}
if (recursionChainBreaker?.[key]) {
return "";
}
recursionChainBreaker[key] = true;
let interpolatedValue = interpolatedSec[key];
if (!interpolatedValue) {
throw new Error(`Couldn't find referenced value - ${key}`);
}
const refs = interpolatedValue.match(INTERPOLATION_SYNTAX_REG);
if (refs) {
for (const interpolationSyntax of refs) {
const interpolationKey = interpolationSyntax.slice(2, interpolationSyntax.length - 1);
const entities = interpolationKey.trim().split(".");
if (entities.length === 1) {
const val = await recursivelyExpandSecret(
expandedSec,
interpolatedSec,
fetchCrossEnv,
recursionChainBreaker,
interpolationKey
);
if (val) {
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
}
continue;
}
if (entities.length > 1) {
const secRefEnv = entities[0];
const secRefPath = entities.slice(1, entities.length - 1);
const secRefKey = entities[entities.length - 1];
const val = await fetchCrossEnv(secRefEnv, secRefPath, secRefKey);
interpolatedValue = interpolatedValue.replaceAll(interpolationSyntax, val);
}
}
}
expandedSec[key] = interpolatedValue;
return interpolatedValue;
};
// used to convert multi line ones to quotes ones with \n
const formatMultiValueEnv = (val?: string) => {
if (!val) return "";
if (!val.match("\n")) return val;
return `"${val.replace(/\n/g, "\\n")}"`;
};
export const expandSecrets = async (
workspaceId: string,
rootEncKey: string,
secrets: Record<string, { value: string; comment?: string }>
) => {
const expandedSec: Record<string, string> = {};
const interpolatedSec: Record<string, string> = {};
const folders = await Folder.find({ workspace: workspaceId });
const crossSecEnvFetch = fetchSecretsCrossEnv(workspaceId, folders, rootEncKey);
Object.keys(secrets).forEach((key) => {
if (secrets[key].value.match(INTERPOLATION_SYNTAX_REG)) {
interpolatedSec[key] = secrets[key].value;
} else {
expandedSec[key] = secrets[key].value;
}
});
for (const key of Object.keys(secrets)) {
if (expandedSec?.[key]) {
secrets[key].value = formatMultiValueEnv(expandedSec[key]);
continue;
}
// this is to avoid recursion loop. So the graph should be direct graph rather than cyclic
// so for any recursion building if there is an entity two times same key meaning it will be looped
const recursionChainBreaker: Record<string, boolean> = {};
const expandedVal = await recursivelyExpandSecret(
expandedSec,
interpolatedSec,
crossSecEnvFetch,
recursionChainBreaker,
key
);
secrets[key].value = formatMultiValueEnv(expandedVal);
}
return secrets;
};

File diff suppressed because it is too large Load Diff

View File

@@ -57,7 +57,7 @@ router.get(
requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: true,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.getSecretByNameRaw
);
@@ -86,7 +86,7 @@ router.post(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: true,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.createSecretRaw
);
@@ -115,7 +115,7 @@ router.patch(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: true,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.updateSecretByNameRaw
);
@@ -143,7 +143,7 @@ router.delete(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: true,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.deleteSecretByNameRaw
);
@@ -169,7 +169,7 @@ router.get(
requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: false,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.getSecrets
);
@@ -205,7 +205,7 @@ router.post(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: false,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.createSecret
);
@@ -232,7 +232,7 @@ router.get(
locationEnvironment: "query",
requiredPermissions: [PERMISSION_READ_SECRETS],
requireBlindIndicesEnabled: true,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.getSecretByName
);
@@ -263,7 +263,7 @@ router.patch(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: false,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.updateSecretByName
);
@@ -291,7 +291,7 @@ router.delete(
requiredPermissions: [PERMISSION_WRITE_SECRETS],
requireBlindIndicesEnabled: true,
requireE2EEOff: false,
checkIPAllowlist: false
checkIPAllowlist: true
}),
secretsController.deleteSecretByName
);

View File

@@ -1,9 +1,7 @@
{
"compilerOptions": {
"target": "es2016",
"lib": [
"es6"
],
"lib": ["es6", "es2021"],
"module": "commonjs",
"rootDir": "src",
"resolveJsonModule": true,
@@ -15,15 +13,8 @@
"strict": true,
"noImplicitAny": true,
"skipLibCheck": true,
"typeRoots": [
"./src/types",
"./node_modules/@types"
]
"typeRoots": ["./src/types", "./node_modules/@types"]
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -3,7 +3,6 @@ module github.com/Infisical/infisical-merge
go 1.19
require (
github.com/99designs/keyring v1.2.2
github.com/charmbracelet/lipgloss v0.5.0
github.com/denisbrodbeck/machineid v1.0.1
github.com/fatih/semgroup v1.2.0
@@ -15,43 +14,40 @@ require (
github.com/muesli/reflow v0.3.0
github.com/muesli/roff v0.1.0
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
github.com/rs/cors v1.9.0
github.com/rs/zerolog v1.26.1
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.8.0
github.com/stretchr/testify v1.8.1
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/term v0.9.0
)
require (
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/muesli/mango v0.1.0 // indirect
github.com/muesli/mango-pflag v0.1.0 // indirect
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/pelletier/go-toml v1.9.3 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/rs/cors v1.9.0 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
@@ -62,6 +58,7 @@ require (
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.7.0 // indirect
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
@@ -74,4 +71,5 @@ require (
github.com/jedib0t/go-pretty v4.3.0+incompatible
github.com/manifoldco/promptui v0.9.0
github.com/spf13/pflag v1.0.5 // indirect
github.com/zalando/go-keyring v0.2.3
)

View File

@@ -37,12 +37,10 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
@@ -72,15 +70,13 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisbrodbeck/machineid v1.0.1 h1:geKr9qtkB876mXguW2X6TU4ZynleN6ezuMSRhl4D7AQ=
github.com/denisbrodbeck/machineid v1.0.1/go.mod h1:dJUwb7PTidGDeYyUBmXZ2GphQBbjJCrnectwCyxcUSI=
github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -107,9 +103,9 @@ github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtK
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -179,8 +175,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
@@ -257,8 +251,6 @@ github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJ
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4=
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
github.com/muesli/mango v0.1.0 h1:DZQK45d2gGbql1arsYA4vfg4d7I9Hfx5rX/GCmzsAvI=
@@ -326,8 +318,9 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -335,8 +328,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
@@ -354,6 +348,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=
github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
@@ -527,15 +523,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=

View File

@@ -0,0 +1,68 @@
package keyringwrapper
import (
"time"
"github.com/zalando/go-keyring"
)
const MAIN_KEYRING_SERVICE = "infisical-cli"
type TimeoutError struct {
message string
}
func (e *TimeoutError) Error() string {
return e.message
}
func Set(key, value string) error {
ch := make(chan error, 1)
go func() {
defer close(ch)
ch <- keyring.Set(MAIN_KEYRING_SERVICE, key, value)
}()
select {
case err := <-ch:
return err
case <-time.After(3 * time.Second):
return &TimeoutError{"timeout while trying to set secret in keyring"}
}
}
func Get(key string) (string, error) {
ch := make(chan struct {
val string
err error
}, 1)
go func() {
defer close(ch)
val, err := keyring.Get(MAIN_KEYRING_SERVICE, key)
ch <- struct {
val string
err error
}{val, err}
}()
select {
case res := <-ch:
return res.val, res.err
case <-time.After(3 * time.Second):
return "", &TimeoutError{"timeout while trying to get secret from keyring"}
}
}
func Delete(key string) error {
ch := make(chan error, 1)
go func() {
defer close(ch)
ch <- keyring.Delete(MAIN_KEYRING_SERVICE, key)
}()
select {
case err := <-ch:
return err
case <-time.After(3 * time.Second):
return &TimeoutError{"timeout while trying to delete secret from keyring"}
}
}

View File

@@ -196,7 +196,7 @@ func CallGetAccessibleEnvironments(httpClient *resty.Client, request GetAccessib
}
if response.IsError() {
return GetAccessibleEnvironmentsResponse{}, fmt.Errorf("CallGetAccessibleEnvironments: Unsuccessful response: [response=%v]", response)
return GetAccessibleEnvironmentsResponse{}, fmt.Errorf("CallGetAccessibleEnvironments: Unsuccessful response: [response=%v] [response-code=%v] [url=%s]", response, response.StatusCode(), response.Request.URL)
}
return accessibleEnvironmentsResponse, nil

View File

@@ -46,6 +46,10 @@ var initCmd = &cobra.Command{
util.HandleError(err, "Unable to get your login details")
}
if userCreds.LoginExpired {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
httpClient := resty.New()
httpClient.SetAuthToken(userCreds.UserCredentials.JTWToken)
workspaceResponse, err := api.CallGetAllWorkSpacesUserBelongsTo(httpClient)

View File

@@ -57,7 +57,7 @@ var loginCmd = &cobra.Command{
Run: func(cmd *cobra.Command, args []string) {
currentLoggedInUserDetails, err := util.GetCurrentLoggedInUserDetails()
// if the key can't be found or there is an error getting current credentials from key ring, allow them to override
if err != nil && (strings.Contains(err.Error(), "The specified item could not be found in the keyring") || strings.Contains(err.Error(), "unable to get key from Keyring") || strings.Contains(err.Error(), "GetUserCredsFromKeyRing")) {
if err != nil && (strings.Contains(err.Error(), "we couldn't find your logged in details")) {
log.Debug().Err(err)
} else if err != nil {
util.HandleError(err)
@@ -117,8 +117,7 @@ var loginCmd = &cobra.Command{
err = util.StoreUserCredsInKeyRing(&userCredentialsToBeStored)
if err != nil {
currentVault, _ := util.GetCurrentVaultBackend()
log.Error().Msgf("Unable to store your credentials in system vault [%s]. Rerun with flag -d to see full logs", currentVault)
log.Error().Msgf("Unable to store your credentials in system vault [%s]")
log.Error().Msgf("\nTo trouble shoot further, read https://infisical.com/docs/cli/faq")
log.Debug().Err(err)
//return here

View File

@@ -26,14 +26,6 @@ var resetCmd = &cobra.Command{
os.RemoveAll(pathToDir)
// delete keyring
keyringInstance, err := util.GetKeyRing()
if err != nil {
util.HandleError(err)
}
keyringInstance.Remove(util.KEYRING_SERVICE_NAME)
// delete secrets backup
util.DeleteBackupSecrets()

View File

@@ -128,6 +128,10 @@ var secretsSetCmd = &cobra.Command{
util.HandleError(err, "Unable to authenticate")
}
if loggedInUserDetails.LoginExpired {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
httpClient := resty.New().
SetAuthToken(loggedInUserDetails.UserCredentials.JTWToken).
SetHeader("Accept", "application/json")
@@ -334,6 +338,10 @@ var secretsDeleteCmd = &cobra.Command{
util.HandleError(err, "Unable to authenticate")
}
if loggedInUserDetails.LoginExpired {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
workspaceFile, err := util.GetWorkSpaceFromFile()
if err != nil {
util.HandleError(err, "Unable to get local project details")

View File

@@ -1,103 +0,0 @@
/*
Copyright (c) 2023 Infisical Inc.
*/
package cmd
import (
"fmt"
"github.com/99designs/keyring"
"github.com/Infisical/infisical-merge/packages/util"
"github.com/posthog/posthog-go"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
)
var vaultSetCmd = &cobra.Command{
Example: `infisical vault set pass`,
Use: "set [vault-name]",
Short: "Used to set the vault backend to store your login details securely at rest",
DisableFlagsInUseLine: true,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
wantedVaultTypeName := args[0]
currentVaultBackend, err := util.GetCurrentVaultBackend()
if err != nil {
log.Error().Msgf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err)
return
}
if wantedVaultTypeName == string(currentVaultBackend) {
log.Error().Msgf("You are already on vault backend [%s]", currentVaultBackend)
return
}
if isVaultToSwitchToValid(wantedVaultTypeName) {
configFile, err := util.GetConfigFile()
if err != nil {
log.Error().Msgf("Unable to set vault to [%s] because of [err=%s]", wantedVaultTypeName, err)
return
}
configFile.VaultBackendType = keyring.BackendType(wantedVaultTypeName) // save selected vault
configFile.LoggedInUserEmail = "" // reset the logged in user to prompt them to re login
err = util.WriteConfigFile(&configFile)
if err != nil {
log.Error().Msgf("Unable to set vault to [%s] because an error occurred when saving the config file [err=%s]", wantedVaultTypeName, err)
return
}
fmt.Printf("\nSuccessfully, switched vault backend from [%s] to [%s]. Please login in again to store your login details in the new vault with [infisical login]\n", currentVaultBackend, wantedVaultTypeName)
Telemetry.CaptureEvent("cli-command:vault set", posthog.NewProperties().Set("currentVault", currentVaultBackend).Set("wantedVault", wantedVaultTypeName).Set("version", util.CLI_VERSION))
} else {
log.Error().Msgf("The requested vault type [%s] is not available on this system. Only the following vault backends are available for you system: %s", wantedVaultTypeName, keyring.AvailableBackends())
}
},
}
// runCmd represents the run command
var vaultCmd = &cobra.Command{
Use: "vault",
Short: "Used to manage where your Infisical login token is saved on your machine",
DisableFlagsInUseLine: true,
Args: cobra.NoArgs,
Run: func(cmd *cobra.Command, args []string) {
printAvailableVaultBackends()
},
}
func printAvailableVaultBackends() {
fmt.Printf("The following vaults are available on your system:")
for _, backend := range keyring.AvailableBackends() {
fmt.Printf("\n- %s", backend)
}
currentVaultBackend, err := util.GetCurrentVaultBackend()
if err != nil {
log.Error().Msgf("printAvailableVaultBackends: unable to print the available vault backend because of error [err=%s]", err)
}
Telemetry.CaptureEvent("cli-command:vault", posthog.NewProperties().Set("currentVault", currentVaultBackend).Set("version", util.CLI_VERSION))
fmt.Printf("\n\nYou are currently using [%s] vault to store your login credentials\n", string(currentVaultBackend))
}
// Checks if the vault that the user wants to switch to is a valid available vault
func isVaultToSwitchToValid(vaultNameToSwitchTo string) bool {
isFound := false
for _, backend := range keyring.AvailableBackends() {
if vaultNameToSwitchTo == string(backend) {
isFound = true
break
}
}
return isFound
}
func init() {
vaultCmd.AddCommand(vaultSetCmd)
rootCmd.AddCommand(vaultCmd)
}

View File

@@ -1,9 +1,5 @@
package models
import (
"github.com/99designs/keyring"
)
type UserCredentials struct {
Email string `json:"email"`
PrivateKey string `json:"privateKey"`
@@ -13,10 +9,9 @@ type UserCredentials struct {
// The file struct for Infisical config file
type ConfigFile struct {
LoggedInUserEmail string `json:"loggedInUserEmail"`
LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"`
VaultBackendType keyring.BackendType `json:"vaultBackendType"`
LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"`
LoggedInUserEmail string `json:"loggedInUserEmail"`
LoggedInUserDomain string `json:"LoggedInUserDomain,omitempty"`
LoggedInUsers []LoggedInUser `json:"loggedInUsers,omitempty"`
}
type LoggedInUser struct {

View File

@@ -11,6 +11,7 @@ import (
"os/exec"
"runtime"
"strings"
"time"
"github.com/fatih/color"
"github.com/rs/zerolog/log"
@@ -20,13 +21,16 @@ func CheckForUpdate() {
if checkEnv := os.Getenv("INFISICAL_DISABLE_UPDATE_CHECK"); checkEnv != "" {
return
}
latestVersion, err := getLatestTag("Infisical", "infisical")
latestVersion, publishedDate, err := getLatestTag("Infisical", "infisical")
if err != nil {
log.Debug().Err(err)
// do nothing and continue
return
}
if latestVersion != CLI_VERSION {
daysSinceRelease, _ := daysSinceDate(publishedDate)
if latestVersion != CLI_VERSION && daysSinceRelease > 2 {
yellow := color.New(color.FgYellow).SprintFunc()
blue := color.New(color.FgCyan).SprintFunc()
black := color.New(color.FgBlack).SprintFunc()
@@ -50,37 +54,38 @@ func CheckForUpdate() {
}
}
func getLatestTag(repoOwner string, repoName string) (string, error) {
func getLatestTag(repoOwner string, repoName string) (string, string, error) {
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", repoOwner, repoName)
resp, err := http.Get(url)
if err != nil {
return "", err
return "", "", err
}
if resp.StatusCode != 200 {
return "", errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
return "", "", errors.New(fmt.Sprintf("gitHub API returned status code %d", resp.StatusCode))
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return "", err
return "", "", err
}
var releaseTag struct {
TagName string `json:"tag_name"`
var releaseDetails struct {
TagName string `json:"tag_name"`
PublishedAt string `json:"published_at"`
}
if err := json.Unmarshal(body, &releaseTag); err != nil {
return "", fmt.Errorf("failed to unmarshal github response: %w", err)
if err := json.Unmarshal(body, &releaseDetails); err != nil {
return "", "", fmt.Errorf("failed to unmarshal github response: %w", err)
}
tag_prefix := "infisical-cli/v"
// Extract the version from the first valid tag
version := strings.TrimPrefix(releaseTag.TagName, tag_prefix)
version := strings.TrimPrefix(releaseDetails.TagName, tag_prefix)
return version, nil
return version, releaseDetails.PublishedAt, nil
}
func GetUpdateInstructions() string {
@@ -145,3 +150,16 @@ func IsRunningInDocker() bool {
return strings.Contains(string(cgroup), "docker")
}
func daysSinceDate(dateString string) (int, error) {
layout := "2006-01-02T15:04:05Z"
parsedDate, err := time.Parse(layout, dateString)
if err != nil {
return 0, err
}
currentTime := time.Now()
difference := currentTime.Sub(parsedDate)
days := int(difference.Hours() / 24)
return days, nil
}

View File

@@ -52,7 +52,6 @@ func WriteInitalConfig(userCredentials *models.UserCredentials) error {
configFile := models.ConfigFile{
LoggedInUserEmail: userCredentials.Email,
LoggedInUserDomain: config.INFISICAL_URL,
VaultBackendType: existingConfigFile.VaultBackendType,
LoggedInUsers: existingConfigFile.LoggedInUsers,
}

View File

@@ -2,14 +2,16 @@ package util
import (
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/99designs/keyring"
keyringwrapper "github.com/Infisical/infisical-merge/internal"
"github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config"
"github.com/Infisical/infisical-merge/packages/models"
"github.com/go-resty/resty/v2"
"github.com/rs/zerolog/log"
"github.com/zalando/go-keyring"
)
type LoggedInUserDetails struct {
@@ -24,17 +26,7 @@ func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
return fmt.Errorf("StoreUserCredsInKeyRing: something went wrong when marshalling user creds [err=%s]", err)
}
// Get keyring
configuredKeyring, err := GetKeyRing()
if err != nil {
return fmt.Errorf("StoreUserCredsInKeyRing: unable to get keyring instance with [err=%s]", err)
}
err = configuredKeyring.Set(keyring.Item{
Key: userCred.Email,
Data: []byte(string(userCredMarshalled)),
})
err = keyringwrapper.Set(userCred.Email, string(userCredMarshalled))
if err != nil {
return fmt.Errorf("StoreUserCredsInKeyRing: unable to store user credentials because [err=%s]", err)
}
@@ -43,20 +35,20 @@ func StoreUserCredsInKeyRing(userCred *models.UserCredentials) error {
}
func GetUserCredsFromKeyRing(userEmail string) (credentials models.UserCredentials, err error) {
// Get keyring
configuredKeyring, err := GetKeyRing()
credentialsValue, err := keyringwrapper.Get(userEmail)
if err != nil {
return models.UserCredentials{}, fmt.Errorf("GetUserCredsFromKeyRing: unable to get keyring instance with [err=%s]", err)
}
credentialsValue, err := configuredKeyring.Get(userEmail)
if err != nil {
return models.UserCredentials{}, fmt.Errorf("GetUserCredsFromKeyRing: unable to get key from Keyring. could not find login credentials in your Keyring. This is common if you have switched vault backend recently. If so, please login in again and retry [err=%s]", err)
if err == keyring.ErrUnsupportedPlatform {
return models.UserCredentials{}, errors.New("your OS does not support keyring. Consider using a service token https://infisical.com/docs/documentation/platform/token")
} else if err == keyring.ErrNotFound {
return models.UserCredentials{}, errors.New("credentials not found in system keyring")
} else {
return models.UserCredentials{}, fmt.Errorf("something went wrong, failed to retrieve value from system keyring [error=%v]", err)
}
}
var userCredentials models.UserCredentials
err = json.Unmarshal([]byte(credentialsValue.Data), &userCredentials)
err = json.Unmarshal([]byte(credentialsValue), &userCredentials)
if err != nil {
return models.UserCredentials{}, fmt.Errorf("getUserCredsFromKeyRing: Something went wrong when unmarshalling user creds [err=%s]", err)
}
@@ -81,7 +73,11 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
userCreds, err := GetUserCredsFromKeyRing(configFile.LoggedInUserEmail)
if err != nil {
return LoggedInUserDetails{}, fmt.Errorf("getCurrentLoggedInUserDetails: unable to your credentials from Keyring [err=%s]", err)
if strings.Contains(err.Error(), "credentials not found in system keyring") {
return LoggedInUserDetails{}, errors.New("we couldn't find your logged in details, try running [infisical login] then try again")
} else {
return LoggedInUserDetails{}, fmt.Errorf("failed to fetch creditnals from keyring because [err=%s]", err)
}
}
// check to to see if the JWT is still valid
@@ -97,19 +93,19 @@ func GetCurrentLoggedInUserDetails() (LoggedInUserDetails, error) {
}
isAuthenticated := api.CallIsAuthenticated(httpClient)
// TODO: add refresh token
// if !isAuthenticated {
// accessTokenResponse, err := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken)
// if err == nil && accessTokenResponse.Token != "" {
// isAuthenticated = true
// userCreds.JTWToken = accessTokenResponse.Token
// }
// }
if !isAuthenticated {
accessTokenResponse, _ := api.CallGetNewAccessTokenWithRefreshToken(httpClient, userCreds.RefreshToken)
if accessTokenResponse.Token != "" {
isAuthenticated = true
userCreds.JTWToken = accessTokenResponse.Token
}
}
err = StoreUserCredsInKeyRing(&userCreds)
if err != nil {
log.Debug().Msg("unable to store your user credentials with new access token")
}
// err = StoreUserCredsInKeyRing(&userCreds)
// if err != nil {
// log.Debug().Msg("unable to store your user credentials with new access token")
// }
if !isAuthenticated {
return LoggedInUserDetails{

View File

@@ -45,5 +45,5 @@ func PrintErrorMessageAndExit(messages ...string) {
}
func printError(e error) {
color.New(color.FgRed).Fprintf(os.Stderr, "Hmm, we ran into an error: %v\n", e)
color.New(color.FgRed).Fprintf(os.Stderr, "error: %v\n", e)
}

View File

@@ -207,6 +207,10 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters) ([]models
return nil, err
}
if loggedInUserDetails.LoginExpired {
PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
}
workspaceFile, err := GetWorkSpaceFromFile()
if err != nil {
return nil, err

View File

@@ -1,258 +0,0 @@
package util
import (
"io"
"os"
"path"
"testing"
"github.com/Infisical/infisical-merge/packages/models"
)
// References to self should return the value unaltered
func Test_SubstituteSecrets_When_ReferenceToSelf(t *testing.T) {
var tests = []struct {
Key string
Value string
ExpectedValue string
}{
{Key: "A", Value: "${A}", ExpectedValue: "${A}"},
{Key: "A", Value: "${A} ${A}", ExpectedValue: "${A} ${A}"},
{Key: "A", Value: "${A}${A}", ExpectedValue: "${A}${A}"},
}
for _, test := range tests {
secret := models.SingleEnvironmentVariable{
Key: test.Key,
Value: test.Value,
}
secrets := []models.SingleEnvironmentVariable{secret}
result := SubstituteSecrets(secrets)
if result[0].Value != test.ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
}
}
}
func Test_SubstituteSecrets_When_ReferenceDoesNotExist(t *testing.T) {
var tests = []struct {
Key string
Value string
ExpectedValue string
}{
{Key: "A", Value: "${X}", ExpectedValue: "${X}"},
{Key: "A", Value: "${H}HELLO", ExpectedValue: "${H}HELLO"},
{Key: "A", Value: "${L}${S}", ExpectedValue: "${L}${S}"},
}
for _, test := range tests {
secret := models.SingleEnvironmentVariable{
Key: test.Key,
Value: test.Value,
}
secrets := []models.SingleEnvironmentVariable{secret}
result := SubstituteSecrets(secrets)
if result[0].Value != test.ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected %s but got %s for input %s", test.ExpectedValue, result[0].Value, test.Value)
}
}
}
func Test_SubstituteSecrets_When_ReferenceDoesNotExist_And_Self_Referencing(t *testing.T) {
tests := []struct {
Key string
Value string
ExpectedValue string
}{
{
Key: "O",
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
},
{
Key: "X",
Value: "DOMAIN",
ExpectedValue: "DOMAIN",
},
{
Key: "A",
Value: "*${A}* ${X}",
ExpectedValue: "*${A}* DOMAIN",
},
{
Key: "H",
Value: "${X} >>>",
ExpectedValue: "DOMAIN >>>",
},
{
Key: "P",
Value: "DOMAIN === ${A} ${H}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>>",
},
{
Key: "T",
Value: "${P} ==$$ ${X} ${UNKNOWN} ${A} ${P} ==$$ ${X} ${UNKNOWN} ${A}",
ExpectedValue: "DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A} DOMAIN === ${A} DOMAIN >>> ==$$ DOMAIN ${UNKNOWN} ${A}",
},
{
Key: "S",
Value: "${ SSS$$ ${HEY}",
ExpectedValue: "${ SSS$$ ${HEY}",
},
}
secrets := []models.SingleEnvironmentVariable{}
for _, test := range tests {
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
}
results := SubstituteSecrets(secrets)
for index, expanded := range results {
if expanded.Value != tests[index].ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
}
}
}
func Test_SubstituteSecrets_When_No_SubstituteNeeded(t *testing.T) {
tests := []struct {
Key string
Value string
ExpectedValue string
}{
{
Key: "DOMAIN",
Value: "infisical.com",
ExpectedValue: "infisical.com",
},
{
Key: "API_KEY",
Value: "hdgsvjshcgkdckhevdkd",
ExpectedValue: "hdgsvjshcgkdckhevdkd",
},
{
Key: "ENV",
Value: "PROD",
ExpectedValue: "PROD",
},
}
secrets := []models.SingleEnvironmentVariable{}
for _, test := range tests {
secrets = append(secrets, models.SingleEnvironmentVariable{Key: test.Key, Value: test.Value})
}
results := SubstituteSecrets(secrets)
for index, expanded := range results {
if expanded.Value != tests[index].ExpectedValue {
t.Errorf("Test_SubstituteSecrets_When_ReferenceToSelf: expected [%s] but got [%s] for input [%s]", tests[index].ExpectedValue, expanded.Value, tests[index].Value)
}
}
}
func Test_Read_Env_From_File(t *testing.T) {
type testCase struct {
TestFile string
ExpectedEnv string
}
var cases = []testCase{
{
TestFile: "testdata/infisical-default-env.json",
ExpectedEnv: "myDefaultEnv",
},
{
TestFile: "testdata/infisical-branch-env.json",
ExpectedEnv: "myMainEnv",
},
{
TestFile: "testdata/infisical-no-matching-branch-env.json",
ExpectedEnv: "myDefaultEnv",
},
}
// create a tmp directory for testing
testDir, err := os.MkdirTemp(os.TempDir(), "infisical-test")
if err != nil {
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to create temp directory: %s", err)
}
// safe the current working directory
originalDir, err := os.Getwd()
if err != nil {
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to get current working directory: %s", err)
}
// backup the original git command
originalGitCmd := getCurrentBranchCmd
// make sure to clean up after the test
t.Cleanup(func() {
os.Chdir(originalDir)
os.RemoveAll(testDir)
getCurrentBranchCmd = originalGitCmd
})
// mock the git command to return "main" as the current branch
getCurrentBranchCmd = execCmd{cmd: "echo", args: []string{"main"}}
for _, c := range cases {
// make sure we start in the original directory
err = os.Chdir(originalDir)
if err != nil {
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
}
// remove old test file if it exists
err = os.Remove(path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
if err != nil && !os.IsNotExist(err) {
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to remove old test file: %s", err)
}
// deploy the test file
copyTestFile(t, c.TestFile, path.Join(testDir, INFISICAL_WORKSPACE_CONFIG_FILE_NAME))
// change the working directory to the tmp directory
err = os.Chdir(testDir)
if err != nil {
t.Errorf("Test_Read_DefaultEnv_From_File: Failed to change working directory: %s", err)
}
// get env from file
env := GetEnvFromWorkspaceFile()
if env != c.ExpectedEnv {
t.Errorf("Test_Read_DefaultEnv_From_File: Expected env to be %s but got %s", c.ExpectedEnv, env)
}
}
}
func copyTestFile(t *testing.T, src, dst string) {
srcFile, err := os.Open(src)
if err != nil {
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to open source file: %s", err)
}
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to create destination file: %s", err)
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
t.Errorf("Test_Read_Env_From_File_By_Branch: Failed to copy file: %s", err)
}
}

View File

@@ -1,73 +0,0 @@
package util
import (
"fmt"
"os"
"github.com/99designs/keyring"
"golang.org/x/term"
)
func GetCurrentVaultBackend() (keyring.BackendType, error) {
configFile, err := GetConfigFile()
if err != nil {
return "", fmt.Errorf("getCurrentVaultBackend: unable to get config file [err=%s]", err)
}
if configFile.VaultBackendType == "" {
return keyring.AvailableBackends()[0], nil
}
return configFile.VaultBackendType, nil
}
func GetKeyRing() (keyring.Keyring, error) {
currentVaultBackend, err := GetCurrentVaultBackend()
if err != nil {
return nil, fmt.Errorf("GetKeyRing: unable to get the current vault backend, [err=%s]", err)
}
keyringInstanceConfig := keyring.Config{
FilePasswordFunc: fileKeyringPassphrasePrompt,
ServiceName: KEYRING_SERVICE_NAME,
LibSecretCollectionName: KEYRING_SERVICE_NAME,
KWalletAppID: KEYRING_SERVICE_NAME,
KWalletFolder: KEYRING_SERVICE_NAME,
KeychainName: "login", // default so user will not be prompted
KeychainTrustApplication: true,
WinCredPrefix: KEYRING_SERVICE_NAME,
FileDir: fmt.Sprintf("~/%s-file-vault", KEYRING_SERVICE_NAME),
KeychainAccessibleWhenUnlocked: true,
}
// if the user explicitly sets a vault backend, then only use that
if currentVaultBackend != "" {
keyringInstanceConfig.AllowedBackends = []keyring.BackendType{keyring.BackendType(currentVaultBackend)}
}
keyringInstance, err := keyring.Open(keyringInstanceConfig)
if err != nil {
return nil, fmt.Errorf("GetKeyRing: Unable to create instance of Keyring because of [err=%s]", err)
}
return keyringInstance, nil
}
func fileKeyringPassphrasePrompt(prompt string) (string, error) {
if password, ok := os.LookupEnv("VAULT_PASS"); ok {
return password, nil
} else if password, ok := os.LookupEnv("INFISICAL_VAULT_FILE_PASSPHRASE"); ok {
return password, nil
} else {
fmt.Println("To avoid repeatedly typing your password, set the environment variable `VAULT_PASS` to your password")
}
fmt.Fprintf(os.Stderr, "%s:", prompt)
b, err := term.ReadPassword(int(os.Stdin.Fd()))
if err != nil {
return "", err
}
fmt.Println("")
return string(b), nil
}

View File

@@ -23,7 +23,8 @@
},
"feedback": {
"suggestEdit": true,
"raiseIssue": true
"raiseIssue": true,
"thumbsRating": true
},
"api": {
"baseUrl": ["https://app.infisical.com", "http://localhost:8080"],

View File

@@ -7,9 +7,6 @@ description: "Use our Helm chart to Install Infisical on your Kubernetes cluster
- Installed [Helm package manager](https://helm.sh/) version v3.11.3 or greater
- You have [kubectl](https://kubernetes.io/docs/reference/kubectl/kubectl/) installed and connected to your kubernetes cluster
<iframe width="100%" height="375" src="https://www.youtube.com/embed/ugJZSCcZaV8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
By deploying Infisical on Kubernetes, you can take advantage of its features to ensure that the application is fault-tolerant, highly available, and scalable.
To make the installation process easier and more streamlined, we have created a Helm chart that you can use to install Infisical on Kubernetes.
@@ -34,10 +31,11 @@ By default, the application will use the latest tag to retrieve the required Doc
However, it's important to specify a particular version of Infisical during installation to prevent any significant updates from disrupting your deployment.
View [properties for frontend and backend](https://github.com/Infisical/infisical/tree/main/helm-charts/infisical#parameters).
To determine the appropriate versions to use for the docker images, follow the links bellow
- [frontend Docker image](https://hub.docker.com/r/infisical/frontend/tags)
- [backend Docker image](https://hub.docker.com/r/infisical/backend/tags)
<Tip>
To find the latest version number of Infisical, follow the links bellow
- [frontend Docker image](https://hub.docker.com/r/infisical/frontend/tags)
- [backend Docker image](https://hub.docker.com/r/infisical/backend/tags)
</Tip>
```yaml simple-values-example.yaml
frontend:
@@ -45,14 +43,14 @@ frontend:
replicaCount: 2
image:
repository: infisical/frontend
tag: "v0.1.3"
tag: "v0.26.0" # <--- frontend version
pullPolicy: Always
backend:
replicaCount: 2
image:
repository: infisical/backend
tag: "v0.1.3"
tag: "v0.26.0" # <--- backend version
pullPolicy: Always
```

View File

@@ -1,4 +1,4 @@
import { forwardRef, InputHTMLAttributes, ReactNode } from "react";
import { ChangeEvent, forwardRef, InputHTMLAttributes, ReactNode } from "react";
import { cva, VariantProps } from "cva";
import { twMerge } from "tailwind-merge";
@@ -10,6 +10,7 @@ type Props = {
rightIcon?: ReactNode;
isDisabled?: boolean;
isReadOnly?: boolean;
autoCapitalization?: boolean;
};
const inputVariants = cva(
@@ -80,10 +81,19 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
variant = "filled",
size = "md",
isReadOnly,
autoCapitalization,
...props
},
ref
): JSX.Element => {
const handleInput = (event: ChangeEvent<HTMLInputElement>) => {
console.log(123, props, autoCapitalization)
if (autoCapitalization) {
// eslint-disable-next-line no-param-reassign
event.target.value = event.target.value.toUpperCase();
}
};
return (
<div className={inputParentContainerVariants({ isRounded, isError, isFullWidth, variant })}>
{leftIcon && <span className="absolute left-0 ml-3 text-sm">{leftIcon}</span>}
@@ -93,6 +103,7 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
ref={ref}
readOnly={isReadOnly}
disabled={isDisabled}
onInput={handleInput}
className={twMerge(
leftIcon ? "pl-10" : "pl-2.5",
rightIcon ? "pr-10" : "pr-2.5",

View File

@@ -971,6 +971,7 @@ export const DashboardPage = () => {
register={register}
control={control}
setValue={setValue}
autoCapitalization={currentWorkspace?.autoCapitalization}
/>
))}
{!isReadOnly && !isRollbackMode && (

View File

@@ -79,6 +79,7 @@ type Props = {
setValue: UseFormSetValue<FormData>;
isKeyError?: boolean;
keyError?: string;
autoCapitalization?: boolean;
};
export const SecretInputRow = memo(
@@ -98,7 +99,8 @@ export const SecretInputRow = memo(
setValue,
isKeyError,
keyError,
secUniqId
secUniqId,
autoCapitalization
}: Props): JSX.Element => {
const isKeySubDisabled = useRef<boolean>(false);
// comment management in a row
@@ -243,6 +245,7 @@ export const SecretInputRow = memo(
isKeySubDisabled.current = false;
field.onBlur();
}}
autoCapitalization={autoCapitalization}
/>
</div>
</td>