Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3cdc812f2b | ||
|
|
71304f356d | ||
|
|
694c83dc7d |
@@ -28,7 +28,10 @@ class DesktopController extends BaseAdminController
|
|||||||
if (empty($identity) || empty($identity['admin_id'])) {
|
if (empty($identity) || empty($identity['admin_id'])) {
|
||||||
return $this->fail('登录已失效,请重新登录');
|
return $this->fail('登录已失效,请重新登录');
|
||||||
}
|
}
|
||||||
if ((int)($identity['terminal'] ?? 0) !== AdminTerminalEnum::WECOM_RPA) {
|
// 桌面软件(7)和客服后台代登的手机控制台(9)都用这个接口换身份;
|
||||||
|
// 返回真实终端,客服后台据此区分两种会话,桌面令牌不能冒充手机令牌。
|
||||||
|
$terminal = (int)($identity['terminal'] ?? 0);
|
||||||
|
if (!in_array($terminal, AdminTerminalEnum::wecomRpaTerminals(), true)) {
|
||||||
return $this->fail('当前登录令牌不属于企业微信客服桌面端');
|
return $this->fail('当前登录令牌不属于企业微信客服桌面端');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ class DesktopController extends BaseAdminController
|
|||||||
'nickname' => $name !== '' ? $name : $account,
|
'nickname' => $name !== '' ? $name : $account,
|
||||||
'mobile' => '',
|
'mobile' => '',
|
||||||
'avatar' => (string)$admin['avatar'],
|
'avatar' => (string)$admin['avatar'],
|
||||||
'terminal' => AdminTerminalEnum::WECOM_RPA,
|
'terminal' => $terminal,
|
||||||
'expire_time' => (int)($identity['expire_time'] ?? 0),
|
'expire_time' => (int)($identity['expire_time'] ?? 0),
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
'account_type' => 'admin',
|
'account_type' => 'admin',
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class LoginValidate extends BaseValidate
|
|||||||
{
|
{
|
||||||
protected $rule = [
|
protected $rule = [
|
||||||
'terminal' => 'require|in:' . AdminTerminalEnum::PC . ',' . AdminTerminalEnum::MOBILE
|
'terminal' => 'require|in:' . AdminTerminalEnum::PC . ',' . AdminTerminalEnum::MOBILE
|
||||||
. ',' . AdminTerminalEnum::WECOM_RPA,
|
. ',' . AdminTerminalEnum::WECOM_RPA . ',' . AdminTerminalEnum::WECOM_RPA_MOBILE,
|
||||||
'account' => 'require',
|
'account' => 'require',
|
||||||
'password' => 'require|password',
|
'password' => 'require|password',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -27,4 +27,16 @@ class AdminTerminalEnum
|
|||||||
const MOBILE = 2;
|
const MOBILE = 2;
|
||||||
// 企业微信客服桌面软件。与网页端分开保存会话,桌面登录不会顶掉浏览器登录。
|
// 企业微信客服桌面软件。与网页端分开保存会话,桌面登录不会顶掉浏览器登录。
|
||||||
const WECOM_RPA = 7;
|
const WECOM_RPA = 7;
|
||||||
|
// 8 已被 AI 浏览器(app/mcp/service/ConsoleService::TERMINAL)占用,不要复用。
|
||||||
|
// 企业微信客服手机远程控制台。由客服后台服务端代为登录,单独保存会话,
|
||||||
|
// 手机登录既不会顶掉桌面软件(7),也不会和网页后台(1)、AI 浏览器(8)互踢。
|
||||||
|
const WECOM_RPA_MOBILE = 9;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可以调用 /adminapi/desktop/session 换取客服身份的终端。
|
||||||
|
*/
|
||||||
|
public static function wecomRpaTerminals(): array
|
||||||
|
{
|
||||||
|
return [self::WECOM_RPA, self::WECOM_RPA_MOBILE];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,47 @@
|
|||||||
|
-- 企业微信客服助手:手机远程控制台(终端 9)使用的菜单权限。
|
||||||
|
--
|
||||||
|
-- 新增一个不在侧边栏显示的目录“企微客服助手”,下挂四个按钮权限。客服后台只读取
|
||||||
|
-- /adminapi/desktop/session 返回的 permissions 做校验,ZYT 这边没有对应页面。
|
||||||
|
--
|
||||||
|
-- wecom_rpa.remote/control 远程启停本人电脑的监听(建议授予客服)
|
||||||
|
-- wecom_rpa.remote/all 查看并控制所有客服的电脑(仅管理员)
|
||||||
|
-- wecom_rpa.blacklist/add 把会话加入黑名单(建议授予客服)
|
||||||
|
-- wecom_rpa.blacklist/remove 把会话移出黑名单(主管 / 管理员)
|
||||||
|
--
|
||||||
|
-- 默认不授予任何角色:先在“权限管理 → 角色管理”里给 1~2 位客服所在角色勾选试用,
|
||||||
|
-- 再逐步放开。登录手机控制台、查看本人电脑与会话不需要这些权限。
|
||||||
|
-- 注意:拥有“全部菜单”的角色原先返回 '*',新增按钮后会改为返回明细权限(与以往
|
||||||
|
-- 新增菜单的行为一致),需要时在角色里补勾这四项。表前缀如非 zyt_ 请调整。
|
||||||
|
|
||||||
|
START TRANSACTION;
|
||||||
|
|
||||||
|
INSERT INTO `zyt_system_menu`
|
||||||
|
(`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`)
|
||||||
|
SELECT
|
||||||
|
0, 'M', '企微客服助手', 'el-icon-Iphone', 0, '', 'wecom_rpa', '', '', '', 0, 0, 0,
|
||||||
|
UNIX_TIMESTAMP(), UNIX_TIMESTAMP()
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `zyt_system_menu` WHERE `type` = 'M' AND `paths` = 'wecom_rpa'
|
||||||
|
);
|
||||||
|
|
||||||
|
SET @wecom_rpa_menu_id = (
|
||||||
|
SELECT `id` FROM `zyt_system_menu`
|
||||||
|
WHERE `type` = 'M' AND `paths` = 'wecom_rpa'
|
||||||
|
ORDER BY `id` ASC LIMIT 1
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO `zyt_system_menu`
|
||||||
|
(`pid`, `type`, `name`, `icon`, `sort`, `perms`, `paths`, `component`, `selected`, `params`, `is_cache`, `is_show`, `is_disable`, `create_time`, `update_time`)
|
||||||
|
SELECT @wecom_rpa_menu_id, 'A', item.`name`, '', item.`sort`, item.`perms`, '', '', '', '', 0, 1, 0,
|
||||||
|
UNIX_TIMESTAMP(), UNIX_TIMESTAMP()
|
||||||
|
FROM (
|
||||||
|
SELECT '远程启停本人电脑' AS `name`, 40 AS `sort`, 'wecom_rpa.remote/control' AS `perms`
|
||||||
|
UNION ALL SELECT '控制所有客服电脑', 30, 'wecom_rpa.remote/all'
|
||||||
|
UNION ALL SELECT '会话加入黑名单', 20, 'wecom_rpa.blacklist/add'
|
||||||
|
UNION ALL SELECT '会话移出黑名单', 10, 'wecom_rpa.blacklist/remove'
|
||||||
|
) AS item
|
||||||
|
WHERE @wecom_rpa_menu_id IS NOT NULL
|
||||||
|
AND NOT EXISTS (SELECT 1 FROM `zyt_system_menu` AS existing WHERE existing.`perms` = item.`perms`);
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -15,6 +15,15 @@ function adminDesktopExpect(bool $condition, string $message): void
|
|||||||
}
|
}
|
||||||
|
|
||||||
adminDesktopExpect(AdminTerminalEnum::WECOM_RPA === 7, 'desktop admin terminal remains 7');
|
adminDesktopExpect(AdminTerminalEnum::WECOM_RPA === 7, 'desktop admin terminal remains 7');
|
||||||
|
adminDesktopExpect(AdminTerminalEnum::WECOM_RPA_MOBILE === 9, 'mobile console terminal is 9');
|
||||||
|
adminDesktopExpect(
|
||||||
|
AdminTerminalEnum::WECOM_RPA_MOBILE !== \app\mcp\service\ConsoleService::TERMINAL,
|
||||||
|
'mobile console must not share the AI browser console terminal'
|
||||||
|
);
|
||||||
|
adminDesktopExpect(
|
||||||
|
AdminTerminalEnum::wecomRpaTerminals() === [7, 9],
|
||||||
|
'only the desktop and mobile console terminals may read the desktop session'
|
||||||
|
);
|
||||||
|
|
||||||
$validator = file_get_contents(dirname(__DIR__) . '/app/adminapi/validate/LoginValidate.php');
|
$validator = file_get_contents(dirname(__DIR__) . '/app/adminapi/validate/LoginValidate.php');
|
||||||
$controller = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/DesktopController.php');
|
$controller = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/DesktopController.php');
|
||||||
@@ -23,6 +32,10 @@ adminDesktopExpect(
|
|||||||
str_contains($validator, 'AdminTerminalEnum::WECOM_RPA'),
|
str_contains($validator, 'AdminTerminalEnum::WECOM_RPA'),
|
||||||
'admin login accepts the dedicated desktop terminal'
|
'admin login accepts the dedicated desktop terminal'
|
||||||
);
|
);
|
||||||
|
adminDesktopExpect(
|
||||||
|
str_contains($validator, 'AdminTerminalEnum::WECOM_RPA_MOBILE'),
|
||||||
|
'admin login accepts the dedicated mobile console terminal'
|
||||||
|
);
|
||||||
adminDesktopExpect(
|
adminDesktopExpect(
|
||||||
str_contains($controller, "public array \$notNeedLogin = ['session'];")
|
str_contains($controller, "public array \$notNeedLogin = ['session'];")
|
||||||
&& str_contains($controller, "'user_id' => 'admin:'")
|
&& str_contains($controller, "'user_id' => 'admin:'")
|
||||||
|
|||||||
@@ -90,8 +90,16 @@ namespace {
|
|||||||
desktopCheck($invoke($valid)['data']['root'] === true, 'root comes from current account');
|
desktopCheck($invoke($valid)['data']['root'] === true, 'root comes from current account');
|
||||||
Admin::$row['root'] = 0;
|
Admin::$row['root'] = 0;
|
||||||
|
|
||||||
|
desktopCheck($result['data']['terminal'] === 7, 'desktop token reports the desktop terminal');
|
||||||
|
$mobile = $invoke(array_replace($valid, ['terminal' => 9]));
|
||||||
|
desktopCheck($mobile['code'] === 1, 'mobile console token (terminal 9) is accepted');
|
||||||
|
desktopCheck($mobile['data']['terminal'] === 9, 'mobile console token reports terminal 9');
|
||||||
|
desktopCheck($mobile['data']['user_id'] === 'admin:9', 'mobile console maps to the same tenant identity');
|
||||||
|
desktopCheck($invoke(array_replace($valid, ['terminal' => 8]))['code'] === 0, 'AI browser console token rejected');
|
||||||
|
|
||||||
desktopCheck($invoke([])['code'] === 0, 'missing or expired cached identity rejected');
|
desktopCheck($invoke([])['code'] === 0, 'missing or expired cached identity rejected');
|
||||||
desktopCheck($invoke(array_replace($valid, ['terminal' => 1]))['code'] === 0, 'web token rejected');
|
desktopCheck($invoke(array_replace($valid, ['terminal' => 1]))['code'] === 0, 'web token rejected');
|
||||||
|
desktopCheck($invoke(array_replace($valid, ['terminal' => 2]))['code'] === 0, 'admin mobile web token rejected');
|
||||||
Admin::$row['disable'] = 1;
|
Admin::$row['disable'] = 1;
|
||||||
desktopCheck($invoke($valid)['code'] === 0, 'disabled account rejected');
|
desktopCheck($invoke($valid)['code'] === 0, 'disabled account rejected');
|
||||||
Admin::$row = [];
|
Admin::$row = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user