Merge branch 'main' of https://github.com/Infisical/infisical into feat/adds-expiring-scim-token-notification

This commit is contained in:
Piyush Gupta
2025-11-27 19:31:53 +05:30
83 changed files with 1160 additions and 2693 deletions
@@ -99,13 +99,28 @@ export const identityOidcAuthServiceFactory = ({
}
const requestAgent = new https.Agent({ ca: caCert, rejectUnauthorized: !!caCert });
const { data: discoveryDoc } = await axios.get<{ jwks_uri: string }>(
`${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`,
{
httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
}
);
let discoveryDoc: { jwks_uri: string };
try {
const response = await axios.get<{ jwks_uri: string }>(
`${identityOidcAuth.oidcDiscoveryUrl}/.well-known/openid-configuration`,
{
httpsAgent: identityOidcAuth.oidcDiscoveryUrl.includes("https") ? requestAgent : undefined
}
);
discoveryDoc = response.data;
} catch (error) {
throw new UnauthorizedError({
message: `Access denied: Failed to fetch OIDC discovery document from ${identityOidcAuth.oidcDiscoveryUrl}. ${error instanceof Error ? error.message : String(error)}`
});
}
const jwksUri = discoveryDoc.jwks_uri;
if (!jwksUri) {
throw new UnauthorizedError({
message: `Access denied: OIDC discovery document does not contain a jwks_uri. The identity provider may be misconfigured.`
});
}
const decodedToken = crypto.jwt().decode(oidcJwt, { complete: true });
if (!decodedToken) {
+28 -1
View File
@@ -64,6 +64,8 @@ import { expandSecretReferencesFactory, getAllSecretReferences } from "../secret
import { TSecretVersionV2DALFactory } from "../secret-v2-bridge/secret-version-dal";
import { TSecretVersionV2TagDALFactory } from "../secret-v2-bridge/secret-version-tag-dal";
import { SmtpTemplates, TSmtpService } from "../smtp/smtp-service";
import { TTelemetryServiceFactory } from "../telemetry/telemetry-service";
import { PostHogEventTypes } from "../telemetry/telemetry-types";
import { TUserDALFactory } from "../user/user-dal";
import { TWebhookDALFactory } from "../webhook/webhook-dal";
import { fnTriggerWebhook } from "../webhook/webhook-fns";
@@ -120,6 +122,7 @@ type TSecretQueueFactoryDep = {
reminderService: Pick<TReminderServiceFactory, "createReminderInternal" | "deleteReminderBySecretId">;
eventBusService: TEventBusService;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
telemetryService: Pick<TTelemetryServiceFactory, "sendPostHogEvents">;
};
export type TGetSecrets = {
@@ -184,7 +187,8 @@ export const secretQueueFactory = ({
eventBusService,
licenseService,
membershipUserDAL,
membershipRoleDAL
membershipRoleDAL,
telemetryService
}: TSecretQueueFactoryDep) => {
const integrationMeter = opentelemetry.metrics.getMeter("Integrations");
const errorHistogram = integrationMeter.createHistogram("integration_secret_sync_errors", {
@@ -1029,6 +1033,29 @@ export const secretQueueFactory = ({
isSynced: response?.isSynced ?? true
});
await telemetryService.sendPostHogEvents({
event: PostHogEventTypes.IntegrationSynced,
distinctId: `project/${projectId}`,
organizationId: project.orgId,
properties: {
integrationId: integration.id,
integration: integration.integration,
environment,
secretPath,
projectId,
url: integration.url ?? undefined,
app: integration.app ?? undefined,
appId: integration.appId ?? undefined,
targetEnvironment: integration.targetEnvironment ?? undefined,
targetEnvironmentId: integration.targetEnvironmentId ?? undefined,
targetService: integration.targetService ?? undefined,
targetServiceId: integration.targetServiceId ?? undefined,
path: integration.path ?? undefined,
region: integration.region ?? undefined,
isManualSync: isManual ?? false
}
});
// May be undefined, if it's undefined we assume the sync was successful, hence the strict equality type check.
if (response?.isSynced === false) {
integrationsFailedToSync.push({
@@ -0,0 +1,68 @@
import { Heading, Section, Text } from "@react-email/components";
import { BaseButton } from "./BaseButton";
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface DynamicSecretLeaseRevocationFailedTemplateProps
extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
siteUrl: string;
dynamicSecretLeaseUrl: string;
dynamicSecretName: string;
projectName: string;
environmentSlug: string;
errorMessage: string;
}
export const DynamicSecretLeaseRevocationFailedTemplate = ({
siteUrl,
dynamicSecretLeaseUrl,
dynamicSecretName,
projectName,
environmentSlug,
errorMessage
}: DynamicSecretLeaseRevocationFailedTemplateProps) => {
return (
<BaseEmailWrapper
title="Dynamic Secret Lease Revocation Failed"
preview={`Dynamic secret lease revocation failed for dynamic secret ${dynamicSecretName}`}
siteUrl={siteUrl}
>
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
Dynamic Secret Lease Revocation Failed
</Heading>
<Section className="px-[24px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
<Text className="text-black text-[14px] leading-[24px]">
One or more leases for the dynamic secret <strong>{dynamicSecretName}</strong> in project{" "}
<strong>{projectName}</strong> and environment <strong>{environmentSlug}</strong> have failed to revoke after
multiple attempts.
</Text>
<Text className="text-black text-[14px] leading-[24px]">
Please review the dynamic secret leases and attempt to revoke them again.
</Text>
</Section>
<Section className="mt-[24px] bg-gray-50 pt-[2px] mb-[25px] pb-[16px] border border-solid border-gray-200 px-[24px] rounded-md text-gray-800">
<Text className="mb-[0px]">
<strong>Latest error message</strong>
</Text>
<Text className="leading-[24px] text-[14px] text-red-600 mt-[4px]">{errorMessage}</Text>
</Section>
<Section className="text-center">
<BaseButton href={dynamicSecretLeaseUrl}>View Dynamic Secret Leases</BaseButton>
</Section>
</BaseEmailWrapper>
);
};
export default DynamicSecretLeaseRevocationFailedTemplate;
DynamicSecretLeaseRevocationFailedTemplate.PreviewProps = {
errorMessage: 'REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM "[REDACTED]" - tuple concurrently updated.',
dynamicSecretLeaseUrl: "https://infisical.com/test",
leaseId: "717d5013-7194-49d9-b6ac-6192328c2914",
dynamicSecretName: "postgres-prod-db",
projectName: "Development Team",
environmentSlug: "dev",
siteUrl: "https://infisical.com"
} as DynamicSecretLeaseRevocationFailedTemplateProps;
+5 -2
View File
@@ -44,6 +44,7 @@ import {
SubOrganizationInvitationTemplate,
UnlockAccountTemplate
} from "./emails";
import DynamicSecretLeaseRevocationFailedTemplate from "./emails/DynamicSecretLeaseRevocationFailedTemplate";
export type TSmtpConfig = SMTPTransport.Options;
export type TSmtpSendMail = {
@@ -91,7 +92,8 @@ export enum SmtpTemplates {
SecretScanningV2ScanFailed = "secretScanningV2ScanFailed",
SecretScanningV2SecretsDetected = "secretScanningV2SecretsDetected",
AccountDeletionConfirmation = "accountDeletionConfirmation",
HealthAlert = "healthAlert"
HealthAlert = "healthAlert",
DynamicSecretLeaseRevocationFailed = "dynamicSecretLeaseRevocationFailed"
}
export enum SmtpHost {
@@ -140,7 +142,8 @@ const EmailTemplateMap: Record<SmtpTemplates, React.FC<any>> = {
[SmtpTemplates.SecretScanningV2ScanFailed]: SecretScanningScanFailedTemplate,
[SmtpTemplates.SecretScanningV2SecretsDetected]: SecretScanningSecretsDetectedTemplate,
[SmtpTemplates.AccountDeletionConfirmation]: AccountDeletionConfirmationTemplate,
[SmtpTemplates.HealthAlert]: HealthAlertTemplate
[SmtpTemplates.HealthAlert]: HealthAlertTemplate,
[SmtpTemplates.DynamicSecretLeaseRevocationFailed]: DynamicSecretLeaseRevocationFailedTemplate
};
export const smtpServiceFactory = (cfg: TSmtpConfig) => {
@@ -21,6 +21,8 @@ export enum PostHogEventTypes {
SecretScannerPush = "cloud secret scan",
ProjectCreated = "Project Created",
IntegrationCreated = "Integration Created",
IntegrationSynced = "Integration Synced",
IntegrationDeleted = "Integration Deleted",
MachineIdentityCreated = "Machine Identity Created",
UserOrgInvitation = "User Org Invitation",
TelemetryInstanceStats = "Self Hosted Instance Stats",
@@ -126,6 +128,47 @@ export type TIntegrationCreatedEvent = {
};
};
export type TIntegrationSyncedEvent = {
event: PostHogEventTypes.IntegrationSynced;
properties: {
projectId: string;
integrationId: string;
integration: string;
environment: string;
secretPath: string;
isManualSync: boolean;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
};
};
export type TIntegrationDeletedEvent = {
event: PostHogEventTypes.IntegrationDeleted;
properties: {
projectId: string;
integrationId: string;
integration: string;
environment: string;
secretPath: string;
url?: string;
app?: string;
appId?: string;
targetEnvironment?: string;
targetEnvironmentId?: string;
targetService?: string;
targetServiceId?: string;
path?: string;
region?: string;
};
};
export type TUserOrgInvitedEvent = {
event: PostHogEventTypes.UserOrgInvitation;
properties: {
@@ -249,6 +292,8 @@ export type TPostHogEvent = { distinctId: string; organizationId?: string } & (
| TUserOrgInvitedEvent
| TMachineIdentityCreatedEvent
| TIntegrationCreatedEvent
| TIntegrationSyncedEvent
| TIntegrationDeletedEvent
| TProjectCreateEvent
| TTelemetryInstanceStatsEvent
| TSecretRequestCreatedEvent