mirror of
https://github.com/Infisical/infisical.git
synced 2025-08-02 08:27:38 +00:00
Compare commits
16 Commits
daniel/str
...
misc/incre
Author | SHA1 | Date | |
---|---|---|---|
|
73e73c5489 | ||
|
f3bcdf74df | ||
|
87cd3ea727 | ||
|
114f42fc14 | ||
|
6daa1aa221 | ||
|
52f85753c5 | ||
|
0a5634aa05 | ||
|
3e8b9aa296 | ||
|
67058d8b55 | ||
|
d112ec2f0a | ||
|
96c0e718d0 | ||
|
522e1dfd0e | ||
|
08145f9b96 | ||
|
1f4db2bd80 | ||
|
bed8efb24c | ||
|
aa9af7b41c |
@@ -0,0 +1,19 @@
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.IdentityMetadata, "value")) {
|
||||
await knex.schema.alterTable(TableName.IdentityMetadata, (t) => {
|
||||
t.string("value", 1020).alter();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
if (await knex.schema.hasColumn(TableName.IdentityMetadata, "value")) {
|
||||
await knex.schema.alterTable(TableName.IdentityMetadata, (t) => {
|
||||
t.string("value", 255).alter();
|
||||
});
|
||||
}
|
||||
}
|
@@ -128,7 +128,10 @@ export const registerSamlRouter = async (server: FastifyZodProvider) => {
|
||||
.map((key) => {
|
||||
// for the ones like in format: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/email
|
||||
const formatedKey = key.startsWith("http") ? key.split("/").at(-1) || "" : key;
|
||||
return { key: formatedKey, value: String((profile.attributes as Record<string, string>)[key]) };
|
||||
return {
|
||||
key: formatedKey,
|
||||
value: String((profile.attributes as Record<string, string>)[key]).substring(0, 1020)
|
||||
};
|
||||
})
|
||||
.filter((el) => el.key && !["email", "firstName", "lastName"].includes(el.key));
|
||||
|
||||
|
@@ -493,6 +493,9 @@ export const registerRoutes = async (
|
||||
authDAL,
|
||||
userDAL
|
||||
});
|
||||
|
||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||
|
||||
const orgService = orgServiceFactory({
|
||||
userAliasDAL,
|
||||
identityMetadataDAL,
|
||||
@@ -515,7 +518,8 @@ export const registerRoutes = async (
|
||||
userDAL,
|
||||
groupDAL,
|
||||
orgBotDAL,
|
||||
oidcConfigDAL
|
||||
oidcConfigDAL,
|
||||
projectBotService
|
||||
});
|
||||
const signupService = authSignupServiceFactory({
|
||||
tokenService,
|
||||
@@ -574,7 +578,6 @@ export const registerRoutes = async (
|
||||
secretScanningDAL,
|
||||
secretScanningQueue
|
||||
});
|
||||
const projectBotService = projectBotServiceFactory({ permissionService, projectBotDAL, projectDAL });
|
||||
|
||||
const projectMembershipService = projectMembershipServiceFactory({
|
||||
projectMembershipDAL,
|
||||
@@ -838,7 +841,10 @@ export const registerRoutes = async (
|
||||
integrationAuthDAL,
|
||||
snapshotDAL,
|
||||
snapshotSecretV2BridgeDAL,
|
||||
secretApprovalRequestDAL
|
||||
secretApprovalRequestDAL,
|
||||
projectKeyDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
orgService
|
||||
});
|
||||
const secretImportService = secretImportServiceFactory({
|
||||
licenseService,
|
||||
|
@@ -138,7 +138,7 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const auditLogs = await server.services.auditLog.listAuditLogs({
|
||||
filter: {
|
||||
|
@@ -214,22 +214,21 @@ export const importDataIntoInfisicalFn = async ({
|
||||
name: "Create secret"
|
||||
});
|
||||
|
||||
const secretsByKeys = await secretDAL.findBySecretKeys(
|
||||
folder.id,
|
||||
secrets.map((el) => ({
|
||||
key: el.secretKey,
|
||||
type: SecretType.Shared
|
||||
})),
|
||||
tx
|
||||
);
|
||||
if (secretsByKeys.length) {
|
||||
throw new BadRequestError({
|
||||
message: `Secret already exist: ${secretsByKeys.map((el) => el.key).join(",")}`
|
||||
});
|
||||
}
|
||||
|
||||
const secretBatches = chunkArray(secrets, 2500);
|
||||
for await (const secretBatch of secretBatches) {
|
||||
const secretsByKeys = await secretDAL.findBySecretKeys(
|
||||
folder.id,
|
||||
secretBatch.map((el) => ({
|
||||
key: el.secretKey,
|
||||
type: SecretType.Shared
|
||||
})),
|
||||
tx
|
||||
);
|
||||
if (secretsByKeys.length) {
|
||||
throw new BadRequestError({
|
||||
message: `Secret already exist: ${secretsByKeys.map((el) => el.key).join(",")}`
|
||||
});
|
||||
}
|
||||
await fnSecretBulkInsert({
|
||||
inputSecrets: secretBatch.map((el) => {
|
||||
const references = getAllNestedSecretReferences(el.secretValue);
|
||||
|
@@ -41,8 +41,9 @@ import { TAuthTokenServiceFactory } from "../auth-token/auth-token-service";
|
||||
import { TokenType } from "../auth-token/auth-token-types";
|
||||
import { TIdentityMetadataDALFactory } from "../identity/identity-metadata-dal";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { assignWorkspaceKeysToMembers } from "../project/project-fns";
|
||||
import { assignWorkspaceKeysToMembers, createProjectKey } from "../project/project-fns";
|
||||
import { TProjectBotDALFactory } from "../project-bot/project-bot-dal";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
@@ -80,7 +81,7 @@ type TOrgServiceFactoryDep = {
|
||||
TProjectMembershipDALFactory,
|
||||
"findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction"
|
||||
>;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey" | "create">;
|
||||
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOne" | "findById">;
|
||||
incidentContactDAL: TIncidentContactsDALFactory;
|
||||
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne" | "findEnforceableSamlCfg">;
|
||||
@@ -94,8 +95,9 @@ type TOrgServiceFactoryDep = {
|
||||
>;
|
||||
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
|
||||
projectRoleDAL: Pick<TProjectRoleDALFactory, "find">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany">;
|
||||
projectBotDAL: Pick<TProjectBotDALFactory, "findOne" | "updateById">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "create">;
|
||||
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
|
||||
};
|
||||
|
||||
export type TOrgServiceFactory = ReturnType<typeof orgServiceFactory>;
|
||||
@@ -122,7 +124,8 @@ export const orgServiceFactory = ({
|
||||
oidcConfigDAL,
|
||||
projectBotDAL,
|
||||
projectUserMembershipRoleDAL,
|
||||
identityMetadataDAL
|
||||
identityMetadataDAL,
|
||||
projectBotService
|
||||
}: TOrgServiceFactoryDep) => {
|
||||
/*
|
||||
* Get organization details by the organization id
|
||||
@@ -718,20 +721,67 @@ export const orgServiceFactory = ({
|
||||
|
||||
const customRolesGroupBySlug = groupBy(customRoles, ({ slug }) => slug);
|
||||
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||
if (!ghostUser) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner"
|
||||
});
|
||||
}
|
||||
// this will auto generate bot
|
||||
const { botKey, bot: autoGeneratedBot } = await projectBotService.getBotKey(projectId, true);
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUser.id, projectId, tx);
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner's latest key"
|
||||
const ghostUser = await projectDAL.findProjectGhostUser(projectId, tx);
|
||||
let ghostUserId = ghostUser?.id;
|
||||
|
||||
// backfill missing ghost user
|
||||
if (!ghostUserId) {
|
||||
const newGhostUser = await addGhostUser(project.orgId, tx);
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: newGhostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
privateKey: newGhostUser.keys.plainPrivateKey,
|
||||
plainProjectKey: botKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: newGhostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: newGhostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(
|
||||
newGhostUser.keys.plainPrivateKey
|
||||
);
|
||||
if (autoGeneratedBot) {
|
||||
await projectBotDAL.updateById(
|
||||
autoGeneratedBot.id,
|
||||
{
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: newGhostUser.keys.publicKey,
|
||||
senderId: newGhostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
ghostUserId = newGhostUser.user.id;
|
||||
}
|
||||
|
||||
const bot = await projectBotDAL.findOne({ projectId }, tx);
|
||||
@@ -742,6 +792,14 @@ export const orgServiceFactory = ({
|
||||
});
|
||||
}
|
||||
|
||||
const ghostUserLatestKey = await projectKeyDAL.findLatestProjectKey(ghostUserId, projectId, tx);
|
||||
if (!ghostUserLatestKey) {
|
||||
throw new NotFoundError({
|
||||
name: "InviteUser",
|
||||
message: "Failed to find project owner's latest key"
|
||||
});
|
||||
}
|
||||
|
||||
const botPrivateKey = infisicalSymmetricDecrypt({
|
||||
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
|
||||
iv: bot.iv,
|
||||
@@ -785,7 +843,7 @@ export const orgServiceFactory = ({
|
||||
newWsMembers.map((el) => ({
|
||||
encryptedKey: el.workspaceEncryptedKey,
|
||||
nonce: el.workspaceEncryptedNonce,
|
||||
senderId: ghostUser.id,
|
||||
senderId: ghostUserId,
|
||||
receiverId: el.orgMembershipId,
|
||||
projectId
|
||||
})),
|
||||
|
@@ -24,14 +24,14 @@ export const getBotKeyFnFactory = (
|
||||
projectBotDAL: TProjectBotDALFactory,
|
||||
projectDAL: Pick<TProjectDALFactory, "findById">
|
||||
) => {
|
||||
const getBotKeyFn = async (projectId: string) => {
|
||||
const getBotKeyFn = async (projectId: string, shouldGetBotKey?: boolean) => {
|
||||
const project = await projectDAL.findById(projectId);
|
||||
if (!project)
|
||||
throw new NotFoundError({
|
||||
message: "Project not found during bot lookup. Are you sure you are using the correct project ID?"
|
||||
});
|
||||
|
||||
if (project.version === 3) {
|
||||
if (project.version === 3 && !shouldGetBotKey) {
|
||||
return { project, shouldUseSecretV2Bridge: true };
|
||||
}
|
||||
|
||||
@@ -65,8 +65,9 @@ export const getBotKeyFnFactory = (
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(botKey.privateKey);
|
||||
const encryptedWorkspaceKey = encryptAsymmetric(workspaceKey, botKey.publicKey, userPrivateKey);
|
||||
|
||||
let botId;
|
||||
if (!bot) {
|
||||
await projectBotDAL.create({
|
||||
const newBot = await projectBotDAL.create({
|
||||
name: "Infisical Bot (Ghost)",
|
||||
projectId,
|
||||
isActive: true,
|
||||
@@ -80,8 +81,9 @@ export const getBotKeyFnFactory = (
|
||||
encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce,
|
||||
senderId: projectV1Keys.userId
|
||||
});
|
||||
botId = newBot.id;
|
||||
} else {
|
||||
await projectBotDAL.updateById(bot.id, {
|
||||
const updatedBot = await projectBotDAL.updateById(bot.id, {
|
||||
isActive: true,
|
||||
tag,
|
||||
iv,
|
||||
@@ -93,8 +95,10 @@ export const getBotKeyFnFactory = (
|
||||
encryptedProjectKeyNonce: encryptedWorkspaceKey.nonce,
|
||||
senderId: projectV1Keys.userId
|
||||
});
|
||||
botId = updatedBot.id;
|
||||
}
|
||||
return { botKey: workspaceKey, project, shouldUseSecretV2Bridge: false };
|
||||
|
||||
return { botKey: workspaceKey, project, shouldUseSecretV2Bridge: false, bot: { id: botId } };
|
||||
}
|
||||
|
||||
const botPrivateKey = getBotPrivateKey({ bot });
|
||||
@@ -104,7 +108,7 @@ export const getBotKeyFnFactory = (
|
||||
nonce: bot.encryptedProjectKeyNonce,
|
||||
publicKey: bot.sender.publicKey
|
||||
});
|
||||
return { botKey, project, shouldUseSecretV2Bridge: false };
|
||||
return { botKey, project, shouldUseSecretV2Bridge: false, bot: { id: bot.id } };
|
||||
};
|
||||
|
||||
return getBotKeyFn;
|
||||
|
@@ -27,8 +27,8 @@ export const projectBotServiceFactory = ({
|
||||
}: TProjectBotServiceFactoryDep) => {
|
||||
const getBotKeyFn = getBotKeyFnFactory(projectBotDAL, projectDAL);
|
||||
|
||||
const getBotKey = async (projectId: string) => {
|
||||
return getBotKeyFn(projectId);
|
||||
const getBotKey = async (projectId: string, shouldGetBotKey?: boolean) => {
|
||||
return getBotKeyFn(projectId, shouldGetBotKey);
|
||||
};
|
||||
|
||||
const findBotByProjectId = async ({
|
||||
|
@@ -17,6 +17,7 @@ import { TSnapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/sn
|
||||
import { KeyStorePrefixes, KeyStoreTtls, TKeyStoreFactory } from "@app/keystore/keystore";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
|
||||
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
|
||||
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { getTimeDifferenceInSeconds, groupBy, isSamePath, unique } from "@app/lib/fn";
|
||||
@@ -37,10 +38,14 @@ import { syncIntegrationSecrets } from "../integration-auth/integration-sync-sec
|
||||
import { TKmsServiceFactory } from "../kms/kms-service";
|
||||
import { KmsDataKey } from "../kms/kms-types";
|
||||
import { TOrgDALFactory } from "../org/org-dal";
|
||||
import { TOrgServiceFactory } from "../org/org-service";
|
||||
import { TProjectDALFactory } from "../project/project-dal";
|
||||
import { createProjectKey } from "../project/project-fns";
|
||||
import { TProjectBotServiceFactory } from "../project-bot/project-bot-service";
|
||||
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
|
||||
import { TProjectKeyDALFactory } from "../project-key/project-key-dal";
|
||||
import { TProjectMembershipDALFactory } from "../project-membership/project-membership-dal";
|
||||
import { TProjectUserMembershipRoleDALFactory } from "../project-membership/project-user-membership-role-dal";
|
||||
import { TSecretFolderDALFactory } from "../secret-folder/secret-folder-dal";
|
||||
import { TSecretImportDALFactory } from "../secret-import/secret-import-dal";
|
||||
import { fnSecretsV2FromImports } from "../secret-import/secret-import-fns";
|
||||
@@ -77,7 +82,8 @@ type TSecretQueueFactoryDep = {
|
||||
projectEnvDAL: Pick<TProjectEnvDALFactory, "findOne" | "find">;
|
||||
projectDAL: TProjectDALFactory;
|
||||
projectBotDAL: TProjectBotDALFactory;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findAllProjectMembers">;
|
||||
projectKeyDAL: Pick<TProjectKeyDALFactory, "create">;
|
||||
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "findAllProjectMembers" | "create">;
|
||||
smtpService: TSmtpService;
|
||||
orgDAL: Pick<TOrgDALFactory, "findOrgByProjectId">;
|
||||
secretVersionDAL: TSecretVersionDALFactory;
|
||||
@@ -95,6 +101,8 @@ type TSecretQueueFactoryDep = {
|
||||
snapshotSecretV2BridgeDAL: Pick<TSnapshotSecretV2DALFactory, "insertMany" | "batchInsert">;
|
||||
keyStore: Pick<TKeyStoreFactory, "acquireLock" | "setItemWithExpiry" | "getItem">;
|
||||
auditLogService: Pick<TAuditLogServiceFactory, "createAuditLog">;
|
||||
orgService: Pick<TOrgServiceFactory, "addGhostUser">;
|
||||
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "create">;
|
||||
};
|
||||
|
||||
export type TGetSecrets = {
|
||||
@@ -111,6 +119,8 @@ type TIntegrationSecret = Record<
|
||||
string,
|
||||
{ value: string; comment?: string; skipMultilineEncoding?: boolean | null | undefined }
|
||||
>;
|
||||
|
||||
// TODO(akhilmhdh): split this into multiple queue
|
||||
export const secretQueueFactory = ({
|
||||
queueService,
|
||||
integrationDAL,
|
||||
@@ -141,7 +151,10 @@ export const secretQueueFactory = ({
|
||||
snapshotSecretV2BridgeDAL,
|
||||
secretApprovalRequestDAL,
|
||||
keyStore,
|
||||
auditLogService
|
||||
auditLogService,
|
||||
orgService,
|
||||
projectUserMembershipRoleDAL,
|
||||
projectKeyDAL
|
||||
}: TSecretQueueFactoryDep) => {
|
||||
const removeSecretReminder = async (dto: TRemoveSecretReminderDTO) => {
|
||||
const appCfg = getConfig();
|
||||
@@ -1028,11 +1041,13 @@ export const secretQueueFactory = ({
|
||||
const {
|
||||
botKey,
|
||||
shouldUseSecretV2Bridge: isProjectUpgradedToV3,
|
||||
project
|
||||
project,
|
||||
bot
|
||||
} = await projectBotService.getBotKey(projectId);
|
||||
if (isProjectUpgradedToV3 || project.upgradeStatus === ProjectUpgradeStatus.InProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!botKey) throw new NotFoundError({ message: "Project bot not found" });
|
||||
await projectDAL.updateById(projectId, { upgradeStatus: ProjectUpgradeStatus.InProgress });
|
||||
|
||||
@@ -1044,6 +1059,57 @@ export const secretQueueFactory = ({
|
||||
const folders = await folderDAL.findByProjectId(projectId);
|
||||
// except secret version and snapshot migrate rest of everything first in a transaction
|
||||
await secretDAL.transaction(async (tx) => {
|
||||
// if project v1 create the project ghost user
|
||||
if (project.version === ProjectVersion.V1) {
|
||||
const ghostUser = await orgService.addGhostUser(project.orgId, tx);
|
||||
const projectMembership = await projectMembershipDAL.create(
|
||||
{
|
||||
userId: ghostUser.user.id,
|
||||
projectId: project.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
await projectUserMembershipRoleDAL.create(
|
||||
{ projectMembershipId: projectMembership.id, role: ProjectMembershipRole.Admin },
|
||||
tx
|
||||
);
|
||||
|
||||
const { key: encryptedProjectKey, iv: encryptedProjectKeyIv } = createProjectKey({
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
privateKey: ghostUser.keys.plainPrivateKey,
|
||||
plainProjectKey: botKey
|
||||
});
|
||||
|
||||
// 4. Save the project key for the ghost user.
|
||||
await projectKeyDAL.create(
|
||||
{
|
||||
projectId: project.id,
|
||||
receiverId: ghostUser.user.id,
|
||||
encryptedKey: encryptedProjectKey,
|
||||
nonce: encryptedProjectKeyIv,
|
||||
senderId: ghostUser.user.id
|
||||
},
|
||||
tx
|
||||
);
|
||||
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(ghostUser.keys.plainPrivateKey);
|
||||
await projectBotDAL.updateById(
|
||||
bot.id,
|
||||
{
|
||||
tag,
|
||||
iv,
|
||||
encryptedProjectKey,
|
||||
encryptedProjectKeyNonce: encryptedProjectKeyIv,
|
||||
encryptedPrivateKey: ciphertext,
|
||||
isActive: true,
|
||||
publicKey: ghostUser.keys.publicKey,
|
||||
senderId: ghostUser.user.id,
|
||||
algorithm,
|
||||
keyEncoding: encoding
|
||||
},
|
||||
tx
|
||||
);
|
||||
}
|
||||
|
||||
for (const folder of folders) {
|
||||
const folderId = folder.id;
|
||||
/*
|
||||
|
@@ -415,6 +415,10 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
|
||||
req.SetQueryParam("recursive", "true")
|
||||
}
|
||||
|
||||
if request.ExpandSecretReferences {
|
||||
req.SetQueryParam("expandSecretReferences", "true")
|
||||
}
|
||||
|
||||
response, err := req.Get(fmt.Sprintf("%v/v3/secrets/raw", config.INFISICAL_URL))
|
||||
|
||||
if err != nil {
|
||||
|
@@ -569,12 +569,13 @@ type CreateDynamicSecretLeaseV1Response struct {
|
||||
}
|
||||
|
||||
type GetRawSecretsV3Request struct {
|
||||
Environment string `json:"environment"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
IncludeImport bool `json:"include_imports"`
|
||||
Recursive bool `json:"recursive"`
|
||||
TagSlugs string `json:"tagSlugs,omitempty"`
|
||||
Environment string `json:"environment"`
|
||||
WorkspaceId string `json:"workspaceId"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
IncludeImport bool `json:"include_imports"`
|
||||
Recursive bool `json:"recursive"`
|
||||
TagSlugs string `json:"tagSlugs,omitempty"`
|
||||
ExpandSecretReferences bool `json:"expandSecretReferences,omitempty"`
|
||||
}
|
||||
|
||||
type GetRawSecretsV3Response struct {
|
||||
@@ -587,6 +588,7 @@ type GetRawSecretsV3Response struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
SecretValue string `json:"secretValue"`
|
||||
SecretComment string `json:"secretComment"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
} `json:"secrets"`
|
||||
Imports []ImportedRawSecretV3 `json:"imports"`
|
||||
ETag string
|
||||
@@ -610,6 +612,7 @@ type GetRawSecretV3ByNameResponse struct {
|
||||
SecretKey string `json:"secretKey"`
|
||||
SecretValue string `json:"secretValue"`
|
||||
SecretComment string `json:"secretComment"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
} `json:"secret"`
|
||||
ETag string
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
@@ -311,9 +312,34 @@ func ParseAgentConfig(configFile []byte) (*Config, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string) ([]models.SingleEnvironmentVariable, error) {
|
||||
return func(projectID, envSlug, secretPath string) ([]models.SingleEnvironmentVariable, error) {
|
||||
res, err := util.GetPlainTextSecretsV3(accessToken, projectID, envSlug, secretPath, false, false, "")
|
||||
type secretArguments struct {
|
||||
IsRecursive bool `json:"recursive"`
|
||||
ShouldExpandSecretReferences *bool `json:"expandSecretReferences,omitempty"`
|
||||
}
|
||||
|
||||
func (s *secretArguments) SetDefaults() {
|
||||
if s.ShouldExpandSecretReferences == nil {
|
||||
var bool = true
|
||||
s.ShouldExpandSecretReferences = &bool
|
||||
}
|
||||
}
|
||||
|
||||
func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string, ...string) ([]models.SingleEnvironmentVariable, error) {
|
||||
// ...string is because golang doesn't have optional arguments.
|
||||
// thus we make it slice and pick it only first element
|
||||
return func(projectID, envSlug, secretPath string, args ...string) ([]models.SingleEnvironmentVariable, error) {
|
||||
var parsedArguments secretArguments
|
||||
// to make it optional
|
||||
if len(args) > 0 {
|
||||
err := json.Unmarshal([]byte(args[0]), &parsedArguments)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
parsedArguments.SetDefaults()
|
||||
|
||||
res, err := util.GetPlainTextSecretsV3(accessToken, projectID, envSlug, secretPath, false, parsedArguments.IsRecursive, "", *parsedArguments.ShouldExpandSecretReferences)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -322,9 +348,7 @@ func secretTemplateFunction(accessToken string, existingEtag string, currentEtag
|
||||
*currentEtag = res.Etag
|
||||
}
|
||||
|
||||
expandedSecrets := util.ExpandSecrets(res.Secrets, models.ExpandSecretsAuthentication{UniversalAuthAccessToken: accessToken}, "")
|
||||
|
||||
return expandedSecrets, nil
|
||||
return res.Secrets, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,7 +480,6 @@ func ProcessLiteralTemplate(templateId int, templateString string, data interfac
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
|
||||
type AgentManager struct {
|
||||
accessToken string
|
||||
accessTokenTTL time.Duration
|
||||
|
@@ -87,11 +87,12 @@ var exportCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
TagSlugs: tagSlugs,
|
||||
WorkspaceId: projectId,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Environment: environmentName,
|
||||
TagSlugs: tagSlugs,
|
||||
WorkspaceId: projectId,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
ExpandSecretReferences: shouldExpandSecrets,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
@@ -137,18 +138,6 @@ var exportCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
var output string
|
||||
if shouldExpandSecrets {
|
||||
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
secrets = util.FilterSecretsByTag(secrets, tagSlugs)
|
||||
secrets = util.SortSecretsByKeys(secrets)
|
||||
|
||||
|
@@ -137,15 +137,16 @@ var runCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
ExpandSecretReferences: shouldExpandSecrets,
|
||||
}
|
||||
|
||||
injectableEnvironment, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, shouldExpandSecrets, token)
|
||||
injectableEnvironment, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, token)
|
||||
if err != nil {
|
||||
util.HandleError(err, "Could not fetch secrets", "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
}
|
||||
@@ -153,7 +154,7 @@ var runCmd = &cobra.Command{
|
||||
log.Debug().Msgf("injecting the following environment variables into shell: %v", injectableEnvironment.Variables)
|
||||
|
||||
if watchMode {
|
||||
executeCommandWithWatchMode(command, args, watchModeInterval, request, projectConfigDir, shouldExpandSecrets, secretOverriding, token)
|
||||
executeCommandWithWatchMode(command, args, watchModeInterval, request, projectConfigDir, secretOverriding, token)
|
||||
} else {
|
||||
if cmd.Flags().Changed("command") {
|
||||
command := cmd.Flag("command").Value.String()
|
||||
@@ -306,7 +307,7 @@ func waitForExitCommand(cmd *exec.Cmd) (int, error) {
|
||||
return waitStatus.ExitStatus(), nil
|
||||
}
|
||||
|
||||
func executeCommandWithWatchMode(commandFlag string, args []string, watchModeInterval int, request models.GetAllSecretsParameters, projectConfigDir string, expandSecrets bool, secretOverriding bool, token *models.TokenDetails) {
|
||||
func executeCommandWithWatchMode(commandFlag string, args []string, watchModeInterval int, request models.GetAllSecretsParameters, projectConfigDir string, secretOverriding bool, token *models.TokenDetails) {
|
||||
|
||||
var cmd *exec.Cmd
|
||||
var err error
|
||||
@@ -420,7 +421,7 @@ func executeCommandWithWatchMode(commandFlag string, args []string, watchModeInt
|
||||
<-recheckSecretsChannel
|
||||
watchMutex.Lock()
|
||||
|
||||
newEnvironmentVariables, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, expandSecrets, token)
|
||||
newEnvironmentVariables, err := fetchAndFormatSecretsForShell(request, projectConfigDir, secretOverriding, token)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("[HOT RELOAD] Failed to fetch secrets")
|
||||
continue
|
||||
@@ -437,7 +438,7 @@ func executeCommandWithWatchMode(commandFlag string, args []string, watchModeInt
|
||||
}
|
||||
}
|
||||
|
||||
func fetchAndFormatSecretsForShell(request models.GetAllSecretsParameters, projectConfigDir string, secretOverriding bool, shouldExpandSecrets bool, token *models.TokenDetails) (models.InjectableEnvironmentResult, error) {
|
||||
func fetchAndFormatSecretsForShell(request models.GetAllSecretsParameters, projectConfigDir string, secretOverriding bool, token *models.TokenDetails) (models.InjectableEnvironmentResult, error) {
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
request.InfisicalToken = token.Token
|
||||
@@ -457,19 +458,6 @@ func fetchAndFormatSecretsForShell(request models.GetAllSecretsParameters, proje
|
||||
secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED)
|
||||
}
|
||||
|
||||
if shouldExpandSecrets {
|
||||
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, projectConfigDir)
|
||||
}
|
||||
|
||||
secretsByKey := getSecretsByKeys(secrets)
|
||||
environmentVariables := make(map[string]string)
|
||||
|
||||
|
@@ -79,12 +79,13 @@ var secretsCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
ExpandSecretReferences: shouldExpandSecrets,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
@@ -104,17 +105,6 @@ var secretsCmd = &cobra.Command{
|
||||
secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED)
|
||||
}
|
||||
|
||||
if shouldExpandSecrets {
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
|
||||
// Sort the secrets by key so we can create a consistent output
|
||||
secrets = util.SortSecretsByKeys(secrets)
|
||||
|
||||
@@ -382,12 +372,13 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
}
|
||||
|
||||
request := models.GetAllSecretsParameters{
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
Environment: environmentName,
|
||||
WorkspaceId: projectId,
|
||||
TagSlugs: tagSlugs,
|
||||
SecretsPath: secretsPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
ExpandSecretReferences: shouldExpand,
|
||||
}
|
||||
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
@@ -407,17 +398,6 @@ func getSecretsByNames(cmd *cobra.Command, args []string) {
|
||||
secrets = util.OverrideSecrets(secrets, util.SECRET_TYPE_SHARED)
|
||||
}
|
||||
|
||||
if shouldExpand {
|
||||
authParams := models.ExpandSecretsAuthentication{}
|
||||
if token != nil && token.Type == util.SERVICE_TOKEN_IDENTIFIER {
|
||||
authParams.InfisicalToken = token.Token
|
||||
} else if token != nil && token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER {
|
||||
authParams.UniversalAuthAccessToken = token.Token
|
||||
}
|
||||
|
||||
secrets = util.ExpandSecrets(secrets, authParams, "")
|
||||
}
|
||||
|
||||
requestedSecrets := []models.SingleEnvironmentVariable{}
|
||||
|
||||
secretsMap := getSecretsByKeys(secrets)
|
||||
|
@@ -30,6 +30,7 @@ type SingleEnvironmentVariable struct {
|
||||
Value string `json:"value"`
|
||||
Type string `json:"type"`
|
||||
ID string `json:"_id"`
|
||||
SecretPath string `json:"secretPath"`
|
||||
Tags []struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
@@ -103,6 +104,7 @@ type GetAllSecretsParameters struct {
|
||||
SecretsPath string
|
||||
IncludeImport bool
|
||||
Recursive bool
|
||||
ExpandSecretReferences bool
|
||||
}
|
||||
|
||||
type InjectableEnvironmentResult struct {
|
||||
|
@@ -8,8 +8,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
@@ -21,7 +19,7 @@ import (
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment string, secretPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) ([]models.SingleEnvironmentVariable, error) {
|
||||
serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4)
|
||||
if len(serviceTokenParts) < 4 {
|
||||
return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again")
|
||||
@@ -49,12 +47,13 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
|
||||
}
|
||||
|
||||
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{
|
||||
WorkspaceId: serviceTokenDetails.Workspace,
|
||||
Environment: environment,
|
||||
SecretPath: secretPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
TagSlugs: tagSlugs,
|
||||
WorkspaceId: serviceTokenDetails.Workspace,
|
||||
Environment: environment,
|
||||
SecretPath: secretPath,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
TagSlugs: tagSlugs,
|
||||
ExpandSecretReferences: expandSecretReferences,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -78,17 +77,18 @@ func GetPlainTextSecretsViaServiceToken(fullServiceToken string, environment str
|
||||
|
||||
}
|
||||
|
||||
func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool, recursive bool, tagSlugs string) (models.PlaintextSecretResult, error) {
|
||||
func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool, recursive bool, tagSlugs string, expandSecretReferences bool) (models.PlaintextSecretResult, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
|
||||
getSecretsRequest := api.GetRawSecretsV3Request{
|
||||
WorkspaceId: workspaceId,
|
||||
Environment: environmentName,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
TagSlugs: tagSlugs,
|
||||
WorkspaceId: workspaceId,
|
||||
Environment: environmentName,
|
||||
IncludeImport: includeImports,
|
||||
Recursive: recursive,
|
||||
TagSlugs: tagSlugs,
|
||||
ExpandSecretReferences: expandSecretReferences,
|
||||
}
|
||||
|
||||
if secretsPath != "" {
|
||||
@@ -104,7 +104,7 @@ func GetPlainTextSecretsV3(accessToken string, workspaceId string, environmentNa
|
||||
plainTextSecrets := []models.SingleEnvironmentVariable{}
|
||||
|
||||
for _, secret := range rawSecrets.Secrets {
|
||||
plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace})
|
||||
plainTextSecrets = append(plainTextSecrets, models.SingleEnvironmentVariable{Key: secret.SecretKey, Value: secret.SecretValue, Type: secret.Type, WorkspaceId: secret.Workspace, SecretPath: secret.SecretPath})
|
||||
}
|
||||
|
||||
if includeImports {
|
||||
@@ -145,6 +145,7 @@ func GetSinglePlainTextSecretByNameV3(accessToken string, workspaceId string, en
|
||||
Type: rawSecret.Secret.Type,
|
||||
ID: rawSecret.Secret.ID,
|
||||
Comment: rawSecret.Secret.SecretComment,
|
||||
SecretPath: rawSecret.Secret.SecretPath,
|
||||
}
|
||||
|
||||
return formattedSecrets, rawSecret.ETag, nil
|
||||
@@ -283,7 +284,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
||||
}
|
||||
|
||||
res, err := GetPlainTextSecretsV3(loggedInUserDetails.UserCredentials.JTWToken, infisicalDotJson.WorkspaceId,
|
||||
params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs)
|
||||
params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, true)
|
||||
log.Debug().Msgf("GetAllEnvironmentVariables: Trying to fetch secrets JTW token [err=%s]", err)
|
||||
|
||||
if err == nil {
|
||||
@@ -312,7 +313,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
||||
} else {
|
||||
if params.InfisicalToken != "" {
|
||||
log.Debug().Msg("Trying to fetch secrets using service token")
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs)
|
||||
secretsToReturn, errorToReturn = GetPlainTextSecretsViaServiceToken(params.InfisicalToken, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences)
|
||||
} else if params.UniversalAuthAccessToken != "" {
|
||||
|
||||
if params.WorkspaceId == "" {
|
||||
@@ -320,7 +321,7 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
||||
}
|
||||
|
||||
log.Debug().Msg("Trying to fetch secrets using universal auth")
|
||||
res, err := GetPlainTextSecretsV3(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs)
|
||||
res, err := GetPlainTextSecretsV3(params.UniversalAuthAccessToken, params.WorkspaceId, params.Environment, params.SecretsPath, params.IncludeImport, params.Recursive, params.TagSlugs, params.ExpandSecretReferences)
|
||||
|
||||
errorToReturn = err
|
||||
secretsToReturn = res.Secrets
|
||||
@@ -330,44 +331,6 @@ func GetAllEnvironmentVariables(params models.GetAllSecretsParameters, projectCo
|
||||
return secretsToReturn, errorToReturn
|
||||
}
|
||||
|
||||
var secRefRegex = regexp.MustCompile(`\${([^\}]*)}`)
|
||||
|
||||
func recursivelyExpandSecret(expandedSecs map[string]string, interpolatedSecs map[string]string, crossSecRefFetch func(env string, path []string, key string) string, key string) string {
|
||||
if v, ok := expandedSecs[key]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
interpolatedVal, ok := interpolatedSecs[key]
|
||||
if !ok {
|
||||
HandleError(fmt.Errorf("could not find refered secret - %s", key), "Kindly check whether its provided")
|
||||
}
|
||||
|
||||
refs := secRefRegex.FindAllStringSubmatch(interpolatedVal, -1)
|
||||
for _, val := range refs {
|
||||
// key: "${something}" val: [${something},something]
|
||||
interpolatedExp, interpolationKey := val[0], val[1]
|
||||
ref := strings.Split(interpolationKey, ".")
|
||||
|
||||
// ${KEY1} => [key1]
|
||||
if len(ref) == 1 {
|
||||
val := recursivelyExpandSecret(expandedSecs, interpolatedSecs, crossSecRefFetch, interpolationKey)
|
||||
interpolatedVal = strings.ReplaceAll(interpolatedVal, interpolatedExp, val)
|
||||
continue
|
||||
}
|
||||
|
||||
// cross board reference ${env.folder.key1} => [env folder key1]
|
||||
if len(ref) > 1 {
|
||||
secEnv, tmpSecPath, secKey := ref[0], ref[1:len(ref)-1], ref[len(ref)-1]
|
||||
interpolatedSecs[interpolationKey] = crossSecRefFetch(secEnv, tmpSecPath, secKey) // get the reference value
|
||||
val := recursivelyExpandSecret(expandedSecs, interpolatedSecs, crossSecRefFetch, interpolationKey)
|
||||
interpolatedVal = strings.ReplaceAll(interpolatedVal, interpolatedExp, val)
|
||||
}
|
||||
|
||||
}
|
||||
expandedSecs[key] = interpolatedVal
|
||||
return interpolatedVal
|
||||
}
|
||||
|
||||
func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]models.SingleEnvironmentVariable {
|
||||
secretMapByName := make(map[string]models.SingleEnvironmentVariable, len(secrets))
|
||||
|
||||
@@ -378,70 +341,6 @@ func getSecretsByKeys(secrets []models.SingleEnvironmentVariable) map[string]mod
|
||||
return secretMapByName
|
||||
}
|
||||
|
||||
func ExpandSecrets(secrets []models.SingleEnvironmentVariable, auth models.ExpandSecretsAuthentication, projectConfigPathDir string) []models.SingleEnvironmentVariable {
|
||||
expandedSecs := make(map[string]string)
|
||||
interpolatedSecs := make(map[string]string)
|
||||
// map[env.secret-path][keyname]Secret
|
||||
crossEnvRefSecs := make(map[string]map[string]models.SingleEnvironmentVariable) // a cache to hold all cross board reference secrets
|
||||
|
||||
for _, sec := range secrets {
|
||||
// get all references in a secret
|
||||
refs := secRefRegex.FindAllStringSubmatch(sec.Value, -1)
|
||||
// nil means its a secret without reference
|
||||
if refs == nil {
|
||||
expandedSecs[sec.Key] = sec.Value // atomic secrets without any interpolation
|
||||
} else {
|
||||
interpolatedSecs[sec.Key] = sec.Value
|
||||
}
|
||||
}
|
||||
|
||||
for i, sec := range secrets {
|
||||
// already present pick that up
|
||||
if expandedVal, ok := expandedSecs[sec.Key]; ok {
|
||||
secrets[i].Value = expandedVal
|
||||
continue
|
||||
}
|
||||
|
||||
expandedVal := recursivelyExpandSecret(expandedSecs, interpolatedSecs, func(env string, secPaths []string, secKey string) string {
|
||||
secPaths = append([]string{"/"}, secPaths...)
|
||||
secPath := path.Join(secPaths...)
|
||||
|
||||
secPathDot := strings.Join(secPaths, ".")
|
||||
uniqKey := fmt.Sprintf("%s.%s", env, secPathDot)
|
||||
|
||||
if crossRefSec, ok := crossEnvRefSecs[uniqKey]; !ok {
|
||||
|
||||
var refSecs []models.SingleEnvironmentVariable
|
||||
var err error
|
||||
|
||||
// if not in cross reference cache, fetch it from server
|
||||
if auth.InfisicalToken != "" {
|
||||
refSecs, err = GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, InfisicalToken: auth.InfisicalToken, SecretsPath: secPath}, projectConfigPathDir)
|
||||
} else if auth.UniversalAuthAccessToken != "" {
|
||||
refSecs, err = GetAllEnvironmentVariables((models.GetAllSecretsParameters{Environment: env, UniversalAuthAccessToken: auth.UniversalAuthAccessToken, SecretsPath: secPath, WorkspaceId: sec.WorkspaceId}), projectConfigPathDir)
|
||||
} else if IsLoggedIn() {
|
||||
refSecs, err = GetAllEnvironmentVariables(models.GetAllSecretsParameters{Environment: env, SecretsPath: secPath}, projectConfigPathDir)
|
||||
} else {
|
||||
HandleError(errors.New("no authentication provided"), "Please provide authentication to fetch secrets")
|
||||
}
|
||||
if err != nil {
|
||||
HandleError(err, fmt.Sprintf("Could not fetch secrets in environment: %s secret-path: %s", env, secPath), "If you are using a service token to fetch secrets, please ensure it is valid")
|
||||
}
|
||||
refSecsByKey := getSecretsByKeys(refSecs)
|
||||
// save it to avoid calling api again for same environment and folder path
|
||||
crossEnvRefSecs[uniqKey] = refSecsByKey
|
||||
return refSecsByKey[secKey].Value
|
||||
|
||||
} else {
|
||||
return crossRefSec[secKey].Value
|
||||
}
|
||||
}, sec.Key)
|
||||
|
||||
secrets[i].Value = expandedVal
|
||||
}
|
||||
return secrets
|
||||
}
|
||||
|
||||
func OverrideSecrets(secrets []models.SingleEnvironmentVariable, secretType string) []models.SingleEnvironmentVariable {
|
||||
personalSecrets := make(map[string]models.SingleEnvironmentVariable)
|
||||
sharedSecrets := make(map[string]models.SingleEnvironmentVariable)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
{{- with secret "6553ccb2b7da580d7f6e7260" "dev" "/" }}
|
||||
{{- with secret "8fac9f01-4a81-44d7-8ff0-3d7be684f56f" "staging" "/" `{"recursive":true, "expandSecretReferences": false}` }}
|
||||
{{- range . }}
|
||||
{{ .Key }}={{ .Value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
@@ -312,21 +312,37 @@ infisical agent --config example-agent-config-file.yaml
|
||||
|
||||
<Accordion title="listSecrets">
|
||||
```bash
|
||||
listSecrets "<project-id>" "environment-slug" "<secret-path>"
|
||||
listSecrets "<project-id>" "environment-slug" "<secret-path>" "<optional-modifier>"
|
||||
```
|
||||
```bash example-template-usage
|
||||
{{- with listSecrets "6553ccb2b7da580d7f6e7260" "dev" "/" }}
|
||||
```bash example-template-usage-1
|
||||
{{- with listSecrets "6553ccb2b7da580d7f6e7260" "dev" "/" `{"recursive": false, "expandSecretReferences": true}` }}
|
||||
{{- range . }}
|
||||
{{ .Key }}={{ .Value }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
```
|
||||
```bash example-template-usage-2
|
||||
{{- with secret "da8056c8-01e2-4d24-b39f-cb4e004b8d44" "staging" "/" `{"recursive": true, "expandSecretReferences": true}` }}
|
||||
{{- range . }}
|
||||
{{- if eq .SecretPath "/"}}
|
||||
{{ .Key }}={{ .Value }}
|
||||
{{- else}}
|
||||
{{ .SecretPath }}/{{ .Key }}={{ .Value }}
|
||||
{{- end}}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
```
|
||||
|
||||
|
||||
|
||||
**Function name**: listSecrets
|
||||
|
||||
**Description**: This function can be used to render the full list of secrets within a given project, environment and secret path.
|
||||
**Description**: This function can be used to render the full list of secrets within a given project, environment and secret path.
|
||||
|
||||
**Returns**: A single secret object with the following keys `Key, WorkspaceId, Value, Type, ID, and Comment`
|
||||
An optional JSON argument is also available. It includes the properties `recursive`, which defaults to false, and `expandSecretReferences`, which defaults to true and expands the returned secrets.
|
||||
|
||||
|
||||
**Returns**: A single secret object with the following keys `Key, WorkspaceId, Value, SecretPath, Type, ID, and Comment`
|
||||
|
||||
</Accordion>
|
||||
|
||||
|
161
docs/mint.json
161
docs/mint.json
@@ -866,5 +866,166 @@
|
||||
"koala": {
|
||||
"publicApiKey": "pk_b50d7184e0e39ddd5cdb43cf6abeadd9b97d"
|
||||
}
|
||||
},
|
||||
"footer": {
|
||||
"socials": {
|
||||
"x": "https://www.twitter.com/infisical/",
|
||||
"linkedin": "https://www.linkedin.com/company/infisical/",
|
||||
"github": "https://github.com/Infisical/infisical-cli",
|
||||
"slack": "https://infisical.com/slack"
|
||||
},
|
||||
"links": [
|
||||
{
|
||||
"title": "PRODUCT",
|
||||
"links": [
|
||||
{ "label": "Secret Management", "url": "https://infisical.com/" },
|
||||
{ "label": "Secret Scanning", "url": "https://infisical.com/radar" },
|
||||
{
|
||||
"label": "Share Secrets",
|
||||
"url": "https://app.infisical.com/share-secret"
|
||||
},
|
||||
{ "label": "Pricing", "url": "https://infisical.com/pricing" },
|
||||
{
|
||||
"label": "Security",
|
||||
"url": "https://infisical.com/docs/internals/security"
|
||||
},
|
||||
{
|
||||
"label": "Blog",
|
||||
"url": "https://infisical.com/blog"
|
||||
},
|
||||
{
|
||||
"label": "Infisical vs Vault",
|
||||
"url": "https://infisical.com/infisical-vs-hashicorp-vault"
|
||||
},
|
||||
{
|
||||
"label": "Forum",
|
||||
"url": "https://questions.infisical.com/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "USE CASES",
|
||||
"links": [
|
||||
{
|
||||
"label": "Infisical Agent",
|
||||
"url": "https://infisical.com/docs/documentation/getting-started/introduction"
|
||||
},
|
||||
{
|
||||
"label": "Kubernetes",
|
||||
"url": "https://infisical.com/docs/integrations/platforms/kubernetes"
|
||||
},
|
||||
{
|
||||
"label": "Dynamic Secrets",
|
||||
"url": "https://infisical.com/docs/documentation/platform/dynamic-secrets/overview"
|
||||
},
|
||||
{
|
||||
"label": "Terraform",
|
||||
"url": "https://infisical.com/docs/integrations/frameworks/terraform"
|
||||
},
|
||||
{
|
||||
"label": "Ansible",
|
||||
"url": "https://infisical.com/docs/integrations/platforms/ansible"
|
||||
},
|
||||
{
|
||||
"label": "Jenkins",
|
||||
"url": "https://infisical.com/docs/integrations/cicd/jenkins"
|
||||
},
|
||||
{
|
||||
"label": "Docker",
|
||||
"url": "https://infisical.com/docs/integrations/platforms/docker-intro"
|
||||
},
|
||||
{
|
||||
"label": "AWS ECS",
|
||||
"url": "https://infisical.com/docs/integrations/platforms/ecs-with-agent"
|
||||
},
|
||||
{
|
||||
"label": "GitLab",
|
||||
"url": "https://infisical.com/docs/integrations/cicd/gitlab"
|
||||
},
|
||||
{
|
||||
"label": "GitHub",
|
||||
"url": "https://infisical.com/docs/integrations/cicd/githubactions"
|
||||
},
|
||||
{
|
||||
"label": "SDK",
|
||||
"url": "https://infisical.com/docs/sdks/overview"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "DEVELOPERS",
|
||||
"links": [
|
||||
{
|
||||
"label": "Changelog",
|
||||
"url": "https://www.infisical.com/docs/changelog"
|
||||
},
|
||||
{
|
||||
"label": "Status",
|
||||
"url": "https://status.infisical.com/"
|
||||
},
|
||||
{
|
||||
"label": "Feedback & Requests",
|
||||
"url": "https://github.com/Infisical/infisical/issues"
|
||||
},
|
||||
{
|
||||
"label": "Trust of Center",
|
||||
"url": "https://app.vanta.com/infisical.com/trust/hoop8cr78cuarxo9sztvs"
|
||||
},
|
||||
{
|
||||
"label": "Open Source Friends",
|
||||
"url": "https://infisical.com/infisical-friends"
|
||||
},
|
||||
{
|
||||
"label": "How to contribute",
|
||||
"url": "https://www.infisical.com/infisical-heroes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "OTHERS",
|
||||
"links": [
|
||||
{
|
||||
"label": "Customers",
|
||||
"url": "https://infisical.com/customers/traba"
|
||||
},
|
||||
{
|
||||
"label": "Company Handbook",
|
||||
"url": "https://infisical.com/wiki/handbook/overview"
|
||||
},
|
||||
{
|
||||
"label": "Careers",
|
||||
"url": "https://infisical.com/careers"
|
||||
},
|
||||
{
|
||||
"label": "Terms of Service",
|
||||
"url": "https://infisical.com/terms"
|
||||
},
|
||||
{
|
||||
"label": "Privacy Policy",
|
||||
"url": "https://infisical.com/privacy"
|
||||
},
|
||||
{
|
||||
"label": "Subprocessors",
|
||||
"url": "https://infisical.com/subprocessors"
|
||||
},
|
||||
{
|
||||
"label": "SLA",
|
||||
"url": "https://infisical.com/sla"
|
||||
},
|
||||
{
|
||||
"label": "Team Email",
|
||||
"url": "mailto:team@infisical.com"
|
||||
},
|
||||
{
|
||||
"label": "Sales",
|
||||
"url": "mailto:sales@infisical.com"
|
||||
},
|
||||
{
|
||||
"label": "Support",
|
||||
"url": "https://infisical.com/slack"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user