Files
YwxAppThink/addon/mqttbroker/command/TestAcl.php
T

95 lines
4.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | YwxApp [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2026-2036 http://ywxapp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: ywxapp<admin@ywxapp.cn>
// +----------------------------------------------------------------------
namespace addon\mqttbroker\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\facade\Db;
use ywxapp\service\AddonService;
use addon\mqttbroker\service\Auth;
/**
* ACL 实网决策验证:用真实 DB 规则校验 Auth::checkAcl 的放行/拒绝逻辑。
* php think mqttbroker:acltest
* 验证项:deny 发布拒绝、默认放行、用户定向拒绝、超管绕过、$SYS 只读。
* 不依赖运行中的 Broker,直接复用 Broker 实际调用的 Auth 逻辑。
*/
class TestAcl extends Command
{
protected function configure()
{
$this->setName('mqttbroker:acltest')
->setDescription('验证 ACL 发布/订阅权限决策(含 deny 拒绝)');
}
protected function execute(Input $input, Output $output)
{
// 配置:开启 ACL,默认放行
$saved = [];
try { $saved = AddonService::config('mqttbroker') ?: []; } catch (\Throwable $e) {}
$config = array_merge($saved, ['acl_enabled' => '1', 'acl_default' => 'allow']);
$auth = new Auth($config);
$auth->ensureTables();
// 插入临时规则(测试后清理)
$rows = [
['target_type' => 'member', 'target' => 'alice', 'topic' => 'alice/secret', 'access' => 2, 'allow' => 0, 'sort' => 5, 'remark' => 'tmp'],
['target_type' => 'all', 'target' => '', 'topic' => 'secret/#', 'access' => 2, 'allow' => 0, 'sort' => 10, 'remark' => 'tmp'],
['target_type' => 'all', 'target' => '', 'topic' => 'public/#', 'access' => 3, 'allow' => 1, 'sort' => 20, 'remark' => 'tmp'],
];
$ids = [];
foreach ($rows as $r) {
$ids[] = Db::name('mqttbroker_acl')->insertGetId(array_merge($r, ['create_at' => time()]));
}
$results = [];
$chk = function (string $case, bool $got, bool $expect) use (&$results) {
$results[$case] = ['got' => $got, 'expect' => $expect, 'ok' => $got === $expect];
};
// 1) deny 发布 secret/# 被拒
$chk('deny publish secret/#', $auth->checkAcl(null, 'c1', 'secret/data', Auth::ACT_PUB), false);
// 2) 同一主题仅 deny 发布,订阅走默认放行
$chk('subscribe secret/# default-allow', $auth->checkAcl(null, 'c1', 'secret/data', Auth::ACT_SUB), true);
// 3) allow 规则 public/# 发布放行
$chk('allow publish public/info', $auth->checkAcl(null, 'c1', 'public/info', Auth::ACT_PUB), true);
// 4) 未命中规则走默认放行
$chk('default allow other/topic', $auth->checkAcl(null, 'c1', 'other/topic', Auth::ACT_PUB), true);
// 5) 用户定向 denyalice 发布 alice/secret 被拒
$chk('user alice deny publish', $auth->checkAcl('alice', 'cA', 'alice/secret', Auth::ACT_PUB), false);
// 6) alice 订阅 alice/secretuser 规则仅限发布)走默认放行
$chk('user alice subscribe (rule is pub-only)', $auth->checkAcl('alice', 'cA', 'alice/secret', Auth::ACT_SUB), true);
// 7) 超管绕过 deny
$chk('superuser bypass deny', $auth->checkAcl('root', 'cR', 'secret/data', Auth::ACT_PUB, true), true);
// 8) $SYS 只读:订阅放行、发布拒绝
$chk('$SYS subscribe allowed', $auth->checkAcl(null, 'c2', '$SYS/broker/uptime', Auth::ACT_SUB), true);
$chk('$SYS publish denied', $auth->checkAcl(null, 'c2', '$SYS/broker/uptime', Auth::ACT_PUB), false);
// 清理临时规则
if ($ids) {
Db::name('mqttbroker_acl')->whereIn('id', $ids)->delete();
}
$pass = 0;
$fail = 0;
foreach ($results as $case => $r) {
$flag = $r['ok'] ? 'PASS' : 'FAIL';
if ($r['ok']) { $pass++; } else { $fail++; }
$output->writeln("[{$flag}] {$case} (got=" . var_export($r['got'], true) . " expect=" . var_export($r['expect'], true) . ")");
}
$output->writeln("");
$output->writeln("ACL 验证结果:PASS={$pass} FAIL={$fail}");
return $fail === 0 ? 0 : 1;
}
}