fix(permissions): mask apiKey on ai.one, add audit logging, remove dead endpoint

This commit is contained in:
Yash Kumar
2026-09-21 01:18:28 +05:30
parent bdefc02e77
commit a7d8378f52
3 changed files with 54 additions and 14 deletions
+42 -13
View File
@@ -15,6 +15,8 @@ import {
getAiSettingById,
getAiSettingsByOrganizationId,
getCustomAiProviders,
maskAiApiKey,
mergeAiApiKey,
saveAiSettings,
saveCustomAiProviders,
suggestVariants,
@@ -41,16 +43,18 @@ import {
withAnyPermission,
withPermission,
} from "@/server/api/trpc";
import { audit } from "@/server/api/utils/audit";
import { generatePassword } from "@/templates/utils";
export const aiRouter = createTRPCRouter({
one: withAnyPermission("ai", ["read", "update"])
.input(z.object({ aiId: z.string() }))
.query(async ({ input, ctx }) => {
return await getAiSettingById(
const setting = await getAiSettingById(
input.aiId,
ctx.session.activeOrganizationId,
);
return maskAiApiKey(setting);
}),
getModels: withAnyPermission("ai", ["read", "create", "update"])
@@ -184,13 +188,39 @@ export const aiRouter = createTRPCRouter({
create: withPermission("ai", "create")
.input(apiCreateAi)
.mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.session.activeOrganizationId, input);
const result = await saveAiSettings(
ctx.session.activeOrganizationId,
input,
);
await audit(ctx, {
action: "create",
resourceType: "ai",
resourceName: input.name,
});
return result;
}),
update: withPermission("ai", "update")
.input(apiUpdateAi)
.mutation(async ({ ctx, input }) => {
return await saveAiSettings(ctx.session.activeOrganizationId, input);
if (input.apiKey) {
const existing = await getAiSettingById(
input.aiId,
ctx.session.activeOrganizationId,
);
input.apiKey = mergeAiApiKey(input.apiKey, existing.apiKey);
}
const result = await saveAiSettings(
ctx.session.activeOrganizationId,
input,
);
await audit(ctx, {
action: "update",
resourceType: "ai",
resourceId: input.aiId,
resourceName: input.name,
});
return result;
}),
getAll: withAnyPermission("ai", ["read", "create", "update", "delete"]).query(
@@ -202,22 +232,21 @@ export const aiRouter = createTRPCRouter({
},
),
get: withPermission("ai", "read")
.input(z.object({ aiId: z.string() }))
.query(async ({ input, ctx }) => {
return await getAiSettingById(
input.aiId,
ctx.session.activeOrganizationId,
);
}),
delete: withPermission("ai", "delete")
.input(z.object({ aiId: z.string() }))
.mutation(async ({ input, ctx }) => {
return await deleteAiSettings(
const setting = await getAiSettingById(
input.aiId,
ctx.session.activeOrganizationId,
);
await deleteAiSettings(input.aiId, ctx.session.activeOrganizationId);
await audit(ctx, {
action: "delete",
resourceType: "ai",
resourceId: input.aiId,
resourceName: setting.name,
});
return true;
}),
getCustomProviders: withAnyPermission("ai", [
+2 -1
View File
@@ -95,4 +95,5 @@ export type AuditResourceType =
| "network"
| "vaultProvider"
| "dnsProvider"
| "telemetryProvider";
| "telemetryProvider"
| "ai";
+10
View File
@@ -10,6 +10,16 @@ import { IS_CLOUD } from "../constants";
import { findServerById } from "./server";
import { getWebServerSettings } from "./web-server-settings";
export const AI_SECRET_MASK = "********";
export const maskAiApiKey = <T extends { apiKey: string }>(settings: T): T => ({
...settings,
apiKey: AI_SECRET_MASK,
});
export const mergeAiApiKey = (incoming: string, existing: string): string =>
incoming === AI_SECRET_MASK ? existing : incoming;
interface SuggestionItem {
id: string;
name: string;