MediaWiki extension that lets people sign in to a wiki with their OTTOhub account, shows their OTTOhub identity on their profile page, and can push wiki notifications to their OTTOhub direct messages.
给 OTTOWiki(
wiki.ottohub.cn)写的扩展:用 OTTOhub 账号登录、在个人资料页展示 OTTOhub 身份、 把站内通知推成 OTTOhub 私信。代码里不包含任何凭据;发件账号的凭据放在站点根之外的 600 文件里。
| 阶段 | 内容 |
|---|---|
| 1 · 统一登录 | 用 OTTOhub 用户名/邮箱 + 口令登录。首次登录自动建号;若 OTTOhub 用户名与站内已有账号同名,走认领流程(要求输入该站内账号的原口令,防止把别人的账号错认给他人)。保留本地口令登录(老用户不受影响),可在绑定后选择停用本地口令。 |
| 2 · 个人资料联动 | 在用户页右栏显示 OTTOhub 头像/用户名/UID/简介。服务端只存 hub uid,展示数据由浏览器直接调 api.ottohub.cn(改名/换头像自动跟随,零存储);用户可在偏好里隐藏该区块。 |
| 3 · 通知推送 | 通过 Echo 增加一个 ottohub 投递通道,把站内通知推成 OTTOhub 私信(POST /api/im/messages)。默认对所有用户关闭,需用户自己在「通知」里逐类别勾选。 |
| 3+ · 审核结果 | 监听核心 PageSaveComplete,借 Moderation 公开的 ApproveHook::isApprovingNow() 判定"这次保存=一次审批通过",给原作者造一条 Echo 事件「你的编辑已通过审核」。不需要给 Moderation 打补丁。 |
登录页会把两组凭据做成标签页(站内账号 / OTTOhub 账号),并在表单下方附登录常见问题; 没有 JavaScript 时退化成两组字段并排显示,功能不受影响。
- MediaWiki >= 1.46(用到了
AuthManager、CsrfTokenSet、MediaWiki\JobQueue\Job等 1.46 的写法) - PHP 需带
curl扩展 - 阶段 3 需要 Echo(
wfLoadExtension( 'Echo' )) - 可选联动:Moderation(审核结果通知)、CommentStreams / SocialProfile(白名单里的通知类别)
零 composer 依赖。
cd /path/to/wiki/extensions
git clone https://github.com/OTTOWiki/OttohubAuth.gitLocalSettings.php:
wfLoadExtension( 'OttohubAuth' );
$wgOttohubAuth_ApiBase = 'https://api.ottohub.cn';
$wgOttohubAuth_Timeout = 8;
$wgOttohubAuth_Throttle = [ 'count' => 5, 'window' => 600 ];
// SSO 首次登录要自动建号。若站点关闭了本地注册($wgGroupPermissions['*']['createaccount'] = false),
// 匿名用户会连 autocreateaccount 一起失去,必须补这一行,否则报 badaccess-group0。
// 它**不会**重新开放本地注册(原生建号页用的仍是 createaccount)。
$wgGroupPermissions['*']['autocreateaccount'] = true;建表:
php maintenance/run.php OttohubAuth:schema| 变量 | 默认 | 说明 |
|---|---|---|
$wgOttohubAuth_ApiBase |
https://api.ottohub.cn |
上游 API 根 |
$wgOttohubAuth_Timeout |
8 |
出网超时(秒) |
$wgOttohubAuth_Throttle |
['count' => 5, 'window' => 600] |
登录/认领共用的节流(IP + 账号) |
$wgOttohubAuth_NotifyEnable |
true |
阶段 3 总开关;false 立刻停止一切推送 |
$wgOttohubAuth_NotifyEvents |
[] |
事件级白名单;留空=由下面的类别可用性决定 |
$wgOttohubAuth_NotifyRateLimit |
['count' => 10, 'window' => 3600] |
每个收件人每窗口最多推几条;count <= 0 不限 |
$wgOttohubAuth_SenderAccount |
'' |
发件账号凭据文件的路径(见下)。留空=不推送 |
$wgOttohubAuth_ExemptEmailConfirmToEdit |
true |
让已绑定 OTTOhub 的账号不受 $wgEmailConfirmToEdit 阻挡(见下方「编辑门槛」) |
- 不能写进
extension.json—— MediaWiki 1.46 禁止扩展清单声明别的扩展(这里指 Echo) 已经声明过的配置键,会直接抛RuntimeException导致全站 500;merge_strategy不豁免。 - Echo 的键名不都带
Echo前缀:EchoNotifiers/EchoNotificationCategories/EchoNotifications有;DefaultNotifyTypeAvailability/NotifyTypeAvailabilityByCategory没有。写错不会报错,只会静默失效(通道永远不触发)。
$wgEchoNotifiers['ottohub'] = 'MediaWiki\\Extension\\OttohubAuth\\OttohubNotifier::notify';
$wgDefaultNotifyTypeAvailability['ottohub'] = false; // 默认对所有类别关闭
$wgNotifyTypeAvailabilityByCategory['mention']['ottohub'] = true;
$wgNotifyTypeAvailabilityByCategory['commentstreams-notification-category']['ottohub'] = true;
$wgNotifyTypeAvailabilityByCategory['edit-user-talk']['ottohub'] = true;
$wgNotifyTypeAvailabilityByCategory['social-msg']['ottohub'] = true;
$wgNotifyTypeAvailabilityByCategory['ottohubauth-moderation']['ottohub'] = true;
$wgEchoNotificationCategories['ottohubauth-moderation'] = [
'priority' => 3,
'tooltip' => 'echo-pref-tooltip-ottohubauth-moderation',
];
$wgEchoNotifications['ottohubauth-moderation-approved'] = [
'category' => 'ottohubauth-moderation',
'section' => 'message',
'group' => 'positive',
'presentation-model' => 'MediaWiki\\Extension\\OttohubAuth\\ModerationApprovedPresentationModel',
];放在站点根之外、Web 服务器不可达的位置,权限 600、属主与 PHP-FPM 用户一致:
{ "uid_email": "<OTTOhub 用户名或邮箱>", "pw": "<OTTOhub 口令>" }$wgOttohubAuth_SenderAccount = '/path/outside/webroot/ottohub-sender.json';凭据只在服务端读取;登录换来的 token 缓存在对象缓存(TTL 30 分钟), 凭据与 token 都不写日志、不进 URL(POST 的 token 放 JSON body)。
本站这类 wiki 常开 $wgEmailConfirmToEdit = true,而 User::isEmailConfirmed() 要求邮箱
格式合法且已确认。OTTOhub 建号的用户本地没有口令,身份由 OTTOhub 认证;如果 OTTOhub
没给出合法邮箱(或本地邮箱从未确认),他们会一个页面都不能编辑。
本扩展默认($wgOttohubAuth_ExemptEmailConfirmToEdit = true)把已绑定 OTTOhub 的账号
视为邮箱已确认,从而免于该门槛 —— 由 EmailConfirmed 钩子实现(PermissionManager 对 edit
的邮箱检查是写死的,没有权限项、也没有能移除它的 hook,这是唯一的口子)。
不想这样就把该项设为 false,行为恢复原样。
email 字段不保证是合法邮箱(实测出现过没有 @ 的串),
所以写入前必须 Sanitizer::validateEmail() 校验 —— 否则会出现"邮箱看起来已确认、
却因为格式非法永远不能编辑"的账号。
| 用途 | 方法 | 路径 | 参数 | 响应 |
|---|---|---|---|---|
| 登录换 token | POST | /api/auth/login |
{"uid_email": <用户名或邮箱>, "pw": <口令>} |
平坦 {"status":"success","uid","token",…} |
| 取本人身份 | GET | /api/profile?token=… |
token 只能放 query | {"status":"success","data":{"uid","username","email"}} |
| 公开用户信息 | GET | /api/user/{uid} |
— | {"status":"success","data":{…}}(前端直接用) |
| 发私信 | POST | /api/im/messages |
{"token","receiver","message"}(token 进 body) |
{"status":"success"}(只有 status) |
| 删除私信 | DELETE | /api/im/messages/{msg_id} |
body {"token"} |
{"status":"success"} |
- 私信正文上限 222 个字符(按字符,不是字节;超出报
too_long_message)。 - 错误码:
missing_argument/error_receiver/error_token(401)/too_long_message/too_short_message/blocked/system_error。 receiver必须是 OTTOhub 侧 uid。本扩展里它只能来自映射表OttohubAccountStore::getHubUidByLocalUser()—— 传错等于把通知发给陌生人。
单表 ottohub_accounts:oa_hub_uid(主键,OTTOhub 侧 uid)、oa_user_id(唯一键,站内 user_id)、
oa_username、oa_linked_by、oa_linked_at。表结构见 sql/mysql/table_ottohub_accounts.sql,
建表用 OttohubAuth:schema(幂等,只碰这一张表)。
php maintenance/run.php OttohubAuth:schema # 建表(幂等)
php maintenance/run.php OttohubAuth:probe # 只读探针:注册/服务/权限/provider/解析用例
php maintenance/run.php OttohubAuth:acceptance # 验收检查(含事务回滚的映射表约束测试)
php maintenance/run.php OttohubAuth:accounts # 列出/查询/解绑映射(CLI 兜底)
php maintenance/run.php OttohubAuth:notify # 阶段 3 总览(--verify / --user= / --send= / --queue)
php maintenance/run.php OttohubAuth:debugrl --module=<名> # ResourceLoader 异常原文
php maintenance/run.php OttohubAuth:renderpage --title=<页面> # CLI 里按真实用户渲染页面抓异常- 收件人 hub uid 只来自映射表;自己触发的事件不推给自己;未绑定 OTTOhub 的用户直接跳过。
- 阶段 3 默认全员不勾选(Echo 只给 web/email 设默认值),不主动勾选就永远收不到推送。
- 每个收件人每窗口限流(默认 10 条/小时),失败按 30/60 秒退避重试,最多 3 次;永久性错误直接丢弃。
- 通知入队/发送过程中的任何异常都被吞掉并记日志 —— 通知失败绝不影响触发它的编辑/评论。
- 密码重置按"当前有无本地口令"封闭:没有本地口令的账号走不了
Special:PasswordReset, 页面会明确提示改用 OTTOhub。
本扩展是为 OTTOWiki 的具体环境写的(Echo、Moderation、CommentStreams、SocialProfile、Citizen 皮肤),
其中的类别白名单、ottohubauth-moderation 事件与通知文案都带着那个站点的取舍。
搬到别的 wiki 上用之前,请按自己的通知类别调整 LocalSettings 里的接线与 i18n/ 文案。
GPL-2.0-or-later,见 LICENSE。