mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-19 23:23:59 +00:00
feat: adds expiring scim token notification
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { TAuditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
||||
import { TScimServiceFactory } from "@app/ee/services/scim/scim-types";
|
||||
import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-dal";
|
||||
import { TKeyValueStoreDALFactory } from "@app/keystore/key-value-store-dal";
|
||||
import { getConfig } from "@app/lib/config/env";
|
||||
@@ -29,6 +30,7 @@ type TDailyResourceCleanUpQueueServiceFactoryDep = {
|
||||
orgService: TOrgServiceFactory;
|
||||
userNotificationDAL: Pick<TUserNotificationDALFactory, "pruneNotifications">;
|
||||
keyValueStoreDAL: Pick<TKeyValueStoreDALFactory, "pruneExpiredKeys">;
|
||||
scimService: Pick<TScimServiceFactory, "notifyExpiringTokens">;
|
||||
};
|
||||
|
||||
export type TDailyResourceCleanUpQueueServiceFactory = ReturnType<typeof dailyResourceCleanUpQueueServiceFactory>;
|
||||
@@ -44,6 +46,7 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
||||
secretVersionV2DAL,
|
||||
identityUniversalAuthClientSecretDAL,
|
||||
serviceTokenService,
|
||||
scimService,
|
||||
orgService,
|
||||
userNotificationDAL,
|
||||
keyValueStoreDAL
|
||||
@@ -86,6 +89,7 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
|
||||
await secretVersionV2DAL.pruneExcessVersions();
|
||||
await secretFolderVersionDAL.pruneExcessVersions();
|
||||
await serviceTokenService.notifyExpiringTokens();
|
||||
await scimService.notifyExpiringTokens();
|
||||
await orgService.notifyInvitedUsers();
|
||||
await auditLogDAL.pruneAuditLog();
|
||||
await userNotificationDAL.pruneNotifications();
|
||||
|
||||
@@ -214,6 +214,8 @@ export const serviceTokenServiceFactory = ({
|
||||
break;
|
||||
}
|
||||
|
||||
const successfullyNotifiedTokenIds: string[] = [];
|
||||
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await Promise.all(
|
||||
expiringTokens.map(async (token) => {
|
||||
@@ -228,13 +230,19 @@ export const serviceTokenServiceFactory = ({
|
||||
url: `${appCfg.SITE_URL}/organizations/${token.orgId}/projects/secret-management/${token.projectId}/access-management?selectedTab=service-tokens`
|
||||
}
|
||||
});
|
||||
await serviceTokenDAL.update({ id: token.id }, { expiryNotificationSent: true });
|
||||
successfullyNotifiedTokenIds.push(token.id);
|
||||
} catch (error) {
|
||||
logger.error(error, `Failed to send expiration notification for token ${token.id}:`);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// Batch update all successfully notified tokens in a single query
|
||||
if (successfullyNotifiedTokenIds.length > 0) {
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await serviceTokenDAL.update({ $in: { id: successfullyNotifiedTokenIds } }, { expiryNotificationSent: true });
|
||||
}
|
||||
|
||||
processedCount += expiringTokens.length;
|
||||
offset += batchSize;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Heading, Section, Text } from "@react-email/components";
|
||||
import React from "react";
|
||||
|
||||
import { BaseButton } from "./BaseButton";
|
||||
import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
|
||||
|
||||
interface ScimTokenExpiryNoticeTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
|
||||
tokenDescription?: string;
|
||||
orgName: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
export const ScimTokenExpiryNoticeTemplate = ({
|
||||
tokenDescription,
|
||||
siteUrl,
|
||||
orgName,
|
||||
url
|
||||
}: ScimTokenExpiryNoticeTemplateProps) => {
|
||||
return (
|
||||
<BaseEmailWrapper title="SCIM Token Expiring Soon" preview="A SCIM token is about to expire." siteUrl={siteUrl}>
|
||||
<Heading className="text-black text-[18px] leading-[28px] text-center font-normal p-0 mx-0">
|
||||
<strong>SCIM token expiry notice</strong>
|
||||
</Heading>
|
||||
<Section className="px-[24px] mb-[28px] mt-[36px] pt-[12px] pb-[8px] border border-solid border-gray-200 rounded-md bg-gray-50">
|
||||
<Text className="text-[14px]">
|
||||
{tokenDescription ? (
|
||||
<>
|
||||
Your SCIM token <strong>{tokenDescription}</strong>
|
||||
</>
|
||||
) : (
|
||||
"One of your SCIM tokens"
|
||||
)}{" "}
|
||||
for the organization <strong>{orgName}</strong> will expire within 24 hours.
|
||||
</Text>
|
||||
<Text>
|
||||
If this token is still needed for your external platform sync, please create a new one before it expires to
|
||||
avoid disruption to your workflow.
|
||||
</Text>
|
||||
</Section>
|
||||
<Section className="text-center">
|
||||
<BaseButton href={url}>Manage SCIM Tokens</BaseButton>
|
||||
</Section>
|
||||
</BaseEmailWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default ScimTokenExpiryNoticeTemplate;
|
||||
|
||||
ScimTokenExpiryNoticeTemplate.PreviewProps = {
|
||||
orgName: "Example Organization",
|
||||
siteUrl: "https://infisical.com",
|
||||
url: "https://infisical.com",
|
||||
tokenDescription: "Example SCIM Token"
|
||||
} as ScimTokenExpiryNoticeTemplateProps;
|
||||
@@ -19,6 +19,7 @@ export * from "./PasswordSetupTemplate";
|
||||
export * from "./PkiExpirationAlertTemplate";
|
||||
export * from "./ProjectAccessRequestTemplate";
|
||||
export * from "./ProjectInvitationTemplate";
|
||||
export * from "./ScimTokenExpiryNoticeTemplate";
|
||||
export * from "./ScimUserProvisionedTemplate";
|
||||
export * from "./SecretApprovalRequestBypassedTemplate";
|
||||
export * from "./SecretApprovalRequestNeedsReviewTemplate";
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
PkiExpirationAlertTemplate,
|
||||
ProjectAccessRequestTemplate,
|
||||
ProjectInvitationTemplate,
|
||||
ScimTokenExpiryNoticeTemplate,
|
||||
ScimUserProvisionedTemplate,
|
||||
SecretApprovalRequestBypassedTemplate,
|
||||
SecretApprovalRequestNeedsReviewTemplate,
|
||||
@@ -74,6 +75,7 @@ export enum SmtpTemplates {
|
||||
SecretLeakIncident = "secretLeakIncident",
|
||||
WorkspaceInvite = "workspaceInvitation",
|
||||
ScimUserProvisioned = "scimUserProvisioned",
|
||||
ScimTokenExpired = "scimTokenExpired",
|
||||
PkiExpirationAlert = "pkiExpirationAlert",
|
||||
IntegrationSyncFailed = "integrationSyncFailed",
|
||||
SecretSyncFailed = "secretSyncFailed",
|
||||
@@ -121,6 +123,7 @@ const EmailTemplateMap: Record<SmtpTemplates, React.FC<any>> = {
|
||||
[SmtpTemplates.SecretLeakIncident]: SecretLeakIncidentTemplate,
|
||||
[SmtpTemplates.WorkspaceInvite]: ProjectInvitationTemplate,
|
||||
[SmtpTemplates.ScimUserProvisioned]: ScimUserProvisionedTemplate,
|
||||
[SmtpTemplates.ScimTokenExpired]: ScimTokenExpiryNoticeTemplate,
|
||||
[SmtpTemplates.SecretRequestCompleted]: SecretRequestCompletedTemplate,
|
||||
[SmtpTemplates.UnlockAccount]: UnlockAccountTemplate,
|
||||
[SmtpTemplates.ServiceTokenExpired]: ServiceTokenExpiryNoticeTemplate,
|
||||
|
||||
Reference in New Issue
Block a user