群主转让加一步密码二次确认,跟账号注销/改密码等其它高风险操作保持一致(第6章复测发现)
serveIMGroupOwnerTransfer 核实过:只认 {target_user_id, version},没有任何
action_token/二次验证机制——不是文档滞后,是这个操作本身就没有服务端强制的二次
确认。但转让群主同样不可逆、后果同样重大("确定后你将立即失去群主身份"),本项目
对同一类场景已有明确先例:personal-settings-view.jsx 的 DeleteAccountConfirm 就是
纯前端加的密码输入门槛,不指望服务端校验它,只是让"手滑点确定"变成需要多打一遍
密码才会发生的事。
OwnerSelect 原来点目标人选后只有一层"确定/取消"的确认表,点确定直接调用真实转让
接口。现在拆成两步:第一步维持原有确认文案不变,第二步新增密码输入,留空点确定
会被拦下(不发请求),填了任意内容才真正调用 transferGroupOwner。
真实验证(真实群+真实候选人):空密码点确定确认没有发出请求;填入密码后
transferGroupOwner 真实调用成功,正确跳转回群组管理页。
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
542aca3dc2
commit
7728afe79f
+41
-4
@@ -12097,6 +12097,8 @@ function OwnerSelect({
|
||||
const [members, setMembers] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(null);
|
||||
const [query, setQuery] = react__WEBPACK_IMPORTED_MODULE_0___default().useState('');
|
||||
const [target, setTarget] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(null);
|
||||
const [passwordStep, setPasswordStep] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
|
||||
const [password, setPassword] = react__WEBPACK_IMPORTED_MODULE_0___default().useState('');
|
||||
const [busy, setBusy] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
|
||||
const [loadError, setLoadError] = react__WEBPACK_IMPORTED_MODULE_0___default().useState(false);
|
||||
react__WEBPACK_IMPORTED_MODULE_0___default().useEffect(() => {
|
||||
@@ -12111,7 +12113,16 @@ function OwnerSelect({
|
||||
const visible = (members || []).filter(m => (m.group_nickname || m.display_name || '').includes(query));
|
||||
const admins = visible.filter(m => m.role === 'admin');
|
||||
const others = visible.filter(m => m.role !== 'admin');
|
||||
const reset = () => {
|
||||
setTarget(null);
|
||||
setPasswordStep(false);
|
||||
setPassword('');
|
||||
};
|
||||
const confirm = () => {
|
||||
if (!password) {
|
||||
onToast('请输入密码以确认');
|
||||
return;
|
||||
}
|
||||
if (busy || !target) return;
|
||||
setBusy(true);
|
||||
(0,_lib_im_api_js__WEBPACK_IMPORTED_MODULE_4__.transferGroupOwner)(topic, target.user_id).then(() => {
|
||||
@@ -12159,7 +12170,7 @@ function OwnerSelect({
|
||||
}, void 0, false), others.map(row)]
|
||||
}, void 0, true) : null]
|
||||
}, void 0, true)
|
||||
}, void 0, false), target ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
}, void 0, false), target && !passwordStep ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
className: "group-sheet-backdrop",
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("section", {
|
||||
className: "group-confirm-sheet",
|
||||
@@ -12170,12 +12181,38 @@ function OwnerSelect({
|
||||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
className: "danger",
|
||||
disabled: busy,
|
||||
onClick: confirm,
|
||||
onClick: () => setPasswordStep(true),
|
||||
children: "确定"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
onClick: () => setTarget(null),
|
||||
onClick: reset,
|
||||
children: "取消"
|
||||
}, void 0, false)]
|
||||
}, void 0, true)
|
||||
}, void 0, false) : null, target && passwordStep ? (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("div", {
|
||||
className: "group-sheet-backdrop",
|
||||
children: (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("section", {
|
||||
className: "group-confirm-sheet",
|
||||
children: [(0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("h2", {
|
||||
children: "输入密码确认转让"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("p", {
|
||||
children: ["请输入登录密码以确认将群主转让给", target.group_nickname || target.display_name, "(该操作立即生效,不可撤销)。"]
|
||||
}, void 0, true), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("input", {
|
||||
type: "password",
|
||||
autoFocus: true,
|
||||
className: "group-password-input",
|
||||
value: password,
|
||||
onChange: e => setPassword(e.target.value),
|
||||
placeholder: "登录密码"
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
className: "danger",
|
||||
disabled: busy,
|
||||
onClick: confirm,
|
||||
children: busy ? '处理中…' : '确定'
|
||||
}, void 0, false), (0,react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxDEV)("button", {
|
||||
type: "button",
|
||||
onClick: reset,
|
||||
children: "取消"
|
||||
}, void 0, false)]
|
||||
}, void 0, true)
|
||||
|
||||
Reference in New Issue
Block a user