Add add/remove/list hosts in ssh host groups functionality

This commit is contained in:
Tuan Dang
2025-04-29 23:31:57 -07:00
parent a9a16c9bd1
commit b06eeb0d40
26 changed files with 820 additions and 376 deletions
+102 -113
View File
@@ -1,7 +1,9 @@
import { z } from "zod";
import { sanitizedSshHost, loginMappingSchema } from "@app/ee/services/ssh-host/ssh-host-schema";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { loginMappingSchema, sanitizedSshHost } from "@app/ee/services/ssh-host/ssh-host-schema";
import { sanitizedSshHostGroup } from "@app/ee/services/ssh-host-group/ssh-host-group-schema";
import { EHostGroupMembershipFilter } from "@app/ee/services/ssh-host-group/ssh-host-group-types";
import { SSH_HOST_GROUPS } from "@app/lib/api-docs";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { slugSchema } from "@app/server/lib/schemas";
@@ -35,17 +37,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.GET_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHostGroup.projectId,
event: {
type: EventType.GET_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
name: sshHostGroup.name
}
}
});
return sshHostGroup;
}
@@ -80,24 +82,18 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
// TODO: audit logs
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.CREATE_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname,
// alias: host.alias ?? null,
// userCertTtl: host.userCertTtl,
// hostCertTtl: host.hostCertTtl,
// loginMappings: host.loginMappings,
// userSshCaId: host.userSshCaId,
// hostSshCaId: host.hostSshCaId
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHostGroup.projectId,
event: {
type: EventType.CREATE_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
name: sshHostGroup.name,
loginMappings: sshHostGroup.loginMappings
}
}
});
return sshHostGroup;
}
@@ -135,23 +131,18 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.UPDATE_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname,
// alias: host.alias,
// userCertTtl: host.userCertTtl,
// hostCertTtl: host.hostCertTtl,
// loginMappings: host.loginMappings,
// userSshCaId: host.userSshCaId,
// hostSshCaId: host.hostSshCaId
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHostGroup.projectId,
event: {
type: EventType.UPDATE_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
name: sshHostGroup.name,
loginMappings: sshHostGroup.loginMappings
}
}
});
return sshHostGroup;
}
@@ -183,18 +174,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
// TODO: audit log
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.DELETE_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHostGroup.projectId,
event: {
type: EventType.DELETE_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
name: sshHostGroup.name
}
}
});
return sshHostGroup;
}
@@ -211,20 +201,30 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
sshHostGroupId: z.string().describe(SSH_HOST_GROUPS.GET.sshHostGroupId)
}),
querystring: z.object({
offset: z.coerce.number().min(0).max(100).default(0).describe(SSH_HOST_GROUPS.LIST_HOSTS.offset),
limit: z.coerce.number().min(1).max(100).default(10).describe(SSH_HOST_GROUPS.LIST_HOSTS.limit)
filter: z.nativeEnum(EHostGroupMembershipFilter).optional().describe(SSH_HOST_GROUPS.GET.filter)
}),
response: {
200: z.object({
hosts: z.array(sanitizedSshHost),
hosts: sanitizedSshHost
.pick({
id: true,
hostname: true,
alias: true
})
.merge(
z.object({
isPartOfGroup: z.boolean(),
joinedGroupAt: z.date().nullable()
})
)
.array(),
totalCount: z.number()
})
}
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
console.log("list hosts in group pre");
const { hosts, totalCount } = await server.services.sshHostGroup.listSshHostGroupHosts({
const { sshHostGroup, hosts, totalCount } = await server.services.sshHostGroup.listSshHostGroupHosts({
sshHostGroupId: req.params.sshHostGroupId,
actor: req.permission.type,
actorId: req.permission.id,
@@ -232,20 +232,17 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId,
...req.query
});
console.log("list hosts in group post");
// TODO: audit logs
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.GET_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname
// }
// }
// });
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHostGroup.projectId,
event: {
type: EventType.GET_SSH_HOST_GROUP_HOSTS,
metadata: {
sshHostGroupId: req.params.sshHostGroupId
}
}
});
return { hosts, totalCount };
}
@@ -270,9 +267,7 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
console.log("add host to group pre");
const host = await server.services.sshHostGroup.addHostToSshHostGroup({
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.addHostToSshHostGroup({
sshHostGroupId: req.params.sshHostGroupId,
hostId: req.params.hostId,
actor: req.permission.type,
@@ -281,22 +276,20 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
console.log("add host to group post");
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHost.projectId,
event: {
type: EventType.ADD_HOST_TO_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
sshHostId: sshHost.id,
hostname: sshHost.hostname
}
}
});
// TODO: audit logs
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.GET_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname
// }
// }
// });
return host;
return sshHost;
}
});
@@ -319,9 +312,7 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
},
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
console.log("remove host from group pre");
const host = await server.services.sshHostGroup.removeHostFromSshHostGroup({
const { sshHostGroup, sshHost } = await server.services.sshHostGroup.removeHostFromSshHostGroup({
sshHostGroupId: req.params.sshHostGroupId,
hostId: req.params.hostId,
actor: req.permission.type,
@@ -330,22 +321,20 @@ export const registerSshHostGroupRouter = async (server: FastifyZodProvider) =>
actorOrgId: req.permission.orgId
});
console.log("remove host from group post");
await server.services.auditLog.createAuditLog({
...req.auditLogInfo,
projectId: sshHost.projectId,
event: {
type: EventType.REMOVE_HOST_FROM_SSH_HOST_GROUP,
metadata: {
sshHostGroupId: sshHostGroup.id,
sshHostId: sshHost.id,
hostname: sshHost.hostname
}
}
});
// TODO: audit logs
// await server.services.auditLog.createAuditLog({
// ...req.auditLogInfo,
// projectId: host.projectId,
// event: {
// type: EventType.GET_SSH_HOST,
// metadata: {
// sshHostId: host.id,
// hostname: host.hostname
// }
// }
// });
return host;
return sshHost;
}
});
};
@@ -12,6 +12,7 @@ import {
import { SshCaStatus, SshCertType } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { SshCertTemplateStatus } from "@app/ee/services/ssh-certificate-template/ssh-certificate-template-types";
import { TLoginMapping } from "@app/ee/services/ssh-host/ssh-host-types";
import { SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
import { AsymmetricKeyAlgorithm, SigningAlgorithm } from "@app/lib/crypto/sign/types";
import { TProjectPermission } from "@app/lib/types";
@@ -191,12 +192,19 @@ export enum EventType {
UPDATE_SSH_CERTIFICATE_TEMPLATE = "update-ssh-certificate-template",
DELETE_SSH_CERTIFICATE_TEMPLATE = "delete-ssh-certificate-template",
GET_SSH_CERTIFICATE_TEMPLATE = "get-ssh-certificate-template",
GET_SSH_HOST = "get-ssh-host",
CREATE_SSH_HOST = "create-ssh-host",
UPDATE_SSH_HOST = "update-ssh-host",
DELETE_SSH_HOST = "delete-ssh-host",
GET_SSH_HOST = "get-ssh-host",
ISSUE_SSH_HOST_USER_CERT = "issue-ssh-host-user-cert",
ISSUE_SSH_HOST_HOST_CERT = "issue-ssh-host-host-cert",
GET_SSH_HOST_GROUP = "get-ssh-host-group",
CREATE_SSH_HOST_GROUP = "create-ssh-host-group",
UPDATE_SSH_HOST_GROUP = "update-ssh-host-group",
DELETE_SSH_HOST_GROUP = "delete-ssh-host-group",
GET_SSH_HOST_GROUP_HOSTS = "get-ssh-host-group-hosts",
ADD_HOST_TO_SSH_HOST_GROUP = "add-host-to-ssh-host-group",
REMOVE_HOST_FROM_SSH_HOST_GROUP = "remove-host-from-ssh-host-group",
CREATE_CA = "create-certificate-authority",
GET_CA = "get-certificate-authority",
UPDATE_CA = "update-certificate-authority",
@@ -1499,12 +1507,7 @@ interface CreateSshHost {
alias: string | null;
userCertTtl: string;
hostCertTtl: string;
loginMappings: {
loginUser: string;
allowedPrincipals: {
usernames: string[];
};
}[];
loginMappings: TLoginMapping[];
userSshCaId: string;
hostSshCaId: string;
};
@@ -1518,12 +1521,7 @@ interface UpdateSshHost {
alias?: string | null;
userCertTtl?: string;
hostCertTtl?: string;
loginMappings?: {
loginUser: string;
allowedPrincipals: {
usernames: string[];
};
}[];
loginMappings?: TLoginMapping[];
userSshCaId?: string;
hostSshCaId?: string;
};
@@ -1567,6 +1565,65 @@ interface IssueSshHostHostCert {
};
}
interface GetSshHostGroupEvent {
type: EventType.GET_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
name: string;
};
}
interface CreateSshHostGroupEvent {
type: EventType.CREATE_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
name: string;
loginMappings: TLoginMapping[];
};
}
interface UpdateSshHostGroupEvent {
type: EventType.UPDATE_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
name?: string;
loginMappings?: TLoginMapping[];
};
}
interface DeleteSshHostGroupEvent {
type: EventType.DELETE_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
name: string;
};
}
interface GetSshHostGroupHostsEvent {
type: EventType.GET_SSH_HOST_GROUP_HOSTS;
metadata: {
sshHostGroupId: string;
};
}
interface AddHostToSshHostGroupEvent {
type: EventType.ADD_HOST_TO_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
sshHostId: string;
hostname: string;
};
}
interface RemoveHostFromSshHostGroupEvent {
type: EventType.REMOVE_HOST_FROM_SSH_HOST_GROUP;
metadata: {
sshHostGroupId: string;
sshHostId: string;
hostname: string;
};
}
interface CreateCa {
type: EventType.CREATE_CA;
metadata: {
@@ -2753,6 +2810,13 @@ export type Event =
| CreateAppConnectionEvent
| UpdateAppConnectionEvent
| DeleteAppConnectionEvent
| GetSshHostGroupEvent
| CreateSshHostGroupEvent
| UpdateSshHostGroupEvent
| DeleteSshHostGroupEvent
| GetSshHostGroupHostsEvent
| AddHostToSshHostGroupEvent
| RemoveHostFromSshHostGroupEvent
| CreateSharedSecretEvent
| DeleteSharedSecretEvent
| ReadSharedSecretEvent
@@ -6,6 +6,8 @@ import { DatabaseError } from "@app/lib/errors";
import { groupBy, unique } from "@app/lib/fn";
import { ormify } from "@app/lib/knex";
import { EHostGroupMembershipFilter } from "./ssh-host-group-types";
export type TSshHostGroupDALFactory = ReturnType<typeof sshHostGroupDALFactory>;
export const sshHostGroupDALFactory = (db: TDbClient) => {
@@ -13,6 +15,7 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
const findSshHostGroupsWithLoginMappings = async (projectId: string, tx?: Knex) => {
try {
// First, get all the SSH host groups with their login mappings
const rows = await (tx || db.replicaNode())(TableName.SshHostGroup)
.leftJoin(
TableName.SshHostLoginUser,
@@ -38,6 +41,25 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
const hostsGrouped = groupBy(rows, (r) => r.sshHostGroupId);
const hostGroupIds = Object.keys(hostsGrouped);
type HostCountRow = {
sshHostGroupId: string;
host_count: string;
};
const hostCountsQuery = (await (tx ||
db
.replicaNode()(TableName.SshHostGroupMembership)
.select(`${TableName.SshHostGroupMembership}.sshHostGroupId`, db.raw(`count(*) as host_count`))
.whereIn(`${TableName.SshHostGroupMembership}.sshHostGroupId`, hostGroupIds)
.groupBy(`${TableName.SshHostGroupMembership}.sshHostGroupId`))) as HostCountRow[];
const hostCountsMap = hostCountsQuery.reduce<Record<string, number>>((acc, { sshHostGroupId, host_count }) => {
acc[sshHostGroupId] = Number(host_count);
return acc;
}, {});
return Object.values(hostsGrouped).map((hostRows) => {
const { sshHostGroupId, name } = hostRows[0];
const loginMappingGrouped = groupBy(
@@ -54,7 +76,8 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
id: sshHostGroupId,
projectId,
name,
loginMappings
loginMappings,
hostCount: hostCountsMap[sshHostGroupId] ?? 0
};
});
} catch (error) {
@@ -116,21 +139,40 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
const findAllSshHostsInGroup = async ({
sshHostGroupId,
offset = 0,
limit
limit,
filter
}: {
sshHostGroupId: string;
offset?: number;
limit?: number;
filter?: EHostGroupMembershipFilter;
}) => {
try {
const sshHostGroup = await db
.replicaNode()(TableName.SshHostGroup)
.where(`${TableName.SshHostGroup}.id`, sshHostGroupId)
.select("projectId")
.first();
if (!sshHostGroup) {
throw new Error(`SSH host group with ID ${sshHostGroupId} not found`);
}
const query = db
.replicaNode()(TableName.SshHostGroupMembership)
.where(`${TableName.SshHostGroupMembership}.sshHostGroupId`, sshHostGroupId)
.join(TableName.SshHost, `${TableName.SshHostGroupMembership}.sshHostId`, `${TableName.SshHost}.id`)
.replicaNode()(TableName.SshHost)
.where(`${TableName.SshHost}.projectId`, sshHostGroup.projectId)
.leftJoin(TableName.SshHostGroupMembership, (bd) => {
bd.on(`${TableName.SshHostGroupMembership}.sshHostId`, "=", `${TableName.SshHost}.id`).andOn(
`${TableName.SshHostGroupMembership}.sshHostGroupId`,
"=",
db.raw("?", [sshHostGroupId])
);
})
.select(
db.ref("id").withSchema(TableName.SshHost),
db.ref("hostname").withSchema(TableName.SshHost),
db.ref("alias").withSchema(TableName.SshHost),
db.ref("sshHostGroupId").withSchema(TableName.SshHostGroupMembership),
db.ref("createdAt").withSchema(TableName.SshHostGroupMembership).as("joinedGroupAt"),
db.raw(`count(*) OVER() as total_count`)
)
@@ -141,13 +183,28 @@ export const sshHostGroupDALFactory = (db: TDbClient) => {
void query.limit(limit);
}
if (filter) {
switch (filter) {
case EHostGroupMembershipFilter.GROUP_MEMBERS:
void query.andWhere(`${TableName.SshHostGroupMembership}.createdAt`, "is not", null);
break;
case EHostGroupMembershipFilter.NON_GROUP_MEMBERS:
void query.andWhere(`${TableName.SshHostGroupMembership}.createdAt`, "is", null);
break;
default:
break;
}
}
const hosts = await query;
return {
hosts: hosts.map(({ id, hostname, alias }) => ({
hosts: hosts.map(({ id, hostname, alias, sshHostGroupId: memberGroupId, joinedGroupAt }) => ({
id,
hostname,
alias
alias,
isPartOfGroup: !!memberGroupId,
joinedGroupAt
})),
// @ts-expect-error col select is raw and not strongly typed
totalCount: Number(hosts?.[0]?.total_count ?? 0)
@@ -226,7 +226,8 @@ export const sshHostGroupServiceFactory = ({
actor,
actorId,
actorAuthMethod,
actorOrgId
actorOrgId,
filter
}: TListSshHostGroupHostsDTO) => {
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
@@ -242,10 +243,8 @@ export const sshHostGroupServiceFactory = ({
ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.SshHostGroups);
// TODO: check
const { hosts, totalCount } = await sshHostGroupDAL.findAllSshHostsInGroup({ sshHostGroupId });
console.log("hosts: ", hosts);
return { hosts, totalCount };
const { hosts, totalCount } = await sshHostGroupDAL.findAllSshHostsInGroup({ sshHostGroupId, filter });
return { sshHostGroup, hosts, totalCount };
};
const addHostToSshHostGroup = async ({
@@ -259,14 +258,14 @@ export const sshHostGroupServiceFactory = ({
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
if (!host) {
const sshHost = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
if (!sshHost) {
throw new NotFoundError({
message: `SSH host with ID ${hostId} not found`
});
}
if (sshHostGroup.projectId !== host.projectId) {
if (sshHostGroup.projectId !== sshHost.projectId) {
throw new NotFoundError({
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
});
@@ -286,7 +285,7 @@ export const sshHostGroupServiceFactory = ({
await sshHostGroupMembershipDAL.create({ sshHostGroupId, sshHostId: hostId });
return host;
return { sshHostGroup, sshHost };
};
const removeHostFromSshHostGroup = async ({
@@ -297,17 +296,21 @@ export const sshHostGroupServiceFactory = ({
actorAuthMethod,
actorOrgId
}: TRemoveHostFromSshHostGroupDTO) => {
console.log("removeHostFromSshHostGroup args: ", {
sshHostGroupId,
hostId
});
const sshHostGroup = await sshHostGroupDAL.findSshHostGroupByIdWithLoginMappings(sshHostGroupId);
if (!sshHostGroup) throw new NotFoundError({ message: `SSH host group with ID '${sshHostGroupId}' not found` });
const host = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
if (!host) {
const sshHost = await sshHostDAL.findSshHostByIdWithLoginMappings(hostId);
if (!sshHost) {
throw new NotFoundError({
message: `SSH host with ID ${hostId} not found`
});
}
if (sshHostGroup.projectId !== host.projectId) {
if (sshHostGroup.projectId !== sshHost.projectId) {
throw new NotFoundError({
message: `SSH host with ID ${hostId} not found in project ${sshHostGroup.projectId}`
});
@@ -336,9 +339,13 @@ export const sshHostGroupServiceFactory = ({
});
}
console.log("boom: ", {
sshHostGroupMembership
});
await sshHostGroupMembershipDAL.deleteById(sshHostGroupMembership.id);
return host;
return { sshHostGroup, sshHost };
};
return {
@@ -1,13 +1,9 @@
import { TLoginMapping } from "@app/ee/services/ssh-host/ssh-host-types";
import { TGenericPermission, TProjectPermission } from "@app/lib/types";
export type TCreateSshHostGroupDTO = {
name: string;
loginMappings: {
loginUser: string;
allowedPrincipals: {
usernames: string[];
};
}[];
loginMappings: TLoginMapping[];
} & TProjectPermission;
export type TUpdateSshHostGroupDTO = {
@@ -30,6 +26,7 @@ export type TDeleteSshHostGroupDTO = {
} & TGenericPermission;
export type TListSshHostGroupHostsDTO = {
sshHostGroupId: string;
filter?: EHostGroupMembershipFilter;
} & TGenericPermission;
export type TAddHostToSshHostGroupDTO = {
@@ -41,3 +38,8 @@ export type TRemoveHostFromSshHostGroupDTO = {
sshHostGroupId: string;
hostId: string;
} & TGenericPermission;
export enum EHostGroupMembershipFilter {
GROUP_MEMBERS = "group-members",
NON_GROUP_MEMBERS = "non-group-members"
}
@@ -9,7 +9,7 @@ import { TUserDALFactory } from "@app/services/user/user-dal";
export type TListSshHostsDTO = Omit<TProjectPermission, "projectId">;
type LoginMapping = {
export type TLoginMapping = {
loginUser: string;
allowedPrincipals: {
usernames: string[];
@@ -21,7 +21,7 @@ export type TCreateSshHostDTO = {
alias?: string;
userCertTtl: string;
hostCertTtl: string;
loginMappings: LoginMapping[];
loginMappings: TLoginMapping[];
userSshCaId?: string;
hostSshCaId?: string;
} & TProjectPermission;
@@ -32,7 +32,7 @@ export type TUpdateSshHostDTO = {
alias?: string;
userCertTtl?: string;
hostCertTtl?: string;
loginMappings?: LoginMapping[];
loginMappings?: TLoginMapping[];
} & Omit<TProjectPermission, "projectId">;
export type TGetSshHostDTO = {
@@ -54,7 +54,7 @@ export type TIssueSshHostHostCertDTO = {
} & Omit<TProjectPermission, "projectId">;
type BaseCreateSshLoginMappingsDTO = {
loginMappings: LoginMapping[];
loginMappings: TLoginMapping[];
sshHostLoginUserDAL: Pick<TSshHostLoginUserDALFactory, "create" | "transaction">;
sshHostLoginUserMappingDAL: Pick<TSshHostLoginUserMappingDALFactory, "insertMany">;
userDAL: Pick<TUserDALFactory, "find">;
+2 -1
View File
@@ -1387,7 +1387,8 @@ export const SSH_CERTIFICATE_TEMPLATES = {
export const SSH_HOST_GROUPS = {
GET: {
sshHostGroupId: "The ID of the SSH host group to get."
sshHostGroupId: "The ID of the SSH host group to get.",
filter: "The filter to apply to the SSH hosts in the SSH host group."
},
CREATE: {
projectId: "The ID of the project to create the SSH host group in.",
@@ -666,7 +666,8 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => {
200: z.object({
groups: z.array(
sanitizedSshHostGroup.extend({
loginMappings: z.array(loginMappingSchema)
loginMappings: z.array(loginMappingSchema),
hostCount: z.number()
})
)
})