This commit is contained in:
gr
2026-09-09 10:48:05 +08:00
parent 9d91ff90ae
commit c1f7287331
2 changed files with 121 additions and 1 deletions
@@ -5,9 +5,11 @@
namespace app\adminapi\controller;
use app\adminapi\logic\auth\AuthLogic;
use app\common\enum\AdminTerminalEnum;
use app\common\enum\YesNoEnum;
use app\common\model\auth\Admin;
use app\common\model\auth\AdminRole;
/**
* 用最小身份信息把 ZYT 管理员账号映射到客服软件租户。
@@ -31,12 +33,23 @@ class DesktopController extends BaseAdminController
}
$admin = Admin::where(['id' => (int)$identity['admin_id']])
->field('id,account,name,avatar,disable')
->field('id,account,name,avatar,disable,root')
->findOrEmpty();
if ($admin->isEmpty() || (int)$admin['disable'] === YesNoEnum::YES) {
return $this->fail('管理员不存在或已禁用');
}
// 身份和权限在专用接口一次返回,客服后台无需跨 IP 调用网页端 mySelf。
$roleIds = array_values(array_unique(array_map('intval',
AdminRole::where('admin_id', (int)$admin['id'])->column('role_id')
)));
sort($roleIds);
$permissions = AuthLogic::getBtnAuthByRoleId([
'id' => (int)$admin['id'],
'root' => (int)$admin['root'],
'role_id' => $roleIds,
]);
$account = trim((string)$admin['account']);
$name = trim((string)$admin['name']);
return $this->data([
@@ -50,6 +63,9 @@ class DesktopController extends BaseAdminController
'expire_time' => (int)($identity['expire_time'] ?? 0),
'status' => 'active',
'account_type' => 'admin',
'root' => (int)$admin['root'] === YesNoEnum::YES,
'role_ids' => $roleIds,
'permissions' => array_values($permissions),
]);
}
}
@@ -0,0 +1,104 @@
<?php
declare(strict_types=1);
namespace app\common\model\auth {
// 所有账号/角色数据在内存中,测试不连接业务数据库。
class Admin extends \ArrayObject
{
public static array $row = [];
public static function where(array $filter): self
{
if ($filter !== ['id' => 9]) { throw new \RuntimeException('wrong identity lookup'); }
return new self(self::$row);
}
public function field(string $fields): self { return $this; }
public function findOrEmpty(): self { return $this; }
public function isEmpty(): bool { return $this->count() === 0; }
}
class AdminRole
{
public static function where(string $field, int $id): self
{
if ($field !== 'admin_id' || $id !== 9) { throw new \RuntimeException('wrong role lookup'); }
return new self();
}
public function column(string $field): array { return ['3', '2', '3']; }
}
}
namespace app\adminapi\logic\auth {
class AuthLogic
{
public static array $permissions = ['tcm.diagnosis/lists'];
public static function getBtnAuthByRoleId(array $admin): array
{
if ($admin['id'] !== 9 || $admin['role_id'] !== [2, 3]) {
throw new \RuntimeException('wrong permission identity');
}
return $admin['root'] ? ['*'] : self::$permissions;
}
}
}
namespace {
require dirname(__DIR__) . '/vendor/autoload.php';
require dirname(__DIR__) . '/vendor/topthink/framework/src/helper.php';
use app\adminapi\controller\DesktopController;
use app\adminapi\http\middleware\AuthMiddleware;
use app\adminapi\logic\auth\AuthLogic;
use app\common\model\auth\Admin;
$app = new think\App();
final class DesktopSessionFixture extends DesktopController
{
public function __construct(think\Request $request) { $this->request = $request; }
protected function data($data) { return ['code' => 1, 'data' => $data]; }
protected function fail(string $msg = 'fail', array $data = [], int $code = 0, int $show = 1)
{
return ['code' => $code, 'msg' => $msg];
}
}
function desktopCheck(bool $ok, string $message): void
{
if (!$ok) { throw new RuntimeException($message); }
}
$valid = ['admin_id' => 9, 'terminal' => 7, 'expire_time' => time() + 3600,
'root' => 0, 'login_ip' => '198.51.100.10'];
$invoke = function (array $identity, string $action = 'session') use ($app) {
$request = (new think\Request())->withServer(['REMOTE_ADDR' => '203.0.113.20']);
$request->setAction($action);
$request->setController('Desktop');
$request->adminInfo = $identity;
$controller = new DesktopSessionFixture($request);
$request->controllerObject = $controller;
$app->instance('request', $request);
return (new AuthMiddleware())->handle($request, static fn() => $controller->session());
};
Admin::$row = ['id' => 9, 'account' => 'doctor9', 'name' => '医助',
'avatar' => '', 'disable' => 0, 'root' => 0];
$result = $invoke($valid);
desktopCheck($result['code'] === 1, 'valid desktop session works across different IPs');
desktopCheck($result['data']['user_id'] === 'admin:9', 'account is namespaced');
desktopCheck($result['data']['role_ids'] === [2, 3], 'current roles are normalized');
desktopCheck($result['data']['permissions'] === ['tcm.diagnosis/lists'], 'current permissions returned');
desktopCheck($result['data']['root'] === false, 'ordinary account stays ordinary');
desktopCheck(!isset($result['data']['token']) && !isset($result['data']['login_ip']), 'minimal response');
AuthLogic::$permissions = [];
desktopCheck($invoke($valid)['data']['permissions'] === [], 'empty permissions remain empty');
Admin::$row['root'] = 1;
desktopCheck($invoke($valid)['data']['permissions'] === ['*'], 'current root privilege returned');
desktopCheck($invoke($valid)['data']['root'] === true, 'root comes from current account');
Admin::$row['root'] = 0;
desktopCheck($invoke([])['code'] === 0, 'missing or expired cached identity rejected');
desktopCheck($invoke(array_replace($valid, ['terminal' => 1]))['code'] === 0, 'web token rejected');
Admin::$row['disable'] = 1;
desktopCheck($invoke($valid)['code'] === 0, 'disabled account rejected');
Admin::$row = [];
desktopCheck($invoke($valid)['code'] === 0, 'deleted account rejected');
$webResult = $invoke($valid, 'mySelf')->getData();
desktopCheck($webResult['code'] === -1 && str_contains($webResult['msg'], 'ip地址'),
'ordinary web requests retain IP validation');
echo "AdminDesktopSessionBehaviorTest passed\n";
}