mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 09:26:47 +00:00
feat(api): filter secrets by metadata
This commit is contained in:
@@ -688,7 +688,9 @@ export const RAW_SECRETS = {
|
|||||||
environment: "The slug of the environment to list secrets from.",
|
environment: "The slug of the environment to list secrets from.",
|
||||||
secretPath: "The secret path to list secrets from.",
|
secretPath: "The secret path to list secrets from.",
|
||||||
includeImports: "Weather to include imported secrets or not.",
|
includeImports: "Weather to include imported secrets or not.",
|
||||||
tagSlugs: "The comma separated tag slugs to filter secrets."
|
tagSlugs: "The comma separated tag slugs to filter secrets.",
|
||||||
|
secretMetadata:
|
||||||
|
"The secret metadata key-value pairs to filter secrets by. When querying for multiple metadata pairs, the query is treated as an AND operation. Secret metadata format is key1:value1,key2:value2."
|
||||||
},
|
},
|
||||||
CREATE: {
|
CREATE: {
|
||||||
secretName: "The name of the secret to create.",
|
secretName: "The name of the secret to create.",
|
||||||
|
|||||||
@@ -181,6 +181,40 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
querystring: z.object({
|
querystring: z.object({
|
||||||
|
secretMetadata: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.transform((value) => {
|
||||||
|
if (!value) return undefined;
|
||||||
|
|
||||||
|
const metadata = value.split(",").map((el) => {
|
||||||
|
const [key, val] = el.split(":");
|
||||||
|
return { key, value: val };
|
||||||
|
});
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
})
|
||||||
|
.superRefine((el, ctx) => {
|
||||||
|
if (el && !Array.isArray(el)) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message: "Invalid secretMetadata format. Correct format is key1:value1,key2:value2"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el) {
|
||||||
|
for (const item of el) {
|
||||||
|
if (!item.key || !item.value) {
|
||||||
|
ctx.addIssue({
|
||||||
|
code: z.ZodIssueCode.custom,
|
||||||
|
message:
|
||||||
|
"Invalid secretMetadata format, key or value is missing. Correct format is key1:value1,key2:value2"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.describe(RAW_SECRETS.LIST.secretMetadata),
|
||||||
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId),
|
workspaceId: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceId),
|
||||||
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
|
workspaceSlug: z.string().trim().optional().describe(RAW_SECRETS.LIST.workspaceSlug),
|
||||||
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
|
environment: z.string().trim().optional().describe(RAW_SECRETS.LIST.environment),
|
||||||
@@ -281,6 +315,7 @@ export const registerSecretRouter = async (server: FastifyZodProvider) => {
|
|||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
projectId: workspaceId,
|
projectId: workspaceId,
|
||||||
path: secretPath,
|
path: secretPath,
|
||||||
|
secretMetadata: req.query.secretMetadata,
|
||||||
includeImports: req.query.include_imports,
|
includeImports: req.query.include_imports,
|
||||||
recursive: req.query.recursive,
|
recursive: req.query.recursive,
|
||||||
tagSlugs: req.query.tagSlugs
|
tagSlugs: req.query.tagSlugs
|
||||||
|
|||||||
@@ -414,6 +414,22 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
`${TableName.SecretTag}.id`
|
`${TableName.SecretTag}.id`
|
||||||
)
|
)
|
||||||
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
.leftJoin(TableName.ResourceMetadata, `${TableName.SecretV2}.id`, `${TableName.ResourceMetadata}.secretId`)
|
||||||
|
.where((bd) => {
|
||||||
|
if (filters?.secretMetadata && filters.secretMetadata?.length > 0) {
|
||||||
|
filters.secretMetadata.forEach((meta) => {
|
||||||
|
void bd.whereExists((qb) => {
|
||||||
|
void qb
|
||||||
|
.select("secretId")
|
||||||
|
.from(TableName.ResourceMetadata)
|
||||||
|
.whereRaw(`"${TableName.ResourceMetadata}"."secretId" = "${TableName.SecretV2}"."id"`)
|
||||||
|
.where({
|
||||||
|
[`${TableName.ResourceMetadata}.key` as string]: meta.key,
|
||||||
|
[`${TableName.ResourceMetadata}.value` as string]: meta.value
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
.select(
|
.select(
|
||||||
selectAllTableCols(TableName.SecretV2),
|
selectAllTableCols(TableName.SecretV2),
|
||||||
db.raw(
|
db.raw(
|
||||||
@@ -481,6 +497,19 @@ export const secretV2BridgeDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// if (secretMetadata) {
|
||||||
|
// return data.filter((s) => {
|
||||||
|
// if (!s.secretMetadata.length) return false;
|
||||||
|
|
||||||
|
// return secretMetadata.every((m) => {
|
||||||
|
// const secretMeta = s.secretMetadata.find((sm) => sm.key === m.key);
|
||||||
|
// if (!secretMeta) return false;
|
||||||
|
// return secretMeta.value === m.value;
|
||||||
|
// });
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "get all secret" });
|
throw new DatabaseError({ error, name: "get all secret" });
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ export type TGetSecretsDTO = {
|
|||||||
includeImports?: boolean;
|
includeImports?: boolean;
|
||||||
recursive?: boolean;
|
recursive?: boolean;
|
||||||
tagSlugs?: string[];
|
tagSlugs?: string[];
|
||||||
|
secretMetadata?: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
orderBy?: SecretsOrderBy;
|
orderBy?: SecretsOrderBy;
|
||||||
orderDirection?: OrderByDirection;
|
orderDirection?: OrderByDirection;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
@@ -310,6 +314,7 @@ export type TFindSecretsByFolderIdsFilter = {
|
|||||||
orderDirection?: OrderByDirection;
|
orderDirection?: OrderByDirection;
|
||||||
search?: string;
|
search?: string;
|
||||||
tagSlugs?: string[];
|
tagSlugs?: string[];
|
||||||
|
secretMetadata?: { key: string; value: string }[];
|
||||||
includeTagsInSearch?: boolean;
|
includeTagsInSearch?: boolean;
|
||||||
keys?: string[];
|
keys?: string[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1263,6 +1263,13 @@ export const secretServiceFactory = ({
|
|||||||
name: "bot_not_found_error"
|
name: "bot_not_found_error"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (paramsV2.secretMetadata) {
|
||||||
|
throw new BadRequestError({
|
||||||
|
message: "Please upgrade your project to filter secrets by metadata",
|
||||||
|
name: "SecretMetadataNotSupported"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { secrets, imports } = await getSecrets({
|
const { secrets, imports } = await getSecrets({
|
||||||
actorId,
|
actorId,
|
||||||
projectId,
|
projectId,
|
||||||
|
|||||||
@@ -182,6 +182,10 @@ export type TGetSecretsRawDTO = {
|
|||||||
includeImports?: boolean;
|
includeImports?: boolean;
|
||||||
recursive?: boolean;
|
recursive?: boolean;
|
||||||
tagSlugs?: string[];
|
tagSlugs?: string[];
|
||||||
|
secretMetadata?: {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
orderBy?: SecretsOrderBy;
|
orderBy?: SecretsOrderBy;
|
||||||
orderDirection?: OrderByDirection;
|
orderDirection?: OrderByDirection;
|
||||||
offset?: number;
|
offset?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user