320 lines
12 KiB
PHP
320 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\SystemSettings;
|
|
|
|
use App\Models\Auth\Permission;
|
|
use App\Models\Auth\Role;
|
|
use App\Models\Settings\DatabaseConnection;
|
|
use App\Utilities\UpstreamCredentialCipher;
|
|
use Illuminate\Encryption\Encrypter;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Modules\EcommerceFinance\Services\UpstreamSourceRegistry;
|
|
use Modules\EcommerceFinance\Services\UpstreamStoredConfiguration;
|
|
use Tests\Feature\FeatureTestCase;
|
|
|
|
class UpstreamDatabasesTest extends FeatureTestCase
|
|
{
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
$this->user->attachPermission(
|
|
Permission::where('name', 'read-admin-panel')->firstOrFail()
|
|
);
|
|
|
|
$connection = (string) config('database.default');
|
|
config(["database.connections.$connection.password" => 'shared-system-database-secret']);
|
|
}
|
|
|
|
public function test_super_admin_can_add_multiple_encrypted_database_configurations(): void
|
|
{
|
|
$this->loginAs()
|
|
->post(route('system-settings.databases.store'), $this->payload([
|
|
'name' => 'JianSun 测试库',
|
|
'source_key' => 'jiansun',
|
|
'password' => 'first-secret',
|
|
]))
|
|
->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$this->post(route('system-settings.databases.store'), $this->payload([
|
|
'name' => '云数据库二号',
|
|
'source_key' => '',
|
|
'host' => '127.0.0.2',
|
|
'password' => 'second-secret',
|
|
]))->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$this->assertSame(2, DatabaseConnection::where('company_id', 0)->count());
|
|
$connection = DatabaseConnection::where('source_key', 'jiansun')->firstOrFail();
|
|
$this->assertSame('first-secret', $connection->password);
|
|
$this->assertNotSame(
|
|
'first-secret',
|
|
DB::table('system_database_connections')->where('id', $connection->id)->value('password')
|
|
);
|
|
$sharedPassword = DB::table('system_database_connections')
|
|
->where('id', $connection->id)
|
|
->value('shared_password');
|
|
$this->assertIsString($sharedPassword);
|
|
$this->assertNotSame('first-secret', $sharedPassword);
|
|
$this->assertSame(
|
|
'first-secret',
|
|
app(UpstreamCredentialCipher::class)->decrypt($sharedPassword)
|
|
);
|
|
|
|
$sources = app(UpstreamSourceRegistry::class);
|
|
$this->assertSame('127.0.0.1', $sources->value('jiansun', 'connection.host'));
|
|
$this->assertSame('first-secret', $sources->value('jiansun', 'connection.password'));
|
|
|
|
$this->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSeeText('JianSun 测试库')
|
|
->assertSeeText('云数据库二号')
|
|
->assertDontSee('first-secret');
|
|
}
|
|
|
|
public function test_editing_without_password_preserves_existing_password(): void
|
|
{
|
|
$connection = $this->connection('original-secret');
|
|
|
|
$this->loginAs()
|
|
->patch(route('system-settings.databases.update', $connection->id), $this->payload([
|
|
'name' => $connection->name,
|
|
'password' => '',
|
|
'host' => '127.0.0.2',
|
|
]))
|
|
->assertRedirect(route('system-settings.databases.index'));
|
|
|
|
$connection->refresh();
|
|
$this->assertSame('original-secret', $connection->password);
|
|
$this->assertSame('127.0.0.2', $connection->host);
|
|
|
|
$this->get(route('system-settings.databases.edit', $connection->id))
|
|
->assertOk()
|
|
->assertDontSee('original-secret');
|
|
}
|
|
|
|
public function test_local_password_is_used_when_shared_record_uses_another_app_key(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEncrypter = $this->app->make('encrypter');
|
|
|
|
try {
|
|
$this->app->instance(
|
|
'encrypter',
|
|
new Encrypter(str_repeat('x', 32), (string) config('app.cipher'))
|
|
);
|
|
config([
|
|
'upstreams.local_configuration.enabled' => true,
|
|
'jiansun.connection.host' => 'local-db.test',
|
|
'jiansun.connection.database' => 'local_database',
|
|
'jiansun.connection.username' => 'local_user',
|
|
'jiansun.connection.password' => 'local-secret',
|
|
'jiansun.connection.port' => 11433,
|
|
]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
|
|
$this->assertSame(
|
|
'local-secret',
|
|
$sources->value('jiansun', 'connection.password')
|
|
);
|
|
$this->assertSame('local-db.test', $sources->value('jiansun', 'connection.host'));
|
|
$this->assertSame('local_database', $sources->value('jiansun', 'connection.database'));
|
|
$this->assertSame('local_user', $sources->value('jiansun', 'connection.username'));
|
|
$this->assertSame(11433, $sources->value('jiansun', 'connection.port'));
|
|
} finally {
|
|
$this->app->instance('encrypter', $originalEncrypter);
|
|
}
|
|
}
|
|
|
|
public function test_shared_password_is_available_with_a_different_app_key(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEncrypter = $this->app->make('encrypter');
|
|
|
|
try {
|
|
$this->app->instance(
|
|
'encrypter',
|
|
new Encrypter(str_repeat('x', 32), (string) config('app.cipher'))
|
|
);
|
|
config(['upstreams.local_configuration.enabled' => false]);
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
|
|
// 兼容尚未重建的旧本地镜像:旧模块仍直接读取 password 属性。
|
|
$this->assertSame('shared-secret', $connection->fresh()->password);
|
|
$this->assertSame(
|
|
'shared-secret',
|
|
$sources->value('jiansun', 'connection.password')
|
|
);
|
|
} finally {
|
|
$this->app->instance('encrypter', $originalEncrypter);
|
|
}
|
|
}
|
|
|
|
public function test_whole_connection_comes_from_shared_system_database(): void
|
|
{
|
|
$connection = $this->connection('shared-secret');
|
|
$connection->update([
|
|
'source_key' => 'jiansun',
|
|
'host' => 'shared-db.test',
|
|
'port' => 14330,
|
|
'database_name' => 'shared_database',
|
|
'username' => 'shared_reader',
|
|
]);
|
|
config([
|
|
'upstreams.local_configuration.enabled' => false,
|
|
'jiansun.connection' => [
|
|
'host' => null,
|
|
'port' => 1433,
|
|
'database' => null,
|
|
'username' => null,
|
|
'password' => null,
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'encryption' => 'off',
|
|
'connect_timeout' => 5,
|
|
'query_timeout' => 15,
|
|
'application_intent' => '',
|
|
],
|
|
]);
|
|
|
|
$sources = new UpstreamSourceRegistry(new UpstreamStoredConfiguration());
|
|
$resolved = $sources->value('jiansun', 'connection', []);
|
|
|
|
$this->assertSame('shared-db.test', $resolved['host']);
|
|
$this->assertSame(14330, $resolved['port']);
|
|
$this->assertSame('shared_database', $resolved['database']);
|
|
$this->assertSame('shared_reader', $resolved['username']);
|
|
$this->assertSame('shared-secret', $resolved['password']);
|
|
$this->assertSame('off', $resolved['encryption']);
|
|
}
|
|
|
|
public function test_global_wildcard_scope_is_rejected(): void
|
|
{
|
|
$this->withExceptionHandling();
|
|
|
|
$this->loginAs()
|
|
->post(route('system-settings.databases.store'), $this->payload([
|
|
'allowed_scopes' => '*',
|
|
]))
|
|
->assertSessionHasErrors('allowed_scopes');
|
|
|
|
$this->assertDatabaseMissing('system_database_connections', ['name' => '测试云数据库']);
|
|
}
|
|
|
|
public function test_regular_admin_can_open_database_settings(): void
|
|
{
|
|
$this->user->roles()->sync([
|
|
Role::where('name', 'admin')->firstOrFail()->id,
|
|
]);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSeeText('财务系统配置数据库')
|
|
->assertSeeText('固定 · 不可删除')
|
|
->assertSeeText('云数据库');
|
|
|
|
$this->get(route('system-settings.databases.system.edit'))
|
|
->assertOk()
|
|
->assertSeeText('测试并保存');
|
|
}
|
|
|
|
public function test_database_page_separates_system_and_cloud_database_configuration(): void
|
|
{
|
|
$connectionName = (string) config('database.default');
|
|
config(["database.connections.$connectionName.password" => 'system-database-test-secret']);
|
|
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.index'))
|
|
->assertOk()
|
|
->assertSee('data-system-database="fixed"', false)
|
|
->assertSeeTextInOrder([
|
|
'系统数据库',
|
|
'财务系统配置数据库',
|
|
'固定 · 不可删除',
|
|
'云数据库',
|
|
'添加云数据库',
|
|
]);
|
|
|
|
$this->get(route('system-settings.databases.system.edit'))
|
|
->assertOk()
|
|
->assertSeeText('测试并保存')
|
|
->assertDontSee('system-database-test-secret');
|
|
}
|
|
|
|
public function test_cloud_database_create_page_opens_inside_system_settings(): void
|
|
{
|
|
$this->loginAs()
|
|
->get(route('system-settings.databases.create'))
|
|
->assertOk()
|
|
->assertSeeText('新增数据库')
|
|
->assertDontSeeText('Not able to create a new user.');
|
|
}
|
|
|
|
public function test_production_stored_configuration_requires_explicit_private_flag(): void
|
|
{
|
|
$connection = $this->connection('production-secret');
|
|
$connection->update(['source_key' => 'jiansun']);
|
|
$originalEnvironment = $this->app->environment();
|
|
|
|
try {
|
|
$this->app['env'] = 'production';
|
|
config(['upstreams.stored_configuration.enabled' => false]);
|
|
|
|
$disabled = app(UpstreamStoredConfiguration::class);
|
|
$this->assertFalse($disabled->editable());
|
|
$this->assertNull($disabled->find('jiansun'));
|
|
|
|
config(['upstreams.stored_configuration.enabled' => true]);
|
|
$enabled = new UpstreamStoredConfiguration();
|
|
|
|
$this->assertTrue($enabled->editable());
|
|
$this->assertSame($connection->id, $enabled->find('jiansun')?->id);
|
|
} finally {
|
|
$this->app['env'] = $originalEnvironment;
|
|
}
|
|
}
|
|
|
|
private function payload(array $overrides = []): array
|
|
{
|
|
return array_merge([
|
|
'name' => '测试云数据库',
|
|
'source_key' => '',
|
|
'host' => '127.0.0.1',
|
|
'port' => 1433,
|
|
'database_name' => 'test_finance',
|
|
'username' => 'readonly_user',
|
|
'password' => 'secret',
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'connect_timeout' => 3,
|
|
'query_timeout' => 8,
|
|
'allowed_scopes' => 'supplier.options',
|
|
'enabled' => '1',
|
|
], $overrides);
|
|
}
|
|
|
|
private function connection(string $password): DatabaseConnection
|
|
{
|
|
return DatabaseConnection::create([
|
|
'company_id' => 0,
|
|
'source_key' => null,
|
|
'name' => '已有云数据库',
|
|
'driver' => 'sqlsrv',
|
|
'host' => '127.0.0.1',
|
|
'port' => 1433,
|
|
'database_name' => 'test_finance',
|
|
'username' => 'readonly_user',
|
|
'password' => $password,
|
|
'odbc_driver' => 'FreeTDS',
|
|
'tds_version' => '7.4',
|
|
'connect_timeout' => 3,
|
|
'query_timeout' => 8,
|
|
'allowed_scopes' => ['supplier.options'],
|
|
'charset' => 'UTF-8',
|
|
'enabled' => true,
|
|
]);
|
|
}
|
|
}
|