Merge branch 'main' into revert-3901-revert-3875-ENG-3009-test

This commit is contained in:
x032205
2025-07-03 02:54:28 -04:00
670 changed files with 11618 additions and 13525 deletions
@@ -27,7 +27,8 @@ export enum AppConnection {
Render = "render",
Flyio = "flyio",
GitLab = "gitlab",
Cloudflare = "cloudflare"
Cloudflare = "cloudflare",
Zabbix = "zabbix"
}
export enum AWSRegion {
@@ -105,6 +105,7 @@ import {
validateWindmillConnectionCredentials,
WindmillConnectionMethod
} from "./windmill";
import { getZabbixConnectionListItem, validateZabbixConnectionCredentials, ZabbixConnectionMethod } from "./zabbix";
export const listAppConnectionOptions = () => {
return [
@@ -136,7 +137,8 @@ export const listAppConnectionOptions = () => {
getRenderConnectionListItem(),
getFlyioConnectionListItem(),
getGitLabConnectionListItem(),
getCloudflareConnectionListItem()
getCloudflareConnectionListItem(),
getZabbixConnectionListItem()
].sort((a, b) => a.name.localeCompare(b.name));
};
@@ -216,7 +218,8 @@ export const validateAppConnectionCredentials = async (
[AppConnection.Render]: validateRenderConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Flyio]: validateFlyioConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.GitLab]: validateGitLabConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator
[AppConnection.Cloudflare]: validateCloudflareConnectionCredentials as TAppConnectionCredentialsValidator,
[AppConnection.Zabbix]: validateZabbixConnectionCredentials as TAppConnectionCredentialsValidator
};
return VALIDATE_APP_CONNECTION_CREDENTIALS_MAP[appConnection.app](appConnection);
@@ -253,6 +256,7 @@ export const getAppConnectionMethodName = (method: TAppConnection["method"]) =>
case VercelConnectionMethod.ApiToken:
case OnePassConnectionMethod.ApiToken:
case CloudflareConnectionMethod.APIToken:
case ZabbixConnectionMethod.ApiToken:
return "API Token";
case PostgresConnectionMethod.UsernameAndPassword:
case MsSqlConnectionMethod.UsernameAndPassword:
@@ -332,7 +336,8 @@ export const TRANSITION_CONNECTION_CREDENTIALS_TO_PLATFORM: Record<
[AppConnection.Render]: platformManagedCredentialsNotSupported,
[AppConnection.Flyio]: platformManagedCredentialsNotSupported,
[AppConnection.GitLab]: platformManagedCredentialsNotSupported,
[AppConnection.Cloudflare]: platformManagedCredentialsNotSupported
[AppConnection.Cloudflare]: platformManagedCredentialsNotSupported,
[AppConnection.Zabbix]: platformManagedCredentialsNotSupported
};
export const enterpriseAppCheck = async (
@@ -29,7 +29,8 @@ export const APP_CONNECTION_NAME_MAP: Record<AppConnection, string> = {
[AppConnection.Render]: "Render",
[AppConnection.Flyio]: "Fly.io",
[AppConnection.GitLab]: "GitLab",
[AppConnection.Cloudflare]: "Cloudflare"
[AppConnection.Cloudflare]: "Cloudflare",
[AppConnection.Zabbix]: "Zabbix"
};
export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanType> = {
@@ -61,5 +62,6 @@ export const APP_CONNECTION_PLAN_MAP: Record<AppConnection, AppConnectionPlanTyp
[AppConnection.Render]: AppConnectionPlanType.Regular,
[AppConnection.Flyio]: AppConnectionPlanType.Regular,
[AppConnection.GitLab]: AppConnectionPlanType.Regular,
[AppConnection.Cloudflare]: AppConnectionPlanType.Regular
[AppConnection.Cloudflare]: AppConnectionPlanType.Regular,
[AppConnection.Zabbix]: AppConnectionPlanType.Regular
};
@@ -80,6 +80,8 @@ import { ValidateVercelConnectionCredentialsSchema } from "./vercel";
import { vercelConnectionService } from "./vercel/vercel-connection-service";
import { ValidateWindmillConnectionCredentialsSchema } from "./windmill";
import { windmillConnectionService } from "./windmill/windmill-connection-service";
import { ValidateZabbixConnectionCredentialsSchema } from "./zabbix";
import { zabbixConnectionService } from "./zabbix/zabbix-connection-service";
export type TAppConnectionServiceFactoryDep = {
appConnectionDAL: TAppConnectionDALFactory;
@@ -119,7 +121,8 @@ const VALIDATE_APP_CONNECTION_CREDENTIALS_MAP: Record<AppConnection, TValidateAp
[AppConnection.Render]: ValidateRenderConnectionCredentialsSchema,
[AppConnection.Flyio]: ValidateFlyioConnectionCredentialsSchema,
[AppConnection.GitLab]: ValidateGitLabConnectionCredentialsSchema,
[AppConnection.Cloudflare]: ValidateCloudflareConnectionCredentialsSchema
[AppConnection.Cloudflare]: ValidateCloudflareConnectionCredentialsSchema,
[AppConnection.Zabbix]: ValidateZabbixConnectionCredentialsSchema
};
export const appConnectionServiceFactory = ({
@@ -529,6 +532,7 @@ export const appConnectionServiceFactory = ({
render: renderConnectionService(connectAppConnectionById),
flyio: flyioConnectionService(connectAppConnectionById),
gitlab: gitlabConnectionService(connectAppConnectionById, appConnectionDAL, kmsService),
cloudflare: cloudflareConnectionService(connectAppConnectionById)
cloudflare: cloudflareConnectionService(connectAppConnectionById),
zabbix: zabbixConnectionService(connectAppConnectionById)
};
};
@@ -165,6 +165,12 @@ import {
TWindmillConnectionConfig,
TWindmillConnectionInput
} from "./windmill";
import {
TValidateZabbixConnectionCredentialsSchema,
TZabbixConnection,
TZabbixConnectionConfig,
TZabbixConnectionInput
} from "./zabbix";
export type TAppConnection = { id: string } & (
| TAwsConnection
@@ -196,6 +202,7 @@ export type TAppConnection = { id: string } & (
| TFlyioConnection
| TGitLabConnection
| TCloudflareConnection
| TZabbixConnection
);
export type TAppConnectionRaw = NonNullable<Awaited<ReturnType<TAppConnectionDALFactory["findById"]>>>;
@@ -232,6 +239,7 @@ export type TAppConnectionInput = { id: string } & (
| TFlyioConnectionInput
| TGitLabConnectionInput
| TCloudflareConnectionInput
| TZabbixConnectionInput
);
export type TSqlConnectionInput =
@@ -275,7 +283,8 @@ export type TAppConnectionConfig =
| TRenderConnectionConfig
| TFlyioConnectionConfig
| TGitLabConnectionConfig
| TCloudflareConnectionConfig;
| TCloudflareConnectionConfig
| TZabbixConnectionConfig;
export type TValidateAppConnectionCredentialsSchema =
| TValidateAwsConnectionCredentialsSchema
@@ -306,7 +315,8 @@ export type TValidateAppConnectionCredentialsSchema =
| TValidateRenderConnectionCredentialsSchema
| TValidateFlyioConnectionCredentialsSchema
| TValidateGitLabConnectionCredentialsSchema
| TValidateCloudflareConnectionCredentialsSchema;
| TValidateCloudflareConnectionCredentialsSchema
| TValidateZabbixConnectionCredentialsSchema;
export type TListAwsConnectionKmsKeys = {
connectionId: string;
@@ -0,0 +1,4 @@
export * from "./zabbix-connection-enums";
export * from "./zabbix-connection-fns";
export * from "./zabbix-connection-schemas";
export * from "./zabbix-connection-types";
@@ -0,0 +1,3 @@
export enum ZabbixConnectionMethod {
ApiToken = "api-token"
}
@@ -0,0 +1,108 @@
import { AxiosError } from "axios";
import RE2 from "re2";
import { request } from "@app/lib/config/request";
import { BadRequestError } from "@app/lib/errors";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { ZabbixConnectionMethod } from "./zabbix-connection-enums";
import {
TZabbixConnection,
TZabbixConnectionConfig,
TZabbixHost,
TZabbixHostListResponse
} from "./zabbix-connection-types";
const TRAILING_SLASH_REGEX = new RE2("/+$");
export const getZabbixConnectionListItem = () => {
return {
name: "Zabbix" as const,
app: AppConnection.Zabbix as const,
methods: Object.values(ZabbixConnectionMethod) as [ZabbixConnectionMethod.ApiToken]
};
};
export const validateZabbixConnectionCredentials = async (config: TZabbixConnectionConfig) => {
const { apiToken, instanceUrl } = config.credentials;
await blockLocalAndPrivateIpAddresses(instanceUrl);
try {
const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`;
const payload = {
jsonrpc: "2.0",
method: "authentication.get",
params: {
output: "extend"
},
id: 1
};
const response: { data: { error?: { message: string }; result?: string } } = await request.post(apiUrl, payload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
if (response.data.error) {
throw new BadRequestError({
message: response.data.error.message
});
}
return config.credentials;
} catch (error) {
if (error instanceof AxiosError) {
throw new BadRequestError({
message: `Failed to connect to Zabbix instance: ${error.message}`
});
}
throw error;
}
};
export const listZabbixHosts = async (appConnection: TZabbixConnection): Promise<TZabbixHost[]> => {
const { apiToken, instanceUrl } = appConnection.credentials;
await blockLocalAndPrivateIpAddresses(instanceUrl);
try {
const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`;
const payload = {
jsonrpc: "2.0",
method: "host.get",
params: {
output: ["hostid", "host"],
sortfield: "host",
sortorder: "ASC"
},
id: 1
};
const response: { data: TZabbixHostListResponse } = await request.post(apiUrl, payload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
return response.data.result
? response.data.result.map((host) => ({
hostId: host.hostid,
host: host.host
}))
: [];
} catch (error: unknown) {
if (error instanceof AxiosError) {
throw new BadRequestError({
message: `Failed to validate credentials: ${error.message || "Unknown error"}`
});
}
throw new BadRequestError({
message: "Unable to validate connection: verify credentials"
});
}
};
@@ -0,0 +1,62 @@
import z from "zod";
import { AppConnections } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import {
BaseAppConnectionSchema,
GenericCreateAppConnectionFieldsSchema,
GenericUpdateAppConnectionFieldsSchema
} from "@app/services/app-connection/app-connection-schemas";
import { ZabbixConnectionMethod } from "./zabbix-connection-enums";
export const ZabbixConnectionApiTokenCredentialsSchema = z.object({
apiToken: z
.string()
.trim()
.min(1, "API Token required")
.max(1000)
.describe(AppConnections.CREDENTIALS.ZABBIX.apiToken),
instanceUrl: z.string().trim().url("Invalid Instance URL").describe(AppConnections.CREDENTIALS.ZABBIX.instanceUrl)
});
const BaseZabbixConnectionSchema = BaseAppConnectionSchema.extend({ app: z.literal(AppConnection.Zabbix) });
export const ZabbixConnectionSchema = BaseZabbixConnectionSchema.extend({
method: z.literal(ZabbixConnectionMethod.ApiToken),
credentials: ZabbixConnectionApiTokenCredentialsSchema
});
export const SanitizedZabbixConnectionSchema = z.discriminatedUnion("method", [
BaseZabbixConnectionSchema.extend({
method: z.literal(ZabbixConnectionMethod.ApiToken),
credentials: ZabbixConnectionApiTokenCredentialsSchema.pick({ instanceUrl: true })
})
]);
export const ValidateZabbixConnectionCredentialsSchema = z.discriminatedUnion("method", [
z.object({
method: z.literal(ZabbixConnectionMethod.ApiToken).describe(AppConnections.CREATE(AppConnection.Zabbix).method),
credentials: ZabbixConnectionApiTokenCredentialsSchema.describe(
AppConnections.CREATE(AppConnection.Zabbix).credentials
)
})
]);
export const CreateZabbixConnectionSchema = ValidateZabbixConnectionCredentialsSchema.and(
GenericCreateAppConnectionFieldsSchema(AppConnection.Zabbix)
);
export const UpdateZabbixConnectionSchema = z
.object({
credentials: ZabbixConnectionApiTokenCredentialsSchema.optional().describe(
AppConnections.UPDATE(AppConnection.Zabbix).credentials
)
})
.and(GenericUpdateAppConnectionFieldsSchema(AppConnection.Zabbix));
export const ZabbixConnectionListItemSchema = z.object({
name: z.literal("Zabbix"),
app: z.literal(AppConnection.Zabbix),
methods: z.nativeEnum(ZabbixConnectionMethod).array()
});
@@ -0,0 +1,30 @@
import { logger } from "@app/lib/logger";
import { OrgServiceActor } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import { listZabbixHosts } from "./zabbix-connection-fns";
import { TZabbixConnection } from "./zabbix-connection-types";
type TGetAppConnectionFunc = (
app: AppConnection,
connectionId: string,
actor: OrgServiceActor
) => Promise<TZabbixConnection>;
export const zabbixConnectionService = (getAppConnection: TGetAppConnectionFunc) => {
const listHosts = async (connectionId: string, actor: OrgServiceActor) => {
const appConnection = await getAppConnection(AppConnection.Zabbix, connectionId, actor);
try {
const hosts = await listZabbixHosts(appConnection);
return hosts;
} catch (error) {
logger.error(error, "Failed to establish connection with zabbix");
return [];
}
};
return {
listHosts
};
};
@@ -0,0 +1,33 @@
import z from "zod";
import { DiscriminativePick } from "@app/lib/types";
import { AppConnection } from "../app-connection-enums";
import {
CreateZabbixConnectionSchema,
ValidateZabbixConnectionCredentialsSchema,
ZabbixConnectionSchema
} from "./zabbix-connection-schemas";
export type TZabbixConnection = z.infer<typeof ZabbixConnectionSchema>;
export type TZabbixConnectionInput = z.infer<typeof CreateZabbixConnectionSchema> & {
app: AppConnection.Zabbix;
};
export type TValidateZabbixConnectionCredentialsSchema = typeof ValidateZabbixConnectionCredentialsSchema;
export type TZabbixConnectionConfig = DiscriminativePick<TZabbixConnectionInput, "method" | "app" | "credentials"> & {
orgId: string;
};
export type TZabbixHost = {
hostId: string;
host: string;
};
export type TZabbixHostListResponse = {
jsonrpc: string;
result: { hostid: string; host: string }[];
error?: { message: string };
};
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectType, TableName } from "@app/db/schemas";
import { TableName } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
@@ -50,10 +50,7 @@ type TCertificateAuthorityServiceFactoryDep = {
>;
externalCertificateAuthorityDAL: Pick<TExternalCertificateAuthorityDALFactory, "create" | "update">;
internalCertificateAuthorityService: TInternalCertificateAuthorityServiceFactory;
projectDAL: Pick<
TProjectDALFactory,
"findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId"
>;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
certificateDAL: Pick<TCertificateDALFactory, "create" | "transaction">;
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
@@ -98,23 +95,12 @@ export const certificateAuthorityServiceFactory = ({
{ type, projectId, name, enableDirectIssuance, configuration, status }: TCreateCertificateAuthorityDTO,
actor: OrgServiceActor
) => {
let finalProjectId: string = projectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
finalProjectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId: finalProjectId,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -126,7 +112,7 @@ export const certificateAuthorityServiceFactory = ({
const ca = await internalCertificateAuthorityService.createCa({
...(configuration as TCreateInternalCertificateAuthorityDTO["configuration"]),
isInternal: true,
projectId: finalProjectId,
projectId,
enableDirectIssuance,
name
});
@@ -142,7 +128,7 @@ export const certificateAuthorityServiceFactory = ({
type,
enableDirectIssuance: ca.enableDirectIssuance,
name: ca.name,
projectId: finalProjectId,
projectId,
status,
configuration: ca.internalCa
} as TCertificateAuthority;
@@ -151,7 +137,7 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.ACME) {
return acmeFns.createCertificateAuthority({
name,
projectId: finalProjectId,
projectId,
configuration: configuration as TCreateAcmeCertificateAuthorityDTO["configuration"],
enableDirectIssuance,
status,
@@ -181,8 +167,7 @@ export const certificateAuthorityServiceFactory = ({
actorId: actor.id,
projectId: certificateAuthority.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -225,23 +210,12 @@ export const certificateAuthorityServiceFactory = ({
{ projectId, type }: { projectId: string; type: CaType },
actor: OrgServiceActor
) => {
let finalProjectId: string = projectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
finalProjectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId: finalProjectId,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -251,7 +225,7 @@ export const certificateAuthorityServiceFactory = ({
if (type === CaType.INTERNAL) {
const cas = await certificateAuthorityDAL.findWithAssociatedCa({
[`${TableName.CertificateAuthority}.projectId` as "projectId"]: finalProjectId,
[`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId,
$notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"]
});
@@ -269,7 +243,7 @@ export const certificateAuthorityServiceFactory = ({
}
if (type === CaType.ACME) {
return acmeFns.listCertificateAuthorities({ projectId: finalProjectId });
return acmeFns.listCertificateAuthorities({ projectId });
}
throw new BadRequestError({ message: "Invalid certificate authority type" });
@@ -294,8 +268,7 @@ export const certificateAuthorityServiceFactory = ({
actorId: actor.id,
projectId: certificateAuthority.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -368,8 +341,7 @@ export const certificateAuthorityServiceFactory = ({
actorId: actor.id,
projectId: certificateAuthority.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -5,13 +5,7 @@ import slugify from "@sindresorhus/slugify";
import crypto, { KeyObject } from "crypto";
import { z } from "zod";
import {
ActionProjectType,
ProjectType,
TableName,
TCertificateAuthorities,
TCertificateTemplates
} from "@app/db/schemas";
import { TableName, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
ProjectPermissionActions,
@@ -105,10 +99,7 @@ type TInternalCertificateAuthorityServiceFactoryDep = {
certificateBodyDAL: Pick<TCertificateBodyDALFactory, "create">;
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
projectDAL: Pick<
TProjectDALFactory,
"findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId"
>;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
@@ -154,21 +145,12 @@ export const internalCertificateAuthorityServiceFactory = ({
if (!project) throw new NotFoundError({ message: `Project with slug '${dto.projectSlug}' not found` });
projectId = project.id;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor: dto.actor,
actorId: dto.actorId,
projectId,
actorAuthMethod: dto.actorAuthMethod,
actorOrgId: dto.actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: dto.actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -351,8 +333,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
@@ -376,8 +357,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId: dto.actorId,
projectId: ca.projectId,
actorAuthMethod: dto.actorAuthMethod,
actorOrgId: dto.actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: dto.actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -409,8 +389,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -435,8 +414,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -499,8 +477,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -786,8 +763,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -823,8 +799,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -904,8 +879,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1052,8 +1026,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1224,8 +1197,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1581,8 +1553,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId: dto.actorId,
projectId: ca.projectId,
actorAuthMethod: dto.actorAuthMethod,
actorOrgId: dto.actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: dto.actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1949,8 +1920,7 @@ export const internalCertificateAuthorityServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
const certificateTemplates = await certificateTemplateDAL.find({ caId });
@@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import bcrypt from "bcrypt";
import { ActionProjectType, TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas";
import { TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -76,8 +76,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -138,8 +137,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -203,8 +201,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -230,8 +227,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -272,8 +268,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -355,8 +350,7 @@ export const certificateTemplateServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -435,8 +429,7 @@ export const certificateTemplateServiceFactory = ({
actorId: dto.actorId,
projectId: certTemplate.projectId,
actorAuthMethod: dto.actorAuthMethod,
actorOrgId: dto.actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId: dto.actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -2,7 +2,6 @@ import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { createPrivateKey, createPublicKey, sign, verify } from "crypto";
import { ActionProjectType, ProjectType } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -48,10 +47,7 @@ type TCertificateServiceFactoryDep = {
certificateAuthoritySecretDAL: Pick<TCertificateAuthoritySecretDALFactory, "findOne">;
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
pkiCollectionItemDAL: Pick<TPkiCollectionItemDALFactory, "create">;
projectDAL: Pick<
TProjectDALFactory,
"findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction" | "getProjectFromSplitId"
>;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug" | "findOne" | "updateById" | "findById" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
};
@@ -83,8 +79,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: cert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -114,8 +109,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: cert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -148,8 +142,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: cert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -198,8 +191,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -247,8 +239,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: cert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -321,23 +312,14 @@ export const certificateServiceFactory = ({
const project = await projectDAL.findProjectBySlug(projectSlug, actorOrgId);
if (!project) throw new NotFoundError({ message: `Project with slug '${projectSlug}' not found` });
let projectId = project.id;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -541,8 +523,7 @@ export const certificateServiceFactory = ({
actorId,
projectId: cert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
+15 -45
View File
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionCmekActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { SigningAlgorithm } from "@app/lib/crypto/sign";
@@ -23,32 +22,23 @@ import { TKmsKeyDALFactory } from "@app/services/kms/kms-key-dal";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsKeyUsage } from "../kms/kms-types";
import { TProjectDALFactory } from "../project/project-dal";
type TCmekServiceFactoryDep = {
kmsService: TKmsServiceFactory;
kmsDAL: TKmsKeyDALFactory;
permissionService: TPermissionServiceFactory;
projectDAL: Pick<TProjectDALFactory, "getProjectFromSplitId">;
};
export type TCmekServiceFactory = ReturnType<typeof cmekServiceFactory>;
export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, projectDAL }: TCmekServiceFactoryDep) => {
const createCmek = async ({ projectId: preSplitProjectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => {
let projectId = preSplitProjectId;
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(projectId, ProjectType.KMS);
if (cmekProjectFromSplit) {
projectId = cmekProjectFromSplit.id;
}
export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService }: TCmekServiceFactoryDep) => {
const createCmek = async ({ projectId, ...dto }: TCreateCmekDTO, actor: OrgServiceActor) => {
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Create, ProjectPermissionSub.Cmek);
@@ -87,8 +77,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Edit, ProjectPermissionSub.Cmek);
@@ -124,8 +113,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Delete, ProjectPermissionSub.Cmek);
@@ -135,23 +123,13 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
return key;
};
const listCmeksByProjectId = async (
{ projectId: preSplitProjectId, ...filters }: TListCmeksByProjectIdDTO,
actor: OrgServiceActor
) => {
let projectId = preSplitProjectId;
const cmekProjectFromSplit = await projectDAL.getProjectFromSplitId(preSplitProjectId, ProjectType.KMS);
if (cmekProjectFromSplit) {
projectId = cmekProjectFromSplit.id;
}
const listCmeksByProjectId = async ({ projectId, ...filters }: TListCmeksByProjectIdDTO, actor: OrgServiceActor) => {
const { permission } = await permissionService.getProjectPermission({
actor: actor.type,
actorId: actor.id,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
@@ -173,8 +151,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
@@ -195,8 +172,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
@@ -218,8 +194,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Encrypt, ProjectPermissionSub.Cmek);
@@ -246,8 +221,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
@@ -294,8 +268,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
@@ -318,8 +291,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Sign, ProjectPermissionSub.Cmek);
@@ -353,8 +325,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Verify, ProjectPermissionSub.Cmek);
@@ -389,8 +360,7 @@ export const cmekServiceFactory = ({ kmsService, kmsDAL, permissionService, proj
actorId: actor.id,
projectId: key.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.KMS
actorOrgId: actor.orgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCmekActions.Decrypt, ProjectPermissionSub.Cmek);
@@ -4,7 +4,7 @@
import { Knex } from "knex";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { ProjectType, TSecretFolderVersions, TSecretVersionsV2 } from "@app/db/schemas";
import { TSecretFolderVersions, TSecretVersionsV2 } from "@app/db/schemas";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { ActorType } from "../auth/auth-type";
@@ -433,8 +433,7 @@ describe("folderCommitServiceFactory", () => {
mockFolderCommitDAL.findCommitsToRecreate.mockResolvedValue([]);
mockProjectDAL.findProjectByEnvId.mockResolvedValue({
id: "project-id",
name: "test-project",
type: ProjectType.SecretManager
name: "test-project"
});
// Act
@@ -2,13 +2,7 @@
import { ForbiddenError } from "@casl/ability";
import { Knex } from "knex";
import {
ActionProjectType,
TSecretFolders,
TSecretFolderVersions,
TSecretV2TagJunctionInsert,
TSecretVersionsV2
} from "@app/db/schemas";
import { TSecretFolders, TSecretFolderVersions, TSecretV2TagJunctionInsert, TSecretVersionsV2 } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionCommitsActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
@@ -223,8 +217,7 @@ export const folderCommitServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionCommitsActions.Read, ProjectPermissionSub.Commits);
@@ -2067,8 +2060,7 @@ export const folderCommitServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
import { ProjectMembershipRole, SecretKeyEncoding, TGroups } from "@app/db/schemas";
import { TListProjectGroupUsersDTO } from "@app/ee/services/group/group-types";
import {
constructPermissionErrorMessage,
@@ -79,8 +79,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Create, ProjectPermissionSub.Groups);
@@ -267,8 +266,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Edit, ProjectPermissionSub.Groups);
@@ -381,8 +379,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Delete, ProjectPermissionSub.Groups);
@@ -426,8 +423,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
@@ -454,8 +450,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
@@ -496,8 +491,7 @@ export const groupProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionGroupActions.Read, ProjectPermissionSub.Groups);
@@ -1,6 +1,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
import { ProjectMembershipRole } from "@app/db/schemas";
import {
constructPermissionErrorMessage,
validatePrivilegeChangeOperation
@@ -62,8 +62,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Create,
@@ -180,8 +179,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Edit,
@@ -291,8 +289,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Delete,
@@ -320,8 +317,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionIdentityActions.Read,
@@ -354,8 +350,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -391,8 +386,7 @@ export const identityProjectServiceFactory = ({
actorId,
projectId: membership.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -4,13 +4,7 @@ import { Octokit } from "@octokit/rest";
import { Client as OctopusClient, SpaceRepository as OctopusSpaceRepository } from "@octopusdeploy/api-client";
import AWS from "aws-sdk";
import {
ActionProjectType,
SecretEncryptionAlgo,
SecretKeyEncoding,
TIntegrationAuths,
TIntegrationAuthsInsert
} from "@app/db/schemas";
import { SecretEncryptionAlgo, SecretKeyEncoding, TIntegrationAuths, TIntegrationAuthsInsert } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
@@ -103,8 +97,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const authorizations = await integrationAuthDAL.find({ projectId });
@@ -122,8 +115,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: auth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
return permission.can(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations) ? auth : null;
@@ -146,8 +138,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
return integrationAuth;
@@ -172,8 +163,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
@@ -282,8 +272,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
@@ -417,8 +406,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
@@ -679,8 +667,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -714,8 +701,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -745,8 +731,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -787,8 +772,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -816,8 +800,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -891,8 +874,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -939,8 +921,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -974,8 +955,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1033,8 +1013,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1070,8 +1049,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1112,8 +1090,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1153,8 +1130,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1194,8 +1170,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1234,8 +1209,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1275,8 +1249,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1344,8 +1317,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1419,8 +1391,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1470,8 +1441,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1519,8 +1489,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1588,8 +1557,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1630,8 +1598,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1743,8 +1710,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
@@ -1767,8 +1733,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
@@ -1801,8 +1766,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(sourcePermission).throwUnlessCan(
@@ -1815,8 +1779,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(targetPermission).throwUnlessCan(
@@ -1849,8 +1812,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -1884,8 +1846,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1925,8 +1886,7 @@ export const integrationAuthServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
const { shouldUseSecretV2Bridge, botKey } = await projectBotService.getBotKey(integrationAuth.projectId);
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -91,8 +90,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integrationAuth.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Integrations);
@@ -167,8 +165,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integration.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
@@ -231,8 +228,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integration.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -259,8 +255,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integration.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -302,8 +297,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integration.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Integrations);
@@ -339,8 +333,7 @@ export const integrationServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -359,8 +352,7 @@ export const integrationServiceFactory = ({
actorId,
projectId: integration.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -400,7 +400,7 @@ export const buildTeamsPayload = (notification: TNotification) => {
{
type: "Action.OpenUrl",
title: "View request in Infisical",
url: `${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${payload.requestId}`
url: `${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${payload.requestId}`
}
]
};
@@ -103,8 +103,41 @@ export const orgMembershipDALFactory = (db: TDbClient) => {
}
};
const findRecentInvitedMemberships = async () => {
try {
const now = new Date();
const oneWeekAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
const oneMonthAgo = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
const threeMonthsAgo = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
const memberships = await db
.replicaNode()(TableName.OrgMembership)
.where("status", "invited")
.where((qb) => {
// lastInvitedAt is null AND createdAt is between 1 week and 3 months ago
void qb
.whereNull(`${TableName.OrgMembership}.lastInvitedAt`)
.whereBetween(`${TableName.OrgMembership}.createdAt`, [threeMonthsAgo, oneWeekAgo]);
})
.orWhere((qb) => {
// lastInvitedAt is older than 1 week ago AND createdAt is younger than 1 month ago
void qb
.where(`${TableName.OrgMembership}.lastInvitedAt`, "<", oneMonthAgo)
.where(`${TableName.OrgMembership}.createdAt`, ">", oneWeekAgo);
});
return memberships;
} catch (error) {
throw new DatabaseError({
error,
name: "Find recent invited memberships"
});
}
};
return {
...orgMembershipOrm,
findOrgMembershipById
findOrgMembershipById,
findRecentInvitedMemberships
};
};
+57 -8
View File
@@ -5,7 +5,6 @@ import jwt from "jsonwebtoken";
import { Knex } from "knex";
import {
ActionProjectType,
OrgMembershipRole,
OrgMembershipStatus,
ProjectMembershipRole,
@@ -108,7 +107,10 @@ type TOrgServiceFactoryDep = {
"findProjectMembershipsByUserId" | "delete" | "create" | "find" | "insertMany" | "transaction"
>;
projectKeyDAL: Pick<TProjectKeyDALFactory, "find" | "delete" | "insertMany" | "findLatestProjectKey" | "create">;
orgMembershipDAL: Pick<TOrgMembershipDALFactory, "findOrgMembershipById" | "findOne" | "findById">;
orgMembershipDAL: Pick<
TOrgMembershipDALFactory,
"findOrgMembershipById" | "findOne" | "findById" | "findRecentInvitedMemberships" | "updateById"
>;
incidentContactDAL: TIncidentContactsDALFactory;
samlConfigDAL: Pick<TSamlConfigDALFactory, "findOne">;
oidcConfigDAL: Pick<TOidcConfigDALFactory, "findOne">;
@@ -235,14 +237,14 @@ export const orgServiceFactory = ({
return org;
};
const findAllWorkspaces = async ({ actor, actorId, orgId, type }: TFindAllWorkspacesDTO) => {
const findAllWorkspaces = async ({ actor, actorId, orgId }: TFindAllWorkspacesDTO) => {
if (actor === ActorType.USER) {
const workspaces = await projectDAL.findUserProjects(actorId, orgId, type || "all");
const workspaces = await projectDAL.findUserProjects(actorId, orgId);
return workspaces;
}
if (actor === ActorType.IDENTITY) {
const workspaces = await projectDAL.findAllProjectsByIdentity(actorId, type);
const workspaces = await projectDAL.findAllProjectsByIdentity(actorId);
return workspaces;
}
@@ -972,8 +974,7 @@ export const orgServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(projectPermission).throwUnlessCan(
ProjectPermissionMemberActions.Create,
@@ -1424,6 +1425,53 @@ export const orgServiceFactory = ({
return incidentContact;
};
/**
* Re-send emails to users who haven't accepted an invite yet
*/
const notifyInvitedUsers = async () => {
const invitedUsers = await orgMembershipDAL.findRecentInvitedMemberships();
const appCfg = getConfig();
const orgCache: Record<string, { name: string; id: string } | undefined> = {};
await Promise.all(
invitedUsers.map(async (invitedUser) => {
let org = orgCache[invitedUser.orgId];
if (!org) {
org = await orgDAL.findById(invitedUser.orgId);
orgCache[invitedUser.orgId] = org;
}
if (!org || !invitedUser.userId) return;
const token = await tokenService.createTokenForUser({
type: TokenType.TOKEN_EMAIL_ORG_INVITATION,
userId: invitedUser.userId,
orgId: org.id
});
if (invitedUser.inviteEmail) {
await smtpService.sendMail({
template: SmtpTemplates.OrgInvite,
subjectLine: `Reminder: You have been invited to ${org.name} on Infisical`,
recipients: [invitedUser.inviteEmail],
substitutions: {
organizationName: org.name,
email: invitedUser.inviteEmail,
organizationId: org.id.toString(),
token,
callback_url: `${appCfg.SITE_URL}/signupinvite`
}
});
}
await orgMembershipDAL.updateById(invitedUser.id, {
lastInvitedAt: new Date()
});
})
);
};
return {
findOrganizationById,
findAllOrgMembers,
@@ -1447,6 +1495,7 @@ export const orgServiceFactory = ({
listProjectMembershipsByOrgMembershipId,
findOrgBySlug,
resendOrgMemberInvitation,
upgradePrivilegeSystem
upgradePrivilegeSystem,
notifyInvitedUsers
};
};
-2
View File
@@ -1,4 +1,3 @@
import { ProjectType } from "@app/db/schemas";
import { TOrgPermission } from "@app/lib/types";
import { ActorAuthMethod, ActorType, MfaMethod } from "../auth/auth-type";
@@ -60,7 +59,6 @@ export type TFindAllWorkspacesDTO = {
actorOrgId: string | undefined;
actorAuthMethod: ActorAuthMethod;
orgId: string;
type?: ProjectType;
};
export type TUpdateOrgDTO = {
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
@@ -9,7 +8,6 @@ import { TPkiCollectionDALFactory } from "@app/services/pki-collection/pki-colle
import { pkiItemTypeToNameMap } from "@app/services/pki-collection/pki-collection-types";
import { SmtpTemplates, TSmtpService } from "@app/services/smtp/smtp-service";
import { TProjectDALFactory } from "../project/project-dal";
import { TPkiAlertDALFactory } from "./pki-alert-dal";
import { TCreateAlertDTO, TDeleteAlertDTO, TGetAlertByIdDTO, TUpdateAlertDTO } from "./pki-alert-types";
@@ -21,7 +19,6 @@ type TPkiAlertServiceFactoryDep = {
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "findById">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
smtpService: Pick<TSmtpService, "sendMail">;
projectDAL: Pick<TProjectDALFactory, "getProjectFromSplitId">;
};
export type TPkiAlertServiceFactory = ReturnType<typeof pkiAlertServiceFactory>;
@@ -30,8 +27,7 @@ export const pkiAlertServiceFactory = ({
pkiAlertDAL,
pkiCollectionDAL,
permissionService,
smtpService,
projectDAL
smtpService
}: TPkiAlertServiceFactoryDep) => {
const sendPkiItemExpiryNotices = async () => {
const allAlertItems = await pkiAlertDAL.getExpiringPkiCollectionItemsForAlerting();
@@ -67,7 +63,7 @@ export const pkiAlertServiceFactory = ({
};
const createPkiAlert = async ({
projectId: preSplitProjectId,
projectId,
name,
pkiCollectionId,
alertBeforeDays,
@@ -77,22 +73,12 @@ export const pkiAlertServiceFactory = ({
actor,
actorOrgId
}: TCreateAlertDTO) => {
let projectId = preSplitProjectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.PkiAlerts);
@@ -121,8 +107,7 @@ export const pkiAlertServiceFactory = ({
actorId,
projectId: alert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiAlerts);
@@ -148,8 +133,7 @@ export const pkiAlertServiceFactory = ({
actorId,
projectId: alert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiAlerts);
@@ -181,8 +165,7 @@ export const pkiAlertServiceFactory = ({
actorId,
projectId: alert.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.PkiAlerts);
@@ -1,13 +1,12 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectType, TPkiCollectionItems } from "@app/db/schemas";
import { TPkiCollectionItems } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
import { TProjectDALFactory } from "../project/project-dal";
import { TPkiCollectionDALFactory } from "./pki-collection-dal";
import { transformPkiCollectionItem } from "./pki-collection-fns";
import { TPkiCollectionItemDALFactory } from "./pki-collection-item-dal";
@@ -31,7 +30,6 @@ type TPkiCollectionServiceFactoryDep = {
certificateAuthorityDAL: Pick<TCertificateAuthorityDALFactory, "find" | "findOne">;
certificateDAL: Pick<TCertificateDALFactory, "find">;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
projectDAL: Pick<TProjectDALFactory, "getProjectFromSplitId">;
};
export type TPkiCollectionServiceFactory = ReturnType<typeof pkiCollectionServiceFactory>;
@@ -41,34 +39,23 @@ export const pkiCollectionServiceFactory = ({
pkiCollectionItemDAL,
certificateAuthorityDAL,
certificateDAL,
permissionService,
projectDAL
permissionService
}: TPkiCollectionServiceFactoryDep) => {
const createPkiCollection = async ({
name,
description,
projectId: preSplitProjectId,
projectId,
actorId,
actorAuthMethod,
actor,
actorOrgId
}: TCreatePkiCollectionDTO) => {
let projectId = preSplitProjectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -100,8 +87,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections);
@@ -125,8 +111,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.PkiCollections);
@@ -153,8 +138,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -183,8 +167,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections);
@@ -227,8 +210,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -315,8 +297,7 @@ export const pkiCollectionServiceFactory = ({
actorId,
projectId: pkiCollection.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -2,7 +2,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { ActionProjectType } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -120,8 +119,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -183,8 +181,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -237,8 +234,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -300,8 +296,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -337,8 +332,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -393,8 +387,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -440,8 +433,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -699,8 +691,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -747,8 +738,7 @@ export const pkiSubscriberServiceFactory = ({
actorId,
projectId: subscriber.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -3,7 +3,6 @@ import { ForbiddenError, subject } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import RE2 from "re2";
import { ActionProjectType } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
@@ -119,8 +118,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -172,8 +170,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -236,8 +233,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -269,8 +265,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -295,8 +290,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
const certTemplate = await pkiTemplatesDAL.find({ projectId }, { limit, offset, count: true });
@@ -338,8 +332,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -385,8 +378,7 @@ export const pkiTemplatesServiceFactory = ({
actorId,
projectId: certTemplate.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectVersion } from "@app/db/schemas";
import { ProjectVersion } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
@@ -46,8 +46,7 @@ export const projectBotServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Integrations);
@@ -113,8 +112,7 @@ export const projectBotServiceFactory = ({
actorId,
projectId: bot.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Integrations);
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
@@ -47,8 +46,7 @@ export const projectEnvServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Environments);
@@ -136,8 +134,7 @@ export const projectEnvServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Environments);
@@ -200,8 +197,7 @@ export const projectEnvServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Environments);
@@ -256,8 +252,7 @@ export const projectEnvServiceFactory = ({
actorId,
projectId: environment.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Environments);
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionMemberActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError } from "@app/lib/errors";
@@ -37,8 +36,7 @@ export const projectKeyServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
@@ -67,8 +65,7 @@ export const projectKeyServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
const latestKey = await projectKeyDAL.findLatestProjectKey(actorId, projectId);
return latestKey;
@@ -86,8 +83,7 @@ export const projectKeyServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
return projectKeyDAL.findAllProjectUserPubKeys(projectId);
@@ -1,7 +1,7 @@
/* eslint-disable no-await-in-loop */
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
import { ProjectMembershipRole, ProjectVersion, TableName } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import {
constructPermissionErrorMessage,
@@ -90,8 +90,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
@@ -134,8 +133,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
@@ -157,8 +155,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Read, ProjectPermissionSub.Member);
@@ -184,8 +181,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Create, ProjectPermissionSub.Member);
const orgMembers = await orgDAL.findMembership({
@@ -265,8 +261,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Edit, ProjectPermissionSub.Member);
@@ -375,8 +370,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Delete, ProjectPermissionSub.Member);
@@ -418,8 +412,7 @@ export const projectMembershipServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionMemberActions.Delete, ProjectPermissionSub.Member);
@@ -11,7 +11,7 @@ import {
} from "@app/ee/services/permission/default-roles";
import { TGetPredefinedRolesDTO } from "@app/services/project-role/project-role-types";
export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetPredefinedRolesDTO) => {
export const getPredefinedRoles = ({ projectId, roleFilter }: TGetPredefinedRolesDTO) => {
return [
{
id: uuidv4(),
@@ -75,5 +75,5 @@ export const getPredefinedRoles = ({ projectId, projectType, roleFilter }: TGetP
createdAt: new Date(),
updatedAt: new Date()
}
].filter(({ slug, type }) => (type ? type === projectType : true) && (!roleFilter || roleFilter === slug));
].filter(({ slug }) => !roleFilter || roleFilter === slug);
};
@@ -2,7 +2,7 @@ import { ForbiddenError, MongoAbility, RawRuleOf } from "@casl/ability";
import { PackRule, packRules, unpackRules } from "@casl/ability/extra";
import { requestContext } from "@fastify/request-context";
import { ActionProjectType, ProjectMembershipRole, ProjectType, TableName, TProjects } from "@app/db/schemas";
import { ProjectMembershipRole, TableName, TProjects } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
ProjectPermissionActions,
@@ -71,8 +71,7 @@ export const projectRoleServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Role);
const existingRole = await projectRoleDAL.findOne({ slug: data.slug, projectId });
@@ -112,14 +111,12 @@ export const projectRoleServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
if (roleSlug !== "custom" && Object.values(ProjectMembershipRole).includes(roleSlug as ProjectMembershipRole)) {
const [predefinedRole] = getPredefinedRoles({
projectId: project.id,
projectType: project.type as ProjectType,
roleFilter: roleSlug as ProjectMembershipRole
});
@@ -142,8 +139,7 @@ export const projectRoleServiceFactory = ({
actorId,
projectId: projectRole.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Role);
@@ -173,8 +169,7 @@ export const projectRoleServiceFactory = ({
actorId,
projectId: projectRole.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Role);
@@ -215,18 +210,14 @@ export const projectRoleServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Role);
const customRoles = await projectRoleDAL.find(
{ projectId: project.id },
{ sort: [[`${TableName.ProjectRoles}.slug` as "slug", "asc"]] }
);
const roles = [
...getPredefinedRoles({ projectId: project.id, projectType: project.type as ProjectType }),
...(customRoles || [])
];
const roles = [...getPredefinedRoles({ projectId: project.id }), ...(customRoles || [])];
return roles;
};
@@ -242,8 +233,7 @@ export const projectRoleServiceFactory = ({
actorId: userId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
// just to satisfy ts
if (!("roles" in membership)) throw new BadRequestError({ message: "Service token not allowed" });
@@ -1,4 +1,4 @@
import { ProjectMembershipRole, ProjectType, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas";
import { ProjectMembershipRole, TOrgRolesUpdate, TProjectRolesInsert } from "@app/db/schemas";
import { TProjectPermission } from "@app/lib/types";
export enum ProjectRoleServiceIdentifierType {
@@ -37,6 +37,5 @@ export type TListRolesDTO = {
export type TGetPredefinedRolesDTO = {
projectId: string;
projectType: ProjectType;
roleFilter?: ProjectMembershipRole;
};
+3 -45
View File
@@ -3,7 +3,6 @@ import { Knex } from "knex";
import { TDbClient } from "@app/db";
import {
ProjectsSchema,
ProjectType,
ProjectUpgradeStatus,
ProjectVersion,
SortDirection,
@@ -22,17 +21,12 @@ export type TProjectDALFactory = ReturnType<typeof projectDALFactory>;
export const projectDALFactory = (db: TDbClient) => {
const projectOrm = ormify(db, TableName.Project);
const findIdentityProjects = async (identityId: string, orgId: string, projectType: ProjectType | "all") => {
const findIdentityProjects = async (identityId: string, orgId: string) => {
try {
const workspaces = await db(TableName.IdentityProjectMembership)
.where({ identityId })
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
if (projectType !== "all") {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
@@ -72,18 +66,13 @@ export const projectDALFactory = (db: TDbClient) => {
}
};
const findUserProjects = async (userId: string, orgId: string, projectType: ProjectType | "all") => {
const findUserProjects = async (userId: string, orgId: string) => {
try {
const workspaces = await db
.replicaNode()(TableName.ProjectMembership)
.where({ userId })
.join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
if (projectType !== "all") {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
@@ -103,11 +92,6 @@ export const projectDALFactory = (db: TDbClient) => {
.whereIn("groupId", groups)
.join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Project}.orgId`, orgId)
.andWhere((qb) => {
if (projectType !== "all") {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.whereNotIn(
`${TableName.Project}.id`,
workspaces.map(({ id }) => id)
@@ -177,17 +161,12 @@ export const projectDALFactory = (db: TDbClient) => {
}
};
const findAllProjectsByIdentity = async (identityId: string, projectType?: ProjectType) => {
const findAllProjectsByIdentity = async (identityId: string) => {
try {
const workspaces = await db
.replicaNode()(TableName.IdentityProjectMembership)
.where({ identityId })
.join(TableName.Project, `${TableName.IdentityProjectMembership}.projectId`, `${TableName.Project}.id`)
.andWhere((qb) => {
if (projectType) {
void qb.where(`${TableName.Project}.type`, projectType);
}
})
.leftJoin(TableName.Environment, `${TableName.Environment}.projectId`, `${TableName.Project}.id`)
.select(
selectAllTableCols(TableName.Project),
@@ -389,27 +368,10 @@ export const projectDALFactory = (db: TDbClient) => {
};
};
const getProjectFromSplitId = async (projectId: string, projectType: ProjectType) => {
try {
const project = await db(TableName.ProjectSplitBackfillIds)
.where({
sourceProjectId: projectId,
destinationProjectType: projectType
})
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.ProjectSplitBackfillIds}.destinationProjectId`)
.select(selectAllTableCols(TableName.Project))
.first();
return project;
} catch (error) {
throw new DatabaseError({ error, name: `Failed to find split project with id ${projectId}` });
}
};
const searchProjects = async (dto: {
orgId: string;
actor: ActorType;
actorId: string;
type?: ProjectType;
limit?: number;
offset?: number;
name?: string;
@@ -464,9 +426,6 @@ export const projectDALFactory = (db: TDbClient) => {
void query.orderBy([{ column: `${TableName.Project}.name`, order: sortDir }]);
}
if (dto.type) {
void query.where(`${TableName.Project}.type`, dto.type);
}
if (dto.name) {
void query.whereILike(`${TableName.Project}.name`, `%${dto.name}%`);
}
@@ -512,7 +471,6 @@ export const projectDALFactory = (db: TDbClient) => {
findProjectBySlug,
findProjectWithOrg,
checkProjectUpgradeStatus,
getProjectFromSplitId,
searchProjects,
findProjectByEnvId,
countOfOrgProjects
+2 -1
View File
@@ -141,7 +141,7 @@ export const bootstrapSshProject = async ({
tx
});
await projectSshConfigDAL.create(
const sshConfig = await projectSshConfigDAL.create(
{
projectId,
defaultHostSshCaId: hostSshCa.id,
@@ -149,4 +149,5 @@ export const bootstrapSshProject = async ({
},
tx
);
return sshConfig;
};
+54 -158
View File
@@ -1,14 +1,7 @@
import { ForbiddenError, subject } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import {
ActionProjectType,
ProjectMembershipRole,
ProjectType,
ProjectVersion,
TableName,
TProjectEnvironments
} from "@app/db/schemas";
import { ProjectMembershipRole, ProjectVersion, TableName, TProjectEnvironments } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns";
@@ -249,8 +242,7 @@ export const projectServiceFactory = ({
kmsKeyId,
tx: trx,
createDefaultEnvs = true,
template = InfisicalProjectTemplate.Default,
type = ProjectType.SecretManager
template = InfisicalProjectTemplate.Default
}: TCreateProjectDTO) => {
const organization = await orgDAL.findOne({ id: actorOrgId });
const { permission, membership: orgMembership } = await permissionService.getOrgPermission(
@@ -266,11 +258,7 @@ export const projectServiceFactory = ({
await tx.raw("SELECT pg_advisory_xact_lock(?)", [PgSqlLock.CreateProject(organization.id)]);
const plan = await licenseService.getPlan(organization.id);
if (
plan.workspaceLimit !== null &&
plan.workspacesUsed >= plan.workspaceLimit &&
type === ProjectType.SecretManager
) {
if (plan.workspaceLimit !== null && plan.workspacesUsed >= plan.workspaceLimit) {
// case: limit imposed on number of workspaces allowed
// case: number of workspaces used exceeds the number of workspaces allowed
throw new BadRequestError({
@@ -307,7 +295,6 @@ export const projectServiceFactory = ({
const project = await projectDAL.create(
{
name: workspaceName,
type,
description: workspaceDescription,
orgId: organization.id,
slug: projectSlug || slugify(`${workspaceName}-${alphaNumericNanoId(4)}`),
@@ -318,16 +305,14 @@ export const projectServiceFactory = ({
tx
);
if (type === ProjectType.SSH) {
await bootstrapSshProject({
projectId: project.id,
sshCertificateAuthorityDAL,
sshCertificateAuthoritySecretDAL,
kmsService,
projectSshConfigDAL,
tx
});
}
await bootstrapSshProject({
projectId: project.id,
sshCertificateAuthorityDAL,
sshCertificateAuthoritySecretDAL,
kmsService,
projectSshConfigDAL,
tx
});
// set ghost user as admin of project
const projectMembership = await projectMembershipDAL.create(
@@ -524,8 +509,7 @@ export const projectServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
@@ -583,18 +567,11 @@ export const projectServiceFactory = ({
return deletedProject;
};
const getProjects = async ({
actorId,
actor,
includeRoles,
actorAuthMethod,
actorOrgId,
type = ProjectType.SecretManager
}: TListProjectsDTO) => {
const getProjects = async ({ actorId, actor, includeRoles, actorAuthMethod, actorOrgId }: TListProjectsDTO) => {
const workspaces =
actor === ActorType.IDENTITY
? await projectDAL.findIdentityProjects(actorId, actorOrgId, type)
: await projectDAL.findUserProjects(actorId, actorOrgId, type);
? await projectDAL.findIdentityProjects(actorId, actorOrgId)
: await projectDAL.findUserProjects(actorId, actorOrgId);
if (includeRoles) {
const { permission } = await permissionService.getUserOrgPermission(
@@ -618,10 +595,7 @@ export const projectServiceFactory = ({
workspaces.map(async (workspace) => {
return {
...workspace,
roles: [
...(workspaceMappedToRoles[workspace.id] || []),
...getPredefinedRoles({ projectId: workspace.id, projectType: workspace.type as ProjectType })
]
roles: [...(workspaceMappedToRoles[workspace.id] || []), ...getPredefinedRoles({ projectId: workspace.id })]
};
})
);
@@ -640,8 +614,7 @@ export const projectServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
return project;
};
@@ -654,8 +627,7 @@ export const projectServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -679,6 +651,7 @@ export const projectServiceFactory = ({
hasDeleteProtection: update.hasDeleteProtection,
slug: update.slug,
secretSharing: update.secretSharing,
defaultProduct: update.defaultProduct,
showSnapshotsLegacy: update.showSnapshotsLegacy
});
@@ -698,8 +671,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -724,8 +696,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -754,8 +725,7 @@ export const projectServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin))
@@ -786,8 +756,7 @@ export const projectServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin)) {
@@ -819,8 +788,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -841,8 +809,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Project);
@@ -912,8 +879,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
@@ -944,22 +910,14 @@ export const projectServiceFactory = ({
actor
}: TListProjectCasDTO) => {
const project = await projectDAL.findProjectByFilter(filter);
let projectId = project.id;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -998,22 +956,14 @@ export const projectServiceFactory = ({
actor
}: TListProjectCertsDTO) => {
const project = await projectDAL.findProjectByFilter(filter);
let projectId = project.id;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const projectId = project.id;
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1046,28 +996,18 @@ export const projectServiceFactory = ({
* Return list of (PKI) alerts configured for project
*/
const listProjectAlerts = async ({
projectId: preSplitProjectId,
projectId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: TListProjectAlertsDTO) => {
let projectId = preSplitProjectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiAlerts);
@@ -1083,27 +1023,18 @@ export const projectServiceFactory = ({
* Return list of PKI collections for project
*/
const listProjectPkiCollections = async ({
projectId: preSplitProjectId,
projectId,
actor,
actorId,
actorAuthMethod,
actorOrgId
}: TListProjectAlertsDTO) => {
let projectId = preSplitProjectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.PkiCollections);
@@ -1130,8 +1061,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
const allowedSubscribers = [];
@@ -1158,28 +1088,18 @@ export const projectServiceFactory = ({
* Return list of certificate templates for project
*/
const listProjectCertificateTemplates = async ({
projectId: preSplitProjectId,
projectId,
actorId,
actorOrgId,
actorAuthMethod,
actor
}: TListProjectCertificateTemplatesDTO) => {
let projectId = preSplitProjectId;
const certManagerProjectFromSplit = await projectDAL.getProjectFromSplitId(
projectId,
ProjectType.CertificateManager
);
if (certManagerProjectFromSplit) {
projectId = certManagerProjectFromSplit.id;
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
actorOrgId
});
const certificateTemplates = await certificateTemplateDAL.getCertTemplatesByProjectId(projectId);
@@ -1209,8 +1129,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1243,8 +1162,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
const allowedHosts = [];
@@ -1283,8 +1201,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
@@ -1311,8 +1228,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
@@ -1350,8 +1266,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1385,8 +1300,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
@@ -1413,8 +1327,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
@@ -1443,8 +1356,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Kms);
@@ -1466,8 +1378,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
if (!membership) {
@@ -1499,8 +1410,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
@@ -1539,8 +1449,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SSH
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -1623,8 +1532,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Settings);
@@ -1696,8 +1604,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -1774,8 +1681,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Settings);
@@ -1898,8 +1804,7 @@ export const projectServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Settings);
@@ -1927,15 +1832,7 @@ export const projectServiceFactory = ({
});
};
const searchProjects = async ({
name,
offset,
permission,
limit,
type,
orderBy,
orderDirection
}: TSearchProjectsDTO) => {
const searchProjects = async ({ name, offset, permission, limit, orderBy, orderDirection }: TSearchProjectsDTO) => {
// check user belong to org
await permissionService.getOrgPermission(
permission.type,
@@ -1949,7 +1846,6 @@ export const projectServiceFactory = ({
limit,
offset,
name,
type,
orgId: permission.orgId,
actor: permission.type,
actorId: permission.id,
@@ -1973,7 +1869,7 @@ export const projectServiceFactory = ({
actor: permission.type,
actorId: permission.id,
projectId,
actionProjectType: ActionProjectType.Any,
actorAuthMethod: permission.authMethod,
actorOrgId: permission.orgId
})
@@ -92,6 +92,7 @@ export type TUpdateProjectDTO = {
description?: string;
autoCapitalization?: boolean;
hasDeleteProtection?: boolean;
defaultProduct?: ProjectType;
slug?: string;
secretSharing?: boolean;
showSnapshotsLegacy?: boolean;
@@ -5,6 +5,7 @@ import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TIdentityAccessTokenDALFactory } from "../identity-access-token/identity-access-token-dal";
import { TIdentityUaClientSecretDALFactory } from "../identity-ua/identity-ua-client-secret-dal";
import { TOrgServiceFactory } from "../org/org-service";
import { TSecretDALFactory } from "../secret/secret-dal";
import { TSecretVersionDALFactory } from "../secret/secret-version-dal";
import { TSecretFolderVersionDALFactory } from "../secret-folder/secret-folder-version-dal";
@@ -24,6 +25,7 @@ type TDailyResourceCleanUpQueueServiceFactoryDep = {
secretSharingDAL: Pick<TSecretSharingDALFactory, "pruneExpiredSharedSecrets" | "pruneExpiredSecretRequests">;
serviceTokenService: Pick<TServiceTokenServiceFactory, "notifyExpiringTokens">;
queueService: TQueueServiceFactory;
orgService: TOrgServiceFactory;
};
export type TDailyResourceCleanUpQueueServiceFactory = ReturnType<typeof dailyResourceCleanUpQueueServiceFactory>;
@@ -39,12 +41,12 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
secretSharingDAL,
secretVersionV2DAL,
identityUniversalAuthClientSecretDAL,
serviceTokenService
serviceTokenService,
orgService
}: TDailyResourceCleanUpQueueServiceFactoryDep) => {
queueService.start(QueueName.DailyResourceCleanUp, async () => {
logger.info(`${QueueName.DailyResourceCleanUp}: queue task started`);
await secretDAL.pruneSecretReminders(queueService);
await auditLogDAL.pruneAuditLog();
await identityAccessTokenDAL.removeExpiredTokens();
await identityUniversalAuthClientSecretDAL.removeExpiredClientSecrets();
await secretSharingDAL.pruneExpiredSharedSecrets();
@@ -54,6 +56,8 @@ export const dailyResourceCleanUpQueueServiceFactory = ({
await secretVersionV2DAL.pruneExcessVersions();
await secretFolderVersionDAL.pruneExcessVersions();
await serviceTokenService.notifyExpiringTokens();
await orgService.notifyInvitedUsers();
await auditLogDAL.pruneAuditLog();
logger.info(`${QueueName.DailyResourceCleanUp}: queue task completed`);
});
@@ -1,4 +1,4 @@
import { ActionProjectType, ProjectMembershipRole } from "@app/db/schemas";
import { ProjectMembershipRole } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
@@ -36,8 +36,7 @@ export const secretBlindIndexServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const secretCount = await secretBlindIndexDAL.countOfSecretsWithNullSecretBlindIndex(projectId);
@@ -56,8 +55,7 @@ export const secretBlindIndexServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin)) {
throw new ForbiddenRequestError({ message: "Insufficient privileges, user must be admin" });
@@ -80,8 +78,7 @@ export const secretBlindIndexServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin)) {
throw new ForbiddenRequestError({ message: "Insufficient privileges, user must be admin" });
@@ -2,9 +2,10 @@ import { ForbiddenError, subject } from "@casl/ability";
import path from "path";
import { v4 as uuidv4, validate as uuidValidate } from "uuid";
import { ActionProjectType, TSecretFoldersInsert } from "@app/db/schemas";
import { TProjectEnvironments, TSecretFolders, TSecretFoldersInsert } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-approval-policy/secret-approval-policy-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { PgSqlLock } from "@app/keystore/keystore";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
@@ -14,6 +15,7 @@ import { buildFolderPath } from "@app/services/secret-folder/secret-folder-fns";
import { ChangeType, CommitType, TFolderCommitServiceFactory } from "../folder-commit/folder-commit-service";
import { TProjectDALFactory } from "../project/project-dal";
import { TProjectEnvDALFactory } from "../project-env/project-env-dal";
import { TSecretV2BridgeDALFactory } from "../secret-v2-bridge/secret-v2-bridge-dal";
import { TSecretFolderDALFactory } from "./secret-folder-dal";
import {
TCreateFolderDTO,
@@ -34,6 +36,8 @@ type TSecretFolderServiceFactoryDep = {
folderVersionDAL: Pick<TSecretFolderVersionDALFactory, "findLatestFolderVersions" | "create" | "insertMany" | "find">;
folderCommitService: Pick<TFolderCommitServiceFactory, "createCommit">;
projectDAL: Pick<TProjectDALFactory, "findProjectBySlug">;
secretApprovalPolicyService: Pick<TSecretApprovalPolicyServiceFactory, "getSecretApprovalPolicy">;
secretV2BridgeDAL: Pick<TSecretV2BridgeDALFactory, "findByFolderIds">;
};
export type TSecretFolderServiceFactory = ReturnType<typeof secretFolderServiceFactory>;
@@ -45,7 +49,9 @@ export const secretFolderServiceFactory = ({
projectEnvDAL,
folderVersionDAL,
folderCommitService,
projectDAL
projectDAL,
secretApprovalPolicyService,
secretV2BridgeDAL
}: TSecretFolderServiceFactoryDep) => {
const createFolder = async ({
projectId,
@@ -63,8 +69,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -245,8 +250,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
folders.forEach(({ environment, path: secretPath }) => {
@@ -377,8 +381,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -464,6 +467,106 @@ export const secretFolderServiceFactory = ({
return { folder: newFolder, old: folder };
};
const $checkFolderPolicy = async ({
projectId,
env,
parentId,
idOrName
}: {
projectId: string;
env: TProjectEnvironments;
parentId: string;
idOrName: string;
}) => {
let targetFolder = await folderDAL
.findOne({
envId: env.id,
name: idOrName,
parentId,
isReserved: false
})
.catch(() => null);
if (!targetFolder && uuidValidate(idOrName)) {
targetFolder = await folderDAL
.findOne({
envId: env.id,
id: idOrName,
parentId,
isReserved: false
})
.catch(() => null);
}
if (!targetFolder) {
throw new NotFoundError({ message: `Target folder not found` });
}
// get environment root folder (as it's needed to get all folders under it)
const rootFolder = await folderDAL.findBySecretPath(projectId, env.slug, "/");
if (!rootFolder) throw new NotFoundError({ message: `Root folder not found` });
// get all folders under environment root folder
const folderPaths = await folderDAL.findByEnvsDeep({ parentIds: [rootFolder.id] });
// create a map of folders by parent id
const normalizeKey = (key: string | null | undefined): string => key ?? "root";
const folderMap = new Map<string, (TSecretFolders & { path: string; depth: number; environment: string })[]>();
for (const folder of folderPaths) {
if (!folderMap.has(normalizeKey(folder.parentId))) {
folderMap.set(normalizeKey(folder.parentId), []);
}
folderMap.get(normalizeKey(folder.parentId))?.push(folder);
}
// Find the target folder in the folderPaths to get its full details
const targetFolderWithPath = folderPaths.find((f) => f.id === targetFolder!.id);
if (!targetFolderWithPath) {
throw new NotFoundError({ message: `Target folder path not found` });
}
// Recursively collect all folders under the target folder (descendants only)
const collectDescendants = (
id: string
): (TSecretFolders & { path: string; depth: number; environment: string })[] => {
const children = folderMap.get(normalizeKey(id)) || [];
return [...children, ...children.flatMap((child) => collectDescendants(child.id))];
};
const targetFolderDescendants = collectDescendants(targetFolder.id);
// Include the target folder itself plus all its descendants
const foldersToCheck = [targetFolderWithPath, ...targetFolderDescendants];
const folderPolicyPaths = foldersToCheck.map((folder) => ({
path: folder.path,
id: folder.id
}));
// get secrets under the given folders
const secrets = await secretV2BridgeDAL.findByFolderIds({
folderIds: folderPolicyPaths.map((p) => p.id)
});
for await (const folderPolicyPath of folderPolicyPaths) {
// eslint-disable-next-line no-continue
if (!secrets.some((s) => s.folderId === folderPolicyPath.id)) continue;
const policy = await secretApprovalPolicyService.getSecretApprovalPolicy(
projectId,
env.slug,
folderPolicyPath.path
);
// if there is a policy and there are secrets under the given folder, throw error
if (policy) {
throw new BadRequestError({
message: `You cannot delete the selected folder because it contains one or more secrets that are protected by the change policy "${policy.name}" at folder path "${folderPolicyPath.path}". Please remove the secrets at folder path "${folderPolicyPath.path}" and try again.`,
name: "DeleteFolderProtectedByPolicy"
});
}
}
};
const deleteFolder = async ({
projectId,
actor,
@@ -479,8 +582,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -498,18 +600,42 @@ export const secretFolderServiceFactory = ({
message: `Folder with path '${secretPath}' in environment with slug '${environment}' not found`
});
await $checkFolderPolicy({ projectId, env, parentId: parentFolder.id, idOrName });
let folderToDelete = await folderDAL
.findOne({
envId: env.id,
name: idOrName,
parentId: parentFolder.id,
isReserved: false
})
.catch(() => null);
if (!folderToDelete && uuidValidate(idOrName)) {
folderToDelete = await folderDAL
.findOne({
envId: env.id,
id: idOrName,
parentId: parentFolder.id,
isReserved: false
})
.catch(() => null);
}
if (!folderToDelete) {
throw new NotFoundError({ message: `Folder with ID '${idOrName}' not found` });
}
const [doc] = await folderDAL.delete(
{
envId: env.id,
[uuidValidate(idOrName) ? "id" : "name"]: idOrName,
id: folderToDelete.id,
parentId: parentFolder.id,
isReserved: false
},
tx
);
if (!doc) throw new NotFoundError({ message: `Failed to delete folder with ID '${idOrName}', not found` });
const folderVersions = await folderVersionDAL.findLatestFolderVersions([doc.id], tx);
await folderCommitService.createCommit(
@@ -562,8 +688,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
@@ -631,8 +756,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const envs = await projectEnvDAL.findBySlugs(projectId, environments);
@@ -673,8 +797,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const envs = await projectEnvDAL.findBySlugs(projectId, environments);
@@ -709,8 +832,7 @@ export const secretFolderServiceFactory = ({
actorId,
projectId: folder.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const [folderWithPath] = await folderDAL.findSecretPathByFolderIds(folder.projectId, [folder.id]);
@@ -738,8 +860,7 @@ export const secretFolderServiceFactory = ({
actorId: actor.id,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId: actor.orgId
});
const envs = await projectEnvDAL.findBySlugs(projectId, environments);
@@ -766,8 +887,7 @@ export const secretFolderServiceFactory = ({
actorId: actor.id,
projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId: actor.orgId
});
const environments = await projectEnvDAL.find({ projectId });
@@ -2,7 +2,7 @@ import path from "node:path";
import { ForbiddenError, subject } from "@casl/ability";
import { ActionProjectType, TableName } from "@app/db/schemas";
import { TableName } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import {
hasSecretReadValueOrDescribePermission,
@@ -87,8 +87,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
// check if user has permission to import into destination path
@@ -205,8 +204,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -303,8 +301,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -378,8 +375,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
// check if user has permission to import into destination path
@@ -455,8 +451,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
@@ -489,8 +484,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const filteredEnvironments = [];
for (const environment of environments) {
@@ -543,8 +537,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
@@ -593,8 +586,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId: folder.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -642,8 +634,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
@@ -678,8 +669,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
@@ -762,8 +752,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const filteredEnvironments = [];
for (const environment of environments) {
@@ -815,8 +804,7 @@ export const secretImportServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (
permission.cannot(
@@ -20,7 +20,8 @@ export enum SecretSync {
Render = "render",
Flyio = "flyio",
GitLab = "gitlab",
CloudflarePages = "cloudflare-pages"
CloudflarePages = "cloudflare-pages",
Zabbix = "zabbix"
}
export enum SecretSyncInitialSyncBehavior {
@@ -45,6 +45,7 @@ import { TEAMCITY_SYNC_LIST_OPTION, TeamCitySyncFns } from "./teamcity";
import { TERRAFORM_CLOUD_SYNC_LIST_OPTION, TerraformCloudSyncFns } from "./terraform-cloud";
import { VERCEL_SYNC_LIST_OPTION, VercelSyncFns } from "./vercel";
import { WINDMILL_SYNC_LIST_OPTION, WindmillSyncFns } from "./windmill";
import { ZABBIX_SYNC_LIST_OPTION, ZabbixSyncFns } from "./zabbix";
const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
[SecretSync.AWSParameterStore]: AWS_PARAMETER_STORE_SYNC_LIST_OPTION,
@@ -68,7 +69,8 @@ const SECRET_SYNC_LIST_OPTIONS: Record<SecretSync, TSecretSyncListItem> = {
[SecretSync.Render]: RENDER_SYNC_LIST_OPTION,
[SecretSync.Flyio]: FLYIO_SYNC_LIST_OPTION,
[SecretSync.GitLab]: GITLAB_SYNC_LIST_OPTION,
[SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION
[SecretSync.CloudflarePages]: CLOUDFLARE_PAGES_SYNC_LIST_OPTION,
[SecretSync.Zabbix]: ZABBIX_SYNC_LIST_OPTION
};
export const listSecretSyncOptions = () => {
@@ -236,6 +238,8 @@ export const SecretSyncFns = {
return GitLabSyncFns.syncSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
case SecretSync.CloudflarePages:
return CloudflarePagesSyncFns.syncSecrets(secretSync, schemaSecretMap);
case SecretSync.Zabbix:
return ZabbixSyncFns.syncSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for sync secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -328,6 +332,9 @@ export const SecretSyncFns = {
case SecretSync.CloudflarePages:
secretMap = await CloudflarePagesSyncFns.getSecrets(secretSync);
break;
case SecretSync.Zabbix:
secretMap = await ZabbixSyncFns.getSecrets(secretSync);
break;
default:
throw new Error(
`Unhandled sync destination for get secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -405,6 +412,8 @@ export const SecretSyncFns = {
return GitLabSyncFns.removeSecrets(secretSync, schemaSecretMap, { appConnectionDAL, kmsService });
case SecretSync.CloudflarePages:
return CloudflarePagesSyncFns.removeSecrets(secretSync, schemaSecretMap);
case SecretSync.Zabbix:
return ZabbixSyncFns.removeSecrets(secretSync, schemaSecretMap);
default:
throw new Error(
`Unhandled sync destination for remove secrets fns: ${(secretSync as TSecretSyncWithCredentials).destination}`
@@ -23,7 +23,8 @@ export const SECRET_SYNC_NAME_MAP: Record<SecretSync, string> = {
[SecretSync.Render]: "Render",
[SecretSync.Flyio]: "Fly.io",
[SecretSync.GitLab]: "GitLab",
[SecretSync.CloudflarePages]: "Cloudflare Pages"
[SecretSync.CloudflarePages]: "Cloudflare Pages",
[SecretSync.Zabbix]: "Zabbix"
};
export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
@@ -48,7 +49,8 @@ export const SECRET_SYNC_CONNECTION_MAP: Record<SecretSync, AppConnection> = {
[SecretSync.Render]: AppConnection.Render,
[SecretSync.Flyio]: AppConnection.Flyio,
[SecretSync.GitLab]: AppConnection.GitLab,
[SecretSync.CloudflarePages]: AppConnection.Cloudflare
[SecretSync.CloudflarePages]: AppConnection.Cloudflare,
[SecretSync.Zabbix]: AppConnection.Zabbix
};
export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
@@ -73,5 +75,6 @@ export const SECRET_SYNC_PLAN_MAP: Record<SecretSync, SecretSyncPlanType> = {
[SecretSync.Render]: SecretSyncPlanType.Regular,
[SecretSync.Flyio]: SecretSyncPlanType.Regular,
[SecretSync.GitLab]: SecretSyncPlanType.Regular,
[SecretSync.CloudflarePages]: SecretSyncPlanType.Regular
[SecretSync.CloudflarePages]: SecretSyncPlanType.Regular,
[SecretSync.Zabbix]: SecretSyncPlanType.Regular
};
@@ -868,7 +868,7 @@ export const secretSyncQueueFactory = ({
secretPath: folder?.path,
environment: environment?.name,
projectName: project.name,
syncUrl: `${appCfg.SITE_URL}/secret-manager/${projectId}/integrations/secret-syncs/${destination}/${secretSync.id}`
syncUrl: `${appCfg.SITE_URL}/projects/${projectId}/secret-manager/integrations/secret-syncs/${destination}/${secretSync.id}`
}
});
};
@@ -1,6 +1,5 @@
import { ForbiddenError, subject } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { throwIfMissingSecretReadValueOrDescribePermission } from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -75,7 +74,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId
});
@@ -111,7 +110,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId
});
@@ -154,7 +153,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -196,7 +195,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -234,7 +233,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId
});
@@ -314,7 +313,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -430,7 +429,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -507,7 +506,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -579,7 +578,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -645,7 +644,7 @@ export const secretSyncServiceFactory = ({
actorId: actor.id,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager,
projectId: secretSync.projectId
});
@@ -113,6 +113,7 @@ import {
TTerraformCloudSyncWithCredentials
} from "./terraform-cloud";
import { TVercelSync, TVercelSyncInput, TVercelSyncListItem, TVercelSyncWithCredentials } from "./vercel";
import { TZabbixSync, TZabbixSyncInput, TZabbixSyncListItem, TZabbixSyncWithCredentials } from "./zabbix";
export type TSecretSync =
| TAwsParameterStoreSync
@@ -136,7 +137,8 @@ export type TSecretSync =
| TRenderSync
| TFlyioSync
| TGitLabSync
| TCloudflarePagesSync;
| TCloudflarePagesSync
| TZabbixSync;
export type TSecretSyncWithCredentials =
| TAwsParameterStoreSyncWithCredentials
@@ -160,7 +162,8 @@ export type TSecretSyncWithCredentials =
| TRenderSyncWithCredentials
| TFlyioSyncWithCredentials
| TGitLabSyncWithCredentials
| TCloudflarePagesSyncWithCredentials;
| TCloudflarePagesSyncWithCredentials
| TZabbixSyncWithCredentials;
export type TSecretSyncInput =
| TAwsParameterStoreSyncInput
@@ -184,7 +187,8 @@ export type TSecretSyncInput =
| TRenderSyncInput
| TFlyioSyncInput
| TGitLabSyncInput
| TCloudflarePagesSyncInput;
| TCloudflarePagesSyncInput
| TZabbixSyncInput;
export type TSecretSyncListItem =
| TAwsParameterStoreSyncListItem
@@ -208,7 +212,8 @@ export type TSecretSyncListItem =
| TRenderSyncListItem
| TFlyioSyncListItem
| TGitLabSyncListItem
| TCloudflarePagesSyncListItem;
| TCloudflarePagesSyncListItem
| TZabbixSyncListItem;
export type TSyncOptionsConfig = {
canImportSecrets: boolean;
@@ -0,0 +1,5 @@
export * from "./zabbix-sync-constants";
export * from "./zabbix-sync-enums";
export * from "./zabbix-sync-fns";
export * from "./zabbix-sync-schemas";
export * from "./zabbix-sync-types";
@@ -0,0 +1,10 @@
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import { TSecretSyncListItem } from "@app/services/secret-sync/secret-sync-types";
export const ZABBIX_SYNC_LIST_OPTION: TSecretSyncListItem = {
name: "Zabbix",
destination: SecretSync.Zabbix,
connection: AppConnection.Zabbix,
canImportSecrets: true
};
@@ -0,0 +1,4 @@
export enum ZabbixSyncScope {
Global = "global",
Host = "host"
}
@@ -0,0 +1,285 @@
import RE2 from "re2";
import { request } from "@app/lib/config/request";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
import { SecretSyncError } from "@app/services/secret-sync/secret-sync-errors";
import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns";
import { TSecretMap } from "@app/services/secret-sync/secret-sync-types";
import {
TZabbixSecret,
TZabbixSyncWithCredentials,
ZabbixApiResponse,
ZabbixMacroCreateResponse,
ZabbixMacroDeleteResponse
} from "@app/services/secret-sync/zabbix/zabbix-sync-types";
import { ZabbixSyncScope } from "./zabbix-sync-enums";
const TRAILING_SLASH_REGEX = new RE2("/+$");
const MACRO_START_REGEX = new RE2("^\\{\\$");
const MACRO_END_REGEX = new RE2("\\}$");
const extractMacroKey = (macro: string): string => {
return macro.replace(MACRO_START_REGEX, "").replace(MACRO_END_REGEX, "");
};
// Helper function to handle Zabbix API responses and errors
const handleZabbixResponse = <T>(response: ZabbixApiResponse<T>): T => {
if (response.data.error) {
const errorMessage = response.data.error.data
? `${response.data.error.message}: ${response.data.error.data}`
: response.data.error.message;
throw new SecretSyncError({
error: new Error(`Zabbix API Error (${response.data.error.code}): ${errorMessage}`)
});
}
if (response.data.result === undefined) {
throw new SecretSyncError({
error: new Error("Zabbix API returned no result")
});
}
return response.data.result;
};
const listZabbixSecrets = async (apiToken: string, instanceUrl: string, hostId?: string): Promise<TZabbixSecret[]> => {
const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`;
// - jsonrpc: Specifies the JSON-RPC protocol version.
// - method: The API method to call, in this case "usermacro.get" for retrieving user macros.
// - id: A unique identifier for the request. Required by JSON-RPC but not used by the API for logic. Typically set to any integer.
const payload = {
jsonrpc: "2.0" as const,
method: "usermacro.get",
params: hostId ? { output: "extend", hostids: hostId } : { output: "extend", globalmacro: true },
id: 1
};
try {
const response: ZabbixApiResponse<TZabbixSecret[]> = await request.post(apiUrl, payload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
return handleZabbixResponse(response) || [];
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to list Zabbix secrets")
});
}
};
const putZabbixSecrets = async (
apiToken: string,
instanceUrl: string,
secretMap: TSecretMap,
destinationConfig: TZabbixSyncWithCredentials["destinationConfig"],
existingSecrets: TZabbixSecret[]
): Promise<void> => {
const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`;
const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined;
const existingMacroMap = new Map(existingSecrets.map((secret) => [secret.macro, secret]));
for (const [key, secret] of Object.entries(secretMap)) {
const macroKey = `{$${key.toUpperCase()}}`;
const existingMacro = existingMacroMap.get(macroKey);
try {
if (existingMacro) {
// Update existing macro
const updatePayload = {
jsonrpc: "2.0" as const,
method: hostId ? "usermacro.update" : "usermacro.updateglobal",
params: {
[hostId ? "hostmacroid" : "globalmacroid"]: existingMacro[hostId ? "hostmacroid" : "globalmacroid"],
value: secret.value,
type: destinationConfig.macroType,
description: secret.comment
},
id: 1
};
// eslint-disable-next-line no-await-in-loop
const response: ZabbixApiResponse<ZabbixMacroCreateResponse> = await request.post(apiUrl, updatePayload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
handleZabbixResponse(response);
} else {
// Create new macro
const createPayload = {
jsonrpc: "2.0" as const,
method: hostId ? "usermacro.create" : "usermacro.createglobal",
params: hostId
? {
hostid: hostId,
macro: macroKey,
value: secret.value,
type: destinationConfig.macroType,
description: secret.comment
}
: {
macro: macroKey,
value: secret.value,
type: destinationConfig.macroType,
description: secret.comment
},
id: 1
};
// eslint-disable-next-line no-await-in-loop
const response: ZabbixApiResponse<ZabbixMacroCreateResponse> = await request.post(apiUrl, createPayload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
handleZabbixResponse(response);
}
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error(`Failed to sync secret ${key}`)
});
}
}
};
const deleteZabbixSecrets = async (
apiToken: string,
instanceUrl: string,
keys: string[],
hostId?: string
): Promise<void> => {
if (keys.length === 0) return;
const apiUrl = `${instanceUrl.replace(TRAILING_SLASH_REGEX, "")}/api_jsonrpc.php`;
try {
// Get existing macros to find their IDs
const existingSecrets = await listZabbixSecrets(apiToken, instanceUrl, hostId);
const macroIds = existingSecrets
.filter((secret) => keys.includes(secret.macro))
.map((secret) => secret[hostId ? "hostmacroid" : "globalmacroid"])
.filter(Boolean);
if (macroIds.length === 0) return;
const payload = {
jsonrpc: "2.0" as const,
method: hostId ? "usermacro.delete" : "usermacro.deleteglobal",
params: macroIds,
id: 1
};
const response: ZabbixApiResponse<ZabbixMacroDeleteResponse> = await request.post(apiUrl, payload, {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiToken}`
}
});
handleZabbixResponse(response);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to delete Zabbix secrets")
});
}
};
export const ZabbixSyncFns = {
syncSecrets: async (secretSync: TZabbixSyncWithCredentials, secretMap: TSecretMap) => {
const { connection, environment, destinationConfig } = secretSync;
const { apiToken, instanceUrl } = connection.credentials;
await blockLocalAndPrivateIpAddresses(instanceUrl);
const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined;
let secrets: TZabbixSecret[] = [];
try {
secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to list Zabbix secrets")
});
}
try {
await putZabbixSecrets(apiToken, instanceUrl, secretMap, destinationConfig, secrets);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to sync secrets")
});
}
if (secretSync.syncOptions.disableSecretDeletion) return;
try {
const shapedSecretMapKeys = Object.keys(secretMap).map((key) => key.toUpperCase());
const keys = secrets
.filter(
(secret) =>
matchesSchema(secret.macro, environment?.slug || "", secretSync.syncOptions.keySchema) &&
!shapedSecretMapKeys.includes(extractMacroKey(secret.macro))
)
.map((secret) => secret.macro);
await deleteZabbixSecrets(apiToken, instanceUrl, keys, hostId);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to delete orphaned secrets")
});
}
},
removeSecrets: async (secretSync: TZabbixSyncWithCredentials, secretMap: TSecretMap) => {
const { connection, destinationConfig } = secretSync;
const { apiToken, instanceUrl } = connection.credentials;
await blockLocalAndPrivateIpAddresses(instanceUrl);
const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined;
try {
const secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId);
const shapedSecretMapKeys = Object.keys(secretMap).map((key) => key.toUpperCase());
const keys = secrets
.filter((secret) => shapedSecretMapKeys.includes(extractMacroKey(secret.macro)))
.map((secret) => secret.macro);
await deleteZabbixSecrets(apiToken, instanceUrl, keys, hostId);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to remove secrets")
});
}
},
getSecrets: async (secretSync: TZabbixSyncWithCredentials) => {
const { connection, destinationConfig } = secretSync;
const { apiToken, instanceUrl } = connection.credentials;
await blockLocalAndPrivateIpAddresses(instanceUrl);
const hostId = destinationConfig.scope === ZabbixSyncScope.Host ? destinationConfig.hostId : undefined;
try {
const secrets = await listZabbixSecrets(apiToken, instanceUrl, hostId);
return Object.fromEntries(
secrets.map((secret) => [
extractMacroKey(secret.macro),
{ value: secret.value ?? "", comment: secret.description }
])
);
} catch (error) {
throw new SecretSyncError({
error: error instanceof Error ? error : new Error("Failed to get secrets")
});
}
}
};
@@ -0,0 +1,67 @@
import { z } from "zod";
import { SecretSyncs } from "@app/lib/api-docs";
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
import { SecretSync } from "@app/services/secret-sync/secret-sync-enums";
import {
BaseSecretSyncSchema,
GenericCreateSecretSyncFieldsSchema,
GenericUpdateSecretSyncFieldsSchema
} from "@app/services/secret-sync/secret-sync-schemas";
import { TSyncOptionsConfig } from "@app/services/secret-sync/secret-sync-types";
import { ZabbixSyncScope } from "./zabbix-sync-enums";
const ZabbixSyncDestinationConfigSchema = z.discriminatedUnion("scope", [
z.object({
scope: z.literal(ZabbixSyncScope.Host).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.scope),
hostId: z.string().trim().min(1, "Host required").max(255).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.hostId),
hostName: z
.string()
.trim()
.min(1, "Host name required")
.max(255)
.describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.hostName),
macroType: z
.number()
.min(0, "Macro type required")
.max(1, "Macro type required")
.describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.macroType)
}),
z.object({
scope: z.literal(ZabbixSyncScope.Global).describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.scope),
macroType: z
.number()
.min(0, "Macro type required")
.max(1, "Macro type required")
.describe(SecretSyncs.DESTINATION_CONFIG.ZABBIX.macroType)
})
]);
const ZabbixSyncOptionsConfig: TSyncOptionsConfig = { canImportSecrets: true };
export const ZabbixSyncSchema = BaseSecretSyncSchema(SecretSync.Zabbix, ZabbixSyncOptionsConfig).extend({
destination: z.literal(SecretSync.Zabbix),
destinationConfig: ZabbixSyncDestinationConfigSchema
});
export const CreateZabbixSyncSchema = GenericCreateSecretSyncFieldsSchema(
SecretSync.Zabbix,
ZabbixSyncOptionsConfig
).extend({
destinationConfig: ZabbixSyncDestinationConfigSchema
});
export const UpdateZabbixSyncSchema = GenericUpdateSecretSyncFieldsSchema(
SecretSync.Zabbix,
ZabbixSyncOptionsConfig
).extend({
destinationConfig: ZabbixSyncDestinationConfigSchema.optional()
});
export const ZabbixSyncListItemSchema = z.object({
name: z.literal("Zabbix"),
connection: z.literal(AppConnection.Zabbix),
destination: z.literal(SecretSync.Zabbix),
canImportSecrets: z.literal(true)
});
@@ -0,0 +1,75 @@
import { z } from "zod";
import { TZabbixConnection } from "@app/services/app-connection/zabbix";
import { CreateZabbixSyncSchema, ZabbixSyncListItemSchema, ZabbixSyncSchema } from "./zabbix-sync-schemas";
export type TZabbixSync = z.infer<typeof ZabbixSyncSchema>;
export type TZabbixSyncInput = z.infer<typeof CreateZabbixSyncSchema>;
export type TZabbixSyncListItem = z.infer<typeof ZabbixSyncListItemSchema>;
export type TZabbixSyncWithCredentials = TZabbixSync & {
connection: TZabbixConnection;
};
export type TZabbixSecret = {
macro: string;
value: string;
description?: string;
globalmacroid?: string;
hostmacroid?: string;
hostid?: string;
type: number;
automatic?: string;
};
export interface ZabbixApiResponse<T = unknown> {
data: {
jsonrpc: "2.0";
result?: T;
error?: {
code: number;
message: string;
data?: string;
};
id: number;
};
}
export interface ZabbixMacroCreateResponse {
hostmacroids?: string[];
globalmacroids?: string[];
}
export interface ZabbixMacroUpdateResponse {
hostmacroids?: string[];
globalmacroids?: string[];
}
export interface ZabbixMacroDeleteResponse {
hostmacroids?: string[];
globalmacroids?: string[];
}
export enum ZabbixMacroType {
TEXT = 0,
SECRET = 1
}
export interface ZabbixMacroInput {
hostid?: string;
macro: string;
value: string;
description?: string;
type?: ZabbixMacroType;
automatic?: "0" | "1";
}
export interface ZabbixMacroUpdate {
hostmacroid?: string;
globalmacroid?: string;
value?: string;
description?: string;
type?: ZabbixMacroType;
automatic?: "0" | "1";
}
@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
@@ -29,8 +28,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Tags);
@@ -61,8 +59,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId: tag.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Tags);
@@ -79,8 +76,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId: tag.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Tags);
@@ -97,8 +93,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId: tag.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
@@ -114,8 +109,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId: tag.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
@@ -128,8 +122,7 @@ export const secretTagServiceFactory = ({ secretTagDAL, permissionService }: TSe
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
@@ -2,14 +2,7 @@ import { ForbiddenError, MongoAbility, subject } from "@casl/ability";
import { Knex } from "knex";
import { z } from "zod";
import {
ActionProjectType,
ProjectMembershipRole,
SecretsV2Schema,
SecretType,
TableName,
TSecretsV2
} from "@app/db/schemas";
import { ProjectMembershipRole, SecretsV2Schema, SecretType, TableName, TSecretsV2 } from "@app/db/schemas";
import {
hasSecretReadValueOrDescribePermission,
throwIfMissingSecretReadValueOrDescribePermission
@@ -243,8 +236,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
@@ -387,8 +379,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (inputSecret.newSecretName === "") {
@@ -615,8 +606,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
@@ -752,8 +742,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
}
@@ -798,8 +787,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
@@ -898,8 +886,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!isInternal) {
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
@@ -952,8 +939,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret);
@@ -1221,8 +1207,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId: secret.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, {
@@ -1285,8 +1270,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const folder = await folderDAL.findBySecretPath(projectId, environment, path);
@@ -1497,8 +1481,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
@@ -1665,8 +1648,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const secretsToUpdateGroupByPath = groupBy(inputSecrets, (el) => el.secretPath || defaultSecretPath);
@@ -2016,8 +1998,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const folder = await folderDAL.findBySecretPath(projectId, environment, secretPath);
@@ -2173,8 +2154,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId: folder.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const canRead =
@@ -2239,8 +2219,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin))
@@ -2287,8 +2266,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const sourceFolder = await folderDAL.findBySecretPath(projectId, sourceEnvironment, sourceSecretPath);
@@ -2674,8 +2652,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret, {
@@ -2767,8 +2744,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.DescribeSecret, {
environment,
@@ -2892,8 +2868,7 @@ export const secretV2BridgeServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const canRead =
+1 -3
View File
@@ -3,7 +3,6 @@ import path from "path";
import RE2 from "re2";
import {
ActionProjectType,
SecretEncryptionAlgo,
SecretKeyEncoding,
SecretType,
@@ -185,8 +184,7 @@ export const recursivelyGetSecretPaths = ({
actorId: auth.actorId,
projectId,
actorAuthMethod: auth.actorAuthMethod,
actorOrgId: auth.actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId: auth.actorOrgId
});
// Filter out paths that the user does not have permission to access, and paths that are not in the current path
+1 -1
View File
@@ -744,7 +744,7 @@ export const secretQueueFactory = ({
environment: jobPayload.environmentName,
count: jobPayload.count,
projectName: project.name,
integrationUrl: `${appCfg.SITE_URL}/secret-manager/${project.id}/integrations?selectedTab=native-integrations`
integrationUrl: `${appCfg.SITE_URL}/projects/${project.id}/secret-manager/integrations?selectedTab=native-integrations`
}
});
}
+15 -31
View File
@@ -3,7 +3,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import {
ActionProjectType,
ProjectMembershipRole,
ProjectUpgradeStatus,
ProjectVersion,
@@ -212,8 +211,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -330,8 +328,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -491,8 +488,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -607,8 +603,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
let paths: { folderId: string; path: string }[] = [];
@@ -713,8 +708,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
throwIfMissingSecretReadValueOrDescribePermission(permission, ProjectPermissionSecretActions.ReadValue, {
environment,
@@ -819,8 +813,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionSecretActions.Create,
@@ -906,8 +899,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -1029,8 +1021,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionSecretActions.Delete,
@@ -2477,8 +2468,7 @@ export const secretServiceFactory = ({
actorId,
projectId: folder.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SecretRollback);
const secretVersions = await secretVersionDAL.findBySecretId(secretId, {
@@ -2568,8 +2558,7 @@ export const secretServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -2674,8 +2663,7 @@ export const secretServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(
@@ -2781,8 +2769,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin))
@@ -2866,8 +2853,7 @@ export const secretServiceFactory = ({
actorId,
projectId: project.id,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
const { botKey } = await projectBotService.getBotKey(project.id);
@@ -3270,8 +3256,7 @@ export const secretServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
if (!hasRole(ProjectMembershipRole.Admin))
@@ -3299,8 +3284,7 @@ export const secretServiceFactory = ({
actorId: actor.id,
projectId: params.projectId,
actorAuthMethod: actor.authMethod,
actorOrgId: actor.orgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId: actor.orgId
});
const secrets = secretV2BridgeService.getSecretsByFolderMappings({ ...params, userId: actor.id }, permission);
@@ -3,7 +3,6 @@ import crypto from "node:crypto";
import { ForbiddenError, subject } from "@casl/ability";
import bcrypt from "bcrypt";
import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
ProjectPermissionActions,
@@ -68,8 +67,7 @@ export const serviceTokenServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.ServiceTokens);
@@ -124,8 +122,7 @@ export const serviceTokenServiceFactory = ({
actorId,
projectId: serviceToken.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.SecretManager
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.ServiceTokens);
@@ -159,8 +156,7 @@ export const serviceTokenServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.ServiceTokens);
@@ -216,7 +212,7 @@ export const serviceTokenServiceFactory = ({
substitutions: {
tokenName: token.name,
projectName: token.projectName,
url: `${appCfg.SITE_URL}/secret-manager/${token.projectId}/access-management?selectedTab=service-tokens`
url: `${appCfg.SITE_URL}/projects/${token.projectId}/secret-manager/access-management?selectedTab=service-tokens`
}
});
await serviceTokenDAL.update({ id: token.id }, { expiryNotificationSent: true });
+1 -1
View File
@@ -50,7 +50,7 @@ const buildSlackPayload = (notification: TNotification) => {
*Secret path*: ${payload.secretPath || "/"}
*Secret Key${payload.secretKeys.length > 1 ? "s" : ""}*: ${payload.secretKeys.join(", ")}
View the complete details <${appCfg.SITE_URL}/secret-manager/${payload.projectId}/approval?requestId=${
View the complete details <${appCfg.SITE_URL}/projects/${payload.projectId}/secret-manager/approval?requestId=${
payload.requestId
}|here>.`;
@@ -1,6 +1,6 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, TWebhooksInsert } from "@app/db/schemas";
import { TWebhooksInsert } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { NotFoundError } from "@app/lib/errors";
@@ -54,8 +54,7 @@ export const webhookServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.Webhooks);
const env = await projectEnvDAL.findOne({ projectId, slug: environment });
@@ -93,8 +92,7 @@ export const webhookServiceFactory = ({
actorId,
projectId: webhook.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Webhooks);
@@ -111,8 +109,7 @@ export const webhookServiceFactory = ({
actorId,
projectId: webhook.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.Webhooks);
@@ -129,8 +126,7 @@ export const webhookServiceFactory = ({
actorId,
projectId: webhook.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
const project = await projectDAL.findById(webhook.projectId);
@@ -181,8 +177,7 @@ export const webhookServiceFactory = ({
actorId,
projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.Any
actorOrgId
});
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Webhooks);