| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485 |
- <?php
- declare(strict_types=1);
- /**
- * 小程序身份冲突盘点。
- *
- * 一个账号只能绑定一个业务身份。Access::identities() 在识别到多个生效身份时抛出 40903,
- * 而该调用位于 AuthService::issue() 的事务内,异常会把本次登录整体回滚——这样的账号既拿不到
- * Token,也没有自助恢复路径,只能由管理员逐条停用旧授权。
- *
- * 默认只读输出。--apply 在事务内停用同一账号除最新一条以外的生效授权,并打印被停用的授权编号,
- * 回滚方式是把这些编号的 status 改回 1。脚本不删除任何记录,也不改动用户和业务档案。
- */
- require dirname(__DIR__) . '/vendor/autoload.php';
- \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load();
- \support\App::loadAllConfig(['route']);
- use app\miniapi\model\MiniGrant;
- use app\miniapi\service\Access;
- $apply = in_array('--apply', $argv, true);
- $active = MiniGrant::query()->where('status', 1)->orderBy('user_id')->orderBy('id')->get();
- $live = [];
- $stale = [];
- foreach ($active as $grant) {
- $model = Access::resourceModel($grant->role);
- $resource = $model === null
- ? null
- : $model::query()->where('id', $grant->resource_id)->where('status', 1)->first();
- if ($resource === null) {
- $stale[] = $grant;
- continue;
- }
- $live[(int) $grant->user_id][] = $grant;
- }
- $conflicts = array_filter($live, static fn (array $rows): bool => count($rows) > 1);
- printf("生效授权 %d 条,其中指向已删除或已停用档案的 %d 条(Access 不计入,不影响登录)。\n", $active->count(), count($stale));
- printf("身份冲突账号 %d 个。\n", count($conflicts));
- foreach ($conflicts as $userId => $rows) {
- printf(" user_id=%d 生效身份 %d 个:", $userId, count($rows));
- foreach ($rows as $grant) {
- printf('grant#%d %s/%d ', $grant->id, $grant->role, $grant->resource_id);
- }
- printf("(保留最新 grant#%d)\n", end($rows)->id);
- }
- if (!$conflicts) {
- echo "无需处理。\n";
- exit;
- }
- if (!$apply) {
- echo "计划:如需停用多余身份,执行 php scripts/audit_mini_identity_conflict.php --apply\n";
- exit;
- }
- $disabled = [];
- (new MiniGrant())->getConnection()->transaction(function () use ($conflicts, &$disabled): void {
- foreach ($conflicts as $userId => $rows) {
- $keep = end($rows);
- foreach ($rows as $grant) {
- if ((int) $grant->id === (int) $keep->id) {
- continue;
- }
- MiniGrant::query()->where('id', $grant->id)->update([
- 'status' => 0,
- 'updated_at' => date('Y-m-d H:i:s'),
- ]);
- $disabled[] = [
- 'grant_id' => (int) $grant->id,
- 'role' => $grant->role,
- 'resource_id' => (int) $grant->resource_id,
- 'kept_grant_id' => (int) $keep->id,
- ];
- }
- }
- });
- printf("已停用 %d 条多余授权。受影响的账号可重新登录;如判断有误,将下列授权的 status 改回 1 即可恢复。\n", count($disabled));
- echo json_encode($disabled, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE) . PHP_EOL;
|