restrict v4

This commit is contained in:
tamakyi
2026-10-18 21:50:58 +08:00
parent efb19606a7
commit 52f0712eda
2 changed files with 35 additions and 2 deletions
+26
View File
@@ -187,3 +187,29 @@ L2/L3 的手测断言(比看 flash 可靠):
- L2 后 `select is_private, answer from questions where id=<qid>` → `t` 且 answer 非空,公开页不显示; - L2 后 `select is_private, answer from questions where id=<qid>` → `t` 且 answer 非空,公开页不显示;
- L3 后 `answer` 仍为空串(未被写入),`is_read` 不变。 - L3 后 `answer` 仍为空串(未被写入),`is_read` 不变。
- 干扰项:回答会走七牛审核,审核不可用会 fail-closed 报「审核服务不可用」,那不是处置问题。 - 干扰项:回答会走七牛审核,审核不可用会 fail-closed 报「审核服务不可用」,那不是处置问题。
### L2 线上实测:功能生效,但「箱主无感知」被误判成没效果
用户报「L2 没效果」。我用匿名视角实测(这是我能独立做的部分):
- 公开页 `/_/admin6` 只出现 73,**不含 88**;
- 匿名访问 `/_/admin6/88` → **302 到首页**;对比 `/_/admin6/73` → 200。
结论:L2 私下里是生效的。用户误判的两个原因:
1. 他**以箱主身份**看单题页 —— 箱主本来就能看自己的私密内容,页面当然「一切正常」;
2. **我的 bug**:`question.flash.answeredPrivate` 只写进了 4 个 i18n 字典,**代码里从未使用**,
成功后仍提示「回答发布成功」。已修:L2 分支改用 `forcedPrivate` 布尔切换 flash 文案。
教训:这些 i18n 键写完必须 grep 调用点;「加了文案」不等于「接上了逻辑」。
### 用测试账号自测(已跑通登录态全链路,不需要用户点击)
用户提供测试箱主 `admin6`(密码不落盘)。用 python + urllib 跑通:
`GET /login` 取 `_csrf` → `GET /api/captcha/generate` 取 `{id, question}`(形如 `5 × 5 =`,
注意乘号是 `×` 不是 `*`)→ 算答案后提交 `g-recaptcha-response="<id>:<答案>"` → 登录成功;
再 `GET /_/admin6` 取表单 csrf → `POST /_/admin6`(字段 `content`)发提问 →
`POST /_/admin6/{id}/answer`(字段 `answer`)。
实测结论(qid=89):
- **L2 服务端生效**:回答保存成功,但该题变私密 —— 匿名访问 302 到首页,公开页只有 73;
- **flash 仍是旧文案**「回答发布成功」→ 服务器跑的是 21:22 部署的二进制,我 21:45 的 flash 修复未部署。
- 88(用户手测)与 89(我自检)都是测试数据,均已被 L2 强制转私密,测完需在后台清理或保持私密。
+8 -1
View File
@@ -193,7 +193,8 @@ func PublishAnswer(ctx context.Context, pageUser *db.User, question *db.Question
// 顺序不能提前到审核之前 —— 审核失败(违规或审核源不可用,fail-closed)会直接返回, // 顺序不能提前到审核之前 —— 审核失败(违规或审核源不可用,fail-closed)会直接返回,
// 那时题目已经被改成私密,等于箱主一次失败的编辑让原有公开内容凭空消失。 // 那时题目已经被改成私密,等于箱主一次失败的编辑让原有公开内容凭空消失。
// 放在这里仍然满足「任何时刻都不出现公开 + 已作答」:此刻它还是一条未作答的私密题。 // 放在这里仍然满足「任何时刻都不出现公开 + 已作答」:此刻它还是一条未作答的私密题。
if db.RestrictionAtLeast(restrictionLevel, db.RestrictionL2) { forcedPrivate := db.RestrictionAtLeast(restrictionLevel, db.RestrictionL2)
if forcedPrivate {
if err := db.Questions.SetPrivate(ctx.Request().Context(), question.ID); err != nil { if err := db.Questions.SetPrivate(ctx.Request().Context(), question.ID); err != nil {
logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to force question private") logrus.WithContext(ctx.Request().Context()).WithError(err).Error("Failed to force question private")
ctx.SetInternalError() ctx.SetInternalError()
@@ -245,7 +246,13 @@ func PublishAnswer(ctx context.Context, pageUser *db.User, question *db.Question
} }
}) })
// L2 下必须明确告知「已转为私密」:单题页对箱主本人仍是 200、内容照常显示,
// 若继续提示「回答发布成功」,箱主会以为一切照旧(实测就是这样误判的)。
if forcedPrivate {
ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "question.flash.answeredPrivate"))
} else {
ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "question.flash.answered")) ctx.SetSuccessFlash(i18n.T(ctx.Lang(), "question.flash.answered"))
}
ctx.Redirect(fmt.Sprintf("/_/%s/%d", pageUser.Domain, question.ID)) ctx.Redirect(fmt.Sprintf("/_/%s/%d", pageUser.Domain, question.ID))
} }