mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-08 21:28:18 +00:00
UI improvements on PKI product
This commit is contained in:
@@ -41,7 +41,7 @@ interface MongoGlobFilter {
|
|||||||
* @param isInverted - Whether this rule is inverted (forbidden)
|
* @param isInverted - Whether this rule is inverted (forbidden)
|
||||||
* @returns Record of field names to arrays of filter configurations
|
* @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 = {};
|
const permissionFilters: PermissionFilters = {};
|
||||||
|
|
||||||
function addFilterToField(key: string, operator: string, value: unknown, isPattern: boolean) {
|
function addFilterToField(key: string, operator: string, value: unknown, isPattern: boolean) {
|
||||||
@@ -184,83 +184,6 @@ export const buildPermissionFiltersFromConditions = (conditions: MongoQuery, isI
|
|||||||
return permissionFilters;
|
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,
|
* Extract permission filters for a subject and action,
|
||||||
* converting them into ProcessedPermissionRules format for use with Knex queries.
|
* converting them into ProcessedPermissionRules format for use with Knex queries.
|
||||||
@@ -300,42 +223,3 @@ export function getProcessedPermissionRules(
|
|||||||
|
|
||||||
return { allowRules, forbidRules };
|
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;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,86 +15,6 @@ export interface ProcessedPermissionRules {
|
|||||||
forbidRules: Array<Record<string, Array<PermissionFilterConfig>>>;
|
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
|
* Applies a single filter configuration to a query
|
||||||
* @param query - The Knex query builder instance
|
* @param query - The Knex query builder instance
|
||||||
@@ -204,65 +124,9 @@ export const applyProcessedPermissionRulesToQuery = (
|
|||||||
|
|
||||||
if (processedRules.forbidRules.length > 0) {
|
if (processedRules.forbidRules.length > 0) {
|
||||||
processedRules.forbidRules.forEach((forbidRule) => {
|
processedRules.forbidRules.forEach((forbidRule) => {
|
||||||
query = query.andWhere((forbidBuilder) => {
|
Object.entries(forbidRule).forEach(([key, filterConfigs]) => {
|
||||||
let hasConditions = false;
|
filterConfigs.forEach((filterConfig) => {
|
||||||
Object.entries(forbidRule).forEach(([key, filterConfigs]) => {
|
applySingleFilter(query, tableName, key, filterConfig);
|
||||||
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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -176,7 +176,8 @@ export const registerCertificateRouter = async (server: FastifyZodProvider) => {
|
|||||||
actor: req.permission.type,
|
actor: req.permission.type,
|
||||||
actorId: req.permission.id,
|
actorId: req.permission.id,
|
||||||
actorAuthMethod: req.permission.authMethod,
|
actorAuthMethod: req.permission.authMethod,
|
||||||
actorOrgId: req.permission.orgId
|
actorOrgId: req.permission.orgId,
|
||||||
|
isInternal: true
|
||||||
});
|
});
|
||||||
const caType = (ca?.externalCa?.type as CaType) ?? CaType.INTERNAL;
|
const caType = (ca?.externalCa?.type as CaType) ?? CaType.INTERNAL;
|
||||||
useOrderFlow = 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 { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, CryptographyError, NotFoundError } from "@app/lib/errors";
|
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 { OrgServiceActor } from "@app/lib/types";
|
||||||
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
@@ -721,7 +721,7 @@ export const AcmeCertificateAuthorityFns = ({
|
|||||||
permissionFilters
|
permissionFilters
|
||||||
}: {
|
}: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
permissionFilters?: PermissionFilters;
|
permissionFilters?: ProcessedPermissionRules;
|
||||||
}) => {
|
}) => {
|
||||||
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
|
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@ import RE2 from "re2";
|
|||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
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 { ms } from "@app/lib/ms";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
import { TAppConnectionDALFactory } from "@app/services/app-connection/app-connection-dal";
|
||||||
@@ -804,7 +804,7 @@ export const AzureAdCsCertificateAuthorityFns = ({
|
|||||||
permissionFilters
|
permissionFilters
|
||||||
}: {
|
}: {
|
||||||
projectId: string;
|
projectId: string;
|
||||||
permissionFilters?: PermissionFilters;
|
permissionFilters?: ProcessedPermissionRules;
|
||||||
}) => {
|
}) => {
|
||||||
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
|
const cas = await certificateAuthorityDAL.findWithAssociatedCa(
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { TDbClient } from "@app/db";
|
|||||||
import { CertificateAuthoritiesSchema, TableName, TCertificateAuthorities } from "@app/db/schemas";
|
import { CertificateAuthoritiesSchema, TableName, TCertificateAuthorities } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, selectAllTableCols, TFindOpt } from "@app/lib/knex";
|
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>;
|
export type TCertificateAuthorityDALFactory = ReturnType<typeof certificateAuthorityDALFactory>;
|
||||||
|
|
||||||
@@ -221,7 +224,7 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
const findWithAssociatedCa = async (
|
const findWithAssociatedCa = async (
|
||||||
filter: Parameters<(typeof caOrm)["find"]>[0] & { dn?: string; type?: string; serialNumber?: string },
|
filter: Parameters<(typeof caOrm)["find"]>[0] & { dn?: string; type?: string; serialNumber?: string },
|
||||||
{ offset, limit, sort = [["createdAt", "desc"]] }: TFindOpt<TCertificateAuthorities> = {},
|
{ offset, limit, sort = [["createdAt", "desc"]] }: TFindOpt<TCertificateAuthorities> = {},
|
||||||
permissionFilters?: PermissionFilters,
|
permissionFilters?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -271,7 +274,11 @@ export const certificateAuthorityDALFactory = (db: TDbClient) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (permissionFilters) {
|
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);
|
if (limit) void query.limit(limit);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import {
|
|||||||
ProjectPermissionCertificateAuthorityActions,
|
ProjectPermissionCertificateAuthorityActions,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} 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 { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
|
|
||||||
@@ -337,7 +337,7 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionFilters = getPermissionFiltersForAbility(
|
const permissionFilters = getProcessedPermissionRules(
|
||||||
permission,
|
permission,
|
||||||
ProjectPermissionCertificateAuthorityActions.Read,
|
ProjectPermissionCertificateAuthorityActions.Read,
|
||||||
ProjectPermissionSub.CertificateAuthorities
|
ProjectPermissionSub.CertificateAuthorities
|
||||||
@@ -711,34 +711,38 @@ export const certificateAuthorityServiceFactory = ({
|
|||||||
actor,
|
actor,
|
||||||
actorId,
|
actorId,
|
||||||
actorAuthMethod,
|
actorAuthMethod,
|
||||||
actorOrgId
|
actorOrgId,
|
||||||
|
isInternal
|
||||||
}: {
|
}: {
|
||||||
caId: string;
|
caId: string;
|
||||||
actor: OrgServiceActor["type"];
|
actor: OrgServiceActor["type"];
|
||||||
actorId: string;
|
actorId: string;
|
||||||
actorAuthMethod: OrgServiceActor["authMethod"];
|
actorAuthMethod: OrgServiceActor["authMethod"];
|
||||||
actorOrgId?: string;
|
actorOrgId?: string;
|
||||||
|
isInternal?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
|
const ca = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
|
||||||
if (!ca) {
|
if (!ca) {
|
||||||
throw new NotFoundError({ message: "CA not found" });
|
throw new NotFoundError({ message: "CA not found" });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { permission } = await permissionService.getProjectPermission({
|
if (!isInternal) {
|
||||||
actor,
|
const { permission } = await permissionService.getProjectPermission({
|
||||||
actorId,
|
actor,
|
||||||
projectId: ca.projectId,
|
actorId,
|
||||||
actorAuthMethod,
|
projectId: ca.projectId,
|
||||||
actorOrgId,
|
actorAuthMethod,
|
||||||
actionProjectType: ActionProjectType.CertificateManager
|
actorOrgId,
|
||||||
});
|
actionProjectType: ActionProjectType.CertificateManager
|
||||||
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionCertificateAuthorityActions.Read,
|
ProjectPermissionCertificateAuthorityActions.Read,
|
||||||
subject(ProjectPermissionSub.CertificateAuthorities, {
|
subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||||
name: ca.name
|
name: ca.name
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return ca;
|
return ca;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName } from "@app/db/schemas";
|
import { TableName } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
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 {
|
import {
|
||||||
EnrollmentType,
|
EnrollmentType,
|
||||||
@@ -277,7 +280,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
|||||||
issuerType?: IssuerType;
|
issuerType?: IssuerType;
|
||||||
caId?: string;
|
caId?: string;
|
||||||
} = {},
|
} = {},
|
||||||
permissionFilters?: PermissionFilters,
|
processedRules?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
): Promise<TCertificateProfile[] | TCertificateProfileWithConfigs[]> => {
|
): Promise<TCertificateProfile[] | TCertificateProfileWithConfigs[]> => {
|
||||||
try {
|
try {
|
||||||
@@ -356,11 +359,11 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
|||||||
db.ref("id").withSchema(TableName.PkiAcmeEnrollmentConfig).as("acmeId")
|
db.ref("id").withSchema(TableName.PkiAcmeEnrollmentConfig).as("acmeId")
|
||||||
);
|
);
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (processedRules) {
|
||||||
query = applyPermissionFiltersToQuery(
|
query = applyProcessedPermissionRulesToQuery(
|
||||||
query,
|
query,
|
||||||
TableName.PkiCertificateProfile,
|
TableName.PkiCertificateProfile,
|
||||||
permissionFilters
|
processedRules
|
||||||
) as typeof query;
|
) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -442,7 +445,7 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
|||||||
issuerType?: IssuerType;
|
issuerType?: IssuerType;
|
||||||
caId?: string;
|
caId?: string;
|
||||||
} = {},
|
} = {},
|
||||||
permissionFilters?: PermissionFilters,
|
processedRules?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
): Promise<number> => {
|
): Promise<number> => {
|
||||||
try {
|
try {
|
||||||
@@ -470,11 +473,11 @@ export const certificateProfileDALFactory = (db: TDbClient) => {
|
|||||||
query = query.where({ issuerType });
|
query = query.where({ issuerType });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (processedRules) {
|
||||||
query = applyPermissionFiltersToQuery(
|
query = applyProcessedPermissionRulesToQuery(
|
||||||
query,
|
query,
|
||||||
TableName.PkiCertificateProfile,
|
TableName.PkiCertificateProfile,
|
||||||
permissionFilters
|
processedRules
|
||||||
) as typeof query;
|
) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -609,7 +609,7 @@ describe("CertificateProfileService", () => {
|
|||||||
caId: undefined,
|
caId: undefined,
|
||||||
issuerType: undefined
|
issuerType: undefined
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -634,7 +634,7 @@ describe("CertificateProfileService", () => {
|
|||||||
caId: "ca-123",
|
caId: "ca-123",
|
||||||
issuerType: undefined
|
issuerType: undefined
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} from "@app/ee/services/permission/project-permission";
|
||||||
import { buildUrl } from "@app/ee/services/pki-acme/pki-acme-fns";
|
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 { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
|
||||||
import { getConfig } from "@app/lib/config/env";
|
import { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
@@ -750,7 +750,7 @@ export const certificateProfileServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateProfiles
|
ProjectPermissionSub.CertificateProfiles
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionFilters = getPermissionFiltersForAbility(
|
const processedRules = getProcessedPermissionRules(
|
||||||
permission,
|
permission,
|
||||||
ProjectPermissionCertificateProfileActions.Read,
|
ProjectPermissionCertificateProfileActions.Read,
|
||||||
ProjectPermissionSub.CertificateProfiles
|
ProjectPermissionSub.CertificateProfiles
|
||||||
@@ -766,7 +766,7 @@ export const certificateProfileServiceFactory = ({
|
|||||||
issuerType,
|
issuerType,
|
||||||
caId
|
caId
|
||||||
},
|
},
|
||||||
permissionFilters
|
processedRules
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalCount = await certificateProfileDAL.countByProjectId(
|
const totalCount = await certificateProfileDAL.countByProjectId(
|
||||||
@@ -777,7 +777,7 @@ export const certificateProfileServiceFactory = ({
|
|||||||
issuerType,
|
issuerType,
|
||||||
caId
|
caId
|
||||||
},
|
},
|
||||||
permissionFilters
|
processedRules
|
||||||
);
|
);
|
||||||
|
|
||||||
const convertedProfiles = await Promise.all(
|
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 { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionCertificateActions,
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionCertificateProfileActions,
|
||||||
ProjectPermissionSet,
|
ProjectPermissionSet,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} from "@app/ee/services/permission/project-permission";
|
||||||
@@ -79,8 +80,8 @@ describe("CertificateRequestService", () => {
|
|||||||
const mockPermission = {
|
const mockPermission = {
|
||||||
permission: createMongoAbility<ProjectPermissionSet>([
|
permission: createMongoAbility<ProjectPermissionSet>([
|
||||||
{
|
{
|
||||||
action: ProjectPermissionCertificateActions.Create,
|
action: ProjectPermissionCertificateProfileActions.IssueCert,
|
||||||
subject: ProjectPermissionSub.Certificates
|
subject: ProjectPermissionSub.CertificateProfiles
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ActionProjectType } from "@app/db/schemas";
|
|||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionCertificateActions,
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionCertificateProfileActions,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} from "@app/ee/services/permission/project-permission";
|
||||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
@@ -110,8 +111,8 @@ export const certificateRequestServiceFactory = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
ForbiddenError.from(permission).throwUnlessCan(
|
ForbiddenError.from(permission).throwUnlessCan(
|
||||||
ProjectPermissionCertificateActions.Create,
|
ProjectPermissionCertificateProfileActions.IssueCert,
|
||||||
ProjectPermissionSub.Certificates
|
ProjectPermissionSub.CertificateProfiles
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import { TableName } from "@app/db/schemas";
|
|||||||
import { TPkiCertificateTemplatesV2Insert } from "@app/db/schemas/pki-certificate-templates-v2";
|
import { TPkiCertificateTemplatesV2Insert } from "@app/db/schemas/pki-certificate-templates-v2";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify } from "@app/lib/knex";
|
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 {
|
import {
|
||||||
TCertificateTemplateV2,
|
TCertificateTemplateV2,
|
||||||
@@ -134,7 +137,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
|
|||||||
limit?: number;
|
limit?: number;
|
||||||
search?: string;
|
search?: string;
|
||||||
} = {},
|
} = {},
|
||||||
permissionFilters?: PermissionFilters,
|
processedRules?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -148,11 +151,11 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (processedRules) {
|
||||||
query = applyPermissionFiltersToQuery(
|
query = applyProcessedPermissionRulesToQuery(
|
||||||
query,
|
query,
|
||||||
TableName.PkiCertificateTemplateV2,
|
TableName.PkiCertificateTemplateV2,
|
||||||
permissionFilters
|
processedRules
|
||||||
) as typeof query;
|
) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,7 +172,7 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
|
|||||||
options: {
|
options: {
|
||||||
search?: string;
|
search?: string;
|
||||||
} = {},
|
} = {},
|
||||||
permissionFilters?: PermissionFilters,
|
processedRules?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -183,11 +186,11 @@ export const certificateTemplateV2DALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (processedRules) {
|
||||||
query = applyPermissionFiltersToQuery(
|
query = applyProcessedPermissionRulesToQuery(
|
||||||
query,
|
query,
|
||||||
TableName.PkiCertificateTemplateV2,
|
TableName.PkiCertificateTemplateV2,
|
||||||
permissionFilters
|
processedRules
|
||||||
) as typeof query;
|
) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-4
@@ -274,14 +274,14 @@ describe("CertificateTemplateV2Service", () => {
|
|||||||
limit: 20,
|
limit: 20,
|
||||||
search: undefined
|
search: undefined
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
|
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
|
||||||
"project-123",
|
"project-123",
|
||||||
{
|
{
|
||||||
search: undefined
|
search: undefined
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
expect(result).toEqual({ templates, totalCount });
|
expect(result).toEqual({ templates, totalCount });
|
||||||
});
|
});
|
||||||
@@ -306,14 +306,14 @@ describe("CertificateTemplateV2Service", () => {
|
|||||||
limit: 20,
|
limit: 20,
|
||||||
search: "web server"
|
search: "web server"
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
|
expect(mockCertificateTemplateV2DAL.countByProjectId).toHaveBeenCalledWith(
|
||||||
"project-123",
|
"project-123",
|
||||||
{
|
{
|
||||||
search: "web server"
|
search: "web server"
|
||||||
},
|
},
|
||||||
{}
|
{ allowRules: [], forbidRules: [] }
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
ProjectPermissionPkiTemplateActions,
|
ProjectPermissionPkiTemplateActions,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
} from "@app/ee/services/permission/project-permission";
|
} 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 { ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
|
||||||
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
import { alphaNumericNanoId } from "@app/lib/nanoid";
|
||||||
|
|
||||||
@@ -872,7 +872,7 @@ export const certificateTemplateV2ServiceFactory = ({
|
|||||||
ProjectPermissionSub.CertificateTemplates
|
ProjectPermissionSub.CertificateTemplates
|
||||||
);
|
);
|
||||||
|
|
||||||
const permissionFilters = getPermissionFiltersForAbility(
|
const processedRules = getProcessedPermissionRules(
|
||||||
permission,
|
permission,
|
||||||
ProjectPermissionPkiTemplateActions.Read,
|
ProjectPermissionPkiTemplateActions.Read,
|
||||||
ProjectPermissionSub.CertificateTemplates
|
ProjectPermissionSub.CertificateTemplates
|
||||||
@@ -880,10 +880,10 @@ export const certificateTemplateV2ServiceFactory = ({
|
|||||||
const templates = await certificateTemplateV2DAL.findByProjectId(
|
const templates = await certificateTemplateV2DAL.findByProjectId(
|
||||||
projectId,
|
projectId,
|
||||||
{ offset, limit, search },
|
{ offset, limit, search },
|
||||||
permissionFilters
|
processedRules
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }, permissionFilters);
|
const totalCount = await certificateTemplateV2DAL.countByProjectId(projectId, { search }, processedRules);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
templates,
|
templates,
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName, TCertificates } from "@app/db/schemas";
|
import { TableName, TCertificates } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { ormify, selectAllTableCols } from "@app/lib/knex";
|
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";
|
import { CertStatus } from "./certificate-types";
|
||||||
|
|
||||||
@@ -142,7 +145,7 @@ export const certificateDALFactory = (db: TDbClient) => {
|
|||||||
const findActiveCertificatesForSync = async (
|
const findActiveCertificatesForSync = async (
|
||||||
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
|
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
|
||||||
options?: { limit?: number; offset?: number },
|
options?: { limit?: number; offset?: number },
|
||||||
permissionFilters?: PermissionFilters
|
permissionFilters?: ProcessedPermissionRules
|
||||||
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
|
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
|
||||||
try {
|
try {
|
||||||
let query = db
|
let query = db
|
||||||
@@ -166,7 +169,7 @@ export const certificateDALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (permissionFilters) {
|
||||||
query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
|
query = applyProcessedPermissionRulesToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options?.offset) {
|
if (options?.offset) {
|
||||||
@@ -274,7 +277,7 @@ export const certificateDALFactory = (db: TDbClient) => {
|
|||||||
const findWithPrivateKeyInfo = async (
|
const findWithPrivateKeyInfo = async (
|
||||||
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
|
filter: Partial<TCertificates & { friendlyName?: string; commonName?: string }>,
|
||||||
options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] },
|
options?: { offset?: number; limit?: number; sort?: [string, "asc" | "desc"][] },
|
||||||
permissionFilters?: PermissionFilters
|
permissionFilters?: ProcessedPermissionRules
|
||||||
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
|
): Promise<(TCertificates & { hasPrivateKey: boolean })[]> => {
|
||||||
try {
|
try {
|
||||||
let query = db
|
let query = db
|
||||||
@@ -295,7 +298,7 @@ export const certificateDALFactory = (db: TDbClient) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (permissionFilters) {
|
||||||
query = applyPermissionFiltersToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
|
query = applyProcessedPermissionRulesToQuery(query, TableName.Certificate, permissionFilters) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options?.offset) {
|
if (options?.offset) {
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import { TDbClient } from "@app/db";
|
|||||||
import { TableName, TPkiSyncs } from "@app/db/schemas";
|
import { TableName, TPkiSyncs } from "@app/db/schemas";
|
||||||
import { DatabaseError } from "@app/lib/errors";
|
import { DatabaseError } from "@app/lib/errors";
|
||||||
import { buildFindFilter, ormify, prependTableNameToFindFilter, selectAllTableCols } from "@app/lib/knex";
|
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";
|
import { PkiSync } from "./pki-sync-enums";
|
||||||
|
|
||||||
@@ -47,12 +50,12 @@ const basePkiSyncWithSubscriberQuery = ({
|
|||||||
filter,
|
filter,
|
||||||
db,
|
db,
|
||||||
tx,
|
tx,
|
||||||
permissionFilters
|
processedRules
|
||||||
}: {
|
}: {
|
||||||
db: TDbClient;
|
db: TDbClient;
|
||||||
filter?: PkiSyncFindFilter;
|
filter?: PkiSyncFindFilter;
|
||||||
tx?: Knex;
|
tx?: Knex;
|
||||||
permissionFilters?: PermissionFilters;
|
processedRules?: ProcessedPermissionRules;
|
||||||
}) => {
|
}) => {
|
||||||
let query = (tx || db.replicaNode())(TableName.PkiSync)
|
let query = (tx || db.replicaNode())(TableName.PkiSync)
|
||||||
.leftJoin(TableName.AppConnection, `${TableName.PkiSync}.connectionId`, `${TableName.AppConnection}.id`)
|
.leftJoin(TableName.AppConnection, `${TableName.PkiSync}.connectionId`, `${TableName.AppConnection}.id`)
|
||||||
@@ -85,8 +88,8 @@ const basePkiSyncWithSubscriberQuery = ({
|
|||||||
void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.PkiSync, filter)));
|
void query.where(buildFindFilter(prependTableNameToFindFilter(TableName.PkiSync, filter)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (permissionFilters) {
|
if (processedRules) {
|
||||||
query = applyPermissionFiltersToQuery(query, TableName.PkiSync, permissionFilters) as typeof query;
|
query = applyProcessedPermissionRulesToQuery(query, TableName.PkiSync, processedRules) as typeof query;
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
@@ -193,7 +196,7 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
|
|||||||
|
|
||||||
const findByProjectIdWithSubscribers = async (
|
const findByProjectIdWithSubscribers = async (
|
||||||
projectId: string,
|
projectId: string,
|
||||||
permissionFilters?: PermissionFilters,
|
processedRules?: ProcessedPermissionRules,
|
||||||
tx?: Knex
|
tx?: Knex
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
@@ -201,7 +204,7 @@ export const pkiSyncDALFactory = (db: TDbClient) => {
|
|||||||
filter: { projectId },
|
filter: { projectId },
|
||||||
db,
|
db,
|
||||||
tx,
|
tx,
|
||||||
permissionFilters
|
processedRules
|
||||||
});
|
});
|
||||||
return pkiSyncs.map(expandPkiSyncWithSubscriber);
|
return pkiSyncs.map(expandPkiSyncWithSubscriber);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ActionProjectType, TCertificateSyncs } from "@app/db/schemas";
|
|||||||
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
|
||||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||||
import { ProjectPermissionPkiSyncActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
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 { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
|
||||||
import { OrgServiceActor } from "@app/lib/types";
|
import { OrgServiceActor } from "@app/lib/types";
|
||||||
import { AppConnection } from "@app/services/app-connection/app-connection-enums";
|
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);
|
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
|
||||||
|
|
||||||
const permissionFilters = getPermissionFiltersForAbility(
|
const processedRules = getProcessedPermissionRules(
|
||||||
permission,
|
permission,
|
||||||
ProjectPermissionPkiSyncActions.Read,
|
ProjectPermissionPkiSyncActions.Read,
|
||||||
ProjectPermissionSub.PkiSyncs
|
ProjectPermissionSub.PkiSyncs
|
||||||
);
|
);
|
||||||
|
|
||||||
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId, permissionFilters);
|
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId, processedRules);
|
||||||
|
|
||||||
if (certificateId) {
|
if (certificateId) {
|
||||||
const syncsWithCertificateInfo = await Promise.all(
|
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 { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal";
|
||||||
import { TSshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
|
import { TSshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
|
||||||
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
|
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 { getConfig } from "@app/lib/config/env";
|
||||||
import { crypto } from "@app/lib/crypto/cryptography";
|
import { crypto } from "@app/lib/crypto/cryptography";
|
||||||
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
import { DatabaseErrorCode } from "@app/lib/error-codes";
|
||||||
@@ -965,18 +965,16 @@ export const projectServiceFactory = ({
|
|||||||
ProjectPermissionSub.Certificates
|
ProjectPermissionSub.Certificates
|
||||||
);
|
);
|
||||||
|
|
||||||
const filters = getDbFiltersForAbility(
|
|
||||||
permission,
|
|
||||||
ProjectPermissionCertificateActions.Read,
|
|
||||||
ProjectPermissionSub.Certificates
|
|
||||||
);
|
|
||||||
|
|
||||||
const regularFilters = {
|
const regularFilters = {
|
||||||
projectId,
|
projectId,
|
||||||
...(friendlyName && { friendlyName }),
|
...(friendlyName && { friendlyName }),
|
||||||
...(commonName && { commonName })
|
...(commonName && { commonName })
|
||||||
};
|
};
|
||||||
const permissionFilters = buildPermissionFiltersFromConditions(filters || {});
|
const permissionFilters = getProcessedPermissionRules(
|
||||||
|
permission,
|
||||||
|
ProjectPermissionCertificateActions.Read,
|
||||||
|
ProjectPermissionSub.Certificates
|
||||||
|
);
|
||||||
|
|
||||||
const certificates = forPkiSync
|
const certificates = forPkiSync
|
||||||
? await certificateDAL.findActiveCertificatesForSync(regularFilters, { offset, limit }, permissionFilters)
|
? await certificateDAL.findActiveCertificatesForSync(regularFilters, { offset, limit }, permissionFilters)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export {
|
|||||||
ProjectPermissionKmipActions,
|
ProjectPermissionKmipActions,
|
||||||
ProjectPermissionMemberActions,
|
ProjectPermissionMemberActions,
|
||||||
ProjectPermissionPkiSubscriberActions,
|
ProjectPermissionPkiSubscriberActions,
|
||||||
|
ProjectPermissionPkiSyncActions,
|
||||||
ProjectPermissionPkiTemplateActions,
|
ProjectPermissionPkiTemplateActions,
|
||||||
ProjectPermissionSshHostActions,
|
ProjectPermissionSshHostActions,
|
||||||
ProjectPermissionSub
|
ProjectPermissionSub
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export {
|
|||||||
ProjectPermissionKmipActions,
|
ProjectPermissionKmipActions,
|
||||||
ProjectPermissionMemberActions,
|
ProjectPermissionMemberActions,
|
||||||
ProjectPermissionPkiSubscriberActions,
|
ProjectPermissionPkiSubscriberActions,
|
||||||
|
ProjectPermissionPkiSyncActions,
|
||||||
ProjectPermissionPkiTemplateActions,
|
ProjectPermissionPkiTemplateActions,
|
||||||
ProjectPermissionSshHostActions,
|
ProjectPermissionSshHostActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
|
|||||||
+5
-5
@@ -72,8 +72,8 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
data &&
|
data &&
|
||||||
data.length > 0 &&
|
data.length > 0 &&
|
||||||
data.map((ca) => {
|
data.map((ca) => {
|
||||||
const canReadCa = permission.can(
|
const canEditCa = permission.can(
|
||||||
ProjectPermissionCertificateAuthorityActions.Read,
|
ProjectPermissionCertificateAuthorityActions.Edit,
|
||||||
subject(ProjectPermissionSub.CertificateAuthorities, {
|
subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||||
name: ca.name
|
name: ca.name
|
||||||
})
|
})
|
||||||
@@ -83,12 +83,12 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
<Tr
|
<Tr
|
||||||
className={twMerge(
|
className={twMerge(
|
||||||
"h-10 transition-colors duration-100",
|
"h-10 transition-colors duration-100",
|
||||||
canReadCa && "cursor-pointer hover:bg-mineshaft-700",
|
canEditCa && "cursor-pointer hover:bg-mineshaft-700",
|
||||||
!canReadCa && "cursor-not-allowed opacity-60"
|
!canEditCa && "cursor-not-allowed opacity-60"
|
||||||
)}
|
)}
|
||||||
key={`ca-${ca.id}`}
|
key={`ca-${ca.id}`}
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
canReadCa &&
|
canEditCa &&
|
||||||
handlePopUpOpen("ca", {
|
handlePopUpOpen("ca", {
|
||||||
caId: ca.id,
|
caId: ca.id,
|
||||||
name: ca.name,
|
name: ca.name,
|
||||||
|
|||||||
+17
-9
@@ -6,6 +6,7 @@ import { ProjectPermissionCan } from "@app/components/permissions";
|
|||||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionCertificateActions,
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionCertificateProfileActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useProject
|
useProject
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
@@ -104,12 +105,12 @@ export const CertificatesSection = () => {
|
|||||||
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||||
<div className="mb-4 flex justify-between">
|
<div className="mb-4 flex justify-between">
|
||||||
<p className="text-xl font-medium text-mineshaft-100">Certificates</p>
|
<p className="text-xl font-medium text-mineshaft-100">Certificates</p>
|
||||||
<ProjectPermissionCan
|
<div className="flex gap-2">
|
||||||
I={ProjectPermissionCertificateActions.Create}
|
<ProjectPermissionCan
|
||||||
a={ProjectPermissionSub.Certificates}
|
I={ProjectPermissionCertificateActions.Import}
|
||||||
>
|
a={ProjectPermissionSub.Certificates}
|
||||||
{(isAllowed) => (
|
>
|
||||||
<div className="flex gap-2">
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
variant="outline_bg"
|
variant="outline_bg"
|
||||||
leftIcon={<FontAwesomeIcon icon={faArrowRight} />}
|
leftIcon={<FontAwesomeIcon icon={faArrowRight} />}
|
||||||
@@ -118,6 +119,13 @@ export const CertificatesSection = () => {
|
|||||||
>
|
>
|
||||||
Import
|
Import
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
|
</ProjectPermissionCan>
|
||||||
|
<ProjectPermissionCan
|
||||||
|
I={ProjectPermissionCertificateProfileActions.IssueCert}
|
||||||
|
a={ProjectPermissionSub.CertificateProfiles}
|
||||||
|
>
|
||||||
|
{(isAllowed) => (
|
||||||
<Button
|
<Button
|
||||||
colorSchema="primary"
|
colorSchema="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -127,9 +135,9 @@ export const CertificatesSection = () => {
|
|||||||
>
|
>
|
||||||
Request
|
Request
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
)}
|
||||||
)}
|
</ProjectPermissionCan>
|
||||||
</ProjectPermissionCan>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CertificatesTable handlePopUpOpen={handlePopUpOpen} />
|
<CertificatesTable handlePopUpOpen={handlePopUpOpen} />
|
||||||
<CertificateIssuanceModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
<CertificateIssuanceModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ import {
|
|||||||
import { Badge } from "@app/components/v3";
|
import { Badge } from "@app/components/v3";
|
||||||
import {
|
import {
|
||||||
ProjectPermissionCertificateActions,
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionPkiSyncActions,
|
||||||
ProjectPermissionSub,
|
ProjectPermissionSub,
|
||||||
useProject
|
useProject,
|
||||||
|
useProjectPermission
|
||||||
} from "@app/context";
|
} from "@app/context";
|
||||||
import { useUpdateRenewalConfig } from "@app/hooks/api";
|
import { useUpdateRenewalConfig } from "@app/hooks/api";
|
||||||
import { caSupportsCapability } from "@app/hooks/api/ca/constants";
|
import { caSupportsCapability } from "@app/hooks/api/ca/constants";
|
||||||
@@ -97,6 +99,7 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
const [perPage, setPerPage] = useState(PER_PAGE_INIT);
|
const [perPage, setPerPage] = useState(PER_PAGE_INIT);
|
||||||
|
|
||||||
const { currentProject } = useProject();
|
const { currentProject } = useProject();
|
||||||
|
const { permission } = useProjectPermission();
|
||||||
const { data, isPending } = useListWorkspaceCertificates({
|
const { data, isPending } = useListWorkspaceCertificates({
|
||||||
projectId: currentProject?.id ?? "",
|
projectId: currentProject?.id ?? "",
|
||||||
offset: (page - 1) * perPage,
|
offset: (page - 1) * perPage,
|
||||||
@@ -208,55 +211,72 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
return "opacity-0 group-hover:opacity-100";
|
return "opacity-0 group-hover:opacity-100";
|
||||||
})()}`}
|
})()}`}
|
||||||
>
|
>
|
||||||
{canShowAutoRenewalIcon && (
|
{canShowAutoRenewalIcon &&
|
||||||
<Tooltip
|
(() => {
|
||||||
content={(() => {
|
const canEditCertificate = permission.can(
|
||||||
if (hasFailed && certificate.renewalError) {
|
ProjectPermissionCertificateActions.Edit,
|
||||||
return `Auto-renewal failed: ${certificate.renewalError}`;
|
subject(ProjectPermissionSub.Certificates, {
|
||||||
}
|
commonName: certificate.commonName,
|
||||||
if (isAutoRenewalEnabled) {
|
altNames: certificate.altNames,
|
||||||
const expiryDate = new Date(certificate.notAfter);
|
serialNumber: certificate.serialNumber
|
||||||
const now = new Date();
|
})
|
||||||
const daysUntilExpiry = Math.ceil(
|
);
|
||||||
(expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)
|
|
||||||
);
|
|
||||||
const daysUntilRenewal = Math.max(
|
|
||||||
0,
|
|
||||||
daysUntilExpiry - (certificate.renewBeforeDays || 0)
|
|
||||||
);
|
|
||||||
return `Auto-renews in ${daysUntilRenewal}d`;
|
|
||||||
}
|
|
||||||
return "Set auto renewal";
|
|
||||||
})()}
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={(() => {
|
|
||||||
if (hasFailed) return "pr-1 text-red-500 hover:text-red-400";
|
|
||||||
return "pr-1 text-primary-500 hover:text-primary-400";
|
|
||||||
})()}
|
|
||||||
aria-label="Certificate auto-renewal"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
if (hasFailed) return;
|
|
||||||
|
|
||||||
handlePopUpOpen("manageRenewal", {
|
return (
|
||||||
certificateId: certificate.id,
|
<Tooltip
|
||||||
commonName: originalDisplayName,
|
content={(() => {
|
||||||
profileId: certificate.profileId || "",
|
if (hasFailed && certificate.renewalError) {
|
||||||
renewBeforeDays: certificate.renewBeforeDays || 7,
|
return `Auto-renewal failed: ${certificate.renewalError}`;
|
||||||
ttlDays: Math.ceil(
|
}
|
||||||
(new Date(certificate.notAfter).getTime() -
|
if (isAutoRenewalEnabled) {
|
||||||
new Date(certificate.notBefore).getTime()) /
|
const expiryDate = new Date(certificate.notAfter);
|
||||||
(24 * 60 * 60 * 1000)
|
const now = new Date();
|
||||||
)
|
const daysUntilExpiry = Math.ceil(
|
||||||
});
|
(expiryDate.getTime() - now.getTime()) / (24 * 60 * 60 * 1000)
|
||||||
}}
|
);
|
||||||
>
|
const daysUntilRenewal = Math.max(
|
||||||
<FontAwesomeIcon icon={faClockRotateLeft} />
|
0,
|
||||||
</button>
|
daysUntilExpiry - (certificate.renewBeforeDays || 0)
|
||||||
</Tooltip>
|
);
|
||||||
)}
|
return `Auto-renews in ${daysUntilRenewal}d`;
|
||||||
|
}
|
||||||
|
return "Set auto renewal";
|
||||||
|
})()}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={(() => {
|
||||||
|
if (hasFailed) return "pr-1 text-red-500 hover:text-red-400";
|
||||||
|
return "pr-1 text-primary-500 hover:text-primary-400";
|
||||||
|
})()}
|
||||||
|
aria-label="Certificate auto-renewal"
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
if (!canEditCertificate) return;
|
||||||
|
if (hasFailed) return;
|
||||||
|
|
||||||
|
handlePopUpOpen("manageRenewal", {
|
||||||
|
certificateId: certificate.id,
|
||||||
|
commonName: originalDisplayName,
|
||||||
|
profileId: certificate.profileId || "",
|
||||||
|
renewBeforeDays: certificate.renewBeforeDays || 7,
|
||||||
|
ttlDays: Math.ceil(
|
||||||
|
(new Date(certificate.notAfter).getTime() -
|
||||||
|
new Date(certificate.notBefore).getTime()) /
|
||||||
|
(24 * 60 * 60 * 1000)
|
||||||
|
),
|
||||||
|
notAfter: certificate.notAfter,
|
||||||
|
renewalError: certificate.renewalError,
|
||||||
|
renewedFromCertificateId: certificate.renewedFromCertificateId,
|
||||||
|
renewedByCertificateId: certificate.renewedByCertificateId
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faClockRotateLeft} />
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||||
@@ -481,13 +501,8 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
{certificate.status === CertStatus.ACTIVE &&
|
{certificate.status === CertStatus.ACTIVE &&
|
||||||
!certificate.renewedByCertificateId && (
|
!certificate.renewedByCertificateId && (
|
||||||
<ProjectPermissionCan
|
<ProjectPermissionCan
|
||||||
I={ProjectPermissionCertificateActions.Edit}
|
I={ProjectPermissionPkiSyncActions.Edit}
|
||||||
a={subject(ProjectPermissionSub.Certificates, {
|
a={ProjectPermissionSub.PkiSyncs}
|
||||||
commonName: certificate.commonName,
|
|
||||||
altNames: certificate.altNames,
|
|
||||||
serialNumber: certificate.serialNumber,
|
|
||||||
friendlyName: certificate.friendlyName
|
|
||||||
})}
|
|
||||||
>
|
>
|
||||||
{(isAllowed) => (
|
{(isAllowed) => (
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
@@ -509,14 +524,14 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
|||||||
)}
|
)}
|
||||||
</ProjectPermissionCan>
|
</ProjectPermissionCan>
|
||||||
)}
|
)}
|
||||||
{/* Only show revoke button if CA supports revocation */}
|
{/* Only show revoke button if CA supports revocation and certificate is not already revoked */}
|
||||||
{(() => {
|
{(() => {
|
||||||
const caType = caCapabilityMap[certificate.caId];
|
const caType = caCapabilityMap[certificate.caId];
|
||||||
const supportsRevocation =
|
const supportsRevocation =
|
||||||
!caType ||
|
!caType ||
|
||||||
caSupportsCapability(caType, CaCapability.REVOKE_CERTIFICATES);
|
caSupportsCapability(caType, CaCapability.REVOKE_CERTIFICATES);
|
||||||
|
|
||||||
if (!supportsRevocation) {
|
if (!supportsRevocation || isRevoked) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,15 @@ import { useState } from "react";
|
|||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { PermissionDeniedBanner } from "@app/components/permissions";
|
||||||
import { ContentLoader, PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
import { ContentLoader, PageHeader, Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
|
||||||
import { useProject } from "@app/context";
|
import { useProject, useProjectPermission } from "@app/context";
|
||||||
|
import {
|
||||||
|
ProjectPermissionCertificateActions,
|
||||||
|
ProjectPermissionCertificateProfileActions,
|
||||||
|
ProjectPermissionPkiTemplateActions,
|
||||||
|
ProjectPermissionSub
|
||||||
|
} from "@app/context/ProjectPermissionContext/types";
|
||||||
import { ProjectType } from "@app/hooks/api/projects/types";
|
import { ProjectType } from "@app/hooks/api/projects/types";
|
||||||
|
|
||||||
import { CertificateProfilesTab } from "./components/CertificateProfilesTab";
|
import { CertificateProfilesTab } from "./components/CertificateProfilesTab";
|
||||||
@@ -20,8 +27,22 @@ enum TabSections {
|
|||||||
export const PoliciesPage = () => {
|
export const PoliciesPage = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { currentProject } = useProject();
|
const { currentProject } = useProject();
|
||||||
|
const { permission } = useProjectPermission();
|
||||||
const [activeTab, setActiveTab] = useState(TabSections.CertificateProfiles);
|
const [activeTab, setActiveTab] = useState(TabSections.CertificateProfiles);
|
||||||
|
|
||||||
|
const canReadCertificateProfiles = permission.can(
|
||||||
|
ProjectPermissionCertificateProfileActions.Read,
|
||||||
|
ProjectPermissionSub.CertificateProfiles
|
||||||
|
);
|
||||||
|
const canReadCertificateTemplates = permission.can(
|
||||||
|
ProjectPermissionPkiTemplateActions.Read,
|
||||||
|
ProjectPermissionSub.CertificateTemplates
|
||||||
|
);
|
||||||
|
const canReadCertificates = permission.can(
|
||||||
|
ProjectPermissionCertificateActions.Read,
|
||||||
|
ProjectPermissionSub.Certificates
|
||||||
|
);
|
||||||
|
|
||||||
if (!currentProject) {
|
if (!currentProject) {
|
||||||
return <ContentLoader />;
|
return <ContentLoader />;
|
||||||
}
|
}
|
||||||
@@ -56,15 +77,19 @@ export const PoliciesPage = () => {
|
|||||||
</TabList>
|
</TabList>
|
||||||
|
|
||||||
<TabPanel value={TabSections.CertificateProfiles}>
|
<TabPanel value={TabSections.CertificateProfiles}>
|
||||||
<CertificateProfilesTab />
|
{canReadCertificateProfiles ? <CertificateProfilesTab /> : <PermissionDeniedBanner />}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel value={TabSections.CertificateTemplatesV2}>
|
<TabPanel value={TabSections.CertificateTemplatesV2}>
|
||||||
<CertificateTemplatesV2Tab />
|
{canReadCertificateTemplates ? (
|
||||||
|
<CertificateTemplatesV2Tab />
|
||||||
|
) : (
|
||||||
|
<PermissionDeniedBanner />
|
||||||
|
)}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
|
|
||||||
<TabPanel value={TabSections.Certificates}>
|
<TabPanel value={TabSections.Certificates}>
|
||||||
<CertificatesTab />
|
{canReadCertificates ? <CertificatesTab /> : <PermissionDeniedBanner />}
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+5
-2
@@ -258,7 +258,7 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const { control, handleSubmit, reset, watch, setValue, formState } = useForm<
|
const { control, handleSubmit, reset, watch, setValue, formState, trigger } = useForm<
|
||||||
FormData & { preset: TemplatePresetId }
|
FormData & { preset: TemplatePresetId }
|
||||||
>({
|
>({
|
||||||
resolver: zodResolver(templateSchema),
|
resolver: zodResolver(templateSchema),
|
||||||
@@ -286,10 +286,11 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
|||||||
};
|
};
|
||||||
const watchedPreset = watch("preset") || TEMPLATE_PRESET_IDS.CUSTOM;
|
const watchedPreset = watch("preset") || TEMPLATE_PRESET_IDS.CUSTOM;
|
||||||
|
|
||||||
const handlePresetChange = (presetId: TemplatePresetId) => {
|
const handlePresetChange = async (presetId: TemplatePresetId) => {
|
||||||
setValue("preset", presetId);
|
setValue("preset", presetId);
|
||||||
|
|
||||||
if (presetId === TEMPLATE_PRESET_IDS.CUSTOM) {
|
if (presetId === TEMPLATE_PRESET_IDS.CUSTOM) {
|
||||||
|
await trigger();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +314,8 @@ export const CreateTemplateModal = ({ isOpen, onClose, template, mode = "create"
|
|||||||
if (selectedPreset.formData.keyAlgorithm) {
|
if (selectedPreset.formData.keyAlgorithm) {
|
||||||
setValue("keyAlgorithm", selectedPreset.formData.keyAlgorithm);
|
setValue("keyAlgorithm", selectedPreset.formData.keyAlgorithm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await trigger();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user