mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 12:38:36 +00:00
feat(pam): PAM Platform V1
This commit is contained in:
@@ -0,0 +1,671 @@
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, TPamAccounts, TPamResources } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionPamAccountActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
import { ActorType } from "@app/services/auth/auth-type";
|
||||
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
|
||||
import { TProjectDALFactory } from "@app/services/project/project-dal";
|
||||
|
||||
import { TGatewayV2ServiceFactory } from "../gateway-v2/gateway-v2-service";
|
||||
import { TLicenseServiceFactory } from "../license/license-service";
|
||||
import { TPamFolderDALFactory } from "../pam-folder/pam-folder-dal";
|
||||
import { getFullPamFolderPath } from "../pam-folder/pam-folder-fns";
|
||||
import { TPamSessionDALFactory } from "../pam-session/pam-session-dal";
|
||||
import { PamSessionStatus } from "../pam-session/pam-session-enums";
|
||||
import { OrgPermissionGatewayActions, OrgPermissionSubjects } from "../permission/org-permission";
|
||||
import { TPamAccountDALFactory } from "./pam-account-dal";
|
||||
import { TPamResourceDALFactory } from "./pam-resource-dal";
|
||||
import { PamResource } from "./pam-resource-enums";
|
||||
import { PAM_RESOURCE_FACTORY_MAP } from "./pam-resource-factory";
|
||||
import {
|
||||
decryptAccount,
|
||||
decryptAccountCredentials,
|
||||
decryptResource,
|
||||
decryptResourceConnectionDetails,
|
||||
encryptAccountCredentials,
|
||||
encryptResourceConnectionDetails,
|
||||
listResourceOptions
|
||||
} from "./pam-resource-fns";
|
||||
import {
|
||||
TAccessAccountDTO,
|
||||
TCreateAccountDTO,
|
||||
TCreateResourceDTO,
|
||||
TPamAccountCredentials,
|
||||
TUpdateAccountDTO,
|
||||
TUpdateResourceDTO
|
||||
} from "./pam-resource-types";
|
||||
|
||||
type TPamResourceServiceFactoryDep = {
|
||||
pamResourceDAL: TPamResourceDALFactory;
|
||||
pamSessionDAL: TPamSessionDALFactory;
|
||||
pamAccountDAL: TPamAccountDALFactory;
|
||||
pamFolderDAL: TPamFolderDALFactory;
|
||||
projectDAL: TProjectDALFactory;
|
||||
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission" | "getOrgPermission">;
|
||||
licenseService: Pick<TLicenseServiceFactory, "getPlan">;
|
||||
kmsService: Pick<TKmsServiceFactory, "createCipherPairWithDataKey">;
|
||||
gatewayV2Service: Pick<TGatewayV2ServiceFactory, "getPlatformConnectionDetailsByGatewayId">;
|
||||
};
|
||||
|
||||
export type TPamResourceServiceFactory = ReturnType<typeof pamResourceServiceFactory>;
|
||||
|
||||
export const pamResourceServiceFactory = ({
|
||||
pamResourceDAL,
|
||||
pamSessionDAL,
|
||||
pamAccountDAL,
|
||||
pamFolderDAL,
|
||||
projectDAL,
|
||||
permissionService,
|
||||
licenseService,
|
||||
kmsService,
|
||||
gatewayV2Service
|
||||
}: TPamResourceServiceFactoryDep) => {
|
||||
const getById = async (id: string, resourceType: PamResource, actor: OrgServiceActor) => {
|
||||
const resource = await pamResourceDAL.findById(id);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${id}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: resource.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PamResources);
|
||||
|
||||
if (resource.resourceType !== resourceType) {
|
||||
throw new BadRequestError({
|
||||
message: `Resource with ID '${id}' is not of type '${resourceType}'`
|
||||
});
|
||||
}
|
||||
|
||||
return decryptResource(resource, actor.orgId, kmsService);
|
||||
};
|
||||
|
||||
const create = async (
|
||||
{ resourceType, connectionDetails, gatewayId, name, projectId }: TCreateResourceDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.PamResources);
|
||||
|
||||
const factory = PAM_RESOURCE_FACTORY_MAP[resourceType](
|
||||
resourceType,
|
||||
connectionDetails,
|
||||
gatewayId,
|
||||
gatewayV2Service
|
||||
);
|
||||
const validatedConnectionDetails = await factory.validateConnection();
|
||||
|
||||
const encryptedConnectionDetails = await encryptResourceConnectionDetails({
|
||||
connectionDetails: validatedConnectionDetails,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const resource = await pamResourceDAL.create({
|
||||
resourceType,
|
||||
encryptedConnectionDetails,
|
||||
gatewayId,
|
||||
name,
|
||||
projectId
|
||||
});
|
||||
|
||||
return decryptResource(resource, actor.orgId, kmsService);
|
||||
};
|
||||
|
||||
const updateById = async ({ connectionDetails, resourceId, name }: TUpdateResourceDTO, actor: OrgServiceActor) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
const resource = await pamResourceDAL.findById(resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: resource.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PamResources);
|
||||
|
||||
const updateDoc: Partial<TPamResources> = {};
|
||||
|
||||
if (name !== undefined) {
|
||||
updateDoc.name = name;
|
||||
}
|
||||
|
||||
if (connectionDetails !== undefined) {
|
||||
const factory = PAM_RESOURCE_FACTORY_MAP[resource.resourceType as PamResource](
|
||||
resource.resourceType as PamResource,
|
||||
connectionDetails,
|
||||
resource.gatewayId,
|
||||
gatewayV2Service
|
||||
);
|
||||
const validatedConnectionDetails = await factory.validateConnection();
|
||||
const encryptedConnectionDetails = await encryptResourceConnectionDetails({
|
||||
connectionDetails: validatedConnectionDetails,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
updateDoc.encryptedConnectionDetails = encryptedConnectionDetails;
|
||||
}
|
||||
|
||||
// If nothing was updated, return the fetched resource
|
||||
if (Object.keys(updateDoc).length === 0) {
|
||||
return decryptResource(resource, actor.orgId, kmsService);
|
||||
}
|
||||
|
||||
const updatedResource = await pamResourceDAL.updateById(resourceId, updateDoc);
|
||||
|
||||
return decryptResource(updatedResource, actor.orgId, kmsService);
|
||||
};
|
||||
|
||||
const deleteById = async (id: string, actor: OrgServiceActor) => {
|
||||
const resource = await pamResourceDAL.findById(id);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${id}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: resource.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.PamResources);
|
||||
|
||||
const deletedResource = await pamResourceDAL.deleteById(id);
|
||||
|
||||
return decryptResource(deletedResource, actor.orgId, kmsService);
|
||||
};
|
||||
|
||||
const list = async (projectId: string, actor: OrgServiceActor) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PamResources);
|
||||
|
||||
const resources = await pamResourceDAL.find({ projectId });
|
||||
|
||||
return {
|
||||
resources: await Promise.all(resources.map((resource) => decryptResource(resource, actor.orgId, kmsService)))
|
||||
};
|
||||
};
|
||||
|
||||
// Accounts
|
||||
const createAccount = async (
|
||||
{ credentials, resourceId, name, description, folderId }: TCreateAccountDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
const resource = await pamResourceDAL.findById(resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: resource.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
const accountPath = await getFullPamFolderPath({
|
||||
pamFolderDAL,
|
||||
folderId,
|
||||
projectId: resource.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Create,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: name,
|
||||
accountPath
|
||||
})
|
||||
);
|
||||
|
||||
const connectionDetails = await decryptResourceConnectionDetails({
|
||||
orgId: actor.orgId,
|
||||
encryptedConnectionDetails: resource.encryptedConnectionDetails,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const factory = PAM_RESOURCE_FACTORY_MAP[resource.resourceType as PamResource](
|
||||
resource.resourceType as PamResource,
|
||||
connectionDetails,
|
||||
resource.gatewayId,
|
||||
gatewayV2Service
|
||||
);
|
||||
const validatedCredentials = await factory.validateAccountCredentials(credentials);
|
||||
|
||||
const encryptedCredentials = await encryptAccountCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const account = await pamAccountDAL.create({
|
||||
projectId: resource.projectId,
|
||||
resourceId: resource.id,
|
||||
encryptedCredentials,
|
||||
name,
|
||||
description,
|
||||
folderId
|
||||
});
|
||||
|
||||
return {
|
||||
...(await decryptAccount(account, actor.orgId, kmsService)),
|
||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
||||
};
|
||||
};
|
||||
|
||||
const updateAccountById = async (
|
||||
{ accountId, credentials, description, name }: TUpdateAccountDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
const account = await pamAccountDAL.findById(accountId);
|
||||
if (!account) throw new NotFoundError({ message: `Account with ID '${accountId}' not found` });
|
||||
|
||||
const resource = await pamResourceDAL.findById(account.resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${account.resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: account.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
const accountPath = await getFullPamFolderPath({
|
||||
pamFolderDAL,
|
||||
folderId: account.folderId,
|
||||
projectId: account.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Edit,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: account.name,
|
||||
accountPath
|
||||
})
|
||||
);
|
||||
|
||||
const updateDoc: Partial<TPamAccounts> = {};
|
||||
|
||||
if (name !== undefined) {
|
||||
updateDoc.name = name;
|
||||
}
|
||||
|
||||
if (description !== undefined) {
|
||||
updateDoc.description = description;
|
||||
}
|
||||
|
||||
if (credentials !== undefined) {
|
||||
const connectionDetails = await decryptResourceConnectionDetails({
|
||||
orgId: actor.orgId,
|
||||
encryptedConnectionDetails: resource.encryptedConnectionDetails,
|
||||
kmsService
|
||||
});
|
||||
|
||||
const factory = PAM_RESOURCE_FACTORY_MAP[resource.resourceType as PamResource](
|
||||
resource.resourceType as PamResource,
|
||||
connectionDetails,
|
||||
resource.gatewayId,
|
||||
gatewayV2Service
|
||||
);
|
||||
|
||||
// Logic to prevent overwriting unedited censored values
|
||||
const finalCredentials = { ...credentials };
|
||||
if (credentials.password === "******") {
|
||||
const decryptedCredentials = await decryptAccountCredentials({
|
||||
encryptedCredentials: account.encryptedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
|
||||
finalCredentials.password = decryptedCredentials.password;
|
||||
}
|
||||
|
||||
const validatedCredentials = await factory.validateAccountCredentials(finalCredentials);
|
||||
const encryptedCredentials = await encryptAccountCredentials({
|
||||
credentials: validatedCredentials,
|
||||
orgId: actor.orgId,
|
||||
kmsService
|
||||
});
|
||||
updateDoc.encryptedCredentials = encryptedCredentials;
|
||||
}
|
||||
|
||||
// If nothing was updated, return the fetched account
|
||||
if (Object.keys(updateDoc).length === 0) {
|
||||
return decryptAccount(account, actor.orgId, kmsService);
|
||||
}
|
||||
|
||||
const updatedAccount = await pamAccountDAL.updateById(accountId, updateDoc);
|
||||
|
||||
return {
|
||||
...(await decryptAccount(updatedAccount, actor.orgId, kmsService)),
|
||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
||||
};
|
||||
};
|
||||
|
||||
const deleteAccountById = async (id: string, actor: OrgServiceActor) => {
|
||||
const account = await pamAccountDAL.findById(id);
|
||||
if (!account) throw new NotFoundError({ message: `Account with ID '${id}' not found` });
|
||||
|
||||
const resource = await pamResourceDAL.findById(account.resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${account.resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: account.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
const accountPath = await getFullPamFolderPath({
|
||||
pamFolderDAL,
|
||||
folderId: account.folderId,
|
||||
projectId: account.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Delete,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: account.name,
|
||||
accountPath
|
||||
})
|
||||
);
|
||||
|
||||
const deletedAccount = await pamAccountDAL.deleteById(id);
|
||||
|
||||
return {
|
||||
...(await decryptAccount(deletedAccount, actor.orgId, kmsService)),
|
||||
resource: { id: resource.id, name: resource.name, resourceType: resource.resourceType }
|
||||
};
|
||||
};
|
||||
|
||||
const listAccounts = async (projectId: string, actor: OrgServiceActor) => {
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
const accountsWithResourceDetails = await pamAccountDAL.findWithResourceDetails({ projectId });
|
||||
|
||||
const canReadFolders = permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.PamFolders);
|
||||
|
||||
const folders = canReadFolders ? await pamFolderDAL.find({ projectId }) : [];
|
||||
|
||||
const decryptedAndPermittedAccounts: Array<
|
||||
TPamAccounts & {
|
||||
resource: Pick<TPamResources, "id" | "name" | "resourceType">;
|
||||
credentials: TPamAccountCredentials;
|
||||
}
|
||||
> = [];
|
||||
|
||||
for await (const account of accountsWithResourceDetails) {
|
||||
const accountPath = await getFullPamFolderPath({
|
||||
pamFolderDAL,
|
||||
folderId: account.folderId,
|
||||
projectId: account.projectId
|
||||
});
|
||||
|
||||
// Check permission for each individual account
|
||||
if (
|
||||
permission.can(
|
||||
ProjectPermissionPamAccountActions.Read,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: account.resource.name,
|
||||
accountName: account.name,
|
||||
accountPath
|
||||
})
|
||||
)
|
||||
) {
|
||||
// Decrypt the account only if the user has permission to read it
|
||||
const decryptedAccount = await decryptAccount(account, actor.orgId, kmsService);
|
||||
decryptedAndPermittedAccounts.push({
|
||||
...decryptedAccount,
|
||||
resource: {
|
||||
id: account.resource.id,
|
||||
name: account.resource.name,
|
||||
resourceType: account.resource.resourceType
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
accounts: decryptedAndPermittedAccounts,
|
||||
folders
|
||||
};
|
||||
};
|
||||
|
||||
const accessAccount = async (
|
||||
{ accountId, actorEmail, actorIp, actorName, actorUserAgent, duration }: TAccessAccountDTO,
|
||||
actor: OrgServiceActor
|
||||
) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
const account = await pamAccountDAL.findById(accountId);
|
||||
if (!account) throw new NotFoundError({ message: `Account with ID '${accountId}' not found` });
|
||||
|
||||
const resource = await pamResourceDAL.findById(account.resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${account.resourceId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor: actor.type,
|
||||
actorAuthMethod: actor.authMethod,
|
||||
actorId: actor.id,
|
||||
actorOrgId: actor.orgId,
|
||||
projectId: account.projectId,
|
||||
actionProjectType: ActionProjectType.PAM
|
||||
});
|
||||
|
||||
const accountPath = await getFullPamFolderPath({
|
||||
pamFolderDAL,
|
||||
folderId: account.folderId,
|
||||
projectId: account.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPamAccountActions.Access,
|
||||
subject(ProjectPermissionSub.PamAccounts, {
|
||||
resourceName: resource.name,
|
||||
accountName: account.name,
|
||||
accountPath
|
||||
})
|
||||
);
|
||||
|
||||
const session = await pamSessionDAL.create({
|
||||
accountName: account.name,
|
||||
actorEmail,
|
||||
actorIp,
|
||||
actorName,
|
||||
actorUserAgent,
|
||||
projectId: account.projectId,
|
||||
resourceName: resource.name,
|
||||
resourceType: resource.resourceType,
|
||||
status: PamSessionStatus.Starting,
|
||||
accountId: account.id,
|
||||
userId: actor.id,
|
||||
expiresAt: duration ? new Date(Date.now() + duration) : null
|
||||
});
|
||||
|
||||
const { connectionDetails, gatewayId, resourceType } = await decryptResource(resource, actor.orgId, kmsService);
|
||||
|
||||
const gatewayConnectionDetails = await gatewayV2Service.getPlatformConnectionDetailsByGatewayId({
|
||||
gatewayId,
|
||||
targetHost: connectionDetails.host,
|
||||
targetPort: connectionDetails.port,
|
||||
actorMetadata: {
|
||||
sessionId: session.id,
|
||||
resourceType: resource.resourceType
|
||||
}
|
||||
});
|
||||
|
||||
if (!gatewayConnectionDetails) {
|
||||
throw new NotFoundError({ message: `Gateway connection details for gateway '${gatewayId}' not found.` });
|
||||
}
|
||||
|
||||
return {
|
||||
sessionId: session.id,
|
||||
resourceType,
|
||||
relayCertificate: gatewayConnectionDetails.relay.clientCertificate,
|
||||
gatewayCertificate: gatewayConnectionDetails.gateway.clientCertificate,
|
||||
relayHost: gatewayConnectionDetails.relayHost,
|
||||
projectId: account.projectId
|
||||
};
|
||||
};
|
||||
|
||||
const getSessionCredentials = async (sessionId: string, actor: OrgServiceActor) => {
|
||||
const orgLicensePlan = await licenseService.getPlan(actor.orgId);
|
||||
if (!orgLicensePlan.pam) {
|
||||
throw new BadRequestError({
|
||||
message: "PAM operation failed due to organization plan restrictions."
|
||||
});
|
||||
}
|
||||
|
||||
// To be hit by gateways only
|
||||
if (actor.type !== ActorType.IDENTITY) {
|
||||
throw new ForbiddenRequestError({ message: "Only gateways can perform this action" });
|
||||
}
|
||||
|
||||
const session = await pamSessionDAL.findById(sessionId);
|
||||
if (!session) throw new NotFoundError({ message: `Session with ID '${sessionId}' not found` });
|
||||
|
||||
const project = await projectDAL.findById(session.projectId);
|
||||
if (!project) throw new NotFoundError({ message: `Project with ID '${session.projectId}' not found` });
|
||||
|
||||
const { permission } = await permissionService.getOrgPermission(
|
||||
actor.type,
|
||||
actor.id,
|
||||
project.orgId,
|
||||
actor.authMethod,
|
||||
actor.orgId
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
OrgPermissionGatewayActions.CreateGateways,
|
||||
OrgPermissionSubjects.Gateway
|
||||
);
|
||||
|
||||
if (!session.accountId) throw new NotFoundError({ message: "Session is missing accountId column" });
|
||||
|
||||
// Verify that the session has not ended
|
||||
if (session.endedAt || (session.expiresAt && session.expiresAt < new Date())) {
|
||||
throw new BadRequestError({ message: "Session has ended or expired" });
|
||||
}
|
||||
|
||||
// Verify that the session has not already had credentials fetched
|
||||
if (session.status !== PamSessionStatus.Starting) {
|
||||
throw new BadRequestError({ message: "Session has already been started" });
|
||||
}
|
||||
|
||||
const account = await pamAccountDAL.findById(session.accountId);
|
||||
if (!account) throw new NotFoundError({ message: `Account with ID '${session.accountId}' not found` });
|
||||
|
||||
const resource = await pamResourceDAL.findById(account.resourceId);
|
||||
if (!resource) throw new NotFoundError({ message: `Resource with ID '${account.resourceId}' not found` });
|
||||
|
||||
const decryptedAccount = await decryptAccount(account, actor.orgId, kmsService);
|
||||
|
||||
const decryptedResource = await decryptResource(resource, actor.orgId, kmsService);
|
||||
|
||||
// Mark session as started
|
||||
await pamSessionDAL.updateById(sessionId, {
|
||||
status: PamSessionStatus.Active,
|
||||
startedAt: new Date()
|
||||
});
|
||||
|
||||
return {
|
||||
credentials: {
|
||||
...decryptedResource.connectionDetails,
|
||||
...decryptedAccount.credentials
|
||||
},
|
||||
projectId: project.id
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
getById,
|
||||
create,
|
||||
updateById,
|
||||
deleteById,
|
||||
list,
|
||||
listResourceOptions,
|
||||
createAccount,
|
||||
updateAccountById,
|
||||
deleteAccountById,
|
||||
listAccounts,
|
||||
accessAccount,
|
||||
getSessionCredentials
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user