mirror of
https://github.com/Infisical/infisical.git
synced 2025-08-31 15:32:32 +00:00
Compare commits
14 Commits
fix/resolv
...
infisical/
Author | SHA1 | Date | |
---|---|---|---|
|
4faa9ced04 | ||
|
b6ff07b605 | ||
|
1753cd76be | ||
|
f75fc54e10 | ||
|
966bd77234 | ||
|
c782df1176 | ||
|
e9c5b7f846 | ||
|
c9b234dbea | ||
|
8497182a7b | ||
|
133841c322 | ||
|
76c9d642a9 | ||
|
3ed5dd6109 | ||
|
08e7815ec1 | ||
|
04d961b832 |
@@ -3,7 +3,6 @@ import { RawAxiosRequestHeaders } from "axios";
|
|||||||
import { SecretKeyEncoding } from "@app/db/schemas";
|
import { SecretKeyEncoding } from "@app/db/schemas";
|
||||||
import { request } from "@app/lib/config/request";
|
import { request } from "@app/lib/config/request";
|
||||||
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
|
||||||
import { logger } from "@app/lib/logger";
|
|
||||||
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||||
|
|
||||||
@@ -113,35 +112,7 @@ export const auditLogQueueServiceFactory = ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
queueService.start(QueueName.AuditLogPrune, async () => {
|
|
||||||
logger.info(`${QueueName.AuditLogPrune}: queue task started`);
|
|
||||||
await auditLogDAL.pruneAuditLog();
|
|
||||||
logger.info(`${QueueName.AuditLogPrune}: queue task completed`);
|
|
||||||
});
|
|
||||||
|
|
||||||
// we do a repeat cron job in utc timezone at 12 Midnight each day
|
|
||||||
const startAuditLogPruneJob = async () => {
|
|
||||||
// clear previous job
|
|
||||||
await queueService.stopRepeatableJob(
|
|
||||||
QueueName.AuditLogPrune,
|
|
||||||
QueueJobs.AuditLogPrune,
|
|
||||||
{ pattern: "0 0 * * *", utc: true },
|
|
||||||
QueueName.AuditLogPrune // just a job id
|
|
||||||
);
|
|
||||||
|
|
||||||
await queueService.queue(QueueName.AuditLogPrune, QueueJobs.AuditLogPrune, undefined, {
|
|
||||||
delay: 5000,
|
|
||||||
jobId: QueueName.AuditLogPrune,
|
|
||||||
repeat: { pattern: "0 0 * * *", utc: true }
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
queueService.listen(QueueName.AuditLogPrune, "failed", (err) => {
|
|
||||||
logger.error(err?.failedReason, `${QueueName.AuditLogPrune}: log pruning failed`);
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pushToLog,
|
pushToLog
|
||||||
startAuditLogPruneJob
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@@ -104,24 +104,68 @@ export const ormify = <DbOps extends object, Tname extends keyof Tables>(db: Kne
|
|||||||
throw new DatabaseError({ error, name: "Create" });
|
throw new DatabaseError({ error, name: "Create" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateById: async (id: string, data: Tables[Tname]["update"], tx?: Knex) => {
|
updateById: async (
|
||||||
|
id: string,
|
||||||
|
{
|
||||||
|
$incr,
|
||||||
|
$decr,
|
||||||
|
...data
|
||||||
|
}: Tables[Tname]["update"] & {
|
||||||
|
$incr?: { [x in keyof Partial<Tables[Tname]["base"]>]: number };
|
||||||
|
$decr?: { [x in keyof Partial<Tables[Tname]["base"]>]: number };
|
||||||
|
},
|
||||||
|
tx?: Knex
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const [res] = await (tx || db)(tableName)
|
const query = (tx || db)(tableName)
|
||||||
.where({ id } as never)
|
.where({ id } as never)
|
||||||
.update(data as never)
|
.update(data as never)
|
||||||
.returning("*");
|
.returning("*");
|
||||||
return res;
|
if ($incr) {
|
||||||
|
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
||||||
|
void query.increment(incrementField, incrementValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($decr) {
|
||||||
|
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
||||||
|
void query.increment(incrementField, incrementValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const [docs] = await query;
|
||||||
|
return docs;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Update by id" });
|
throw new DatabaseError({ error, name: "Update by id" });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
update: async (filter: TFindFilter<Tables[Tname]["base"]>, data: Tables[Tname]["update"], tx?: Knex) => {
|
update: async (
|
||||||
|
filter: TFindFilter<Tables[Tname]["base"]>,
|
||||||
|
{
|
||||||
|
$incr,
|
||||||
|
$decr,
|
||||||
|
...data
|
||||||
|
}: Tables[Tname]["update"] & {
|
||||||
|
$incr?: { [x in keyof Partial<Tables[Tname]["base"]>]: number };
|
||||||
|
$decr?: { [x in keyof Partial<Tables[Tname]["base"]>]: number };
|
||||||
|
},
|
||||||
|
tx?: Knex
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const res = await (tx || db)(tableName)
|
const query = (tx || db)(tableName)
|
||||||
.where(buildFindFilter(filter))
|
.where(buildFindFilter(filter))
|
||||||
.update(data as never)
|
.update(data as never)
|
||||||
.returning("*");
|
.returning("*");
|
||||||
return res;
|
// increment and decrement operation in update
|
||||||
|
if ($incr) {
|
||||||
|
Object.entries($incr).forEach(([incrementField, incrementValue]) => {
|
||||||
|
void query.increment(incrementField, incrementValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ($decr) {
|
||||||
|
Object.entries($decr).forEach(([incrementField, incrementValue]) => {
|
||||||
|
void query.increment(incrementField, incrementValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return await query;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new DatabaseError({ error, name: "Update" });
|
throw new DatabaseError({ error, name: "Update" });
|
||||||
}
|
}
|
||||||
|
@@ -12,7 +12,9 @@ export enum QueueName {
|
|||||||
SecretRotation = "secret-rotation",
|
SecretRotation = "secret-rotation",
|
||||||
SecretReminder = "secret-reminder",
|
SecretReminder = "secret-reminder",
|
||||||
AuditLog = "audit-log",
|
AuditLog = "audit-log",
|
||||||
|
// TODO(akhilmhdh): This will get removed later. For now this is kept to stop the repeatable queue
|
||||||
AuditLogPrune = "audit-log-prune",
|
AuditLogPrune = "audit-log-prune",
|
||||||
|
DailyResourceCleanUp = "daily-resource-cleanup",
|
||||||
TelemetryInstanceStats = "telemtry-self-hosted-stats",
|
TelemetryInstanceStats = "telemtry-self-hosted-stats",
|
||||||
IntegrationSync = "sync-integrations",
|
IntegrationSync = "sync-integrations",
|
||||||
SecretWebhook = "secret-webhook",
|
SecretWebhook = "secret-webhook",
|
||||||
@@ -26,7 +28,9 @@ export enum QueueJobs {
|
|||||||
SecretReminder = "secret-reminder-job",
|
SecretReminder = "secret-reminder-job",
|
||||||
SecretRotation = "secret-rotation-job",
|
SecretRotation = "secret-rotation-job",
|
||||||
AuditLog = "audit-log-job",
|
AuditLog = "audit-log-job",
|
||||||
|
// TODO(akhilmhdh): This will get removed later. For now this is kept to stop the repeatable queue
|
||||||
AuditLogPrune = "audit-log-prune-job",
|
AuditLogPrune = "audit-log-prune-job",
|
||||||
|
DailyResourceCleanUp = "daily-resource-cleanup-job",
|
||||||
SecWebhook = "secret-webhook-trigger",
|
SecWebhook = "secret-webhook-trigger",
|
||||||
TelemetryInstanceStats = "telemetry-self-hosted-stats",
|
TelemetryInstanceStats = "telemetry-self-hosted-stats",
|
||||||
IntegrationSync = "secret-integration-pull",
|
IntegrationSync = "secret-integration-pull",
|
||||||
@@ -55,6 +59,10 @@ export type TQueueJobTypes = {
|
|||||||
name: QueueJobs.AuditLog;
|
name: QueueJobs.AuditLog;
|
||||||
payload: TCreateAuditLogDTO;
|
payload: TCreateAuditLogDTO;
|
||||||
};
|
};
|
||||||
|
[QueueName.DailyResourceCleanUp]: {
|
||||||
|
name: QueueJobs.DailyResourceCleanUp;
|
||||||
|
payload: undefined;
|
||||||
|
};
|
||||||
[QueueName.AuditLogPrune]: {
|
[QueueName.AuditLogPrune]: {
|
||||||
name: QueueJobs.AuditLogPrune;
|
name: QueueJobs.AuditLogPrune;
|
||||||
payload: undefined;
|
payload: undefined;
|
||||||
@@ -172,7 +180,9 @@ export const queueServiceFactory = (redisUrl: string) => {
|
|||||||
jobId?: string
|
jobId?: string
|
||||||
) => {
|
) => {
|
||||||
const q = queueContainer[name];
|
const q = queueContainer[name];
|
||||||
return q.removeRepeatable(job, repeatOpt, jobId);
|
if (q) {
|
||||||
|
return q.removeRepeatable(job, repeatOpt, jobId);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopRepeatableJobByJobId = async <T extends QueueName>(name: T, jobId: string) => {
|
const stopRepeatableJobByJobId = async <T extends QueueName>(name: T, jobId: string) => {
|
||||||
|
@@ -6,6 +6,7 @@ const headersOrder = [
|
|||||||
"cf-connecting-ip", // Cloudflare
|
"cf-connecting-ip", // Cloudflare
|
||||||
"Cf-Pseudo-IPv4", // Cloudflare
|
"Cf-Pseudo-IPv4", // Cloudflare
|
||||||
"x-client-ip", // Most common
|
"x-client-ip", // Most common
|
||||||
|
"x-envoy-external-address", // for envoy
|
||||||
"x-forwarded-for", // Mostly used by proxies
|
"x-forwarded-for", // Mostly used by proxies
|
||||||
"fastly-client-ip",
|
"fastly-client-ip",
|
||||||
"true-client-ip", // Akamai and Cloudflare
|
"true-client-ip", // Akamai and Cloudflare
|
||||||
@@ -23,7 +24,21 @@ export const fastifyIp = fp(async (fastify) => {
|
|||||||
const forwardedIpHeader = headersOrder.find((header) => Boolean(req.headers[header]));
|
const forwardedIpHeader = headersOrder.find((header) => Boolean(req.headers[header]));
|
||||||
const forwardedIp = forwardedIpHeader ? req.headers[forwardedIpHeader] : undefined;
|
const forwardedIp = forwardedIpHeader ? req.headers[forwardedIpHeader] : undefined;
|
||||||
if (forwardedIp) {
|
if (forwardedIp) {
|
||||||
req.realIp = Array.isArray(forwardedIp) ? forwardedIp[0] : forwardedIp;
|
if (Array.isArray(forwardedIp)) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
req.realIp = forwardedIp[0];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forwardedIp.includes(",")) {
|
||||||
|
// the ip header when placed with load balancers that proxy request
|
||||||
|
// will attach the internal ips to header by appending with comma
|
||||||
|
// https://github.com/go-chi/chi/blob/master/middleware/realip.go
|
||||||
|
const clientIPFromProxy = forwardedIp.slice(0, forwardedIp.indexOf(",")).trim();
|
||||||
|
req.realIp = clientIPFromProxy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
req.realIp = forwardedIp;
|
||||||
} else {
|
} else {
|
||||||
req.realIp = req.ip;
|
req.realIp = req.ip;
|
||||||
}
|
}
|
||||||
|
@@ -115,6 +115,7 @@ import { projectMembershipServiceFactory } from "@app/services/project-membershi
|
|||||||
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
import { projectUserMembershipRoleDALFactory } from "@app/services/project-membership/project-user-membership-role-dal";
|
||||||
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
import { projectRoleDALFactory } from "@app/services/project-role/project-role-dal";
|
||||||
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
import { projectRoleServiceFactory } from "@app/services/project-role/project-role-service";
|
||||||
|
import { dailyResourceCleanUpQueueServiceFactory } from "@app/services/resource-cleanup/resource-cleanup-queue";
|
||||||
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
import { secretDALFactory } from "@app/services/secret/secret-dal";
|
||||||
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
import { secretQueueFactory } from "@app/services/secret/secret-queue";
|
||||||
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
import { secretServiceFactory } from "@app/services/secret/secret-service";
|
||||||
@@ -769,14 +770,19 @@ export const registerRoutes = async (
|
|||||||
folderDAL,
|
folderDAL,
|
||||||
licenseService
|
licenseService
|
||||||
});
|
});
|
||||||
|
const dailyResourceCleanUp = dailyResourceCleanUpQueueServiceFactory({
|
||||||
|
auditLogDAL,
|
||||||
|
queueService,
|
||||||
|
identityAccessTokenDAL
|
||||||
|
});
|
||||||
|
|
||||||
await superAdminService.initServerCfg();
|
await superAdminService.initServerCfg();
|
||||||
//
|
//
|
||||||
// setup the communication with license key server
|
// setup the communication with license key server
|
||||||
await licenseService.init();
|
await licenseService.init();
|
||||||
|
|
||||||
await auditLogQueue.startAuditLogPruneJob();
|
|
||||||
await telemetryQueue.startTelemetryCheck();
|
await telemetryQueue.startTelemetryCheck();
|
||||||
|
await dailyResourceCleanUp.startCleanUp();
|
||||||
|
|
||||||
// inject all services
|
// inject all services
|
||||||
server.decorate<FastifyZodProvider["services"]>("services", {
|
server.decorate<FastifyZodProvider["services"]>("services", {
|
||||||
|
@@ -70,5 +70,48 @@ export const identityAccessTokenDALFactory = (db: TDbClient) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...identityAccessTokenOrm, findOne };
|
const removeExpiredTokens = async (tx?: Knex) => {
|
||||||
|
try {
|
||||||
|
const docs = (tx || db)(TableName.IdentityAccessToken)
|
||||||
|
.where({
|
||||||
|
isAccessTokenRevoked: true
|
||||||
|
})
|
||||||
|
.orWhere((qb) => {
|
||||||
|
void qb
|
||||||
|
.where("accessTokenNumUsesLimit", ">", 0)
|
||||||
|
.andWhere(
|
||||||
|
"accessTokenNumUses",
|
||||||
|
">=",
|
||||||
|
db.ref("accessTokenNumUsesLimit").withSchema(TableName.IdentityAccessToken)
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.orWhere((qb) => {
|
||||||
|
void qb.where("accessTokenTTL", ">", 0).andWhere((qb2) => {
|
||||||
|
void qb2
|
||||||
|
.where((qb3) => {
|
||||||
|
void qb3
|
||||||
|
.whereNotNull("accessTokenLastRenewedAt")
|
||||||
|
// accessTokenLastRenewedAt + convert_integer_to_seconds(accessTokenTTL) < present_date
|
||||||
|
.andWhereRaw(
|
||||||
|
`"${TableName.IdentityAccessToken}"."accessTokenLastRenewedAt" + make_interval(secs => "${TableName.IdentityAccessToken}"."accessTokenTTL") < NOW()`
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.orWhere((qb3) => {
|
||||||
|
void qb3
|
||||||
|
.whereNull("accessTokenLastRenewedAt")
|
||||||
|
// created + convert_integer_to_seconds(accessTokenTTL) < present_date
|
||||||
|
.andWhereRaw(
|
||||||
|
`"${TableName.IdentityAccessToken}"."createdAt" + make_interval(secs => "${TableName.IdentityAccessToken}"."accessTokenTTL") < NOW()`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.delete();
|
||||||
|
return await docs;
|
||||||
|
} catch (error) {
|
||||||
|
throw new DatabaseError({ error, name: "IdentityAccessTokenPrune" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...identityAccessTokenOrm, findOne, removeExpiredTokens };
|
||||||
};
|
};
|
||||||
|
@@ -21,17 +21,18 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
identityAccessTokenDAL,
|
identityAccessTokenDAL,
|
||||||
identityOrgMembershipDAL
|
identityOrgMembershipDAL
|
||||||
}: TIdentityAccessTokenServiceFactoryDep) => {
|
}: TIdentityAccessTokenServiceFactoryDep) => {
|
||||||
const validateAccessTokenExp = (identityAccessToken: TIdentityAccessTokens) => {
|
const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => {
|
||||||
const {
|
const {
|
||||||
|
id: tokenId,
|
||||||
accessTokenTTL,
|
accessTokenTTL,
|
||||||
accessTokenNumUses,
|
accessTokenNumUses,
|
||||||
accessTokenNumUsesLimit,
|
accessTokenNumUsesLimit,
|
||||||
accessTokenLastRenewedAt,
|
accessTokenLastRenewedAt,
|
||||||
accessTokenMaxTTL,
|
|
||||||
createdAt: accessTokenCreatedAt
|
createdAt: accessTokenCreatedAt
|
||||||
} = identityAccessToken;
|
} = identityAccessToken;
|
||||||
|
|
||||||
if (accessTokenNumUsesLimit > 0 && accessTokenNumUses > 0 && accessTokenNumUses >= accessTokenNumUsesLimit) {
|
if (accessTokenNumUsesLimit > 0 && accessTokenNumUses > 0 && accessTokenNumUses >= accessTokenNumUsesLimit) {
|
||||||
|
await identityAccessTokenDAL.deleteById(tokenId);
|
||||||
throw new BadRequestError({
|
throw new BadRequestError({
|
||||||
message: "Unable to renew because access token number of uses limit reached"
|
message: "Unable to renew because access token number of uses limit reached"
|
||||||
});
|
});
|
||||||
@@ -46,41 +47,26 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
const ttlInMilliseconds = Number(accessTokenTTL) * 1000;
|
const ttlInMilliseconds = Number(accessTokenTTL) * 1000;
|
||||||
const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds);
|
const expirationDate = new Date(accessTokenRenewed.getTime() + ttlInMilliseconds);
|
||||||
|
|
||||||
if (currentDate > expirationDate)
|
if (currentDate > expirationDate) {
|
||||||
|
await identityAccessTokenDAL.deleteById(tokenId);
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Failed to renew MI access token due to TTL expiration"
|
message: "Failed to renew MI access token due to TTL expiration"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// access token has never been renewed
|
// access token has never been renewed
|
||||||
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
||||||
const ttlInMilliseconds = Number(accessTokenTTL) * 1000;
|
const ttlInMilliseconds = Number(accessTokenTTL) * 1000;
|
||||||
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
||||||
|
|
||||||
if (currentDate > expirationDate)
|
if (currentDate > expirationDate) {
|
||||||
|
await identityAccessTokenDAL.deleteById(tokenId);
|
||||||
throw new UnauthorizedError({
|
throw new UnauthorizedError({
|
||||||
message: "Failed to renew MI access token due to TTL expiration"
|
message: "Failed to renew MI access token due to TTL expiration"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// max ttl checks
|
|
||||||
if (Number(accessTokenMaxTTL) > 0) {
|
|
||||||
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
|
||||||
const ttlInMilliseconds = Number(accessTokenMaxTTL) * 1000;
|
|
||||||
const currentDate = new Date();
|
|
||||||
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
|
||||||
|
|
||||||
if (currentDate > expirationDate)
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Failed to renew MI access token due to Max TTL expiration"
|
|
||||||
});
|
|
||||||
|
|
||||||
const extendToDate = new Date(currentDate.getTime() + Number(accessTokenTTL));
|
|
||||||
if (extendToDate > expirationDate)
|
|
||||||
throw new UnauthorizedError({
|
|
||||||
message: "Failed to renew MI access token past its Max TTL expiration"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => {
|
const renewAccessToken = async ({ accessToken }: TRenewAccessTokenDTO) => {
|
||||||
@@ -97,7 +83,32 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!identityAccessToken) throw new UnauthorizedError();
|
if (!identityAccessToken) throw new UnauthorizedError();
|
||||||
|
|
||||||
validateAccessTokenExp(identityAccessToken);
|
await validateAccessTokenExp(identityAccessToken);
|
||||||
|
|
||||||
|
const { accessTokenMaxTTL, createdAt: accessTokenCreatedAt, accessTokenTTL } = identityAccessToken;
|
||||||
|
|
||||||
|
// max ttl checks - will it go above max ttl
|
||||||
|
if (Number(accessTokenMaxTTL) > 0) {
|
||||||
|
const accessTokenCreated = new Date(accessTokenCreatedAt);
|
||||||
|
const ttlInMilliseconds = Number(accessTokenMaxTTL) * 1000;
|
||||||
|
const currentDate = new Date();
|
||||||
|
const expirationDate = new Date(accessTokenCreated.getTime() + ttlInMilliseconds);
|
||||||
|
|
||||||
|
if (currentDate > expirationDate) {
|
||||||
|
await identityAccessTokenDAL.deleteById(identityAccessToken.id);
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Failed to renew MI access token due to Max TTL expiration"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const extendToDate = new Date(currentDate.getTime() + Number(accessTokenTTL * 1000));
|
||||||
|
if (extendToDate > expirationDate) {
|
||||||
|
await identityAccessTokenDAL.deleteById(identityAccessToken.id);
|
||||||
|
throw new UnauthorizedError({
|
||||||
|
message: "Failed to renew MI access token past its Max TTL expiration"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
const updatedIdentityAccessToken = await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
||||||
accessTokenLastRenewedAt: new Date()
|
accessTokenLastRenewedAt: new Date()
|
||||||
@@ -131,7 +142,7 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
});
|
});
|
||||||
if (!identityAccessToken) throw new UnauthorizedError();
|
if (!identityAccessToken) throw new UnauthorizedError();
|
||||||
|
|
||||||
if (ipAddress) {
|
if (ipAddress && identityAccessToken) {
|
||||||
checkIPAgainstBlocklist({
|
checkIPAgainstBlocklist({
|
||||||
ipAddress,
|
ipAddress,
|
||||||
trustedIps: identityAccessToken?.accessTokenTrustedIps as TIp[]
|
trustedIps: identityAccessToken?.accessTokenTrustedIps as TIp[]
|
||||||
@@ -146,7 +157,14 @@ export const identityAccessTokenServiceFactory = ({
|
|||||||
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
|
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
|
||||||
}
|
}
|
||||||
|
|
||||||
validateAccessTokenExp(identityAccessToken);
|
await validateAccessTokenExp(identityAccessToken);
|
||||||
|
|
||||||
|
await identityAccessTokenDAL.updateById(identityAccessToken.id, {
|
||||||
|
accessTokenLastUsedAt: new Date(),
|
||||||
|
$incr: {
|
||||||
|
accessTokenNumUses: 1
|
||||||
|
}
|
||||||
|
});
|
||||||
return { ...identityAccessToken, orgId: identityOrgMembership.orgId };
|
return { ...identityAccessToken, orgId: identityOrgMembership.orgId };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -0,0 +1,58 @@
|
|||||||
|
import { TAuditLogDALFactory } from "@app/ee/services/audit-log/audit-log-dal";
|
||||||
|
import { logger } from "@app/lib/logger";
|
||||||
|
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
|
||||||
|
|
||||||
|
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
|
||||||
|
|
||||||
|
type TDailyResourceCleanUpQueueServiceFactoryDep = {
|
||||||
|
auditLogDAL: Pick<TAuditLogDALFactory, "pruneAuditLog">;
|
||||||
|
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "removeExpiredTokens">;
|
||||||
|
queueService: TQueueServiceFactory;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TDailyResourceCleanUpQueueServiceFactory = ReturnType<typeof dailyResourceCleanUpQueueServiceFactory>;
|
||||||
|
|
||||||
|
export const dailyResourceCleanUpQueueServiceFactory = ({
|
||||||
|
auditLogDAL,
|
||||||
|
queueService,
|
||||||
|
identityAccessTokenDAL
|
||||||
|
}: TDailyResourceCleanUpQueueServiceFactoryDep) => {
|
||||||
|
queueService.start(QueueName.DailyResourceCleanUp, async () => {
|
||||||
|
logger.info(`${QueueName.DailyResourceCleanUp}: queue task started`);
|
||||||
|
await auditLogDAL.pruneAuditLog();
|
||||||
|
await identityAccessTokenDAL.removeExpiredTokens();
|
||||||
|
logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// we do a repeat cron job in utc timezone at 12 Midnight each day
|
||||||
|
const startCleanUp = async () => {
|
||||||
|
// TODO(akhilmhdh): remove later
|
||||||
|
await queueService.stopRepeatableJob(
|
||||||
|
QueueName.AuditLogPrune,
|
||||||
|
QueueJobs.AuditLogPrune,
|
||||||
|
{ pattern: "0 0 * * *", utc: true },
|
||||||
|
QueueName.AuditLogPrune // just a job id
|
||||||
|
);
|
||||||
|
// clear previous job
|
||||||
|
await queueService.stopRepeatableJob(
|
||||||
|
QueueName.DailyResourceCleanUp,
|
||||||
|
QueueJobs.DailyResourceCleanUp,
|
||||||
|
{ pattern: "0 0 * * *", utc: true },
|
||||||
|
QueueName.DailyResourceCleanUp // just a job id
|
||||||
|
);
|
||||||
|
|
||||||
|
await queueService.queue(QueueName.DailyResourceCleanUp, QueueJobs.DailyResourceCleanUp, undefined, {
|
||||||
|
delay: 5000,
|
||||||
|
jobId: QueueName.DailyResourceCleanUp,
|
||||||
|
repeat: { pattern: "0 0 * * *", utc: true }
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
queueService.listen(QueueName.DailyResourceCleanUp, "failed", (_, err) => {
|
||||||
|
logger.error(err, `${QueueName.DailyResourceCleanUp}: resource cleanup failed`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
startCleanUp
|
||||||
|
};
|
||||||
|
};
|
@@ -51,6 +51,8 @@ description: "How to sync secrets from Infisical to GCP Secret Manager"
|
|||||||
<Warning>
|
<Warning>
|
||||||
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
Using Infisical to sync secrets to GCP Secret Manager requires that you enable
|
||||||
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
the Service Usage API and Cloud Resource Manager API in the Google Cloud project you want to sync secrets to. More on that [here](https://cloud.google.com/service-usage/docs/set-up-development-environment).
|
||||||
|
|
||||||
|
Additionally, ensure that your GCP account has sufficient permission to manage secret and service resources (you can assign Secret Manager Admin and Service Usage Admin roles for testing purposes)
|
||||||
</Warning>
|
</Warning>
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
@@ -115,6 +117,7 @@ description: "How to sync secrets from Infisical to GCP Secret Manager"
|
|||||||
</Steps>
|
</Steps>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</AccordionGroup>
|
</AccordionGroup>
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Self-Hosted Setup">
|
<Tab title="Self-Hosted Setup">
|
||||||
Using the GCP Secret Manager integration (via the OAuth2 method) on a self-hosted instance of Infisical requires configuring an OAuth2 application in GCP
|
Using the GCP Secret Manager integration (via the OAuth2 method) on a self-hosted instance of Infisical requires configuring an OAuth2 application in GCP
|
||||||
@@ -144,6 +147,6 @@ description: "How to sync secrets from Infisical to GCP Secret Manager"
|
|||||||
Once added, restart your Infisical instance and use the GCP Secret Manager integration.
|
Once added, restart your Infisical instance and use the GCP Secret Manager integration.
|
||||||
</Step>
|
</Step>
|
||||||
</Steps>
|
</Steps>
|
||||||
|
|
||||||
</Tab>
|
</Tab>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
|
|
||||||
|
@@ -152,7 +152,7 @@ export const SecretDropzone = ({
|
|||||||
|
|
||||||
e.dataTransfer.dropEffect = "copy";
|
e.dataTransfer.dropEffect = "copy";
|
||||||
setDragActive.off();
|
setDragActive.off();
|
||||||
parseFile(e.dataTransfer.files[0]);
|
parseFile(e.dataTransfer.files[0], e.dataTransfer.files[0].type === "application/json");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
|
const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
Reference in New Issue
Block a user