From f13d5ac05ae036175a32efaf7f99fe25dfc4ce4c Mon Sep 17 00:00:00 2001 From: Raghuram Banda Date: Thu, 21 May 2026 11:49:51 -0400 Subject: [PATCH] fix: add ownership checks to promote and delete agent routes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - On draft→review promote: verify createdBy matches caller for non-admins, preventing users from submitting other people's drafts for review - Add DELETE /agents/:agentId route that only allows deletion of draft agents, with ownership verification for non-admin callers - Add deleteAgentConfig to frontend AugmentApi interface and implementation - Add createdBy field to ChatAgentConfig (prerequisite for ownership checks) - Add agent.delete audit action type - Update report.api.md files Part of Epic #3208 --- .../augment-backend/src/routes/agentRoutes.ts | 61 +++++++++++++++++++ .../src/services/AuditLogger.ts | 1 + .../plugins/augment-common/report.api.md | 2 + .../augment-common/src/types/shared.ts | 2 + .../augment/plugins/augment/report.api.md | 1 + .../plugins/augment/src/api/AugmentApi.ts | 11 ++++ 6 files changed, 78 insertions(+) diff --git a/workspaces/augment/plugins/augment-backend/src/routes/agentRoutes.ts b/workspaces/augment/plugins/augment-backend/src/routes/agentRoutes.ts index 9a831d9037..cfa6ab03e2 100644 --- a/workspaces/augment/plugins/augment-backend/src/routes/agentRoutes.ts +++ b/workspaces/augment/plugins/augment-backend/src/routes/agentRoutes.ts @@ -299,6 +299,18 @@ export function registerAgentRoutes( }); return; } + } else { + const isAdmin = await ctx.checkIsAdmin(req); + if ( + !isAdmin && + existing?.createdBy && + existing.createdBy !== userRef + ) { + res.status(403).json({ + error: 'You can only submit your own agents for review.', + }); + return; + } } const now = new Date().toISOString(); @@ -430,6 +442,55 @@ export function registerAgentRoutes( ), ); + // --------------------------------------------------------------------------- + // DELETE /agents/:agentId -- remove a draft agent's config entry + // Non-admin callers can only delete agents they created. + // --------------------------------------------------------------------------- + router.delete( + '/agents/:agentId', + withRoute( + 'DELETE /agents/:agentId', + 'Failed to delete agent', + async (req, res) => { + const agentId = decodeURIComponent(req.params.agentId); + const userRef = await ctx.getUserRef(req); + const configs = await loadChatAgentConfigs(); + const existing = configs.find(c => c.agentId === agentId); + + if (!existing) { + throw new InputError(`Agent "${agentId}" not found in config.`); + } + + const stage = normalizeLifecycleStage(existing.lifecycleStage); + if (stage !== 'draft') { + throw new InputError( + `Only draft agents can be deleted. Agent "${agentId}" is in stage "${stage}".`, + ); + } + + const isAdmin = await ctx.checkIsAdmin(req); + if (!isAdmin && existing.createdBy && existing.createdBy !== userRef) { + res.status(403).json({ + error: 'You can only delete your own agents.', + }); + return; + } + + const updated = configs.filter(c => c.agentId !== agentId); + await saveChatAgentConfigs(updated, userRef); + audit.log({ + action: 'agent.delete', + actor: userRef, + target: agentId, + outcome: 'success', + sourceIp: AuditLogger.extractIp(req), + }); + logger.info(`Draft agent "${agentId}" deleted by ${userRef}`); + res.json({ success: true, agentId }); + }, + ), + ); + // --------------------------------------------------------------------------- // PUT /agents/:agentId/publish -- shortcut: promote to production // --------------------------------------------------------------------------- diff --git a/workspaces/augment/plugins/augment-backend/src/services/AuditLogger.ts b/workspaces/augment/plugins/augment-backend/src/services/AuditLogger.ts index 84789f9753..9fa8612967 100644 --- a/workspaces/augment/plugins/augment-backend/src/services/AuditLogger.ts +++ b/workspaces/augment/plugins/augment-backend/src/services/AuditLogger.ts @@ -25,6 +25,7 @@ export type AuditAction = | 'tool.approval' | 'tool.lifecycle' | 'agent.lifecycle' + | 'agent.delete' | 'document.upload' | 'document.delete' | 'admin.login'; diff --git a/workspaces/augment/plugins/augment-common/report.api.md b/workspaces/augment/plugins/augment-common/report.api.md index 217aed3085..7e4417c8c5 100644 --- a/workspaces/augment/plugins/augment-common/report.api.md +++ b/workspaces/augment/plugins/augment-common/report.api.md @@ -257,6 +257,7 @@ export interface ChatAgent { agentRole?: AgentRole; avatarUrl?: string; createdAt?: string; + createdBy?: string; description?: string; framework?: string; id: string; @@ -281,6 +282,7 @@ export interface ChatAgentConfig { agentId: string; avatarUrl?: string; conversationStarters?: string[]; + createdBy?: string; description?: string; displayName?: string; featured: boolean; diff --git a/workspaces/augment/plugins/augment-common/src/types/shared.ts b/workspaces/augment/plugins/augment-common/src/types/shared.ts index 44d83f6334..a6080da334 100644 --- a/workspaces/augment/plugins/augment-common/src/types/shared.ts +++ b/workspaces/augment/plugins/augment-common/src/types/shared.ts @@ -404,6 +404,8 @@ export interface ChatAgentConfig { greeting?: string; /** Suggested prompts shown on the agent card and below the input */ conversationStarters?: string[]; + /** User ref of who originally created this agent config entry */ + createdBy?: string; } /** diff --git a/workspaces/augment/plugins/augment/report.api.md b/workspaces/augment/plugins/augment/report.api.md index d342dcd378..cf45306e53 100644 --- a/workspaces/augment/plugins/augment/report.api.md +++ b/workspaces/augment/plugins/augment/report.api.md @@ -162,6 +162,7 @@ export interface AugmentApi { createVectorStore( config?: Record, ): Promise; + deleteAgentConfig(agentId: string): Promise; deleteAdminConfig(key: AdminConfigKey): Promise<{ deleted: boolean; }>; diff --git a/workspaces/augment/plugins/augment/src/api/AugmentApi.ts b/workspaces/augment/plugins/augment/src/api/AugmentApi.ts index e79f9af0c2..3d6b027875 100644 --- a/workspaces/augment/plugins/augment/src/api/AugmentApi.ts +++ b/workspaces/augment/plugins/augment/src/api/AugmentApi.ts @@ -114,6 +114,11 @@ export interface AugmentApi { >, ): Promise; + /** + * Delete a draft agent's configuration entry. + */ + deleteAgentConfig(agentId: string): Promise; + /** * List tools with lifecycle overlay in a provider-agnostic format. */ @@ -807,6 +812,12 @@ export class AugmentApiClient implements AugmentApi { }); } + async deleteAgentConfig(agentId: string): Promise { + await this.fetchJson(`/agents/${encodeURIComponent(agentId)}`, { + method: 'DELETE', + }); + } + async listToolsWithLifecycle(options?: { published?: boolean }): Promise< (import('@red-hat-developer-hub/backstage-plugin-augment-common').KagentiToolSummary & { published?: boolean;