mirror of
https://github.com/Portabase/portabase.git
synced 2026-07-14 11:16:13 +02:00
fix(database): restrict db delete to agent-owning org (system agents admin-only)
- UI: delete button gated by canDeleteDatabases prop threaded from each page (org page: only org-created agents; admin page: system agents) - authz: org path requires agent.organizationId === active org; drop join-based attribution so a system agent merely linked to an org is admin-delete-only
This commit is contained in:
@@ -23,9 +23,10 @@ import {HealthcheckLog} from "@/db/schema/15_healthcheck-log";
|
||||
type AgentContentPageProps = {
|
||||
edgeKey: string;
|
||||
agent: AgentWithDatabases
|
||||
canDeleteDatabases?: boolean
|
||||
}
|
||||
|
||||
export const AgentContentPage = ({edgeKey, agent: initialAgent}: AgentContentPageProps) => {
|
||||
export const AgentContentPage = ({edgeKey, agent: initialAgent, canDeleteDatabases = false}: AgentContentPageProps) => {
|
||||
|
||||
const {data} = useQuery({
|
||||
queryKey: ["agent-data", initialAgent.id],
|
||||
@@ -121,6 +122,7 @@ export const AgentContentPage = ({edgeKey, agent: initialAgent}: AgentContentPag
|
||||
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
|
||||
)}
|
||||
cardItem={AgentDatabaseCard}
|
||||
canDeleteDatabases={canDeleteDatabases}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import {Database} from "@/db/schema/07_database";
|
||||
import {DatabaseCard} from "@/components/common/database-card";
|
||||
import {DatabaseDeleteButton} from "@/features/agents/components/database-delete-button";
|
||||
import { Database } from "@/db/schema/07_database";
|
||||
import { DatabaseCard } from "@/components/common/database-card";
|
||||
import { DatabaseDeleteButton } from "@/features/agents/components/database-delete-button";
|
||||
|
||||
export type agentDatabaseCardProps = {
|
||||
export type AgentDatabaseCardProps = {
|
||||
data: Database;
|
||||
canDeleteDatabases?: boolean;
|
||||
};
|
||||
|
||||
export const AgentDatabaseCard = (props: agentDatabaseCardProps) => {
|
||||
const {data: database} = props;
|
||||
export const AgentDatabaseCard = (props: AgentDatabaseCardProps) => {
|
||||
const { data: database, canDeleteDatabases = false } = props;
|
||||
|
||||
return (
|
||||
<DatabaseCard
|
||||
withDetails={false}
|
||||
data={database}
|
||||
deleteButton={
|
||||
database.agentId ? (
|
||||
<DatabaseDeleteButton databaseId={database.id} agentId={database.agentId}/>
|
||||
canDeleteDatabases && database.agentId ? (
|
||||
<DatabaseDeleteButton
|
||||
databaseId={database.id}
|
||||
agentId={database.agentId}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
};
|
||||
@@ -35,11 +35,7 @@ async function assertCanDeleteDatabase(databaseId: string): Promise<void> {
|
||||
const database = await db.query.database.findFirst({
|
||||
where: eq(drizzleDb.schemas.database.id, databaseId),
|
||||
with: {
|
||||
agent: {
|
||||
with: {
|
||||
organizations: true,
|
||||
},
|
||||
},
|
||||
agent: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -64,10 +60,11 @@ async function assertCanDeleteDatabase(databaseId: string): Promise<void> {
|
||||
const canManage = activeMember
|
||||
? computeOrganizationPermissions(activeMember).canManageAgents
|
||||
: false;
|
||||
// Only the organization that CREATED the agent may delete its databases.
|
||||
// A system agent merely attributed to an org via the join table is
|
||||
// handled by the isAdmin branch above (agent.organizationId === null).
|
||||
const hasAccess =
|
||||
!!organization &&
|
||||
(agent.organizationId === organization.id ||
|
||||
agent.organizations.some((o) => o.organizationId === organization.id));
|
||||
!!organization && agent.organizationId === organization.id;
|
||||
authorized = canManage && hasAccess;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user