diff --git a/backend/src/@types/fastify.d.ts b/backend/src/@types/fastify.d.ts index 5d10830ad..389bca823 100644 --- a/backend/src/@types/fastify.d.ts +++ b/backend/src/@types/fastify.d.ts @@ -37,6 +37,7 @@ import { TSecretScanningServiceFactory } from "@app/ee/services/secret-scanning/ import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service"; import { TSshCertificateAuthorityServiceFactory } from "@app/ee/services/ssh/ssh-certificate-authority-service"; import { TSshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service"; +import { TSshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service"; import { TTrustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service"; import { TAuthMode } from "@app/server/plugins/auth/inject-identity"; import { TApiKeyServiceFactory } from "@app/services/api-key/api-key-service"; @@ -205,6 +206,7 @@ declare module "fastify" { certificateTemplate: TCertificateTemplateServiceFactory; sshCertificateAuthority: TSshCertificateAuthorityServiceFactory; sshCertificateTemplate: TSshCertificateTemplateServiceFactory; + sshHost: TSshHostServiceFactory; certificateAuthority: TCertificateAuthorityServiceFactory; certificateAuthorityCrl: TCertificateAuthorityCrlServiceFactory; certificateEst: TCertificateEstServiceFactory; diff --git a/backend/src/@types/knex.d.ts b/backend/src/@types/knex.d.ts index 346b08e4c..5ad27a605 100644 --- a/backend/src/@types/knex.d.ts +++ b/backend/src/@types/knex.d.ts @@ -356,6 +356,12 @@ import { TSshCertificateTemplates, TSshCertificateTemplatesInsert, TSshCertificateTemplatesUpdate, + TSshHosts, + TSshHostsInsert, + TSshHostsUpdate, + TSshHostLoginMappings, + TSshHostLoginMappingsInsert, + TSshHostLoginMappingsUpdate, TSuperAdmin, TSuperAdminInsert, TSuperAdminUpdate, @@ -419,6 +425,7 @@ declare module "knex/types/tables" { interface Tables { [TableName.Users]: KnexOriginal.CompositeTableType; [TableName.Groups]: KnexOriginal.CompositeTableType; + [TableName.SshHost]: KnexOriginal.CompositeTableType; [TableName.SshCertificateAuthority]: KnexOriginal.CompositeTableType< TSshCertificateAuthorities, TSshCertificateAuthoritiesInsert, @@ -444,6 +451,11 @@ declare module "knex/types/tables" { TSshCertificateBodiesInsert, TSshCertificateBodiesUpdate >; + [TableName.SshHostLoginMapping]: KnexOriginal.CompositeTableType< + TSshHostLoginMappings, + TSshHostLoginMappingsInsert, + TSshHostLoginMappingsUpdate + >; [TableName.CertificateAuthority]: KnexOriginal.CompositeTableType< TCertificateAuthorities, TCertificateAuthoritiesInsert, diff --git a/backend/src/db/migrations/20250405185753_ssh-mgmt-v2.ts b/backend/src/db/migrations/20250405185753_ssh-mgmt-v2.ts new file mode 100644 index 000000000..14616da8d --- /dev/null +++ b/backend/src/db/migrations/20250405185753_ssh-mgmt-v2.ts @@ -0,0 +1,39 @@ +import { Knex } from "knex"; + +import { TableName } from "../schemas"; +import { createOnUpdateTrigger, dropOnUpdateTrigger } from "../utils"; + +export async function up(knex: Knex): Promise { + if (!(await knex.schema.hasTable(TableName.SshHost))) { + await knex.schema.createTable(TableName.SshHost, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.timestamps(true, true, true); + t.string("projectId").notNullable(); + t.foreign("projectId").references("id").inTable(TableName.Project).onDelete("CASCADE"); + t.string("hostname").notNullable(); + t.string("userCertTtl").notNullable(); + t.string("hostCertTtl").notNullable(); + }); + await createOnUpdateTrigger(knex, TableName.SshHost); + } + + if (!(await knex.schema.hasTable(TableName.SshHostLoginMapping))) { + await knex.schema.createTable(TableName.SshHostLoginMapping, (t) => { + t.uuid("id", { primaryKey: true }).defaultTo(knex.fn.uuid()); + t.timestamps(true, true, true); + t.uuid("sshHostId").notNullable(); + t.foreign("sshHostId").references("id").inTable(TableName.SshHost).onDelete("CASCADE"); + t.string("loginUser").notNullable(); + t.specificType("allowedPrincipals", "text[]").notNullable(); + }); + await createOnUpdateTrigger(knex, TableName.SshHostLoginMapping); + } +} + +export async function down(knex: Knex): Promise { + await knex.schema.dropTableIfExists(TableName.SshHostLoginMapping); + await dropOnUpdateTrigger(knex, TableName.SshHostLoginMapping); + + await knex.schema.dropTableIfExists(TableName.SshHost); + await dropOnUpdateTrigger(knex, TableName.SshHost); +} diff --git a/backend/src/db/schemas/access-approval-policies-approvers.ts b/backend/src/db/schemas/access-approval-policies-approvers.ts index 1ecd80513..db61e60c5 100644 --- a/backend/src/db/schemas/access-approval-policies-approvers.ts +++ b/backend/src/db/schemas/access-approval-policies-approvers.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AccessApprovalPoliciesApproversSchema = z.object({ @@ -17,10 +19,5 @@ export const AccessApprovalPoliciesApproversSchema = z.object({ }); export type TAccessApprovalPoliciesApprovers = z.infer; -export type TAccessApprovalPoliciesApproversInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TAccessApprovalPoliciesApproversUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TAccessApprovalPoliciesApproversInsert = Omit, TImmutableDBKeys>; +export type TAccessApprovalPoliciesApproversUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/access-approval-policies.ts b/backend/src/db/schemas/access-approval-policies.ts index 19a98675f..9f207e2d4 100644 --- a/backend/src/db/schemas/access-approval-policies.ts +++ b/backend/src/db/schemas/access-approval-policies.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AccessApprovalPoliciesSchema = z.object({ @@ -15,13 +17,11 @@ export const AccessApprovalPoliciesSchema = z.object({ envId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), - enforcementLevel: z.string().default("hard"), + enforcementLevel: z.string().default('hard'), deletedAt: z.date().nullable().optional(), allowedSelfApprovals: z.boolean().default(true) }); export type TAccessApprovalPolicies = z.infer; export type TAccessApprovalPoliciesInsert = Omit, TImmutableDBKeys>; -export type TAccessApprovalPoliciesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TAccessApprovalPoliciesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/access-approval-requests-reviewers.ts b/backend/src/db/schemas/access-approval-requests-reviewers.ts index a209df206..cf87545c5 100644 --- a/backend/src/db/schemas/access-approval-requests-reviewers.ts +++ b/backend/src/db/schemas/access-approval-requests-reviewers.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AccessApprovalRequestsReviewersSchema = z.object({ @@ -18,10 +20,5 @@ export const AccessApprovalRequestsReviewersSchema = z.object({ }); export type TAccessApprovalRequestsReviewers = z.infer; -export type TAccessApprovalRequestsReviewersInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TAccessApprovalRequestsReviewersUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TAccessApprovalRequestsReviewersInsert = Omit, TImmutableDBKeys>; +export type TAccessApprovalRequestsReviewersUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/access-approval-requests.ts b/backend/src/db/schemas/access-approval-requests.ts index 0b20202f5..12d8644bf 100644 --- a/backend/src/db/schemas/access-approval-requests.ts +++ b/backend/src/db/schemas/access-approval-requests.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AccessApprovalRequestsSchema = z.object({ @@ -22,6 +24,4 @@ export const AccessApprovalRequestsSchema = z.object({ export type TAccessApprovalRequests = z.infer; export type TAccessApprovalRequestsInsert = Omit, TImmutableDBKeys>; -export type TAccessApprovalRequestsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TAccessApprovalRequestsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/api-keys.ts b/backend/src/db/schemas/api-keys.ts index cf836fd88..1db00d26a 100644 --- a/backend/src/db/schemas/api-keys.ts +++ b/backend/src/db/schemas/api-keys.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ApiKeysSchema = z.object({ diff --git a/backend/src/db/schemas/audit-log-streams.ts b/backend/src/db/schemas/audit-log-streams.ts index 901dd8d27..582401c07 100644 --- a/backend/src/db/schemas/audit-log-streams.ts +++ b/backend/src/db/schemas/audit-log-streams.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AuditLogStreamsSchema = z.object({ diff --git a/backend/src/db/schemas/audit-logs.ts b/backend/src/db/schemas/audit-logs.ts index d1c239724..4301d94fd 100644 --- a/backend/src/db/schemas/audit-logs.ts +++ b/backend/src/db/schemas/audit-logs.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AuditLogsSchema = z.object({ diff --git a/backend/src/db/schemas/auth-token-sessions.ts b/backend/src/db/schemas/auth-token-sessions.ts index 3a9376c83..3f87f9832 100644 --- a/backend/src/db/schemas/auth-token-sessions.ts +++ b/backend/src/db/schemas/auth-token-sessions.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AuthTokenSessionsSchema = z.object({ diff --git a/backend/src/db/schemas/auth-tokens.ts b/backend/src/db/schemas/auth-tokens.ts index dd8563b85..507cfb942 100644 --- a/backend/src/db/schemas/auth-tokens.ts +++ b/backend/src/db/schemas/auth-tokens.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const AuthTokensSchema = z.object({ diff --git a/backend/src/db/schemas/backup-private-key.ts b/backend/src/db/schemas/backup-private-key.ts index 5a2148aa1..93a24315d 100644 --- a/backend/src/db/schemas/backup-private-key.ts +++ b/backend/src/db/schemas/backup-private-key.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const BackupPrivateKeySchema = z.object({ diff --git a/backend/src/db/schemas/certificate-authorities.ts b/backend/src/db/schemas/certificate-authorities.ts index ffe0f7c44..070405f61 100644 --- a/backend/src/db/schemas/certificate-authorities.ts +++ b/backend/src/db/schemas/certificate-authorities.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const CertificateAuthoritiesSchema = z.object({ @@ -34,6 +36,4 @@ export const CertificateAuthoritiesSchema = z.object({ export type TCertificateAuthorities = z.infer; export type TCertificateAuthoritiesInsert = Omit, TImmutableDBKeys>; -export type TCertificateAuthoritiesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TCertificateAuthoritiesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/certificate-authority-certs.ts b/backend/src/db/schemas/certificate-authority-certs.ts index 7074ce409..b18fc4f20 100644 --- a/backend/src/db/schemas/certificate-authority-certs.ts +++ b/backend/src/db/schemas/certificate-authority-certs.ts @@ -22,6 +22,4 @@ export const CertificateAuthorityCertsSchema = z.object({ export type TCertificateAuthorityCerts = z.infer; export type TCertificateAuthorityCertsInsert = Omit, TImmutableDBKeys>; -export type TCertificateAuthorityCertsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TCertificateAuthorityCertsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/certificate-authority-crl.ts b/backend/src/db/schemas/certificate-authority-crl.ts index 3d63be5d8..b57e94435 100644 --- a/backend/src/db/schemas/certificate-authority-crl.ts +++ b/backend/src/db/schemas/certificate-authority-crl.ts @@ -20,6 +20,4 @@ export const CertificateAuthorityCrlSchema = z.object({ export type TCertificateAuthorityCrl = z.infer; export type TCertificateAuthorityCrlInsert = Omit, TImmutableDBKeys>; -export type TCertificateAuthorityCrlUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TCertificateAuthorityCrlUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/certificate-authority-secret.ts b/backend/src/db/schemas/certificate-authority-secret.ts index 36ab1c506..d3128d5bf 100644 --- a/backend/src/db/schemas/certificate-authority-secret.ts +++ b/backend/src/db/schemas/certificate-authority-secret.ts @@ -18,10 +18,5 @@ export const CertificateAuthoritySecretSchema = z.object({ }); export type TCertificateAuthoritySecret = z.infer; -export type TCertificateAuthoritySecretInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TCertificateAuthoritySecretUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TCertificateAuthoritySecretInsert = Omit, TImmutableDBKeys>; +export type TCertificateAuthoritySecretUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/certificate-template-est-configs.ts b/backend/src/db/schemas/certificate-template-est-configs.ts index 262f22b06..8dc2ec8b9 100644 --- a/backend/src/db/schemas/certificate-template-est-configs.ts +++ b/backend/src/db/schemas/certificate-template-est-configs.ts @@ -21,10 +21,5 @@ export const CertificateTemplateEstConfigsSchema = z.object({ }); export type TCertificateTemplateEstConfigs = z.infer; -export type TCertificateTemplateEstConfigsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TCertificateTemplateEstConfigsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TCertificateTemplateEstConfigsInsert = Omit, TImmutableDBKeys>; +export type TCertificateTemplateEstConfigsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/certificate-templates.ts b/backend/src/db/schemas/certificate-templates.ts index c332d7cf7..793315d05 100644 --- a/backend/src/db/schemas/certificate-templates.ts +++ b/backend/src/db/schemas/certificate-templates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const CertificateTemplatesSchema = z.object({ diff --git a/backend/src/db/schemas/certificates.ts b/backend/src/db/schemas/certificates.ts index bde35002f..6f7f94ed0 100644 --- a/backend/src/db/schemas/certificates.ts +++ b/backend/src/db/schemas/certificates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const CertificatesSchema = z.object({ @@ -20,7 +22,7 @@ export const CertificatesSchema = z.object({ notAfter: z.date(), revokedAt: z.date().nullable().optional(), revocationReason: z.number().nullable().optional(), - altNames: z.string().default("").nullable().optional(), + altNames: z.string().default('').nullable().optional(), caCertId: z.string().uuid(), certificateTemplateId: z.string().uuid().nullable().optional(), keyUsages: z.string().array().nullable().optional(), diff --git a/backend/src/db/schemas/dynamic-secret-leases.ts b/backend/src/db/schemas/dynamic-secret-leases.ts index 8c16bcb55..31ccdb32b 100644 --- a/backend/src/db/schemas/dynamic-secret-leases.ts +++ b/backend/src/db/schemas/dynamic-secret-leases.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const DynamicSecretLeasesSchema = z.object({ diff --git a/backend/src/db/schemas/dynamic-secrets.ts b/backend/src/db/schemas/dynamic-secrets.ts index 913a6d475..fac36af7f 100644 --- a/backend/src/db/schemas/dynamic-secrets.ts +++ b/backend/src/db/schemas/dynamic-secrets.ts @@ -19,8 +19,8 @@ export const DynamicSecretsSchema = z.object({ inputIV: z.string().nullable().optional(), inputCiphertext: z.string().nullable().optional(), inputTag: z.string().nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), folderId: z.string().uuid(), status: z.string().nullable().optional(), statusDetails: z.string().nullable().optional(), diff --git a/backend/src/db/schemas/external-group-org-role-mappings.ts b/backend/src/db/schemas/external-group-org-role-mappings.ts index f7e6eab25..6b38c6421 100644 --- a/backend/src/db/schemas/external-group-org-role-mappings.ts +++ b/backend/src/db/schemas/external-group-org-role-mappings.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ExternalGroupOrgRoleMappingsSchema = z.object({ @@ -18,10 +20,5 @@ export const ExternalGroupOrgRoleMappingsSchema = z.object({ }); export type TExternalGroupOrgRoleMappings = z.infer; -export type TExternalGroupOrgRoleMappingsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TExternalGroupOrgRoleMappingsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TExternalGroupOrgRoleMappingsInsert = Omit, TImmutableDBKeys>; +export type TExternalGroupOrgRoleMappingsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/git-app-install-sessions.ts b/backend/src/db/schemas/git-app-install-sessions.ts index 986ae9d8e..7100f1108 100644 --- a/backend/src/db/schemas/git-app-install-sessions.ts +++ b/backend/src/db/schemas/git-app-install-sessions.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const GitAppInstallSessionsSchema = z.object({ diff --git a/backend/src/db/schemas/git-app-org.ts b/backend/src/db/schemas/git-app-org.ts index 627df6b4c..ffc45eb80 100644 --- a/backend/src/db/schemas/git-app-org.ts +++ b/backend/src/db/schemas/git-app-org.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const GitAppOrgSchema = z.object({ diff --git a/backend/src/db/schemas/group-project-membership-roles.ts b/backend/src/db/schemas/group-project-membership-roles.ts index d837ca8e7..b2c3f57f1 100644 --- a/backend/src/db/schemas/group-project-membership-roles.ts +++ b/backend/src/db/schemas/group-project-membership-roles.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const GroupProjectMembershipRolesSchema = z.object({ @@ -22,10 +24,5 @@ export const GroupProjectMembershipRolesSchema = z.object({ }); export type TGroupProjectMembershipRoles = z.infer; -export type TGroupProjectMembershipRolesInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TGroupProjectMembershipRolesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TGroupProjectMembershipRolesInsert = Omit, TImmutableDBKeys>; +export type TGroupProjectMembershipRolesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/group-project-memberships.ts b/backend/src/db/schemas/group-project-memberships.ts index 7787a3574..cc5f0f2ff 100644 --- a/backend/src/db/schemas/group-project-memberships.ts +++ b/backend/src/db/schemas/group-project-memberships.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const GroupProjectMembershipsSchema = z.object({ @@ -17,6 +19,4 @@ export const GroupProjectMembershipsSchema = z.object({ export type TGroupProjectMemberships = z.infer; export type TGroupProjectMembershipsInsert = Omit, TImmutableDBKeys>; -export type TGroupProjectMembershipsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TGroupProjectMembershipsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/groups.ts b/backend/src/db/schemas/groups.ts index 9733d253e..cf1bfbd6d 100644 --- a/backend/src/db/schemas/groups.ts +++ b/backend/src/db/schemas/groups.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const GroupsSchema = z.object({ diff --git a/backend/src/db/schemas/identities.ts b/backend/src/db/schemas/identities.ts index adf3a6ef2..bf51f13ac 100644 --- a/backend/src/db/schemas/identities.ts +++ b/backend/src/db/schemas/identities.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentitiesSchema = z.object({ diff --git a/backend/src/db/schemas/identity-access-tokens.ts b/backend/src/db/schemas/identity-access-tokens.ts index bbff1b88c..c6642903a 100644 --- a/backend/src/db/schemas/identity-access-tokens.ts +++ b/backend/src/db/schemas/identity-access-tokens.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityAccessTokensSchema = z.object({ diff --git a/backend/src/db/schemas/identity-aws-auths.ts b/backend/src/db/schemas/identity-aws-auths.ts index f4444b00f..98dfe745a 100644 --- a/backend/src/db/schemas/identity-aws-auths.ts +++ b/backend/src/db/schemas/identity-aws-auths.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityAwsAuthsSchema = z.object({ diff --git a/backend/src/db/schemas/identity-azure-auths.ts b/backend/src/db/schemas/identity-azure-auths.ts index 856f7b8f1..b595c6c0b 100644 --- a/backend/src/db/schemas/identity-azure-auths.ts +++ b/backend/src/db/schemas/identity-azure-auths.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityAzureAuthsSchema = z.object({ diff --git a/backend/src/db/schemas/identity-gcp-auths.ts b/backend/src/db/schemas/identity-gcp-auths.ts index 208058f60..2ff644bdb 100644 --- a/backend/src/db/schemas/identity-gcp-auths.ts +++ b/backend/src/db/schemas/identity-gcp-auths.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityGcpAuthsSchema = z.object({ diff --git a/backend/src/db/schemas/identity-kubernetes-auths.ts b/backend/src/db/schemas/identity-kubernetes-auths.ts index 448cec386..57efbd2b5 100644 --- a/backend/src/db/schemas/identity-kubernetes-auths.ts +++ b/backend/src/db/schemas/identity-kubernetes-auths.ts @@ -34,6 +34,4 @@ export const IdentityKubernetesAuthsSchema = z.object({ export type TIdentityKubernetesAuths = z.infer; export type TIdentityKubernetesAuthsInsert = Omit, TImmutableDBKeys>; -export type TIdentityKubernetesAuthsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityKubernetesAuthsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-metadata.ts b/backend/src/db/schemas/identity-metadata.ts index de1cec104..2584677be 100644 --- a/backend/src/db/schemas/identity-metadata.ts +++ b/backend/src/db/schemas/identity-metadata.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityMetadataSchema = z.object({ diff --git a/backend/src/db/schemas/identity-org-memberships.ts b/backend/src/db/schemas/identity-org-memberships.ts index 2f29c52e4..71c6c49dd 100644 --- a/backend/src/db/schemas/identity-org-memberships.ts +++ b/backend/src/db/schemas/identity-org-memberships.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityOrgMembershipsSchema = z.object({ @@ -19,6 +21,4 @@ export const IdentityOrgMembershipsSchema = z.object({ export type TIdentityOrgMemberships = z.infer; export type TIdentityOrgMembershipsInsert = Omit, TImmutableDBKeys>; -export type TIdentityOrgMembershipsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityOrgMembershipsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-project-additional-privilege.ts b/backend/src/db/schemas/identity-project-additional-privilege.ts index 7a9dbe19e..564fabfec 100644 --- a/backend/src/db/schemas/identity-project-additional-privilege.ts +++ b/backend/src/db/schemas/identity-project-additional-privilege.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityProjectAdditionalPrivilegeSchema = z.object({ @@ -22,10 +24,5 @@ export const IdentityProjectAdditionalPrivilegeSchema = z.object({ }); export type TIdentityProjectAdditionalPrivilege = z.infer; -export type TIdentityProjectAdditionalPrivilegeInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TIdentityProjectAdditionalPrivilegeUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityProjectAdditionalPrivilegeInsert = Omit, TImmutableDBKeys>; +export type TIdentityProjectAdditionalPrivilegeUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-project-membership-role.ts b/backend/src/db/schemas/identity-project-membership-role.ts index 90a0a3538..50ae2602e 100644 --- a/backend/src/db/schemas/identity-project-membership-role.ts +++ b/backend/src/db/schemas/identity-project-membership-role.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityProjectMembershipRoleSchema = z.object({ @@ -22,10 +24,5 @@ export const IdentityProjectMembershipRoleSchema = z.object({ }); export type TIdentityProjectMembershipRole = z.infer; -export type TIdentityProjectMembershipRoleInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TIdentityProjectMembershipRoleUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityProjectMembershipRoleInsert = Omit, TImmutableDBKeys>; +export type TIdentityProjectMembershipRoleUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-project-memberships.ts b/backend/src/db/schemas/identity-project-memberships.ts index 2f17c36d8..26e2de10f 100644 --- a/backend/src/db/schemas/identity-project-memberships.ts +++ b/backend/src/db/schemas/identity-project-memberships.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityProjectMembershipsSchema = z.object({ @@ -16,10 +18,5 @@ export const IdentityProjectMembershipsSchema = z.object({ }); export type TIdentityProjectMemberships = z.infer; -export type TIdentityProjectMembershipsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TIdentityProjectMembershipsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityProjectMembershipsInsert = Omit, TImmutableDBKeys>; +export type TIdentityProjectMembershipsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-token-auths.ts b/backend/src/db/schemas/identity-token-auths.ts index 0f3c8c9ff..2ff183ae7 100644 --- a/backend/src/db/schemas/identity-token-auths.ts +++ b/backend/src/db/schemas/identity-token-auths.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityTokenAuthsSchema = z.object({ diff --git a/backend/src/db/schemas/identity-ua-client-secrets.ts b/backend/src/db/schemas/identity-ua-client-secrets.ts index bd549ca5b..61eb8e57a 100644 --- a/backend/src/db/schemas/identity-ua-client-secrets.ts +++ b/backend/src/db/schemas/identity-ua-client-secrets.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityUaClientSecretsSchema = z.object({ @@ -24,6 +26,4 @@ export const IdentityUaClientSecretsSchema = z.object({ export type TIdentityUaClientSecrets = z.infer; export type TIdentityUaClientSecretsInsert = Omit, TImmutableDBKeys>; -export type TIdentityUaClientSecretsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityUaClientSecretsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/identity-universal-auths.ts b/backend/src/db/schemas/identity-universal-auths.ts index eeec2f666..5be5c675b 100644 --- a/backend/src/db/schemas/identity-universal-auths.ts +++ b/backend/src/db/schemas/identity-universal-auths.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IdentityUniversalAuthsSchema = z.object({ @@ -22,6 +24,4 @@ export const IdentityUniversalAuthsSchema = z.object({ export type TIdentityUniversalAuths = z.infer; export type TIdentityUniversalAuthsInsert = Omit, TImmutableDBKeys>; -export type TIdentityUniversalAuthsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TIdentityUniversalAuthsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/incident-contacts.ts b/backend/src/db/schemas/incident-contacts.ts index 23c8503b0..b6d86d930 100644 --- a/backend/src/db/schemas/incident-contacts.ts +++ b/backend/src/db/schemas/incident-contacts.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IncidentContactsSchema = z.object({ diff --git a/backend/src/db/schemas/index.ts b/backend/src/db/schemas/index.ts index 92fc47c23..9de470da3 100644 --- a/backend/src/db/schemas/index.ts +++ b/backend/src/db/schemas/index.ts @@ -120,6 +120,8 @@ export * from "./ssh-certificate-authority-secrets"; export * from "./ssh-certificate-bodies"; export * from "./ssh-certificate-templates"; export * from "./ssh-certificates"; +export * from "./ssh-host-login-mappings"; +export * from "./ssh-hosts"; export * from "./super-admin"; export * from "./totp-configs"; export * from "./trusted-ips"; diff --git a/backend/src/db/schemas/integrations.ts b/backend/src/db/schemas/integrations.ts index 47cf9e627..975fe7a13 100644 --- a/backend/src/db/schemas/integrations.ts +++ b/backend/src/db/schemas/integrations.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const IntegrationsSchema = z.object({ @@ -25,7 +27,7 @@ export const IntegrationsSchema = z.object({ metadata: z.unknown().nullable().optional(), integrationAuthId: z.string().uuid(), envId: z.string().uuid(), - secretPath: z.string().default("/"), + secretPath: z.string().default('/'), createdAt: z.date(), updatedAt: z.date(), lastUsed: z.date().nullable().optional(), diff --git a/backend/src/db/schemas/kmip-client-certificates.ts b/backend/src/db/schemas/kmip-client-certificates.ts index a42d94a98..cf46358d1 100644 --- a/backend/src/db/schemas/kmip-client-certificates.ts +++ b/backend/src/db/schemas/kmip-client-certificates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const KmipClientCertificatesSchema = z.object({ @@ -18,6 +20,4 @@ export const KmipClientCertificatesSchema = z.object({ export type TKmipClientCertificates = z.infer; export type TKmipClientCertificatesInsert = Omit, TImmutableDBKeys>; -export type TKmipClientCertificatesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TKmipClientCertificatesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/kmip-clients.ts b/backend/src/db/schemas/kmip-clients.ts index eb8f31bfb..5c9f27ca2 100644 --- a/backend/src/db/schemas/kmip-clients.ts +++ b/backend/src/db/schemas/kmip-clients.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const KmipClientsSchema = z.object({ diff --git a/backend/src/db/schemas/kmip-org-server-certificates.ts b/backend/src/db/schemas/kmip-org-server-certificates.ts index 66e5dcbd6..6abf849ed 100644 --- a/backend/src/db/schemas/kmip-org-server-certificates.ts +++ b/backend/src/db/schemas/kmip-org-server-certificates.ts @@ -24,6 +24,4 @@ export const KmipOrgServerCertificatesSchema = z.object({ export type TKmipOrgServerCertificates = z.infer; export type TKmipOrgServerCertificatesInsert = Omit, TImmutableDBKeys>; -export type TKmipOrgServerCertificatesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TKmipOrgServerCertificatesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/kms-keys.ts b/backend/src/db/schemas/kms-keys.ts index b56fab7bf..65e4f25eb 100644 --- a/backend/src/db/schemas/kms-keys.ts +++ b/backend/src/db/schemas/kms-keys.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const KmsKeysSchema = z.object({ diff --git a/backend/src/db/schemas/kms-root-config.ts b/backend/src/db/schemas/kms-root-config.ts index c9c1ebda5..cd4552a1c 100644 --- a/backend/src/db/schemas/kms-root-config.ts +++ b/backend/src/db/schemas/kms-root-config.ts @@ -12,7 +12,7 @@ import { TImmutableDBKeys } from "./models"; export const KmsRootConfigSchema = z.object({ id: z.string().uuid(), encryptedRootKey: zodBuffer, - encryptionStrategy: z.string().default("SOFTWARE").nullable().optional(), + encryptionStrategy: z.string().default('SOFTWARE').nullable().optional(), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/db/schemas/ldap-configs.ts b/backend/src/db/schemas/ldap-configs.ts index 778e7be6e..90fc5c288 100644 --- a/backend/src/db/schemas/ldap-configs.ts +++ b/backend/src/db/schemas/ldap-configs.ts @@ -26,10 +26,10 @@ export const LdapConfigsSchema = z.object({ caCertTag: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), - groupSearchBase: z.string().default(""), - groupSearchFilter: z.string().default(""), - searchFilter: z.string().default(""), - uniqueUserAttribute: z.string().default(""), + groupSearchBase: z.string().default(''), + groupSearchFilter: z.string().default(''), + searchFilter: z.string().default(''), + uniqueUserAttribute: z.string().default(''), encryptedLdapBindDN: zodBuffer, encryptedLdapBindPass: zodBuffer, encryptedLdapCaCertificate: zodBuffer.nullable().optional() diff --git a/backend/src/db/schemas/ldap-group-maps.ts b/backend/src/db/schemas/ldap-group-maps.ts index d51d151b8..ad4c93383 100644 --- a/backend/src/db/schemas/ldap-group-maps.ts +++ b/backend/src/db/schemas/ldap-group-maps.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const LdapGroupMapsSchema = z.object({ diff --git a/backend/src/db/schemas/models.ts b/backend/src/db/schemas/models.ts index e626443c3..67055d331 100644 --- a/backend/src/db/schemas/models.ts +++ b/backend/src/db/schemas/models.ts @@ -2,6 +2,8 @@ import { z } from "zod"; export enum TableName { Users = "users", + SshHost = "ssh_hosts", + SshHostLoginMapping = "ssh_host_login_mappings", SshCertificateAuthority = "ssh_certificate_authorities", SshCertificateAuthoritySecret = "ssh_certificate_authority_secrets", SshCertificateTemplate = "ssh_certificate_templates", diff --git a/backend/src/db/schemas/org-bots.ts b/backend/src/db/schemas/org-bots.ts index 77be907ec..42d278c7e 100644 --- a/backend/src/db/schemas/org-bots.ts +++ b/backend/src/db/schemas/org-bots.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const OrgBotsSchema = z.object({ diff --git a/backend/src/db/schemas/org-memberships.ts b/backend/src/db/schemas/org-memberships.ts index e77b6e9c9..d200c6fb2 100644 --- a/backend/src/db/schemas/org-memberships.ts +++ b/backend/src/db/schemas/org-memberships.ts @@ -5,12 +5,14 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const OrgMembershipsSchema = z.object({ id: z.string().uuid(), role: z.string(), - status: z.string().default("invited"), + status: z.string().default('invited'), inviteEmail: z.string().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), diff --git a/backend/src/db/schemas/org-roles.ts b/backend/src/db/schemas/org-roles.ts index ca01c6574..848ee104d 100644 --- a/backend/src/db/schemas/org-roles.ts +++ b/backend/src/db/schemas/org-roles.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const OrgRolesSchema = z.object({ diff --git a/backend/src/db/schemas/organizations.ts b/backend/src/db/schemas/organizations.ts index a18e258c7..b01a404e0 100644 --- a/backend/src/db/schemas/organizations.ts +++ b/backend/src/db/schemas/organizations.ts @@ -20,7 +20,7 @@ export const OrganizationsSchema = z.object({ scimEnabled: z.boolean().default(false).nullable().optional(), kmsDefaultKeyId: z.string().uuid().nullable().optional(), kmsEncryptedDataKey: zodBuffer.nullable().optional(), - defaultMembershipRole: z.string().default("member"), + defaultMembershipRole: z.string().default('member'), enforceMfa: z.boolean().default(false), selectedMfaMethod: z.string().nullable().optional(), allowSecretSharingOutsideOrganization: z.boolean().default(true).nullable().optional(), diff --git a/backend/src/db/schemas/pki-alerts.ts b/backend/src/db/schemas/pki-alerts.ts index 7bc9d2d7c..325a17974 100644 --- a/backend/src/db/schemas/pki-alerts.ts +++ b/backend/src/db/schemas/pki-alerts.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const PkiAlertsSchema = z.object({ diff --git a/backend/src/db/schemas/pki-collection-items.ts b/backend/src/db/schemas/pki-collection-items.ts index f04f5a1ee..27a69ebb2 100644 --- a/backend/src/db/schemas/pki-collection-items.ts +++ b/backend/src/db/schemas/pki-collection-items.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const PkiCollectionItemsSchema = z.object({ diff --git a/backend/src/db/schemas/pki-collections.ts b/backend/src/db/schemas/pki-collections.ts index 0f9a5675f..f565d698f 100644 --- a/backend/src/db/schemas/pki-collections.ts +++ b/backend/src/db/schemas/pki-collections.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const PkiCollectionsSchema = z.object({ diff --git a/backend/src/db/schemas/project-bots.ts b/backend/src/db/schemas/project-bots.ts index 1fa59eb78..ff58696f7 100644 --- a/backend/src/db/schemas/project-bots.ts +++ b/backend/src/db/schemas/project-bots.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectBotsSchema = z.object({ diff --git a/backend/src/db/schemas/project-environments.ts b/backend/src/db/schemas/project-environments.ts index 76556b7e9..823fe67da 100644 --- a/backend/src/db/schemas/project-environments.ts +++ b/backend/src/db/schemas/project-environments.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectEnvironmentsSchema = z.object({ diff --git a/backend/src/db/schemas/project-gateways.ts b/backend/src/db/schemas/project-gateways.ts index f4b572661..d86d067c5 100644 --- a/backend/src/db/schemas/project-gateways.ts +++ b/backend/src/db/schemas/project-gateways.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectGatewaysSchema = z.object({ diff --git a/backend/src/db/schemas/project-keys.ts b/backend/src/db/schemas/project-keys.ts index 924918b12..ce958f82e 100644 --- a/backend/src/db/schemas/project-keys.ts +++ b/backend/src/db/schemas/project-keys.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectKeysSchema = z.object({ diff --git a/backend/src/db/schemas/project-memberships.ts b/backend/src/db/schemas/project-memberships.ts index e522d6280..21e12029c 100644 --- a/backend/src/db/schemas/project-memberships.ts +++ b/backend/src/db/schemas/project-memberships.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectMembershipsSchema = z.object({ diff --git a/backend/src/db/schemas/project-roles.ts b/backend/src/db/schemas/project-roles.ts index 37eb58fcb..5f9fc0610 100644 --- a/backend/src/db/schemas/project-roles.ts +++ b/backend/src/db/schemas/project-roles.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectRolesSchema = z.object({ diff --git a/backend/src/db/schemas/project-slack-configs.ts b/backend/src/db/schemas/project-slack-configs.ts index 0a46e5aae..9ec1ee367 100644 --- a/backend/src/db/schemas/project-slack-configs.ts +++ b/backend/src/db/schemas/project-slack-configs.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectSlackConfigsSchema = z.object({ @@ -12,9 +14,9 @@ export const ProjectSlackConfigsSchema = z.object({ projectId: z.string(), slackIntegrationId: z.string().uuid(), isAccessRequestNotificationEnabled: z.boolean().default(false), - accessRequestChannels: z.string().default(""), + accessRequestChannels: z.string().default(''), isSecretRequestNotificationEnabled: z.boolean().default(false), - secretRequestChannels: z.string().default(""), + secretRequestChannels: z.string().default(''), createdAt: z.date(), updatedAt: z.date() }); diff --git a/backend/src/db/schemas/project-split-backfill-ids.ts b/backend/src/db/schemas/project-split-backfill-ids.ts index 182d85049..d674dd590 100644 --- a/backend/src/db/schemas/project-split-backfill-ids.ts +++ b/backend/src/db/schemas/project-split-backfill-ids.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectSplitBackfillIdsSchema = z.object({ @@ -16,6 +18,4 @@ export const ProjectSplitBackfillIdsSchema = z.object({ export type TProjectSplitBackfillIds = z.infer; export type TProjectSplitBackfillIdsInsert = Omit, TImmutableDBKeys>; -export type TProjectSplitBackfillIdsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TProjectSplitBackfillIdsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/project-templates.ts b/backend/src/db/schemas/project-templates.ts index 68f37d256..981dc581e 100644 --- a/backend/src/db/schemas/project-templates.ts +++ b/backend/src/db/schemas/project-templates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectTemplatesSchema = z.object({ diff --git a/backend/src/db/schemas/project-user-additional-privilege.ts b/backend/src/db/schemas/project-user-additional-privilege.ts index e657fc945..8eb581670 100644 --- a/backend/src/db/schemas/project-user-additional-privilege.ts +++ b/backend/src/db/schemas/project-user-additional-privilege.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectUserAdditionalPrivilegeSchema = z.object({ @@ -24,10 +26,5 @@ export const ProjectUserAdditionalPrivilegeSchema = z.object({ }); export type TProjectUserAdditionalPrivilege = z.infer; -export type TProjectUserAdditionalPrivilegeInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TProjectUserAdditionalPrivilegeUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TProjectUserAdditionalPrivilegeInsert = Omit, TImmutableDBKeys>; +export type TProjectUserAdditionalPrivilegeUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/project-user-membership-roles.ts b/backend/src/db/schemas/project-user-membership-roles.ts index bc7b67208..e0f40505a 100644 --- a/backend/src/db/schemas/project-user-membership-roles.ts +++ b/backend/src/db/schemas/project-user-membership-roles.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ProjectUserMembershipRolesSchema = z.object({ @@ -22,10 +24,5 @@ export const ProjectUserMembershipRolesSchema = z.object({ }); export type TProjectUserMembershipRoles = z.infer; -export type TProjectUserMembershipRolesInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TProjectUserMembershipRolesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TProjectUserMembershipRolesInsert = Omit, TImmutableDBKeys>; +export type TProjectUserMembershipRolesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/rate-limit.ts b/backend/src/db/schemas/rate-limit.ts index 233f6cdbc..1cea9b556 100644 --- a/backend/src/db/schemas/rate-limit.ts +++ b/backend/src/db/schemas/rate-limit.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const RateLimitSchema = z.object({ diff --git a/backend/src/db/schemas/resource-metadata.ts b/backend/src/db/schemas/resource-metadata.ts index f496b29db..748bdd0e6 100644 --- a/backend/src/db/schemas/resource-metadata.ts +++ b/backend/src/db/schemas/resource-metadata.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ResourceMetadataSchema = z.object({ diff --git a/backend/src/db/schemas/scim-tokens.ts b/backend/src/db/schemas/scim-tokens.ts index ab6e10d27..3de5057d0 100644 --- a/backend/src/db/schemas/scim-tokens.ts +++ b/backend/src/db/schemas/scim-tokens.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ScimTokensSchema = z.object({ diff --git a/backend/src/db/schemas/secret-approval-policies-approvers.ts b/backend/src/db/schemas/secret-approval-policies-approvers.ts index f9aebf019..578da0df0 100644 --- a/backend/src/db/schemas/secret-approval-policies-approvers.ts +++ b/backend/src/db/schemas/secret-approval-policies-approvers.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalPoliciesApproversSchema = z.object({ @@ -17,10 +19,5 @@ export const SecretApprovalPoliciesApproversSchema = z.object({ }); export type TSecretApprovalPoliciesApprovers = z.infer; -export type TSecretApprovalPoliciesApproversInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalPoliciesApproversUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalPoliciesApproversInsert = Omit, TImmutableDBKeys>; +export type TSecretApprovalPoliciesApproversUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-policies.ts b/backend/src/db/schemas/secret-approval-policies.ts index 8b9174456..72a0caaa2 100644 --- a/backend/src/db/schemas/secret-approval-policies.ts +++ b/backend/src/db/schemas/secret-approval-policies.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalPoliciesSchema = z.object({ @@ -15,13 +17,11 @@ export const SecretApprovalPoliciesSchema = z.object({ envId: z.string().uuid(), createdAt: z.date(), updatedAt: z.date(), - enforcementLevel: z.string().default("hard"), + enforcementLevel: z.string().default('hard'), deletedAt: z.date().nullable().optional(), allowedSelfApprovals: z.boolean().default(true) }); export type TSecretApprovalPolicies = z.infer; export type TSecretApprovalPoliciesInsert = Omit, TImmutableDBKeys>; -export type TSecretApprovalPoliciesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalPoliciesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-request-secret-tags-v2.ts b/backend/src/db/schemas/secret-approval-request-secret-tags-v2.ts index 6ded05a5d..713b5bb10 100644 --- a/backend/src/db/schemas/secret-approval-request-secret-tags-v2.ts +++ b/backend/src/db/schemas/secret-approval-request-secret-tags-v2.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestSecretTagsV2Schema = z.object({ @@ -16,10 +18,5 @@ export const SecretApprovalRequestSecretTagsV2Schema = z.object({ }); export type TSecretApprovalRequestSecretTagsV2 = z.infer; -export type TSecretApprovalRequestSecretTagsV2Insert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalRequestSecretTagsV2Update = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestSecretTagsV2Insert = Omit, TImmutableDBKeys>; +export type TSecretApprovalRequestSecretTagsV2Update = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-request-secret-tags.ts b/backend/src/db/schemas/secret-approval-request-secret-tags.ts index 2851321e2..dfa840528 100644 --- a/backend/src/db/schemas/secret-approval-request-secret-tags.ts +++ b/backend/src/db/schemas/secret-approval-request-secret-tags.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestSecretTagsSchema = z.object({ @@ -16,10 +18,5 @@ export const SecretApprovalRequestSecretTagsSchema = z.object({ }); export type TSecretApprovalRequestSecretTags = z.infer; -export type TSecretApprovalRequestSecretTagsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalRequestSecretTagsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestSecretTagsInsert = Omit, TImmutableDBKeys>; +export type TSecretApprovalRequestSecretTagsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-requests-reviewers.ts b/backend/src/db/schemas/secret-approval-requests-reviewers.ts index 147646b8d..820f7ab4c 100644 --- a/backend/src/db/schemas/secret-approval-requests-reviewers.ts +++ b/backend/src/db/schemas/secret-approval-requests-reviewers.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestsReviewersSchema = z.object({ @@ -18,10 +20,5 @@ export const SecretApprovalRequestsReviewersSchema = z.object({ }); export type TSecretApprovalRequestsReviewers = z.infer; -export type TSecretApprovalRequestsReviewersInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalRequestsReviewersUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestsReviewersInsert = Omit, TImmutableDBKeys>; +export type TSecretApprovalRequestsReviewersUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts b/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts index 298985fed..1502b5d28 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets-v2.ts @@ -29,10 +29,5 @@ export const SecretApprovalRequestsSecretsV2Schema = z.object({ }); export type TSecretApprovalRequestsSecretsV2 = z.infer; -export type TSecretApprovalRequestsSecretsV2Insert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalRequestsSecretsV2Update = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestsSecretsV2Insert = Omit, TImmutableDBKeys>; +export type TSecretApprovalRequestsSecretsV2Update = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-requests-secrets.ts b/backend/src/db/schemas/secret-approval-requests-secrets.ts index b795b47b4..5e82e2095 100644 --- a/backend/src/db/schemas/secret-approval-requests-secrets.ts +++ b/backend/src/db/schemas/secret-approval-requests-secrets.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestsSecretsSchema = z.object({ @@ -23,8 +25,8 @@ export const SecretApprovalRequestsSecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), createdAt: z.date(), updatedAt: z.date(), @@ -35,10 +37,5 @@ export const SecretApprovalRequestsSecretsSchema = z.object({ }); export type TSecretApprovalRequestsSecrets = z.infer; -export type TSecretApprovalRequestsSecretsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretApprovalRequestsSecretsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestsSecretsInsert = Omit, TImmutableDBKeys>; +export type TSecretApprovalRequestsSecretsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-approval-requests.ts b/backend/src/db/schemas/secret-approval-requests.ts index 218a0f922..78f91cfc8 100644 --- a/backend/src/db/schemas/secret-approval-requests.ts +++ b/backend/src/db/schemas/secret-approval-requests.ts @@ -5,13 +5,15 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretApprovalRequestsSchema = z.object({ id: z.string().uuid(), policyId: z.string().uuid(), hasMerged: z.boolean().default(false), - status: z.string().default("open"), + status: z.string().default('open'), conflicts: z.unknown().nullable().optional(), slug: z.string(), folderId: z.string().uuid(), @@ -25,6 +27,4 @@ export const SecretApprovalRequestsSchema = z.object({ export type TSecretApprovalRequests = z.infer; export type TSecretApprovalRequestsInsert = Omit, TImmutableDBKeys>; -export type TSecretApprovalRequestsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretApprovalRequestsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-blind-indexes.ts b/backend/src/db/schemas/secret-blind-indexes.ts index 474e0aa54..09c8f8b89 100644 --- a/backend/src/db/schemas/secret-blind-indexes.ts +++ b/backend/src/db/schemas/secret-blind-indexes.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretBlindIndexesSchema = z.object({ @@ -12,8 +14,8 @@ export const SecretBlindIndexesSchema = z.object({ encryptedSaltCipherText: z.string(), saltIV: z.string(), saltTag: z.string(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), projectId: z.string(), createdAt: z.date(), updatedAt: z.date() diff --git a/backend/src/db/schemas/secret-folder-versions.ts b/backend/src/db/schemas/secret-folder-versions.ts index 8bef6e83f..e7348bb59 100644 --- a/backend/src/db/schemas/secret-folder-versions.ts +++ b/backend/src/db/schemas/secret-folder-versions.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretFolderVersionsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-folders.ts b/backend/src/db/schemas/secret-folders.ts index 09e2fe8c1..c0564eff1 100644 --- a/backend/src/db/schemas/secret-folders.ts +++ b/backend/src/db/schemas/secret-folders.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretFoldersSchema = z.object({ diff --git a/backend/src/db/schemas/secret-imports.ts b/backend/src/db/schemas/secret-imports.ts index 4bb1e669d..eaa82bea5 100644 --- a/backend/src/db/schemas/secret-imports.ts +++ b/backend/src/db/schemas/secret-imports.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretImportsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-references-v2.ts b/backend/src/db/schemas/secret-references-v2.ts index ed339e65c..c4098ee5a 100644 --- a/backend/src/db/schemas/secret-references-v2.ts +++ b/backend/src/db/schemas/secret-references-v2.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretReferencesV2Schema = z.object({ diff --git a/backend/src/db/schemas/secret-references.ts b/backend/src/db/schemas/secret-references.ts index b3e6a8629..83e479498 100644 --- a/backend/src/db/schemas/secret-references.ts +++ b/backend/src/db/schemas/secret-references.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretReferencesSchema = z.object({ diff --git a/backend/src/db/schemas/secret-rotation-output-v2.ts b/backend/src/db/schemas/secret-rotation-output-v2.ts index 28d45413a..5be9d4646 100644 --- a/backend/src/db/schemas/secret-rotation-output-v2.ts +++ b/backend/src/db/schemas/secret-rotation-output-v2.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretRotationOutputV2Schema = z.object({ @@ -16,6 +18,4 @@ export const SecretRotationOutputV2Schema = z.object({ export type TSecretRotationOutputV2 = z.infer; export type TSecretRotationOutputV2Insert = Omit, TImmutableDBKeys>; -export type TSecretRotationOutputV2Update = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretRotationOutputV2Update = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-rotation-outputs.ts b/backend/src/db/schemas/secret-rotation-outputs.ts index 3ac5c2c9e..dc4b49cdf 100644 --- a/backend/src/db/schemas/secret-rotation-outputs.ts +++ b/backend/src/db/schemas/secret-rotation-outputs.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretRotationOutputsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-scanning-git-risks.ts b/backend/src/db/schemas/secret-scanning-git-risks.ts index 08ba690e4..677dc2e73 100644 --- a/backend/src/db/schemas/secret-scanning-git-risks.ts +++ b/backend/src/db/schemas/secret-scanning-git-risks.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretScanningGitRisksSchema = z.object({ @@ -43,6 +45,4 @@ export const SecretScanningGitRisksSchema = z.object({ export type TSecretScanningGitRisks = z.infer; export type TSecretScanningGitRisksInsert = Omit, TImmutableDBKeys>; -export type TSecretScanningGitRisksUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretScanningGitRisksUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-sharing.ts b/backend/src/db/schemas/secret-sharing.ts index 24ea26677..22bdac3d5 100644 --- a/backend/src/db/schemas/secret-sharing.ts +++ b/backend/src/db/schemas/secret-sharing.ts @@ -21,13 +21,13 @@ export const SecretSharingSchema = z.object({ createdAt: z.date(), updatedAt: z.date(), expiresAfterViews: z.number().nullable().optional(), - accessType: z.string().default("anyone"), + accessType: z.string().default('anyone'), name: z.string().nullable().optional(), lastViewedAt: z.date().nullable().optional(), password: z.string().nullable().optional(), encryptedSecret: zodBuffer.nullable().optional(), identifier: z.string().nullable().optional(), - type: z.string().default("share") + type: z.string().default('share') }); export type TSecretSharing = z.infer; diff --git a/backend/src/db/schemas/secret-snapshot-folders.ts b/backend/src/db/schemas/secret-snapshot-folders.ts index 3e2853cbe..89d3e07a4 100644 --- a/backend/src/db/schemas/secret-snapshot-folders.ts +++ b/backend/src/db/schemas/secret-snapshot-folders.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretSnapshotFoldersSchema = z.object({ diff --git a/backend/src/db/schemas/secret-snapshot-secrets-v2.ts b/backend/src/db/schemas/secret-snapshot-secrets-v2.ts index d643f7f8c..8dea884e4 100644 --- a/backend/src/db/schemas/secret-snapshot-secrets-v2.ts +++ b/backend/src/db/schemas/secret-snapshot-secrets-v2.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretSnapshotSecretsV2Schema = z.object({ @@ -18,6 +20,4 @@ export const SecretSnapshotSecretsV2Schema = z.object({ export type TSecretSnapshotSecretsV2 = z.infer; export type TSecretSnapshotSecretsV2Insert = Omit, TImmutableDBKeys>; -export type TSecretSnapshotSecretsV2Update = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretSnapshotSecretsV2Update = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-snapshot-secrets.ts b/backend/src/db/schemas/secret-snapshot-secrets.ts index 121c5d56e..510a69e99 100644 --- a/backend/src/db/schemas/secret-snapshot-secrets.ts +++ b/backend/src/db/schemas/secret-snapshot-secrets.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretSnapshotSecretsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-snapshots.ts b/backend/src/db/schemas/secret-snapshots.ts index 7f070075f..866bcd42f 100644 --- a/backend/src/db/schemas/secret-snapshots.ts +++ b/backend/src/db/schemas/secret-snapshots.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretSnapshotsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-syncs.ts b/backend/src/db/schemas/secret-syncs.ts index 0e0728e87..488cd0f6c 100644 --- a/backend/src/db/schemas/secret-syncs.ts +++ b/backend/src/db/schemas/secret-syncs.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretSyncsSchema = z.object({ diff --git a/backend/src/db/schemas/secret-tag-junction.ts b/backend/src/db/schemas/secret-tag-junction.ts index d14384fab..52e98fd7d 100644 --- a/backend/src/db/schemas/secret-tag-junction.ts +++ b/backend/src/db/schemas/secret-tag-junction.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretTagJunctionSchema = z.object({ diff --git a/backend/src/db/schemas/secret-tags.ts b/backend/src/db/schemas/secret-tags.ts index 7ba7204a4..3828d0553 100644 --- a/backend/src/db/schemas/secret-tags.ts +++ b/backend/src/db/schemas/secret-tags.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretTagsSchema = z.object({ @@ -15,7 +17,7 @@ export const SecretTagsSchema = z.object({ updatedAt: z.date(), createdBy: z.string().uuid().nullable().optional(), projectId: z.string(), - createdByActorType: z.string().default("user") + createdByActorType: z.string().default('user') }); export type TSecretTags = z.infer; diff --git a/backend/src/db/schemas/secret-v2-tag-junction.ts b/backend/src/db/schemas/secret-v2-tag-junction.ts index 982e11f50..04ecd7526 100644 --- a/backend/src/db/schemas/secret-v2-tag-junction.ts +++ b/backend/src/db/schemas/secret-v2-tag-junction.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretV2TagJunctionSchema = z.object({ diff --git a/backend/src/db/schemas/secret-version-tag-junction.ts b/backend/src/db/schemas/secret-version-tag-junction.ts index b36e28c72..a93e9f586 100644 --- a/backend/src/db/schemas/secret-version-tag-junction.ts +++ b/backend/src/db/schemas/secret-version-tag-junction.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretVersionTagJunctionSchema = z.object({ @@ -15,6 +17,4 @@ export const SecretVersionTagJunctionSchema = z.object({ export type TSecretVersionTagJunction = z.infer; export type TSecretVersionTagJunctionInsert = Omit, TImmutableDBKeys>; -export type TSecretVersionTagJunctionUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretVersionTagJunctionUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-version-v2-tag-junction.ts b/backend/src/db/schemas/secret-version-v2-tag-junction.ts index 4679116a0..1706bf31b 100644 --- a/backend/src/db/schemas/secret-version-v2-tag-junction.ts +++ b/backend/src/db/schemas/secret-version-v2-tag-junction.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretVersionV2TagJunctionSchema = z.object({ @@ -14,10 +16,5 @@ export const SecretVersionV2TagJunctionSchema = z.object({ }); export type TSecretVersionV2TagJunction = z.infer; -export type TSecretVersionV2TagJunctionInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSecretVersionV2TagJunctionUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSecretVersionV2TagJunctionInsert = Omit, TImmutableDBKeys>; +export type TSecretVersionV2TagJunctionUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/secret-versions-v2.ts b/backend/src/db/schemas/secret-versions-v2.ts index 593a46b06..3ac217c81 100644 --- a/backend/src/db/schemas/secret-versions-v2.ts +++ b/backend/src/db/schemas/secret-versions-v2.ts @@ -12,7 +12,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretVersionsV2Schema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), key: z.string(), encryptedValue: zodBuffer.nullable().optional(), encryptedComment: zodBuffer.nullable().optional(), diff --git a/backend/src/db/schemas/secret-versions.ts b/backend/src/db/schemas/secret-versions.ts index d60db9b75..cddecd67a 100644 --- a/backend/src/db/schemas/secret-versions.ts +++ b/backend/src/db/schemas/secret-versions.ts @@ -5,12 +5,14 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretVersionsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), @@ -24,8 +26,8 @@ export const SecretVersionsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), envId: z.string().uuid().nullable().optional(), secretId: z.string().uuid(), diff --git a/backend/src/db/schemas/secrets-v2.ts b/backend/src/db/schemas/secrets-v2.ts index 18fa3b476..5c74f704e 100644 --- a/backend/src/db/schemas/secrets-v2.ts +++ b/backend/src/db/schemas/secrets-v2.ts @@ -12,7 +12,7 @@ import { TImmutableDBKeys } from "./models"; export const SecretsV2Schema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), key: z.string(), encryptedValue: zodBuffer.nullable().optional(), encryptedComment: zodBuffer.nullable().optional(), diff --git a/backend/src/db/schemas/secrets.ts b/backend/src/db/schemas/secrets.ts index f261c40bb..874ffb73c 100644 --- a/backend/src/db/schemas/secrets.ts +++ b/backend/src/db/schemas/secrets.ts @@ -5,12 +5,14 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SecretsSchema = z.object({ id: z.string().uuid(), version: z.number().default(1), - type: z.string().default("shared"), + type: z.string().default('shared'), secretBlindIndex: z.string().nullable().optional(), secretKeyCiphertext: z.string(), secretKeyIV: z.string(), @@ -24,8 +26,8 @@ export const SecretsSchema = z.object({ secretReminderNote: z.string().nullable().optional(), secretReminderRepeatDays: z.number().nullable().optional(), skipMultilineEncoding: z.boolean().default(false).nullable().optional(), - algorithm: z.string().default("aes-256-gcm"), - keyEncoding: z.string().default("utf8"), + algorithm: z.string().default('aes-256-gcm'), + keyEncoding: z.string().default('utf8'), metadata: z.unknown().nullable().optional(), userId: z.string().uuid().nullable().optional(), folderId: z.string().uuid(), diff --git a/backend/src/db/schemas/service-tokens.ts b/backend/src/db/schemas/service-tokens.ts index 720c8fd6f..f7a74b695 100644 --- a/backend/src/db/schemas/service-tokens.ts +++ b/backend/src/db/schemas/service-tokens.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const ServiceTokensSchema = z.object({ diff --git a/backend/src/db/schemas/ssh-certificate-authorities.ts b/backend/src/db/schemas/ssh-certificate-authorities.ts index 75603406f..3a273aaa8 100644 --- a/backend/src/db/schemas/ssh-certificate-authorities.ts +++ b/backend/src/db/schemas/ssh-certificate-authorities.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SshCertificateAuthoritiesSchema = z.object({ @@ -20,6 +22,4 @@ export const SshCertificateAuthoritiesSchema = z.object({ export type TSshCertificateAuthorities = z.infer; export type TSshCertificateAuthoritiesInsert = Omit, TImmutableDBKeys>; -export type TSshCertificateAuthoritiesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSshCertificateAuthoritiesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/ssh-certificate-authority-secrets.ts b/backend/src/db/schemas/ssh-certificate-authority-secrets.ts index 934c10ab2..4c4f6320e 100644 --- a/backend/src/db/schemas/ssh-certificate-authority-secrets.ts +++ b/backend/src/db/schemas/ssh-certificate-authority-secrets.ts @@ -18,10 +18,5 @@ export const SshCertificateAuthoritySecretsSchema = z.object({ }); export type TSshCertificateAuthoritySecrets = z.infer; -export type TSshCertificateAuthoritySecretsInsert = Omit< - z.input, - TImmutableDBKeys ->; -export type TSshCertificateAuthoritySecretsUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSshCertificateAuthoritySecretsInsert = Omit, TImmutableDBKeys>; +export type TSshCertificateAuthoritySecretsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/ssh-certificate-templates.ts b/backend/src/db/schemas/ssh-certificate-templates.ts index 6c16c3942..88846673c 100644 --- a/backend/src/db/schemas/ssh-certificate-templates.ts +++ b/backend/src/db/schemas/ssh-certificate-templates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SshCertificateTemplatesSchema = z.object({ @@ -25,6 +27,4 @@ export const SshCertificateTemplatesSchema = z.object({ export type TSshCertificateTemplates = z.infer; export type TSshCertificateTemplatesInsert = Omit, TImmutableDBKeys>; -export type TSshCertificateTemplatesUpdate = Partial< - Omit, TImmutableDBKeys> ->; +export type TSshCertificateTemplatesUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/ssh-certificates.ts b/backend/src/db/schemas/ssh-certificates.ts index 351875bbb..2344359e5 100644 --- a/backend/src/db/schemas/ssh-certificates.ts +++ b/backend/src/db/schemas/ssh-certificates.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const SshCertificatesSchema = z.object({ diff --git a/backend/src/db/schemas/ssh-host-login-mappings.ts b/backend/src/db/schemas/ssh-host-login-mappings.ts new file mode 100644 index 000000000..12f851a93 --- /dev/null +++ b/backend/src/db/schemas/ssh-host-login-mappings.ts @@ -0,0 +1,23 @@ +// 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 { TImmutableDBKeys } from "./models"; + +export const SshHostLoginMappingsSchema = z.object({ + id: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), + sshHostId: z.string().uuid(), + loginUser: z.string(), + allowedPrincipals: z.string().array() +}); + +export type TSshHostLoginMappings = z.infer; +export type TSshHostLoginMappingsInsert = Omit, TImmutableDBKeys>; +export type TSshHostLoginMappingsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/ssh-hosts.ts b/backend/src/db/schemas/ssh-hosts.ts new file mode 100644 index 000000000..3b21823c8 --- /dev/null +++ b/backend/src/db/schemas/ssh-hosts.ts @@ -0,0 +1,24 @@ +// 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 { TImmutableDBKeys } from "./models"; + +export const SshHostsSchema = z.object({ + id: z.string().uuid(), + createdAt: z.date(), + updatedAt: z.date(), + projectId: z.string(), + hostname: z.string(), + userCertTtl: z.string(), + hostCertTtl: z.string() +}); + +export type TSshHosts = z.infer; +export type TSshHostsInsert = Omit, TImmutableDBKeys>; +export type TSshHostsUpdate = Partial, TImmutableDBKeys>>; diff --git a/backend/src/db/schemas/trusted-ips.ts b/backend/src/db/schemas/trusted-ips.ts index f9973a843..80579504e 100644 --- a/backend/src/db/schemas/trusted-ips.ts +++ b/backend/src/db/schemas/trusted-ips.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const TrustedIpsSchema = z.object({ diff --git a/backend/src/db/schemas/user-actions.ts b/backend/src/db/schemas/user-actions.ts index 89d269847..9937f36d6 100644 --- a/backend/src/db/schemas/user-actions.ts +++ b/backend/src/db/schemas/user-actions.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const UserActionsSchema = z.object({ diff --git a/backend/src/db/schemas/user-aliases.ts b/backend/src/db/schemas/user-aliases.ts index 14147abf8..0fdd59892 100644 --- a/backend/src/db/schemas/user-aliases.ts +++ b/backend/src/db/schemas/user-aliases.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const UserAliasesSchema = z.object({ diff --git a/backend/src/db/schemas/user-encryption-keys.ts b/backend/src/db/schemas/user-encryption-keys.ts index fd9d21a9d..4911cdf53 100644 --- a/backend/src/db/schemas/user-encryption-keys.ts +++ b/backend/src/db/schemas/user-encryption-keys.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const UserEncryptionKeysSchema = z.object({ diff --git a/backend/src/db/schemas/user-group-membership.ts b/backend/src/db/schemas/user-group-membership.ts index 6b5fccd46..b23d8b15e 100644 --- a/backend/src/db/schemas/user-group-membership.ts +++ b/backend/src/db/schemas/user-group-membership.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const UserGroupMembershipSchema = z.object({ diff --git a/backend/src/db/schemas/users.ts b/backend/src/db/schemas/users.ts index 1c1f579ea..d18722db7 100644 --- a/backend/src/db/schemas/users.ts +++ b/backend/src/db/schemas/users.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const UsersSchema = z.object({ diff --git a/backend/src/db/schemas/webhooks.ts b/backend/src/db/schemas/webhooks.ts index 60f031fff..1648b7041 100644 --- a/backend/src/db/schemas/webhooks.ts +++ b/backend/src/db/schemas/webhooks.ts @@ -11,7 +11,7 @@ import { TImmutableDBKeys } from "./models"; export const WebhooksSchema = z.object({ id: z.string().uuid(), - secretPath: z.string().default("/"), + secretPath: z.string().default('/'), url: z.string().nullable().optional(), lastStatus: z.string().nullable().optional(), lastRunErrorMessage: z.string().nullable().optional(), @@ -27,7 +27,7 @@ export const WebhooksSchema = z.object({ urlCipherText: z.string().nullable().optional(), urlIV: z.string().nullable().optional(), urlTag: z.string().nullable().optional(), - type: z.string().default("general").nullable().optional(), + type: z.string().default('general').nullable().optional(), encryptedPassKey: zodBuffer.nullable().optional(), encryptedUrl: zodBuffer }); diff --git a/backend/src/db/schemas/workflow-integrations.ts b/backend/src/db/schemas/workflow-integrations.ts index ae1ae9a25..060b6ac2f 100644 --- a/backend/src/db/schemas/workflow-integrations.ts +++ b/backend/src/db/schemas/workflow-integrations.ts @@ -5,6 +5,8 @@ import { z } from "zod"; + + import { TImmutableDBKeys } from "./models"; export const WorkflowIntegrationsSchema = z.object({ diff --git a/backend/src/ee/routes/v1/index.ts b/backend/src/ee/routes/v1/index.ts index e793c687d..2bf85e9c4 100644 --- a/backend/src/ee/routes/v1/index.ts +++ b/backend/src/ee/routes/v1/index.ts @@ -32,6 +32,7 @@ import { registerSnapshotRouter } from "./snapshot-router"; import { registerSshCaRouter } from "./ssh-certificate-authority-router"; import { registerSshCertRouter } from "./ssh-certificate-router"; import { registerSshCertificateTemplateRouter } from "./ssh-certificate-template-router"; +import { registerSshHostRouter } from "./ssh-host-router"; import { registerTrustedIpRouter } from "./trusted-ip-router"; import { registerUserAdditionalPrivilegeRouter } from "./user-additional-privilege-router"; @@ -82,6 +83,7 @@ export const registerV1EERoutes = async (server: FastifyZodProvider) => { await sshRouter.register(registerSshCaRouter, { prefix: "/ca" }); await sshRouter.register(registerSshCertRouter, { prefix: "/certificates" }); await sshRouter.register(registerSshCertificateTemplateRouter, { prefix: "/certificate-templates" }); + await sshRouter.register(registerSshHostRouter, { prefix: "/hosts" }); }, { prefix: "/ssh" } ); diff --git a/backend/src/ee/routes/v1/ssh-host-router.ts b/backend/src/ee/routes/v1/ssh-host-router.ts new file mode 100644 index 000000000..671b29619 --- /dev/null +++ b/backend/src/ee/routes/v1/ssh-host-router.ts @@ -0,0 +1,265 @@ +import { z } from "zod"; + +import { sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema"; +import { isValidHostname } from "@app/ee/services/ssh-host/ssh-host-validators"; +import { SSH_HOSTS } from "@app/lib/api-docs"; +import { ms } from "@app/lib/ms"; +import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; +import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; +import { AuthMode } from "@app/services/auth/auth-type"; + +export const registerSshHostRouter = async (server: FastifyZodProvider) => { + server.route({ + method: "GET", + url: "/:sshHostId", + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + sshHostId: z.string().describe(SSH_HOSTS.GET.sshHostId) + }), + response: { + 200: sanitizedSshHost.extend({ + loginMappings: z.array( + z.object({ + loginUser: z.string(), + allowedPrincipals: z.array(z.string()) + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const host = await server.services.sshHost.getSshHost({ + sshHostId: req.params.sshHostId, + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId + }); + + // TODO: audit log + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: certificateTemplate.projectId, + // event: { + // type: EventType.GET_SSH_CERTIFICATE_TEMPLATE, + // metadata: { + // certificateTemplateId: certificateTemplate.id + // } + // } + // }); + + return host; + } + }); + + server.route({ + method: "POST", + url: "/", + config: { + rateLimit: writeLimit + }, + schema: { + description: "Create SSH Host", + body: z.object({ + projectId: z.string().describe(SSH_HOSTS.CREATE.projectId), + hostname: z + .string() + .min(1) + .refine((v) => isValidHostname(v), { + message: "Hostname must be a valid hostname" + }) + .describe(SSH_HOSTS.CREATE.hostname), + userCertTtl: z + .string() + .refine((val) => ms(val) > 0, "TTL must be a positive number") + .default("8h") + .describe(SSH_HOSTS.CREATE.userCertTtl), + hostCertTtl: z + .string() + .refine((val) => ms(val) > 0, "TTL must be a positive number") + .default("1y") + .describe(SSH_HOSTS.CREATE.hostCertTtl), + loginMappings: z + .object({ + loginUser: z.string().describe(SSH_HOSTS.CREATE.loginUser), // TODO: reinforce validation + allowedPrincipals: z.array(z.string()).describe(SSH_HOSTS.CREATE.allowedPrincipals) // TODO: reinforce validation + }) + .array() + .default([]) + .describe(SSH_HOSTS.CREATE.loginMappings) + }), + response: { + 200: sanitizedSshHost.extend({ + loginMappings: z.array( + z.object({ + loginUser: z.string(), + allowedPrincipals: z.array(z.string()) + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const host = await server.services.sshHost.createSshHost({ + ...req.body, + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId + }); + + // TODO: audit log + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: ca.projectId, + // event: { + // type: EventType.CREATE_SSH_CERTIFICATE_TEMPLATE, + // metadata: { + // certificateTemplateId: certificateTemplate.id, + // sshCaId: ca.id, + // name: certificateTemplate.name, + // ttl: certificateTemplate.ttl, + // maxTTL: certificateTemplate.maxTTL, + // allowedUsers: certificateTemplate.allowedUsers, + // allowedHosts: certificateTemplate.allowedHosts, + // allowUserCertificates: certificateTemplate.allowUserCertificates, + // allowHostCertificates: certificateTemplate.allowHostCertificates, + // allowCustomKeyIds: certificateTemplate.allowCustomKeyIds + // } + // } + // }); + + return host; + } + }); + + server.route({ + method: "PATCH", + url: "/:sshHostId", + config: { + rateLimit: writeLimit + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + schema: { + description: "Update SSH Host", + params: z.object({ + sshHostId: z.string().trim().describe(SSH_HOSTS.UPDATE.sshHostId) + }), + body: z.object({ + hostname: z + .string() + .min(1) + .refine((v) => isValidHostname(v), { + message: "Hostname must be a valid hostname" + }) + .describe(SSH_HOSTS.UPDATE.hostname), + userCertTtl: z + .string() + .refine((val) => ms(val) > 0, "TTL must be a positive number") + .default("1h") + .describe(SSH_HOSTS.UPDATE.userCertTtl), + hostCertTtl: z + .string() + .refine((val) => ms(val) > 0, "TTL must be a positive number") + .default("1y") + .describe(SSH_HOSTS.UPDATE.hostCertTtl), + loginMappings: z + .object({ + loginUser: z.string().describe(SSH_HOSTS.CREATE.loginUser), + allowedPrincipals: z.array(z.string()).describe(SSH_HOSTS.CREATE.allowedPrincipals) + }) + .array() + .optional() + .describe(SSH_HOSTS.CREATE.loginMappings) + }), + response: { + 200: sanitizedSshHost.extend({ + loginMappings: z.array( + z.object({ + loginUser: z.string(), + allowedPrincipals: z.array(z.string()) + }) + ) + }) + } + }, + handler: async (req) => { + const host = await server.services.sshHost.updateSshHost({ + sshHostId: req.params.sshHostId, + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId, + ...req.body + }); + + // TODO: audit log + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: ca.projectId, + // event: { + // type: EventType.UPDATE_SSH_CA, + // metadata: { + // sshCaId: ca.id, + // friendlyName: ca.friendlyName, + // status: ca.status as SshCaStatus + // } + // } + // }); + + return host; + } + }); + + server.route({ + method: "DELETE", + url: "/:sshHostId", + config: { + rateLimit: writeLimit + }, + schema: { + params: z.object({ + sshHostId: z.string().describe(SSH_HOSTS.DELETE.sshHostId) + }), + response: { + 200: sanitizedSshHost.extend({ + loginMappings: z.array( + z.object({ + loginUser: z.string(), + allowedPrincipals: z.array(z.string()) + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const host = await server.services.sshHost.deleteSshHost({ + sshHostId: req.params.sshHostId, + actor: req.permission.type, + actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, + actorOrgId: req.permission.orgId + }); + + // TODO: audit log + // await server.services.auditLog.createAuditLog({ + // ...req.auditLogInfo, + // projectId: certificateTemplate.projectId, + // event: { + // type: EventType.DELETE_SSH_CERTIFICATE_TEMPLATE, + // metadata: { + // certificateTemplateId: certificateTemplate.id + // } + // } + // }); + + return host; + } + }); +}; diff --git a/backend/src/ee/services/permission/project-permission.ts b/backend/src/ee/services/permission/project-permission.ts index 2637db994..231d22743 100644 --- a/backend/src/ee/services/permission/project-permission.ts +++ b/backend/src/ee/services/permission/project-permission.ts @@ -112,6 +112,7 @@ export enum ProjectPermissionSub { SshCertificateAuthorities = "ssh-certificate-authorities", SshCertificates = "ssh-certificates", SshCertificateTemplates = "ssh-certificate-templates", + SshHosts = "ssh-hosts", PkiAlerts = "pki-alerts", PkiCollections = "pki-collections", Kms = "kms", @@ -195,6 +196,7 @@ export type ProjectPermissionSet = | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificates] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates] + | [ProjectPermissionActions, ProjectPermissionSub.SshHosts] | [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts] | [ProjectPermissionActions, ProjectPermissionSub.PkiCollections] | [ProjectPermissionSecretSyncActions, ProjectPermissionSub.SecretSyncs] @@ -418,6 +420,12 @@ const GeneralPermissionSchema = [ "Describe what action an entity can take." ) }), + z.object({ + subject: z.literal(ProjectPermissionSub.SshHosts).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.PkiAlerts).describe("The entity this permission pertains to."), action: CASL_ACTION_SCHEMA_NATIVE_ENUM(ProjectPermissionActions).describe( @@ -571,7 +579,8 @@ const buildAdminPermissionRules = () => { ProjectPermissionSub.PkiCollections, ProjectPermissionSub.SshCertificateAuthorities, ProjectPermissionSub.SshCertificates, - ProjectPermissionSub.SshCertificateTemplates + ProjectPermissionSub.SshCertificateTemplates, + ProjectPermissionSub.SshHosts ].forEach((el) => { can( [ @@ -831,7 +840,7 @@ const buildMemberPermissionRules = () => { can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificates); can([ProjectPermissionActions.Create], ProjectPermissionSub.SshCertificates); can([ProjectPermissionActions.Read], ProjectPermissionSub.SshCertificateTemplates); - + can([ProjectPermissionActions.Read], ProjectPermissionSub.SshHosts); can( [ ProjectPermissionCmekActions.Create, @@ -892,6 +901,7 @@ const buildViewerPermissionRules = () => { can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateAuthorities); can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificates); can(ProjectPermissionActions.Read, ProjectPermissionSub.SshCertificateTemplates); + can(ProjectPermissionActions.Read, ProjectPermissionSub.SshHosts); can(ProjectPermissionSecretSyncActions.Read, ProjectPermissionSub.SecretSyncs); return rules; diff --git a/backend/src/ee/services/ssh-host/ssh-host-dal.ts b/backend/src/ee/services/ssh-host/ssh-host-dal.ts new file mode 100644 index 000000000..aeb180191 --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-dal.ts @@ -0,0 +1,113 @@ +import { Knex } from "knex"; + +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { DatabaseError } from "@app/lib/errors"; +import { groupBy, unique } from "@app/lib/fn"; +import { ormify } from "@app/lib/knex"; + +export type TSshHostDALFactory = ReturnType; + +export const sshHostDALFactory = (db: TDbClient) => { + const sshHostOrm = ormify(db, TableName.SshHost); + + const findSshHostsWithLoginMappings = async (projectId: string, tx?: Knex) => { + try { + const rows = await (tx || db.replicaNode())(TableName.SshHost) + .leftJoin( + TableName.SshHostLoginMapping, + `${TableName.SshHost}.id`, + `${TableName.SshHostLoginMapping}.sshHostId` + ) + .where(`${TableName.SshHost}.projectId`, projectId) + .select( + db.ref("id").withSchema(TableName.SshHost).as("sshHostId"), + db.ref("projectId").withSchema(TableName.SshHost), + db.ref("hostname").withSchema(TableName.SshHost), + db.ref("userCertTtl").withSchema(TableName.SshHost), + db.ref("hostCertTtl").withSchema(TableName.SshHost), + db.ref("loginUser").withSchema(TableName.SshHostLoginMapping), + db.ref("allowedPrincipals").withSchema(TableName.SshHostLoginMapping) + ) + .orderBy(`${TableName.SshHost}.updatedAt`, "desc"); + + const hostsGrouped = groupBy(rows, (r) => r.sshHostId); + return Object.values(hostsGrouped).map((hostRows) => { + const { sshHostId, hostname, userCertTtl, hostCertTtl } = hostRows[0]; + + const loginMappingGrouped = groupBy( + hostRows.filter((r) => r.loginUser), + (r) => r.loginUser + ); + + const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({ + loginUser, + allowedPrincipals: unique(entries.flatMap((entry) => entry.allowedPrincipals ?? [])) + })); + + return { + id: sshHostId, + projectId, + hostname, + userCertTtl, + hostCertTtl, + loginMappings + }; + }); + } catch (error) { + throw new DatabaseError({ error, name: `${TableName.SshHost}: FindSshHostsWithLoginMappings` }); + } + }; + + const findSshHostByIdWithLoginMappings = async (sshHostId: string, tx?: Knex) => { + try { + const rows = await (tx || db.replicaNode())(TableName.SshHost) + .leftJoin( + TableName.SshHostLoginMapping, + `${TableName.SshHost}.id`, + `${TableName.SshHostLoginMapping}.sshHostId` + ) + .where(`${TableName.SshHost}.id`, sshHostId) + .select( + db.ref("id").withSchema(TableName.SshHost).as("sshHostId"), + db.ref("projectId").withSchema(TableName.SshHost), + db.ref("hostname").withSchema(TableName.SshHost), + db.ref("userCertTtl").withSchema(TableName.SshHost), + db.ref("hostCertTtl").withSchema(TableName.SshHost), + db.ref("loginUser").withSchema(TableName.SshHostLoginMapping), + db.ref("allowedPrincipals").withSchema(TableName.SshHostLoginMapping) + ); + + if (rows.length === 0) return null; + + const { sshHostId: id, projectId, hostname, userCertTtl, hostCertTtl } = rows[0]; + + const loginMappingGrouped = groupBy( + rows.filter((r) => r.loginUser), + (r) => r.loginUser + ); + + const loginMappings = Object.entries(loginMappingGrouped).map(([loginUser, entries]) => ({ + loginUser, + allowedPrincipals: unique(entries.flatMap((entry) => entry.allowedPrincipals ?? [])) + })); + + return { + id, + projectId, + hostname, + userCertTtl, + hostCertTtl, + loginMappings + }; + } catch (error) { + throw new DatabaseError({ error, name: `${TableName.SshHost}: FindSshHostByIdWithLoginMappings` }); + } + }; + + return { + ...sshHostOrm, + findSshHostsWithLoginMappings, + findSshHostByIdWithLoginMappings + }; +}; diff --git a/backend/src/ee/services/ssh-host/ssh-host-login-mapping-dal.ts b/backend/src/ee/services/ssh-host/ssh-host-login-mapping-dal.ts new file mode 100644 index 000000000..4fdd2c289 --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-login-mapping-dal.ts @@ -0,0 +1,10 @@ +import { TDbClient } from "@app/db"; +import { TableName } from "@app/db/schemas"; +import { ormify } from "@app/lib/knex"; + +export type TSshHostLoginMappingDALFactory = ReturnType; + +export const sshHostLoginMappingDALFactory = (db: TDbClient) => { + const sshHostLoginMappingOrm = ormify(db, TableName.SshHostLoginMapping); + return sshHostLoginMappingOrm; +}; diff --git a/backend/src/ee/services/ssh-host/ssh-host-schema.ts b/backend/src/ee/services/ssh-host/ssh-host-schema.ts new file mode 100644 index 000000000..4547d7662 --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-schema.ts @@ -0,0 +1,9 @@ +import { SshHostsSchema } from "@app/db/schemas"; + +export const sanitizedSshHost = SshHostsSchema.pick({ + id: true, + projectId: true, + hostname: true, + userCertTtl: true, + hostCertTtl: true +}); diff --git a/backend/src/ee/services/ssh-host/ssh-host-service.ts b/backend/src/ee/services/ssh-host/ssh-host-service.ts new file mode 100644 index 000000000..434479ced --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-service.ts @@ -0,0 +1,215 @@ +import { ForbiddenError } from "@casl/ability"; + +import { ActionProjectType } from "@app/db/schemas"; +import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission"; +import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; +import { TSshHostLoginMappingDALFactory } from "@app/ee/services/ssh-host/ssh-host-login-mapping-dal"; +import { BadRequestError, NotFoundError } from "@app/lib/errors"; + +import { TCreateSshHostDTO, TDeleteSshHostDTO, TGetSshHostDTO, TUpdateSshHostDTO } from "./ssh-host-types"; + +type TSshCertificateAuthorityServiceFactoryDep = { + sshHostDAL: Pick< + TSshHostDALFactory, + "transaction" | "create" | "findById" | "updateById" | "deleteById" | "findOne" | "findSshHostByIdWithLoginMappings" + >; + sshHostLoginMappingDAL: Pick< + TSshHostLoginMappingDALFactory, + "transaction" | "create" | "findById" | "updateById" | "deleteById" | "findOne" | "insertMany" | "delete" + >; + permissionService: Pick; +}; + +export type TSshHostServiceFactory = ReturnType; + +/** + * Checklist: + * - check permissions across various roles to make sure it makes sense for the SSH hosts + */ + +export const sshHostServiceFactory = ({ + sshHostDAL, + sshHostLoginMappingDAL, + permissionService +}: TSshCertificateAuthorityServiceFactoryDep) => { + const createSshHost = async ({ + projectId, + hostname, + userCertTtl, + hostCertTtl, + loginMappings, + actorId, + actorAuthMethod, + actor, + actorOrgId + }: TCreateSshHostDTO) => { + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.SSH + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Create, ProjectPermissionSub.SshHosts); + + const newSshHost = await sshHostDAL.transaction(async (tx) => { + const existingHost = await sshHostDAL.findOne( + { + projectId, + hostname + }, + tx + ); + + if (existingHost) { + throw new BadRequestError({ + message: `SSH host with hostname ${hostname} already exists` + }); + } + + const host = await sshHostDAL.create( + { + projectId, + hostname, + userCertTtl, + hostCertTtl + }, + tx + ); + + await sshHostLoginMappingDAL.insertMany( + loginMappings.map(({ loginUser, allowedPrincipals }) => ({ + sshHostId: host.id, + loginUser, + allowedPrincipals + })), + tx + ); + + const newSshHostWithLoginMappings = await sshHostDAL.findSshHostByIdWithLoginMappings(host.id, tx); + if (!newSshHostWithLoginMappings) { + throw new NotFoundError({ message: `SSH host with ID '${host.id}' not found` }); + } + + return newSshHostWithLoginMappings; + }); + + return newSshHost; + }; + + const updateSshHost = async ({ + sshHostId, + hostname, + userCertTtl, + hostCertTtl, + loginMappings, + actorId, + actorAuthMethod, + actor, + actorOrgId + }: TUpdateSshHostDTO) => { + const host = await sshHostDAL.findById(sshHostId); + if (!host) throw new NotFoundError({ message: `SSH host with ID '${sshHostId}' not found` }); + + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId: host.projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.SSH + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.SshHosts); + + const updatedHost = await sshHostDAL.transaction(async (tx) => { + await sshHostDAL.updateById( + sshHostId, + { + hostname, + userCertTtl, + hostCertTtl + }, + tx + ); + + if (loginMappings) { + await sshHostLoginMappingDAL.delete({ sshHostId }, tx); + if (loginMappings.length) { + await sshHostLoginMappingDAL.insertMany( + loginMappings.map(({ loginUser, allowedPrincipals }) => ({ + sshHostId: host.id, + loginUser, + allowedPrincipals + })), + tx + ); + } + } + + const updatedHostWithLoginMappings = await sshHostDAL.findSshHostByIdWithLoginMappings(sshHostId, tx); + if (!updatedHostWithLoginMappings) { + throw new NotFoundError({ message: `SSH host with ID '${sshHostId}' not found` }); + } + + return updatedHostWithLoginMappings; + }); + + return updatedHost; + }; + + const deleteSshHost = async ({ sshHostId, actorId, actorAuthMethod, actor, actorOrgId }: TDeleteSshHostDTO) => { + const host = await sshHostDAL.findSshHostByIdWithLoginMappings(sshHostId); + if (!host) throw new NotFoundError({ message: `SSH host with ID '${sshHostId}' not found` }); + + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId: host.projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.SSH + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Delete, ProjectPermissionSub.SshHosts); + + await sshHostDAL.transaction(async (tx) => { + await sshHostLoginMappingDAL.delete({ sshHostId }, tx); + await sshHostDAL.deleteById(sshHostId, tx); + }); + + return host; + }; + + const getSshHost = async ({ sshHostId, actorId, actorAuthMethod, actor, actorOrgId }: TGetSshHostDTO) => { + const host = await sshHostDAL.findSshHostByIdWithLoginMappings(sshHostId); + if (!host) { + throw new NotFoundError({ + message: `SSH host with ID ${sshHostId} not found` + }); + } + + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId: host.projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.SSH + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHosts); + + return host; + }; + + return { + createSshHost, + updateSshHost, + deleteSshHost, + getSshHost + }; +}; diff --git a/backend/src/ee/services/ssh-host/ssh-host-types.ts b/backend/src/ee/services/ssh-host/ssh-host-types.ts new file mode 100644 index 000000000..df480cd6c --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-types.ts @@ -0,0 +1,30 @@ +import { TProjectPermission } from "@app/lib/types"; + +export type TCreateSshHostDTO = { + hostname: string; + userCertTtl: string; + hostCertTtl: string; + loginMappings: { + loginUser: string; + allowedPrincipals: string[]; + }[]; +} & TProjectPermission; + +export type TUpdateSshHostDTO = { + sshHostId: string; + hostname?: string; + userCertTtl?: string; + hostCertTtl?: string; + loginMappings?: { + loginUser: string; + allowedPrincipals: string[]; + }[]; +} & Omit; + +export type TGetSshHostDTO = { + sshHostId: string; +} & Omit; + +export type TDeleteSshHostDTO = { + sshHostId: string; +} & Omit; diff --git a/backend/src/ee/services/ssh-host/ssh-host-validators.ts b/backend/src/ee/services/ssh-host/ssh-host-validators.ts new file mode 100644 index 000000000..7b739b9cb --- /dev/null +++ b/backend/src/ee/services/ssh-host/ssh-host-validators.ts @@ -0,0 +1,15 @@ +import { isFQDN } from "@app/lib/validator/validate-url"; + +export const isValidHostname = (value: string): boolean => { + if (typeof value !== "string") return false; + if (value.length > 255) return false; + + // Only allow strict FQDNs, no wildcards or IPs + return isFQDN(value, { + require_tld: true, + allow_underscores: false, + allow_trailing_dot: false, + allow_numeric_tld: true, + allow_wildcard: false + }); +}; diff --git a/backend/src/lib/api-docs/constants.ts b/backend/src/lib/api-docs/constants.ts index 1f67cd6e0..1b35b7ab1 100644 --- a/backend/src/lib/api-docs/constants.ts +++ b/backend/src/lib/api-docs/constants.ts @@ -503,6 +503,9 @@ export const PROJECTS = { LIST_SSH_CAS: { projectId: "The ID of the project to list SSH CAs for." }, + LIST_SSH_HOSTS: { + projectId: "The ID of the project to list SSH hosts 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.", @@ -1313,6 +1316,35 @@ export const SSH_CERTIFICATE_TEMPLATES = { } }; +export const SSH_HOSTS = { + GET: { + sshHostId: "The ID of the SSH host to get." + }, + CREATE: { + projectId: "The ID of the project to create the SSH host in.", + hostname: "The hostname of the SSH host.", + userCertTtl: "The time to live for user certificates issued under this host.", + hostCertTtl: "The time to live for host certificates issued under this host.", + loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')", + allowedPrincipals: "A list of allowed principals that can log in as the login user.", + loginMappings: + "A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals." + }, + UPDATE: { + sshHostId: "The ID of the SSH host to update.", + hostname: "The hostname of the SSH host to update to.", + userCertTtl: "The time to live for user certificates issued under this host to update to.", + hostCertTtl: "The time to live for host certificates issued under this host to update to.", + loginUser: "A login user on the remote machine (e.g. 'ec2-user', 'deploy', 'admin')", + allowedPrincipals: "A list of allowed principals that can log in as the login user.", + loginMappings: + "A list of login mappings for the SSH host. Each login mapping contains a login user and a list of corresponding allowed principals." + }, + DELETE: { + sshHostId: "The ID of the SSH host to delete." + } +}; + export const CERTIFICATE_AUTHORITIES = { CREATE: { projectSlug: "Slug of the project to create the CA in.", diff --git a/backend/src/server/routes/index.ts b/backend/src/server/routes/index.ts index dac88791f..850081b27 100644 --- a/backend/src/server/routes/index.ts +++ b/backend/src/server/routes/index.ts @@ -93,6 +93,9 @@ import { sshCertificateBodyDALFactory } from "@app/ee/services/ssh-certificate/s 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 { sshCertificateTemplateServiceFactory } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-service"; +import { sshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; +import { sshHostLoginMappingDALFactory } from "@app/ee/services/ssh-host/ssh-host-login-mapping-dal"; +import { sshHostServiceFactory } from "@app/ee/services/ssh-host/ssh-host-service"; import { trustedIpDALFactory } from "@app/ee/services/trusted-ip/trusted-ip-dal"; import { trustedIpServiceFactory } from "@app/ee/services/trusted-ip/trusted-ip-service"; import { TKeyStoreFactory } from "@app/keystore/keystore"; @@ -382,6 +385,8 @@ export const registerRoutes = async ( const sshCertificateAuthorityDAL = sshCertificateAuthorityDALFactory(db); const sshCertificateAuthoritySecretDAL = sshCertificateAuthoritySecretDALFactory(db); const sshCertificateTemplateDAL = sshCertificateTemplateDALFactory(db); + const sshHostDAL = sshHostDALFactory(db); + const sshHostLoginMappingDAL = sshHostLoginMappingDALFactory(db); const kmsDAL = kmskeyDALFactory(db); const internalKmsDAL = internalKmsDALFactory(db); @@ -790,6 +795,12 @@ export const registerRoutes = async ( permissionService }); + const sshHostService = sshHostServiceFactory({ + sshHostDAL, + sshHostLoginMappingDAL, + permissionService + }); + const certificateAuthorityService = certificateAuthorityServiceFactory({ certificateAuthorityDAL, certificateAuthorityCertDAL, @@ -955,6 +966,7 @@ export const registerRoutes = async ( sshCertificateAuthorityDAL, sshCertificateDAL, sshCertificateTemplateDAL, + sshHostDAL, projectUserMembershipRoleDAL, identityProjectMembershipRoleDAL, keyStore, @@ -1567,6 +1579,7 @@ export const registerRoutes = async ( certificate: certificateService, sshCertificateAuthority: sshCertificateAuthorityService, sshCertificateTemplate: sshCertificateTemplateService, + sshHost: sshHostService, certificateAuthority: certificateAuthorityService, certificateTemplate: certificateTemplateService, certificateAuthorityCrl: certificateAuthorityCrlService, diff --git a/backend/src/server/routes/v2/project-router.ts b/backend/src/server/routes/v2/project-router.ts index 84d2ee6cd..2c6030d70 100644 --- a/backend/src/server/routes/v2/project-router.ts +++ b/backend/src/server/routes/v2/project-router.ts @@ -13,6 +13,7 @@ import { InfisicalProjectTemplate } from "@app/ee/services/project-template/proj 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 { sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema"; import { PROJECTS } from "@app/lib/api-docs"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { slugSchema } from "@app/server/lib/schemas"; @@ -600,4 +601,43 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { return { cas }; } }); + + server.route({ + method: "GET", + url: "/:projectId/ssh-hosts", + config: { + rateLimit: readLimit + }, + schema: { + params: z.object({ + projectId: z.string().trim().describe(PROJECTS.LIST_SSH_HOSTS.projectId) + }), + response: { + 200: z.object({ + hosts: z.array( + sanitizedSshHost.extend({ + loginMappings: z.array( + z.object({ + loginUser: z.string(), + allowedPrincipals: z.array(z.string()) + }) + ) + }) + ) + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]), + handler: async (req) => { + const hosts = await server.services.project.listProjectSshHosts({ + actorId: req.permission.id, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + actor: req.permission.type, + projectId: req.params.projectId + }); + + return { hosts }; + } + }); }; diff --git a/backend/src/services/project/project-service.ts b/backend/src/services/project/project-service.ts index dbef33c10..0e39554f8 100644 --- a/backend/src/services/project/project-service.ts +++ b/backend/src/services/project/project-service.ts @@ -22,6 +22,7 @@ import { InfisicalProjectTemplate } from "@app/ee/services/project-template/proj 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 { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal"; import { TKeyStoreFactory } from "@app/keystore/keystore"; import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption"; import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors"; @@ -75,6 +76,7 @@ import { TListProjectSshCasDTO, TListProjectSshCertificatesDTO, TListProjectSshCertificateTemplatesDTO, + TListProjectSshHostsDTO, TLoadProjectKmsBackupDTO, TToggleProjectAutoCapitalizationDTO, TUpdateAuditLogsRetentionDTO, @@ -119,6 +121,7 @@ type TProjectServiceFactoryDep = { sshCertificateAuthorityDAL: Pick; sshCertificateDAL: Pick; sshCertificateTemplateDAL: Pick; + sshHostDAL: Pick; permissionService: TPermissionServiceFactory; orgService: Pick; licenseService: Pick; @@ -171,6 +174,7 @@ export const projectServiceFactory = ({ sshCertificateAuthorityDAL, sshCertificateDAL, sshCertificateTemplateDAL, + sshHostDAL, keyStore, kmsService, projectBotDAL, @@ -1037,6 +1041,32 @@ export const projectServiceFactory = ({ return cas; }; + /** + * Return list of SSH hosts for project + */ + const listProjectSshHosts = async ({ + actorId, + actorOrgId, + actorAuthMethod, + actor, + projectId + }: TListProjectSshHostsDTO) => { + const { permission } = await permissionService.getProjectPermission({ + actor, + actorId, + projectId, + actorAuthMethod, + actorOrgId, + actionProjectType: ActionProjectType.SSH + }); + + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHosts); + + const hosts = await sshHostDAL.findSshHostsWithLoginMappings(projectId); + + return hosts; + }; + /** * Return list of SSH certificates for project */ @@ -1355,6 +1385,7 @@ export const projectServiceFactory = ({ listProjectPkiCollections, listProjectCertificateTemplates, listProjectSshCas, + listProjectSshHosts, listProjectSshCertificates, listProjectSshCertificateTemplates, updateVersionLimit, diff --git a/backend/src/services/project/project-types.ts b/backend/src/services/project/project-types.ts index 5ccf33d23..b9dee98bc 100644 --- a/backend/src/services/project/project-types.ts +++ b/backend/src/services/project/project-types.ts @@ -143,6 +143,7 @@ export type TGetProjectKmsKey = TProjectPermission; export type TListProjectCertificateTemplatesDTO = TProjectPermission; export type TListProjectSshCasDTO = TProjectPermission; +export type TListProjectSshHostsDTO = TProjectPermission; export type TListProjectSshCertificateTemplatesDTO = TProjectPermission; export type TListProjectSshCertificatesDTO = { offset: number; diff --git a/docs/documentation/platform/ssh.mdx b/docs/documentation/platform/ssh.mdx index 00f7cf269..14c6d93e9 100644 --- a/docs/documentation/platform/ssh.mdx +++ b/docs/documentation/platform/ssh.mdx @@ -163,7 +163,7 @@ as part of the SSH operation. ## Guide to Using Infisical SSH to Access a Host -In the following steps, we show how to obtain a SSH certificate and use it for a client to access a host via CLI: +In the following steps, we show how to obtain an SSH certificate and use it for a client to access a host via CLI: The subsequent guide assumes the following prerequisites: @@ -221,7 +221,7 @@ In the following steps, we show how to configure host key signing for clients to This guide expects that the remote host already has an existing SSH key pair (typically found in the `/etc/ssh/` folder at `/etc/ssh/ssh_host__key` and `.pub`). -In the event that the remote host does not have an existing SSH key pair, you can generate a new key pair using the `ssh-keygen` command: `ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ''`. This will generate: +If the remote host does not have an existing SSH key pair, you can generate a new key pair using the `ssh-keygen` command: `ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ''`. This will generate: - A private key: `/etc/ssh/ssh_host_rsa_key`. - A public key: `/etc/ssh/ssh_host_rsa_key.pub`. diff --git a/frontend/src/context/ProjectPermissionContext/types.ts b/frontend/src/context/ProjectPermissionContext/types.ts index fb74e675c..2312e89a8 100644 --- a/frontend/src/context/ProjectPermissionContext/types.ts +++ b/frontend/src/context/ProjectPermissionContext/types.ts @@ -139,6 +139,7 @@ export enum ProjectPermissionSub { SshCertificateAuthorities = "ssh-certificate-authorities", SshCertificateTemplates = "ssh-certificate-templates", SshCertificates = "ssh-certificates", + SshHosts = "ssh-hosts", PkiAlerts = "pki-alerts", PkiCollections = "pki-collections", Kms = "kms", @@ -224,6 +225,7 @@ export type ProjectPermissionSet = | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateAuthorities] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificateTemplates] | [ProjectPermissionActions, ProjectPermissionSub.SshCertificates] + | [ProjectPermissionActions, ProjectPermissionSub.SshHosts] | [ProjectPermissionActions, ProjectPermissionSub.PkiAlerts] | [ProjectPermissionActions, ProjectPermissionSub.PkiCollections] | [ProjectPermissionSecretSyncActions, ProjectPermissionSub.SecretSyncs] diff --git a/frontend/src/hooks/api/index.tsx b/frontend/src/hooks/api/index.tsx index 8311dbf2d..52d6dceb2 100644 --- a/frontend/src/hooks/api/index.tsx +++ b/frontend/src/hooks/api/index.tsx @@ -41,6 +41,7 @@ export * from "./serverDetails"; export * from "./serviceTokens"; export * from "./sshCa"; export * from "./sshCertificateTemplates"; +export * from "./sshHost"; export * from "./ssoConfig"; export * from "./subscriptions"; export * from "./tags"; diff --git a/frontend/src/hooks/api/sshHost/index.tsx b/frontend/src/hooks/api/sshHost/index.tsx new file mode 100644 index 000000000..2da612286 --- /dev/null +++ b/frontend/src/hooks/api/sshHost/index.tsx @@ -0,0 +1,2 @@ +export { useCreateSshHost, useDeleteSshHost, useUpdateSshHost } from "./mutations"; +export { useGetSshHostById } from "./queries"; diff --git a/frontend/src/hooks/api/sshHost/mutations.tsx b/frontend/src/hooks/api/sshHost/mutations.tsx new file mode 100644 index 000000000..f6b831f3e --- /dev/null +++ b/frontend/src/hooks/api/sshHost/mutations.tsx @@ -0,0 +1,45 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { workspaceKeys } from "../workspace/query-keys"; +import { TCreateSshHostDTO, TDeleteSshHostDTO, TSshHost, TUpdateSshHostDTO } from "./types"; + +export const useCreateSshHost = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (body) => { + const { data: host } = await apiRequest.post("/api/v1/ssh/hosts", body); + return host; + }, + onSuccess: ({ projectId }) => { + queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceSshHosts(projectId) }); + } + }); +}; + +export const useUpdateSshHost = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ sshHostId, ...body }) => { + const { data: host } = await apiRequest.patch(`/api/v1/ssh/hosts/${sshHostId}`, body); + return host; + }, + onSuccess: ({ projectId }) => { + queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceSshHosts(projectId) }); + } + }); +}; + +export const useDeleteSshHost = () => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async ({ sshHostId }) => { + const { data: host } = await apiRequest.delete(`/api/v1/ssh/hosts/${sshHostId}`); + return host; + }, + onSuccess: ({ projectId }) => { + queryClient.invalidateQueries({ queryKey: workspaceKeys.getWorkspaceSshHosts(projectId) }); + } + }); +}; diff --git a/frontend/src/hooks/api/sshHost/queries.tsx b/frontend/src/hooks/api/sshHost/queries.tsx new file mode 100644 index 000000000..b8fac7270 --- /dev/null +++ b/frontend/src/hooks/api/sshHost/queries.tsx @@ -0,0 +1,20 @@ +import { useQuery } from "@tanstack/react-query"; + +import { apiRequest } from "@app/config/request"; + +import { TSshHost } from "./types"; + +export const sshHostKeys = { + getSshHostById: (sshHostId: string) => [{ sshHostId }, "ssh-host"] +}; + +export const useGetSshHostById = (sshHostId: string) => { + return useQuery({ + queryKey: sshHostKeys.getSshHostById(sshHostId), + queryFn: async () => { + const { data: sshHost } = await apiRequest.get(`/api/v1/ssh/hosts/${sshHostId}`); + return sshHost; + }, + enabled: Boolean(sshHostId) + }); +}; diff --git a/frontend/src/hooks/api/sshHost/types.ts b/frontend/src/hooks/api/sshHost/types.ts new file mode 100644 index 000000000..02202233f --- /dev/null +++ b/frontend/src/hooks/api/sshHost/types.ts @@ -0,0 +1,37 @@ +export type TSshHost = { + id: string; + projectId: string; + hostname: string; + userCertTtl: string; + hostCertTtl: string; + loginMappings: { + loginUser: string; + allowedPrincipals: string[]; + }[]; +}; + +export type TCreateSshHostDTO = { + projectId: string; + hostname: string; + userCertTtl?: string; + hostCertTtl?: string; + loginMappings: { + loginUser: string; + allowedPrincipals: string[]; + }[]; +}; + +export type TUpdateSshHostDTO = { + sshHostId: string; + hostname?: string; + userCertTtl?: string; + hostCertTtl?: string; + loginMappings?: { + loginUser: string; + allowedPrincipals: string[]; + }[]; +}; + +export type TDeleteSshHostDTO = { + sshHostId: string; +}; diff --git a/frontend/src/hooks/api/workspace/index.tsx b/frontend/src/hooks/api/workspace/index.tsx index 665462e7d..58d60bc1e 100644 --- a/frontend/src/hooks/api/workspace/index.tsx +++ b/frontend/src/hooks/api/workspace/index.tsx @@ -35,6 +35,7 @@ export { useListWorkspaceSshCas, useListWorkspaceSshCertificates, useListWorkspaceSshCertificateTemplates, + useListWorkspaceSshHosts, useNameWorkspaceSecrets, useToggleAutoCapitalization, useUpdateIdentityWorkspaceRole, diff --git a/frontend/src/hooks/api/workspace/queries.tsx b/frontend/src/hooks/api/workspace/queries.tsx index 0b154b78b..d2937381b 100644 --- a/frontend/src/hooks/api/workspace/queries.tsx +++ b/frontend/src/hooks/api/workspace/queries.tsx @@ -17,6 +17,7 @@ import { TPkiCollection } from "../pkiCollections/types"; import { EncryptedSecret } from "../secrets/types"; import { TSshCertificate, TSshCertificateAuthority } from "../sshCa/types"; import { TSshCertificateTemplate } from "../sshCertificateTemplates/types"; +import { TSshHost } from "../sshHost/types"; import { userKeys } from "../users/query-keys"; import { TWorkspaceUser } from "../users/types"; import { ProjectSlackConfig } from "../workflowIntegrations/types"; @@ -809,6 +810,19 @@ export const useListWorkspaceSshCas = (projectId: string) => { }); }; +export const useListWorkspaceSshHosts = (projectId: string) => { + return useQuery({ + queryKey: workspaceKeys.getWorkspaceSshHosts(projectId), + queryFn: async () => { + const { + data: { hosts } + } = await apiRequest.get<{ hosts: TSshHost[] }>(`/api/v2/workspace/${projectId}/ssh-hosts`); + return hosts; + }, + enabled: Boolean(projectId) + }); +}; + export const useListWorkspaceSshCertificateTemplates = (projectId: string) => { return useQuery({ queryKey: workspaceKeys.getWorkspaceSshCertificateTemplates(projectId), diff --git a/frontend/src/hooks/api/workspace/query-keys.tsx b/frontend/src/hooks/api/workspace/query-keys.tsx index 44fee901c..a0ed5b11f 100644 --- a/frontend/src/hooks/api/workspace/query-keys.tsx +++ b/frontend/src/hooks/api/workspace/query-keys.tsx @@ -57,6 +57,7 @@ export const workspaceKeys = { getWorkspaceSshCas: (projectId: string) => [{ projectId }, "workspace-ssh-cas"] as const, allWorkspaceSshCertificates: (projectId: string) => [{ projectId }, "workspace-ssh-certificates"] as const, + getWorkspaceSshHosts: (projectId: string) => [{ projectId }, "workspace-ssh-hosts"] as const, specificWorkspaceSshCertificates: ({ offset, limit, diff --git a/frontend/src/layouts/ProjectLayout/ProjectLayout.tsx b/frontend/src/layouts/ProjectLayout/ProjectLayout.tsx index e6b39369f..372857fb0 100644 --- a/frontend/src/layouts/ProjectLayout/ProjectLayout.tsx +++ b/frontend/src/layouts/ProjectLayout/ProjectLayout.tsx @@ -120,18 +120,32 @@ export const ProjectLayout = () => { )} {isSSH && ( - - {({ isActive }) => ( - - Overview - - )} - + <> + + {({ isActive }) => ( + + Overview + + )} + + + {({ isActive }) => ( + + Hosts + + )} + + )} {isSecretManager && ( { + const { t } = useTranslation(); + return ( + <> + + {t("common.head-title", { title: "Certificates" })} + +
+
+
+ + +
+
+
+ + ); +}; diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx new file mode 100644 index 000000000..5baca41ab --- /dev/null +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostModal.tsx @@ -0,0 +1,285 @@ +import { useEffect } from "react"; +import { Controller, useFieldArray, useForm } from "react-hook-form"; +import { faPlus, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useNavigate } from "@tanstack/react-router"; +import ms from "ms"; +import { z } from "zod"; + +import { createNotification } from "@app/components/notifications"; +import { + Button, + FormControl, + FormLabel, + IconButton, + Input, + Modal, + ModalContent +} from "@app/components/v2"; +import { useWorkspace } from "@app/context"; +import { useCreateSshHost, useGetSshHostById, useUpdateSshHost } from "@app/hooks/api"; +import { UsePopUpState } from "@app/hooks/usePopUp"; + +type Props = { + popUp: UsePopUpState<["sshHost"]>; + handlePopUpToggle: (popUpName: keyof UsePopUpState<["sshHost"]>, state?: boolean) => void; +}; + +const schema = z + .object({ + hostname: z.string(), + userCertTtl: z + .string() + .trim() + .refine( + (val) => ms(val) > 0, + "TTL must be a valid time string such as 2 days, 1d, 2h 1y, ..." + ) + .default("8h"), + loginMappings: z + .object({ + loginUser: z.string().trim().min(1), + allowedPrincipals: z.string().trim().min(1) + }) + .array() + .default([]) + }) + .required(); + +export type FormData = z.infer; + +export const SshHostModal = ({ popUp, handlePopUpToggle }: Props) => { + const navigate = useNavigate(); + const { currentWorkspace } = useWorkspace(); + const projectId = currentWorkspace?.id || ""; + const { data: sshHost } = useGetSshHostById( + (popUp?.sshHost?.data as { sshHostId: string })?.sshHostId || "" + ); + + const { mutateAsync: createMutateAsync } = useCreateSshHost(); + const { mutateAsync: updateMutateAsync } = useUpdateSshHost(); + + const { + control, + handleSubmit, + reset, + formState: { isSubmitting } + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + hostname: "", + userCertTtl: "8h", + loginMappings: [] + } + }); + + const loginMappingsFormFields = useFieldArray({ + control, + name: "loginMappings" + }); + + useEffect(() => { + if (sshHost) { + reset({ + hostname: sshHost.hostname, + userCertTtl: sshHost.userCertTtl, + loginMappings: sshHost.loginMappings.map(({ loginUser, allowedPrincipals }) => ({ + loginUser, + allowedPrincipals: allowedPrincipals.join(",") + })) + }); + } else { + reset({ + hostname: "", + userCertTtl: "8h", + loginMappings: [] + }); + } + }, [sshHost]); + + const onFormSubmit = async ({ hostname, userCertTtl, loginMappings }: FormData) => { + try { + if (!projectId) return; + + if (sshHost) { + await updateMutateAsync({ + sshHostId: sshHost.id, + hostname, + userCertTtl, + loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ + loginUser, + allowedPrincipals: allowedPrincipals.split(",") + })) + }); + } else { + await createMutateAsync({ + projectId, + hostname, + userCertTtl, + loginMappings: loginMappings.map(({ loginUser, allowedPrincipals }) => ({ + loginUser, + allowedPrincipals: allowedPrincipals.split(",") + })) + }); + + // navigate({ + // to: `/${ProjectType.SSH}/$projectId/ca/$caId` as const, + // params: { + // projectId, + // caId: newCaId + // } + // }); + } + + reset(); + handlePopUpToggle("sshHost", false); + + createNotification({ + text: `Successfully ${sshHost ? "updated" : "added"} SSH host`, + type: "success" + }); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to add SSH host", + type: "error" + }); + } + }; + + return ( + { + reset(); + handlePopUpToggle("sshHost", isOpen); + }} + > + +
+ {sshHost && ( + + + + )} + ( + + + + )} + /> + ( + + + + )} + /> +
+ +
+
+ {loginMappingsFormFields.fields.map(({ id: metadataFieldId }, i) => ( +
+
+ {i === 0 && Login User} + ( + + + + )} + /> +
+
+ {i === 0 && ( + + )} + ( + + + + )} + /> +
+ loginMappingsFormFields.remove(i)} + > + + +
+ ))} +
+
+ +
+
+ + +
+ +
+
+ ); +}; diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx new file mode 100644 index 000000000..5e67b090f --- /dev/null +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsSection.tsx @@ -0,0 +1,74 @@ +import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +import { createNotification } from "@app/components/notifications"; +import { ProjectPermissionCan } from "@app/components/permissions"; +import { Button, DeleteActionModal } from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub } from "@app/context"; +import { useDeleteSshHost } from "@app/hooks/api"; +import { usePopUp } from "@app/hooks/usePopUp"; + +import { SshHostModal } from "./SshHostModal"; +import { SshHostsTable } from "./SshHostsTable"; + +export const SshHostsSection = () => { + const { mutateAsync: deleteSshHost } = useDeleteSshHost(); + + const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ + "sshHost", + "deleteSshHost" + ] as const); + + const onRemoveSshHostSubmit = async (sshHostId: string) => { + try { + console.log("pre onRemoveSshHostSubmit"); + const host = await deleteSshHost({ sshHostId }); + console.log("post onRemoveSshHostSubmit deleted host: ", host); + + createNotification({ + text: `Successfully deleted SSH host: ${host.hostname}`, + type: "success" + }); + + handlePopUpClose("deleteSshHost"); + } catch (err) { + console.error(err); + createNotification({ + text: "Failed to delete SSH host", + type: "error" + }); + } + }; + + return ( +
+
+

Hosts

+ + {(isAllowed) => ( + + )} + +
+ + + handlePopUpToggle("deleteSshHost", isOpen)} + deleteKey="confirm" + onDeleteApproved={() => + onRemoveSshHostSubmit((popUp?.deleteSshHost?.data as { sshHostId: string })?.sshHostId) + } + /> +
+ ); +}; diff --git a/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx new file mode 100644 index 000000000..dbd1f9a6a --- /dev/null +++ b/frontend/src/pages/ssh/SshHostsPage/components/SshHostsTable.tsx @@ -0,0 +1,140 @@ +import { faEllipsis, faPencil, faServer, faTrash } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { twMerge } from "tailwind-merge"; + +import { ProjectPermissionCan } from "@app/components/permissions"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + EmptyState, + Table, + TableContainer, + TableSkeleton, + TBody, + Td, + Th, + THead, + Tooltip, + Tr +} from "@app/components/v2"; +import { ProjectPermissionActions, ProjectPermissionSub, useWorkspace } from "@app/context"; +import { useListWorkspaceSshHosts } from "@app/hooks/api"; +import { UsePopUpState } from "@app/hooks/usePopUp"; + +type Props = { + handlePopUpOpen: ( + popUpName: keyof UsePopUpState<["deleteSshHost", "sshHost"]>, + data?: object + ) => void; +}; + +// TODO: something to do with allowed principals + +export const SshHostsTable = ({ handlePopUpOpen }: Props) => { + const { currentWorkspace } = useWorkspace(); + const { data, isPending } = useListWorkspaceSshHosts(currentWorkspace?.id || ""); + + console.log("hosts data: ", data); + + return ( +
+ + + + + + + + + {isPending && } + {!isPending && + data && + data.length > 0 && + data.map((host) => { + return ( + + // navigate({ + // to: `/${ProjectType.SSH}/$projectId/ca/$caId` as const, + // params: { + // projectId: currentWorkspace.id, + // caId: ca.id + // } + // }) + // } + > + + + + ); + })} + +
Hostname +
{host.hostname} + + +
+ + + +
+
+ + + {(isAllowed) => ( + { + e.stopPropagation(); + handlePopUpOpen("sshHost", { + sshHostId: host.id + }); + }} + disabled={!isAllowed} + icon={} + > + Edit SSH host + + )} + + + {(isAllowed) => ( + { + e.stopPropagation(); + handlePopUpOpen("deleteSshHost", { + sshHostId: host.id + }); + }} + disabled={!isAllowed} + icon={} + > + Delete SSH host + + )} + + +
+
+ {!isPending && data?.length === 0 && ( + + )} +
+
+ ); +}; diff --git a/frontend/src/pages/ssh/SshHostsPage/components/index.tsx b/frontend/src/pages/ssh/SshHostsPage/components/index.tsx new file mode 100644 index 000000000..5f82c5ad8 --- /dev/null +++ b/frontend/src/pages/ssh/SshHostsPage/components/index.tsx @@ -0,0 +1 @@ +export { SshHostsSection } from "./SshHostsSection"; diff --git a/frontend/src/pages/ssh/SshHostsPage/route.tsx b/frontend/src/pages/ssh/SshHostsPage/route.tsx new file mode 100644 index 000000000..3b0bbfd79 --- /dev/null +++ b/frontend/src/pages/ssh/SshHostsPage/route.tsx @@ -0,0 +1,9 @@ +import { createFileRoute } from "@tanstack/react-router"; + +import { SshHostsPage } from "./SshHostsPage"; + +export const Route = createFileRoute( + "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts" +)({ + component: SshHostsPage +}); diff --git a/frontend/src/routeTree.gen.ts b/frontend/src/routeTree.gen.ts index ef31a5a33..64f44583b 100644 --- a/frontend/src/routeTree.gen.ts +++ b/frontend/src/routeTree.gen.ts @@ -79,6 +79,7 @@ import { Route as secretManagerIntegrationsRouteAzureAppConfigurationsOauthRedir import { Route as projectAccessControlPageRouteCertManagerImport } from './pages/project/AccessControlPage/route-cert-manager' import { Route as sshSettingsPageRouteImport } from './pages/ssh/SettingsPage/route' import { Route as sshOverviewPageRouteImport } from './pages/ssh/OverviewPage/route' +import { Route as sshSshHostsPageRouteImport } from './pages/ssh/SshHostsPage/route' import { Route as secretManagerSettingsPageRouteImport } from './pages/secret-manager/SettingsPage/route' import { Route as secretManagerSecretRotationPageRouteImport } from './pages/secret-manager/SecretRotationPage/route' import { Route as secretManagerOverviewPageRouteImport } from './pages/secret-manager/OverviewPage/route' @@ -806,6 +807,12 @@ const sshOverviewPageRouteRoute = sshOverviewPageRouteImport.update({ getParentRoute: () => sshLayoutRoute, } as any) +const sshSshHostsPageRouteRoute = sshSshHostsPageRouteImport.update({ + id: '/hosts', + path: '/hosts', + getParentRoute: () => sshLayoutRoute, +} as any) + const secretManagerSettingsPageRouteRoute = secretManagerSettingsPageRouteImport.update({ id: '/settings', @@ -2146,6 +2153,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof secretManagerSettingsPageRouteImport parentRoute: typeof secretManagerLayoutImport } + '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts': { + id: '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts' + path: '/hosts' + fullPath: '/ssh/$projectId/hosts' + preLoaderRoute: typeof sshSshHostsPageRouteImport + parentRoute: typeof sshLayoutImport + } '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview': { id: '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview' path: '/overview' @@ -3463,6 +3477,7 @@ const AuthenticateInjectOrgDetailsOrgLayoutSecretManagerProjectIdRouteWithChildr ) interface sshLayoutRouteChildren { + sshSshHostsPageRouteRoute: typeof sshSshHostsPageRouteRoute sshOverviewPageRouteRoute: typeof sshOverviewPageRouteRoute sshSettingsPageRouteRoute: typeof sshSettingsPageRouteRoute projectAccessControlPageRouteSshRoute: typeof projectAccessControlPageRouteSshRoute @@ -3473,6 +3488,7 @@ interface sshLayoutRouteChildren { } const sshLayoutRouteChildren: sshLayoutRouteChildren = { + sshSshHostsPageRouteRoute: sshSshHostsPageRouteRoute, sshOverviewPageRouteRoute: sshOverviewPageRouteRoute, sshSettingsPageRouteRoute: sshSettingsPageRouteRoute, projectAccessControlPageRouteSshRoute: projectAccessControlPageRouteSshRoute, @@ -3755,6 +3771,7 @@ export interface FileRoutesByFullPath { '/secret-manager/$projectId/overview': typeof secretManagerOverviewPageRouteRoute '/secret-manager/$projectId/secret-rotation': typeof secretManagerSecretRotationPageRouteRoute '/secret-manager/$projectId/settings': typeof secretManagerSettingsPageRouteRoute + '/ssh/$projectId/hosts': typeof sshSshHostsPageRouteRoute '/ssh/$projectId/overview': typeof sshOverviewPageRouteRoute '/ssh/$projectId/settings': typeof sshSettingsPageRouteRoute '/cert-manager/$projectId/access-management': typeof projectAccessControlPageRouteCertManagerRoute @@ -3927,6 +3944,7 @@ export interface FileRoutesByTo { '/secret-manager/$projectId/overview': typeof secretManagerOverviewPageRouteRoute '/secret-manager/$projectId/secret-rotation': typeof secretManagerSecretRotationPageRouteRoute '/secret-manager/$projectId/settings': typeof secretManagerSettingsPageRouteRoute + '/ssh/$projectId/hosts': typeof sshSshHostsPageRouteRoute '/ssh/$projectId/overview': typeof sshOverviewPageRouteRoute '/ssh/$projectId/settings': typeof sshSettingsPageRouteRoute '/cert-manager/$projectId/access-management': typeof projectAccessControlPageRouteCertManagerRoute @@ -4115,6 +4133,7 @@ export interface FileRoutesById { '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/overview': typeof secretManagerOverviewPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/secret-rotation': typeof secretManagerSecretRotationPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/settings': typeof secretManagerSettingsPageRouteRoute + '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts': typeof sshSshHostsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview': typeof sshOverviewPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/settings': typeof sshSettingsPageRouteRoute '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/access-management': typeof projectAccessControlPageRouteCertManagerRoute @@ -4296,6 +4315,7 @@ export interface FileRouteTypes { | '/secret-manager/$projectId/overview' | '/secret-manager/$projectId/secret-rotation' | '/secret-manager/$projectId/settings' + | '/ssh/$projectId/hosts' | '/ssh/$projectId/overview' | '/ssh/$projectId/settings' | '/cert-manager/$projectId/access-management' @@ -4467,6 +4487,7 @@ export interface FileRouteTypes { | '/secret-manager/$projectId/overview' | '/secret-manager/$projectId/secret-rotation' | '/secret-manager/$projectId/settings' + | '/ssh/$projectId/hosts' | '/ssh/$projectId/overview' | '/ssh/$projectId/settings' | '/cert-manager/$projectId/access-management' @@ -4653,6 +4674,7 @@ export interface FileRouteTypes { | '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/overview' | '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/secret-rotation' | '/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout/settings' + | '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts' | '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview' | '/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/settings' | '/_authenticate/_inject-org-details/_org-layout/cert-manager/$projectId/_cert-manager-layout/access-management' @@ -5195,6 +5217,7 @@ export const routeTree = rootRoute "filePath": "ssh/layout.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId", "children": [ + "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts", "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview", "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/settings", "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/access-management", @@ -5244,6 +5267,10 @@ export const routeTree = rootRoute "filePath": "secret-manager/SettingsPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/secret-manager/$projectId/_secret-manager-layout" }, + "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/hosts": { + "filePath": "ssh/SshHostsPage/route.tsx", + "parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout" + }, "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout/overview": { "filePath": "ssh/OverviewPage/route.tsx", "parent": "/_authenticate/_inject-org-details/_org-layout/ssh/$projectId/_ssh-layout" diff --git a/frontend/src/routes.ts b/frontend/src/routes.ts index 19cf8fc62..ecc09e7d2 100644 --- a/frontend/src/routes.ts +++ b/frontend/src/routes.ts @@ -307,6 +307,7 @@ const kmsRoutes = route("/kms/$projectId", [ const sshRoutes = route("/ssh/$projectId", [ layout("ssh-layout", "ssh/layout.tsx", [ route("/overview", "ssh/OverviewPage/route.tsx"), + route("/hosts", "ssh/SshHostsPage/route.tsx"), route("/ca/$caId", "ssh/SshCaByIDPage/route.tsx"), route("/settings", "ssh/SettingsPage/route.tsx"), route("/access-management", "project/AccessControlPage/route-ssh.tsx"),