fix(阶段3): 匿名不可读用户档案,真名改为默认不下发
F1:GET /profiles/:username 只挂了 optionalAuth、handler 内无登录判断,
匿名可读 email、adminType、className、lastLogin。用户名又能经 /rankings/users
公开枚举,等于可以无 cookie 批量收集全校学生的邮箱与最后登录时间。
handler 开头补上未登录即返回空,对齐旧后端 account/views/oj.py 的
UserProfileAPI.get 首行 `if not user.is_authenticated: return self.success()`。
F2:旧后端把「是否下发真名」做成 UsernameSerializer(need_real_name=False)
的默认关闭开关,全仓 11 处调用只有比赛榜单一处显式打开;新后端没搬这一层,
真名随用户对象无条件下发,13 个下发点里 8 个匿名可达。
这里补回同一层:helpers.ts 新增 sampleUser(),realName 默认不下发,
需要的地方显式传 { includeRealName: true }。12 个下发点改为走这个函数,
只有比赛榜单一处打开(对齐 contest/serializers.py:84 的 is_contest_admin)。
没有逐处删字段 —— 那样下次新增端点还会重犯。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,7 +19,7 @@ import { requireAuth, type AppEnv } from "../auth/middleware"
|
||||
import { db, schema } from "../db"
|
||||
import { failure, success } from "../http"
|
||||
import { JudgeStatus } from "../judge/status"
|
||||
import { isSuperAdmin, objectValue, queryInteger } from "./helpers"
|
||||
import { isSuperAdmin, objectValue, queryInteger, sampleUser } from "./helpers"
|
||||
|
||||
export const contentRoutes = new Hono<AppEnv>()
|
||||
|
||||
@@ -40,7 +40,7 @@ contentRoutes.get("/announcements", async (c) => {
|
||||
title: announcement.title,
|
||||
tag: announcement.tag,
|
||||
top: announcement.top,
|
||||
createdBy: { id: user.id, username: user.username, realName },
|
||||
createdBy: sampleUser(user, realName),
|
||||
createTime: announcement.createTime,
|
||||
lastUpdateTime: announcement.lastUpdateTime,
|
||||
})),
|
||||
@@ -61,7 +61,7 @@ contentRoutes.get("/announcements/:id", async (c) => {
|
||||
tag: row.announcement.tag,
|
||||
content: row.announcement.content,
|
||||
top: row.announcement.top,
|
||||
createdBy: { id: row.user.id, username: row.user.username, realName: row.realName },
|
||||
createdBy: sampleUser(row.user, row.realName),
|
||||
createTime: row.announcement.createTime,
|
||||
lastUpdateTime: row.announcement.lastUpdateTime,
|
||||
}))
|
||||
@@ -82,7 +82,7 @@ contentRoutes.get("/messages", requireAuth, async (c) => {
|
||||
return success(c, messageListSchema.parse({
|
||||
results: rows.map(({ message, sender, realName, submission }) => messageSchema.parse({
|
||||
id: message.id,
|
||||
sender: { id: sender.id, username: sender.username, realName },
|
||||
sender: sampleUser(sender, realName),
|
||||
createTime: message.createTime,
|
||||
message: message.message,
|
||||
submission: submissionDetailSchema.parse({
|
||||
@@ -193,7 +193,7 @@ contentRoutes.get("/tutorials/:id", async (c) => {
|
||||
isPublic: row.tutorial.isPublic,
|
||||
order: row.tutorial.order,
|
||||
type: row.tutorial.type,
|
||||
createdBy: { id: row.user.id, username: row.user.username, realName: row.realName },
|
||||
createdBy: sampleUser(row.user, row.realName),
|
||||
createdAt: row.tutorial.createdAt,
|
||||
updatedAt: row.tutorial.updatedAt,
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user