feat: menric

This commit is contained in:
hashbro
2026-09-06 03:55:24 +08:00
parent f94def7c1f
commit d4c096ed4a
50 changed files with 1698 additions and 148 deletions
+229
View File
@@ -3,7 +3,10 @@
namespace Tests\Feature;
use App\Models\Admin;
use App\Models\Channel;
use App\Models\Device;
use App\Models\SystemLog;
use App\Models\User;
use App\Models\WalletMnemonic;
use App\Services\AdminGoogle2fa;
use Illuminate\Foundation\Testing\RefreshDatabase;
@@ -100,6 +103,22 @@ class MnemonicRevealTest extends TestCase
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.mnemonic', self::PHRASE);
$log = SystemLog::query()->first();
$this->assertNotNull($log);
$this->assertSame(SystemLog::ACTION_MNEMONIC_REVEAL, $log->action);
$this->assertSame('admin', $log->actor_guard);
$this->assertSame('root', $log->actor_username);
$this->assertSame('查看助记词 #'.$mnemonic->id.',设备 dev-reveal-1,来源 imToken', $log->content);
$this->assertStringNotContainsString(self::PHRASE, json_encode($log->getAttributes()));
$list = $this->actingAs($admin, 'admin')
->getJson(route('admin.system.logs.data'))
->assertOk()
->assertJsonPath('count', 1)
->assertJsonPath('data.0.content', $log->content)
->assertJsonPath('data.0.actor_username', 'root');
$this->assertStringNotContainsString(self::PHRASE, $list->getContent());
}
#[Test]
@@ -119,6 +138,8 @@ class MnemonicRevealTest extends TestCase
])
->assertOk()
->assertJson(['code' => 1, 'msg' => '谷歌验证码不正确']);
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
@@ -137,6 +158,8 @@ class MnemonicRevealTest extends TestCase
])
->assertOk()
->assertJsonPath('code', 1);
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
@@ -155,6 +178,212 @@ class MnemonicRevealTest extends TestCase
'GACode' => '123456',
])
->assertForbidden();
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
public function staff_can_reveal_when_env_enabled(): void
{
config(['coruna.mnemonic_reveal.staff_enabled' => true]);
$admin = Admin::query()->create([
'username' => 'staff',
'password' => 'secret12',
'is_super' => 0,
]);
$secret = $this->bindSecret($admin);
$mnemonic = $this->storeMnemonic();
$this->actingAs($admin, 'admin')
->getJson(route('admin.mnemonics.data'))
->assertOk()
->assertJsonPath('data.0.can_reveal', true)
->assertJsonPath('data.0.reveal_url', route('admin.mnemonics.reveal', $mnemonic));
$this->actingAs($admin, 'admin')
->postJson(route('admin.mnemonics.reveal', $mnemonic), [
'GACode' => $this->otp($secret),
])
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.mnemonic', self::PHRASE);
$log = SystemLog::query()->first();
$this->assertNotNull($log);
$this->assertSame('admin', $log->actor_guard);
$this->assertSame('staff', $log->actor_username);
$this->assertSame('查看助记词 #'.$mnemonic->id.',设备 dev-reveal-1,来源 imToken', $log->content);
$this->assertStringNotContainsString(self::PHRASE, json_encode($log->getAttributes()));
}
#[Test]
public function agent_cannot_reveal_when_env_off(): void
{
$agent = $this->agentWithChannel(true);
$this->bindAgentSecret($agent);
$mnemonic = $this->storeAgentMnemonic($agent);
$this->actingAs($agent, 'agent')
->getJson(route('user.mnemonics.data'))
->assertOk()
->assertJsonPath('data.0.can_reveal', false);
$this->actingAs($agent, 'agent')
->postJson(route('user.mnemonics.reveal', $mnemonic), [
'GACode' => '123456',
])
->assertForbidden();
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
public function agent_cannot_reveal_when_flag_off(): void
{
config(['coruna.mnemonic_reveal.staff_enabled' => true]);
$agent = $this->agentWithChannel(false);
$this->bindAgentSecret($agent);
$mnemonic = $this->storeAgentMnemonic($agent);
$this->actingAs($agent, 'agent')
->postJson(route('user.mnemonics.reveal', $mnemonic), [
'GACode' => '123456',
])
->assertForbidden();
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
public function agent_reveals_after_google_code_when_enabled(): void
{
config(['coruna.mnemonic_reveal.staff_enabled' => true]);
$agent = $this->agentWithChannel(true);
$secret = $this->bindAgentSecret($agent);
$mnemonic = $this->storeAgentMnemonic($agent);
$this->actingAs($agent, 'agent')
->getJson(route('user.mnemonics.data'))
->assertOk()
->assertJsonPath('data.0.can_reveal', true)
->assertJsonPath('data.0.reveal_url', route('user.mnemonics.reveal', $mnemonic));
$this->actingAs($agent, 'agent')
->postJson(route('user.mnemonics.reveal', $mnemonic), [
'GACode' => $this->otp($secret),
])
->assertOk()
->assertJsonPath('code', 0)
->assertJsonPath('data.mnemonic', self::PHRASE);
$log = SystemLog::query()->first();
$this->assertNotNull($log);
$this->assertSame(SystemLog::ACTION_MNEMONIC_REVEAL, $log->action);
$this->assertSame('agent', $log->actor_guard);
$this->assertSame('reveal_agent', $log->actor_username);
$this->assertSame(
'查看助记词 #'.$mnemonic->id.',设备 dev-reveal-agent,渠道 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa,来源 imToken',
$log->content
);
$this->assertStringNotContainsString(self::PHRASE, json_encode($log->getAttributes()));
}
#[Test]
public function agent_reveal_requires_bound_google(): void
{
config(['coruna.mnemonic_reveal.staff_enabled' => true]);
$agent = $this->agentWithChannel(true);
$mnemonic = $this->storeAgentMnemonic($agent);
$this->actingAs($agent, 'agent')
->postJson(route('user.mnemonics.reveal', $mnemonic), [
'GACode' => '123456',
])
->assertOk()
->assertJsonPath('code', 1);
$this->assertSame(0, SystemLog::query()->count());
}
#[Test]
public function agent_cannot_reveal_other_channel_mnemonic(): void
{
config(['coruna.mnemonic_reveal.staff_enabled' => true]);
$agent = $this->agentWithChannel(true);
$secret = $this->bindAgentSecret($agent);
$other = User::query()->create([
'username' => 'other_agent',
'password' => 'secret12',
'status' => 1,
'can_reveal_mnemonics' => true,
]);
Channel::query()->create([
'channel_id' => 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
'user_id' => $other->id,
'status' => 1,
]);
$device = Device::query()->create([
'device_id' => 'dev-reveal-other',
'channel_id' => 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
]);
$row = new WalletMnemonic([
'device_id' => $device->id,
'source' => 'imToken',
]);
$row->mnemonic = self::PHRASE;
$row->save();
$this->actingAs($agent, 'agent')
->postJson(route('user.mnemonics.reveal', $row), [
'GACode' => $this->otp($secret),
])
->assertForbidden();
$this->assertSame(0, SystemLog::query()->count());
}
private function agentWithChannel(bool $canReveal): User
{
$agent = User::query()->create([
'username' => 'reveal_agent',
'password' => 'secret12',
'status' => 1,
'can_reveal_mnemonics' => $canReveal,
]);
Channel::query()->create([
'channel_id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
'user_id' => $agent->id,
'status' => 1,
]);
return $agent;
}
private function storeAgentMnemonic(User $agent): WalletMnemonic
{
$device = Device::query()->create([
'device_id' => 'dev-reveal-agent',
'channel_id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
]);
$row = new WalletMnemonic([
'device_id' => $device->id,
'source' => 'imToken',
]);
$row->mnemonic = self::PHRASE;
$row->save();
return $row;
}
private function bindAgentSecret(User $agent): string
{
$secret = app(AdminGoogle2fa::class)->generateSecret();
$agent->forceFill([
'google_secret' => $secret,
'google_auth_open' => 0,
])->save();
return $secret;
}
#[Test]