UI improvements on PKI product

This commit is contained in:
Carlos Monastyrski
2025-12-04 18:55:26 -03:00
parent 003aa1cd59
commit 90c73def68
26 changed files with 243 additions and 418 deletions
+1 -117
View File
@@ -41,7 +41,7 @@ interface MongoGlobFilter {
* @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 buildPermissionFiltersFromConditions = (conditions: MongoQuery, isInverted = false): PermissionFilters => {
const permissionFilters: PermissionFilters = {};
function addFilterToField(key: string, operator: string, value: unknown, isPattern: boolean) {
@@ -184,83 +184,6 @@ export const buildPermissionFiltersFromConditions = (conditions: MongoQuery, isI
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<string, unknown>): Record<string, unknown> {
const dbFilter: Record<string, unknown> = {};
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<string, unknown>)[operator];
switch (operator) {
case "$glob": {
// Convert "*.es" glob → regex /^.*\.es$/i
const regex = new RE2(`^${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<string, unknown> {
const matchingRules = ability.rules.filter((rule: RawRuleOf<MongoAbility>) => {
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<MongoAbility>) =>
convertConditionToDbFilter(rule.conditions as Record<string, unknown>)
);
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.
@@ -300,42 +223,3 @@ export function getProcessedPermissionRules(
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;
}
+3 -139
View File
@@ -15,86 +15,6 @@ export interface ProcessedPermissionRules {
forbidRules: Array<Record<string, Array<PermissionFilterConfig>>>;
}
/**
* 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
@@ -204,65 +124,9 @@ export const applyProcessedPermissionRulesToQuery = (
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;
}
});
Object.entries(forbidRule).forEach(([key, filterConfigs]) => {
filterConfigs.forEach((filterConfig) => {
applySingleFilter(query, tableName, key, filterConfig);
});
});
});
@@ -176,7 +176,8 @@ export const registerCertificateRouter = async (server: FastifyZodProvider) => {
actor: req.permission.type,
actorId: req.permission.id,
actorAuthMethod: req.permission.authMethod,
actorOrgId: req.permission.orgId
actorOrgId: req.permission.orgId,
isInternal: true
});
const caType = (ca?.externalCa?.type as CaType) ?? CaType.INTERNAL;
useOrderFlow = caType !== CaType.INTERNAL;
@@ -7,7 +7,7 @@ import { TableName } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, CryptographyError, NotFoundError } from "@app/lib/errors";
import { PermissionFilters } from "@app/lib/knex/permission-filter-utils";
import { ProcessedPermissionRules } from "@app/lib/knex/permission-filter-utils";
import { OrgServiceActor } from "@app/lib/types";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
@@ -721,7 +721,7 @@ export const AcmeCertificateAuthorityFns = ({
permissionFilters
}: {
projectId: string;
permissionFilters?: PermissionFilters;
permissionFilters?: ProcessedPermissionRules;
}) => {
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
{
@@ -5,7 +5,7 @@ import RE2 from "re2";
import { TableName } from "@app/db/schemas";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { PermissionFilters } from "@app/lib/knex/permission-filter-utils";
import { ProcessedPermissionRules } from "@app/lib/knex/permission-filter-utils";
import { ms } from "@app/lib/ms";
import { OrgServiceActor } from "@app/lib/types";
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
@@ -804,7 +804,7 @@ export const AzureAdCsCertificateAuthorityFns = ({
permissionFilters
}: {
projectId: string;
permissionFilters?: PermissionFilters;
permissionFilters?: ProcessedPermissionRules;
}) => {
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
{
@@ -4,7 +4,10 @@ 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";
import {
applyProcessedPermissionRulesToQuery,
type ProcessedPermissionRules
} from "@app/lib/knex/permission-filter-utils";
export type TCertificateAuthorityDALFactory = ReturnType<typeof certificateAuthorityDALFactory>;
@@ -221,7 +224,7 @@ 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<TCertificateAuthorities> = {},
permissionFilters?: PermissionFilters,
permissionFilters?: ProcessedPermissionRules,
tx?: Knex
) => {
try {
@@ -271,7 +274,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
);
if (permissionFilters) {
query = applyPermissionFiltersToQuery(query, TableName.CertificateAuthority, permissionFilters) as typeof query;
query = applyProcessedPermissionRulesToQuery(
query,
TableName.CertificateAuthority,
permissionFilters
) as typeof query;
}
if (limit) void query.limit(limit);
@@ -6,7 +6,7 @@ import {
ProjectPermissionCertificateAuthorityActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils";
import { getProcessedPermissionRules } from "@app/lib/casl/permission-filter-utils";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
@@ -337,7 +337,7 @@ export const certificateAuthorityServiceFactory = ({
ProjectPermissionSub.CertificateAuthorities
);
const permissionFilters = getPermissionFiltersForAbility(
const permissionFilters = getProcessedPermissionRules(
permission,
ProjectPermissionCertificateAuthorityActions.Read,
ProjectPermissionSub.CertificateAuthorities
@@ -711,34 +711,38 @@ export const certificateAuthorityServiceFactory = ({
actor,
actorId,
actorAuthMethod,
actorOrgId
actorOrgId,
isInternal
}: {
caId: string;
actor: OrgServiceActor["type"];
actorId: string;
actorAuthMethod: OrgServiceActor["authMethod"];
actorOrgId?: string;
isInternal?: boolean;
}) => {
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
if (!ca) {
throw new NotFoundError({ message: "CA not found" });
}
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
});
if (!isInternal) {
const { permission } = await permissionService.getProjectPermission({
actor,
actorId,
projectId: ca.projectId,
actorAuthMethod,
actorOrgId,
actionProjectType: ActionProjectType.CertificateManager
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionCertificateAuthorityActions.Read,
subject(ProjectPermissionSub.CertificateAuthorities, {
name: ca.name
})
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionCertificateAuthorityActions.Read,
subject(ProjectPermissionSub.CertificateAuthorities, {
name: ca.name
})
);
}
return ca;
};
@@ -4,7 +4,10 @@ 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 {
applyProcessedPermissionRulesToQuery,
type ProcessedPermissionRules
} from "@app/lib/knex/permission-filter-utils";
import {
EnrollmentType,
@@ -277,7 +280,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
issuerType?: IssuerType;
caId?: string;
} = {},
permissionFilters?: PermissionFilters,
processedRules?: ProcessedPermissionRules,
tx?: Knex
): Promise<TCertificateProfile[] | TCertificateProfileWithConfigs[]> => {
try {
@@ -356,11 +359,11 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
db.ref("id").withSchema(TableName.PkiAcmeEnrollmentConfig).as("acmeId")
);
if (permissionFilters) {
query = applyPermissionFiltersToQuery(
if (processedRules) {
query = applyProcessedPermissionRulesToQuery(
query,
TableName.PkiCertificateProfile,
permissionFilters
processedRules
) as typeof query;
}
@@ -442,7 +445,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
issuerType?: IssuerType;
caId?: string;
} = {},
permissionFilters?: PermissionFilters,
processedRules?: ProcessedPermissionRules,
tx?: Knex
): Promise<number> => {
try {
@@ -470,11 +473,11 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
query = query.where({ issuerType });
}
if (permissionFilters) {
query = applyPermissionFiltersToQuery(
if (processedRules) {
query = applyProcessedPermissionRulesToQuery(
query,
TableName.PkiCertificateProfile,
permissionFilters
processedRules
) as typeof query;
}
@@ -609,7 +609,7 @@ describe("CertificateProfileService", () => {
caId: undefined,
issuerType: undefined
},
{}
{ allowRules: [], forbidRules: [] }
);
});
@@ -634,7 +634,7 @@ describe("CertificateProfileService", () => {
caId: "ca-123",
issuerType: undefined
},
{}
{ allowRules: [], forbidRules: [] }
);
});
});
@@ -10,7 +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 { getProcessedPermissionRules } 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";
@@ -750,7 +750,7 @@ export const certificateProfileServiceFactory = ({
ProjectPermissionSub.CertificateProfiles
);
const permissionFilters = getPermissionFiltersForAbility(
const processedRules = getProcessedPermissionRules(
permission,
ProjectPermissionCertificateProfileActions.Read,
ProjectPermissionSub.CertificateProfiles
@@ -766,7 +766,7 @@ export const certificateProfileServiceFactory = ({
issuerType,
caId
},
permissionFilters
processedRules
);
const totalCount = await certificateProfileDAL.countByProjectId(
@@ -777,7 +777,7 @@ export const certificateProfileServiceFactory = ({
issuerType,
caId
},
permissionFilters
processedRules
);
const convertedProfiles = await Promise.all(
@@ -10,6 +10,7 @@ import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
ProjectPermissionCertificateActions,
ProjectPermissionCertificateProfileActions,
ProjectPermissionSet,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
@@ -79,8 +80,8 @@ describe("CertificateRequestService", () => {
const mockPermission = {
permission: createMongoAbility<ProjectPermissionSet>([
{
action: ProjectPermissionCertificateActions.Create,
subject: ProjectPermissionSub.Certificates
action: ProjectPermissionCertificateProfileActions.IssueCert,
subject: ProjectPermissionSub.CertificateProfiles
}
])
};
@@ -6,6 +6,7 @@ import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import {
ProjectPermissionCertificateActions,
ProjectPermissionCertificateProfileActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
@@ -110,8 +111,8 @@ export const certificateRequestServiceFactory = ({
});
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionCertificateActions.Create,
ProjectPermissionSub.Certificates
ProjectPermissionCertificateProfileActions.IssueCert,
ProjectPermissionSub.CertificateProfiles
);
}
@@ -5,7 +5,10 @@ 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 {
applyProcessedPermissionRulesToQuery,
type ProcessedPermissionRules
} from "@app/lib/knex/permission-filter-utils";
import {
TCertificateTemplateV2,
@@ -134,7 +137,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
limit?: number;
search?: string;
} = {},
permissionFilters?: PermissionFilters,
processedRules?: ProcessedPermissionRules,
tx?: Knex
) => {
try {
@@ -148,11 +151,11 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
});
}
if (permissionFilters) {
query = applyPermissionFiltersToQuery(
if (processedRules) {
query = applyProcessedPermissionRulesToQuery(
query,
TableName.PkiCertificateTemplateV2,
permissionFilters
processedRules
) as typeof query;
}
@@ -169,7 +172,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
options: {
search?: string;
} = {},
permissionFilters?: PermissionFilters,
processedRules?: ProcessedPermissionRules,
tx?: Knex
) => {
try {
@@ -183,11 +186,11 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
});
}
if (permissionFilters) {
query = applyPermissionFiltersToQuery(
if (processedRules) {
query = applyProcessedPermissionRulesToQuery(
query,
TableName.PkiCertificateTemplateV2,
permissionFilters
processedRules
) as typeof query;
}
@@ -274,14 +274,14 @@ describe("CertificateTemplateV2Service", () => {
limit: 20,
search: undefined
},
{}
{ allowRules: [], forbidRules: [] }
);
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
"project-123",
{
search: undefined
},
{}
{ allowRules: [], forbidRules: [] }
);
expect(result).toEqual({ templates, totalCount });
});
@@ -306,14 +306,14 @@ describe("CertificateTemplateV2Service", () => {
limit: 20,
search: "web server"
},
{}
{ allowRules: [], forbidRules: [] }
);
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
"project-123",
{
search: "web server"
},
{}
{ allowRules: [], forbidRules: [] }
);
});
});
@@ -8,7 +8,7 @@ import {
ProjectPermissionPkiTemplateActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { getPermissionFiltersForAbility } from "@app/lib/casl/permission-filter-utils";
import { getProcessedPermissionRules } from "@app/lib/casl/permission-filter-utils";
import { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -872,7 +872,7 @@ export const certificateTemplateV2ServiceFactory = ({
ProjectPermissionSub.CertificateTemplates
);
const permissionFilters = getPermissionFiltersForAbility(
const processedRules = getProcessedPermissionRules(
permission,
ProjectPermissionPkiTemplateActions.Read,
ProjectPermissionSub.CertificateTemplates
@@ -880,10 +880,10 @@ export const certificateTemplateV2ServiceFactory = ({
const templates = await certificateTemplateV2DAL.findByProjectId(
projectId,
{ offset, limit, search },
permissionFilters
processedRules
);
const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }, permissionFilters);
const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }, processedRules);
return {
templates,
@@ -4,7 +4,10 @@ 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 {
applyProcessedPermissionRulesToQuery,
type ProcessedPermissionRules
} from "@app/lib/knex/permission-filter-utils";
import { CertStatus } from "./certificate-types";
@@ -142,7 +145,7 @@ export const certificateDALFactory = (db: TDbClient) => {
const findActiveCertificatesForSync = async (
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
options?: { limit?: number; offset?: number },
permissionFilters?: PermissionFilters
permissionFilters?: ProcessedPermissionRules
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
try {
let query = db
@@ -166,7 +169,7 @@ export const certificateDALFactory = (db: TDbClient) => {
});
if (permissionFilters) {
query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
query = applyProcessedPermissionRulesToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
}
if (options?.offset) {
@@ -274,7 +277,7 @@ export const certificateDALFactory = (db: TDbClient) => {
const findWithPrivateKeyInfo = async (
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] },
permissionFilters?: PermissionFilters
permissionFilters?: ProcessedPermissionRules
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
try {
let query = db
@@ -295,7 +298,7 @@ export const certificateDALFactory = (db: TDbClient) => {
});
if (permissionFilters) {
query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
query = applyProcessedPermissionRulesToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
}
if (options?.offset) {
+10 -7
View File
@@ -4,7 +4,10 @@ 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 {
applyProcessedPermissionRulesToQuery,
type ProcessedPermissionRules
} from "@app/lib/knex/permission-filter-utils";
import { PkiSync } from "./pki-sync-enums";
@@ -47,12 +50,12 @@ const basePkiSyncWithSubscriberQuery = ({
filter,
db,
tx,
permissionFilters
processedRules
}: {
db: TDbClient;
filter?: PkiSyncFindFilter;
tx?: Knex;
permissionFilters?: PermissionFilters;
processedRules?: ProcessedPermissionRules;
}) => {
let query = (tx || db.replicaNode())(TableName.PkiSync)
.leftJoin(TableName.AppConnection, `${TableName.PkiSync}.connectionId`, `${TableName.AppConnection}.id`)
@@ -85,8 +88,8 @@ const basePkiSyncWithSubscriberQuery = ({
void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.PkiSync, filter)));
}
if (permissionFilters) {
query = applyPermissionFiltersToQuery(query, TableName.PkiSync, permissionFilters) as typeof query;
if (processedRules) {
query = applyProcessedPermissionRulesToQuery(query, TableName.PkiSync, processedRules) as typeof query;
}
return query;
@@ -193,7 +196,7 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
const findByProjectIdWithSubscribers = async (
projectId: string,
permissionFilters?: PermissionFilters,
processedRules?: ProcessedPermissionRules,
tx?: Knex
) => {
try {
@@ -201,7 +204,7 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
filter: { projectId },
db,
tx,
permissionFilters
processedRules
});
return pkiSyncs.map(expandPkiSyncWithSubscriber);
} catch (error) {
@@ -4,7 +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 { getProcessedPermissionRules } 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";
@@ -358,13 +358,13 @@ export const pkiSyncServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
const permissionFilters = getPermissionFiltersForAbility(
const processedRules = getProcessedPermissionRules(
permission,
ProjectPermissionPkiSyncActions.Read,
ProjectPermissionSub.PkiSyncs
);
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId, permissionFilters);
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId, processedRules);
if (certificateId) {
const syncsWithCertificateInfo = await Promise.all(
@@ -40,7 +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 { getProcessedPermissionRules } 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";
@@ -965,18 +965,16 @@ export const projectServiceFactory = ({
ProjectPermissionSub.Certificates
);
const filters = getDbFiltersForAbility(
permission,
ProjectPermissionCertificateActions.Read,
ProjectPermissionSub.Certificates
);
const regularFilters = {
projectId,
...(friendlyName && { friendlyName }),
...(commonName && { commonName })
};
const permissionFilters = buildPermissionFiltersFromConditions(filters || {});
const permissionFilters = getProcessedPermissionRules(
permission,
ProjectPermissionCertificateActions.Read,
ProjectPermissionSub.Certificates
);
const certificates = forPkiSync
? await certificateDAL.findActiveCertificatesForSync(regularFilters, { offset, limit }, permissionFilters)