mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
PKI refine access control
This commit is contained in:
@@ -4,7 +4,10 @@ import * as x509 from "@peculiar/x509";
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import {
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { NotFoundError } from "@app/lib/errors";
|
||||
import { TCertificateAuthorityDALFactory } from "@app/services/certificate-authority/certificate-authority-dal";
|
||||
import { expandInternalCa } from "@app/services/certificate-authority/certificate-authority-fns";
|
||||
@@ -83,7 +86,7 @@ export const certificateAuthorityCrlServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
ProjectPermissionAppConnectionActions,
|
||||
ProjectPermissionAuditLogsActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionCmekActions,
|
||||
ProjectPermissionCommitsActions,
|
||||
@@ -46,7 +47,6 @@ const buildAdminPermissionRules = () => {
|
||||
ProjectPermissionSub.Tags,
|
||||
ProjectPermissionSub.AuditLogs,
|
||||
ProjectPermissionSub.IpAllowList,
|
||||
ProjectPermissionSub.CertificateAuthorities,
|
||||
ProjectPermissionSub.PkiAlerts,
|
||||
ProjectPermissionSub.PkiCollections,
|
||||
ProjectPermissionSub.SshCertificateAuthorities,
|
||||
@@ -67,6 +67,19 @@ const buildAdminPermissionRules = () => {
|
||||
);
|
||||
});
|
||||
|
||||
can(
|
||||
[
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
ProjectPermissionCertificateAuthorityActions.Create,
|
||||
ProjectPermissionCertificateAuthorityActions.Edit,
|
||||
ProjectPermissionCertificateAuthorityActions.Delete,
|
||||
ProjectPermissionCertificateAuthorityActions.List,
|
||||
ProjectPermissionCertificateAuthorityActions.Renew,
|
||||
ProjectPermissionCertificateAuthorityActions.SignIntermediate
|
||||
],
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
can(
|
||||
[
|
||||
ProjectPermissionPkiTemplateActions.Read,
|
||||
@@ -95,7 +108,9 @@ const buildAdminPermissionRules = () => {
|
||||
ProjectPermissionCertificateActions.Edit,
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionCertificateActions.Import,
|
||||
ProjectPermissionCertificateActions.List
|
||||
],
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
@@ -103,6 +118,7 @@ const buildAdminPermissionRules = () => {
|
||||
can(
|
||||
[
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionCertificateProfileActions.List,
|
||||
ProjectPermissionCertificateProfileActions.Edit,
|
||||
ProjectPermissionCertificateProfileActions.Create,
|
||||
ProjectPermissionCertificateProfileActions.Delete,
|
||||
@@ -460,7 +476,7 @@ const buildMemberPermissionRules = () => {
|
||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.IpAllowList);
|
||||
|
||||
// double check if all CRUD are needed for CA and Certificates
|
||||
can([ProjectPermissionActions.Read], ProjectPermissionSub.CertificateAuthorities);
|
||||
can([ProjectPermissionCertificateAuthorityActions.Read], ProjectPermissionSub.CertificateAuthorities);
|
||||
can([ProjectPermissionPkiTemplateActions.Read], ProjectPermissionSub.CertificateTemplates);
|
||||
|
||||
can(
|
||||
@@ -468,7 +484,9 @@ const buildMemberPermissionRules = () => {
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionCertificateActions.Edit,
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionCertificateActions.Delete
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionCertificateActions.Import,
|
||||
ProjectPermissionCertificateActions.List
|
||||
],
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
@@ -476,6 +494,7 @@ const buildMemberPermissionRules = () => {
|
||||
can(
|
||||
[
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionCertificateProfileActions.List,
|
||||
ProjectPermissionCertificateProfileActions.Edit,
|
||||
ProjectPermissionCertificateProfileActions.Create,
|
||||
ProjectPermissionCertificateProfileActions.Delete
|
||||
@@ -599,8 +618,9 @@ const buildViewerPermissionRules = () => {
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.Tags);
|
||||
can(ProjectPermissionAuditLogsActions.Read, ProjectPermissionSub.AuditLogs);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.IpAllowList);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
||||
can(ProjectPermissionCertificateAuthorityActions.Read, ProjectPermissionSub.CertificateAuthorities);
|
||||
can(ProjectPermissionCertificateActions.Read, ProjectPermissionSub.Certificates);
|
||||
can(ProjectPermissionCertificateActions.List, ProjectPermissionSub.Certificates);
|
||||
can(ProjectPermissionPkiTemplateActions.Read, ProjectPermissionSub.CertificateTemplates);
|
||||
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
|
||||
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
|
||||
|
||||
@@ -23,12 +23,24 @@ export enum ProjectPermissionCommitsActions {
|
||||
PerformRollback = "perform-rollback"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionCertificateAuthorityActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
List = "list",
|
||||
Renew = "renew",
|
||||
SignIntermediate = "sign-intermediate"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionCertificateActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
ReadPrivateKey = "read-private-key"
|
||||
List = "list",
|
||||
ReadPrivateKey = "read-private-key",
|
||||
Import = "import"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionSecretActions {
|
||||
@@ -117,6 +129,7 @@ export enum ProjectPermissionPkiSubscriberActions {
|
||||
|
||||
export enum ProjectPermissionCertificateProfileActions {
|
||||
Read = "read",
|
||||
List = "list",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
@@ -140,6 +153,7 @@ export enum ProjectPermissionPkiSyncActions {
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
List = "list",
|
||||
SyncCertificates = "sync-certificates",
|
||||
ImportCertificates = "import-certificates",
|
||||
RemoveCertificates = "remove-certificates"
|
||||
@@ -242,6 +256,7 @@ export enum ProjectPermissionSub {
|
||||
CertificateAuthorities = "certificate-authorities",
|
||||
Certificates = "certificates",
|
||||
CertificateTemplates = "certificate-templates",
|
||||
CertificateTemplatesV2 = "certificate-templates-v2",
|
||||
SshCertificateAuthorities = "ssh-certificate-authorities",
|
||||
SshCertificates = "ssh-certificates",
|
||||
SshCertificateTemplates = "ssh-certificate-templates",
|
||||
@@ -292,7 +307,7 @@ export type SecretSyncSubjectFields = {
|
||||
};
|
||||
|
||||
export type PkiSyncSubjectFields = {
|
||||
subscriberName: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type DynamicSecretSubjectFields = {
|
||||
@@ -332,6 +347,26 @@ export type PkiSubscriberSubjectFields = {
|
||||
// (dangtony98): consider adding [commonName] as a subject field in the future
|
||||
};
|
||||
|
||||
export type CertificateAuthoritySubjectFields = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type CertificateSubjectFields = {
|
||||
commonName?: string;
|
||||
altNames?: string;
|
||||
serialNumber?: string;
|
||||
friendlyName?: string;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type CertificateProfileSubjectFields = {
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type CertificateTemplateV2SubjectFields = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type AppConnectionSubjectFields = {
|
||||
connectionId: string;
|
||||
};
|
||||
@@ -399,8 +434,17 @@ export type ProjectPermissionSet =
|
||||
ProjectPermissionIdentityActions,
|
||||
ProjectPermissionSub.Identity | (ForcedSubject<ProjectPermissionSub.Identity> & IdentityManagementSubjectFields)
|
||||
]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
|
||||
| [ProjectPermissionCertificateActions, ProjectPermissionSub.Certificates]
|
||||
| [
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
(
|
||||
| ProjectPermissionSub.CertificateAuthorities
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateAuthorities> & CertificateAuthoritySubjectFields)
|
||||
)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionSub.Certificates | (ForcedSubject<ProjectPermissionSub.Certificates> & CertificateSubjectFields)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
(
|
||||
@@ -408,6 +452,13 @@ export type ProjectPermissionSet =
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateTemplates> & PkiTemplateSubjectFields)
|
||||
)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionActions,
|
||||
(
|
||||
| ProjectPermissionSub.CertificateTemplatesV2
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateTemplatesV2> & CertificateTemplateV2SubjectFields)
|
||||
)
|
||||
]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
|
||||
@@ -454,7 +505,13 @@ export type ProjectPermissionSet =
|
||||
ProjectPermissionSub.PamAccounts | (ForcedSubject<ProjectPermissionSub.PamAccounts> & PamAccountSubjectFields)
|
||||
]
|
||||
| [ProjectPermissionPamSessionActions, ProjectPermissionSub.PamSessions]
|
||||
| [ProjectPermissionCertificateProfileActions, ProjectPermissionSub.CertificateProfiles];
|
||||
| [
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
(
|
||||
| ProjectPermissionSub.CertificateProfiles
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateProfiles> & CertificateProfileSubjectFields)
|
||||
)
|
||||
];
|
||||
|
||||
const SECRET_PATH_MISSING_SLASH_ERR_MSG = "Invalid Secret Path; it must start with a '/'";
|
||||
const SECRET_PATH_PERMISSION_OPERATOR_SCHEMA = z.union([
|
||||
@@ -572,7 +629,7 @@ const SecretSyncConditionV2Schema = z
|
||||
|
||||
const PkiSyncConditionSchema = z
|
||||
.object({
|
||||
subscriberName: z.union([
|
||||
name: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
@@ -749,6 +806,113 @@ const PamAccountConditionSchema = z
|
||||
})
|
||||
.partial();
|
||||
|
||||
const CertificateAuthorityConditionSchema = z
|
||||
.object({
|
||||
name: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
])
|
||||
})
|
||||
.partial();
|
||||
|
||||
const CertificateConditionSchema = z
|
||||
.object({
|
||||
commonName: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
]),
|
||||
altNames: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
]),
|
||||
serialNumber: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
]),
|
||||
friendlyName: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
]),
|
||||
status: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN]
|
||||
})
|
||||
.partial()
|
||||
])
|
||||
})
|
||||
.partial();
|
||||
|
||||
const CertificateProfileConditionSchema = z
|
||||
.object({
|
||||
slug: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
])
|
||||
})
|
||||
.partial();
|
||||
|
||||
const CertificateTemplateV2ConditionSchema = z
|
||||
.object({
|
||||
name: z.union([
|
||||
z.string(),
|
||||
z
|
||||
.object({
|
||||
[PermissionConditionOperators.$EQ]: PermissionConditionSchema[PermissionConditionOperators.$EQ],
|
||||
[PermissionConditionOperators.$NEQ]: PermissionConditionSchema[PermissionConditionOperators.$NEQ],
|
||||
[PermissionConditionOperators.$IN]: PermissionConditionSchema[PermissionConditionOperators.$IN],
|
||||
[PermissionConditionOperators.$GLOB]: PermissionConditionSchema[PermissionConditionOperators.$GLOB]
|
||||
})
|
||||
.partial()
|
||||
])
|
||||
})
|
||||
.partial();
|
||||
|
||||
const GeneralPermissionSchema = [
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.SecretApproval).describe("The entity this permission pertains to."),
|
||||
@@ -828,18 +992,6 @@ const GeneralPermissionSchema = [
|
||||
"Describe what action an entity can take."
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.CertificateAuthorities).describe("The entity this permission pertains to."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.Certificates).describe("The entity this permission pertains to."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCertificateActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
)
|
||||
}),
|
||||
z.object({
|
||||
subject: z
|
||||
.literal(ProjectPermissionSub.SshCertificateAuthorities)
|
||||
@@ -1130,7 +1282,40 @@ export const ProjectPermissionV2Schema = z.discriminatedUnion("subject", [
|
||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCertificateProfileActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
)
|
||||
),
|
||||
conditions: CertificateProfileConditionSchema.describe(
|
||||
"When specified, only matching conditions will be allowed to access given resource."
|
||||
).optional()
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.CertificateAuthorities).describe("The entity this permission pertains to."),
|
||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCertificateAuthorityActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
),
|
||||
conditions: CertificateAuthorityConditionSchema.describe(
|
||||
"When specified, only matching conditions will be allowed to access given resource."
|
||||
).optional()
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.Certificates).describe("The entity this permission pertains to."),
|
||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionCertificateActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
),
|
||||
conditions: CertificateConditionSchema.describe(
|
||||
"When specified, only matching conditions will be allowed to access given resource."
|
||||
).optional()
|
||||
}),
|
||||
z.object({
|
||||
subject: z.literal(ProjectPermissionSub.CertificateTemplatesV2).describe("The entity this permission pertains to."),
|
||||
inverted: z.boolean().optional().describe("Whether rule allows or forbids."),
|
||||
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
|
||||
"Describe what action an entity can take."
|
||||
),
|
||||
conditions: CertificateTemplateV2ConditionSchema.describe(
|
||||
"When specified, only matching conditions will be allowed to access given resource."
|
||||
).optional()
|
||||
}),
|
||||
...GeneralPermissionSchema
|
||||
]);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
|
||||
import { ActionProjectType, TableName } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
|
||||
import {
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/ee/services/permission/project-permission";
|
||||
import { BadRequestError, NotFoundError } from "@app/lib/errors";
|
||||
import { OrgServiceActor } from "@app/lib/types";
|
||||
|
||||
@@ -137,8 +140,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Create,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -207,8 +210,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: certificateAuthority.name })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -222,6 +225,7 @@ export const certificateAuthorityServiceFactory = ({
|
||||
id: certificateAuthority.id,
|
||||
type,
|
||||
enableDirectIssuance: certificateAuthority.enableDirectIssuance,
|
||||
subject: ProjectPermissionSub.CertificateAuthorities,
|
||||
name: certificateAuthority.name,
|
||||
projectId: certificateAuthority.projectId,
|
||||
configuration: certificateAuthority.internalCa,
|
||||
@@ -270,8 +274,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: caName })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -323,8 +327,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: "*" })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -378,8 +382,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Edit,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: certificateAuthority.name })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -454,8 +458,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Delete,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: certificateAuthority.name })
|
||||
);
|
||||
|
||||
if (!certificateAuthority.internalCa?.id && type === CaType.INTERNAL) {
|
||||
@@ -519,8 +523,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Edit,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: certificateAuthority.name })
|
||||
);
|
||||
|
||||
if (type === CaType.INTERNAL) {
|
||||
@@ -601,8 +605,8 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Delete,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: certificateAuthority.name })
|
||||
);
|
||||
|
||||
if (!certificateAuthority.internalCa?.id && type === CaType.INTERNAL) {
|
||||
@@ -657,6 +661,13 @@ export const certificateAuthorityServiceFactory = ({
|
||||
actorAuthMethod: OrgServiceActor["authMethod"];
|
||||
actorOrgId?: string;
|
||||
}) => {
|
||||
const certificateAuthority = await certificateAuthorityDAL.findByIdWithAssociatedCa(caId);
|
||||
|
||||
if (!certificateAuthority)
|
||||
throw new NotFoundError({
|
||||
message: `Could not find certificate authority with id "${caId}"`
|
||||
});
|
||||
|
||||
const { permission } = await permissionService.getProjectPermission({
|
||||
actor,
|
||||
actorId,
|
||||
@@ -667,8 +678,10 @@ export const certificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
name: certificateAuthority.name
|
||||
})
|
||||
);
|
||||
|
||||
return azureAdCsFns.getTemplates({
|
||||
|
||||
@@ -7,8 +7,8 @@ import { Knex } from "knex";
|
||||
import { ActionProjectType, TableName, TCertificateAuthorities, TCertificateTemplates } from "@app/db/schemas";
|
||||
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
ProjectPermissionSub
|
||||
@@ -173,8 +173,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Create,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: commonName })
|
||||
);
|
||||
} else {
|
||||
projectId = dto.projectId;
|
||||
@@ -356,8 +356,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
return expandInternalCa(ca);
|
||||
@@ -382,8 +382,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Edit,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -415,8 +415,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Delete,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
await certificateAuthorityDAL.deleteById(ca.id);
|
||||
@@ -441,8 +441,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Create,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
if (ca.internalCa.type === InternalCaType.ROOT)
|
||||
@@ -505,8 +505,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Renew,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
@@ -792,8 +792,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
const caCertChains = await getCaCertChains({
|
||||
@@ -829,8 +829,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
const { caCert, caCertChain, serialNumber } = await getCaCertChain({
|
||||
@@ -910,8 +910,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.SignIntermediate,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
if (ca.status === CaStatus.DISABLED) throw new BadRequestError({ message: "CA is disabled" });
|
||||
@@ -1058,8 +1058,8 @@ export const internalCertificateAuthorityServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
ProjectPermissionCertificateAuthorityActions.Create,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, { name: ca.name })
|
||||
);
|
||||
|
||||
if (ca.internalCa.parentCaId) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
@@ -235,7 +235,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Create,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: data.slug
|
||||
})
|
||||
);
|
||||
|
||||
const project = await projectDAL.findById(projectId);
|
||||
@@ -381,7 +383,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Edit,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: existingProfile.slug
|
||||
})
|
||||
);
|
||||
|
||||
if (data.certificateTemplateId) {
|
||||
@@ -494,7 +498,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
const converted = convertDalToService(profile);
|
||||
@@ -530,7 +536,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
if (profile.estConfig && profile.estConfig.caChain) {
|
||||
@@ -589,7 +597,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug
|
||||
})
|
||||
);
|
||||
|
||||
const profile = await certificateProfileDAL.findBySlugAndProjectId(slug, projectId);
|
||||
@@ -637,7 +647,7 @@ export const certificateProfileServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionCertificateProfileActions.List,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
);
|
||||
|
||||
@@ -740,7 +750,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Delete,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
const deletedProfile = await certificateProfileDAL.deleteById(profileId);
|
||||
@@ -786,7 +798,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
const certificates = await certificateProfileDAL.getCertificatesByProfile(profileId, {
|
||||
@@ -827,17 +841,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
const cert = await certificateProfileDAL.getLatestActiveCertificateForProfile(profileId);
|
||||
@@ -846,6 +852,24 @@ export const certificateProfileServiceFactory = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
const certBody = await certificateBodyDAL.findOne({ certId: cert.id });
|
||||
|
||||
const certificateManagerKeyId = await getProjectKmsCertificateKeyId({
|
||||
@@ -939,7 +963,9 @@ export const certificateProfileServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.Read,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -990,7 +1016,9 @@ export const certificateProfileServiceFactory = ({
|
||||
});
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
if (profile.enrollmentType !== EnrollmentType.ACME) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import slugify from "@sindresorhus/slugify";
|
||||
import RE2 from "re2";
|
||||
|
||||
@@ -77,12 +77,12 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
};
|
||||
|
||||
const validateSubjectAttributePolicy = (
|
||||
subject: Array<{ type: string; allowed?: string[]; required?: string[]; denied?: string[] }>
|
||||
subjectAttributes: Array<{ type: string; allowed?: string[]; required?: string[]; denied?: string[] }>
|
||||
) => {
|
||||
if (!subject || subject.length === 0) return;
|
||||
if (!subjectAttributes || subjectAttributes.length === 0) return;
|
||||
|
||||
// Validate each subject attribute policy
|
||||
for (const attr of subject) {
|
||||
for (const attr of subjectAttributes) {
|
||||
// Ensure at least one field is provided
|
||||
if (!attr.allowed && !attr.required && !attr.denied) {
|
||||
throw new ForbiddenRequestError({
|
||||
@@ -634,7 +634,9 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Create,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: data.name
|
||||
})
|
||||
);
|
||||
|
||||
if (!data) {
|
||||
@@ -711,7 +713,9 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Edit,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: existingTemplate.name
|
||||
})
|
||||
);
|
||||
|
||||
const consolidatedData = {
|
||||
@@ -784,7 +788,9 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Read,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: template.name
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@@ -815,16 +821,17 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
actionProjectType: ActionProjectType.CertificateManager
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Read,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
);
|
||||
|
||||
const template = await certificateTemplateV2DAL.findByNameAndProjectId(slug, projectId);
|
||||
if (!template) {
|
||||
throw new NotFoundError({ message: "Certificate template not found" });
|
||||
}
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: template.name
|
||||
})
|
||||
);
|
||||
return template;
|
||||
};
|
||||
|
||||
@@ -907,7 +914,9 @@ export const certificateTemplateV2ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiTemplateActions.Delete,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: template.name
|
||||
})
|
||||
);
|
||||
|
||||
const isInUse = await certificateTemplateV2DAL.isTemplateInUse(templateId);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
import { randomUUID } from "crypto";
|
||||
import RE2 from "re2";
|
||||
@@ -155,7 +155,9 @@ const validateProfileAndPermissions = async (
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.IssueCert,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, {
|
||||
slug: profile.slug
|
||||
})
|
||||
);
|
||||
|
||||
return profile;
|
||||
@@ -1275,7 +1277,7 @@ export const certificateV3ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateProfileActions.IssueCert,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
subject(ProjectPermissionSub.CertificateProfiles, { slug: profile?.slug || "*" })
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1568,7 +1570,11 @@ export const certificateV3ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Edit,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames ?? undefined,
|
||||
serialNumber: certificate.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
if (!certificate.profileId) {
|
||||
@@ -1671,7 +1677,11 @@ export const certificateV3ServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Edit,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames ?? undefined,
|
||||
serialNumber: certificate.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
if (!certificate.profileId) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import { ForbiddenError } from "@casl/ability";
|
||||
import { ForbiddenError, subject } from "@casl/ability";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
|
||||
import { ActionProjectType } from "@app/db/schemas";
|
||||
@@ -108,7 +108,11 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -140,7 +144,11 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
const { certPrivateKey } = await getCertificateCredentials({
|
||||
@@ -174,7 +182,11 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
const deletedCert = await certificateDAL.deleteById(cert.id);
|
||||
@@ -234,7 +246,13 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Delete,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber,
|
||||
friendlyName: cert.friendlyName,
|
||||
status: cert.status
|
||||
})
|
||||
);
|
||||
|
||||
if (cert.status === CertStatus.REVOKED) throw new Error("Certificate already revoked");
|
||||
@@ -309,7 +327,11 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber
|
||||
})
|
||||
);
|
||||
|
||||
const certBody = await certificateBodyDAL.findOne({ certId: cert.id });
|
||||
@@ -397,7 +419,7 @@ export const certificateServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Create,
|
||||
ProjectPermissionCertificateActions.Import,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
@@ -610,11 +632,23 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber,
|
||||
friendlyName: cert.friendlyName,
|
||||
status: cert.status
|
||||
})
|
||||
);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber,
|
||||
friendlyName: cert.friendlyName,
|
||||
status: cert.status
|
||||
})
|
||||
);
|
||||
|
||||
const certBody = await certificateBodyDAL.findOne({ certId: cert.id });
|
||||
@@ -726,7 +760,13 @@ export const certificateServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.ReadPrivateKey,
|
||||
ProjectPermissionSub.Certificates
|
||||
subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: cert.commonName,
|
||||
altNames: cert.altNames ?? undefined,
|
||||
serialNumber: cert.serialNumber,
|
||||
friendlyName: cert.friendlyName,
|
||||
status: cert.status
|
||||
})
|
||||
);
|
||||
|
||||
// Get certificate bundle (certificate, chain, private key)
|
||||
|
||||
@@ -145,9 +145,7 @@ export const pkiSyncServiceFactory = ({
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Create,
|
||||
subscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: subscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
subject(ProjectPermissionSub.PkiSyncs, { name: subscriber ? subscriber.name : name })
|
||||
);
|
||||
|
||||
// Get the destination app type based on PKI sync destination
|
||||
@@ -236,8 +234,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Edit,
|
||||
currentSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: currentSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: currentSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
if (name && name !== pkiSync.name) {
|
||||
@@ -332,8 +330,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Delete,
|
||||
pkiSyncSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: pkiSyncSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: pkiSyncSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
return pkiSyncDAL.deleteById(id);
|
||||
@@ -352,7 +350,7 @@ export const pkiSyncServiceFactory = ({
|
||||
projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.List, ProjectPermissionSub.PkiSyncs);
|
||||
|
||||
const pkiSyncsWithSubscribers = await pkiSyncDAL.findByProjectIdWithSubscribers(projectId);
|
||||
|
||||
@@ -407,8 +405,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Read,
|
||||
findSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: findSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: findSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
const result = {
|
||||
@@ -443,8 +441,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.SyncCertificates,
|
||||
syncSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: syncSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: syncSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
await pkiSyncQueue.queuePkiSyncSyncCertificatesById({ syncId: id });
|
||||
@@ -484,8 +482,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.ImportCertificates,
|
||||
importSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: importSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: importSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
await pkiSyncQueue.queuePkiSyncImportCertificatesById({ syncId: id });
|
||||
@@ -517,8 +515,8 @@ export const pkiSyncServiceFactory = ({
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.RemoveCertificates,
|
||||
removeSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { subscriberName: removeSubscriber.name })
|
||||
: ProjectPermissionSub.PkiSyncs
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: removeSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
await pkiSyncQueue.queuePkiSyncRemoveCertificatesById({ syncId: id });
|
||||
@@ -549,7 +547,17 @@ export const pkiSyncServiceFactory = ({
|
||||
projectId: pkiSync.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Edit, ProjectPermissionSub.PkiSyncs);
|
||||
let pkiSyncSubscriber;
|
||||
if (pkiSync.subscriberId) {
|
||||
pkiSyncSubscriber = await pkiSubscriberDAL.findById(pkiSync.subscriberId);
|
||||
}
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Edit,
|
||||
pkiSyncSubscriber
|
||||
? subject(ProjectPermissionSub.PkiSyncs, { name: pkiSyncSubscriber.name })
|
||||
: subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
await validateCertificatesProjectOwnership(certificateIds, pkiSync.projectId);
|
||||
|
||||
@@ -588,7 +596,10 @@ export const pkiSyncServiceFactory = ({
|
||||
projectId: pkiSync.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Edit, ProjectPermissionSub.PkiSyncs);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Edit,
|
||||
subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
const removedCount = await certificateSyncDAL.removeCertificates(pkiSyncId, certificateIds);
|
||||
|
||||
@@ -626,7 +637,10 @@ export const pkiSyncServiceFactory = ({
|
||||
projectId: pkiSync.projectId
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionPkiSyncActions.Read, ProjectPermissionSub.PkiSyncs);
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionPkiSyncActions.Read,
|
||||
subject(ProjectPermissionSub.PkiSyncs, { name: pkiSync.name })
|
||||
);
|
||||
|
||||
const result = await certificateSyncDAL.findWithDetails({
|
||||
pkiSyncId,
|
||||
|
||||
@@ -20,6 +20,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionMemberActions,
|
||||
ProjectPermissionPkiSubscriberActions,
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
@@ -911,7 +912,7 @@ export const projectServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionActions.Read,
|
||||
ProjectPermissionCertificateAuthorityActions.List,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
@@ -959,7 +960,7 @@ export const projectServiceFactory = ({
|
||||
});
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(
|
||||
ProjectPermissionCertificateActions.Read,
|
||||
ProjectPermissionCertificateActions.List,
|
||||
ProjectPermissionSub.Certificates
|
||||
);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ export {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionAuditLogsActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionCmekActions,
|
||||
ProjectPermissionDynamicSecretActions,
|
||||
|
||||
@@ -12,7 +12,9 @@ export enum ProjectPermissionCertificateActions {
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
ReadPrivateKey = "read-private-key"
|
||||
List = "list",
|
||||
ReadPrivateKey = "read-private-key",
|
||||
Import = "import"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionSecretActions {
|
||||
@@ -67,6 +69,7 @@ export enum ProjectPermissionPkiSyncActions {
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
List = "list",
|
||||
SyncCertificates = "sync-certificates",
|
||||
ImportCertificates = "import-certificates",
|
||||
RemoveCertificates = "remove-certificates"
|
||||
@@ -128,13 +131,25 @@ export enum ProjectPermissionPkiTemplateActions {
|
||||
ListCerts = "list-certs"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionCertificateProfileActions {
|
||||
export enum ProjectPermissionCertificateAuthorityActions {
|
||||
Read = "read",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
List = "list",
|
||||
Renew = "renew",
|
||||
SignIntermediate = "sign-intermediate"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionCertificateProfileActions {
|
||||
Read = "read",
|
||||
List = "list",
|
||||
Create = "create",
|
||||
Edit = "edit",
|
||||
Delete = "delete",
|
||||
IssueCert = "issue-cert",
|
||||
RevealAcmeEabSecret = "reveal-acme-eab-secret"
|
||||
RevealAcmeEabSecret = "reveal-acme-eab-secret",
|
||||
RotateAcmeEabSecret = "rotate-acme-eab-secret"
|
||||
}
|
||||
|
||||
export enum ProjectPermissionSecretRotationActions {
|
||||
@@ -230,6 +245,9 @@ export type ConditionalProjectPermissionSubject =
|
||||
| ProjectPermissionSub.SshHosts
|
||||
| ProjectPermissionSub.PkiSubscribers
|
||||
| ProjectPermissionSub.CertificateTemplates
|
||||
| ProjectPermissionSub.CertificateAuthorities
|
||||
| ProjectPermissionSub.Certificates
|
||||
| ProjectPermissionSub.CertificateProfiles
|
||||
| ProjectPermissionSub.SecretFolders
|
||||
| ProjectPermissionSub.SecretImports
|
||||
| ProjectPermissionSub.SecretRotation
|
||||
@@ -361,7 +379,24 @@ export type SecretSyncSubjectFields = {
|
||||
};
|
||||
|
||||
export type PkiSyncSubjectFields = {
|
||||
subscriberId: string;
|
||||
subscriberName: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type CertificateAuthoritySubjectFields = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
export type CertificateSubjectFields = {
|
||||
commonName?: string;
|
||||
altNames?: string;
|
||||
serialNumber?: string;
|
||||
friendlyName?: string;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
export type CertificateProfileSubjectFields = {
|
||||
slug: string;
|
||||
};
|
||||
|
||||
export type SecretRotationSubjectFields = {
|
||||
@@ -457,8 +492,21 @@ export type ProjectPermissionSet =
|
||||
| (ForcedSubject<ProjectPermissionSub.Identity> & IdentityManagementSubjectFields)
|
||||
)
|
||||
]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
|
||||
| [ProjectPermissionCertificateActions, ProjectPermissionSub.Certificates]
|
||||
| [
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
(
|
||||
| ProjectPermissionSub.CertificateAuthorities
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateAuthorities> &
|
||||
CertificateAuthoritySubjectFields)
|
||||
)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionCertificateActions,
|
||||
(
|
||||
| ProjectPermissionSub.Certificates
|
||||
| (ForcedSubject<ProjectPermissionSub.Certificates> & CertificateSubjectFields)
|
||||
)
|
||||
]
|
||||
| [
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
(
|
||||
@@ -484,7 +532,14 @@ export type ProjectPermissionSet =
|
||||
| (ForcedSubject<ProjectPermissionSub.PkiSubscribers> & PkiSubscriberSubjectFields)
|
||||
)
|
||||
]
|
||||
| [ProjectPermissionCertificateProfileActions, ProjectPermissionSub.CertificateProfiles]
|
||||
| [
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
(
|
||||
| ProjectPermissionSub.CertificateProfiles
|
||||
| (ForcedSubject<ProjectPermissionSub.CertificateProfiles> &
|
||||
CertificateProfileSubjectFields)
|
||||
)
|
||||
]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
|
||||
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
|
||||
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
|
||||
|
||||
@@ -15,6 +15,7 @@ export {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionAuditLogsActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionCmekActions,
|
||||
ProjectPermissionDynamicSecretActions,
|
||||
|
||||
@@ -72,7 +72,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.SIGN_INTERMEDIATE]: "Sign intermediate",
|
||||
[EventType.IMPORT_CA_CERT]: "Import CA certificate",
|
||||
[EventType.GET_CA_CRL]: "Get CA CRL",
|
||||
[EventType.ISSUE_CERT]: "Issue certificate",
|
||||
[EventType.ISSUE_CERT]: "Request certificate",
|
||||
[EventType.IMPORT_CERT]: "Import certificate",
|
||||
[EventType.GET_CERT]: "Get certificate",
|
||||
[EventType.DELETE_CERT]: "Delete certificate",
|
||||
@@ -225,7 +225,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.UPDATE_PKI_SUBSCRIBER]: "Update PKI subscriber",
|
||||
[EventType.DELETE_PKI_SUBSCRIBER]: "Delete PKI subscriber",
|
||||
[EventType.GET_PKI_SUBSCRIBER]: "Get PKI subscriber",
|
||||
[EventType.ISSUE_PKI_SUBSCRIBER_CERT]: "Issue PKI subscriber certificate",
|
||||
[EventType.ISSUE_PKI_SUBSCRIBER_CERT]: "Request PKI subscriber certificate",
|
||||
[EventType.SIGN_PKI_SUBSCRIBER_CERT]: "Sign PKI subscriber certificate",
|
||||
[EventType.AUTOMATED_RENEW_SUBSCRIBER_CERT]: "Automated renew PKI subscriber certificate",
|
||||
[EventType.LIST_PKI_SUBSCRIBER_CERTS]: "List PKI subscriber certificates",
|
||||
@@ -287,7 +287,7 @@ export const eventToNameMap: { [K in EventType]: string } = {
|
||||
[EventType.DELETE_CERTIFICATE_PROFILE]: "Delete Certificate Profile",
|
||||
[EventType.GET_CERTIFICATE_PROFILE]: "Get Certificate Profile",
|
||||
[EventType.LIST_CERTIFICATE_PROFILES]: "List Certificate Profiles",
|
||||
[EventType.ISSUE_CERTIFICATE_FROM_PROFILE]: "Issue Certificate From Profile",
|
||||
[EventType.ISSUE_CERTIFICATE_FROM_PROFILE]: "Request Certificate From Profile",
|
||||
[EventType.SIGN_CERTIFICATE_FROM_PROFILE]: "Sign Certificate From Profile",
|
||||
[EventType.ORDER_CERTIFICATE_FROM_PROFILE]: "Order Certificate From Profile",
|
||||
[EventType.GET_CERTIFICATE_PROFILE_LATEST_ACTIVE_BUNDLE]:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { subject } from "@casl/ability";
|
||||
import { faChevronLeft } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { Link, useNavigate, useParams } from "@tanstack/react-router";
|
||||
@@ -7,6 +8,7 @@ import { twMerge } from "tailwind-merge";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
AccessRestrictedBanner,
|
||||
Button,
|
||||
DeleteActionModal,
|
||||
DropdownMenu,
|
||||
@@ -18,7 +20,7 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub,
|
||||
useOrganization,
|
||||
useProject
|
||||
@@ -88,63 +90,82 @@ const Page = () => {
|
||||
return (
|
||||
<div className="mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
{data && (
|
||||
<div className="mx-auto mb-6 w-full max-w-8xl">
|
||||
<Link
|
||||
to="/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities"
|
||||
params={{
|
||||
orgId: currentOrg.id,
|
||||
projectId
|
||||
}}
|
||||
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
|
||||
>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
Certificate Authorities
|
||||
</Link>
|
||||
<PageHeader
|
||||
scope={ProjectType.CertificateManager}
|
||||
description="Manage certificate authority"
|
||||
title={data.name}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||
<Tooltip content="More options">
|
||||
<Button variant="outline_bg">More</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="p-1">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateAuthorityActions.Read}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: data.id,
|
||||
name: data.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed ? (
|
||||
<div className="mx-auto mb-6 w-full max-w-8xl">
|
||||
<Link
|
||||
to="/organizations/$orgId/projects/cert-management/$projectId/certificate-authorities"
|
||||
params={{
|
||||
orgId: currentOrg.id,
|
||||
projectId
|
||||
}}
|
||||
className="mb-4 flex items-center gap-x-2 text-sm text-mineshaft-400"
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
isAllowed
|
||||
? "hover:bg-red-500! hover:text-white!"
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={() => handlePopUpOpen("deleteCa")}
|
||||
disabled={!isAllowed}
|
||||
>
|
||||
Delete CA
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageHeader>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<CaDetailsSection caId={data.id} handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<CaCertificatesSection caId={data.id} />
|
||||
<CaCrlsSection caId={data.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<FontAwesomeIcon icon={faChevronLeft} />
|
||||
Certificate Authorities
|
||||
</Link>
|
||||
<PageHeader
|
||||
scope={ProjectType.CertificateManager}
|
||||
description="Manage certificate authority"
|
||||
title={data.name}
|
||||
>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild className="rounded-lg">
|
||||
<div className="hover:text-primary-400 data-[state=open]:text-primary-400">
|
||||
<Tooltip content="More options">
|
||||
<Button variant="outline_bg">More</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="p-1">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateAuthorityActions.Delete}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: data.id,
|
||||
name: data.name
|
||||
})}
|
||||
>
|
||||
{(canDelete) => (
|
||||
<DropdownMenuItem
|
||||
className={twMerge(
|
||||
canDelete
|
||||
? "hover:bg-red-500! hover:text-white!"
|
||||
: "pointer-events-none cursor-not-allowed opacity-50"
|
||||
)}
|
||||
onClick={() => handlePopUpOpen("deleteCa")}
|
||||
disabled={!canDelete}
|
||||
>
|
||||
Delete CA
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</PageHeader>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<CaDetailsSection caId={data.id} handlePopUpOpen={handlePopUpOpen} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<CaCertificatesSection caId={data.id} caName={data.name} />
|
||||
<CaCrlsSection caId={data.id} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="container mx-auto flex h-full items-center justify-center">
|
||||
<AccessRestrictedBanner />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<CaModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
<CaRenewalModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
|
||||
|
||||
@@ -2,9 +2,10 @@ import { CaCertificatesTable } from "./CaCertificatesTable";
|
||||
|
||||
type Props = {
|
||||
caId: string;
|
||||
caName: string;
|
||||
};
|
||||
|
||||
export const CaCertificatesSection = ({ caId }: Props) => {
|
||||
export const CaCertificatesSection = ({ caId, caName }: Props) => {
|
||||
return (
|
||||
<div className="w-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
@@ -21,7 +22,7 @@ export const CaCertificatesSection = ({ caId }: Props) => {
|
||||
</IconButton> */}
|
||||
</div>
|
||||
<div className="py-4">
|
||||
<CaCertificatesTable caId={caId} />
|
||||
<CaCertificatesTable caId={caId} caName={caName} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import { faCertificate, faEllipsis } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import * as x509 from "@peculiar/x509";
|
||||
@@ -22,14 +23,15 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionCertificateAuthorityActions, ProjectPermissionSub } from "@app/context";
|
||||
import { useGetCaCerts } from "@app/hooks/api";
|
||||
|
||||
type Props = {
|
||||
caId: string;
|
||||
caName: string;
|
||||
};
|
||||
|
||||
export const CaCertificatesTable = ({ caId }: Props) => {
|
||||
export const CaCertificatesTable = ({ caId, caName }: Props) => {
|
||||
const { data: caCerts, isPending } = useGetCaCerts(caId);
|
||||
|
||||
const downloadTxtFile = (filename: string, content: string) => {
|
||||
@@ -77,8 +79,10 @@ export const CaCertificatesTable = ({ caId }: Props) => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.Identity}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Read}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
name: caName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -96,8 +100,10 @@ export const CaCertificatesTable = ({ caId }: Props) => {
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={ProjectPermissionSub.Identity}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Read}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
name: caName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import { faCheck, faCopy, faPencil } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { format } from "date-fns";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, IconButton, Tooltip } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionCertificateAuthorityActions, ProjectPermissionSub } from "@app/context";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { CaStatus, CaType, InternalCaType, useGetCa } from "@app/hooks/api";
|
||||
import { caStatusToNameMap, caTypeToNameMap } from "@app/hooks/api/ca/constants";
|
||||
@@ -39,7 +40,10 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
|
||||
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-medium text-mineshaft-100">CA Details</h3>
|
||||
<ProjectPermissionCan I={ProjectPermissionActions.Edit} a={ProjectPermissionSub.Identity}>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateAuthorityActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, { caId: ca.id, name: ca.name })}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
<Tooltip content="Edit CA">
|
||||
@@ -154,8 +158,8 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
|
||||
</div>
|
||||
{ca.status === CaStatus.ACTIVE && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Renew}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, { caId: ca.id, name: ca.name })}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
@@ -190,8 +194,8 @@ export const CaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
|
||||
)}
|
||||
{ca.status === CaStatus.PENDING_CERTIFICATE && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Create}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, { caId: ca.id, name: ca.name })}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
|
||||
@@ -4,7 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub,
|
||||
useProject
|
||||
} from "@app/context";
|
||||
import { CaStatus, CaType, useDeleteCa, useUpdateCa } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -57,7 +61,7 @@ export const CaSection = () => {
|
||||
<div className="mb-4 flex justify-between">
|
||||
<p className="text-xl font-medium text-mineshaft-100">Internal Certificate Authorities</p>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Create}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import { faBan, faCertificate, faEllipsis, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
@@ -23,10 +24,11 @@ import {
|
||||
} from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub,
|
||||
useOrganization,
|
||||
useProject
|
||||
useProject,
|
||||
useProjectPermission
|
||||
} from "@app/context";
|
||||
import { CaStatus, CaType, useListCasByTypeAndProjectId } from "@app/hooks/api";
|
||||
import {
|
||||
@@ -53,6 +55,7 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
const navigate = useNavigate();
|
||||
const { currentOrg } = useOrganization();
|
||||
const { currentProject } = useProject();
|
||||
const { permission } = useProjectPermission();
|
||||
const { data, isPending } = useListCasByTypeAndProjectId(CaType.INTERNAL, currentProject.id);
|
||||
const cas = data as TInternalCertificateAuthority[];
|
||||
|
||||
@@ -75,11 +78,24 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
cas &&
|
||||
cas.length > 0 &&
|
||||
cas.map((ca) => {
|
||||
const canReadCa = permission.can(
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
className={twMerge(
|
||||
"h-10 transition-colors duration-100",
|
||||
canReadCa && "cursor-pointer hover:bg-mineshaft-700",
|
||||
!canReadCa && "cursor-not-allowed opacity-60"
|
||||
)}
|
||||
key={`ca-${ca.id}`}
|
||||
onClick={() =>
|
||||
canReadCa &&
|
||||
navigate({
|
||||
to: "/organizations/$orgId/projects/cert-management/$projectId/ca/$caId",
|
||||
params: {
|
||||
@@ -118,8 +134,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
{ca.status === CaStatus.PENDING_CERTIFICATE && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Create}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -143,8 +162,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
)}
|
||||
{ca.status !== CaStatus.PENDING_CERTIFICATE && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Read}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Read}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -168,8 +190,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
)}
|
||||
{(ca.status === CaStatus.ACTIVE || ca.status === CaStatus.DISABLED) && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -196,8 +221,11 @@ export const CaTable = ({ handlePopUpOpen }: Props) => {
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Delete}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
|
||||
@@ -4,7 +4,11 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub,
|
||||
useProject
|
||||
} from "@app/context";
|
||||
import { CaStatus, CaType, useDeleteCa, useUpdateCa } from "@app/hooks/api";
|
||||
import { usePopUp } from "@app/hooks/usePopUp";
|
||||
|
||||
@@ -61,7 +65,7 @@ export const ExternalCaSection = () => {
|
||||
<div className="mb-4 flex justify-between">
|
||||
<p className="text-xl font-medium text-mineshaft-100">External Certificate Authorities</p>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Create}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Create}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faBan,
|
||||
faCertificate,
|
||||
@@ -26,7 +27,12 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionActions, ProjectPermissionSub, useProject } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionSub,
|
||||
useProject,
|
||||
useProjectPermission
|
||||
} from "@app/context";
|
||||
import { CaStatus, CaType, useListExternalCasByProjectId } from "@app/hooks/api";
|
||||
import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
@@ -45,6 +51,7 @@ type Props = {
|
||||
|
||||
export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
||||
const { currentProject } = useProject();
|
||||
const { permission } = useProjectPermission();
|
||||
const { data, isPending } = useListExternalCasByProjectId(currentProject.id);
|
||||
|
||||
return (
|
||||
@@ -65,17 +72,30 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
||||
data &&
|
||||
data.length > 0 &&
|
||||
data.map((ca) => {
|
||||
const canReadCa = permission.can(
|
||||
ProjectPermissionCertificateAuthorityActions.Read,
|
||||
subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<Tr
|
||||
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
|
||||
className={twMerge(
|
||||
"h-10 transition-colors duration-100",
|
||||
canReadCa && "cursor-pointer hover:bg-mineshaft-700",
|
||||
!canReadCa && "cursor-not-allowed opacity-60"
|
||||
)}
|
||||
key={`ca-${ca.id}`}
|
||||
onClick={() => {
|
||||
onClick={() =>
|
||||
canReadCa &&
|
||||
handlePopUpOpen("ca", {
|
||||
caId: ca.id,
|
||||
name: ca.name,
|
||||
type: ca.type
|
||||
});
|
||||
}}
|
||||
})
|
||||
}
|
||||
>
|
||||
<Td>{ca.name}</Td>
|
||||
<Td>{ca.type}</Td>
|
||||
@@ -95,8 +115,11 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -120,8 +143,11 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
||||
</ProjectPermissionCan>
|
||||
{(ca.status === CaStatus.ACTIVE || ca.status === CaStatus.DISABLED) && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Edit}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -149,8 +175,11 @@ export const ExternalCaTable = ({ handlePopUpOpen }: Props) => {
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionActions.Delete}
|
||||
a={ProjectPermissionSub.CertificateAuthorities}
|
||||
I={ProjectPermissionCertificateAuthorityActions.Delete}
|
||||
a={subject(ProjectPermissionSub.CertificateAuthorities, {
|
||||
caId: ca.id,
|
||||
name: ca.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
|
||||
@@ -321,13 +321,13 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
|
||||
const getModalTitle = () => {
|
||||
if (certificateDetails) return "Certificate Created Successfully";
|
||||
if (cert) return "Certificate Details";
|
||||
return "Issue New Certificate";
|
||||
return "Request New Certificate";
|
||||
};
|
||||
|
||||
const getModalSubTitle = () => {
|
||||
if (certificateDetails) return "Certificate has been successfully created and is ready for use";
|
||||
if (cert) return "View certificate information";
|
||||
return "Issue a new certificate using a certificate profile";
|
||||
return "Request a new certificate using a certificate profile";
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -498,7 +498,7 @@ export const CertificateIssuanceModal = ({ popUp, handlePopUpToggle, profileId }
|
||||
isLoading={isSubmitting}
|
||||
isDisabled={isSubmitting || (!actualSelectedProfile && !profileId)}
|
||||
>
|
||||
{cert ? "Update" : "Issue Certificate"}
|
||||
{cert ? "Update" : "Request Certificate"}
|
||||
</Button>
|
||||
<Button
|
||||
colorSchema="secondary"
|
||||
|
||||
@@ -241,7 +241,7 @@ export const CertificateModal = ({ popUp, handlePopUpToggle, preselectedTemplate
|
||||
setCertificateDetails(null);
|
||||
}}
|
||||
>
|
||||
<ModalContent title={`${cert ? "View" : "Issue"} Certificate`}>
|
||||
<ModalContent title={`${cert ? "View" : "Request"} Certificate`}>
|
||||
{!certificateDetails ? (
|
||||
<form onSubmit={handleSubmit(onFormSubmit)}>
|
||||
<Controller
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faBan,
|
||||
faCertificate,
|
||||
@@ -268,7 +269,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Read}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -291,7 +297,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
{isLegacyTemplatesEnabled && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Read}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -327,7 +338,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
return (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Edit}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => {
|
||||
return (
|
||||
@@ -391,7 +407,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
return (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Edit}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -428,7 +449,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
return (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Edit}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -456,7 +482,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
!certificate.renewedByCertificateId && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Edit}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -492,7 +523,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
return (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Delete}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
@@ -516,7 +552,12 @@ export const CertificatesTable = ({ handlePopUpOpen }: Props) => {
|
||||
})()}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateActions.Delete}
|
||||
a={ProjectPermissionSub.Certificates}
|
||||
a={subject(ProjectPermissionSub.Certificates, {
|
||||
commonName: certificate.commonName,
|
||||
altNames: certificate.altNames,
|
||||
serialNumber: certificate.serialNumber,
|
||||
friendlyName: certificate.friendlyName
|
||||
})}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<DropdownMenuItem
|
||||
|
||||
@@ -117,263 +117,287 @@ export const PkiSyncRow = ({
|
||||
const destinationDetails = PKI_SYNC_MAP[destination];
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriberId || ""
|
||||
subscriberId: subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name
|
||||
});
|
||||
|
||||
return (
|
||||
<Tr
|
||||
onClick={() => {
|
||||
// console.log("PKI Sync navigation:", { syncId: id, projectId });
|
||||
navigate({
|
||||
to: ROUTE_PATHS.CertManager.PkiSyncDetailsByIDPage.path,
|
||||
params: {
|
||||
syncId: id,
|
||||
projectId,
|
||||
orgId: currentOrg.id
|
||||
}
|
||||
});
|
||||
}}
|
||||
className={twMerge(
|
||||
"group h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700",
|
||||
syncStatus === PkiSyncStatus.Failed && "bg-red/5 hover:bg-red/10"
|
||||
)}
|
||||
key={`sync-${id}`}
|
||||
>
|
||||
<Td>
|
||||
<img
|
||||
alt={`${destinationDetails.name} sync`}
|
||||
src={`/images/integrations/${destinationDetails.image}`}
|
||||
className="min-w-7"
|
||||
/>
|
||||
</Td>
|
||||
<Td className="max-w-0 min-w-32!">
|
||||
<div>
|
||||
<div className="flex w-full items-center">
|
||||
<p className="truncate">{name}</p>
|
||||
{description && (
|
||||
<Tooltip content={description}>
|
||||
<FontAwesomeIcon
|
||||
icon={faInfoCircle}
|
||||
size="xs"
|
||||
className="ml-1 text-mineshaft-400"
|
||||
/>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
<p className="truncate text-xs leading-4 text-bunker-300">{destinationDetails.name}</p>
|
||||
</div>
|
||||
</Td>
|
||||
<PkiSyncDestinationCol pkiSync={pkiSync} />
|
||||
<Td>
|
||||
<div className="flex items-center gap-1">
|
||||
{syncStatus && (
|
||||
<Tooltip
|
||||
position="left"
|
||||
className="max-w-sm"
|
||||
content={
|
||||
[PkiSyncStatus.Succeeded, PkiSyncStatus.Failed].includes(syncStatus) ? (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{lastSyncedAt && (
|
||||
<div>
|
||||
<div
|
||||
className={`mb-2 flex self-start ${syncStatus === PkiSyncStatus.Failed ? "text-yellow" : "text-green"}`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faCalendarCheck}
|
||||
className="ml-1 pt-0.5 pr-1.5 text-sm"
|
||||
/>
|
||||
<div className="text-xs">Last Synced</div>
|
||||
</div>
|
||||
<div className="rounded-sm bg-mineshaft-600 p-2 text-xs">
|
||||
{format(new Date(lastSyncedAt), "yyyy-MM-dd, hh:mm aaa")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{failureMessage && (
|
||||
<div>
|
||||
<div className="mb-2 flex self-start text-red">
|
||||
<FontAwesomeIcon icon={faXmark} className="ml-1 pt-0.5 pr-1.5 text-sm" />
|
||||
<div className="text-xs">Failure Reason</div>
|
||||
</div>
|
||||
<div className="rounded-sm bg-mineshaft-600 p-2 text-xs break-words">
|
||||
{failureMessage}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : undefined
|
||||
<ProjectPermissionCan I={ProjectPermissionPkiSyncActions.Read} a={permissionSubject}>
|
||||
{(isAllowed: boolean) => {
|
||||
return (
|
||||
<Tr
|
||||
onClick={() => {
|
||||
if (!isAllowed) {
|
||||
return;
|
||||
}
|
||||
>
|
||||
// console.log("PKI Sync navigation:", { syncId: id, projectId });
|
||||
navigate({
|
||||
to: ROUTE_PATHS.CertManager.PkiSyncDetailsByIDPage.path,
|
||||
params: {
|
||||
syncId: id,
|
||||
projectId,
|
||||
orgId: currentOrg.id
|
||||
}
|
||||
});
|
||||
}}
|
||||
className={twMerge(
|
||||
"group h-10 transition-colors duration-100 hover:bg-mineshaft-700",
|
||||
syncStatus === PkiSyncStatus.Failed && "bg-red/5 hover:bg-red/10",
|
||||
isAllowed ? "cursor-pointer" : "cursor-not-allowed"
|
||||
)}
|
||||
key={`sync-${id}`}
|
||||
>
|
||||
<Td>
|
||||
<img
|
||||
alt={`${destinationDetails.name} sync`}
|
||||
src={`/images/integrations/${destinationDetails.image}`}
|
||||
className="min-w-7"
|
||||
/>
|
||||
</Td>
|
||||
<Td className="max-w-0 min-w-32!">
|
||||
<div>
|
||||
<PkiSyncStatusBadge status={syncStatus} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
{!isAutoSyncEnabled && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="Auto-Sync is disabled. Certificate changes in the PKI subscriber will not be automatically synced to the destination."
|
||||
>
|
||||
<Badge variant="neutral">
|
||||
<BanIcon />
|
||||
{!syncStatus && "Auto-Sync Disabled"}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
)}
|
||||
{syncOption?.canImportCertificates && <PkiSyncImportStatusBadge mini pkiSync={pkiSync} />}
|
||||
<PkiSyncRemoveStatusBadge mini pkiSync={pkiSync} />
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<Tooltip className="max-w-sm text-center" content="Options">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton
|
||||
ariaLabel="Options"
|
||||
colorSchema="secondary"
|
||||
className="w-6"
|
||||
variant="plain"
|
||||
>
|
||||
<FontAwesomeIcon icon={faEllipsisV} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={2} align="end">
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={isIdCopied ? faCheck : faCopy} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleCopyId();
|
||||
}}
|
||||
>
|
||||
Copy Sync ID
|
||||
</DropdownMenuItem>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.SyncCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faRotate} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerSyncCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Manually trigger a sync for this ${destinationName} destination.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span> Trigger Sync</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-full items-center">
|
||||
<p className="truncate">{name}</p>
|
||||
{description && (
|
||||
<Tooltip content={description}>
|
||||
<FontAwesomeIcon
|
||||
icon={faInfoCircle}
|
||||
size="xs"
|
||||
className="ml-1 text-mineshaft-400"
|
||||
/>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</div>
|
||||
<p className="truncate text-xs leading-4 text-bunker-300">
|
||||
{destinationDetails.name}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<PkiSyncDestinationCol pkiSync={pkiSync} />
|
||||
<Td>
|
||||
<div className="flex items-center gap-1">
|
||||
{syncStatus && (
|
||||
<Tooltip
|
||||
position="left"
|
||||
className="max-w-sm"
|
||||
content={
|
||||
[PkiSyncStatus.Succeeded, PkiSyncStatus.Failed].includes(syncStatus) ? (
|
||||
<div className="flex flex-col gap-2 py-1 whitespace-normal">
|
||||
{lastSyncedAt && (
|
||||
<div>
|
||||
<div
|
||||
className={`mb-2 flex self-start ${syncStatus === PkiSyncStatus.Failed ? "text-yellow" : "text-green"}`}
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faCalendarCheck}
|
||||
className="ml-1 pt-0.5 pr-1.5 text-sm"
|
||||
/>
|
||||
<div className="text-xs">Last Synced</div>
|
||||
</div>
|
||||
<div className="rounded-sm bg-mineshaft-600 p-2 text-xs">
|
||||
{format(new Date(lastSyncedAt), "yyyy-MM-dd, hh:mm aaa")}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{failureMessage && (
|
||||
<div>
|
||||
<div className="mb-2 flex self-start text-red">
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="ml-1 pt-0.5 pr-1.5 text-sm"
|
||||
/>
|
||||
<div className="text-xs">Failure Reason</div>
|
||||
</div>
|
||||
<div className="rounded-sm bg-mineshaft-600 p-2 text-xs break-words">
|
||||
{failureMessage}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<PkiSyncStatusBadge status={syncStatus} />
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
{syncOption?.canImportCertificates && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
{!isAutoSyncEnabled && (
|
||||
<Tooltip
|
||||
className="text-xs"
|
||||
content="Auto-Sync is disabled. Certificate changes in the PKI subscriber will not be automatically synced to the destination."
|
||||
>
|
||||
<Badge variant="neutral">
|
||||
<BanIcon />
|
||||
{!syncStatus && "Auto-Sync Disabled"}
|
||||
</Badge>
|
||||
</Tooltip>
|
||||
)}
|
||||
{syncOption?.canImportCertificates && (
|
||||
<PkiSyncImportStatusBadge mini pkiSync={pkiSync} />
|
||||
)}
|
||||
<PkiSyncRemoveStatusBadge mini pkiSync={pkiSync} />
|
||||
</div>
|
||||
</Td>
|
||||
<Td>
|
||||
<Tooltip className="max-w-sm text-center" content="Options">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<IconButton
|
||||
ariaLabel="Options"
|
||||
colorSchema="secondary"
|
||||
className="w-6"
|
||||
variant="plain"
|
||||
>
|
||||
<FontAwesomeIcon icon={faEllipsisV} />
|
||||
</IconButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent sideOffset={2} align="end">
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
icon={<FontAwesomeIcon icon={isIdCopied ? faCheck : faCopy} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerImportCertificates(pkiSync);
|
||||
handleCopyId();
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
Copy Sync ID
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faEraser} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerRemoveCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!isAllowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Remove certificates synced by Infisical from this ${destinationName} destination.`}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.SyncCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Remove Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan I={ProjectPermissionPkiSyncActions.Edit} a={permissionSubject}>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
isDisabled={!isAllowed}
|
||||
icon={<FontAwesomeIcon icon={isAutoSyncEnabled ? faToggleOff : faToggleOn} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggleEnable(pkiSync);
|
||||
}}
|
||||
>
|
||||
{isAutoSyncEnabled ? "Disable" : "Enable"} Auto-Sync
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.Delete}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(isAllowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
isDisabled={!isAllowed}
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(pkiSync);
|
||||
}}
|
||||
>
|
||||
Delete Sync
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</Tooltip>
|
||||
</Td>
|
||||
</Tr>
|
||||
{(allowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faRotate} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerSyncCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!allowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Manually trigger a sync for this ${destinationName} destination.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span> Trigger Sync</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
{syncOption?.canImportCertificates && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.ImportCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(allowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faDownload} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerImportCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!allowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Import certificates from this ${destinationName} destination into Infisical.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Import Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.RemoveCertificates}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(allowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
icon={<FontAwesomeIcon icon={faEraser} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onTriggerRemoveCertificates(pkiSync);
|
||||
}}
|
||||
isDisabled={!allowed}
|
||||
>
|
||||
<Tooltip
|
||||
position="left"
|
||||
sideOffset={42}
|
||||
content={`Remove certificates synced by Infisical from this ${destinationName} destination.`}
|
||||
>
|
||||
<div className="flex h-full w-full items-center justify-between gap-1">
|
||||
<span>Remove Certificates</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-bunker-300"
|
||||
size="sm"
|
||||
icon={faInfoCircle}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.Edit}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(allowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
isDisabled={!allowed}
|
||||
icon={
|
||||
<FontAwesomeIcon icon={isAutoSyncEnabled ? faToggleOff : faToggleOn} />
|
||||
}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onToggleEnable(pkiSync);
|
||||
}}
|
||||
>
|
||||
{isAutoSyncEnabled ? "Disable" : "Enable"} Auto-Sync
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiSyncActions.Delete}
|
||||
a={permissionSubject}
|
||||
>
|
||||
{(allowed: boolean) => (
|
||||
<DropdownMenuItem
|
||||
isDisabled={!allowed}
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDelete(pkiSync);
|
||||
}}
|
||||
>
|
||||
Delete Sync
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</Tooltip>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
}}
|
||||
</ProjectPermissionCan>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -112,7 +112,9 @@ export const PkiSyncActionTriggers = ({ pkiSync }: Props) => {
|
||||
}, [updatePkiSyncMutation, id, projectId, pkiSync.isAutoSyncEnabled]);
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriberId || ""
|
||||
subscriberId: subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { useListPkiSyncCertificates, useRemoveCertificatesFromPkiSync } from "@app/hooks/api";
|
||||
import { CertificateSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
@@ -84,8 +85,12 @@ export const PkiSyncCertificatesSection = ({ pkiSync }: Props) => {
|
||||
const totalCount = data?.totalCount || 0;
|
||||
const removeCertificatesFromSync = useRemoveCertificatesFromPkiSync();
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: pkiSync.subscriberId || ""
|
||||
subscriberId: pkiSync.subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
const handleRemoveCertificate = async (certificateId: string) => {
|
||||
|
||||
@@ -55,7 +55,9 @@ export const PkiSyncDestinationSection = ({ pkiSync, onEditDestination }: Props)
|
||||
}
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriberId || ""
|
||||
subscriberId: subscriberId || "",
|
||||
subscriberName: destinationDetails.name,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,6 +10,7 @@ import { PkiSyncStatusBadge } from "@app/components/pki-syncs";
|
||||
import { IconButton } from "@app/components/v2";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSyncStatus, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
const GenericFieldLabel = ({
|
||||
@@ -50,8 +51,12 @@ export const PkiSyncDetailsSection = ({ pkiSync, onEditDetails }: Props) => {
|
||||
return null;
|
||||
}, [syncStatus, lastSyncMessage]);
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriber?.id || subscriberId || ""
|
||||
subscriberId: subscriber?.id || subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,7 @@ import { IconButton } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { PkiSync, TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
const GenericFieldLabel = ({
|
||||
@@ -35,9 +36,12 @@ export const PkiSyncFieldMappingsSection = ({ pkiSync, onEditMappings }: Props)
|
||||
}
|
||||
|
||||
const fieldMappings = pkiSync.syncOptions?.fieldMappings;
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: pkiSync.subscriberId || ""
|
||||
subscriberId: pkiSync.subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -7,6 +7,7 @@ import { IconButton } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
const GenericFieldLabel = ({
|
||||
@@ -34,8 +35,12 @@ export const PkiSyncOptionsSection = ({ pkiSync, onEditOptions }: Props) => {
|
||||
syncOptions: { canRemoveCertificates }
|
||||
} = pkiSync;
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: pkiSync.subscriberId || ""
|
||||
subscriberId: pkiSync.subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,6 +9,7 @@ import { IconButton, Tooltip } from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPkiSyncActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { PKI_SYNC_MAP } from "@app/helpers/pkiSyncs";
|
||||
import { TPkiSync } from "@app/hooks/api/pkiSyncs";
|
||||
|
||||
const GenericFieldLabel = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
||||
@@ -26,8 +27,12 @@ type Props = {
|
||||
export const PkiSyncSourceSection = ({ pkiSync, onEditSource }: Props) => {
|
||||
const { subscriberId, subscriber } = pkiSync;
|
||||
|
||||
const destinationName = PKI_SYNC_MAP[pkiSync.destination].name;
|
||||
|
||||
const permissionSubject = subject(ProjectPermissionSub.PkiSyncs, {
|
||||
subscriberId: subscriberId || ""
|
||||
subscriberId: subscriberId || "",
|
||||
subscriberName: destinationName,
|
||||
name: pkiSync.name
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -4,10 +4,10 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { UpgradePlanModal } from "@app/components/license/UpgradePlanModal";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { useProjectPermission } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { usePopUp } from "@app/hooks";
|
||||
@@ -21,8 +21,6 @@ import { ProfileList } from "./ProfileList";
|
||||
import { RevealAcmeEabSecretModal } from "./RevealAcmeEabSecretModal";
|
||||
|
||||
export const CertificateProfilesTab = () => {
|
||||
const { permission } = useProjectPermission();
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
@@ -35,11 +33,6 @@ export const CertificateProfilesTab = () => {
|
||||
|
||||
const deleteProfile = useDeleteCertificateProfile();
|
||||
|
||||
const canCreateProfile = permission.can(
|
||||
ProjectPermissionActions.Create,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const handleCreateProfile = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
@@ -84,16 +77,22 @@ export const CertificateProfilesTab = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{canCreateProfile && (
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="button"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={handleCreateProfile}
|
||||
>
|
||||
Create Profile
|
||||
</Button>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateProfileActions.Create}
|
||||
a={ProjectPermissionSub.CertificateProfiles}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isDisabled={!isAllowed}
|
||||
colorSchema="primary"
|
||||
type="button"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={handleCreateProfile}
|
||||
>
|
||||
Create Profile
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
|
||||
<ProfileList
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useCallback } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faCheck,
|
||||
faCircleInfo,
|
||||
@@ -12,6 +13,7 @@ import {
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -22,9 +24,7 @@ import {
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { Badge } from "@app/components/v3";
|
||||
import { useProjectPermission } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
@@ -47,8 +47,6 @@ export const ProfileRow = ({
|
||||
onRevealProfileAcmeEabSecret,
|
||||
onDeleteProfile
|
||||
}: Props) => {
|
||||
const { permission } = useProjectPermission();
|
||||
|
||||
const { data: caData } = useGetInternalCaById(profile.caId ?? "");
|
||||
|
||||
const { popUp, handlePopUpToggle } = usePopUp(["issueCertificate"] as const);
|
||||
@@ -71,26 +69,6 @@ export const ProfileRow = ({
|
||||
templateId: profile.certificateTemplateId
|
||||
});
|
||||
|
||||
const canEditProfile = permission.can(
|
||||
ProjectPermissionActions.Edit,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const canRevealProfileAcmeEabSecret = permission.can(
|
||||
ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
);
|
||||
|
||||
const canIssueCertificate = permission.can(
|
||||
ProjectPermissionCertificateProfileActions.IssueCert,
|
||||
ProjectPermissionSub.CertificateProfiles
|
||||
);
|
||||
|
||||
const canDeleteProfile = permission.can(
|
||||
ProjectPermissionActions.Delete,
|
||||
ProjectPermissionSub.CertificateAuthorities
|
||||
);
|
||||
|
||||
const getEnrollmentTypeBadge = (enrollmentType: string) => {
|
||||
const config = {
|
||||
api: { variant: "ghost" as const, label: "API" },
|
||||
@@ -149,50 +127,82 @@ export const ProfileRow = ({
|
||||
>
|
||||
Copy Profile ID
|
||||
</DropdownMenuItem>
|
||||
{canEditProfile && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditProfile(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEdit} className="w-3" />}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateProfileActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateProfiles, { slug: profile.slug })}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditProfile(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEdit} className="w-3" />}
|
||||
>
|
||||
Edit Profile
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
{profile.enrollmentType === "acme" && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret}
|
||||
a={subject(ProjectPermissionSub.CertificateProfiles, { slug: profile.slug })}
|
||||
>
|
||||
Edit Profile
|
||||
</DropdownMenuItem>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRevealProfileAcmeEabSecret(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEye} className="w-3" />}
|
||||
>
|
||||
Reveal ACME EAB
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
{canRevealProfileAcmeEabSecret && profile.enrollmentType === "acme" && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onRevealProfileAcmeEabSecret(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEye} className="w-3" />}
|
||||
{profile.enrollmentType === "api" && (
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateProfileActions.IssueCert}
|
||||
a={subject(ProjectPermissionSub.CertificateProfiles, { slug: profile.slug })}
|
||||
>
|
||||
Reveal ACME EAB
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canIssueCertificate && profile.enrollmentType === "api" && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePopUpToggle("issueCertificate");
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faPlus} className="w-3" />}
|
||||
>
|
||||
Issue Certificate
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canDeleteProfile && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteProfile(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faTrash} className="w-3" />}
|
||||
>
|
||||
Delete Profile
|
||||
</DropdownMenuItem>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handlePopUpToggle("issueCertificate");
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faPlus} className="w-3" />}
|
||||
>
|
||||
Request Certificate
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionCertificateProfileActions.Delete}
|
||||
a={subject(ProjectPermissionSub.CertificateProfiles, { slug: profile.slug })}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteProfile(profile);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faTrash} className="w-3" />}
|
||||
>
|
||||
Delete Profile
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<CertificateIssuanceModal
|
||||
|
||||
@@ -3,8 +3,8 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { Button, DeleteActionModal } from "@app/components/v2";
|
||||
import { useProjectPermission } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
ProjectPermissionSub
|
||||
@@ -16,8 +16,6 @@ import { CreateTemplateModal } from "./CreateTemplateModal";
|
||||
import { TemplateList } from "./TemplateList";
|
||||
|
||||
export const CertificateTemplatesV2Tab = () => {
|
||||
const { permission } = useProjectPermission();
|
||||
|
||||
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
|
||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
@@ -26,11 +24,6 @@ export const CertificateTemplatesV2Tab = () => {
|
||||
|
||||
const deleteTemplateV2 = useDeleteCertificateTemplateV2WithPolicies();
|
||||
|
||||
const canCreateTemplate = permission.can(
|
||||
ProjectPermissionPkiTemplateActions.Create,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
);
|
||||
|
||||
const handleCreateTemplate = () => {
|
||||
setIsCreateModalOpen(true);
|
||||
};
|
||||
@@ -70,16 +63,22 @@ export const CertificateTemplatesV2Tab = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{canCreateTemplate && (
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
type="button"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={handleCreateTemplate}
|
||||
>
|
||||
Create Template
|
||||
</Button>
|
||||
)}
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiTemplateActions.Create}
|
||||
a={ProjectPermissionSub.CertificateTemplates}
|
||||
>
|
||||
{(isAllowed) => (
|
||||
<Button
|
||||
isDisabled={!isAllowed}
|
||||
colorSchema="primary"
|
||||
type="button"
|
||||
leftIcon={<FontAwesomeIcon icon={faPlus} />}
|
||||
onClick={handleCreateTemplate}
|
||||
>
|
||||
Create Template
|
||||
</Button>
|
||||
)}
|
||||
</ProjectPermissionCan>
|
||||
</div>
|
||||
|
||||
<TemplateList onEditTemplate={handleEditTemplate} onDeleteTemplate={handleDeleteTemplate} />
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
import { faCircleInfo, faEdit, faEllipsis, faTrash } from "@fortawesome/free-solid-svg-icons";
|
||||
import { useCallback } from "react";
|
||||
import { subject } from "@casl/ability";
|
||||
import {
|
||||
faCheck,
|
||||
faCircleInfo,
|
||||
faCopy,
|
||||
faEdit,
|
||||
faEllipsis,
|
||||
faTrash
|
||||
} from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -17,11 +28,12 @@ import {
|
||||
Tooltip,
|
||||
Tr
|
||||
} from "@app/components/v2";
|
||||
import { useProject, useProjectPermission } from "@app/context";
|
||||
import { useProject } from "@app/context";
|
||||
import {
|
||||
ProjectPermissionPkiTemplateActions,
|
||||
ProjectPermissionSub
|
||||
} from "@app/context/ProjectPermissionContext/types";
|
||||
import { useToggle } from "@app/hooks";
|
||||
import { useListCertificateTemplatesV2 } from "@app/hooks/api/certificateTemplates/queries";
|
||||
import { TCertificateTemplateV2WithPolicies } from "@app/hooks/api/certificateTemplates/types";
|
||||
|
||||
@@ -31,8 +43,8 @@ interface Props {
|
||||
}
|
||||
|
||||
export const TemplateList = ({ onEditTemplate, onDeleteTemplate }: Props) => {
|
||||
const { permission } = useProjectPermission();
|
||||
const { currentProject } = useProject();
|
||||
const [isIdCopied, setIsIdCopied] = useToggle(false);
|
||||
|
||||
const { data, isLoading } = useListCertificateTemplatesV2({
|
||||
projectId: currentProject?.id || "",
|
||||
@@ -42,20 +54,25 @@ export const TemplateList = ({ onEditTemplate, onDeleteTemplate }: Props) => {
|
||||
|
||||
const templates = data?.certificateTemplates || [];
|
||||
|
||||
const handleCopyId = useCallback(
|
||||
(templateId: string) => {
|
||||
setIsIdCopied.on();
|
||||
navigator.clipboard.writeText(templateId);
|
||||
|
||||
createNotification({
|
||||
text: "Template ID copied to clipboard",
|
||||
type: "info"
|
||||
});
|
||||
|
||||
setTimeout(() => setIsIdCopied.off(), 2000);
|
||||
},
|
||||
[setIsIdCopied]
|
||||
);
|
||||
|
||||
if (!currentProject?.id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const canEditTemplate = permission.can(
|
||||
ProjectPermissionPkiTemplateActions.Edit,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
);
|
||||
|
||||
const canDeleteTemplate = permission.can(
|
||||
ProjectPermissionPkiTemplateActions.Delete,
|
||||
ProjectPermissionSub.CertificateTemplates
|
||||
);
|
||||
|
||||
const formatDate = (dateString: string) => {
|
||||
return new Date(dateString).toLocaleDateString();
|
||||
};
|
||||
@@ -110,28 +127,55 @@ export const TemplateList = ({ onEditTemplate, onDeleteTemplate }: Props) => {
|
||||
</div>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="p-1">
|
||||
{canEditTemplate && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditTemplate(template);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEdit} />}
|
||||
>
|
||||
Edit Template
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
{canDeleteTemplate && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteTemplate(template);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
>
|
||||
Delete Template
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleCopyId(template.id);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={isIdCopied ? faCheck : faCopy} />}
|
||||
>
|
||||
Copy Template ID
|
||||
</DropdownMenuItem>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiTemplateActions.Edit}
|
||||
a={subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: template.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onEditTemplate(template);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faEdit} />}
|
||||
>
|
||||
Edit Template
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPkiTemplateActions.Delete}
|
||||
a={subject(ProjectPermissionSub.CertificateTemplates, {
|
||||
name: template.name
|
||||
})}
|
||||
>
|
||||
{(isAllowed) =>
|
||||
isAllowed && (
|
||||
<DropdownMenuItem
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onDeleteTemplate(template);
|
||||
}}
|
||||
icon={<FontAwesomeIcon icon={faTrash} />}
|
||||
>
|
||||
Delete Template
|
||||
</DropdownMenuItem>
|
||||
)
|
||||
}
|
||||
</ProjectPermissionCan>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</Td>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ProjectPermissionSub } from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { ConditionsFields } from "./ConditionsFields";
|
||||
|
||||
type Props = {
|
||||
position?: number;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
export const CertificateAuthorityPermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
||||
return (
|
||||
<ConditionsFields
|
||||
isDisabled={isDisabled}
|
||||
subject={ProjectPermissionSub.CertificateAuthorities}
|
||||
position={position}
|
||||
selectOptions={[{ value: "name", label: "Name" }]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { ProjectPermissionSub } from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { ConditionsFields } from "./ConditionsFields";
|
||||
|
||||
type Props = {
|
||||
position?: number;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
export const CertificatePermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
||||
return (
|
||||
<ConditionsFields
|
||||
isDisabled={isDisabled}
|
||||
subject={ProjectPermissionSub.Certificates}
|
||||
position={position}
|
||||
selectOptions={[
|
||||
{ value: "id", label: "Certificate ID" },
|
||||
{ value: "commonName", label: "Common Name" },
|
||||
{ value: "altNames", label: "Subject Alternative Names" },
|
||||
{ value: "serialNumber", label: "Serial Number" }
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { ProjectPermissionSub } from "@app/context/ProjectPermissionContext/types";
|
||||
|
||||
import { ConditionsFields } from "./ConditionsFields";
|
||||
|
||||
type Props = {
|
||||
position?: number;
|
||||
isDisabled?: boolean;
|
||||
};
|
||||
|
||||
export const CertificateProfilePermissionConditions = ({ position = 0, isDisabled }: Props) => {
|
||||
return (
|
||||
<ConditionsFields
|
||||
isDisabled={isDisabled}
|
||||
subject={ProjectPermissionSub.CertificateProfiles}
|
||||
position={position}
|
||||
selectOptions={[{ value: "slug", label: "Name" }]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -13,7 +13,7 @@ export const PkiSyncPermissionConditions = ({ position = 0, isDisabled }: Props)
|
||||
isDisabled={isDisabled}
|
||||
subject={ProjectPermissionSub.PkiSyncs}
|
||||
position={position}
|
||||
selectOptions={[{ value: "subscriberName", label: "Subscriber Name" }]}
|
||||
selectOptions={[{ value: "name", label: "Sync Name" }]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Tooltip } from "@app/components/v2";
|
||||
import {
|
||||
ProjectPermissionActions,
|
||||
ProjectPermissionCertificateActions,
|
||||
ProjectPermissionCertificateAuthorityActions,
|
||||
ProjectPermissionCertificateProfileActions,
|
||||
ProjectPermissionCmekActions,
|
||||
ProjectPermissionSub
|
||||
@@ -56,7 +57,19 @@ const CertificatePolicyActionSchema = z.object({
|
||||
[ProjectPermissionCertificateActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateActions.Read]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateActions.ReadPrivateKey]: z.boolean().optional()
|
||||
[ProjectPermissionCertificateActions.List]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateActions.ReadPrivateKey]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateActions.Import]: z.boolean().optional()
|
||||
});
|
||||
|
||||
const CertificateAuthorityPolicyActionSchema = z.object({
|
||||
[ProjectPermissionCertificateAuthorityActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.Read]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.List]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.Renew]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateAuthorityActions.SignIntermediate]: z.boolean().optional()
|
||||
});
|
||||
|
||||
const SecretPolicyActionSchema = z.object({
|
||||
@@ -110,6 +123,7 @@ const PkiSyncPolicyActionSchema = z.object({
|
||||
[ProjectPermissionPkiSyncActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.List]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.SyncCertificates]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.ImportCertificates]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSyncActions.RemoveCertificates]: z.boolean().optional()
|
||||
@@ -208,7 +222,6 @@ const PkiSubscriberPolicyActionSchema = z.object({
|
||||
[ProjectPermissionPkiSubscriberActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSubscriberActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSubscriberActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSubscriberActions.IssueCert]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiSubscriberActions.ListCerts]: z.boolean().optional()
|
||||
});
|
||||
|
||||
@@ -217,15 +230,17 @@ const PkiTemplatePolicyActionSchema = z.object({
|
||||
[ProjectPermissionPkiTemplateActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiTemplateActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiTemplateActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiTemplateActions.IssueCert]: z.boolean().optional(),
|
||||
[ProjectPermissionPkiTemplateActions.ListCerts]: z.boolean().optional()
|
||||
});
|
||||
const CertificateProfilePolicyActionSchema = z.object({
|
||||
[ProjectPermissionCertificateProfileActions.Read]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.List]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.Create]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.Edit]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.Delete]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.IssueCert]: z.boolean().optional()
|
||||
[ProjectPermissionCertificateProfileActions.IssueCert]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret]: z.boolean().optional(),
|
||||
[ProjectPermissionCertificateProfileActions.RotateAcmeEabSecret]: z.boolean().optional()
|
||||
});
|
||||
|
||||
const SecretEventsPolicyActionSchema = z.object({
|
||||
@@ -381,8 +396,18 @@ export const projectRoleFormSchema = z.object({
|
||||
[ProjectPermissionSub.Environments]: GeneralPolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.AuditLogs]: AuditLogsPolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.IpAllowList]: GeneralPolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.CertificateAuthorities]: GeneralPolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.Certificates]: CertificatePolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.CertificateAuthorities]: CertificateAuthorityPolicyActionSchema.extend({
|
||||
inverted: z.boolean().optional(),
|
||||
conditions: ConditionSchema
|
||||
})
|
||||
.array()
|
||||
.default([]),
|
||||
[ProjectPermissionSub.Certificates]: CertificatePolicyActionSchema.extend({
|
||||
inverted: z.boolean().optional(),
|
||||
conditions: ConditionSchema
|
||||
})
|
||||
.array()
|
||||
.default([]),
|
||||
[ProjectPermissionSub.PkiSubscribers]: PkiSubscriberPolicyActionSchema.extend({
|
||||
inverted: z.boolean().optional(),
|
||||
conditions: ConditionSchema
|
||||
@@ -397,8 +422,12 @@ export const projectRoleFormSchema = z.object({
|
||||
})
|
||||
.array()
|
||||
.default([]),
|
||||
[ProjectPermissionSub.CertificateProfiles]:
|
||||
CertificateProfilePolicyActionSchema.array().default([]),
|
||||
[ProjectPermissionSub.CertificateProfiles]: CertificateProfilePolicyActionSchema.extend({
|
||||
inverted: z.boolean().optional(),
|
||||
conditions: ConditionSchema
|
||||
})
|
||||
.array()
|
||||
.default([]),
|
||||
[ProjectPermissionSub.SshCertificateAuthorities]: GeneralPolicyActionSchema.array().default(
|
||||
[]
|
||||
),
|
||||
@@ -458,6 +487,9 @@ type TConditionalFields =
|
||||
| ProjectPermissionSub.DynamicSecrets
|
||||
| ProjectPermissionSub.PkiSubscribers
|
||||
| ProjectPermissionSub.CertificateTemplates
|
||||
| ProjectPermissionSub.CertificateAuthorities
|
||||
| ProjectPermissionSub.Certificates
|
||||
| ProjectPermissionSub.CertificateProfiles
|
||||
| ProjectPermissionSub.SshHosts
|
||||
| ProjectPermissionSub.SecretRotation
|
||||
| ProjectPermissionSub.Identity
|
||||
@@ -479,6 +511,9 @@ export const isConditionalSubjects = (
|
||||
subject === ProjectPermissionSub.SecretRotation ||
|
||||
subject === ProjectPermissionSub.PkiSubscribers ||
|
||||
subject === ProjectPermissionSub.CertificateTemplates ||
|
||||
subject === ProjectPermissionSub.CertificateAuthorities ||
|
||||
subject === ProjectPermissionSub.Certificates ||
|
||||
subject === ProjectPermissionSub.CertificateProfiles ||
|
||||
subject === ProjectPermissionSub.SecretSyncs ||
|
||||
subject === ProjectPermissionSub.PkiSyncs ||
|
||||
subject === ProjectPermissionSub.SecretEvents ||
|
||||
@@ -661,6 +696,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
const canEdit = action.includes(ProjectPermissionPkiSyncActions.Edit);
|
||||
const canDelete = action.includes(ProjectPermissionPkiSyncActions.Delete);
|
||||
const canCreate = action.includes(ProjectPermissionPkiSyncActions.Create);
|
||||
const canList = action.includes(ProjectPermissionPkiSyncActions.List);
|
||||
const canSyncCertificates = action.includes(
|
||||
ProjectPermissionPkiSyncActions.SyncCertificates
|
||||
);
|
||||
@@ -679,6 +715,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
[ProjectPermissionPkiSyncActions.Create]: canCreate,
|
||||
[ProjectPermissionPkiSyncActions.Edit]: canEdit,
|
||||
[ProjectPermissionPkiSyncActions.Delete]: canDelete,
|
||||
[ProjectPermissionPkiSyncActions.List]: canList,
|
||||
[ProjectPermissionPkiSyncActions.SyncCertificates]: canSyncCertificates,
|
||||
[ProjectPermissionPkiSyncActions.ImportCertificates]: canImportCertificates,
|
||||
[ProjectPermissionPkiSyncActions.RemoveCertificates]: canRemoveCertificates,
|
||||
@@ -822,6 +859,33 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.CertificateAuthorities) {
|
||||
const canRead = action.includes(ProjectPermissionCertificateAuthorityActions.Read);
|
||||
const canCreate = action.includes(ProjectPermissionCertificateAuthorityActions.Create);
|
||||
const canEdit = action.includes(ProjectPermissionCertificateAuthorityActions.Edit);
|
||||
const canDelete = action.includes(ProjectPermissionCertificateAuthorityActions.Delete);
|
||||
const canList = action.includes(ProjectPermissionCertificateAuthorityActions.List);
|
||||
const canRenew = action.includes(ProjectPermissionCertificateAuthorityActions.Renew);
|
||||
const canSignIntermediate = action.includes(
|
||||
ProjectPermissionCertificateAuthorityActions.SignIntermediate
|
||||
);
|
||||
|
||||
// from above statement we are sure it won't be undefined
|
||||
formVal[subject]!.push({
|
||||
[ProjectPermissionCertificateAuthorityActions.Read]: canRead,
|
||||
[ProjectPermissionCertificateAuthorityActions.Create]: canCreate,
|
||||
[ProjectPermissionCertificateAuthorityActions.Edit]: canEdit,
|
||||
[ProjectPermissionCertificateAuthorityActions.Delete]: canDelete,
|
||||
[ProjectPermissionCertificateAuthorityActions.List]: canList,
|
||||
[ProjectPermissionCertificateAuthorityActions.Renew]: canRenew,
|
||||
[ProjectPermissionCertificateAuthorityActions.SignIntermediate]: canSignIntermediate,
|
||||
conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [],
|
||||
inverted
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// for other subjects
|
||||
const canRead = action.includes(ProjectPermissionActions.Read);
|
||||
const canEdit = action.includes(ProjectPermissionActions.Edit);
|
||||
@@ -873,17 +937,24 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
const canEdit = action.includes(ProjectPermissionCertificateActions.Edit);
|
||||
const canDelete = action.includes(ProjectPermissionCertificateActions.Delete);
|
||||
const canCreate = action.includes(ProjectPermissionCertificateActions.Create);
|
||||
const canList = action.includes(ProjectPermissionCertificateActions.List);
|
||||
const canImport = action.includes(ProjectPermissionCertificateActions.Import);
|
||||
const canReadPrivateKey = action.includes(ProjectPermissionCertificateActions.ReadPrivateKey);
|
||||
|
||||
if (!formVal[subject]) formVal[subject] = [{}];
|
||||
if (!formVal[subject]) formVal[subject] = [];
|
||||
|
||||
// from above statement we are sure it won't be undefined
|
||||
if (canRead) formVal[subject]![0].read = true;
|
||||
if (canEdit) formVal[subject]![0].edit = true;
|
||||
if (canCreate) formVal[subject]![0].create = true;
|
||||
if (canDelete) formVal[subject]![0].delete = true;
|
||||
if (canReadPrivateKey)
|
||||
formVal[subject]![0][ProjectPermissionCertificateActions.ReadPrivateKey] = true;
|
||||
formVal[subject]!.push({
|
||||
[ProjectPermissionCertificateActions.Read]: canRead,
|
||||
[ProjectPermissionCertificateActions.Edit]: canEdit,
|
||||
[ProjectPermissionCertificateActions.Create]: canCreate,
|
||||
[ProjectPermissionCertificateActions.Delete]: canDelete,
|
||||
[ProjectPermissionCertificateActions.List]: canList,
|
||||
[ProjectPermissionCertificateActions.ReadPrivateKey]: canReadPrivateKey,
|
||||
[ProjectPermissionCertificateActions.Import]: canImport,
|
||||
conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [],
|
||||
inverted
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1129,9 +1200,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
[ProjectPermissionPkiSubscriberActions.Read]: action.includes(
|
||||
ProjectPermissionPkiSubscriberActions.Read
|
||||
),
|
||||
[ProjectPermissionPkiSubscriberActions.IssueCert]: action.includes(
|
||||
ProjectPermissionPkiSubscriberActions.IssueCert
|
||||
),
|
||||
[ProjectPermissionPkiSubscriberActions.ListCerts]: action.includes(
|
||||
ProjectPermissionPkiSubscriberActions.ListCerts
|
||||
),
|
||||
@@ -1157,9 +1225,6 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
[ProjectPermissionPkiTemplateActions.Read]: action.includes(
|
||||
ProjectPermissionPkiTemplateActions.Read
|
||||
),
|
||||
[ProjectPermissionPkiTemplateActions.IssueCert]: action.includes(
|
||||
ProjectPermissionPkiTemplateActions.IssueCert
|
||||
),
|
||||
[ProjectPermissionPkiTemplateActions.ListCerts]: action.includes(
|
||||
ProjectPermissionPkiTemplateActions.ListCerts
|
||||
),
|
||||
@@ -1185,9 +1250,20 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
[ProjectPermissionCertificateProfileActions.Read]: action.includes(
|
||||
ProjectPermissionCertificateProfileActions.Read
|
||||
),
|
||||
[ProjectPermissionCertificateProfileActions.List]: action.includes(
|
||||
ProjectPermissionCertificateProfileActions.List
|
||||
),
|
||||
[ProjectPermissionCertificateProfileActions.IssueCert]: action.includes(
|
||||
ProjectPermissionCertificateProfileActions.IssueCert
|
||||
)
|
||||
),
|
||||
[ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret]: action.includes(
|
||||
ProjectPermissionCertificateProfileActions.RevealAcmeEabSecret
|
||||
),
|
||||
[ProjectPermissionCertificateProfileActions.RotateAcmeEabSecret]: action.includes(
|
||||
ProjectPermissionCertificateProfileActions.RotateAcmeEabSecret
|
||||
),
|
||||
conditions: conditions ? convertCaslConditionToFormOperator(conditions) : [],
|
||||
inverted
|
||||
});
|
||||
|
||||
return;
|
||||
@@ -1196,7 +1272,7 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
|
||||
if (subject === ProjectPermissionSub.PamAccounts) {
|
||||
if (!formVal[subject]) formVal[subject] = [];
|
||||
|
||||
formVal[subject].push({
|
||||
formVal[subject]!.push({
|
||||
[ProjectPermissionPamAccountActions.Access]: action.includes(
|
||||
ProjectPermissionPamAccountActions.Access
|
||||
),
|
||||
@@ -1552,18 +1628,26 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
[ProjectPermissionSub.CertificateAuthorities]: {
|
||||
title: "Certificate Authorities",
|
||||
actions: [
|
||||
{ label: "Read", value: "read" },
|
||||
{ label: "Create", value: "create" },
|
||||
{ label: "Modify", value: "edit" },
|
||||
{ label: "Remove", value: "delete" }
|
||||
{ label: "Read", value: ProjectPermissionCertificateAuthorityActions.Read },
|
||||
{ label: "List", value: ProjectPermissionCertificateAuthorityActions.List },
|
||||
{ label: "Create", value: ProjectPermissionCertificateAuthorityActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionCertificateAuthorityActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionCertificateAuthorityActions.Delete },
|
||||
{ label: "Renew", value: ProjectPermissionCertificateAuthorityActions.Renew },
|
||||
{
|
||||
label: "Sign Intermediate",
|
||||
value: ProjectPermissionCertificateAuthorityActions.SignIntermediate
|
||||
}
|
||||
]
|
||||
},
|
||||
[ProjectPermissionSub.Certificates]: {
|
||||
title: "Certificates",
|
||||
actions: [
|
||||
{ label: "Read", value: ProjectPermissionCertificateActions.Read },
|
||||
{ label: "List", value: ProjectPermissionCertificateActions.List },
|
||||
{ label: "Read Private Key", value: ProjectPermissionCertificateActions.ReadPrivateKey },
|
||||
{ label: "Create", value: ProjectPermissionCertificateActions.Create },
|
||||
// { label: "Create", value: ProjectPermissionCertificateActions.Create }, // Hidden from UI - kept for backend compatibility
|
||||
{ label: "Import", value: ProjectPermissionCertificateActions.Import },
|
||||
{ label: "Modify", value: ProjectPermissionCertificateActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionCertificateActions.Delete }
|
||||
]
|
||||
@@ -1575,7 +1659,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
{ label: "Create", value: ProjectPermissionPkiTemplateActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionPkiTemplateActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionPkiTemplateActions.Delete },
|
||||
{ label: "Issue Certificates", value: ProjectPermissionPkiTemplateActions.IssueCert },
|
||||
{ label: "List Certificates", value: ProjectPermissionPkiTemplateActions.ListCerts }
|
||||
]
|
||||
},
|
||||
@@ -1583,10 +1666,11 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
title: "Certificate Profiles",
|
||||
actions: [
|
||||
{ label: "Read", value: ProjectPermissionCertificateProfileActions.Read },
|
||||
{ label: "List", value: ProjectPermissionCertificateProfileActions.List },
|
||||
{ label: "Create", value: ProjectPermissionCertificateProfileActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionCertificateProfileActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionCertificateProfileActions.Delete },
|
||||
{ label: "Issue Certificates", value: ProjectPermissionCertificateProfileActions.IssueCert }
|
||||
{ label: "Request Certificates", value: ProjectPermissionCertificateProfileActions.IssueCert }
|
||||
]
|
||||
},
|
||||
[ProjectPermissionSub.SshCertificateAuthorities]: {
|
||||
@@ -1642,7 +1726,6 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
{ label: "Create", value: ProjectPermissionPkiSubscriberActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionPkiSubscriberActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionPkiSubscriberActions.Delete },
|
||||
{ label: "Issue Certificate", value: ProjectPermissionPkiSubscriberActions.IssueCert },
|
||||
{ label: "List Certificates", value: ProjectPermissionPkiSubscriberActions.ListCerts }
|
||||
]
|
||||
},
|
||||
@@ -1716,6 +1799,7 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
|
||||
title: "Certificate Syncs",
|
||||
actions: [
|
||||
{ label: "Read", value: ProjectPermissionPkiSyncActions.Read },
|
||||
{ label: "List", value: ProjectPermissionPkiSyncActions.List },
|
||||
{ label: "Create", value: ProjectPermissionPkiSyncActions.Create },
|
||||
{ label: "Modify", value: ProjectPermissionPkiSyncActions.Edit },
|
||||
{ label: "Remove", value: ProjectPermissionPkiSyncActions.Delete },
|
||||
|
||||
@@ -21,6 +21,9 @@ import { ProjectMembershipRole } from "@app/hooks/api/roles/types";
|
||||
|
||||
import { AddPoliciesButton } from "./AddPoliciesButton";
|
||||
import { AppConnectionPermissionConditions } from "./AppConnectionPermissionConditions";
|
||||
import { CertificateAuthorityPermissionConditions } from "./CertificateAuthorityPermissionConditions";
|
||||
import { CertificatePermissionConditions } from "./CertificatePermissionConditions";
|
||||
import { CertificateProfilePermissionConditions } from "./CertificateProfilePermissionConditions";
|
||||
import { DynamicSecretPermissionConditions } from "./DynamicSecretPermissionConditions";
|
||||
import { GeneralPermissionConditions } from "./GeneralPermissionConditions";
|
||||
import { GeneralPermissionPolicies } from "./GeneralPermissionPolicies";
|
||||
@@ -96,6 +99,18 @@ export const renderConditionalComponents = (
|
||||
return <PamAccountPermissionConditions isDisabled={isDisabled} />;
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.CertificateAuthorities) {
|
||||
return <CertificateAuthorityPermissionConditions isDisabled={isDisabled} />;
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.Certificates) {
|
||||
return <CertificatePermissionConditions isDisabled={isDisabled} />;
|
||||
}
|
||||
|
||||
if (subject === ProjectPermissionSub.CertificateProfiles) {
|
||||
return <CertificateProfilePermissionConditions isDisabled={isDisabled} />;
|
||||
}
|
||||
|
||||
return <GeneralPermissionConditions isDisabled={isDisabled} type={subject} />;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user