mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-14 14:34:09 +00:00
Add add/remove/list hosts in ssh host groups functionality
This commit is contained in:
@@ -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">;
|
||||
|
||||
@@ -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()
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user