fix: restrict user email visibility
CI / lint (pull_request) Successful in 32s
CI / format (pull_request) Successful in 33s
CI / install-and-build (pull_request) Successful in 1m9s
CI / typecheck-tests (pull_request) Successful in 28s
CI / typecheck-source (pull_request) Successful in 28s
CI / test (pull_request) Successful in 39s

Co-Authored-By: gpt-5.6-sol <noreply@openai.com>
This commit is contained in:
2026-07-23 18:49:49 +00:00
co-authored by Codex
parent d7e31dfce9
commit a1992eb57a
4 changed files with 232 additions and 5 deletions
+45 -4
View File
@@ -15,11 +15,20 @@ import {
updateuser,
} from "../schema/validators.js";
import { authenticate } from "./middleware/authenticate.js";
import { InvalidAbodeError, NotFoundAbodeError } from "../db/types/errors.js";
import {
InvalidAbodeError,
NotAuthorizedAbodeError,
NotFoundAbodeError,
} from "../db/types/errors.js";
import { isExportable } from "../db/types/ExportImport.js";
import type { ExportFilter, ExportKind } from "../db/types/ExportImport.js";
import { intersectExportFilters, isExportKind } from "../db/export/filter.js";
import { computeForcedExportFilter } from "./exportScope.js";
import {
hasGlobalUserVisibility,
hideUserEmail,
userForCaller,
} from "./userVisibility.js";
function parseExportFilter(query: Record<string, unknown>): ExportFilter {
const list = (v: unknown): string[] | undefined => {
@@ -92,17 +101,32 @@ export function apirouter(db: BackendDbInterface): KoaRouter {
router.use("/users", authenticate(db));
router.get("/users", async (ctx) => {
ctx.body = await db.listUsers();
const users = await db.listUsers();
ctx.body = users.map((user) =>
userForCaller(user, { user: ctx.user!, session: ctx.session! }),
);
});
router.post("/users", jsonBody({ validate: createuser }), async (ctx) => {
ctx.body = await db.createUser(ctx.request.body);
});
router.get("/users/by-email", async (ctx) => {
if (typeof ctx.query.email !== "string") throw new InvalidAbodeError();
if (
!hasGlobalUserVisibility({
user: ctx.user!,
session: ctx.session!,
})
) {
throw new NotAuthorizedAbodeError();
}
ctx.body = await db.getUserByEmail(ctx.query.email);
});
router.get("/users/:uid", async (ctx) => {
ctx.body = await db.getUserById(ctx.params.uid);
const user = await db.getUserById(ctx.params.uid);
ctx.body = userForCaller(user, {
user: ctx.user!,
session: ctx.session!,
});
});
router.patch(
"/users/:uid",
@@ -176,7 +200,24 @@ export function apirouter(db: BackendDbInterface): KoaRouter {
ctx.body = await db.listResidentsByAbodeId(ctx.params.aid);
});
router.get("/abodes/:aid/users", async (ctx) => {
ctx.body = await db.listUsersByAbodeId(ctx.params.aid);
const users = await db.listUsersByAbodeId(ctx.params.aid);
const globalVisibility = hasGlobalUserVisibility({
user: ctx.user!,
session: ctx.session!,
});
const residents = globalVisibility
? []
: await db.listResidentsByAbodeId(ctx.params.aid);
const abodeAdmin = residents.some(
(resident) =>
resident.uid === ctx.user!.uid && resident.flags.admin === true,
);
ctx.body =
globalVisibility || abodeAdmin
? users
: users.map((user) =>
user.uid === ctx.user!.uid ? user : hideUserEmail(user),
);
});
router.get("/abodes/:aid/notes", async (ctx) => {
ctx.body = await db.listNotesByAbodeId(ctx.params.aid);