Move ssh back to project level

This commit is contained in:
Tuan Dang
2024-12-09 21:36:42 -08:00
parent 42249726d4
commit 7cf297344b
61 changed files with 837 additions and 635 deletions
+8
View File
@@ -317,6 +317,9 @@ import {
TSshCertificateAuthoritySecrets, TSshCertificateAuthoritySecrets,
TSshCertificateAuthoritySecretsInsert, TSshCertificateAuthoritySecretsInsert,
TSshCertificateAuthoritySecretsUpdate, TSshCertificateAuthoritySecretsUpdate,
TSshCertificateBodies,
TSshCertificateBodiesInsert,
TSshCertificateBodiesUpdate,
TSshCertificates, TSshCertificates,
TSshCertificatesInsert, TSshCertificatesInsert,
TSshCertificatesUpdate, TSshCertificatesUpdate,
@@ -404,6 +407,11 @@ declare module "knex/types/tables" {
TSshCertificatesInsert, TSshCertificatesInsert,
TSshCertificatesUpdate TSshCertificatesUpdate
>; >;
[TableName.SshCertificateBody]: KnexOriginal.CompositeTableType<
TSshCertificateBodies,
TSshCertificateBodiesInsert,
TSshCertificateBodiesUpdate
>;
[TableName.CertificateAuthority]: KnexOriginal.CompositeTableType< [TableName.CertificateAuthority]: KnexOriginal.CompositeTableType<
TCertificateAuthorities, TCertificateAuthorities,
TCertificateAuthoritiesInsert, TCertificateAuthoritiesInsert,
@@ -8,8 +8,8 @@ export async function up(knex: Knex): Promise<void> {
await knex.schema.createTable(TableName.SshCertificateAuthority, (t) => { await knex.schema.createTable(TableName.SshCertificateAuthority, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true); t.timestamps(true, true, true);
t.uuid("orgId").notNullable(); t.string("projectId").notNullable();
t.foreign("orgId").references("id").inTable(TableName.Organization).onDelete("CASCADE"); t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE");
t.string("status").notNullable(); // active / disabled t.string("status").notNullable(); // active / disabled
t.string("friendlyName").notNullable(); t.string("friendlyName").notNullable();
t.string("keyAlgorithm").notNullable(); t.string("keyAlgorithm").notNullable();
@@ -60,7 +60,6 @@ export async function up(knex: Knex): Promise<void> {
.onDelete("SET NULL"); .onDelete("SET NULL");
t.string("serialNumber").notNullable().unique(); t.string("serialNumber").notNullable().unique();
t.string("certType").notNullable(); // user or host t.string("certType").notNullable(); // user or host
t.text("publicKey").notNullable(); // public key in OpenSSH format
t.specificType("principals", "text[]").notNullable(); t.specificType("principals", "text[]").notNullable();
t.string("keyId").notNullable(); t.string("keyId").notNullable();
t.datetime("notBefore").notNullable(); t.datetime("notBefore").notNullable();
@@ -68,9 +67,24 @@ export async function up(knex: Knex): Promise<void> {
}); });
await createOnUpdateTrigger(knex, TableName.SshCertificate); await createOnUpdateTrigger(knex, TableName.SshCertificate);
} }
if (!(await knex.schema.hasTable(TableName.SshCertificateBody))) {
await knex.schema.createTable(TableName.SshCertificateBody, (t) => {
t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid());
t.timestamps(true, true, true);
t.uuid("sshCertId").notNullable().unique();
t.foreign("sshCertId").references("id").inTable(TableName.SshCertificate).onDelete("CASCADE");
t.binary("encryptedCertificate").notNullable();
});
await createOnUpdateTrigger(knex, TableName.SshCertificateBody);
}
} }
export async function down(knex: Knex): Promise<void> { export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists(TableName.SshCertificateBody);
await dropOnUpdateTrigger(knex, TableName.SshCertificateBody);
await knex.schema.dropTableIfExists(TableName.SshCertificate); await knex.schema.dropTableIfExists(TableName.SshCertificate);
await dropOnUpdateTrigger(knex, TableName.SshCertificate); await dropOnUpdateTrigger(knex, TableName.SshCertificate);
+1
View File
@@ -107,6 +107,7 @@ export * from "./service-tokens";
export * from "./slack-integrations"; export * from "./slack-integrations";
export * from "./ssh-certificate-authorities"; export * from "./ssh-certificate-authorities";
export * from "./ssh-certificate-authority-secrets"; export * from "./ssh-certificate-authority-secrets";
export * from "./ssh-certificate-bodies";
export * from "./ssh-certificate-templates"; export * from "./ssh-certificate-templates";
export * from "./ssh-certificates"; export * from "./ssh-certificates";
export * from "./super-admin"; export * from "./super-admin";
+1
View File
@@ -6,6 +6,7 @@ export enum TableName {
SshCertificateAuthoritySecret = "ssh_certificate_authority_secrets", SshCertificateAuthoritySecret = "ssh_certificate_authority_secrets",
SshCertificateTemplate = "ssh_certificate_templates", SshCertificateTemplate = "ssh_certificate_templates",
SshCertificate = "ssh_certificates", SshCertificate = "ssh_certificates",
SshCertificateBody = "ssh_certificate_bodies",
CertificateAuthority = "certificate_authorities", CertificateAuthority = "certificate_authorities",
CertificateTemplateEstConfig = "certificate_template_est_configs", CertificateTemplateEstConfig = "certificate_template_est_configs",
CertificateAuthorityCert = "certificate_authority_certs", CertificateAuthorityCert = "certificate_authority_certs",
@@ -11,7 +11,7 @@ export const SshCertificateAuthoritiesSchema = z.object({
id: z.string().uuid(), id: z.string().uuid(),
createdAt: z.date(), createdAt: z.date(),
updatedAt: z.date(), updatedAt: z.date(),
orgId: z.string().uuid(), projectId: z.string(),
status: z.string(), status: z.string(),
friendlyName: z.string(), friendlyName: z.string(),
keyAlgorithm: z.string() keyAlgorithm: z.string()
@@ -0,0 +1,22 @@
// Code generated by automation script, DO NOT EDIT.
// Automated by pulling database and generating zod schema
// To update. Just run npm run generate:schema
// Written by akhilmhdh.
import { z } from "zod";
import { zodBuffer } from "@app/lib/zod";
import { TImmutableDBKeys } from "./models";
export const SshCertificateBodiesSchema = z.object({
id: z.string().uuid(),
createdAt: z.date(),
updatedAt: z.date(),
sshCertId: z.string().uuid(),
encryptedCertificate: zodBuffer
});
export type TSshCertificateBodies = z.infer<typeof SshCertificateBodiesSchema>;
export type TSshCertificateBodiesInsert = Omit<z.input<typeof SshCertificateBodiesSchema>, TImmutableDBKeys>;
export type TSshCertificateBodiesUpdate = Partial<Omit<z.input<typeof SshCertificateBodiesSchema>, TImmutableDBKeys>>;
@@ -15,7 +15,6 @@ export const SshCertificatesSchema = z.object({
sshCertificateTemplateId: z.string().uuid().nullable().optional(), sshCertificateTemplateId: z.string().uuid().nullable().optional(),
serialNumber: z.string(), serialNumber: z.string(),
certType: z.string(), certType: z.string(),
publicKey: z.string(),
principals: z.string().array(), principals: z.string().array(),
keyId: z.string(), keyId: z.string(),
notBefore: z.date(), notBefore: z.date(),
@@ -21,6 +21,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
description: "Create SSH CA", description: "Create SSH CA",
body: z.object({ body: z.object({
projectId: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.projectId),
friendlyName: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.friendlyName), friendlyName: z.string().describe(SSH_CERTIFICATE_AUTHORITIES.CREATE.friendlyName),
keyAlgorithm: z keyAlgorithm: z
.nativeEnum(CertKeyAlgorithm) .nativeEnum(CertKeyAlgorithm)
@@ -46,7 +47,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.CREATE_SSH_CA, type: EventType.CREATE_SSH_CA,
metadata: { metadata: {
@@ -93,7 +94,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.GET_SSH_CA, type: EventType.GET_SSH_CA,
metadata: { metadata: {
@@ -169,7 +170,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.UPDATE_SSH_CA, type: EventType.UPDATE_SSH_CA,
metadata: { metadata: {
@@ -215,7 +216,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.DELETE_SSH_CA, type: EventType.DELETE_SSH_CA,
metadata: { metadata: {
@@ -260,7 +261,7 @@ export const registerSshCaRouter = async (server: FastifyZodProvider) => {
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.GET_SSH_CA_CERTIFICATE_TEMPLATES, type: EventType.GET_SSH_CA_CERTIFICATE_TEMPLATES,
metadata: { metadata: {
@@ -41,7 +41,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: certificateTemplate.orgId, projectId: certificateTemplate.projectId,
event: { event: {
type: EventType.GET_SSH_CERTIFICATE_TEMPLATE, type: EventType.GET_SSH_CERTIFICATE_TEMPLATE,
metadata: { metadata: {
@@ -107,7 +107,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: ca.orgId, projectId: ca.projectId,
event: { event: {
type: EventType.CREATE_SSH_CERTIFICATE_TEMPLATE, type: EventType.CREATE_SSH_CERTIFICATE_TEMPLATE,
metadata: { metadata: {
@@ -178,7 +178,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
}, },
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => { handler: async (req) => {
const { certificateTemplate, orgId } = await server.services.sshCertificateTemplate.updateSshCertTemplate({ const { certificateTemplate, projectId } = await server.services.sshCertificateTemplate.updateSshCertTemplate({
...req.body, ...req.body,
id: req.params.certificateTemplateId, id: req.params.certificateTemplateId,
actor: req.permission.type, actor: req.permission.type,
@@ -189,7 +189,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId, projectId,
event: { event: {
type: EventType.UPDATE_SSH_CERTIFICATE_TEMPLATE, type: EventType.UPDATE_SSH_CERTIFICATE_TEMPLATE,
metadata: { metadata: {
@@ -238,7 +238,7 @@ export const registerSshCertificateTemplateRouter = async (server: FastifyZodPro
await server.services.auditLog.createAuditLog({ await server.services.auditLog.createAuditLog({
...req.auditLogInfo, ...req.auditLogInfo,
orgId: certificateTemplate.orgId, projectId: certificateTemplate.projectId,
event: { event: {
type: EventType.DELETE_SSH_CERTIFICATE_TEMPLATE, type: EventType.DELETE_SSH_CERTIFICATE_TEMPLATE,
metadata: { metadata: {
+2
View File
@@ -20,6 +20,7 @@ export const registerSshRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
description: "Sign SSH public key", description: "Sign SSH public key",
body: z.object({ body: z.object({
projectId: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.projectId),
templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.templateName), templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.templateName),
publicKey: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.publicKey), publicKey: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.SIGN_SSH_KEY.publicKey),
certType: z certType: z
@@ -86,6 +87,7 @@ export const registerSshRouter = async (server: FastifyZodProvider) => {
schema: { schema: {
description: "Issue SSH credentials (certificate + key)", description: "Issue SSH credentials (certificate + key)",
body: z.object({ body: z.object({
projectId: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.projectId),
templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.templateName), templateName: z.string().trim().describe(SSH_CERTIFICATE_AUTHORITIES.ISSUE_SSH_CREDENTIALS.templateName),
keyAlgorithm: z keyAlgorithm: z
.nativeEnum(CertKeyAlgorithm) .nativeEnum(CertKeyAlgorithm)
@@ -27,10 +27,7 @@ export enum OrgPermissionSubjects {
Kms = "kms", Kms = "kms",
AdminConsole = "organization-admin-console", AdminConsole = "organization-admin-console",
AuditLogs = "audit-logs", AuditLogs = "audit-logs",
ProjectTemplates = "project-templates", ProjectTemplates = "project-templates"
SshCertificates = "ssh-certificates",
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificateTemplates = "ssh-certificate-templates"
} }
export type OrgPermissionSet = export type OrgPermissionSet =
@@ -49,10 +46,7 @@ export type OrgPermissionSet =
| [OrgPermissionActions, OrgPermissionSubjects.Kms] | [OrgPermissionActions, OrgPermissionSubjects.Kms]
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs] | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates] | [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates]
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole] | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole];
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateAuthorities]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificates]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateTemplates];
const buildAdminPermission = () => { const buildAdminPermission = () => {
const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility); const { can, rules } = new AbilityBuilder<MongoAbility<OrgPermissionSet>>(createMongoAbility);
@@ -129,19 +123,6 @@ const buildAdminPermission = () => {
can(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates); can(OrgPermissionActions.Edit, OrgPermissionSubjects.ProjectTemplates);
can(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates); can(OrgPermissionActions.Delete, OrgPermissionSubjects.ProjectTemplates);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificates);
can(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificates);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificateAuthorities);
can(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificateAuthorities);
can(OrgPermissionActions.Edit, OrgPermissionSubjects.SshCertificateAuthorities);
can(OrgPermissionActions.Delete, OrgPermissionSubjects.SshCertificateAuthorities);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificateTemplates);
can(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificateTemplates);
can(OrgPermissionActions.Edit, OrgPermissionSubjects.SshCertificateTemplates);
can(OrgPermissionActions.Delete, OrgPermissionSubjects.SshCertificateTemplates);
can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole); can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole);
return rules; return rules;
@@ -172,11 +153,6 @@ const buildMemberPermission = () => {
can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs); can(OrgPermissionActions.Read, OrgPermissionSubjects.AuditLogs);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificateAuthorities);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificates);
can(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificates);
can(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificateTemplates);
return rules; return rules;
}; };
@@ -54,6 +54,9 @@ export enum ProjectPermissionSub {
CertificateAuthorities = "certificate-authorities", CertificateAuthorities = "certificate-authorities",
Certificates = "certificates", Certificates = "certificates",
CertificateTemplates = "certificate-templates", CertificateTemplates = "certificate-templates",
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificates = "ssh-certificates",
SshCertificateTemplates = "ssh-certificate-templates",
PkiAlerts = "pki-alerts", PkiAlerts = "pki-alerts",
PkiCollections = "pki-collections", PkiCollections = "pki-collections",
Kms = "kms", Kms = "kms",
@@ -125,6 +128,9 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.Certificates] | [ProjectPermissionActions, ProjectPermissionSub.Certificates]
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates] | [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts] | [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections] | [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
| [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek] | [ProjectPermissionCmekActions, ProjectPermissionSub.Cmek]
@@ -322,6 +328,28 @@ const GeneralPermissionSchema = [
"Describe what action an entity can take." "Describe what action an entity can take."
) )
}), }),
z.object({
subject: z
.literal(ProjectPermissionSub.SshCertificateAuthorities)
.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.SshCertificates).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.SshCertificateTemplates)
.describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
"Describe what action an entity can take."
)
}),
z.object({ z.object({
subject: z.literal(ProjectPermissionSub.PkiAlerts).describe("The entity this permission pertains to."), subject: z.literal(ProjectPermissionSub.PkiAlerts).describe("The entity this permission pertains to."),
action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe(
@@ -448,7 +476,10 @@ const buildAdminPermissionRules = () => {
ProjectPermissionSub.Certificates, ProjectPermissionSub.Certificates,
ProjectPermissionSub.CertificateTemplates, ProjectPermissionSub.CertificateTemplates,
ProjectPermissionSub.PkiAlerts, ProjectPermissionSub.PkiAlerts,
ProjectPermissionSub.PkiCollections ProjectPermissionSub.PkiCollections,
ProjectPermissionSub.SshCertificateAuthorities,
ProjectPermissionSub.SshCertificates,
ProjectPermissionSub.SshCertificateTemplates
].forEach((el) => { ].forEach((el) => {
can( can(
[ [
@@ -633,6 +664,11 @@ const buildMemberPermissionRules = () => {
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts); can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiAlerts);
can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections); can([ProjectPermissionActions.Read], ProjectPermissionSub.PkiCollections);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateAuthorities);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates);
can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates);
can( can(
[ [
ProjectPermissionCmekActions.Create, ProjectPermissionCmekActions.Create,
@@ -675,6 +711,9 @@ const buildViewerPermissionRules = () => {
can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities); can(ProjectPermissionActions.Read, ProjectPermissionSub.CertificateAuthorities);
can(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates); can(ProjectPermissionActions.Read, ProjectPermissionSub.Certificates);
can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek); can(ProjectPermissionCmekActions.Read, ProjectPermissionSub.Cmek);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateAuthorities);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates);
return rules; return rules;
}; };
@@ -18,11 +18,11 @@ export const sshCertificateTemplateDALFactory = (db: TDbClient) => {
`${TableName.SshCertificateAuthority}.id`, `${TableName.SshCertificateAuthority}.id`,
`${TableName.SshCertificateTemplate}.sshCaId` `${TableName.SshCertificateTemplate}.sshCaId`
) )
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.SshCertificateAuthority}.orgId`) .join(TableName.Project, `${TableName.Project}.id`, `${TableName.SshCertificateAuthority}.projectId`)
.where(`${TableName.SshCertificateTemplate}.id`, "=", id) .where(`${TableName.SshCertificateTemplate}.id`, "=", id)
.select(selectAllTableCols(TableName.SshCertificateTemplate)) .select(selectAllTableCols(TableName.SshCertificateTemplate))
.select( .select(
db.ref("orgId").withSchema(TableName.SshCertificateAuthority), db.ref("projectId").withSchema(TableName.SshCertificateAuthority),
db.ref("friendlyName").as("caName").withSchema(TableName.SshCertificateAuthority), db.ref("friendlyName").as("caName").withSchema(TableName.SshCertificateAuthority),
db.ref("status").as("caStatus").withSchema(TableName.SshCertificateAuthority) db.ref("status").as("caStatus").withSchema(TableName.SshCertificateAuthority)
) )
@@ -34,7 +34,10 @@ export const sshCertificateTemplateDALFactory = (db: TDbClient) => {
} }
}; };
const getByName = async (name: string, orgId: string, tx?: Knex) => { /**
* Returns the SSH certificate template named [name] within project with id [projectId]
*/
const getByName = async (name: string, projectId: string, tx?: Knex) => {
try { try {
const certTemplate = await (tx || db.replicaNode())(TableName.SshCertificateTemplate) const certTemplate = await (tx || db.replicaNode())(TableName.SshCertificateTemplate)
.join( .join(
@@ -42,12 +45,12 @@ export const sshCertificateTemplateDALFactory = (db: TDbClient) => {
`${TableName.SshCertificateAuthority}.id`, `${TableName.SshCertificateAuthority}.id`,
`${TableName.SshCertificateTemplate}.sshCaId` `${TableName.SshCertificateTemplate}.sshCaId`
) )
.join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.SshCertificateAuthority}.orgId`) .join(TableName.Project, `${TableName.Project}.id`, `${TableName.SshCertificateAuthority}.projectId`)
.where(`${TableName.SshCertificateTemplate}.name`, "=", name) .where(`${TableName.SshCertificateTemplate}.name`, "=", name)
.where(`${TableName.Organization}.id`, "=", orgId) .where(`${TableName.Project}.id`, "=", projectId)
.select(selectAllTableCols(TableName.SshCertificateTemplate)) .select(selectAllTableCols(TableName.SshCertificateTemplate))
.select( .select(
db.ref("orgId").withSchema(TableName.SshCertificateAuthority), db.ref("projectId").withSchema(TableName.SshCertificateAuthority),
db.ref("friendlyName").as("caName").withSchema(TableName.SshCertificateAuthority), db.ref("friendlyName").as("caName").withSchema(TableName.SshCertificateAuthority),
db.ref("status").as("caStatus").withSchema(TableName.SshCertificateAuthority) db.ref("status").as("caStatus").withSchema(TableName.SshCertificateAuthority)
) )
@@ -1,8 +1,8 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import ms from "ms"; import ms from "ms";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TSshCertificateAuthorityDALFactory } from "../ssh/ssh-certificate-authority-dal"; import { TSshCertificateAuthorityDALFactory } from "../ssh/ssh-certificate-authority-dal";
@@ -21,7 +21,7 @@ type TSshCertificateTemplateServiceFactoryDep = {
"transaction" | "getByName" | "create" | "updateById" | "deleteById" | "getById" "transaction" | "getByName" | "create" | "updateById" | "deleteById" | "getById"
>; >;
sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "findById">; sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "findById">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
}; };
export type TSshCertificateTemplateServiceFactory = ReturnType<typeof sshCertificateTemplateServiceFactory>; export type TSshCertificateTemplateServiceFactory = ReturnType<typeof sshCertificateTemplateServiceFactory>;
@@ -53,17 +53,17 @@ export const sshCertificateTemplateServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
ca.orgId, ca.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Create, ProjectPermissionActions.Create,
OrgPermissionSubjects.SshCertificateTemplates ProjectPermissionSub.SshCertificateTemplates
); );
if (ms(ttl) > ms(maxTTL)) { if (ms(ttl) > ms(maxTTL)) {
@@ -73,7 +73,7 @@ export const sshCertificateTemplateServiceFactory = ({
} }
const newCertificateTemplate = await sshCertificateTemplateDAL.transaction(async (tx) => { const newCertificateTemplate = await sshCertificateTemplateDAL.transaction(async (tx) => {
const existingTemplate = await sshCertificateTemplateDAL.getByName(name, ca.orgId, tx); const existingTemplate = await sshCertificateTemplateDAL.getByName(name, ca.projectId, tx);
if (existingTemplate) { if (existingTemplate) {
throw new BadRequestError({ throw new BadRequestError({
message: `SSH certificate template with name ${name} already exists` message: `SSH certificate template with name ${name} already exists`
@@ -125,22 +125,22 @@ export const sshCertificateTemplateServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
certTemplate.orgId, certTemplate.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Edit, ProjectPermissionActions.Edit,
OrgPermissionSubjects.SshCertificateTemplates ProjectPermissionSub.SshCertificateTemplates
); );
const updatedCertificateTemplate = await sshCertificateTemplateDAL.transaction(async (tx) => { const updatedCertificateTemplate = await sshCertificateTemplateDAL.transaction(async (tx) => {
if (name) { if (name) {
const existingTemplate = await sshCertificateTemplateDAL.getByName(name, actorOrgId, tx); const existingTemplate = await sshCertificateTemplateDAL.getByName(name, certTemplate.projectId, tx);
if (existingTemplate && existingTemplate.id !== id) { if (existingTemplate && existingTemplate.id !== id) {
throw new BadRequestError({ throw new BadRequestError({
message: `SSH certificate template with name ${name} already exists` message: `SSH certificate template with name ${name} already exists`
@@ -175,7 +175,7 @@ export const sshCertificateTemplateServiceFactory = ({
return { return {
certificateTemplate: updatedCertificateTemplate, certificateTemplate: updatedCertificateTemplate,
orgId: certTemplate.orgId projectId: certTemplate.projectId
}; };
}; };
@@ -193,17 +193,17 @@ export const sshCertificateTemplateServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
certificateTemplate.orgId, certificateTemplate.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Delete, ProjectPermissionActions.Delete,
OrgPermissionSubjects.SshCertificateTemplates ProjectPermissionSub.SshCertificateTemplates
); );
await sshCertificateTemplateDAL.deleteById(certificateTemplate.id); await sshCertificateTemplateDAL.deleteById(certificateTemplate.id);
@@ -219,17 +219,17 @@ export const sshCertificateTemplateServiceFactory = ({
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
certTemplate.orgId, certTemplate.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read, ProjectPermissionActions.Read,
OrgPermissionSubjects.SshCertificateTemplates ProjectPermissionSub.SshCertificateTemplates
); );
return certTemplate; return certTemplate;
@@ -0,0 +1,10 @@
import { TDbClient } from "@app/db";
import { TableName } from "@app/db/schemas";
import { ormify } from "@app/lib/knex";
export type TSshCertificateBodyDALFactory = ReturnType<typeof sshCertificateBodyDALFactory>;
export const sshCertificateBodyDALFactory = (db: TDbClient) => {
const sshCertificateBodyOrm = ormify(db, TableName.SshCertificateBody);
return sshCertificateBodyOrm;
};
@@ -8,7 +8,7 @@ export type TSshCertificateDALFactory = ReturnType<typeof sshCertificateDALFacto
export const sshCertificateDALFactory = (db: TDbClient) => { export const sshCertificateDALFactory = (db: TDbClient) => {
const sshCertificateOrm = ormify(db, TableName.SshCertificate); const sshCertificateOrm = ormify(db, TableName.SshCertificate);
const countSshCertificatesInOrg = async (orgId: string) => { const countSshCertificatesInProject = async (projectId: string) => {
try { try {
interface CountResult { interface CountResult {
count: string; count: string;
@@ -21,18 +21,18 @@ export const sshCertificateDALFactory = (db: TDbClient) => {
`${TableName.SshCertificate}.sshCaId`, `${TableName.SshCertificate}.sshCaId`,
`${TableName.SshCertificateAuthority}.id` `${TableName.SshCertificateAuthority}.id`
) )
.join(TableName.Organization, `${TableName.SshCertificateAuthority}.orgId`, `${TableName.Organization}.id`) .join(TableName.Project, `${TableName.SshCertificateAuthority}.projectId`, `${TableName.Project}.id`)
.where(`${TableName.Organization}.id`, orgId); .where(`${TableName.Project}.id`, projectId);
const count = await query.count("*").first(); const count = await query.count("*").first();
return parseInt((count as unknown as CountResult).count || "0", 10); return parseInt((count as unknown as CountResult).count || "0", 10);
} catch (error) { } catch (error) {
throw new DatabaseError({ error, name: "Count all SSH certificates in organization" }); throw new DatabaseError({ error, name: "Count all SSH certificates in project" });
} }
}; };
return { return {
...sshCertificateOrm, ...sshCertificateOrm,
countSshCertificatesInOrg countSshCertificatesInProject
}; };
}; };
@@ -2,7 +2,7 @@ import { SshCertificateAuthoritiesSchema } from "@app/db/schemas";
export const sanitizedSshCa = SshCertificateAuthoritiesSchema.pick({ export const sanitizedSshCa = SshCertificateAuthoritiesSchema.pick({
id: true, id: true,
orgId: true, projectId: true,
friendlyName: true, friendlyName: true,
status: true, status: true,
keyAlgorithm: true keyAlgorithm: true
@@ -1,13 +1,15 @@
import { ForbiddenError } from "@casl/ability"; import { ForbiddenError } from "@casl/ability";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
import { TSshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal"; import { TSshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal";
import { TSshCertificateBodyDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-body-dal";
import { TSshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal"; import { TSshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal";
import { TSshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal"; import { TSshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal";
import { BadRequestError, NotFoundError } from "@app/lib/errors"; import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TKmsServiceFactory } from "@app/services/kms/kms-service"; import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { KmsDataKey } from "@app/services/kms/kms-types";
import { SshCertTemplateStatus } from "../ssh-certificate-template/ssh-certificate-template-types"; import { SshCertTemplateStatus } from "../ssh-certificate-template/ssh-certificate-template-types";
import { import {
@@ -37,18 +39,25 @@ type TSshCertificateAuthorityServiceFactoryDep = {
>; >;
sshCertificateAuthoritySecretDAL: Pick<TSshCertificateAuthoritySecretDALFactory, "create" | "findOne">; sshCertificateAuthoritySecretDAL: Pick<TSshCertificateAuthoritySecretDALFactory, "create" | "findOne">;
sshCertificateTemplateDAL: Pick<TSshCertificateTemplateDALFactory, "find" | "getByName">; sshCertificateTemplateDAL: Pick<TSshCertificateTemplateDALFactory, "find" | "getByName">;
sshCertificateDAL: Pick<TSshCertificateDALFactory, "create">; sshCertificateDAL: Pick<TSshCertificateDALFactory, "create" | "transaction">;
kmsService: Pick<TKmsServiceFactory, "generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey" | "getOrgKmsKeyId">; sshCertificateBodyDAL: Pick<TSshCertificateBodyDALFactory, "create">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; kmsService: Pick<
TKmsServiceFactory,
"generateKmsKey" | "encryptWithKmsKey" | "decryptWithKmsKey" | "getOrgKmsKeyId" | "createCipherPairWithDataKey"
>;
permissionService: Pick<TPermissionServiceFactory, "getProjectPermission">;
}; };
export type TSshCertificateAuthorityServiceFactory = ReturnType<typeof sshCertificateAuthorityServiceFactory>; export type TSshCertificateAuthorityServiceFactory = ReturnType<typeof sshCertificateAuthorityServiceFactory>;
// TODO: secretManagerEncryptor -> sshEncryptor (cc akhil)
export const sshCertificateAuthorityServiceFactory = ({ export const sshCertificateAuthorityServiceFactory = ({
sshCertificateAuthorityDAL, sshCertificateAuthorityDAL,
sshCertificateAuthoritySecretDAL, sshCertificateAuthoritySecretDAL,
sshCertificateTemplateDAL, sshCertificateTemplateDAL,
sshCertificateDAL, sshCertificateDAL,
sshCertificateBodyDAL,
kmsService, kmsService,
permissionService permissionService
}: TSshCertificateAuthorityServiceFactoryDep) => { }: TSshCertificateAuthorityServiceFactoryDep) => {
@@ -56,6 +65,7 @@ export const sshCertificateAuthorityServiceFactory = ({
* Generates a new SSH CA * Generates a new SSH CA
*/ */
const createSshCa = async ({ const createSshCa = async ({
projectId,
friendlyName, friendlyName,
keyAlgorithm, keyAlgorithm,
actorId, actorId,
@@ -63,23 +73,23 @@ export const sshCertificateAuthorityServiceFactory = ({
actor, actor,
actorOrgId actorOrgId
}: TCreateSshCaDTO) => { }: TCreateSshCaDTO) => {
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
actorOrgId, projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Create, ProjectPermissionActions.Create,
OrgPermissionSubjects.SshCertificateAuthorities ProjectPermissionSub.SshCertificateAuthorities
); );
const newCa = await sshCertificateAuthorityDAL.transaction(async (tx) => { const newCa = await sshCertificateAuthorityDAL.transaction(async (tx) => {
const ca = await sshCertificateAuthorityDAL.create( const ca = await sshCertificateAuthorityDAL.create(
{ {
orgId: actorOrgId, projectId,
friendlyName, friendlyName,
status: SshCaStatus.ACTIVE, status: SshCaStatus.ACTIVE,
keyAlgorithm keyAlgorithm
@@ -89,19 +99,16 @@ export const sshCertificateAuthorityServiceFactory = ({
const { publicKey, privateKey } = createSshKeyPair(keyAlgorithm, ca.friendlyName); const { publicKey, privateKey } = createSshKeyPair(keyAlgorithm, ca.friendlyName);
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); // TODO: update to sshEncryptor
const kmsEncryptor = await kmsService.encryptWithKmsKey({ const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
kmsId: orgKmsKeyId type: KmsDataKey.SecretManager,
}); projectId
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
plainText: Buffer.from(privateKey, "utf8")
}); });
await sshCertificateAuthoritySecretDAL.create( await sshCertificateAuthoritySecretDAL.create(
{ {
sshCaId: ca.id, sshCaId: ca.id,
encryptedPrivateKey encryptedPrivateKey: secretManagerEncryptor({ plainText: Buffer.from(privateKey, "utf8") }).cipherTextBlob
}, },
tx tx
); );
@@ -119,28 +126,28 @@ export const sshCertificateAuthorityServiceFactory = ({
const ca = await sshCertificateAuthorityDAL.findById(caId); const ca = await sshCertificateAuthorityDAL.findById(caId);
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` }); if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
ca.orgId, ca.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read, ProjectPermissionActions.Read,
OrgPermissionSubjects.SshCertificateAuthorities ProjectPermissionSub.SshCertificateAuthorities
); );
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id }); const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id });
// decrypt secret // TODO: update to sshDecryptor
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const kmsDecryptor = await kmsService.decryptWithKmsKey({ type: KmsDataKey.SecretManager,
kmsId: orgKmsKeyId projectId: ca.projectId
}); });
const decryptedCaPrivateKey = await kmsDecryptor({ const decryptedCaPrivateKey = secretManagerDecryptor({
cipherTextBlob: sshCaSecret.encryptedPrivateKey cipherTextBlob: sshCaSecret.encryptedPrivateKey
}); });
@@ -158,13 +165,13 @@ export const sshCertificateAuthorityServiceFactory = ({
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id }); const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id });
// decrypt secret // TODO: update to sshDecryptor
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(ca.orgId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const kmsDecryptor = await kmsService.decryptWithKmsKey({ type: KmsDataKey.SecretManager,
kmsId: orgKmsKeyId projectId: ca.projectId
}); });
const decryptedCaPrivateKey = await kmsDecryptor({ const decryptedCaPrivateKey = secretManagerDecryptor({
cipherTextBlob: sshCaSecret.encryptedPrivateKey cipherTextBlob: sshCaSecret.encryptedPrivateKey
}); });
@@ -189,30 +196,30 @@ export const sshCertificateAuthorityServiceFactory = ({
const ca = await sshCertificateAuthorityDAL.findById(caId); const ca = await sshCertificateAuthorityDAL.findById(caId);
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` }); if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
ca.orgId, ca.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Edit, ProjectPermissionActions.Edit,
OrgPermissionSubjects.SshCertificateAuthorities ProjectPermissionSub.SshCertificateAuthorities
); );
const updatedCa = await sshCertificateAuthorityDAL.updateById(caId, { friendlyName, status }); const updatedCa = await sshCertificateAuthorityDAL.updateById(caId, { friendlyName, status });
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id }); const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: ca.id });
// decrypt secret // TODO: update to sshDecryptor
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const kmsDecryptor = await kmsService.decryptWithKmsKey({ type: KmsDataKey.SecretManager,
kmsId: orgKmsKeyId projectId: ca.projectId
}); });
const decryptedCaPrivateKey = await kmsDecryptor({ const decryptedCaPrivateKey = secretManagerDecryptor({
cipherTextBlob: sshCaSecret.encryptedPrivateKey cipherTextBlob: sshCaSecret.encryptedPrivateKey
}); });
@@ -228,17 +235,17 @@ export const sshCertificateAuthorityServiceFactory = ({
const ca = await sshCertificateAuthorityDAL.findById(caId); const ca = await sshCertificateAuthorityDAL.findById(caId);
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` }); if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
ca.orgId, ca.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Delete, ProjectPermissionActions.Delete,
OrgPermissionSubjects.SshCertificateAuthorities ProjectPermissionSub.SshCertificateAuthorities
); );
const deletedCa = await sshCertificateAuthorityDAL.deleteById(caId); const deletedCa = await sshCertificateAuthorityDAL.deleteById(caId);
@@ -251,6 +258,7 @@ export const sshCertificateAuthorityServiceFactory = ({
* SSH public key is signed using CA behind SSH certificate with name [templateName]. * SSH public key is signed using CA behind SSH certificate with name [templateName].
*/ */
const issueSshCreds = async ({ const issueSshCreds = async ({
projectId,
templateName, templateName,
keyAlgorithm, keyAlgorithm,
certType, certType,
@@ -262,22 +270,25 @@ export const sshCertificateAuthorityServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
}: TIssueSshCredsDTO) => { }: TIssueSshCredsDTO) => {
const sshCertificateTemplate = await sshCertificateTemplateDAL.getByName(templateName, actorOrgId); const sshCertificateTemplate = await sshCertificateTemplateDAL.getByName(templateName, projectId);
if (!sshCertificateTemplate) { if (!sshCertificateTemplate) {
throw new NotFoundError({ throw new NotFoundError({
message: "No SSH certificate template found with specified name" message: "No SSH certificate template found with specified name"
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
actorOrgId, sshCertificateTemplate.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificates); ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SshCertificates
);
if (sshCertificateTemplate.caStatus === SshCaStatus.DISABLED) { if (sshCertificateTemplate.caStatus === SshCaStatus.DISABLED) {
throw new BadRequestError({ throw new BadRequestError({
@@ -307,13 +318,13 @@ export const sshCertificateAuthorityServiceFactory = ({
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId }); const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId });
// decrypt secret // TODO: update to sshDecryptor
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const kmsDecryptor = await kmsService.decryptWithKmsKey({ type: KmsDataKey.SecretManager,
kmsId: orgKmsKeyId projectId
}); });
const decryptedCaPrivateKey = await kmsDecryptor({ const decryptedCaPrivateKey = secretManagerDecryptor({
cipherTextBlob: sshCaSecret.encryptedPrivateKey cipherTextBlob: sshCaSecret.encryptedPrivateKey
}); });
@@ -329,16 +340,38 @@ export const sshCertificateAuthorityServiceFactory = ({
certType certType
}); });
await sshCertificateDAL.create({ // TODO: update to sshEncryptor
sshCaId: sshCertificateTemplate.sshCaId, const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
sshCertificateTemplateId: sshCertificateTemplate.id, type: KmsDataKey.SecretManager,
serialNumber, projectId: sshCertificateTemplate.projectId
certType, });
publicKey,
principals, const encryptedCertificate = secretManagerEncryptor({
keyId, plainText: Buffer.from(signedPublicKey, "utf8")
notBefore: new Date(), }).cipherTextBlob;
notAfter: new Date(Date.now() + ttl * 1000)
await sshCertificateDAL.transaction(async (tx) => {
const cert = await sshCertificateDAL.create(
{
sshCaId: sshCertificateTemplate.sshCaId,
sshCertificateTemplateId: sshCertificateTemplate.id,
serialNumber,
certType,
principals,
keyId,
notBefore: new Date(),
notAfter: new Date(Date.now() + ttl * 1000)
},
tx
);
await sshCertificateBodyDAL.create(
{
sshCertId: cert.id,
encryptedCertificate
},
tx
);
}); });
return { return {
@@ -357,6 +390,7 @@ export const sshCertificateAuthorityServiceFactory = ({
* using CA behind SSH certificate template with name [templateName] * using CA behind SSH certificate template with name [templateName]
*/ */
const signSshKey = async ({ const signSshKey = async ({
projectId,
templateName, templateName,
publicKey, publicKey,
certType, certType,
@@ -368,22 +402,25 @@ export const sshCertificateAuthorityServiceFactory = ({
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
}: TSignSshKeyDTO) => { }: TSignSshKeyDTO) => {
const sshCertificateTemplate = await sshCertificateTemplateDAL.getByName(templateName, actorOrgId); const sshCertificateTemplate = await sshCertificateTemplateDAL.getByName(templateName, projectId);
if (!sshCertificateTemplate) { if (!sshCertificateTemplate) {
throw new NotFoundError({ throw new NotFoundError({
message: "No SSH certificate template found with specified name" message: "No SSH certificate template found with specified name"
}); });
} }
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
actorOrgId, sshCertificateTemplate.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.SshCertificates); ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Create,
ProjectPermissionSub.SshCertificates
);
if (sshCertificateTemplate.caStatus === SshCaStatus.DISABLED) { if (sshCertificateTemplate.caStatus === SshCaStatus.DISABLED) {
throw new BadRequestError({ throw new BadRequestError({
@@ -413,13 +450,13 @@ export const sshCertificateAuthorityServiceFactory = ({
const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId }); const sshCaSecret = await sshCertificateAuthoritySecretDAL.findOne({ sshCaId: sshCertificateTemplate.sshCaId });
// decrypt secret // TODO: update to sshDecryptor
const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); const { decryptor: secretManagerDecryptor } = await kmsService.createCipherPairWithDataKey({
const kmsDecryptor = await kmsService.decryptWithKmsKey({ type: KmsDataKey.SecretManager,
kmsId: orgKmsKeyId projectId
}); });
const decryptedCaPrivateKey = await kmsDecryptor({ const decryptedCaPrivateKey = secretManagerDecryptor({
cipherTextBlob: sshCaSecret.encryptedPrivateKey cipherTextBlob: sshCaSecret.encryptedPrivateKey
}); });
@@ -432,16 +469,38 @@ export const sshCertificateAuthorityServiceFactory = ({
certType certType
}); });
await sshCertificateDAL.create({ // TODO: update to sshEncryptor
sshCaId: sshCertificateTemplate.sshCaId, const { encryptor: secretManagerEncryptor } = await kmsService.createCipherPairWithDataKey({
sshCertificateTemplateId: sshCertificateTemplate.id, type: KmsDataKey.SecretManager,
serialNumber, projectId: sshCertificateTemplate.projectId
certType, });
publicKey,
principals, const encryptedCertificate = secretManagerEncryptor({
keyId, plainText: Buffer.from(signedPublicKey, "utf8")
notBefore: new Date(), }).cipherTextBlob;
notAfter: new Date(Date.now() + ttl * 1000)
await sshCertificateDAL.transaction(async (tx) => {
const cert = await sshCertificateDAL.create(
{
sshCaId: sshCertificateTemplate.sshCaId,
sshCertificateTemplateId: sshCertificateTemplate.id,
serialNumber,
certType,
principals,
keyId,
notBefore: new Date(),
notAfter: new Date(Date.now() + ttl * 1000)
},
tx
);
await sshCertificateBodyDAL.create(
{
sshCertId: cert.id,
encryptedCertificate
},
tx
);
}); });
return { serialNumber, signedPublicKey, certificateTemplate: sshCertificateTemplate, ttl, keyId }; return { serialNumber, signedPublicKey, certificateTemplate: sshCertificateTemplate, ttl, keyId };
@@ -457,17 +516,17 @@ export const sshCertificateAuthorityServiceFactory = ({
const ca = await sshCertificateAuthorityDAL.findById(caId); const ca = await sshCertificateAuthorityDAL.findById(caId);
if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` }); if (!ca) throw new NotFoundError({ message: `SSH CA with ID '${caId}' not found` });
const { permission } = await permissionService.getOrgPermission( const { permission } = await permissionService.getProjectPermission(
actor, actor,
actorId, actorId,
actorOrgId, ca.projectId,
actorAuthMethod, actorAuthMethod,
actorOrgId actorOrgId
); );
ForbiddenError.from(permission).throwUnlessCan( ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read, ProjectPermissionActions.Read,
OrgPermissionSubjects.SshCertificateTemplates ProjectPermissionSub.SshCertificateTemplates
); );
const certificateTemplates = await sshCertificateTemplateDAL.find({ sshCaId: caId }); const certificateTemplates = await sshCertificateTemplateDAL.find({ sshCaId: caId });
@@ -1,4 +1,4 @@
import { TOrgPermission } from "@app/lib/types"; import { TProjectPermission } from "@app/lib/types";
import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types"; import { CertKeyAlgorithm } from "@app/services/certificate/certificate-types";
export enum SshCaStatus { export enum SshCaStatus {
@@ -14,11 +14,11 @@ export enum SshCertType {
export type TCreateSshCaDTO = { export type TCreateSshCaDTO = {
friendlyName: string; friendlyName: string;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
} & Omit<TOrgPermission, "orgId">; } & TProjectPermission;
export type TGetSshCaDTO = { export type TGetSshCaDTO = {
caId: string; caId: string;
} & Omit<TOrgPermission, "orgId">; } & Omit<TProjectPermission, "projectId">;
export type TGetSshCaPublicKeyDTO = { export type TGetSshCaPublicKeyDTO = {
caId: string; caId: string;
@@ -28,11 +28,11 @@ export type TUpdateSshCaDTO = {
caId: string; caId: string;
friendlyName?: string; friendlyName?: string;
status?: SshCaStatus; status?: SshCaStatus;
} & Omit<TOrgPermission, "orgId">; } & Omit<TProjectPermission, "projectId">;
export type TDeleteSshCaDTO = { export type TDeleteSshCaDTO = {
caId: string; caId: string;
} & Omit<TOrgPermission, "orgId">; } & Omit<TProjectPermission, "projectId">;
export type TIssueSshCredsDTO = { export type TIssueSshCredsDTO = {
templateName: string; templateName: string;
@@ -41,7 +41,7 @@ export type TIssueSshCredsDTO = {
principals: string[]; principals: string[];
ttl?: string; ttl?: string;
keyId?: string; keyId?: string;
} & Omit<TOrgPermission, "orgId">; } & TProjectPermission;
export type TSignSshKeyDTO = { export type TSignSshKeyDTO = {
templateName: string; templateName: string;
@@ -50,11 +50,11 @@ export type TSignSshKeyDTO = {
principals: string[]; principals: string[];
ttl?: string; ttl?: string;
keyId?: string; keyId?: string;
} & Omit<TOrgPermission, "orgId">; } & TProjectPermission;
export type TGetSshCaCertificateTemplatesDTO = { export type TGetSshCaCertificateTemplatesDTO = {
caId: string; caId: string;
} & Omit<TOrgPermission, "orgId">; } & Omit<TProjectPermission, "projectId">;
export type TCreateSshCertDTO = { export type TCreateSshCertDTO = {
caPrivateKey: string; caPrivateKey: string;
+12 -12
View File
@@ -384,17 +384,6 @@ export const ORGANIZATIONS = {
}, },
LIST_GROUPS: { LIST_GROUPS: {
organizationId: "The ID of the organization to list groups for." organizationId: "The ID of the organization to list groups for."
},
LIST_SSH_CAS: {
organizationId: "The ID of the organization to list SSH CAs for."
},
LIST_SSH_CERTIFICATES: {
organizationId: "The ID of the organization to list SSH certificates for.",
offset: "The offset to start from. If you enter 10, it will start from the 10th SSH certificate.",
limit: "The number of SSH certificates to return."
},
LIST_SSH_CERTIFICATE_TEMPLATES: {
organizationId: "The ID of the organization to list SSH certificate templates for."
} }
} as const; } as const;
@@ -455,7 +444,15 @@ export const PROJECTS = {
workspaceId: "The ID of the project to list integration auths for." workspaceId: "The ID of the project to list integration auths for."
}, },
LIST_SSH_CAS: { LIST_SSH_CAS: {
slug: "The slug of the project to list SSH CAs for." projectId: "The ID of the project to list SSH CAs for."
},
LIST_SSH_CERTIFICATES: {
projectId: "The ID of the project to list SSH certificates for.",
offset: "The offset to start from. If you enter 10, it will start from the 10th SSH certificate.",
limit: "The number of SSH certificates to return."
},
LIST_SSH_CERTIFICATE_TEMPLATES: {
projectId: "The ID of the project to list SSH certificate templates for."
}, },
LIST_CAS: { LIST_CAS: {
slug: "The slug of the project to list CAs for.", slug: "The slug of the project to list CAs for.",
@@ -1148,6 +1145,7 @@ export const AUDIT_LOG_STREAMS = {
export const SSH_CERTIFICATE_AUTHORITIES = { export const SSH_CERTIFICATE_AUTHORITIES = {
CREATE: { CREATE: {
projectId: "The ID of the project to create the SSH CA in.",
friendlyName: "A friendly name for the SSH CA.", friendlyName: "A friendly name for the SSH CA.",
keyAlgorithm: "The type of public key algorithm and size, in bits, of the key pair for the SSH CA." keyAlgorithm: "The type of public key algorithm and size, in bits, of the key pair for the SSH CA."
}, },
@@ -1169,6 +1167,7 @@ export const SSH_CERTIFICATE_AUTHORITIES = {
sshCaId: "The ID of the SSH CA to get the certificate templates for." sshCaId: "The ID of the SSH CA to get the certificate templates for."
}, },
SIGN_SSH_KEY: { SIGN_SSH_KEY: {
projectId: "The ID of the project to sign the SSH public key for.",
templateName: "The name of the SSH certificate template to sign the SSH public key with.", templateName: "The name of the SSH certificate template to sign the SSH public key with.",
publicKey: "The SSH public key to sign.", publicKey: "The SSH public key to sign.",
certType: "The type of certificate to issue. This can be one of user or host.", certType: "The type of certificate to issue. This can be one of user or host.",
@@ -1179,6 +1178,7 @@ export const SSH_CERTIFICATE_AUTHORITIES = {
signedKey: "The SSH certificate or signed SSH public key." signedKey: "The SSH certificate or signed SSH public key."
}, },
ISSUE_SSH_CREDENTIALS: { ISSUE_SSH_CREDENTIALS: {
projectId: "The ID of the project to issue the SSH credentials for.",
templateName: "The name of the SSH certificate template to issue the SSH credentials with.", templateName: "The name of the SSH certificate template to issue the SSH credentials with.",
keyAlgorithm: "The type of public key algorithm and size, in bits, of the key pair for the SSH CA.", keyAlgorithm: "The type of public key algorithm and size, in bits, of the key pair for the SSH CA.",
certType: "The type of certificate to issue. This can be one of user or host.", certType: "The type of certificate to issue. This can be one of user or host.",
+7 -4
View File
@@ -78,6 +78,7 @@ import { snapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/sna
import { sshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal"; import { sshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
import { sshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal"; import { sshCertificateAuthoritySecretDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-secret-dal";
import { sshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service"; import { sshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service";
import { sshCertificateBodyDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-body-dal";
import { sshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal"; import { sshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal";
import { sshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal"; import { sshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal";
import { sshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service"; import { sshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service";
@@ -349,6 +350,7 @@ export const registerRoutes = async (
const dynamicSecretLeaseDAL = dynamicSecretLeaseDALFactory(db); const dynamicSecretLeaseDAL = dynamicSecretLeaseDALFactory(db);
const sshCertificateDAL = sshCertificateDALFactory(db); const sshCertificateDAL = sshCertificateDALFactory(db);
const sshCertificateBodyDAL = sshCertificateBodyDALFactory(db);
const sshCertificateAuthorityDAL = sshCertificateAuthorityDALFactory(db); const sshCertificateAuthorityDAL = sshCertificateAuthorityDALFactory(db);
const sshCertificateAuthoritySecretDAL = sshCertificateAuthoritySecretDALFactory(db); const sshCertificateAuthoritySecretDAL = sshCertificateAuthoritySecretDALFactory(db);
const sshCertificateTemplateDAL = sshCertificateTemplateDALFactory(db); const sshCertificateTemplateDAL = sshCertificateTemplateDALFactory(db);
@@ -565,10 +567,7 @@ export const registerRoutes = async (
groupDAL, groupDAL,
orgBotDAL, orgBotDAL,
oidcConfigDAL, oidcConfigDAL,
projectBotService, projectBotService
sshCertificateAuthorityDAL,
sshCertificateDAL,
sshCertificateTemplateDAL
}); });
const signupService = authSignupServiceFactory({ const signupService = authSignupServiceFactory({
tokenService, tokenService,
@@ -721,6 +720,7 @@ export const registerRoutes = async (
sshCertificateAuthoritySecretDAL, sshCertificateAuthoritySecretDAL,
sshCertificateTemplateDAL, sshCertificateTemplateDAL,
sshCertificateDAL, sshCertificateDAL,
sshCertificateBodyDAL,
kmsService, kmsService,
permissionService permissionService
}); });
@@ -817,6 +817,9 @@ export const registerRoutes = async (
certificateDAL, certificateDAL,
pkiAlertDAL, pkiAlertDAL,
pkiCollectionDAL, pkiCollectionDAL,
sshCertificateAuthorityDAL,
sshCertificateDAL,
sshCertificateTemplateDAL,
projectUserMembershipRoleDAL, projectUserMembershipRoleDAL,
identityProjectMembershipRoleDAL, identityProjectMembershipRoleDAL,
keyStore, keyStore,
@@ -11,9 +11,6 @@ import {
UsersSchema UsersSchema
} from "@app/db/schemas"; } from "@app/db/schemas";
import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType, UserAgentType } from "@app/ee/services/audit-log/audit-log-types";
import { sanitizedSshCa } from "@app/ee/services/ssh/ssh-certificate-authority-schema";
import { sanitizedSshCertificate } from "@app/ee/services/ssh-certificate/ssh-certificate-schema";
import { sanitizedSshCertificateTemplate } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-schema";
import { AUDIT_LOGS, ORGANIZATIONS } from "@app/lib/api-docs"; import { AUDIT_LOGS, ORGANIZATIONS } from "@app/lib/api-docs";
import { getLastMidnightDateISO } from "@app/lib/fn"; import { getLastMidnightDateISO } from "@app/lib/fn";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@@ -407,101 +404,4 @@ export const registerOrgRouter = async (server: FastifyZodProvider) => {
return { groups }; return { groups };
} }
}); });
server.route({
method: "GET",
url: "/:organizationId/ssh-certificates",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_SSH_CAS.organizationId)
}),
querystring: z.object({
offset: z.coerce.number().default(0).describe(ORGANIZATIONS.LIST_SSH_CERTIFICATES.offset),
limit: z.coerce.number().default(25).describe(ORGANIZATIONS.LIST_SSH_CERTIFICATES.limit)
}),
response: {
200: z.object({
certificates: z.array(sanitizedSshCertificate),
totalCount: z.number()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { certificates, totalCount } = await server.services.org.listOrgSshCertificates({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
orgId: req.params.organizationId,
offset: req.query.offset,
limit: req.query.limit
});
return { certificates, totalCount };
}
});
server.route({
method: "GET",
url: "/:organizationId/ssh-certificate-templates",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_SSH_CERTIFICATE_TEMPLATES.organizationId)
}),
response: {
200: z.object({
certificateTemplates: z.array(sanitizedSshCertificateTemplate)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { certificateTemplates } = await server.services.org.listOrgSshCertificateTemplates({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
orgId: req.params.organizationId
});
return { certificateTemplates };
}
});
server.route({
method: "GET",
url: "/:organizationId/ssh-cas",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
organizationId: z.string().trim().describe(ORGANIZATIONS.LIST_SSH_CAS.organizationId)
}),
response: {
200: z.object({
cas: z.array(sanitizedSshCa)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const cas = await server.services.org.listOrgSshCas({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
orgId: req.params.organizationId
});
return { cas };
}
});
}; };
@@ -10,6 +10,9 @@ import {
} from "@app/db/schemas"; } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types"; import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types"; import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types";
import { sanitizedSshCa } from "@app/ee/services/ssh/ssh-certificate-authority-schema";
import { sanitizedSshCertificate } from "@app/ee/services/ssh-certificate/ssh-certificate-schema";
import { sanitizedSshCertificateTemplate } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-schema";
import { PROJECTS } from "@app/lib/api-docs"; import { PROJECTS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry"; import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
@@ -517,4 +520,101 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
return { certificateTemplates }; return { certificateTemplates };
} }
}); });
server.route({
method: "GET",
url: "/:projectId/ssh-certificates",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CAS.projectId)
}),
querystring: z.object({
offset: z.coerce.number().default(0).describe(PROJECTS.LIST_SSH_CERTIFICATES.offset),
limit: z.coerce.number().default(25).describe(PROJECTS.LIST_SSH_CERTIFICATES.limit)
}),
response: {
200: z.object({
certificates: z.array(sanitizedSshCertificate),
totalCount: z.number()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { certificates, totalCount } = await server.services.project.listProjectSshCertificates({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
projectId: req.params.projectId,
offset: req.query.offset,
limit: req.query.limit
});
return { certificates, totalCount };
}
});
server.route({
method: "GET",
url: "/:projectId/ssh-certificate-templates",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CERTIFICATE_TEMPLATES.projectId)
}),
response: {
200: z.object({
certificateTemplates: z.array(sanitizedSshCertificateTemplate)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const { certificateTemplates } = await server.services.project.listProjectSshCertificateTemplates({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
projectId: req.params.projectId
});
return { certificateTemplates };
}
});
server.route({
method: "GET",
url: "/:projectId/ssh-cas",
config: {
rateLimit: readLimit
},
schema: {
params: z.object({
projectId: z.string().trim().describe(PROJECTS.LIST_SSH_CAS.projectId)
}),
response: {
200: z.object({
cas: z.array(sanitizedSshCa)
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const cas = await server.services.project.listProjectSshCas({
actorId: req.permission.id,
actorOrgId: req.permission.orgId,
actorAuthMethod: req.permission.authMethod,
actor: req.permission.type,
projectId: req.params.projectId
});
return { cas };
}
});
}; };
+1 -101
View File
@@ -24,9 +24,6 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal"; import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal"; import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
import { TSshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal";
import { TSshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal";
import { getConfig } from "@app/lib/config/env"; import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto"; import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import { generateSymmetricKey, infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { generateSymmetricKey, infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
@@ -65,9 +62,6 @@ import {
TGetOrgGroupsDTO, TGetOrgGroupsDTO,
TGetOrgMembershipDTO, TGetOrgMembershipDTO,
TInviteUserToOrgDTO, TInviteUserToOrgDTO,
TListOrgSshCasDTO,
TListOrgSshCertificatesDTO,
TListOrgSshCertificateTemplatesDTO,
TListProjectMembershipsByOrgMembershipIdDTO, TListProjectMembershipsByOrgMembershipIdDTO,
TUpdateOrgDTO, TUpdateOrgDTO,
TUpdateOrgMembershipDTO, TUpdateOrgMembershipDTO,
@@ -104,9 +98,6 @@ type TOrgServiceFactoryDep = {
projectBotDAL: Pick<TProjectBotDALFactory, "findOne" | "updateById">; projectBotDAL: Pick<TProjectBotDALFactory, "findOne" | "updateById">;
projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "create">; projectUserMembershipRoleDAL: Pick<TProjectUserMembershipRoleDALFactory, "insertMany" | "create">;
projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">; projectBotService: Pick<TProjectBotServiceFactory, "getBotKey">;
sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "find">;
sshCertificateDAL: Pick<TSshCertificateDALFactory, "find" | "countSshCertificatesInOrg">;
sshCertificateTemplateDAL: Pick<TSshCertificateTemplateDALFactory, "find">;
}; };
export type TOrgServiceFactory = ReturnType<typeof orgServiceFactory>; export type TOrgServiceFactory = ReturnType<typeof orgServiceFactory>;
@@ -134,9 +125,6 @@ export const orgServiceFactory = ({
projectBotDAL, projectBotDAL,
projectUserMembershipRoleDAL, projectUserMembershipRoleDAL,
identityMetadataDAL, identityMetadataDAL,
sshCertificateAuthorityDAL,
sshCertificateDAL,
sshCertificateTemplateDAL,
projectBotService projectBotService
}: TOrgServiceFactoryDep) => { }: TOrgServiceFactoryDep) => {
/* /*
@@ -1139,91 +1127,6 @@ export const orgServiceFactory = ({
return incidentContact; return incidentContact;
}; };
/**
* Return list of SSH CAs for organization
*/
const listOrgSshCas = async ({ actorId, actorOrgId, actorAuthMethod, actor, orgId }: TListOrgSshCasDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read,
OrgPermissionSubjects.SshCertificateAuthorities
);
const cas = await sshCertificateAuthorityDAL.find(
{
orgId
},
{ sort: [["updatedAt", "desc"]] }
);
return cas;
};
/**
* Return list of SSH certificates for organization
*/
const listOrgSshCertificates = async ({
limit = 25,
offset = 0,
actorId,
actorOrgId,
actorAuthMethod,
actor,
orgId
}: TListOrgSshCertificatesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Read, OrgPermissionSubjects.SshCertificates);
const cas = await sshCertificateAuthorityDAL.find({
orgId
});
const certificates = await sshCertificateDAL.find(
{
$in: {
sshCaId: cas.map((ca) => ca.id)
}
},
{ offset, limit, sort: [["updatedAt", "desc"]] }
);
const count = await sshCertificateDAL.countSshCertificatesInOrg(orgId);
return { certificates, totalCount: count };
};
/**
* Return list of SSH certificate templates for organization
*/
const listOrgSshCertificateTemplates = async ({
actorId,
actorOrgId,
actorAuthMethod,
actor,
orgId
}: TListOrgSshCertificateTemplatesDTO) => {
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
ForbiddenError.from(permission).throwUnlessCan(
OrgPermissionActions.Read,
OrgPermissionSubjects.SshCertificateTemplates
);
const cas = await sshCertificateAuthorityDAL.find({
orgId
});
const certificateTemplates = await sshCertificateTemplateDAL.find({
$in: {
sshCaId: cas.map((ca) => ca.id)
}
});
return { certificateTemplates };
};
return { return {
findOrganizationById, findOrganizationById,
findAllOrgMembers, findAllOrgMembers,
@@ -1245,9 +1148,6 @@ export const orgServiceFactory = ({
deleteIncidentContact, deleteIncidentContact,
getOrgGroups, getOrgGroups,
listProjectMembershipsByOrgMembershipId, listProjectMembershipsByOrgMembershipId,
findOrgBySlug, findOrgBySlug
listOrgSshCas,
listOrgSshCertificates,
listOrgSshCertificateTemplates
}; };
}; };
-7
View File
@@ -75,13 +75,6 @@ export type TListProjectMembershipsByOrgMembershipIdDTO = {
orgMembershipId: string; orgMembershipId: string;
} & TOrgPermission; } & TOrgPermission;
export type TListOrgSshCasDTO = TOrgPermission;
export type TListOrgSshCertificateTemplatesDTO = TOrgPermission;
export type TListOrgSshCertificatesDTO = {
offset: number;
limit: number;
} & TOrgPermission;
export enum OrgAuthMethod { export enum OrgAuthMethod {
OIDC = "oidc", OIDC = "oidc",
SAML = "saml" SAML = "saml"
@@ -8,6 +8,9 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service"; import { TProjectTemplateServiceFactory } from "@app/ee/services/project-template/project-template-service";
import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types"; import { InfisicalProjectTemplate } from "@app/ee/services/project-template/project-template-types";
import { TSshCertificateAuthorityDALFactory } from "@app/ee/services/ssh/ssh-certificate-authority-dal";
import { TSshCertificateDALFactory } from "@app/ee/services/ssh-certificate/ssh-certificate-dal";
import { TSshCertificateTemplateDALFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-dal";
import { TKeyStoreFactory } from "@app/keystore/keystore"; import { TKeyStoreFactory } from "@app/keystore/keystore";
import { isAtLeastAsPrivileged } from "@app/lib/casl"; import { isAtLeastAsPrivileged } from "@app/lib/casl";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
@@ -53,6 +56,9 @@ import {
TListProjectCertificateTemplatesDTO, TListProjectCertificateTemplatesDTO,
TListProjectCertsDTO, TListProjectCertsDTO,
TListProjectsDTO, TListProjectsDTO,
TListProjectSshCasDTO,
TListProjectSshCertificatesDTO,
TListProjectSshCertificateTemplatesDTO,
TLoadProjectKmsBackupDTO, TLoadProjectKmsBackupDTO,
TToggleProjectAutoCapitalizationDTO, TToggleProjectAutoCapitalizationDTO,
TUpdateAuditLogsRetentionDTO, TUpdateAuditLogsRetentionDTO,
@@ -90,6 +96,9 @@ type TProjectServiceFactoryDep = {
certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "getCertTemplatesByProjectId">; certificateTemplateDAL: Pick<TCertificateTemplateDALFactory, "getCertTemplatesByProjectId">;
pkiAlertDAL: Pick<TPkiAlertDALFactory, "find">; pkiAlertDAL: Pick<TPkiAlertDALFactory, "find">;
pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "find">; pkiCollectionDAL: Pick<TPkiCollectionDALFactory, "find">;
sshCertificateAuthorityDAL: Pick<TSshCertificateAuthorityDALFactory, "find">;
sshCertificateDAL: Pick<TSshCertificateDALFactory, "find" | "countSshCertificatesInProject">;
sshCertificateTemplateDAL: Pick<TSshCertificateTemplateDALFactory, "find">;
permissionService: TPermissionServiceFactory; permissionService: TPermissionServiceFactory;
orgService: Pick<TOrgServiceFactory, "addGhostUser">; orgService: Pick<TOrgServiceFactory, "addGhostUser">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
@@ -133,6 +142,9 @@ export const projectServiceFactory = ({
certificateTemplateDAL, certificateTemplateDAL,
pkiCollectionDAL, pkiCollectionDAL,
pkiAlertDAL, pkiAlertDAL,
sshCertificateAuthorityDAL,
sshCertificateDAL,
sshCertificateTemplateDAL,
keyStore, keyStore,
kmsService, kmsService,
projectBotDAL, projectBotDAL,
@@ -859,6 +871,115 @@ export const projectServiceFactory = ({
}; };
}; };
/**
* Return list of SSH CAs for project
*/
const listProjectSshCas = async ({
actorId,
actorOrgId,
actorAuthMethod,
actor,
projectId
}: TListProjectSshCasDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SshCertificateAuthorities
);
const cas = await sshCertificateAuthorityDAL.find(
{
projectId
},
{ sort: [["updatedAt", "desc"]] }
);
return cas;
};
/**
* Return list of SSH certificates for organization
*/
const listProjectSshCertificates = async ({
limit = 25,
offset = 0,
actorId,
actorOrgId,
actorAuthMethod,
actor,
projectId
}: TListProjectSshCertificatesDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates);
const cas = await sshCertificateAuthorityDAL.find({
projectId
});
const certificates = await sshCertificateDAL.find(
{
$in: {
sshCaId: cas.map((ca) => ca.id)
}
},
{ offset, limit, sort: [["updatedAt", "desc"]] }
);
const count = await sshCertificateDAL.countSshCertificatesInProject(projectId);
return { certificates, totalCount: count };
};
/**
* Return list of SSH certificate templates for organization
*/
const listProjectSshCertificateTemplates = async ({
actorId,
actorOrgId,
actorAuthMethod,
actor,
projectId
}: TListProjectSshCertificateTemplatesDTO) => {
const { permission } = await permissionService.getProjectPermission(
actor,
actorId,
projectId,
actorAuthMethod,
actorOrgId
);
ForbiddenError.from(permission).throwUnlessCan(
ProjectPermissionActions.Read,
ProjectPermissionSub.SshCertificateTemplates
);
const cas = await sshCertificateAuthorityDAL.find({
projectId
});
const certificateTemplates = await sshCertificateTemplateDAL.find({
$in: {
sshCaId: cas.map((ca) => ca.id)
}
});
return { certificateTemplates };
};
const updateProjectKmsKey = async ({ const updateProjectKmsKey = async ({
projectId, projectId,
kms, kms,
@@ -1092,6 +1213,9 @@ export const projectServiceFactory = ({
listProjectAlerts, listProjectAlerts,
listProjectPkiCollections, listProjectPkiCollections,
listProjectCertificateTemplates, listProjectCertificateTemplates,
listProjectSshCas,
listProjectSshCertificates,
listProjectSshCertificateTemplates,
updateVersionLimit, updateVersionLimit,
updateAuditLogsRetention, updateAuditLogsRetention,
updateProjectKmsKey, updateProjectKmsKey,
@@ -130,6 +130,13 @@ export type TGetProjectKmsKey = TProjectPermission;
export type TListProjectCertificateTemplatesDTO = TProjectPermission; export type TListProjectCertificateTemplatesDTO = TProjectPermission;
export type TListProjectSshCasDTO = TProjectPermission;
export type TListProjectSshCertificateTemplatesDTO = TProjectPermission;
export type TListProjectSshCertificatesDTO = {
offset: number;
limit: number;
} & TProjectPermission;
export type TGetProjectSlackConfig = TProjectPermission; export type TGetProjectSlackConfig = TProjectPermission;
export type TUpdateProjectSlackConfig = { export type TUpdateProjectSlackConfig = {
@@ -23,10 +23,7 @@ export enum OrgPermissionSubjects {
Kms = "kms", Kms = "kms",
AdminConsole = "organization-admin-console", AdminConsole = "organization-admin-console",
AuditLogs = "audit-logs", AuditLogs = "audit-logs",
ProjectTemplates = "project-templates", ProjectTemplates = "project-templates"
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificateTemplates = "ssh-certificate-templates",
SshCertificates = "ssh-certificates"
} }
export enum OrgPermissionAdminConsoleAction { export enum OrgPermissionAdminConsoleAction {
@@ -50,9 +47,6 @@ export type OrgPermissionSet =
| [OrgPermissionActions, OrgPermissionSubjects.Kms] | [OrgPermissionActions, OrgPermissionSubjects.Kms]
| [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole] | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]
| [OrgPermissionActions, OrgPermissionSubjects.AuditLogs] | [OrgPermissionActions, OrgPermissionSubjects.AuditLogs]
| [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates] | [OrgPermissionActions, OrgPermissionSubjects.ProjectTemplates];
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateAuthorities]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificates]
| [OrgPermissionActions, OrgPermissionSubjects.SshCertificateTemplates];
export type TOrgPermission = MongoAbility<OrgPermissionSet>; export type TOrgPermission = MongoAbility<OrgPermissionSet>;
@@ -81,6 +81,9 @@ export enum ProjectPermissionSub {
CertificateAuthorities = "certificate-authorities", CertificateAuthorities = "certificate-authorities",
Certificates = "certificates", Certificates = "certificates",
CertificateTemplates = "certificate-templates", CertificateTemplates = "certificate-templates",
SshCertificateAuthorities = "ssh-certificate-authorities",
SshCertificateTemplates = "ssh-certificate-templates",
SshCertificates = "ssh-certificates",
PkiAlerts = "pki-alerts", PkiAlerts = "pki-alerts",
PkiCollections = "pki-collections", PkiCollections = "pki-collections",
Kms = "kms", Kms = "kms",
@@ -155,6 +158,9 @@ export type ProjectPermissionSet =
| [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.CertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.Certificates] | [ProjectPermissionActions, ProjectPermissionSub.Certificates]
| [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates] | [ProjectPermissionActions, ProjectPermissionSub.CertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates]
| [ProjectPermissionActions, ProjectPermissionSub.SshCertificates]
| [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts] | [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts]
| [ProjectPermissionActions, ProjectPermissionSub.PkiCollections] | [ProjectPermissionActions, ProjectPermissionSub.PkiCollections]
| [ProjectPermissionActions.Delete, ProjectPermissionSub.Project] | [ProjectPermissionActions.Delete, ProjectPermissionSub.Project]
+2 -4
View File
@@ -19,8 +19,6 @@ export {
useGetOrgPmtMethods, useGetOrgPmtMethods,
useGetOrgTaxIds, useGetOrgTaxIds,
useGetOrgTrialUrl, useGetOrgTrialUrl,
useListOrgSshCas,
useListOrgSshCertificates,
useListOrgSshCertificateTemplates,
useUpdateOrg, useUpdateOrg,
useUpdateOrgBillingDetails} from "./queries"; useUpdateOrgBillingDetails
} from "./queries";
@@ -4,8 +4,6 @@ import { apiRequest } from "@app/config/request";
import { OrderByDirection } from "@app/hooks/api/generic/types"; import { OrderByDirection } from "@app/hooks/api/generic/types";
import { TGroupOrgMembership } from "../groups/types"; import { TGroupOrgMembership } from "../groups/types";
import { TSshCertificate,TSshCertificateAuthority } from "../ssh-ca/types";
import { TSshCertificateTemplate } from "../sshCertificateTemplates/types";
import { IntegrationAuth } from "../types"; import { IntegrationAuth } from "../types";
import { import {
BillingDetails, BillingDetails,
@@ -43,12 +41,7 @@ export const organizationKeys = {
}: TListOrgIdentitiesDTO) => }: TListOrgIdentitiesDTO) =>
[...organizationKeys.getOrgIdentityMemberships(orgId), params] as const, [...organizationKeys.getOrgIdentityMemberships(orgId), params] as const,
getOrgGroups: (orgId: string) => [{ orgId }, "organization-groups"] as const, getOrgGroups: (orgId: string) => [{ orgId }, "organization-groups"] as const,
getOrgIntegrationAuths: (orgId: string) => [{ orgId }, "integration-auths"] as const, getOrgIntegrationAuths: (orgId: string) => [{ orgId }, "integration-auths"] as const
getOrgSshCas: ({ orgId }: { orgId: string }) => [{ orgId }, "org-ssh-cas"] as const,
allOrgSshCertificates: () => ["org-ssh-certificates"] as const,
specificOrgSshCertificates: ({ offset, limit }: { offset: number; limit: number }) =>
[...organizationKeys.allOrgSshCertificates(), { offset, limit }] as const,
getOrgSshCertificateTemplates: () => ["org-ssh-certificate-templates"] as const
}; };
export const fetchOrganizations = async () => { export const fetchOrganizations = async () => {
@@ -502,63 +495,3 @@ export const useGetOrgIntegrationAuths = <TData = IntegrationAuth[],>(
select select
}); });
}; };
export const useListOrgSshCas = ({ orgId }: { orgId: string }) => {
return useQuery({
queryKey: organizationKeys.getOrgSshCas({ orgId }),
queryFn: async () => {
const {
data: { cas }
} = await apiRequest.get<{ cas: Omit<TSshCertificateAuthority, "publicKey">[] }>(
`/api/v1/organization/${orgId}/ssh-cas`
);
return cas;
},
enabled: Boolean(orgId)
});
};
export const useListOrgSshCertificates = ({
orgId,
offset,
limit
}: {
orgId: string;
offset: number;
limit: number;
}) => {
return useQuery({
queryKey: organizationKeys.specificOrgSshCertificates({
offset,
limit
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit)
});
const { data } = await apiRequest.get<{
certificates: TSshCertificate[];
totalCount: number;
}>(`/api/v1/organization/${orgId}/ssh-certificates`, {
params
});
return data;
},
enabled: Boolean(orgId)
});
};
export const useListOrgSshCertificateTemplates = ({ orgId }: { orgId: string }) => {
return useQuery({
queryKey: organizationKeys.getOrgSshCertificateTemplates(),
queryFn: async () => {
const { data } = await apiRequest.get<{ certificateTemplates: TSshCertificateTemplate[] }>(
`/api/v1/organization/${orgId}/ssh-certificate-templates`
);
return data;
},
enabled: Boolean(orgId)
});
};
+11 -11
View File
@@ -2,7 +2,7 @@ import { useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@app/config/request"; import { apiRequest } from "@app/config/request";
import { organizationKeys } from "../organization/queries"; import { workspaceKeys } from "../workspace/query-keys";
import { import {
TCreateSshCaDTO, TCreateSshCaDTO,
TDeleteSshCaDTO, TDeleteSshCaDTO,
@@ -27,8 +27,8 @@ export const useCreateSshCa = () => {
} = await apiRequest.post<{ ca: TSshCertificateAuthority }>("/api/v1/ssh/ca/", body); } = await apiRequest.post<{ ca: TSshCertificateAuthority }>("/api/v1/ssh/ca/", body);
return ca; return ca;
}, },
onSuccess: ({ orgId }) => { onSuccess: ({ projectId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId })); queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
} }
}); });
}; };
@@ -42,8 +42,8 @@ export const useUpdateSshCa = () => {
} = await apiRequest.patch<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`, body); } = await apiRequest.patch<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`, body);
return ca; return ca;
}, },
onSuccess: ({ orgId }, { caId }) => { onSuccess: ({ projectId }, { caId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId })); queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
queryClient.invalidateQueries(sshCaKeys.getSshCaById(caId)); queryClient.invalidateQueries(sshCaKeys.getSshCaById(caId));
} }
}); });
@@ -58,8 +58,8 @@ export const useDeleteSshCa = () => {
} = await apiRequest.delete<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`); } = await apiRequest.delete<{ ca: TSshCertificateAuthority }>(`/api/v1/ssh/ca/${caId}`);
return ca; return ca;
}, },
onSuccess: ({ orgId }) => { onSuccess: ({ projectId }) => {
queryClient.invalidateQueries(organizationKeys.getOrgSshCas({ orgId })); queryClient.invalidateQueries(workspaceKeys.getWorkspaceSshCas(projectId));
} }
}); });
}; };
@@ -71,8 +71,8 @@ export const useSignSshKey = () => {
const { data } = await apiRequest.post<TSignSshKeyResponse>("/api/v1/ssh/sign", body); const { data } = await apiRequest.post<TSignSshKeyResponse>("/api/v1/ssh/sign", body);
return data; return data;
}, },
onSuccess: () => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries(organizationKeys.allOrgSshCertificates()); queryClient.invalidateQueries(workspaceKeys.allWorkspaceSshCertificates(projectId));
} }
}); });
}; };
@@ -84,8 +84,8 @@ export const useIssueSshCreds = () => {
const { data } = await apiRequest.post<TIssueSshCredsResponse>("/api/v1/ssh/issue", body); const { data } = await apiRequest.post<TIssueSshCredsResponse>("/api/v1/ssh/issue", body);
return data; return data;
}, },
onSuccess: () => { onSuccess: (_, { projectId }) => {
queryClient.invalidateQueries(organizationKeys.allOrgSshCertificates()); queryClient.invalidateQueries(workspaceKeys.allWorkspaceSshCertificates(projectId));
} }
}); });
}; };
+4 -2
View File
@@ -7,7 +7,6 @@ export type TSshCertificate = {
sshCertificateTemplateId: string; sshCertificateTemplateId: string;
serialNumber: string; serialNumber: string;
certType: SshCertType; certType: SshCertType;
publicKey: string;
principals: string[]; principals: string[];
keyId: string; keyId: string;
notBefore: string; notBefore: string;
@@ -16,7 +15,7 @@ export type TSshCertificate = {
export type TSshCertificateAuthority = { export type TSshCertificateAuthority = {
id: string; id: string;
orgId: string; projectId: string;
status: SshCaStatus; status: SshCaStatus;
friendlyName: string; friendlyName: string;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
@@ -26,6 +25,7 @@ export type TSshCertificateAuthority = {
}; };
export type TCreateSshCaDTO = { export type TCreateSshCaDTO = {
projectId: string;
friendlyName?: string; friendlyName?: string;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
}; };
@@ -41,6 +41,7 @@ export type TDeleteSshCaDTO = {
}; };
export type TSignSshKeyDTO = { export type TSignSshKeyDTO = {
projectId: string;
templateName: string; templateName: string;
publicKey?: string; publicKey?: string;
certType: SshCertType; certType: SshCertType;
@@ -55,6 +56,7 @@ export type TSignSshKeyResponse = {
}; };
export type TIssueSshCredsDTO = { export type TIssueSshCredsDTO = {
projectId: string;
templateName: string; templateName: string;
keyAlgorithm: CertKeyAlgorithm; keyAlgorithm: CertKeyAlgorithm;
certType: SshCertType; certType: SshCertType;
@@ -32,6 +32,9 @@ export {
useListWorkspaceGroups, useListWorkspaceGroups,
useListWorkspacePkiAlerts, useListWorkspacePkiAlerts,
useListWorkspacePkiCollections, useListWorkspacePkiCollections,
useListWorkspaceSshCas,
useListWorkspaceSshCertificates,
useListWorkspaceSshCertificateTemplates,
useNameWorkspaceSecrets, useNameWorkspaceSecrets,
useToggleAutoCapitalization, useToggleAutoCapitalization,
useUpdateIdentityWorkspaceRole, useUpdateIdentityWorkspaceRole,
@@ -15,6 +15,8 @@ import { TIntegration } from "../integrations/types";
import { TPkiAlert } from "../pkiAlerts/types"; import { TPkiAlert } from "../pkiAlerts/types";
import { TPkiCollection } from "../pkiCollections/types"; import { TPkiCollection } from "../pkiCollections/types";
import { EncryptedSecret } from "../secrets/types"; import { EncryptedSecret } from "../secrets/types";
import { TSshCertificate, TSshCertificateAuthority } from "../ssh-ca/types";
import { TSshCertificateTemplate } from "../sshCertificateTemplates/types";
import { userKeys } from "../users/query-keys"; import { userKeys } from "../users/query-keys";
import { TWorkspaceUser } from "../users/types"; import { TWorkspaceUser } from "../users/types";
import { ProjectSlackConfig } from "../workflowIntegrations/types"; import { ProjectSlackConfig } from "../workflowIntegrations/types";
@@ -713,6 +715,67 @@ export const useListWorkspaceCertificateTemplates = ({ workspaceId }: { workspac
}); });
}; };
export const useListWorkspaceSshCertificates = ({
offset,
limit,
projectId
}: {
offset: number;
limit: number;
projectId: string;
}) => {
return useQuery({
queryKey: workspaceKeys.specificWorkspaceSshCertificates({
offset,
limit,
projectId
}),
queryFn: async () => {
const params = new URLSearchParams({
offset: String(offset),
limit: String(limit)
});
const { data } = await apiRequest.get<{
certificates: TSshCertificate[];
totalCount: number;
}>(`/api/v2/workspace/${projectId}/ssh-certificates`, {
params
});
return data;
},
enabled: Boolean(projectId)
});
};
export const useListWorkspaceSshCas = (projectId: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceSshCas(projectId),
queryFn: async () => {
const {
data: { cas }
} = await apiRequest.get<{ cas: Omit<TSshCertificateAuthority, "publicKey">[] }>(
`/api/v2/workspace/${projectId}/ssh-cas`
);
return cas;
},
enabled: Boolean(projectId)
});
};
export const useListWorkspaceSshCertificateTemplates = (projectId: string) => {
return useQuery({
queryKey: workspaceKeys.getWorkspaceSshCertificateTemplates(projectId),
queryFn: async () => {
const { data } = await apiRequest.get<{ certificateTemplates: TSshCertificateTemplate[] }>(
`/api/v2/workspace/${projectId}/ssh-certificate-templates`
);
return data;
},
enabled: Boolean(projectId)
});
};
export const useGetWorkspaceSlackConfig = ({ workspaceId }: { workspaceId: string }) => { export const useGetWorkspaceSlackConfig = ({ workspaceId }: { workspaceId: string }) => {
return useQuery({ return useQuery({
queryKey: workspaceKeys.getWorkspaceSlackConfig(workspaceId), queryKey: workspaceKeys.getWorkspaceSlackConfig(workspaceId),
@@ -52,5 +52,19 @@ export const workspaceKeys = {
getWorkspaceCertificateTemplates: (workspaceId: string) => getWorkspaceCertificateTemplates: (workspaceId: string) =>
[{ workspaceId }, "workspace-certificate-templates"] as const, [{ workspaceId }, "workspace-certificate-templates"] as const,
getWorkspaceSlackConfig: (workspaceId: string) => getWorkspaceSlackConfig: (workspaceId: string) =>
[{ workspaceId }, "workspace-slack-config"] as const [{ workspaceId }, "workspace-slack-config"] as const,
getWorkspaceSshCas: (projectId: string) => [{ projectId }, "workspace-ssh-cas"] as const,
allWorkspaceSshCertificates: (projectId: string) =>
[{ projectId }, "workspace-ssh-certificates"] as const,
specificWorkspaceSshCertificates: ({
offset,
limit,
projectId
}: {
offset: number;
limit: number;
projectId: string;
}) => [...workspaceKeys.allWorkspaceSshCertificates(projectId), { offset, limit }] as const,
getWorkspaceSshCertificateTemplates: (projectId: string) =>
[{ projectId }, "workspace-ssh-certificate-templates"] as const
}; };
+10 -10
View File
@@ -424,6 +424,16 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
<Link href={`/project/${currentWorkspace?.id}/ssh`} passHref>
<a>
<MenuItem
isSelected={router.asPath === `/project/${currentWorkspace?.id}/ssh`}
icon="system-outline-90-lock-closed"
>
SSH
</MenuItem>
</a>
</Link>
<Link href={`/project/${currentWorkspace?.id}/members`} passHref> <Link href={`/project/${currentWorkspace?.id}/members`} passHref>
<a> <a>
<MenuItem <MenuItem
@@ -513,16 +523,6 @@ export const AppLayout = ({ children }: LayoutProps) => {
</MenuItem> </MenuItem>
</a> </a>
</Link> </Link>
<Link href={`/org/${currentOrg?.id}/ssh`} passHref>
<a>
<MenuItem
isSelected={router.asPath === `/org/${currentOrg?.id}/ssh`}
icon="system-outline-90-lock-closed"
>
SSH
</MenuItem>
</a>
</Link>
<Link href={`/org/${currentOrg?.id}/secret-scanning`} passHref> <Link href={`/org/${currentOrg?.id}/secret-scanning`} passHref>
<a> <a>
<MenuItem <MenuItem
-26
View File
@@ -1,26 +0,0 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { SshPage } from "@app/views/Org/SshPage";
const Ssh = () => {
const { t } = useTranslation();
return (
<>
<Head>
<title>{t("common.head-title", { title: t("approval.title") })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={String(t("approval.og-title"))} />
<meta name="og:description" content={String(t("approval.og-description"))} />
</Head>
<div className="h-full">
<SshPage />
</div>
</>
);
};
export default Ssh;
Ssh.requireAuth = true;
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-unused-vars */
import Head from "next/head"; import Head from "next/head";
import { SshCaPage } from "@app/views/Org/SshCaPage"; import { SshCaPage } from "@app/views/Project/SshCaPage";
export default function SshCa() { export default function SshCa() {
return ( return (
@@ -0,0 +1,23 @@
import { useTranslation } from "react-i18next";
import Head from "next/head";
import { SshPage } from "@app/views/Project/SshPage";
const Ssh = () => {
const { t } = useTranslation();
return (
<div className="h-full bg-bunker-800">
<Head>
<title>{t("common.head-title", { title: "Certificates" })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
</Head>
<SshPage />
</div>
);
};
export default Ssh;
Ssh.requireAuth = true;
@@ -49,10 +49,7 @@ export const formSchema = z.object({
identity: generalPermissionSchema, identity: generalPermissionSchema,
"organization-admin-console": adminConsolePermissionSchmea, "organization-admin-console": adminConsolePermissionSchmea,
[OrgPermissionSubjects.Kms]: generalPermissionSchema, [OrgPermissionSubjects.Kms]: generalPermissionSchema,
[OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema, [OrgPermissionSubjects.ProjectTemplates]: generalPermissionSchema
[OrgPermissionSubjects.SshCertificateAuthorities]: generalPermissionSchema,
[OrgPermissionSubjects.SshCertificates]: generalPermissionSchema,
[OrgPermissionSubjects.SshCertificateTemplates]: generalPermissionSchema
}) })
.optional() .optional()
}); });
@@ -69,19 +69,7 @@ const SIMPLE_PERMISSION_OPTIONS = [
title: "External KMS", title: "External KMS",
formName: OrgPermissionSubjects.Kms formName: OrgPermissionSubjects.Kms
}, },
{ title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates }, { title: "Project Templates", formName: OrgPermissionSubjects.ProjectTemplates }
{
title: "SSH Certificate Authorities",
formName: OrgPermissionSubjects.SshCertificateAuthorities
},
{
title: "SSH Certificates",
formName: OrgPermissionSubjects.SshCertificates
},
{
title: "SSH Certificate Templates",
formName: OrgPermissionSubjects.SshCertificateTemplates
}
] as const; ] as const;
type Props = { type Props = {
@@ -121,6 +121,11 @@ export const projectRoleFormSchema = z.object({
[ProjectPermissionSub.PkiAlerts]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.PkiAlerts]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.PkiCollections]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.PkiCollections]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.CertificateTemplates]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.CertificateTemplates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SshCertificateAuthorities]: GeneralPolicyActionSchema.array().default(
[]
),
[ProjectPermissionSub.SshCertificates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SshCertificateTemplates]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretApproval]: GeneralPolicyActionSchema.array().default([]), [ProjectPermissionSub.SecretApproval]: GeneralPolicyActionSchema.array().default([]),
[ProjectPermissionSub.SecretRollback]: SecretRollbackPolicyActionSchema.array().default([]), [ProjectPermissionSub.SecretRollback]: SecretRollbackPolicyActionSchema.array().default([]),
[ProjectPermissionSub.Project]: WorkspacePolicyActionSchema.array().default([]), [ProjectPermissionSub.Project]: WorkspacePolicyActionSchema.array().default([]),
@@ -203,6 +208,9 @@ export const rolePermission2Form = (permissions: TProjectPermission[] = []) => {
ProjectPermissionSub.PkiAlerts, ProjectPermissionSub.PkiAlerts,
ProjectPermissionSub.PkiCollections, ProjectPermissionSub.PkiCollections,
ProjectPermissionSub.CertificateTemplates, ProjectPermissionSub.CertificateTemplates,
ProjectPermissionSub.SshCertificateAuthorities,
ProjectPermissionSub.SshCertificates,
ProjectPermissionSub.SshCertificateTemplates,
ProjectPermissionSub.SecretApproval, ProjectPermissionSub.SecretApproval,
ProjectPermissionSub.Tags, ProjectPermissionSub.Tags,
ProjectPermissionSub.SecretRotation, ProjectPermissionSub.SecretRotation,
@@ -589,6 +597,33 @@ export const PROJECT_PERMISSION_OBJECT: TProjectPermissionObject = {
{ label: "Remove", value: "delete" } { label: "Remove", value: "delete" }
] ]
}, },
[ProjectPermissionSub.SshCertificateAuthorities]: {
title: "SSH Certificate Authorities",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.SshCertificates]: {
title: "SSH Certificates",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.SshCertificateTemplates]: {
title: "SSH Certificate Templates",
actions: [
{ label: "Read", value: "read" },
{ label: "Create", value: "create" },
{ label: "Modify", value: "edit" },
{ label: "Remove", value: "delete" }
]
},
[ProjectPermissionSub.PkiCollections]: { [ProjectPermissionSub.PkiCollections]: {
title: "PKI Collections", title: "PKI Collections",
actions: [ actions: [
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Button, Button,
DeleteActionModal, DeleteActionModal,
@@ -15,17 +15,18 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
Tooltip Tooltip
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context";
import { withPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { useDeleteSshCa, useGetSshCaById } from "@app/hooks/api"; import { useDeleteSshCa, useGetSshCaById } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { SshCaModal } from "../SshPage/components/SshCaModal"; import { SshCaModal } from "../SshPage/components/SshCaModal";
import { SshCaDetailsSection, SshCertificateTemplatesSection } from "./components"; import { SshCaDetailsSection, SshCertificateTemplatesSection } from "./components";
export const SshCaPage = withPermission( export const SshCaPage = withProjectPermission(
() => { () => {
const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const router = useRouter(); const router = useRouter();
const caId = router.query.caId as string; const caId = router.query.caId as string;
const { data } = useGetSshCaById(caId); const { data } = useGetSshCaById(caId);
@@ -39,7 +40,7 @@ export const SshCaPage = withPermission(
const onRemoveCaSubmit = async (caIdToDelete: string) => { const onRemoveCaSubmit = async (caIdToDelete: string) => {
try { try {
if (!currentOrg?.id) return; if (!projectId) return;
await deleteSshCa({ caId: caIdToDelete }); await deleteSshCa({ caId: caIdToDelete });
@@ -49,7 +50,7 @@ export const SshCaPage = withPermission(
}); });
handlePopUpClose("deleteSshCa"); handlePopUpClose("deleteSshCa");
router.push(`/org/${currentOrg.id}/ssh`); router.push(`/project/${projectId}/ssh`);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
createNotification({ createNotification({
@@ -67,7 +68,7 @@ export const SshCaPage = withPermission(
variant="link" variant="link"
type="submit" type="submit"
leftIcon={<FontAwesomeIcon icon={faChevronLeft} />} leftIcon={<FontAwesomeIcon icon={faChevronLeft} />}
onClick={() => router.push(`/org/${currentOrg?.id}/ssh`)} onClick={() => router.push(`/project/${projectId}/ssh`)}
className="mb-4" className="mb-4"
> >
SSH Certificate Authorities SSH Certificate Authorities
@@ -83,9 +84,9 @@ export const SshCaPage = withPermission(
</div> </div>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1"> <DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateAuthorities} a={ProjectPermissionSub.SshCertificateAuthorities}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -104,7 +105,7 @@ export const SshCaPage = withPermission(
Delete SSH CA Delete SSH CA
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</div> </div>
@@ -131,5 +132,5 @@ export const SshCaPage = withPermission(
</div> </div>
); );
}, },
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.SshCertificateAuthorities } { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.SshCertificateAuthorities }
); );
@@ -1,10 +1,10 @@
import { faCheck, faCopy, faDownload,faPencil } from "@fortawesome/free-solid-svg-icons"; import { faCheck, faCopy, faDownload, faPencil } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import FileSaver from "file-saver"; import FileSaver from "file-saver";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { IconButton, Tooltip } from "@app/components/v2"; import { IconButton, Tooltip } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { useTimedReset } from "@app/hooks"; import { useTimedReset } from "@app/hooks";
import { useGetSshCaById } from "@app/hooks/api"; import { useGetSshCaById } from "@app/hooks/api";
import { caStatusToNameMap } from "@app/hooks/api/ca/constants"; import { caStatusToNameMap } from "@app/hooks/api/ca/constants";
@@ -35,9 +35,9 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <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"> <div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">SSH CA Details</h3> <h3 className="text-lg font-semibold text-mineshaft-100">SSH CA Details</h3>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateAuthorities} a={ProjectPermissionSub.SshCertificateAuthorities}
> >
{(isAllowed) => { {(isAllowed) => {
return ( return (
@@ -59,7 +59,7 @@ export const SshCaDetailsSection = ({ caId, handlePopUpOpen }: Props) => {
</Tooltip> </Tooltip>
); );
}} }}
</OrgPermissionCan> </ProjectPermissionCan>
</div> </div>
<div className="pt-4"> <div className="pt-4">
<div className="mb-4"> <div className="mb-4">
@@ -13,13 +13,14 @@ import {
Select, Select,
SelectItem SelectItem
} from "@app/components/v2"; } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useWorkspace } from "@app/context";
import { import {
SshCertTemplateStatus, SshCertTemplateStatus,
useGetSshCertTemplate, useGetSshCertTemplate,
useIssueSshCreds, useIssueSshCreds,
useListOrgSshCertificateTemplates, useListWorkspaceSshCertificateTemplates,
useSignSshKey} from "@app/hooks/api"; useSignSshKey
} from "@app/hooks/api";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants"; import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums"; import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
import { SshCertType } from "@app/hooks/api/ssh-ca/constants"; import { SshCertType } from "@app/hooks/api/ssh-ca/constants";
@@ -62,7 +63,8 @@ enum SshCertificateOperation {
} }
export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => { export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const [operation, setOperation] = useState<SshCertificateOperation>( const [operation, setOperation] = useState<SshCertificateOperation>(
SshCertificateOperation.SIGN_SSH_KEY SshCertificateOperation.SIGN_SSH_KEY
); );
@@ -77,9 +79,7 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
templateId: string; templateId: string;
}; };
const { data: templatesData } = useListOrgSshCertificateTemplates({ const { data: templatesData } = useListWorkspaceSshCertificateTemplates(projectId);
orgId: currentOrg?.id || ""
});
const { const {
control, control,
@@ -117,10 +117,12 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
}: FormData) => { }: FormData) => {
try { try {
if (!templateData) return; if (!templateData) return;
if (!projectId) return;
switch (operation) { switch (operation) {
case SshCertificateOperation.SIGN_SSH_KEY: { case SshCertificateOperation.SIGN_SSH_KEY: {
const { serialNumber, signedKey } = await signSshKey({ const { serialNumber, signedKey } = await signSshKey({
projectId: currentWorkspace?.id || "",
templateName: templateData.name, templateName: templateData.name,
publicKey: existingPublicKey, publicKey: existingPublicKey,
certType, certType,
@@ -137,6 +139,7 @@ export const SshCertificateModal = ({ popUp, handlePopUpToggle }: Props) => {
} }
case SshCertificateOperation.ISSUE_SSH_CREDS: { case SshCertificateOperation.ISSUE_SSH_CREDS: {
const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({ const { serialNumber, publicKey, privateKey, signedKey } = await issueSshCreds({
projectId,
templateName: templateData.name, templateName: templateData.name,
keyAlgorithm, keyAlgorithm,
certType, certType,
@@ -14,12 +14,12 @@ import {
SelectItem, SelectItem,
Switch Switch
} from "@app/components/v2"; } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useWorkspace } from "@app/context";
import { import {
useCreateSshCertTemplate, useCreateSshCertTemplate,
useGetSshCaById, useGetSshCaById,
useGetSshCertTemplate, useGetSshCertTemplate,
useListOrgSshCas, useListWorkspaceSshCas,
useUpdateSshCertTemplate useUpdateSshCertTemplate
} from "@app/hooks/api"; } from "@app/hooks/api";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -48,7 +48,7 @@ type Props = {
}; };
export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId }: Props) => { export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId }: Props) => {
const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace();
const { data: ca } = useGetSshCaById(sshCaId); const { data: ca } = useGetSshCaById(sshCaId);
@@ -56,9 +56,7 @@ export const SshCertificateTemplateModal = ({ popUp, handlePopUpToggle, sshCaId
(popUp?.sshCertificateTemplate?.data as { id: string })?.id || "" (popUp?.sshCertificateTemplate?.data as { id: string })?.id || ""
); );
const { data: cas } = useListOrgSshCas({ const { data: cas } = useListWorkspaceSshCas(currentWorkspace?.id || "");
orgId: currentOrg?.id ?? ""
});
const { mutateAsync: createSshCertTemplate } = useCreateSshCertTemplate(); const { mutateAsync: createSshCertTemplate } = useCreateSshCertTemplate();
const { mutateAsync: updateSshCertTemplate } = useUpdateSshCertTemplate(); const { mutateAsync: updateSshCertTemplate } = useUpdateSshCertTemplate();
@@ -2,14 +2,15 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { DeleteActionModal, IconButton } from "@app/components/v2"; import { DeleteActionModal, IconButton } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { usePopUp } from "@app/hooks"; import { usePopUp } from "@app/hooks";
import { import {
SshCertTemplateStatus, SshCertTemplateStatus,
useDeleteSshCertTemplate, useDeleteSshCertTemplate,
useUpdateSshCertTemplate} from "@app/hooks/api"; useUpdateSshCertTemplate
} from "@app/hooks/api";
import { SshCertificateModal } from "./SshCertificateModal"; import { SshCertificateModal } from "./SshCertificateModal";
import { SshCertificateTemplateModal } from "./SshCertificateTemplateModal"; import { SshCertificateTemplateModal } from "./SshCertificateTemplateModal";
@@ -85,9 +86,9 @@ export const SshCertificateTemplatesSection = ({ caId }: Props) => {
<div className="h-full rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="h-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"> <div className="flex items-center justify-between border-b border-mineshaft-400 pb-4">
<h3 className="text-lg font-semibold text-mineshaft-100">Certificate Templates</h3> <h3 className="text-lg font-semibold text-mineshaft-100">Certificate Templates</h3>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Create} I={ProjectPermissionActions.Create}
a={OrgPermissionSubjects.SshCertificateTemplates} a={ProjectPermissionSub.SshCertificateTemplates}
> >
{(isAllowed) => ( {(isAllowed) => (
<IconButton <IconButton
@@ -100,7 +101,7 @@ export const SshCertificateTemplatesSection = ({ caId }: Props) => {
<FontAwesomeIcon icon={faPlus} /> <FontAwesomeIcon icon={faPlus} />
</IconButton> </IconButton>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</div> </div>
<div className="py-4"> <div className="py-4">
<SshCertificateTemplatesTable handlePopUpOpen={handlePopUpOpen} sshCaId={caId} /> <SshCertificateTemplatesTable handlePopUpOpen={handlePopUpOpen} sshCaId={caId} />
@@ -8,7 +8,7 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Badge, Badge,
DropdownMenu, DropdownMenu,
@@ -26,8 +26,8 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { SshCertTemplateStatus,useGetSshCaCertTemplates } from "@app/hooks/api"; import { SshCertTemplateStatus, useGetSshCaCertTemplates } from "@app/hooks/api";
import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants"; import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -89,9 +89,9 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
</div> </div>
</DropdownMenuTrigger> </DropdownMenuTrigger>
<DropdownMenuContent align="start" className="p-1"> <DropdownMenuContent align="start" className="p-1">
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates} a={ProjectPermissionSub.SshCertificateTemplates}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -118,10 +118,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
} Template`} } Template`}
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates} a={ProjectPermissionSub.SshCertificateTemplates}
> >
<DropdownMenuItem <DropdownMenuItem
onClick={() => { onClick={() => {
@@ -136,10 +136,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
> >
Issue Certificate Issue Certificate
</DropdownMenuItem> </DropdownMenuItem>
</OrgPermissionCan> </ProjectPermissionCan>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateTemplates} a={ProjectPermissionSub.SshCertificateTemplates}
> >
<DropdownMenuItem <DropdownMenuItem
onClick={() => onClick={() =>
@@ -151,10 +151,10 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
> >
Edit Template Edit Template
</DropdownMenuItem> </DropdownMenuItem>
</OrgPermissionCan> </ProjectPermissionCan>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateTemplates} a={ProjectPermissionSub.SshCertificateTemplates}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -173,7 +173,7 @@ export const SshCertificateTemplatesTable = ({ handlePopUpOpen, sshCaId }: Props
Delete Template Delete Template
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</Td> </Td>
@@ -1,8 +1,8 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2"; import { Tab, TabList, TabPanel, Tabs } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { withPermission } from "@app/hoc"; import { withProjectPermission } from "@app/hoc";
import { SshCaSection, SshCertificatesSection } from "./components"; import { SshCaSection, SshCertificatesSection } from "./components";
@@ -11,7 +11,7 @@ enum TabSections {
SshCertificates = "ssh-certificates" SshCertificates = "ssh-certificates"
} }
export const SshPage = withPermission( export const SshPage = withProjectPermission(
() => { () => {
return ( return (
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white"> <div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
@@ -49,5 +49,5 @@ export const SshPage = withPermission(
</div> </div>
); );
}, },
{ action: OrgPermissionActions.Read, subject: OrgPermissionSubjects.SshCertificateAuthorities } { action: ProjectPermissionActions.Read, subject: ProjectPermissionSub.SshCertificateAuthorities }
); );
@@ -13,6 +13,7 @@ import {
Select, Select,
SelectItem SelectItem
} from "@app/components/v2"; } from "@app/components/v2";
import { useWorkspace } from "@app/context";
import { useCreateSshCa, useGetSshCaById, useUpdateSshCa } from "@app/hooks/api"; import { useCreateSshCa, useGetSshCaById, useUpdateSshCa } from "@app/hooks/api";
import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants"; import { certKeyAlgorithms } from "@app/hooks/api/certificates/constants";
import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums"; import { CertKeyAlgorithm } from "@app/hooks/api/certificates/enums";
@@ -38,6 +39,8 @@ const schema = z
export type FormData = z.infer<typeof schema>; export type FormData = z.infer<typeof schema>;
export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => { export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const { currentWorkspace } = useWorkspace();
const projectId = currentWorkspace?.id || "";
const { data: ca } = useGetSshCaById((popUp?.sshCa?.data as { caId: string })?.caId || ""); const { data: ca } = useGetSshCaById((popUp?.sshCa?.data as { caId: string })?.caId || "");
const { mutateAsync: createMutateAsync } = useCreateSshCa(); const { mutateAsync: createMutateAsync } = useCreateSshCa();
@@ -72,6 +75,8 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
const onFormSubmit = async ({ friendlyName, keyAlgorithm }: FormData) => { const onFormSubmit = async ({ friendlyName, keyAlgorithm }: FormData) => {
try { try {
if (!projectId) return;
if (ca) { if (ca) {
await updateMutateAsync({ await updateMutateAsync({
caId: ca.id, caId: ca.id,
@@ -79,6 +84,7 @@ export const SshCaModal = ({ popUp, handlePopUpToggle }: Props) => {
}); });
} else { } else {
await createMutateAsync({ await createMutateAsync({
projectId,
friendlyName, friendlyName,
keyAlgorithm keyAlgorithm
}); });
@@ -2,9 +2,9 @@ import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { createNotification } from "@app/components/notifications"; import { createNotification } from "@app/components/notifications";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { Button, DeleteActionModal } from "@app/components/v2"; import { Button, DeleteActionModal } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { SshCaStatus, useDeleteSshCa, useUpdateSshCa } from "@app/hooks/api"; import { SshCaStatus, useDeleteSshCa, useUpdateSshCa } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
@@ -64,9 +64,9 @@ export const SshCaSection = () => {
<div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="mb-6 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex justify-between"> <div className="mb-4 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Certificate Authorities</p> <p className="text-xl font-semibold text-mineshaft-100">Certificate Authorities</p>
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Create} I={ProjectPermissionActions.Create}
a={OrgPermissionSubjects.SshCertificateAuthorities} a={ProjectPermissionSub.SshCertificateAuthorities}
> >
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
@@ -79,7 +79,7 @@ export const SshCaSection = () => {
Create SSH CA Create SSH CA
</Button> </Button>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</div> </div>
<SshCaTable handlePopUpOpen={handlePopUpOpen} /> <SshCaTable handlePopUpOpen={handlePopUpOpen} />
<SshCaModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <SshCaModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
@@ -3,7 +3,7 @@ import { faBan, faCertificate, faEllipsis, faTrash } from "@fortawesome/free-sol
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { import {
Badge, Badge,
DropdownMenu, DropdownMenu,
@@ -21,8 +21,8 @@ import {
Tooltip, Tooltip,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects, useOrganization } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub,useWorkspace } from "@app/context";
import { SshCaStatus , useListOrgSshCas } from "@app/hooks/api"; import { SshCaStatus, useListWorkspaceSshCas } from "@app/hooks/api";
import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants"; import { caStatusToNameMap, getCaStatusBadgeVariant } from "@app/hooks/api/ca/constants";
import { UsePopUpState } from "@app/hooks/usePopUp"; import { UsePopUpState } from "@app/hooks/usePopUp";
@@ -35,10 +35,8 @@ type Props = {
export const SshCaTable = ({ handlePopUpOpen }: Props) => { export const SshCaTable = ({ handlePopUpOpen }: Props) => {
const router = useRouter(); const router = useRouter();
const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace();
const { data, isLoading } = useListOrgSshCas({ const { data, isLoading } = useListWorkspaceSshCas(currentWorkspace?.id || "");
orgId: currentOrg?.id ?? ""
});
return ( return (
<div> <div>
@@ -61,7 +59,7 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
<Tr <Tr
className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700" className="h-10 cursor-pointer transition-colors duration-100 hover:bg-mineshaft-700"
key={`ca-${ca.id}`} key={`ca-${ca.id}`}
onClick={() => router.push(`/org/${currentOrg?.id}/ssh/ca/${ca.id}`)} onClick={() => router.push(`/project/${currentWorkspace?.id}/ssh/ca/${ca.id}`)}
> >
<Td>{ca.friendlyName}</Td> <Td>{ca.friendlyName}</Td>
<Td> <Td>
@@ -81,9 +79,9 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
<DropdownMenuContent align="start" className="p-1"> <DropdownMenuContent align="start" className="p-1">
{(ca.status === SshCaStatus.ACTIVE || {(ca.status === SshCaStatus.ACTIVE ||
ca.status === SshCaStatus.DISABLED) && ( ca.status === SshCaStatus.DISABLED) && (
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Edit} I={ProjectPermissionActions.Edit}
a={OrgPermissionSubjects.SshCertificateAuthorities} a={ProjectPermissionSub.SshCertificateAuthorities}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -109,11 +107,11 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
} SSH CA`} } SSH CA`}
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
)} )}
<OrgPermissionCan <ProjectPermissionCan
I={OrgPermissionActions.Delete} I={ProjectPermissionActions.Delete}
a={OrgPermissionSubjects.SshCertificateAuthorities} a={ProjectPermissionSub.SshCertificateAuthorities}
> >
{(isAllowed) => ( {(isAllowed) => (
<DropdownMenuItem <DropdownMenuItem
@@ -132,7 +130,7 @@ export const SshCaTable = ({ handlePopUpOpen }: Props) => {
Delete SSH CA Delete SSH CA
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
</Td> </Td>
@@ -1,9 +1,9 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { OrgPermissionCan } from "@app/components/permissions"; import { ProjectPermissionCan } from "@app/components/permissions";
import { Button } from "@app/components/v2"; import { Button } from "@app/components/v2";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/context"; import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { SshCertificateModal } from "../../SshCaPage/components/SshCertificateModal"; import { SshCertificateModal } from "../../SshCaPage/components/SshCertificateModal";
@@ -15,7 +15,10 @@ export const SshCertificatesSection = () => {
<div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4"> <div className="rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
<div className="mb-4 flex justify-between"> <div className="mb-4 flex justify-between">
<p className="text-xl font-semibold text-mineshaft-100">Certificates</p> <p className="text-xl font-semibold text-mineshaft-100">Certificates</p>
<OrgPermissionCan I={OrgPermissionActions.Create} a={OrgPermissionSubjects.SshCertificates}> <ProjectPermissionCan
I={ProjectPermissionActions.Create}
a={ProjectPermissionSub.SshCertificates}
>
{(isAllowed) => ( {(isAllowed) => (
<Button <Button
colorSchema="primary" colorSchema="primary"
@@ -27,7 +30,7 @@ export const SshCertificatesSection = () => {
Request Request
</Button> </Button>
)} )}
</OrgPermissionCan> </ProjectPermissionCan>
</div> </div>
<SshCertificatesTable /> <SshCertificatesTable />
<SshCertificateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} /> <SshCertificateModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} />
@@ -15,20 +15,20 @@ import {
THead, THead,
Tr Tr
} from "@app/components/v2"; } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useWorkspace } from "@app/context";
import { useListOrgSshCertificates } from "@app/hooks/api"; import { useListWorkspaceSshCertificates } from "@app/hooks/api";
import { getSshCertStatusBadgeDetails } from "./SshCertificatesTable.utils"; import { getSshCertStatusBadgeDetails } from "./SshCertificatesTable.utils";
const PER_PAGE_INIT = 25; const PER_PAGE_INIT = 25;
export const SshCertificatesTable = () => { export const SshCertificatesTable = () => {
const { currentOrg } = useOrganization(); const { currentWorkspace } = useWorkspace();
const [page, setPage] = useState(1); const [page, setPage] = useState(1);
const [perPage, setPerPage] = useState(PER_PAGE_INIT); const [perPage, setPerPage] = useState(PER_PAGE_INIT);
const { data, isLoading } = useListOrgSshCertificates({ const { data, isLoading } = useListWorkspaceSshCertificates({
orgId: currentOrg?.id ?? "", projectId: currentWorkspace?.id || "",
offset: (page - 1) * perPage, offset: (page - 1) * perPage,
limit: perPage limit: perPage
}); });