fix: scope SAM sessions to owning user (#354)
This commit is contained in:
parent
b22dc13b51
commit
36bd1cd7de
@ -72,7 +72,10 @@ async function authorizeSamChat(
|
|||||||
} catch {
|
} catch {
|
||||||
return new Response("Unauthorized", { status: 401 });
|
return new Response("Unauthorized", { status: 401 });
|
||||||
}
|
}
|
||||||
const session = await SamSessionRepository.getActiveSession(sessionId);
|
const session = await SamSessionRepository.getActiveSession(
|
||||||
|
sessionId,
|
||||||
|
context.userId,
|
||||||
|
);
|
||||||
const project = session
|
const project = session
|
||||||
? await ProjectRepository.getProjectForOrganization(
|
? await ProjectRepository.getProjectForOrganization(
|
||||||
session.projectId,
|
session.projectId,
|
||||||
|
|||||||
@ -21,7 +21,8 @@ async function createSession(input: CreateSamSessionInput) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Callers must have already authorized the project (requireProjectContext).
|
// Callers must have already authorized the project (requireProjectContext).
|
||||||
async function listSessionsForProject(projectId: string) {
|
// Scoped to userId so a project member only sees their own sessions.
|
||||||
|
async function listSessionsForProject(projectId: string, userId: string) {
|
||||||
return db
|
return db
|
||||||
.select({
|
.select({
|
||||||
id: samSessions.id,
|
id: samSessions.id,
|
||||||
@ -31,7 +32,11 @@ async function listSessionsForProject(projectId: string) {
|
|||||||
})
|
})
|
||||||
.from(samSessions)
|
.from(samSessions)
|
||||||
.where(
|
.where(
|
||||||
and(eq(samSessions.projectId, projectId), isNull(samSessions.archivedAt)),
|
and(
|
||||||
|
eq(samSessions.projectId, projectId),
|
||||||
|
eq(samSessions.userId, userId),
|
||||||
|
isNull(samSessions.archivedAt),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.orderBy(desc(samSessions.updatedAt), desc(samSessions.id));
|
.orderBy(desc(samSessions.updatedAt), desc(samSessions.id));
|
||||||
}
|
}
|
||||||
@ -49,16 +54,23 @@ async function getSessionById(id: string) {
|
|||||||
return row ?? null;
|
return row ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Excludes archived sessions so callers treat them like deleted ones
|
// Look up a caller's own active session by id, scoped to userId so one org
|
||||||
// (connection refused / not archivable) even though the row and DO transcript
|
// member can't act on another's session. Excludes archived sessions so callers
|
||||||
// are kept. Does NOT authorize: callers must check the caller's access to
|
// treat them like deleted ones (connection refused / not archivable) even
|
||||||
|
// though the row and DO transcript are kept. Callers must still check access to
|
||||||
// row.projectId via the canonical project-access path
|
// row.projectId via the canonical project-access path
|
||||||
// (ProjectRepository.getProjectForOrganization) before acting on the session.
|
// (ProjectRepository.getProjectForOrganization) before acting on the session.
|
||||||
async function getActiveSession(id: string) {
|
async function getActiveSession(id: string, userId: string) {
|
||||||
const [row] = await db
|
const [row] = await db
|
||||||
.select()
|
.select()
|
||||||
.from(samSessions)
|
.from(samSessions)
|
||||||
.where(and(eq(samSessions.id, id), isNull(samSessions.archivedAt)))
|
.where(
|
||||||
|
and(
|
||||||
|
eq(samSessions.id, id),
|
||||||
|
eq(samSessions.userId, userId),
|
||||||
|
isNull(samSessions.archivedAt),
|
||||||
|
),
|
||||||
|
)
|
||||||
.limit(1);
|
.limit(1);
|
||||||
return row ?? null;
|
return row ?? null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,10 @@ export const listSamSessions = createServerFn({ method: "GET" })
|
|||||||
.middleware(requireProjectContext)
|
.middleware(requireProjectContext)
|
||||||
.inputValidator((data: unknown) => projectScopedSchema.parse(data))
|
.inputValidator((data: unknown) => projectScopedSchema.parse(data))
|
||||||
.handler(async ({ context }) => {
|
.handler(async ({ context }) => {
|
||||||
return SamSessionRepository.listSessionsForProject(context.projectId);
|
return SamSessionRepository.listSessionsForProject(
|
||||||
|
context.projectId,
|
||||||
|
context.userId,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Creates a new SAM chat session and returns its id; the client then opens a DO
|
// Creates a new SAM chat session and returns its id; the client then opens a DO
|
||||||
@ -47,7 +50,10 @@ export const archiveSamSession = createServerFn({ method: "POST" })
|
|||||||
.handler(async ({ data, context }) => {
|
.handler(async ({ data, context }) => {
|
||||||
// Authorize against the session's project (the canonical project-access
|
// Authorize against the session's project (the canonical project-access
|
||||||
// path), not the caller's org directly.
|
// path), not the caller's org directly.
|
||||||
const session = await SamSessionRepository.getActiveSession(data.sessionId);
|
const session = await SamSessionRepository.getActiveSession(
|
||||||
|
data.sessionId,
|
||||||
|
context.userId,
|
||||||
|
);
|
||||||
const project = session
|
const project = session
|
||||||
? await ProjectRepository.getProjectForOrganization(
|
? await ProjectRepository.getProjectForOrganization(
|
||||||
session.projectId,
|
session.projectId,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user