mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-16 23:25:26 +00:00
Merge branch 'main' into feat/showWarningOnImportedSecretDeletion
This commit is contained in:
@@ -24,8 +24,14 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
method: I["method"];
|
||||
credentials: I["credentials"];
|
||||
description?: string | null;
|
||||
isPlatformManagedCredentials?: boolean;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{
|
||||
name?: string;
|
||||
credentials?: I["credentials"];
|
||||
description?: string | null;
|
||||
isPlatformManagedCredentials?: boolean;
|
||||
}>;
|
||||
updateSchema: z.ZodType<{ name?: string; credentials?: I["credentials"]; description?: string | null }>;
|
||||
sanitizedResponseSchema: z.ZodTypeAny;
|
||||
}) => {
|
||||
const appName = APP_CONNECTION_NAME_MAP[app];
|
||||
@@ -208,10 +214,10 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { name, method, credentials, description } = req.body;
|
||||
const { name, method, credentials, description, isPlatformManagedCredentials } = req.body;
|
||||
|
||||
const appConnection = (await server.services.appConnection.createAppConnection(
|
||||
{ name, method, app, credentials, description },
|
||||
{ name, method, app, credentials, description, isPlatformManagedCredentials },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
@@ -224,7 +230,8 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
name,
|
||||
method,
|
||||
app,
|
||||
connectionId: appConnection.id
|
||||
connectionId: appConnection.id,
|
||||
isPlatformManagedCredentials
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -251,11 +258,11 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { name, credentials, description } = req.body;
|
||||
const { name, credentials, description, isPlatformManagedCredentials } = req.body;
|
||||
const { connectionId } = req.params;
|
||||
|
||||
const appConnection = (await server.services.appConnection.updateAppConnection(
|
||||
{ name, credentials, connectionId, description },
|
||||
{ name, credentials, connectionId, description, isPlatformManagedCredentials },
|
||||
req.permission
|
||||
)) as T;
|
||||
|
||||
@@ -268,7 +275,8 @@ export const registerAppConnectionEndpoints = <T extends TAppConnection, I exten
|
||||
name,
|
||||
description,
|
||||
credentialsUpdated: Boolean(credentials),
|
||||
connectionId
|
||||
connectionId,
|
||||
isPlatformManagedCredentials
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -22,6 +22,11 @@ import {
|
||||
HumanitecConnectionListItemSchema,
|
||||
SanitizedHumanitecConnectionSchema
|
||||
} from "@app/services/app-connection/humanitec";
|
||||
import { MsSqlConnectionListItemSchema, SanitizedMsSqlConnectionSchema } from "@app/services/app-connection/mssql";
|
||||
import {
|
||||
PostgresConnectionListItemSchema,
|
||||
SanitizedPostgresConnectionSchema
|
||||
} from "@app/services/app-connection/postgres";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
|
||||
// can't use discriminated due to multiple schemas for certain apps
|
||||
@@ -32,7 +37,9 @@ const SanitizedAppConnectionSchema = z.union([
|
||||
...SanitizedAzureKeyVaultConnectionSchema.options,
|
||||
...SanitizedAzureAppConfigurationConnectionSchema.options,
|
||||
...SanitizedDatabricksConnectionSchema.options,
|
||||
...SanitizedHumanitecConnectionSchema.options
|
||||
...SanitizedHumanitecConnectionSchema.options,
|
||||
...SanitizedPostgresConnectionSchema.options,
|
||||
...SanitizedMsSqlConnectionSchema.options
|
||||
]);
|
||||
|
||||
const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
@@ -42,7 +49,9 @@ const AppConnectionOptionsSchema = z.discriminatedUnion("app", [
|
||||
AzureKeyVaultConnectionListItemSchema,
|
||||
AzureAppConfigurationConnectionListItemSchema,
|
||||
DatabricksConnectionListItemSchema,
|
||||
HumanitecConnectionListItemSchema
|
||||
HumanitecConnectionListItemSchema,
|
||||
PostgresConnectionListItemSchema,
|
||||
MsSqlConnectionListItemSchema
|
||||
]);
|
||||
|
||||
export const registerAppConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
|
||||
@@ -7,6 +7,8 @@ import { registerDatabricksConnectionRouter } from "./databricks-connection-rout
|
||||
import { registerGcpConnectionRouter } from "./gcp-connection-router";
|
||||
import { registerGitHubConnectionRouter } from "./github-connection-router";
|
||||
import { registerHumanitecConnectionRouter } from "./humanitec-connection-router";
|
||||
import { registerMsSqlConnectionRouter } from "./mssql-connection-router";
|
||||
import { registerPostgresConnectionRouter } from "./postgres-connection-router";
|
||||
|
||||
export * from "./app-connection-router";
|
||||
|
||||
@@ -18,5 +20,7 @@ export const APP_CONNECTION_REGISTER_ROUTER_MAP: Record<AppConnection, (server:
|
||||
[AppConnection.AzureKeyVault]: registerAzureKeyVaultConnectionRouter,
|
||||
[AppConnection.AzureAppConfiguration]: registerAzureAppConfigurationConnectionRouter,
|
||||
[AppConnection.Databricks]: registerDatabricksConnectionRouter,
|
||||
[AppConnection.Humanitec]: registerHumanitecConnectionRouter
|
||||
[AppConnection.Humanitec]: registerHumanitecConnectionRouter,
|
||||
[AppConnection.Postgres]: registerPostgresConnectionRouter,
|
||||
[AppConnection.MsSql]: registerMsSqlConnectionRouter
|
||||
};
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreateMsSqlConnectionSchema,
|
||||
SanitizedMsSqlConnectionSchema,
|
||||
UpdateMsSqlConnectionSchema
|
||||
} from "@app/services/app-connection/mssql";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerMsSqlConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.MsSql,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedMsSqlConnectionSchema,
|
||||
createSchema: CreateMsSqlConnectionSchema,
|
||||
updateSchema: UpdateMsSqlConnectionSchema
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
||||
import {
|
||||
CreatePostgresConnectionSchema,
|
||||
SanitizedPostgresConnectionSchema,
|
||||
UpdatePostgresConnectionSchema
|
||||
} from "@app/services/app-connection/postgres";
|
||||
|
||||
import { registerAppConnectionEndpoints } from "./app-connection-endpoints";
|
||||
|
||||
export const registerPostgresConnectionRouter = async (server: FastifyZodProvider) => {
|
||||
registerAppConnectionEndpoints({
|
||||
app: AppConnection.Postgres,
|
||||
server,
|
||||
sanitizedResponseSchema: SanitizedPostgresConnectionSchema,
|
||||
createSchema: CreatePostgresConnectionSchema,
|
||||
updateSchema: UpdatePostgresConnectionSchema
|
||||
});
|
||||
};
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
ProjectPermissionSecretActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { SecretRotationV2Schema } from "@app/ee/services/secret-rotation-v2/secret-rotation-v2-union-schema";
|
||||
import { DASHBOARD } from "@app/lib/api-docs";
|
||||
import { BadRequestError } from "@app/lib/errors";
|
||||
import { removeTrailingSlash } from "@app/lib/fn";
|
||||
@@ -101,12 +102,30 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
includeSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeSecrets),
|
||||
includeFolders: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeFolders),
|
||||
includeImports: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeImports),
|
||||
includeSecretRotations: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeSecretRotations),
|
||||
includeDynamicSecrets: booleanSchema.describe(DASHBOARD.SECRET_OVERVIEW_LIST.includeDynamicSecrets)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
folders: SecretFoldersSchema.extend({ environment: z.string() }).array().optional(),
|
||||
dynamicSecrets: SanitizedDynamicSecretSchema.extend({ environment: z.string() }).array().optional(),
|
||||
secretRotations: z
|
||||
.intersection(
|
||||
SecretRotationV2Schema,
|
||||
z.object({
|
||||
secrets: secretRawSchema
|
||||
.extend({
|
||||
secretValueHidden: z.boolean(),
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.nullable()
|
||||
.array()
|
||||
})
|
||||
)
|
||||
.array()
|
||||
.optional(),
|
||||
secrets: secretRawSchema
|
||||
.extend({
|
||||
secretValueHidden: z.boolean(),
|
||||
@@ -127,6 +146,7 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
totalDynamicSecretCount: z.number().optional(),
|
||||
totalSecretCount: z.number().optional(),
|
||||
totalImportCount: z.number().optional(),
|
||||
totalSecretRotationCount: z.number().optional(),
|
||||
totalCount: z.number()
|
||||
})
|
||||
}
|
||||
@@ -144,7 +164,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
includeFolders,
|
||||
includeSecrets,
|
||||
includeImports,
|
||||
includeDynamicSecrets
|
||||
includeDynamicSecrets,
|
||||
includeSecretRotations
|
||||
} = req.query;
|
||||
|
||||
const environments = req.query.environments.split(",");
|
||||
@@ -166,11 +187,15 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
let dynamicSecrets:
|
||||
| Awaited<ReturnType<typeof server.services.dynamicSecret.listDynamicSecretsByEnvs>>
|
||||
| undefined;
|
||||
let secretRotations:
|
||||
| Awaited<ReturnType<typeof server.services.secretRotationV2.getDashboardSecretRotations>>
|
||||
| undefined;
|
||||
|
||||
let totalFolderCount: number | undefined;
|
||||
let totalDynamicSecretCount: number | undefined;
|
||||
let totalSecretCount: number | undefined;
|
||||
let totalImportCount: number | undefined;
|
||||
let totalSecretRotationCount: number | undefined;
|
||||
|
||||
if (includeImports) {
|
||||
totalImportCount = await server.services.secretImport.getProjectImportMultiEnvCount({
|
||||
@@ -322,6 +347,56 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (includeSecretRotations) {
|
||||
totalSecretRotationCount = await server.services.secretRotationV2.getDashboardSecretRotationCount(
|
||||
{
|
||||
projectId,
|
||||
search,
|
||||
environments,
|
||||
secretPath
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
if (remainingLimit > 0 && totalSecretRotationCount > adjustedOffset) {
|
||||
secretRotations = await server.services.secretRotationV2.getDashboardSecretRotations(
|
||||
{
|
||||
projectId,
|
||||
search,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
environments,
|
||||
secretPath,
|
||||
limit: remainingLimit,
|
||||
offset: adjustedOffset
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_ROTATIONS,
|
||||
metadata: {
|
||||
count: secretRotations.length,
|
||||
rotationIds: secretRotations.map((rotation) => rotation.id),
|
||||
secretPath,
|
||||
environment: environments.join(",")
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// get the count of unique secret rotation names to properly adjust remaining limit
|
||||
const uniqueSecretRotationCount = new Set(secretRotations.map((rotation) => rotation.name)).size;
|
||||
|
||||
remainingLimit -= uniqueSecretRotationCount;
|
||||
adjustedOffset = 0;
|
||||
} else {
|
||||
adjustedOffset = Math.max(0, adjustedOffset - totalSecretRotationCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (includeSecrets) {
|
||||
// this is the unique count, ie duplicate secrets across envs only count as 1
|
||||
totalSecretCount = await server.services.secret.getSecretsCountMultiEnv({
|
||||
@@ -353,38 +428,44 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
offset: adjustedOffset,
|
||||
isInternal: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for await (const environment of environments) {
|
||||
const secretCountFromEnv = secrets.filter((secret) => secret.environment === environment).length;
|
||||
if (secrets?.length || secretRotations?.length) {
|
||||
for await (const environment of environments) {
|
||||
const secretCountFromEnv =
|
||||
(secrets?.filter((secret) => secret.environment === environment).length ?? 0) +
|
||||
(secretRotations
|
||||
?.filter((rotation) => rotation.environment.slug === environment)
|
||||
.flatMap((rotation) => rotation.secrets.filter((secret) => Boolean(secret))).length ?? 0);
|
||||
|
||||
if (secretCountFromEnv) {
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
metadata: {
|
||||
environment,
|
||||
secretPath,
|
||||
numberOfSecrets: secretCountFromEnv
|
||||
}
|
||||
if (secretCountFromEnv) {
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
metadata: {
|
||||
environment,
|
||||
secretPath,
|
||||
numberOfSecrets: secretCountFromEnv
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (getUserAgentType(req.headers["user-agent"]) !== UserAgentType.K8_OPERATOR) {
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.SecretPulled,
|
||||
distinctId: getTelemetryDistinctId(req),
|
||||
properties: {
|
||||
numberOfSecrets: secretCountFromEnv,
|
||||
workspaceId: projectId,
|
||||
environment,
|
||||
secretPath,
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
...req.auditLogInfo
|
||||
}
|
||||
});
|
||||
|
||||
if (getUserAgentType(req.headers["user-agent"]) !== UserAgentType.K8_OPERATOR) {
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.SecretPulled,
|
||||
distinctId: getTelemetryDistinctId(req),
|
||||
properties: {
|
||||
numberOfSecrets: secretCountFromEnv,
|
||||
workspaceId: projectId,
|
||||
environment,
|
||||
secretPath,
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
...req.auditLogInfo
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -395,12 +476,18 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
dynamicSecrets,
|
||||
secrets,
|
||||
imports,
|
||||
secretRotations,
|
||||
totalFolderCount,
|
||||
totalDynamicSecretCount,
|
||||
totalImportCount,
|
||||
totalSecretCount,
|
||||
totalSecretRotationCount,
|
||||
totalCount:
|
||||
(totalFolderCount ?? 0) + (totalDynamicSecretCount ?? 0) + (totalSecretCount ?? 0) + (totalImportCount ?? 0)
|
||||
(totalFolderCount ?? 0) +
|
||||
(totalDynamicSecretCount ?? 0) +
|
||||
(totalSecretCount ?? 0) +
|
||||
(totalImportCount ?? 0) +
|
||||
(totalSecretRotationCount ?? 0)
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -445,7 +532,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
includeSecrets: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeSecrets),
|
||||
includeFolders: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeFolders),
|
||||
includeDynamicSecrets: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeDynamicSecrets),
|
||||
includeImports: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeImports)
|
||||
includeImports: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeImports),
|
||||
includeSecretRotations: booleanSchema.describe(DASHBOARD.SECRET_DETAILS_LIST.includeSecretRotations)
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -457,6 +545,23 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
.optional(),
|
||||
folders: SecretFoldersSchema.array().optional(),
|
||||
dynamicSecrets: SanitizedDynamicSecretSchema.array().optional(),
|
||||
secretRotations: z
|
||||
.intersection(
|
||||
SecretRotationV2Schema,
|
||||
z.object({
|
||||
secrets: secretRawSchema
|
||||
.extend({
|
||||
secretValueHidden: z.boolean(),
|
||||
secretPath: z.string().optional(),
|
||||
secretMetadata: ResourceMetadataSchema.optional(),
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.nullable()
|
||||
.array()
|
||||
})
|
||||
)
|
||||
.array()
|
||||
.optional(),
|
||||
secrets: secretRawSchema
|
||||
.extend({
|
||||
secretValueHidden: z.boolean(),
|
||||
@@ -470,7 +575,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
totalFolderCount: z.number().optional(),
|
||||
totalDynamicSecretCount: z.number().optional(),
|
||||
totalSecretCount: z.number().optional(),
|
||||
totalCount: z.number(),
|
||||
importedBy: z
|
||||
.object({
|
||||
envName: z.string(),
|
||||
@@ -482,7 +586,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
.array()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
.optional(),
|
||||
totalSecretRotationCount: z.number().optional(),
|
||||
totalCount: z.number()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -500,7 +606,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
includeFolders,
|
||||
includeSecrets,
|
||||
includeDynamicSecrets,
|
||||
includeImports
|
||||
includeImports,
|
||||
includeSecretRotations
|
||||
} = req.query;
|
||||
|
||||
if (!projectId || !environment) throw new BadRequestError({ message: "Missing workspace id or environment" });
|
||||
@@ -519,11 +626,15 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
let folders: Awaited<ReturnType<typeof server.services.folder.getFolders>> | undefined;
|
||||
let secrets: Awaited<ReturnType<typeof server.services.secret.getSecretsRaw>>["secrets"] | undefined;
|
||||
let dynamicSecrets: Awaited<ReturnType<typeof server.services.dynamicSecret.listDynamicSecretsByEnv>> | undefined;
|
||||
let secretRotations:
|
||||
| Awaited<ReturnType<typeof server.services.secretRotationV2.getDashboardSecretRotations>>
|
||||
| undefined;
|
||||
|
||||
let totalImportCount: number | undefined;
|
||||
let totalFolderCount: number | undefined;
|
||||
let totalDynamicSecretCount: number | undefined;
|
||||
let totalSecretCount: number | undefined;
|
||||
let totalSecretRotationCount: number | undefined;
|
||||
|
||||
if (includeImports) {
|
||||
totalImportCount = await server.services.secretImport.getProjectImportCount({
|
||||
@@ -606,6 +717,53 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (includeSecretRotations) {
|
||||
totalSecretRotationCount = await server.services.secretRotationV2.getDashboardSecretRotationCount(
|
||||
{
|
||||
projectId,
|
||||
search,
|
||||
environments: [environment],
|
||||
secretPath
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
if (remainingLimit > 0 && totalSecretRotationCount > adjustedOffset) {
|
||||
secretRotations = await server.services.secretRotationV2.getDashboardSecretRotations(
|
||||
{
|
||||
projectId,
|
||||
search,
|
||||
orderBy,
|
||||
orderDirection,
|
||||
environments: [environment],
|
||||
secretPath,
|
||||
limit: remainingLimit,
|
||||
offset: adjustedOffset
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_ROTATIONS,
|
||||
metadata: {
|
||||
count: secretRotations.length,
|
||||
rotationIds: secretRotations.map((rotation) => rotation.id),
|
||||
secretPath,
|
||||
environment
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
remainingLimit -= secretRotations.length;
|
||||
adjustedOffset = 0;
|
||||
} else {
|
||||
adjustedOffset = Math.max(0, adjustedOffset - totalSecretRotationCount);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (includeDynamicSecrets) {
|
||||
totalDynamicSecretCount = await server.services.dynamicSecret.getDynamicSecretCount({
|
||||
@@ -641,7 +799,13 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
adjustedOffset = Math.max(0, adjustedOffset - totalDynamicSecretCount);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!(error instanceof ForbiddenError)) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (includeSecrets) {
|
||||
totalSecretCount = await server.services.secret.getSecretsCount({
|
||||
actorId: req.permission.id,
|
||||
@@ -675,34 +839,6 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
tagSlugs: tags
|
||||
})
|
||||
).secrets;
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
metadata: {
|
||||
environment,
|
||||
secretPath,
|
||||
numberOfSecrets: secrets.length
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (getUserAgentType(req.headers["user-agent"]) !== UserAgentType.K8_OPERATOR) {
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.SecretPulled,
|
||||
distinctId: getTelemetryDistinctId(req),
|
||||
properties: {
|
||||
numberOfSecrets: secrets.length,
|
||||
workspaceId: projectId,
|
||||
environment,
|
||||
secretPath,
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
...req.auditLogInfo
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -720,19 +856,58 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId
|
||||
});
|
||||
if (secrets?.length || secretRotations?.length) {
|
||||
const secretCount =
|
||||
(secrets?.length ?? 0) +
|
||||
(secretRotations?.flatMap((rotation) => rotation.secrets.filter((secret) => Boolean(secret))).length ?? 0);
|
||||
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRETS,
|
||||
metadata: {
|
||||
environment,
|
||||
secretPath,
|
||||
numberOfSecrets: secretCount
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (getUserAgentType(req.headers["user-agent"]) !== UserAgentType.K8_OPERATOR) {
|
||||
await server.services.telemetry.sendPostHogEvents({
|
||||
event: PostHogEventTypes.SecretPulled,
|
||||
distinctId: getTelemetryDistinctId(req),
|
||||
properties: {
|
||||
numberOfSecrets: secretCount,
|
||||
workspaceId: projectId,
|
||||
environment,
|
||||
secretPath,
|
||||
channel: getUserAgentType(req.headers["user-agent"]),
|
||||
...req.auditLogInfo
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
imports,
|
||||
folders,
|
||||
dynamicSecrets,
|
||||
secrets,
|
||||
secretRotations,
|
||||
totalImportCount,
|
||||
totalFolderCount,
|
||||
totalDynamicSecretCount,
|
||||
totalSecretCount,
|
||||
totalSecretRotationCount,
|
||||
importedBy,
|
||||
totalCount:
|
||||
(totalImportCount ?? 0) + (totalFolderCount ?? 0) + (totalDynamicSecretCount ?? 0) + (totalSecretCount ?? 0),
|
||||
importedBy
|
||||
(totalImportCount ?? 0) +
|
||||
(totalFolderCount ?? 0) +
|
||||
(totalDynamicSecretCount ?? 0) +
|
||||
(totalSecretCount ?? 0) +
|
||||
(totalSecretRotationCount ?? 0)
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -770,7 +945,8 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
tags: SanitizedTagSchema.array().optional()
|
||||
})
|
||||
.array()
|
||||
.optional()
|
||||
.optional(),
|
||||
secretRotations: SecretRotationV2Schema.array().optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
@@ -834,6 +1010,17 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
req.permission
|
||||
);
|
||||
|
||||
const secretRotations = searchHasTags
|
||||
? []
|
||||
: await server.services.secretRotationV2.getQuickSearchSecretRotations(
|
||||
{
|
||||
projectId,
|
||||
folderMappings,
|
||||
filters: sharedFilters
|
||||
},
|
||||
req.permission
|
||||
);
|
||||
|
||||
for await (const environment of environments) {
|
||||
const secretCountForEnv = secrets.filter((secret) => secret.environment === environment).length;
|
||||
|
||||
@@ -866,6 +1053,24 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const secretRotationsFromEnv = secretRotations.filter((rotation) => rotation.environment.slug === environment);
|
||||
|
||||
if (secretRotationsFromEnv.length) {
|
||||
await server.services.auditLog.createAuditLog({
|
||||
projectId,
|
||||
...req.auditLogInfo,
|
||||
event: {
|
||||
type: EventType.GET_SECRET_ROTATIONS,
|
||||
metadata: {
|
||||
count: secretRotationsFromEnv.length,
|
||||
rotationIds: secretRotationsFromEnv.map((rotation) => rotation.id),
|
||||
secretPath,
|
||||
environment
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const sliceQuickSearch = <T>(array: T[]) => array.slice(0, 25);
|
||||
@@ -879,6 +1084,9 @@ export const registerDashboardRouter = async (server: FastifyZodProvider) => {
|
||||
? dynamicSecrets.filter((dynamicSecret) => dynamicSecret.path.endsWith(searchPath))
|
||||
: dynamicSecrets
|
||||
),
|
||||
secretRotations: sliceQuickSearch(
|
||||
searchPath ? secretRotations.filter((rotation) => rotation.folder.path.endsWith(searchPath)) : secretRotations
|
||||
),
|
||||
folders: searchHasTags
|
||||
? []
|
||||
: sliceQuickSearch(
|
||||
|
||||
@@ -8,15 +8,17 @@ import {
|
||||
ProjectSlackConfigsSchema,
|
||||
ProjectType,
|
||||
SecretFoldersSchema,
|
||||
SortDirection,
|
||||
UserEncryptionKeysSchema,
|
||||
UsersSchema
|
||||
} from "@app/db/schemas";
|
||||
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
|
||||
import { PROJECTS } from "@app/lib/api-docs";
|
||||
import { CharacterType, characterValidator } from "@app/lib/validator/validate-string";
|
||||
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
|
||||
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
|
||||
import { AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType } from "@app/services/project/project-types";
|
||||
import { ActorType, AuthMode } from "@app/services/auth/auth-type";
|
||||
import { ProjectFilterType, SearchProjectSortBy } from "@app/services/project/project-types";
|
||||
import { validateSlackChannelsField } from "@app/services/slack/slack-auth-validators";
|
||||
|
||||
import { integrationAuthPubSchema, SanitizedProjectSchema } from "../sanitizedSchemas";
|
||||
@@ -704,4 +706,107 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
|
||||
return environmentsFolders;
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/search",
|
||||
config: {
|
||||
rateLimit: readLimit
|
||||
},
|
||||
schema: {
|
||||
body: z.object({
|
||||
limit: z.number().default(100),
|
||||
offset: z.number().default(0),
|
||||
type: z.nativeEnum(ProjectType).optional(),
|
||||
orderBy: z.nativeEnum(SearchProjectSortBy).optional().default(SearchProjectSortBy.NAME),
|
||||
orderDirection: z.nativeEnum(SortDirection).optional().default(SortDirection.ASC),
|
||||
name: z
|
||||
.string()
|
||||
.trim()
|
||||
.refine((val) => characterValidator([CharacterType.AlphaNumeric, CharacterType.Hyphen])(val), {
|
||||
message: "Invalid pattern: only alphanumeric characters, - are allowed."
|
||||
})
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
projects: SanitizedProjectSchema.extend({ isMember: z.boolean() }).array(),
|
||||
totalCount: z.number()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
|
||||
handler: async (req) => {
|
||||
const { docs: projects, totalCount } = await server.services.project.searchProjects({
|
||||
permission: req.permission,
|
||||
...req.body
|
||||
});
|
||||
|
||||
return { projects, totalCount };
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
method: "POST",
|
||||
url: "/:workspaceId/project-access",
|
||||
config: {
|
||||
rateLimit: writeLimit
|
||||
},
|
||||
schema: {
|
||||
params: z.object({
|
||||
workspaceId: z.string().trim()
|
||||
}),
|
||||
body: z.object({
|
||||
comment: z
|
||||
.string()
|
||||
.trim()
|
||||
.max(2500)
|
||||
.refine(
|
||||
(val) =>
|
||||
characterValidator([
|
||||
CharacterType.AlphaNumeric,
|
||||
CharacterType.Hyphen,
|
||||
CharacterType.Comma,
|
||||
CharacterType.Fullstop,
|
||||
CharacterType.Spaces,
|
||||
CharacterType.Exclamation
|
||||
])(val),
|
||||
{
|
||||
message: "Invalid pattern: only alphanumeric characters, spaces, -.!, are allowed."
|
||||
}
|
||||
)
|
||||
.optional()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
message: z.string()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
await server.services.project.requestProjectAccess({
|
||||
permission: req.permission,
|
||||
comment: req.body.comment,
|
||||
projectId: req.params.workspaceId
|
||||
});
|
||||
|
||||
if (req.auth.actor === ActorType.USER) {
|
||||
await server.services.auditLog.createAuditLog({
|
||||
...req.auditLogInfo,
|
||||
projectId: req.params.workspaceId,
|
||||
event: {
|
||||
type: EventType.PROJECT_ACCESS_REQUEST,
|
||||
metadata: {
|
||||
projectId: req.params.workspaceId,
|
||||
requesterEmail: req.auth.user.email || req.auth.user.username,
|
||||
requesterId: req.auth.userId
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return { message: "Project access request has been send to project admins" };
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user