diff --git a/backend/src/ee/services/permission/default-roles.ts b/backend/src/ee/services/permission/default-roles.ts index a12f28709..4151face4 100644 --- a/backend/src/ee/services/permission/default-roles.ts +++ b/backend/src/ee/services/permission/default-roles.ts @@ -74,7 +74,6 @@ const buildAdminPermissionRules = () => { ProjectPermissionCertificateAuthorityActions.Create, ProjectPermissionCertificateAuthorityActions.Edit, ProjectPermissionCertificateAuthorityActions.Delete, - ProjectPermissionCertificateAuthorityActions.List, ProjectPermissionCertificateAuthorityActions.Renew, ProjectPermissionCertificateAuthorityActions.SignIntermediate ], @@ -111,7 +110,7 @@ const buildAdminPermissionRules = () => { ProjectPermissionCertificateActions.Delete, ProjectPermissionCertificateActions.ReadPrivateKey, ProjectPermissionCertificateActions.Import, - ProjectPermissionCertificateActions.List + ProjectPermissionCertificateActions.Read ], ProjectPermissionSub.Certificates ); @@ -119,7 +118,6 @@ const buildAdminPermissionRules = () => { can( [ ProjectPermissionCertificateProfileActions.Read, - ProjectPermissionCertificateProfileActions.List, ProjectPermissionCertificateProfileActions.Edit, ProjectPermissionCertificateProfileActions.Create, ProjectPermissionCertificateProfileActions.Delete, @@ -487,7 +485,7 @@ const buildMemberPermissionRules = () => { ProjectPermissionCertificateActions.Create, ProjectPermissionCertificateActions.Delete, ProjectPermissionCertificateActions.Import, - ProjectPermissionCertificateActions.List + ProjectPermissionCertificateActions.Read ], ProjectPermissionSub.Certificates ); @@ -495,7 +493,6 @@ const buildMemberPermissionRules = () => { can( [ ProjectPermissionCertificateProfileActions.Read, - ProjectPermissionCertificateProfileActions.List, ProjectPermissionCertificateProfileActions.Edit, ProjectPermissionCertificateProfileActions.Create, ProjectPermissionCertificateProfileActions.Delete @@ -621,7 +618,6 @@ const buildViewerPermissionRules = () => { can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList); can(ProjectPermissionCertificateAuthorityActions.Read, ProjectPermissionSub.CertificateAuthorities); can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates); - can(ProjectPermissionCertificateActions.List, ProjectPermissionSub.Certificates); can(ProjectPermissionPkiTemplateActions.Read, ProjectPermissionSub.CertificateTemplates); can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates); diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index d9fb2ae6a..20f9c1f09 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -28,7 +28,6 @@ export enum ProjectPermissionCertificateAuthorityActions { Create = "create", Edit = "edit", Delete = "delete", - List = "list", Renew = "renew", SignIntermediate = "sign-intermediate" } @@ -38,7 +37,6 @@ export enum ProjectPermissionCertificateActions { Create = "create", Edit = "edit", Delete = "delete", - List = "list", ReadPrivateKey = "read-private-key", Import = "import" } @@ -129,7 +127,6 @@ export enum ProjectPermissionPkiSubscriberActions { export enum ProjectPermissionCertificateProfileActions { Read = "read", - List = "list", Create = "create", Edit = "edit", Delete = "delete", @@ -153,7 +150,6 @@ export enum ProjectPermissionPkiSyncActions { Create = "create", Edit = "edit", Delete = "delete", - List = "list", SyncCertificates = "sync-certificates", ImportCertificates = "import-certificates", RemoveCertificates = "remove-certificates" @@ -256,7 +252,6 @@ export enum ProjectPermissionSub { CertificateAuthorities = "certificate-authorities", Certificates = "certificates", CertificateTemplates = "certificate-templates", - CertificateTemplatesV2 = "certificate-templates-v2", SshCertificateAuthorities = "ssh-certificate-authorities", SshCertificates = "ssh-certificates", SshCertificateTemplates = "ssh-certificate-templates", @@ -453,13 +448,6 @@ export type ProjectPermissionSet = | (ForcedSubject & PkiTemplateSubjectFields) ) ] - | [ - ProjectPermissionActions, - ( - | ProjectPermissionSub.CertificateTemplatesV2 - | (ForcedSubject & CertificateTemplateV2SubjectFields) - ) - ] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificates] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates] @@ -640,6 +628,17 @@ const PkiSyncConditionSchema = z [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB] }) .partial() + ]), + subscriberName: z.union([ + z.string(), + z + .object({ + [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], + [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], + [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN], + [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB] + }) + .partial() ]) }) .partial(); @@ -756,6 +755,7 @@ const PkiTemplateConditionSchema = z z .object({ [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], + [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB], [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN] }) @@ -875,7 +875,8 @@ const CertificateConditionSchema = z .object({ [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], - [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN] + [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN], + [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB] }) .partial() ]) @@ -898,22 +899,6 @@ const CertificateProfileConditionSchema = z }) .partial(); -const CertificateTemplateV2ConditionSchema = z - .object({ - name: z.union([ - z.string(), - z - .object({ - [PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ], - [PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ], - [PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN], - [PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB] - }) - .partial() - ]) - }) - .partial(); - const GeneralPermissionSchema = [ z.object({ subject: z.literal(ProjectPermissionSub.SecretApproval).describe("The entity this permission pertains to."), @@ -1308,16 +1293,6 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [ "When specified, only matching conditions will be allowed to access given resource." ).optional() }), - z.object({ - subject: z.literal(ProjectPermissionSub.CertificateTemplatesV2).describe("The entity this permission pertains to."), - inverted: z.boolean().optional().describe("Whether rule allows or forbids."), - action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( - "Describe what action an entity can take." - ), - conditions: CertificateTemplateV2ConditionSchema.describe( - "When specified, only matching conditions will be allowed to access given resource." - ).optional() - }), ...GeneralPermissionSchema ]); diff --git a/backend/src/lib/casl/permission-filter-utils.ts b/backend/src/lib/casl/permission-filter-utils.ts new file mode 100644 index 000000000..06dabe0a0 --- /dev/null +++ b/backend/src/lib/casl/permission-filter-utils.ts @@ -0,0 +1,341 @@ +import type { MongoAbility, MongoQuery, RawRuleOf } from "@casl/ability"; +import RE2 from "re2"; + +export interface PermissionFilterConfig { + operator: string; + value: unknown; + isPattern: boolean; + isInverted?: boolean; +} + +export type PermissionFilters = Record>; + +export interface ProcessedPermissionRules { + allowRules: Array>>; + forbidRules: Array>>; +} + +interface MongoRegexFilter { + $regex: RegExp; +} + +interface MongoEqFilter { + $eq: unknown; +} + +interface MongoInFilter { + $in: unknown[]; +} + +interface MongoNeFilter { + $ne: unknown; +} + +interface MongoGlobFilter { + $glob: unknown; +} + +/** + * Builds permission filters from CASL MongoDB-style conditions + * @param conditions - MongoDB-style conditions from CASL ability + * @param isInverted - Whether this rule is inverted (forbidden) + * @returns Record of field names to arrays of filter configurations + */ +export const buildPermissionFiltersFromConditions = (conditions: MongoQuery, isInverted = false): PermissionFilters => { + const permissionFilters: PermissionFilters = {}; + + function addFilterToField(key: string, operator: string, value: unknown, isPattern: boolean) { + if (!permissionFilters[key]) { + permissionFilters[key] = []; + } + + // Convert operators for inverted/forbidden rules + let finalOperator = operator; + if (isInverted) { + switch (operator) { + case "=": + finalOperator = "!="; + break; + case "!=": + finalOperator = "="; + break; + case "LIKE": + finalOperator = "NOT LIKE"; + break; + case "NOT LIKE": + finalOperator = "LIKE"; + break; + case "IN": + finalOperator = "NOT IN"; + break; + case "NOT IN": + finalOperator = "IN"; + break; + case ">": + finalOperator = "<="; + break; + case ">=": + finalOperator = "<"; + break; + case "<": + finalOperator = ">="; + break; + case "<=": + finalOperator = ">"; + break; + case "IS NULL": + finalOperator = "IS NOT NULL"; + break; + case "IS NOT NULL": + finalOperator = "IS NULL"; + break; + // Default: keep the same operator + default: + finalOperator = operator; + break; + } + } + + permissionFilters[key].push({ operator: finalOperator, value, isPattern, isInverted }); + } + + function processCondition(key: string, value: unknown) { + if (value && typeof value === "object") { + const valueObj = value as Record; + + const operatorKeys = ["$regex", "$eq", "$in", "$glob", "$ne"]; + const presentOperators = operatorKeys.filter((op) => op in valueObj); + + if (presentOperators.length > 1) { + if ("$eq" in valueObj) { + addFilterToField(key, "=", valueObj.$eq, false); + } + if ("$glob" in valueObj) { + addFilterToField(key, "LIKE", valueObj.$glob, true); + } + if ("$regex" in valueObj) { + const regexValue = valueObj.$regex as RegExp; + const regexPattern = regexValue.source; + const globPattern = regexPattern + .replace(new RE2("^\\\\\\^"), "") + .replace(new RE2("\\\\\\$$"), "") + .replace(new RE2("\\\\\\.\\*", "g"), "*"); + addFilterToField(key, "LIKE", globPattern, true); + } + if ("$ne" in valueObj) { + const valueStr = String(valueObj.$ne); + const hasWildcards = valueStr.includes("*") || valueStr.includes("?"); + addFilterToField(key, hasWildcards ? "NOT LIKE" : "!=", valueObj.$ne, hasWildcards); + } + if ("$in" in valueObj) { + const inValues = valueObj.$in as unknown[]; + addFilterToField(key, "IN", inValues, false); + } + } else if ("$regex" in value) { + const regexFilter = value as MongoRegexFilter; + const regexPattern = regexFilter.$regex.source; + const globPattern = regexPattern + .replace(new RE2("^\\\\\\^"), "") + .replace(new RE2("\\\\\\$$"), "") + .replace(new RE2("\\\\\\.\\*", "g"), "*"); + addFilterToField(key, "LIKE", globPattern, true); + } else if ("$eq" in value) { + const eqFilter = value as MongoEqFilter; + addFilterToField(key, "=", eqFilter.$eq, false); + } else if ("$in" in value) { + const inFilter = value as MongoInFilter; + addFilterToField(key, "IN", inFilter.$in, false); + } else if ("$glob" in value) { + const globFilter = value as MongoGlobFilter; + addFilterToField(key, "LIKE", globFilter.$glob, true); + } else if ("$ne" in value) { + const neFilter = value as MongoNeFilter; + const valueStr = String(neFilter.$ne); + const hasWildcards = valueStr.includes("*") || valueStr.includes("?"); + addFilterToField(key, hasWildcards ? "NOT LIKE" : "!=", neFilter.$ne, hasWildcards); + } + } else { + addFilterToField(key, "=", value, false); + } + } + + function processConditions(mongoConditions: MongoQuery) { + if ( + mongoConditions && + typeof mongoConditions === "object" && + "$or" in mongoConditions && + Array.isArray(mongoConditions.$or) + ) { + mongoConditions.$or.forEach((orCondition: MongoQuery) => { + processConditions(orCondition); + }); + } else if (mongoConditions && typeof mongoConditions === "object") { + Object.entries(mongoConditions).forEach(([key, value]) => { + if (key.startsWith("$")) return; + processCondition(key, value); + }); + } + } + + if (conditions && typeof conditions === "object") { + processConditions(conditions); + } + + return permissionFilters; +}; + +/** + * Convert CASL condition object to MongoDB-compatible database filter + * @param condition - CASL condition object + * @returns MongoDB filter object + */ +export function convertConditionToDbFilter(condition: Record): Record { + const dbFilter: Record = {}; + + for (const key in condition) { + if (Object.prototype.hasOwnProperty.call(condition, key)) { + const value = condition[key]; + + if (value && typeof value === "object" && !Array.isArray(value)) { + const operator = Object.keys(value)[0]; + const opValue = (value as Record)[operator]; + + switch (operator) { + case "$glob": { + // Convert "*.es" glob → regex /^.*\.es$/i + const regex = new RegExp(`^${String(opValue).replace(/\*/g, ".*")}$`, "i"); + dbFilter[key] = { $regex: regex }; + break; + } + + case "$in": + dbFilter[key] = { $in: opValue }; + break; + + case "$eq": + dbFilter[key] = { $eq: opValue }; + break; + + default: + dbFilter[key] = value; + break; + } + } else { + dbFilter[key] = { $eq: value }; + } + } + } + + return dbFilter; +} + +/** + * Extract ALL conditions for a subject and action, + * converting them into DB-safe filter objects. + * @param ability - CASL MongoAbility instance + * @param action - Permission action to filter for + * @param subjectName - Permission subject to filter for + * @returns MongoDB filter object with $or conditions + */ +export function getDbFiltersForAbility( + ability: MongoAbility, + action: string, + subjectName: string +): Record { + const matchingRules = ability.rules.filter((rule: RawRuleOf) => { + const actionMatches = Array.isArray(rule.action) ? rule.action.includes(action) : rule.action === action; + const subjectMatches = Array.isArray(rule.subject) + ? rule.subject.includes(subjectName) + : rule.subject === subjectName; + return actionMatches && subjectMatches && rule.conditions; + }); + + const converted = matchingRules.map((rule: RawRuleOf) => + convertConditionToDbFilter(rule.conditions as Record) + ); + + if (converted.length === 0) { + return {}; + } + + return { $or: converted }; +} + +/** + * Extract permission filters for a subject and action, + * converting them into ProcessedPermissionRules format for use with Knex queries. + * @param ability - CASL MongoAbility instance + * @param action - Permission action to filter for + * @param subjectName - Permission subject to filter for + * @returns ProcessedPermissionRules object for use with applyPermissionFiltersToQuery + */ +export function getProcessedPermissionRules( + ability: MongoAbility, + action: string, + subjectName: string +): ProcessedPermissionRules { + const matchingRules = ability.rules.filter((rule: RawRuleOf) => { + const actionMatches = Array.isArray(rule.action) ? rule.action.includes(action) : rule.action === action; + const subjectMatches = Array.isArray(rule.subject) + ? rule.subject.includes(subjectName) + : rule.subject === subjectName; + return actionMatches && subjectMatches && rule.conditions; + }); + + const allowRules: Array>> = []; + const forbidRules: Array>> = []; + + matchingRules.forEach((rule: RawRuleOf) => { + if (rule.conditions) { + const isInverted = rule.inverted || false; + const ruleFilters = buildPermissionFiltersFromConditions(rule.conditions, isInverted); + + if (isInverted) { + forbidRules.push(ruleFilters); + } else { + allowRules.push(ruleFilters); + } + } + }); + + return { allowRules, forbidRules }; +} + +/** + * Extract permission filters for a subject and action, + * converting them into PermissionFilters format for use with Knex queries. + * This is a simplified version that maintains backward compatibility. + * @param ability - CASL MongoAbility instance + * @param action - Permission action to filter for + * @param subjectName - Permission subject to filter for + * @returns PermissionFilters object for use with applyPermissionFiltersToQuery + */ +export function getPermissionFiltersForAbility( + ability: MongoAbility, + action: string, + subjectName: string +): PermissionFilters { + const processedRules = getProcessedPermissionRules(ability, action, subjectName); + + const mergedFilters: PermissionFilters = {}; + + processedRules.allowRules.forEach((rule) => { + Object.entries(rule).forEach(([key, filterConfigs]) => { + if (!mergedFilters[key]) { + mergedFilters[key] = []; + } + mergedFilters[key].push(...filterConfigs); + }); + }); + + processedRules.forbidRules.forEach((rule) => { + Object.entries(rule).forEach(([key, filterConfigs]) => { + if (!mergedFilters[key]) { + mergedFilters[key] = []; + } + mergedFilters[key].push(...filterConfigs); + }); + }); + + return mergedFilters; +} diff --git a/backend/src/lib/knex/permission-filter-utils.ts b/backend/src/lib/knex/permission-filter-utils.ts new file mode 100644 index 000000000..3b8f448ec --- /dev/null +++ b/backend/src/lib/knex/permission-filter-utils.ts @@ -0,0 +1,281 @@ +import type { Knex } from "knex"; +import RE2 from "re2"; + +export interface PermissionFilterConfig { + operator: string; + value: unknown; + isPattern: boolean; + isInverted?: boolean; +} + +export type PermissionFilters = Record>; + +export interface ProcessedPermissionRules { + allowRules: Array>>; + forbidRules: Array>>; +} + +/** + * Applies permission filters to a Knex query for any table + * @param query - The Knex query builder instance + * @param tableName - The name of the table to apply filters to + * @param permissionFilters - Record of field names to arrays of filter configurations + * @returns The modified query builder with permission filters applied + */ +export const applyPermissionFiltersToQuery = ( + originalQuery: Knex.QueryBuilder, + tableName: string, + permissionFilters?: PermissionFilters +): Knex.QueryBuilder => { + if (!permissionFilters) { + return originalQuery; + } + + let query = originalQuery; + + Object.entries(permissionFilters).forEach(([key, filterConfigs]) => { + filterConfigs.forEach((filterConfig) => { + if (filterConfig.value !== undefined && filterConfig.value !== null) { + const { operator, value, isPattern } = filterConfig; + const fieldName = `${tableName}.${key}`; + + switch (operator) { + case "=": + query = query.andWhere(fieldName, "=", value as string | number); + break; + case "!=": + query = query.andWhere(fieldName, "!=", value as string | number); + break; + case "LIKE": { + const likePattern = isPattern ? String(value).replace(new RE2("\\*", "g"), "%") : String(value); + query = query.andWhere(fieldName, "like", likePattern); + break; + } + case "NOT LIKE": { + const notLikePattern = isPattern ? String(value).replace(new RE2("\\*", "g"), "%") : String(value); + query = query.andWhere(fieldName, "not like", notLikePattern); + break; + } + case "IN": { + const inValues = Array.isArray(value) ? value : [value]; + query = query.andWhere(fieldName, "in", inValues as (string | number)[]); + break; + } + case "NOT IN": { + const notInValues = Array.isArray(value) ? value : [value]; + query = query.andWhere(fieldName, "not in", notInValues as (string | number)[]); + break; + } + case ">": + query = query.andWhere(fieldName, ">", value as string | number); + break; + case ">=": + query = query.andWhere(fieldName, ">=", value as string | number); + break; + case "<": + query = query.andWhere(fieldName, "<", value as string | number); + break; + case "<=": + query = query.andWhere(fieldName, "<=", value as string | number); + break; + case "IS NULL": + query = query.andWhere(fieldName, "is", null); + break; + case "IS NOT NULL": + query = query.andWhere(fieldName, "is not", null); + break; + default: + query = query.andWhere(fieldName, "=", value as string | number); + break; + } + } + }); + }); + + return query; +}; + +/** + * Applies a single filter configuration to a query + * @param query - The Knex query builder instance + * @param tableName - The name of the table to apply filters to + * @param key - The field name + * @param filterConfig - The filter configuration + */ +const applySingleFilter = ( + query: Knex.QueryBuilder, + tableName: string, + key: string, + filterConfig: PermissionFilterConfig +): void => { + if (filterConfig.value !== undefined && filterConfig.value !== null) { + const { operator, value, isPattern } = filterConfig; + const fieldName = `${tableName}.${key}`; + + switch (operator) { + case "=": + void query.andWhere(fieldName, "=", value as string | number); + break; + case "!=": + void query.andWhere(fieldName, "!=", value as string | number); + break; + case "LIKE": { + const likePattern = isPattern ? String(value).replace(new RE2("\\*", "g"), "%") : String(value); + void query.andWhere(fieldName, "like", likePattern); + break; + } + case "NOT LIKE": { + const notLikePattern = isPattern ? String(value).replace(new RE2("\\*", "g"), "%") : String(value); + void query.andWhere(fieldName, "not like", notLikePattern); + break; + } + case "IN": { + const inValues = Array.isArray(value) ? value : [value]; + void query.andWhere(fieldName, "in", inValues as (string | number)[]); + break; + } + case "NOT IN": { + const notInValues = Array.isArray(value) ? value : [value]; + void query.andWhere(fieldName, "not in", notInValues as (string | number)[]); + break; + } + case ">": + void query.andWhere(fieldName, ">", value as string | number); + break; + case ">=": + void query.andWhere(fieldName, ">=", value as string | number); + break; + case "<": + void query.andWhere(fieldName, "<", value as string | number); + break; + case "<=": + void query.andWhere(fieldName, "<=", value as string | number); + break; + case "IS NULL": + void query.andWhere(fieldName, "is", null); + break; + case "IS NOT NULL": + void query.andWhere(fieldName, "is not", null); + break; + default: + void query.andWhere(fieldName, "=", value as string | number); + break; + } + } +}; + +/** + * Applies complex permission rules to a Knex query with proper OR/AND logic + * @param query - The Knex query builder instance + * @param tableName - The name of the table to apply filters to + * @param processedRules - Processed permission rules with allow and forbid rules + * @returns The modified query builder with permission rules applied + */ +export const applyProcessedPermissionRulesToQuery = ( + originalQuery: Knex.QueryBuilder, + tableName: string, + processedRules?: ProcessedPermissionRules +): Knex.QueryBuilder => { + if (!processedRules || (processedRules.allowRules.length === 0 && processedRules.forbidRules.length === 0)) { + return originalQuery; + } + + let query = originalQuery; + + if (processedRules.allowRules.length > 0) { + query = query.andWhere((allowBuilder) => { + processedRules.allowRules.forEach((rule, index) => { + const ruleBuilder = (ruleSubBuilder: Knex.QueryBuilder) => { + Object.entries(rule).forEach(([key, filterConfigs]) => { + filterConfigs.forEach((filterConfig) => { + applySingleFilter(ruleSubBuilder, tableName, key, filterConfig); + }); + }); + }; + + if (index === 0) { + void allowBuilder.where(ruleBuilder); + } else { + void allowBuilder.orWhere(ruleBuilder); + } + }); + }); + } + + if (processedRules.forbidRules.length > 0) { + processedRules.forbidRules.forEach((forbidRule) => { + query = query.andWhere((forbidBuilder) => { + let hasConditions = false; + Object.entries(forbidRule).forEach(([key, filterConfigs]) => { + filterConfigs.forEach((filterConfig) => { + const negatedConfig = { ...filterConfig }; + + switch (filterConfig.operator) { + case "=": + negatedConfig.operator = "!="; + break; + case "!=": + negatedConfig.operator = "="; + break; + case "LIKE": + negatedConfig.operator = "NOT LIKE"; + break; + case "NOT LIKE": + negatedConfig.operator = "LIKE"; + break; + case "IN": + negatedConfig.operator = "NOT IN"; + break; + case "NOT IN": + negatedConfig.operator = "IN"; + break; + case ">": + negatedConfig.operator = "<="; + break; + case ">=": + negatedConfig.operator = "<"; + break; + case "<": + negatedConfig.operator = ">="; + break; + case "<=": + negatedConfig.operator = ">"; + break; + case "IS NULL": + negatedConfig.operator = "IS NOT NULL"; + break; + case "IS NOT NULL": + negatedConfig.operator = "IS NULL"; + break; + default: + negatedConfig.operator = "!="; + break; + } + + if (hasConditions) { + void forbidBuilder.orWhere((subBuilder) => { + applySingleFilter(subBuilder, tableName, key, negatedConfig); + }); + } else { + void forbidBuilder.where((subBuilder) => { + applySingleFilter(subBuilder, tableName, key, negatedConfig); + }); + hasConditions = true; + } + }); + }); + }); + }); + } + + return query; +}; + +/** + * Sanitizes a string value for safe use in SQL LIKE queries + * @param value - The string value to sanitize + * @returns The sanitized string with SQL special characters escaped + */ +export const sanitizeForLike = (value: string): string => { + return String(value).replace(new RE2("[%_\\\\]", "g"), "\\$&"); +}; diff --git a/backend/src/services/certificate-authority/acme/acme-certificate-authority-fns.ts b/backend/src/services/certificate-authority/acme/acme-certificate-authority-fns.ts index b48b1076f..6ef479971 100644 --- a/backend/src/services/certificate-authority/acme/acme-certificate-authority-fns.ts +++ b/backend/src/services/certificate-authority/acme/acme-certificate-authority-fns.ts @@ -715,11 +715,21 @@ export const AcmeCertificateAuthorityFns = ({ return castDbEntryToAcmeCertificateAuthority(updatedCa); }; - const listCertificateAuthorities = async ({ projectId }: { projectId: string }) => { - const cas = await certificateAuthorityDAL.findWithAssociatedCa({ - [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, - [`${TableName.ExternalCertificateAuthority}.type` as "type"]: CaType.ACME - }); + const listCertificateAuthorities = async ({ + projectId, + permissionFilters + }: { + projectId: string; + permissionFilters?: import("@app/lib/knex/permission-filter-utils").PermissionFilters; + }) => { + const cas = await certificateAuthorityDAL.findWithAssociatedCa( + { + [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, + [`${TableName.ExternalCertificateAuthority}.type` as "type"]: CaType.ACME + }, + {}, + permissionFilters + ); return cas.map(castDbEntryToAcmeCertificateAuthority); }; diff --git a/backend/src/services/certificate-authority/azure-ad-cs/azure-ad-cs-certificate-authority-fns.ts b/backend/src/services/certificate-authority/azure-ad-cs/azure-ad-cs-certificate-authority-fns.ts index 421da28aa..eea877b6e 100644 --- a/backend/src/services/certificate-authority/azure-ad-cs/azure-ad-cs-certificate-authority-fns.ts +++ b/backend/src/services/certificate-authority/azure-ad-cs/azure-ad-cs-certificate-authority-fns.ts @@ -798,11 +798,21 @@ export const AzureAdCsCertificateAuthorityFns = ({ return castDbEntryToAzureAdCsCertificateAuthority(updatedCa); }; - const listCertificateAuthorities = async ({ projectId }: { projectId: string }) => { - const cas = await certificateAuthorityDAL.findWithAssociatedCa({ - [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, - [`${TableName.ExternalCertificateAuthority}.type` as "type"]: CaType.AZURE_AD_CS - }); + const listCertificateAuthorities = async ({ + projectId, + permissionFilters + }: { + projectId: string; + permissionFilters?: import("@app/lib/knex/permission-filter-utils").PermissionFilters; + }) => { + const cas = await certificateAuthorityDAL.findWithAssociatedCa( + { + [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, + [`${TableName.ExternalCertificateAuthority}.type` as "type"]: CaType.AZURE_AD_CS + }, + {}, + permissionFilters + ); return cas.map(castDbEntryToAzureAdCsCertificateAuthority); }; diff --git a/backend/src/services/certificate-authority/certificate-authority-dal.ts b/backend/src/services/certificate-authority/certificate-authority-dal.ts index 352675441..5809b04d9 100644 --- a/backend/src/services/certificate-authority/certificate-authority-dal.ts +++ b/backend/src/services/certificate-authority/certificate-authority-dal.ts @@ -4,6 +4,7 @@ import { TDbClient } from "@app/db"; import { CertificateAuthoritiesSchema, TableName, TCertificateAuthorities } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { buildFindFilter, ormify, selectAllTableCols, TFindOpt } from "@app/lib/knex"; +import { applyPermissionFiltersToQuery, type PermissionFilters } from "@app/lib/knex/permission-filter-utils"; export type TCertificateAuthorityDALFactory = ReturnType; @@ -220,10 +221,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => { const findWithAssociatedCa = async ( filter: Parameters<(typeof caOrm)["find"]>[0] & { dn?: string; type?: string; serialNumber?: string }, { offset, limit, sort = [["createdAt", "desc"]] }: TFindOpt = {}, + permissionFilters?: PermissionFilters, tx?: Knex ) => { try { - const query = (tx || db.replicaNode())(TableName.CertificateAuthority) + let query = (tx || db.replicaNode())(TableName.CertificateAuthority) .leftJoin( TableName.InternalCertificateAuthority, `${TableName.CertificateAuthority}.id`, @@ -268,6 +270,10 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => { db.ref("appConnectionId").withSchema(TableName.ExternalCertificateAuthority).as("externalAppConnectionId") ); + if (permissionFilters) { + query = applyPermissionFiltersToQuery(query, TableName.CertificateAuthority, permissionFilters) as typeof query; + } + if (limit) void query.limit(limit); if (offset) void query.offset(offset); if (sort) { diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 3c0b22bb8..4be0de84c 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -6,6 +6,7 @@ import { ProjectPermissionCertificateAuthorityActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils"; import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { OrgServiceActor } from "@app/lib/types"; @@ -332,15 +333,25 @@ export const certificateAuthorityServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionCertificateAuthorityActions.List, + ProjectPermissionCertificateAuthorityActions.Read, + ProjectPermissionSub.CertificateAuthorities + ); + + const permissionFilters = getPermissionFiltersForAbility( + permission, + ProjectPermissionCertificateAuthorityActions.Read, ProjectPermissionSub.CertificateAuthorities ); if (type === CaType.INTERNAL) { - const cas = await certificateAuthorityDAL.findWithAssociatedCa({ - [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, - $notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"] - }); + const cas = await certificateAuthorityDAL.findWithAssociatedCa( + { + [`${TableName.CertificateAuthority}.projectId` as "projectId"]: projectId, + $notNull: [`${TableName.InternalCertificateAuthority}.id` as "id"] + }, + {}, + permissionFilters + ); return cas .filter((ca): ca is typeof ca & { internalCa: NonNullable } => Boolean(ca.internalCa)) @@ -356,11 +367,11 @@ export const certificateAuthorityServiceFactory = ({ } if (type === CaType.ACME) { - return acmeFns.listCertificateAuthorities({ projectId }); + return acmeFns.listCertificateAuthorities({ projectId, permissionFilters }); } if (type === CaType.AZURE_AD_CS) { - return azureAdCsFns.listCertificateAuthorities({ projectId }); + return azureAdCsFns.listCertificateAuthorities({ projectId, permissionFilters }); } throw new BadRequestError({ message: "Invalid certificate authority type" }); diff --git a/backend/src/services/certificate-profile/certificate-profile-dal.ts b/backend/src/services/certificate-profile/certificate-profile-dal.ts index 7a6494d34..5bab780eb 100644 --- a/backend/src/services/certificate-profile/certificate-profile-dal.ts +++ b/backend/src/services/certificate-profile/certificate-profile-dal.ts @@ -4,6 +4,7 @@ import { TDbClient } from "@app/db"; import { TableName } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; +import { applyPermissionFiltersToQuery, type PermissionFilters } from "@app/lib/knex/permission-filter-utils"; import { EnrollmentType, @@ -276,6 +277,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => { issuerType?: IssuerType; caId?: string; } = {}, + permissionFilters?: PermissionFilters, tx?: Knex ): Promise => { try { @@ -308,7 +310,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => { baseQuery = baseQuery.where(`${TableName.PkiCertificateProfile}.issuerType`, issuerType); } - const query = baseQuery + let query = baseQuery .leftJoin( TableName.CertificateAuthority, `${TableName.PkiCertificateProfile}.caId`, @@ -354,6 +356,14 @@ export const certificateProfileDALFactory = (db: TDbClient) => { db.ref("id").withSchema(TableName.PkiAcmeEnrollmentConfig).as("acmeId") ); + if (permissionFilters) { + query = applyPermissionFiltersToQuery( + query, + TableName.PkiCertificateProfile, + permissionFilters + ) as typeof query; + } + const results = (await query .orderBy(`${TableName.PkiCertificateProfile}.createdAt`, "desc") .offset(offset) @@ -432,6 +442,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => { issuerType?: IssuerType; caId?: string; } = {}, + permissionFilters?: PermissionFilters, tx?: Knex ): Promise => { try { @@ -459,6 +470,14 @@ export const certificateProfileDALFactory = (db: TDbClient) => { query = query.where({ issuerType }); } + if (permissionFilters) { + query = applyPermissionFiltersToQuery( + query, + TableName.PkiCertificateProfile, + permissionFilters + ) as typeof query; + } + const result = await query.count("*").first(); return parseInt((result as unknown as { count: string }).count || "0", 10); } catch (error) { diff --git a/backend/src/services/certificate-profile/certificate-profile-service.ts b/backend/src/services/certificate-profile/certificate-profile-service.ts index aaa29a772..20da7891a 100644 --- a/backend/src/services/certificate-profile/certificate-profile-service.ts +++ b/backend/src/services/certificate-profile/certificate-profile-service.ts @@ -10,6 +10,7 @@ import { ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { buildUrl } from "@app/ee/services/pki-acme/pki-acme-fns"; +import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils"; import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate"; import { getConfig } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; @@ -745,25 +746,39 @@ export const certificateProfileServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionCertificateProfileActions.List, + ProjectPermissionCertificateProfileActions.Read, ProjectPermissionSub.CertificateProfiles ); - const profiles = await certificateProfileDAL.findByProjectId(projectId, { - offset, - limit, - search, - enrollmentType, - issuerType, - caId - }); + const permissionFilters = getPermissionFiltersForAbility( + permission, + ProjectPermissionCertificateProfileActions.Read, + ProjectPermissionSub.CertificateProfiles + ); - const totalCount = await certificateProfileDAL.countByProjectId(projectId, { - search, - enrollmentType, - issuerType, - caId - }); + const profiles = await certificateProfileDAL.findByProjectId( + projectId, + { + offset, + limit, + search, + enrollmentType, + issuerType, + caId + }, + permissionFilters + ); + + const totalCount = await certificateProfileDAL.countByProjectId( + projectId, + { + search, + enrollmentType, + issuerType, + caId + }, + permissionFilters + ); const convertedProfiles = await Promise.all( profiles.map(async (profile) => { diff --git a/backend/src/services/certificate-template-v2/certificate-template-v2-dal.ts b/backend/src/services/certificate-template-v2/certificate-template-v2-dal.ts index 3b935f26a..a2ed17a4c 100644 --- a/backend/src/services/certificate-template-v2/certificate-template-v2-dal.ts +++ b/backend/src/services/certificate-template-v2/certificate-template-v2-dal.ts @@ -5,6 +5,7 @@ import { TableName } from "@app/db/schemas"; import { TPkiCertificateTemplatesV2Insert } from "@app/db/schemas/pki-certificate-templates-v2"; import { DatabaseError } from "@app/lib/errors"; import { ormify } from "@app/lib/knex"; +import { applyPermissionFiltersToQuery, type PermissionFilters } from "@app/lib/knex/permission-filter-utils"; import { TCertificateTemplateV2, @@ -133,6 +134,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => { limit?: number; search?: string; } = {}, + permissionFilters?: PermissionFilters, tx?: Knex ) => { try { @@ -146,6 +148,14 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => { }); } + if (permissionFilters) { + query = applyPermissionFiltersToQuery( + query, + TableName.PkiCertificateTemplateV2, + permissionFilters + ) as typeof query; + } + const certificateTemplatesV2 = await query.orderBy("createdAt", "desc").offset(offset).limit(limit); return certificateTemplatesV2.map((template: Record) => parseJsonFields(template)); @@ -159,6 +169,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => { options: { search?: string; } = {}, + permissionFilters?: PermissionFilters, tx?: Knex ) => { try { @@ -172,6 +183,14 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => { }); } + if (permissionFilters) { + query = applyPermissionFiltersToQuery( + query, + TableName.PkiCertificateTemplateV2, + permissionFilters + ) as typeof query; + } + const result = await query.count("*").first(); return parseInt((result as unknown as { count: string }).count || "0", 10); } catch (error) { diff --git a/backend/src/services/certificate-template-v2/certificate-template-v2-service.ts b/backend/src/services/certificate-template-v2/certificate-template-v2-service.ts index 9cc3d22c4..36d77e4ff 100644 --- a/backend/src/services/certificate-template-v2/certificate-template-v2-service.ts +++ b/backend/src/services/certificate-template-v2/certificate-template-v2-service.ts @@ -8,6 +8,7 @@ import { ProjectPermissionPkiTemplateActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils"; import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; import { alphaNumericNanoId } from "@app/lib/nanoid"; @@ -871,13 +872,18 @@ export const certificateTemplateV2ServiceFactory = ({ ProjectPermissionSub.CertificateTemplates ); - const templates = await certificateTemplateV2DAL.findByProjectId(projectId, { - offset, - limit, - search - }); + const permissionFilters = getPermissionFiltersForAbility( + permission, + ProjectPermissionPkiTemplateActions.Read, + ProjectPermissionSub.CertificateTemplates + ); + const templates = await certificateTemplateV2DAL.findByProjectId( + projectId, + { offset, limit, search }, + permissionFilters + ); - const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }); + const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }, permissionFilters); return { templates, diff --git a/backend/src/services/certificate-v3/certificate-v3-service.ts b/backend/src/services/certificate-v3/certificate-v3-service.ts index e50df78bc..af6162d70 100644 --- a/backend/src/services/certificate-v3/certificate-v3-service.ts +++ b/backend/src/services/certificate-v3/certificate-v3-service.ts @@ -1491,10 +1491,12 @@ export const certificateV3ServiceFactory = ({ actionProjectType: ActionProjectType.CertificateManager }); - ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionCertificateProfileActions.IssueCert, - subject(ProjectPermissionSub.CertificateProfiles, { slug: profile?.slug || "*" }) - ); + if (profile) { + ForbiddenError.from(permission).throwUnlessCan( + ProjectPermissionCertificateProfileActions.IssueCert, + subject(ProjectPermissionSub.CertificateProfiles, { slug: profile.slug }) + ); + } } const issuerType = profile?.issuerType || (originalCert.caId ? IssuerType.CA : IssuerType.SELF_SIGNED); diff --git a/backend/src/services/certificate/certificate-dal.ts b/backend/src/services/certificate/certificate-dal.ts index 7af79319b..f61c46c28 100644 --- a/backend/src/services/certificate/certificate-dal.ts +++ b/backend/src/services/certificate/certificate-dal.ts @@ -4,6 +4,7 @@ import { TDbClient } from "@app/db"; import { TableName, TCertificates } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { ormify, selectAllTableCols } from "@app/lib/knex"; +import { applyPermissionFiltersToQuery, type PermissionFilters } from "@app/lib/knex/permission-filter-utils"; import { CertStatus } from "./certificate-types"; @@ -140,7 +141,8 @@ export const certificateDALFactory = (db: TDbClient) => { const findActiveCertificatesForSync = async ( filter: Partial, - options?: { limit?: number; offset?: number } + options?: { limit?: number; offset?: number }, + permissionFilters?: PermissionFilters ): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => { try { let query = db @@ -163,6 +165,10 @@ export const certificateDALFactory = (db: TDbClient) => { } }); + if (permissionFilters) { + query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query; + } + if (options?.offset) { query = query.offset(options.offset); } @@ -267,7 +273,8 @@ export const certificateDALFactory = (db: TDbClient) => { const findWithPrivateKeyInfo = async ( filter: Partial, - options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] } + options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] }, + permissionFilters?: PermissionFilters ): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => { try { let query = db @@ -287,6 +294,10 @@ export const certificateDALFactory = (db: TDbClient) => { } }); + if (permissionFilters) { + query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query; + } + if (options?.offset) { query = query.offset(options.offset); } diff --git a/backend/src/services/pki-sync/pki-sync-dal.ts b/backend/src/services/pki-sync/pki-sync-dal.ts index 460bbfc42..294ebff80 100644 --- a/backend/src/services/pki-sync/pki-sync-dal.ts +++ b/backend/src/services/pki-sync/pki-sync-dal.ts @@ -4,6 +4,7 @@ import { TDbClient } from "@app/db"; import { TableName, TPkiSyncs } from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { buildFindFilter, ormify, prependTableNameToFindFilter, selectAllTableCols } from "@app/lib/knex"; +import { applyPermissionFiltersToQuery, type PermissionFilters } from "@app/lib/knex/permission-filter-utils"; import { PkiSync } from "./pki-sync-enums"; @@ -45,13 +46,15 @@ const basePkiSyncQuery = ({ filter, db, tx }: { db: TDbClient; filter?: PkiSyncF const basePkiSyncWithSubscriberQuery = ({ filter, db, - tx + tx, + permissionFilters }: { db: TDbClient; filter?: PkiSyncFindFilter; tx?: Knex; + permissionFilters?: PermissionFilters; }) => { - const query = (tx || db.replicaNode())(TableName.PkiSync) + let query = (tx || db.replicaNode())(TableName.PkiSync) .leftJoin(TableName.AppConnection, `${TableName.PkiSync}.connectionId`, `${TableName.AppConnection}.id`) .leftJoin(TableName.PkiSubscriber, `${TableName.PkiSync}.subscriberId`, `${TableName.PkiSubscriber}.id`) .select(selectAllTableCols(TableName.PkiSync)) @@ -82,6 +85,10 @@ const basePkiSyncWithSubscriberQuery = ({ void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.PkiSync, filter))); } + if (permissionFilters) { + query = applyPermissionFiltersToQuery(query, TableName.PkiSync, permissionFilters) as typeof query; + } + return query; }; @@ -184,9 +191,18 @@ export const pkiSyncDALFactory = (db: TDbClient) => { } }; - const findByProjectIdWithSubscribers = async (projectId: string, tx?: Knex) => { + const findByProjectIdWithSubscribers = async ( + projectId: string, + permissionFilters?: PermissionFilters, + tx?: Knex + ) => { try { - const pkiSyncs = await basePkiSyncWithSubscriberQuery({ filter: { projectId }, db, tx }); + const pkiSyncs = await basePkiSyncWithSubscriberQuery({ + filter: { projectId }, + db, + tx, + permissionFilters + }); return pkiSyncs.map(expandPkiSyncWithSubscriber); } catch (error) { throw new DatabaseError({ error, name: "Find By Project ID With Subscribers - PKI Sync" }); diff --git a/backend/src/services/pki-sync/pki-sync-service.ts b/backend/src/services/pki-sync/pki-sync-service.ts index 57a9a2ace..2a4044208 100644 --- a/backend/src/services/pki-sync/pki-sync-service.ts +++ b/backend/src/services/pki-sync/pki-sync-service.ts @@ -4,6 +4,7 @@ import { ActionProjectType, TCertificateSyncs } from "@app/db/schemas"; import { TLicenseServiceFactory } from "@app/ee/services/license/license-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types"; import { ProjectPermissionPkiSyncActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils"; import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors"; import { OrgServiceActor } from "@app/lib/types"; import { AppConnection } from "@app/services/app-connection/app-connection-enums"; @@ -355,9 +356,15 @@ export const pkiSyncServiceFactory = ({ projectId }); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.List, ProjectPermissionSub.PkiSyncs); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs); - const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId); + const permissionFilters = getPermissionFiltersForAbility( + permission, + ProjectPermissionPkiSyncActions.Read, + ProjectPermissionSub.PkiSyncs + ); + + const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId, permissionFilters); if (certificateId) { const syncsWithCertificateInfo = await Promise.all( diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index 9c24171bb..2ea6503eb 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -40,6 +40,7 @@ import { TSshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certific import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; import { TSshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal"; import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore"; +import { buildPermissionFiltersFromConditions, getDbFiltersForAbility } from "@app/lib/casl/permission-filter-utils"; import { getConfig } from "@app/lib/config/env"; import { crypto } from "@app/lib/crypto/cryptography"; import { DatabaseErrorCode } from "@app/lib/error-codes"; @@ -912,7 +913,7 @@ export const projectServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionCertificateAuthorityActions.List, + ProjectPermissionCertificateAuthorityActions.Read, ProjectPermissionSub.CertificateAuthorities ); @@ -960,39 +961,44 @@ export const projectServiceFactory = ({ }); ForbiddenError.from(permission).throwUnlessCan( - ProjectPermissionCertificateActions.List, + ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates ); + const filters = getDbFiltersForAbility( + permission, + ProjectPermissionCertificateActions.Read, + ProjectPermissionSub.Certificates + ); + + const regularFilters = { + projectId, + ...(friendlyName && { friendlyName }), + ...(commonName && { commonName }) + }; + const permissionFilters = buildPermissionFiltersFromConditions(filters || {}); + const certificates = forPkiSync - ? await certificateDAL.findActiveCertificatesForSync( - { - projectId, - ...(friendlyName && { friendlyName }), - ...(commonName && { commonName }) - }, - { offset, limit } - ) + ? await certificateDAL.findActiveCertificatesForSync(regularFilters, { offset, limit }, permissionFilters) : await certificateDAL.findWithPrivateKeyInfo( + regularFilters, { - projectId, - ...(friendlyName && { friendlyName }), - ...(commonName && { commonName }) + offset, + limit, + sort: [["notAfter", "desc"]] }, - { offset, limit, sort: [["notAfter", "desc"]] } + permissionFilters ); + const countFilter = { + projectId, + ...(regularFilters.friendlyName && { friendlyName: String(regularFilters.friendlyName) }), + ...(regularFilters.commonName && { commonName: String(regularFilters.commonName) }) + }; + const count = forPkiSync - ? await certificateDAL.countActiveCertificatesForSync({ - projectId, - friendlyName, - commonName - }) - : await certificateDAL.countCertificatesInProject({ - projectId, - friendlyName, - commonName - }); + ? await certificateDAL.countActiveCertificatesForSync(countFilter) + : await certificateDAL.countCertificatesInProject(countFilter); return { certificates, diff --git a/docs/docs.json b/docs/docs.json index be76a10c1..5a3d965fd 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -2493,8 +2493,6 @@ "pages": [ "api-reference/endpoints/certificates/list", "api-reference/endpoints/certificates/read", - "api-reference/endpoints/certificates/issue-certificate", - "api-reference/endpoints/certificates/sign-certificate", "api-reference/endpoints/certificates/renew", "api-reference/endpoints/certificates/update-config", "api-reference/endpoints/certificates/revoke", diff --git a/frontend/src/context/ProjectPermissionContext/types.ts b/frontend/src/context/ProjectPermissionContext/types.ts index d8406ab25..83eb2fecd 100644 --- a/frontend/src/context/ProjectPermissionContext/types.ts +++ b/frontend/src/context/ProjectPermissionContext/types.ts @@ -391,8 +391,6 @@ export type CertificateSubjectFields = { commonName?: string; altNames?: string; serialNumber?: string; - friendlyName?: string; - status?: string; }; export type CertificateProfileSubjectFields = { diff --git a/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/CertAuthDetailsByIDPage.tsx b/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/CertAuthDetailsByIDPage.tsx index 22a3ab1d6..4f12c3950 100644 --- a/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/CertAuthDetailsByIDPage.tsx +++ b/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/CertAuthDetailsByIDPage.tsx @@ -93,7 +93,6 @@ const Page = () => { @@ -128,7 +127,6 @@ const Page = () => { diff --git a/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/components/CaDetailsSection.tsx b/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/components/CaDetailsSection.tsx index 489c59984..8f88a3f2e 100644 --- a/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/components/CaDetailsSection.tsx +++ b/frontend/src/pages/cert-manager/CertAuthDetailsByIDPage/components/CaDetailsSection.tsx @@ -42,7 +42,7 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {

CA Details

{(isAllowed) => { return ( @@ -159,7 +159,7 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => { {ca.status === CaStatus.ACTIVE && ( {(isAllowed) => { return ( @@ -195,7 +195,7 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => { {ca.status === CaStatus.PENDING_CERTIFICATE && ( {(isAllowed) => { return ( diff --git a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaSection.tsx b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaSection.tsx index 145248bae..2870f390d 100644 --- a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaSection.tsx +++ b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaSection.tsx @@ -104,6 +104,7 @@ export const CaSection = () => { : "This action will prevent the CA from issuing new certificates." } onChange={(isOpen) => handlePopUpToggle("caStatus", isOpen)} + buttonText="Confirm" deleteKey="confirm" onDeleteApproved={() => onUpdateCaStatus(popUp?.caStatus?.data as { caId: string; status: CaStatus }) diff --git a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaTable.tsx b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaTable.tsx index 06cb97532..b52ca3daa 100644 --- a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaTable.tsx +++ b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/CaTable.tsx @@ -81,7 +81,6 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { const canReadCa = permission.can( ProjectPermissionCertificateAuthorityActions.Read, subject(ProjectPermissionSub.CertificateAuthorities, { - caId: ca.id, name: ca.name }) ); @@ -136,7 +135,6 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { @@ -164,7 +162,6 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { @@ -192,7 +189,6 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { @@ -223,7 +219,6 @@ export const CaTable = ({ handlePopUpOpen }: Props) => { diff --git a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/ExternalCaTable.tsx b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/ExternalCaTable.tsx index 6d2b56f3f..e31320d59 100644 --- a/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/ExternalCaTable.tsx +++ b/frontend/src/pages/cert-manager/CertificateAuthoritiesPage/components/ExternalCaTable.tsx @@ -75,7 +75,6 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => { const canReadCa = permission.can( ProjectPermissionCertificateAuthorityActions.Read, subject(ProjectPermissionSub.CertificateAuthorities, { - caId: ca.id, name: ca.name }) ); @@ -117,7 +116,6 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => { @@ -145,7 +143,6 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => { @@ -177,7 +174,6 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => { diff --git a/frontend/src/pages/cert-manager/IntegrationsListPage/components/PkiSyncsTab/PkiSyncTable/PkiSyncRow.tsx b/frontend/src/pages/cert-manager/IntegrationsListPage/components/PkiSyncsTab/PkiSyncTable/PkiSyncRow.tsx index a6cff113a..7d424c1bc 100644 --- a/frontend/src/pages/cert-manager/IntegrationsListPage/components/PkiSyncsTab/PkiSyncTable/PkiSyncRow.tsx +++ b/frontend/src/pages/cert-manager/IntegrationsListPage/components/PkiSyncsTab/PkiSyncTable/PkiSyncRow.tsx @@ -67,7 +67,6 @@ export const PkiSyncRow = ({ const navigate = useNavigate(); const { id, - subscriberId, lastSyncMessage, destination, lastSyncedAt, @@ -117,7 +116,6 @@ export const PkiSyncRow = ({ const destinationDetails = PKI_SYNC_MAP[destination]; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: subscriberId || "", subscriberName: destinationName, name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncActionTriggers.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncActionTriggers.tsx index f87f8cdba..adb054149 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncActionTriggers.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncActionTriggers.tsx @@ -53,7 +53,7 @@ type Props = { }; export const PkiSyncActionTriggers = ({ pkiSync }: Props) => { - const { destination, subscriberId, projectId, id } = pkiSync; + const { destination, projectId, id } = pkiSync; const navigate = useNavigate(); const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp([ @@ -112,7 +112,6 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => { }, [updatePkiSyncMutation, id, projectId, pkiSync.isAutoSyncEnabled]); const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncCertificatesSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncCertificatesSection.tsx index 8a990bd4c..761f52cdd 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncCertificatesSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncCertificatesSection.tsx @@ -88,7 +88,6 @@ export const PkiSyncCertificatesSection = ({ pkiSync }: Props) => { const destinationName = PKI_SYNC_MAP[pkiSync.destination].name; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: pkiSync.subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDestinationSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDestinationSection.tsx index 22611171b..651b03403 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDestinationSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDestinationSection.tsx @@ -31,7 +31,7 @@ type Props = { }; export const PkiSyncDestinationSection = ({ pkiSync, onEditDestination }: Props) => { - const { destination, subscriberId } = pkiSync; + const { destination } = pkiSync; const destinationDetails = PKI_SYNC_MAP[destination]; @@ -55,7 +55,6 @@ export const PkiSyncDestinationSection = ({ pkiSync, onEditDestination }: Props) } const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: subscriberId || "", subscriberName: destinationDetails.name, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDetailsSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDetailsSection.tsx index 45fc9f371..5ebfc659b 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDetailsSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncDetailsSection.tsx @@ -34,8 +34,7 @@ type Props = { }; export const PkiSyncDetailsSection = ({ pkiSync, onEditDetails }: Props) => { - const { syncStatus, lastSyncMessage, lastSyncedAt, name, description, subscriberId, subscriber } = - pkiSync; + const { syncStatus, lastSyncMessage, lastSyncedAt, name, description, subscriber } = pkiSync; const failureMessage = useMemo(() => { if (syncStatus === PkiSyncStatus.Failed) { @@ -54,7 +53,6 @@ export const PkiSyncDetailsSection = ({ pkiSync, onEditDetails }: Props) => { const destinationName = PKI_SYNC_MAP[pkiSync.destination].name; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: subscriber?.id || subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncFieldMappingsSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncFieldMappingsSection.tsx index c293f4982..41745bc86 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncFieldMappingsSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncFieldMappingsSection.tsx @@ -39,7 +39,6 @@ export const PkiSyncFieldMappingsSection = ({ pkiSync, onEditMappings }: Props) const destinationName = PKI_SYNC_MAP[pkiSync.destination].name; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: pkiSync.subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncOptionsSection/PkiSyncOptionsSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncOptionsSection/PkiSyncOptionsSection.tsx index 8cf57b874..891b4c3f0 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncOptionsSection/PkiSyncOptionsSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncOptionsSection/PkiSyncOptionsSection.tsx @@ -38,7 +38,6 @@ export const PkiSyncOptionsSection = ({ pkiSync, onEditOptions }: Props) => { const destinationName = PKI_SYNC_MAP[pkiSync.destination].name; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: pkiSync.subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncSourceSection.tsx b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncSourceSection.tsx index 3097dd4c8..3abaee734 100644 --- a/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncSourceSection.tsx +++ b/frontend/src/pages/cert-manager/PkiSyncDetailsByIDPage/components/PkiSyncSourceSection.tsx @@ -30,7 +30,6 @@ export const PkiSyncSourceSection = ({ pkiSync, onEditSource }: Props) => { const destinationName = PKI_SYNC_MAP[pkiSync.destination].name; const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, { - subscriberId: subscriberId || "", subscriberName: destinationName, name: pkiSync.name }); diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/CertificatePermissionConditions.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/CertificatePermissionConditions.tsx index 06eb5a8f9..7fa49de53 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/CertificatePermissionConditions.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/CertificatePermissionConditions.tsx @@ -14,7 +14,6 @@ export const CertificatePermissionConditions = ({ position = 0, isDisabled }: Pr subject={ProjectPermissionSub.Certificates} position={position} selectOptions={[ - { value: "id", label: "Certificate ID" }, { value: "commonName", label: "Common Name" }, { value: "altNames", label: "Subject Alternative Names" }, { value: "serialNumber", label: "Serial Number" } diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/PermissionConditionHelpers.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/PermissionConditionHelpers.tsx index f6c44b386..c8408d1cf 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/PermissionConditionHelpers.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/PermissionConditionHelpers.tsx @@ -36,6 +36,7 @@ export const renderOperatorSelectItems = (type: string) => { return ( <> Equal + Not Equal Glob Match In diff --git a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx index 1178102f2..566a5f93a 100644 --- a/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx +++ b/frontend/src/pages/project/RoleDetailsBySlugPage/components/ProjectRoleModifySection.utils.tsx @@ -57,7 +57,6 @@ const CertificatePolicyActionSchema = z.object({ [ProjectPermissionCertificateActions.Delete]: z.boolean().optional(), [ProjectPermissionCertificateActions.Edit]: z.boolean().optional(), [ProjectPermissionCertificateActions.Read]: z.boolean().optional(), - [ProjectPermissionCertificateActions.List]: z.boolean().optional(), [ProjectPermissionCertificateActions.ReadPrivateKey]: z.boolean().optional(), [ProjectPermissionCertificateActions.Import]: z.boolean().optional() }); @@ -67,7 +66,6 @@ const CertificateAuthorityPolicyActionSchema = z.object({ [ProjectPermissionCertificateAuthorityActions.Delete]: z.boolean().optional(), [ProjectPermissionCertificateAuthorityActions.Edit]: z.boolean().optional(), [ProjectPermissionCertificateAuthorityActions.Read]: z.boolean().optional(), - [ProjectPermissionCertificateAuthorityActions.List]: z.boolean().optional(), [ProjectPermissionCertificateAuthorityActions.Renew]: z.boolean().optional(), [ProjectPermissionCertificateAuthorityActions.SignIntermediate]: z.boolean().optional() }); @@ -123,7 +121,6 @@ const PkiSyncPolicyActionSchema = z.object({ [ProjectPermissionPkiSyncActions.Create]: z.boolean().optional(), [ProjectPermissionPkiSyncActions.Edit]: z.boolean().optional(), [ProjectPermissionPkiSyncActions.Delete]: z.boolean().optional(), - [ProjectPermissionPkiSyncActions.List]: z.boolean().optional(), [ProjectPermissionPkiSyncActions.SyncCertificates]: z.boolean().optional(), [ProjectPermissionPkiSyncActions.ImportCertificates]: z.boolean().optional(), [ProjectPermissionPkiSyncActions.RemoveCertificates]: z.boolean().optional() @@ -234,7 +231,6 @@ const PkiTemplatePolicyActionSchema = z.object({ }); const CertificateProfilePolicyActionSchema = z.object({ [ProjectPermissionCertificateProfileActions.Read]: z.boolean().optional(), - [ProjectPermissionCertificateProfileActions.List]: z.boolean().optional(), [ProjectPermissionCertificateProfileActions.Create]: z.boolean().optional(), [ProjectPermissionCertificateProfileActions.Edit]: z.boolean().optional(), [ProjectPermissionCertificateProfileActions.Delete]: z.boolean().optional(), @@ -696,7 +692,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { const canEdit = action.includes(ProjectPermissionPkiSyncActions.Edit); const canDelete = action.includes(ProjectPermissionPkiSyncActions.Delete); const canCreate = action.includes(ProjectPermissionPkiSyncActions.Create); - const canList = action.includes(ProjectPermissionPkiSyncActions.List); const canSyncCertificates = action.includes( ProjectPermissionPkiSyncActions.SyncCertificates ); @@ -715,7 +710,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { [ProjectPermissionPkiSyncActions.Create]: canCreate, [ProjectPermissionPkiSyncActions.Edit]: canEdit, [ProjectPermissionPkiSyncActions.Delete]: canDelete, - [ProjectPermissionPkiSyncActions.List]: canList, [ProjectPermissionPkiSyncActions.SyncCertificates]: canSyncCertificates, [ProjectPermissionPkiSyncActions.ImportCertificates]: canImportCertificates, [ProjectPermissionPkiSyncActions.RemoveCertificates]: canRemoveCertificates, @@ -864,7 +858,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { const canCreate = action.includes(ProjectPermissionCertificateAuthorityActions.Create); const canEdit = action.includes(ProjectPermissionCertificateAuthorityActions.Edit); const canDelete = action.includes(ProjectPermissionCertificateAuthorityActions.Delete); - const canList = action.includes(ProjectPermissionCertificateAuthorityActions.List); const canRenew = action.includes(ProjectPermissionCertificateAuthorityActions.Renew); const canSignIntermediate = action.includes( ProjectPermissionCertificateAuthorityActions.SignIntermediate @@ -876,7 +869,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { [ProjectPermissionCertificateAuthorityActions.Create]: canCreate, [ProjectPermissionCertificateAuthorityActions.Edit]: canEdit, [ProjectPermissionCertificateAuthorityActions.Delete]: canDelete, - [ProjectPermissionCertificateAuthorityActions.List]: canList, [ProjectPermissionCertificateAuthorityActions.Renew]: canRenew, [ProjectPermissionCertificateAuthorityActions.SignIntermediate]: canSignIntermediate, conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [], @@ -937,7 +929,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { const canEdit = action.includes(ProjectPermissionCertificateActions.Edit); const canDelete = action.includes(ProjectPermissionCertificateActions.Delete); const canCreate = action.includes(ProjectPermissionCertificateActions.Create); - const canList = action.includes(ProjectPermissionCertificateActions.List); const canImport = action.includes(ProjectPermissionCertificateActions.Import); const canReadPrivateKey = action.includes(ProjectPermissionCertificateActions.ReadPrivateKey); @@ -949,7 +940,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { [ProjectPermissionCertificateActions.Edit]: canEdit, [ProjectPermissionCertificateActions.Create]: canCreate, [ProjectPermissionCertificateActions.Delete]: canDelete, - [ProjectPermissionCertificateActions.List]: canList, [ProjectPermissionCertificateActions.ReadPrivateKey]: canReadPrivateKey, [ProjectPermissionCertificateActions.Import]: canImport, conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [], @@ -1250,9 +1240,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => { [ProjectPermissionCertificateProfileActions.Read]: action.includes( ProjectPermissionCertificateProfileActions.Read ), - [ProjectPermissionCertificateProfileActions.List]: action.includes( - ProjectPermissionCertificateProfileActions.List - ), [ProjectPermissionCertificateProfileActions.IssueCert]: action.includes( ProjectPermissionCertificateProfileActions.IssueCert ), @@ -1629,7 +1616,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { title: "Certificate Authorities", actions: [ { label: "Read", value: ProjectPermissionCertificateAuthorityActions.Read }, - { label: "List", value: ProjectPermissionCertificateAuthorityActions.List }, { label: "Create", value: ProjectPermissionCertificateAuthorityActions.Create }, { label: "Modify", value: ProjectPermissionCertificateAuthorityActions.Edit }, { label: "Remove", value: ProjectPermissionCertificateAuthorityActions.Delete }, @@ -1644,7 +1630,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { title: "Certificates", actions: [ { label: "Read", value: ProjectPermissionCertificateActions.Read }, - { label: "List", value: ProjectPermissionCertificateActions.List }, { label: "Read Private Key", value: ProjectPermissionCertificateActions.ReadPrivateKey }, // { label: "Create", value: ProjectPermissionCertificateActions.Create }, // Hidden from UI - kept for backend compatibility { label: "Import", value: ProjectPermissionCertificateActions.Import }, @@ -1666,7 +1651,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { title: "Certificate Profiles", actions: [ { label: "Read", value: ProjectPermissionCertificateProfileActions.Read }, - { label: "List", value: ProjectPermissionCertificateProfileActions.List }, { label: "Create", value: ProjectPermissionCertificateProfileActions.Create }, { label: "Modify", value: ProjectPermissionCertificateProfileActions.Edit }, { label: "Remove", value: ProjectPermissionCertificateProfileActions.Delete }, @@ -1799,7 +1783,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = { title: "Certificate Syncs", actions: [ { label: "Read", value: ProjectPermissionPkiSyncActions.Read }, - { label: "List", value: ProjectPermissionPkiSyncActions.List }, { label: "Create", value: ProjectPermissionPkiSyncActions.Create }, { label: "Modify", value: ProjectPermissionPkiSyncActions.Edit }, { label: "Remove", value: ProjectPermissionPkiSyncActions.Delete },