fix: review comments

This commit is contained in:
Piyush Gupta
2025-11-27 20:42:56 +05:30
parent 119dd2005e
commit e09b763e50
3 changed files with 49 additions and 36 deletions

View File

@@ -14,44 +14,35 @@ export const scimDALFactory = (db: TDbClient) => {
const findExpiringTokens = async (tx?: Knex, batchSize = 500, offset = 0): Promise<TExpiringScimToken[]> => {
try {
const conn = tx || db.replicaNode();
const batch = await conn(TableName.ScimToken)
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.ScimToken}.orgId`)
const batch = await (tx || db.replicaNode())(TableName.ScimToken)
.leftJoin(TableName.Organization, `${TableName.Organization}.id`, `${TableName.ScimToken}.orgId`)
.leftJoin(TableName.Membership, `${TableName.Membership}.scopeOrgId`, `${TableName.ScimToken}.orgId`)
.leftJoin(TableName.MembershipRole, `${TableName.MembershipRole}.membershipId`, `${TableName.Membership}.id`)
.leftJoin(TableName.Users, `${TableName.Users}.id`, `${TableName.Membership}.actorUserId`)
.whereRaw(
`
(${TableName.ScimToken}."ttlDays" > 0 AND
(${TableName.ScimToken}."createdAt" + INTERVAL '1 day' * ${TableName.ScimToken}."ttlDays") < NOW() + INTERVAL '1 day' AND
(${TableName.ScimToken}."createdAt" + INTERVAL '1 day' * ${TableName.ScimToken}."ttlDays") > NOW())
(${TableName.ScimToken}."createdAt" + INTERVAL '1 day' * ${TableName.ScimToken}."ttlDays") < NOW() + INTERVAL '7 days' AND
(${TableName.ScimToken}."createdAt" + INTERVAL '1 day' * ${TableName.ScimToken}."ttlDays") > NOW())
`
)
.where(`${TableName.ScimToken}.expiryNotificationSent`, false)
.select<TExpiringScimToken[]>(
conn.ref("id").withSchema(TableName.ScimToken),
conn.ref("ttlDays").withSchema(TableName.ScimToken),
conn.ref("description").withSchema(TableName.ScimToken),
conn.ref("orgId").withSchema(TableName.ScimToken),
conn.ref("createdAt").withSchema(TableName.ScimToken),
conn.ref("name").withSchema(TableName.Organization).as("orgName"),
conn.raw(`
COALESCE(
(
SELECT array_agg(${TableName.Users}.email)
FROM ${TableName.Membership}
JOIN ${TableName.MembershipRole} ON ${TableName.Membership}.id = ${TableName.MembershipRole}."membershipId"
JOIN ${TableName.Users} ON ${TableName.Membership}."actorUserId" = ${TableName.Users}.id
WHERE ${TableName.Membership}."scopeOrgId" = ${TableName.ScimToken}."orgId"
AND ${TableName.Membership}.scope = '${AccessScope.Organization}'
AND ${TableName.MembershipRole}.role = '${OrgMembershipRole.Admin}'
AND ${TableName.Membership}.status != '${OrgMembershipStatus.Invited}'
AND ${TableName.Membership}."actorUserId" IS NOT NULL
AND ${TableName.Users}."isGhost" = false
AND ${TableName.Users}.email IS NOT NULL
),
ARRAY[]::text[]
) as "adminEmails"
`)
)
.where(`${TableName.Membership}.scope`, AccessScope.Organization)
.where(`${TableName.MembershipRole}.role`, OrgMembershipRole.Admin)
.whereNot(`${TableName.Membership}.status`, OrgMembershipStatus.Invited)
.whereNotNull(`${TableName.Membership}.actorUserId`)
.where(`${TableName.Users}.isGhost`, false)
.whereNotNull(`${TableName.Users}.email`)
.groupBy([`${TableName.ScimToken}.id`, `${TableName.Organization}.name`])
.select<TExpiringScimToken[]>([
db.ref("id").withSchema(TableName.ScimToken),
db.ref("ttlDays").withSchema(TableName.ScimToken),
db.ref("description").withSchema(TableName.ScimToken),
db.ref("orgId").withSchema(TableName.ScimToken),
db.ref("createdAt").withSchema(TableName.ScimToken),
db.ref("name").withSchema(TableName.Organization).as("orgName"),
db.raw(`array_agg(${TableName.Users}."email") as "adminEmails"`)
])
.limit(batchSize)
.offset(offset);

View File

@@ -1266,6 +1266,9 @@ export const scimServiceFactory = ({
return;
}
const createdOn = new Date(token.createdAt);
const expiringOn = new Date(createdOn.getTime() + Number(token.ttlDays) * 86400 * 1000);
await smtpService.sendMail({
recipients: token.adminEmails,
subjectLine: "SCIM Token Expiry Notice",
@@ -1273,7 +1276,9 @@ export const scimServiceFactory = ({
substitutions: {
tokenDescription: token.description,
orgName: token.orgName,
url: `${appCfg.SITE_URL}/organizations/${token.orgId}/settings?selectedTab=provisioning-settings`
url: `${appCfg.SITE_URL}/organizations/${token.orgId}/settings?selectedTab=provisioning-settings`,
createdOn,
expiringOn
}
});

View File

@@ -7,6 +7,8 @@ import { BaseEmailWrapper, BaseEmailWrapperProps } from "./BaseEmailWrapper";
interface ScimTokenExpiryNoticeTemplateProps extends Omit<BaseEmailWrapperProps, "title" | "preview" | "children"> {
tokenDescription?: string;
orgName: string;
createdOn: Date;
expiringOn: Date;
url: string;
}
@@ -14,8 +16,20 @@ export const ScimTokenExpiryNoticeTemplate = ({
tokenDescription,
siteUrl,
orgName,
url
url,
createdOn,
expiringOn
}: ScimTokenExpiryNoticeTemplateProps) => {
const formatDate = (date: Date) =>
date.toLocaleDateString("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const createdOnDisplay = formatDate(createdOn);
const expiringOnDisplay = formatDate(expiringOn);
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">
@@ -30,7 +44,8 @@ export const ScimTokenExpiryNoticeTemplate = ({
) : (
"One of your SCIM tokens"
)}{" "}
for the organization <strong>{orgName}</strong> will expire within 24 hours.
for <strong>{orgName}</strong>, created on <strong>{createdOnDisplay}</strong>, is scheduled to expire on{" "}
<strong>{expiringOnDisplay}</strong>.
</Text>
<Text>
If this token is still needed for your external platform sync, please create a new one before it expires to
@@ -50,5 +65,7 @@ ScimTokenExpiryNoticeTemplate.PreviewProps = {
orgName: "Example Organization",
siteUrl: "https://infisical.com",
url: "https://infisical.com",
tokenDescription: "Example SCIM Token"
tokenDescription: "Example SCIM Token",
createdOn: new Date("2025-11-27T00:00:00Z"),
expiringOn: new Date("2025-12-27T00:00:00Z")
} as ScimTokenExpiryNoticeTemplateProps;