Merge remote-tracking branch 'origin' into feature/est-simpleenroll

This commit is contained in:
Tuan Dang
2024-08-22 14:54:34 -07:00
65 changed files with 2875 additions and 4735 deletions
@@ -0,0 +1,125 @@
import { z } from "zod";
import { KeyStorePrefixes, KeyStoreTtls, TKeyStoreFactory } from "@app/keystore/keystore";
import { applyJitter, secondsToMillis } from "@app/lib/dates";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TServiceTokenDALFactory } from "../service-token/service-token-dal";
type TAccessTokenQueueServiceFactoryDep = {
queueService: TQueueServiceFactory;
keyStore: Pick<TKeyStoreFactory, "getItem" | "setItemWithExpiry">;
identityAccessTokenDAL: Pick<TIdentityAccessTokenDALFactory, "updateById">;
serviceTokenDAL: Pick<TServiceTokenDALFactory, "updateById">;
};
export type TAccessTokenQueueServiceFactory = ReturnType<typeof accessTokenQueueServiceFactory>;
export const AccessTokenStatusSchema = z.object({
lastUpdatedAt: z.string().datetime(),
numberOfUses: z.number()
});
export const accessTokenQueueServiceFactory = ({
queueService,
keyStore,
identityAccessTokenDAL,
serviceTokenDAL
}: TAccessTokenQueueServiceFactoryDep) => {
const getIdentityTokenDetailsInCache = async (identityAccessTokenId: string) => {
const tokenDetailsInCache = await keyStore.getItem(
KeyStorePrefixes.IdentityAccessTokenStatusUpdate(identityAccessTokenId)
);
if (tokenDetailsInCache) {
return AccessTokenStatusSchema.parseAsync(JSON.parse(tokenDetailsInCache));
}
};
const updateServiceTokenStatus = async (serviceTokenId: string) => {
await keyStore.setItemWithExpiry(
KeyStorePrefixes.ServiceTokenStatusUpdate(serviceTokenId),
KeyStoreTtls.AccessTokenStatusUpdateInSeconds,
JSON.stringify({ lastUpdatedAt: new Date() })
);
await queueService.queue(
QueueName.AccessTokenStatusUpdate,
QueueJobs.ServiceTokenStatusUpdate,
{
serviceTokenId
},
{
delay: applyJitter(secondsToMillis(KeyStoreTtls.AccessTokenStatusUpdateInSeconds / 2), secondsToMillis(10)),
// https://docs.bullmq.io/guide/jobs/job-ids
jobId: KeyStorePrefixes.ServiceTokenStatusUpdate(serviceTokenId).replaceAll(":", "_"),
removeOnFail: true,
removeOnComplete: true
}
);
};
const updateIdentityAccessTokenStatus = async (identityAccessTokenId: string, numberOfUses: number) => {
await keyStore.setItemWithExpiry(
KeyStorePrefixes.IdentityAccessTokenStatusUpdate(identityAccessTokenId),
KeyStoreTtls.AccessTokenStatusUpdateInSeconds,
JSON.stringify({ lastUpdatedAt: new Date(), numberOfUses })
);
await queueService.queue(
QueueName.AccessTokenStatusUpdate,
QueueJobs.IdentityAccessTokenStatusUpdate,
{
identityAccessTokenId,
numberOfUses
},
{
delay: applyJitter(secondsToMillis(KeyStoreTtls.AccessTokenStatusUpdateInSeconds / 2), secondsToMillis(10)),
jobId: KeyStorePrefixes.IdentityAccessTokenStatusUpdate(identityAccessTokenId).replaceAll(":", "_"),
removeOnFail: true,
removeOnComplete: true
}
);
};
queueService.start(QueueName.AccessTokenStatusUpdate, async (job) => {
// for identity token update
if (job.name === QueueJobs.IdentityAccessTokenStatusUpdate && "identityAccessTokenId" in job.data) {
const { identityAccessTokenId } = job.data;
const tokenDetails = { lastUpdatedAt: new Date(job.timestamp), numberOfUses: job.data.numberOfUses };
const tokenDetailsInCache = await getIdentityTokenDetailsInCache(identityAccessTokenId);
if (tokenDetailsInCache) {
tokenDetails.numberOfUses = tokenDetailsInCache.numberOfUses;
tokenDetails.lastUpdatedAt = new Date(tokenDetailsInCache.lastUpdatedAt);
}
await identityAccessTokenDAL.updateById(identityAccessTokenId, {
accessTokenLastUsedAt: tokenDetails.lastUpdatedAt,
accessTokenNumUses: tokenDetails.numberOfUses
});
return;
}
// for service token
if (job.name === QueueJobs.ServiceTokenStatusUpdate && "serviceTokenId" in job.data) {
const { serviceTokenId } = job.data;
const tokenDetailsInCache = await keyStore.getItem(KeyStorePrefixes.ServiceTokenStatusUpdate(serviceTokenId));
let lastUsed = new Date(job.timestamp);
if (tokenDetailsInCache) {
const tokenDetails = await AccessTokenStatusSchema.pick({ lastUpdatedAt: true }).parseAsync(
JSON.parse(tokenDetailsInCache)
);
lastUsed = new Date(tokenDetails.lastUpdatedAt);
}
await serviceTokenDAL.updateById(serviceTokenId, {
lastUsed
});
}
});
queueService.listen(QueueName.AccessTokenStatusUpdate, "failed", (_, err) => {
logger.error(err, `${QueueName.AccessTokenStatusUpdate}: Failed to updated access token status`);
});
return { updateIdentityAccessTokenStatus, updateServiceTokenStatus, getIdentityTokenDetailsInCache };
};
@@ -368,7 +368,6 @@ export const certificateAuthorityServiceFactory = ({
);
if (ca.type === CaType.ROOT) throw new BadRequestError({ message: "Root CA cannot generate CSR" });
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA already has a certificate installed" });
const { caPrivateKey, caPublicKey } = await getCaCredentials({
caId,
@@ -407,7 +406,8 @@ export const certificateAuthorityServiceFactory = ({
/**
* Renew certificate for CA with id [caId]
* Note: Currently implements CA renewal with same key-pair only
* Note 1: This CA renewal method is only applicable to CAs with internal parent CAs
* Note 2: Currently implements CA renewal with same key-pair only
*/
const renewCaCert = async ({ caId, notAfter, actorId, actorAuthMethod, actor, actorOrgId }: TRenewCaCertDTO) => {
const ca = await certificateAuthorityDAL.findById(caId);
@@ -888,9 +888,9 @@ export const certificateAuthorityServiceFactory = ({
};
/**
* Import certificate for (un-installed) CA with id [caId].
* Import certificate for CA with id [caId].
* Note: Can be used to import an external certificate and certificate chain
* to be installed into the CA.
* to be into an installed or uninstalled CA.
*/
const importCertToCa = async ({
caId,
@@ -917,7 +917,18 @@ export const certificateAuthorityServiceFactory = ({
ProjectPermissionSub.CertificateAuthorities
);
if (ca.activeCaCertId) throw new BadRequestError({ message: "CA has already imported a certificate" });
if (ca.parentCaId) {
/**
* re-evaluate in the future if we should allow users to import a new CA certificate for an intermediate
* CA chained to an internal parent CA. Doing so would allow users to re-chain the CA to a different
* internal CA.
*/
throw new BadRequestError({
message: "Cannot import certificate to intermediate CA chained to internal parent CA"
});
}
const caCert = ca.activeCaCertId ? await certificateAuthorityCertDAL.findById(ca.activeCaCertId) : undefined;
const certObj = new x509.X509Certificate(certificate);
const maxPathLength = certObj.getExtension(x509.BasicConstraintsExtension)?.pathLength;
@@ -988,7 +999,7 @@ export const certificateAuthorityServiceFactory = ({
caId: ca.id,
encryptedCertificate,
encryptedCertificateChain,
version: 1,
version: caCert ? caCert.version + 1 : 1,
caSecretId: caSecret.id
},
tx
@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { TableName, TUserEncryptionKeys } from "@app/db/schemas";
import { DatabaseError } from "@app/lib/errors";
import { ormify, sqlNestRelationships } from "@app/lib/knex";
@@ -95,5 +95,107 @@ export const groupProjectDALFactory = (db: TDbClient) => {
}
};
return { ...groupProjectOrm, findByProjectId };
// The GroupProjectMembership table has a reference to the project (projectId) AND the group (groupId).
// We need to join the GroupProjectMembership table with the Groups table to get the group name and slug.
// We also need to join the GroupProjectMembershipRole table to get the role of the group in the project.
const findAllProjectGroupMembers = async (projectId: string) => {
const docs = await db(TableName.UserGroupMembership)
// Join the GroupProjectMembership table with the Groups table to get the group name and slug.
.join(
TableName.GroupProjectMembership,
`${TableName.UserGroupMembership}.groupId`,
`${TableName.GroupProjectMembership}.groupId` // this gives us access to the project id in the group membership
)
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.GroupProjectMembership}.projectId`, projectId)
.join(TableName.Users, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`)
.join<TUserEncryptionKeys>(
TableName.UserEncryptionKey,
`${TableName.UserEncryptionKey}.userId`,
`${TableName.Users}.id`
)
.join(
TableName.GroupProjectMembershipRole,
`${TableName.GroupProjectMembershipRole}.projectMembershipId`,
`${TableName.GroupProjectMembership}.id`
)
.leftJoin(
TableName.ProjectRoles,
`${TableName.GroupProjectMembershipRole}.customRoleId`,
`${TableName.ProjectRoles}.id`
)
.select(
db.ref("id").withSchema(TableName.GroupProjectMembership),
db.ref("isGhost").withSchema(TableName.Users),
db.ref("username").withSchema(TableName.Users),
db.ref("email").withSchema(TableName.Users),
db.ref("publicKey").withSchema(TableName.UserEncryptionKey),
db.ref("firstName").withSchema(TableName.Users),
db.ref("lastName").withSchema(TableName.Users),
db.ref("id").withSchema(TableName.Users).as("userId"),
db.ref("role").withSchema(TableName.GroupProjectMembershipRole),
db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("membershipRoleId"),
db.ref("customRoleId").withSchema(TableName.GroupProjectMembershipRole),
db.ref("name").withSchema(TableName.ProjectRoles).as("customRoleName"),
db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"),
db.ref("temporaryMode").withSchema(TableName.GroupProjectMembershipRole),
db.ref("isTemporary").withSchema(TableName.GroupProjectMembershipRole),
db.ref("temporaryRange").withSchema(TableName.GroupProjectMembershipRole),
db.ref("temporaryAccessStartTime").withSchema(TableName.GroupProjectMembershipRole),
db.ref("temporaryAccessEndTime").withSchema(TableName.GroupProjectMembershipRole),
db.ref("name").as("projectName").withSchema(TableName.Project)
)
.where({ isGhost: false });
const members = sqlNestRelationships({
data: docs,
parentMapper: ({ email, firstName, username, lastName, publicKey, isGhost, id, userId, projectName }) => ({
isGroupMember: true,
id,
userId,
projectId,
project: {
id: projectId,
name: projectName
},
user: { email, username, firstName, lastName, id: userId, publicKey, isGhost }
}),
key: "id",
childrenMapper: [
{
label: "roles" as const,
key: "membershipRoleId",
mapper: ({
role,
customRoleId,
customRoleName,
customRoleSlug,
membershipRoleId,
temporaryRange,
temporaryMode,
temporaryAccessEndTime,
temporaryAccessStartTime,
isTemporary
}) => ({
id: membershipRoleId,
role,
customRoleId,
customRoleName,
customRoleSlug,
temporaryRange,
temporaryMode,
temporaryAccessEndTime,
temporaryAccessStartTime,
isTemporary
})
}
]
});
return members;
};
return { ...groupProjectOrm, findByProjectId, findAllProjectGroupMembers };
};
@@ -5,6 +5,7 @@ import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { checkIPAgainstBlocklist, TIp } from "@app/lib/ip";
import { TAccessTokenQueueServiceFactory } from "../access-token-queue/access-token-queue";
import { AuthTokenType } from "../auth/auth-type";
import { TIdentityOrgDALFactory } from "../identity/identity-org-dal";
import { TIdentityAccessTokenDALFactory } from "./identity-access-token-dal";
@@ -13,19 +14,24 @@ import { TIdentityAccessTokenJwtPayload, TRenewAccessTokenDTO } from "./identity
type TIdentityAccessTokenServiceFactoryDep = {
identityAccessTokenDAL: TIdentityAccessTokenDALFactory;
identityOrgMembershipDAL: TIdentityOrgDALFactory;
accessTokenQueue: Pick<
TAccessTokenQueueServiceFactory,
"updateIdentityAccessTokenStatus" | "getIdentityTokenDetailsInCache"
>;
};
export type TIdentityAccessTokenServiceFactory = ReturnType<typeof identityAccessTokenServiceFactory>;
export const identityAccessTokenServiceFactory = ({
identityAccessTokenDAL,
identityOrgMembershipDAL
identityOrgMembershipDAL,
accessTokenQueue
}: TIdentityAccessTokenServiceFactoryDep) => {
const validateAccessTokenExp = async (identityAccessToken: TIdentityAccessTokens) => {
const {
id: tokenId,
accessTokenTTL,
accessTokenNumUses,
accessTokenTTL,
accessTokenNumUsesLimit,
accessTokenLastRenewedAt,
createdAt: accessTokenCreatedAt
@@ -83,7 +89,12 @@ export const identityAccessTokenServiceFactory = ({
});
if (!identityAccessToken) throw new UnauthorizedError();
await validateAccessTokenExp(identityAccessToken);
let { accessTokenNumUses } = identityAccessToken;
const tokenStatusInCache = await accessTokenQueue.getIdentityTokenDetailsInCache(identityAccessToken.id);
if (tokenStatusInCache) {
accessTokenNumUses = tokenStatusInCache.numberOfUses;
}
await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses });
const { accessTokenMaxTTL, createdAt: accessTokenCreatedAt, accessTokenTTL } = identityAccessToken;
@@ -164,14 +175,14 @@ export const identityAccessTokenServiceFactory = ({
throw new UnauthorizedError({ message: "Identity does not belong to any organization" });
}
await validateAccessTokenExp(identityAccessToken);
let { accessTokenNumUses } = identityAccessToken;
const tokenStatusInCache = await accessTokenQueue.getIdentityTokenDetailsInCache(identityAccessToken.id);
if (tokenStatusInCache) {
accessTokenNumUses = tokenStatusInCache.numberOfUses;
}
await validateAccessTokenExp({ ...identityAccessToken, accessTokenNumUses });
await identityAccessTokenDAL.updateById(identityAccessToken.id, {
accessTokenLastUsedAt: new Date(),
$incr: {
accessTokenNumUses: 1
}
});
await accessTokenQueue.updateIdentityAccessTokenStatus(identityAccessToken.id, Number(accessTokenNumUses) + 1);
return { ...identityAccessToken, orgId: identityOrgMembership.orgId };
};
@@ -65,7 +65,7 @@ export const identityAwsAuthServiceFactory = ({
}
}: { data: TGetCallerIdentityResponse } = await axios({
method: iamHttpRequestMethod,
url: identityAwsAuth.stsEndpoint,
url: headers?.Host ? `https://${headers.Host}` : identityAwsAuth.stsEndpoint,
headers,
data: body
});
+10
View File
@@ -1,4 +1,5 @@
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { TOrgDALFactory } from "@app/services/org/org-dal";
import { TProjectKeyDALFactory } from "@app/services/project-key/project-key-dal";
import { TProjectMembershipDALFactory } from "@app/services/project-membership/project-membership-dal";
@@ -12,6 +13,7 @@ type TDeleteOrgMembership = {
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete">;
userAliasDAL: Pick<TUserAliasDALFactory, "delete">;
licenseService: Pick<TLicenseServiceFactory, "updateSubscriptionOrgMemberCount">;
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
};
export const deleteOrgMembershipFn = async ({
@@ -19,6 +21,7 @@ export const deleteOrgMembershipFn = async ({
orgId,
orgDAL,
projectMembershipDAL,
projectUserAdditionalPrivilegeDAL,
projectKeyDAL,
userAliasDAL,
licenseService
@@ -39,6 +42,13 @@ export const deleteOrgMembershipFn = async ({
tx
);
await projectUserAdditionalPrivilegeDAL.delete(
{
userId: orgMembership.userId
},
tx
);
// Get all the project memberships of the user in the organization
const projectMemberships = await projectMembershipDAL.findProjectMembershipsByUserId(orgId, orgMembership.userId);
+4
View File
@@ -10,6 +10,7 @@ import { TGroupDALFactory } from "@app/ee/services/group/group-dal";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
@@ -67,6 +68,7 @@ type TOrgServiceFactoryDep = {
TLicenseServiceFactory,
"getPlan" | "updateSubscriptionOrgMemberCount" | "generateOrgCustomerId" | "removeOrgCustomer"
>;
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
};
export type TOrgServiceFactory = ReturnType<typeof orgServiceFactory>;
@@ -84,6 +86,7 @@ export const orgServiceFactory = ({
projectMembershipDAL,
projectKeyDAL,
orgMembershipDAL,
projectUserAdditionalPrivilegeDAL,
tokenService,
orgBotDAL,
licenseService,
@@ -632,6 +635,7 @@ export const orgServiceFactory = ({
orgId,
orgDAL,
projectMembershipDAL,
projectUserAdditionalPrivilegeDAL,
projectKeyDAL,
userAliasDAL,
licenseService
@@ -12,6 +12,7 @@ import {
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors";
@@ -19,6 +20,7 @@ import { groupBy } from "@app/lib/fn";
import { TUserGroupMembershipDALFactory } from "../../ee/services/group/user-group-membership-dal";
import { ActorType } from "../auth/auth-type";
import { TGroupProjectDALFactory } from "../group-project/group-project-dal";
import { TOrgDALFactory } from "../org/org-dal";
import { TProjectDALFactory } from "../project/project-dal";
import { assignWorkspaceKeysToMembers } from "../project/project-fns";
@@ -54,6 +56,8 @@ type TProjectMembershipServiceFactoryDep = {
projectDAL: Pick<TProjectDALFactory, "findById" | "findProjectGhostUser" | "transaction">;
projectKeyDAL: Pick<TProjectKeyDALFactory, "findLatestProjectKey" | "delete" | "insertMany">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
projectUserAdditionalPrivilegeDAL: Pick<TProjectUserAdditionalPrivilegeDALFactory, "delete">;
groupProjectDAL: TGroupProjectDALFactory;
};
export type TProjectMembershipServiceFactory = ReturnType<typeof projectMembershipServiceFactory>;
@@ -66,8 +70,10 @@ export const projectMembershipServiceFactory = ({
projectRoleDAL,
projectBotDAL,
orgDAL,
projectUserAdditionalPrivilegeDAL,
userDAL,
userGroupMembershipDAL,
groupProjectDAL,
projectDAL,
projectKeyDAL,
licenseService
@@ -77,6 +83,7 @@ export const projectMembershipServiceFactory = ({
actor,
actorOrgId,
actorAuthMethod,
includeGroupMembers,
projectId
}: TGetProjectMembershipDTO) => {
const { permission } = await permissionService.getProjectPermission(
@@ -88,7 +95,25 @@ export const projectMembershipServiceFactory = ({
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member);
return projectMembershipDAL.findAllProjectMembers(projectId);
const projectMembers = await projectMembershipDAL.findAllProjectMembers(projectId);
// projectMembers[0].project
if (includeGroupMembers) {
const groupMembers = await groupProjectDAL.findAllProjectGroupMembers(projectId);
const allMembers = [
...projectMembers.map((m) => ({ ...m, isGroupMember: false })),
...groupMembers.map((m) => ({ ...m, isGroupMember: true }))
];
// Ensure the userId is unique
const membersIds = new Set(allMembers.map((entity) => entity.user.id));
const uniqueMembers = allMembers.filter((entity) => membersIds.has(entity.user.id));
return uniqueMembers;
}
return projectMembers.map((m) => ({ ...m, isGroupMember: false }));
};
const getProjectMembershipByUsername = async ({
@@ -502,6 +527,16 @@ export const projectMembershipServiceFactory = ({
);
const memberships = await projectMembershipDAL.transaction(async (tx) => {
await projectUserAdditionalPrivilegeDAL.delete(
{
projectId,
$in: {
userId: projectMembers.map((membership) => membership.user.id)
}
},
tx
);
const deletedMemberships = await projectMembershipDAL.delete(
{
projectId,
@@ -564,12 +599,25 @@ export const projectMembershipServiceFactory = ({
});
}
const deletedMembership = (
await projectMembershipDAL.delete({
projectId: project.id,
userId: actorId
})
)?.[0];
const deletedMembership = await projectMembershipDAL.transaction(async (tx) => {
await projectUserAdditionalPrivilegeDAL.delete(
{
projectId: project.id,
userId: actorId
},
tx
);
const membership = (
await projectMembershipDAL.delete(
{
projectId: project.id,
userId: actorId
},
tx
)
)?.[0];
return membership;
});
if (!deletedMembership) {
throw new BadRequestError({ message: "Failed to leave project" });
@@ -1,6 +1,6 @@
import { TProjectPermission } from "@app/lib/types";
export type TGetProjectMembershipDTO = TProjectPermission;
export type TGetProjectMembershipDTO = { includeGroupMembers?: boolean } & TProjectPermission;
export type TLeaveProjectDTO = Omit<TProjectPermission, "actorOrgId" | "actorAuthMethod">;
export enum ProjectUserMembershipTemporaryMode {
Relative = "relative"
@@ -8,6 +8,7 @@ import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services
import { getConfig } from "@app/lib/config/env";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { TAccessTokenQueueServiceFactory } from "../access-token-queue/access-token-queue";
import { ActorType } from "../auth/auth-type";
import { TProjectDALFactory } from "../project/project-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
@@ -26,6 +27,7 @@ type TServiceTokenServiceFactoryDep = {
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectEnvDAL: Pick<TProjectEnvDALFactory, "findBySlugs">;
projectDAL: Pick<TProjectDALFactory, "findById">;
accessTokenQueue: Pick<TAccessTokenQueueServiceFactory, "updateServiceTokenStatus">;
};
export type TServiceTokenServiceFactory = ReturnType<typeof serviceTokenServiceFactory>;
@@ -35,7 +37,8 @@ export const serviceTokenServiceFactory = ({
userDAL,
permissionService,
projectEnvDAL,
projectDAL
projectDAL,
accessTokenQueue
}: TServiceTokenServiceFactoryDep) => {
const createServiceToken = async ({
iv,
@@ -166,11 +169,9 @@ export const serviceTokenServiceFactory = ({
const isMatch = await bcrypt.compare(TOKEN_SECRET, serviceToken.secretHash);
if (!isMatch) throw new UnauthorizedError();
const updatedToken = await serviceTokenDAL.updateById(serviceToken.id, {
lastUsed: new Date()
});
await accessTokenQueue.updateServiceTokenStatus(serviceToken.id);
return { ...serviceToken, lastUsed: updatedToken.lastUsed, orgId: project.orgId };
return { ...serviceToken, lastUsed: new Date(), orgId: project.orgId };
};
return {