mirror of
https://github.com/archtechx/tenancy.git
synced 2025-12-12 18:04:03 +00:00
* queue.yml: remove TENANCY_VERSION env var from test.sh * add DisallowSqliteAttach feature * Fix code style (php-cs-fixer) * ci: add cd to each step * ci: simpler solution to race conditions, proper os/arch matrix * ci: fix runs-on matrix * ci: fix workflow on windows, fix makefile * Auto-build: Update extensions [skip ci] * Auto-build: Update extensions [skip ci] * ci: try fixing retry logic, make makefile use cl on Windows * ci: use the current branch for rebase * ci: try calling vcvars64 * ci: misc minor fixes * ci: try fixing c compiler on windows * ci: misc minor fixes * ci: add debug steps * ci: try to fix windows build * ci: try using clang on windows * ci: windows fixes, makefile fix * Auto-build: Update extensions [skip ci] * ci: dont produce .exp .lib on Windows * ci: try forcing shell: bash on commit step * ci: try to get linux cross-compilation working * ci: reformulate condition * ci: fix syntax error * ci: correct debian image name * Auto-build: Update extensions [skip ci] * ci: try to set up macOS cross-compilation * ci: add ARCH variable to makefile, override it during cross-compilation * Auto-build: Update extensions [skip ci] * ci: X64 -> x64 * ci: only trigger extensions.yml on pushes to extensions/ * fix tests on x64 * ci: try using bash for pushing on windows; ignore phpstan error * fix test failing in ci but passing locally * bump php version in composer.json, trigger extensions.yml build * remove comment * noattach: more explicit return values, avoid potential non-bool return values * makefile: use -Os on Windows * ci: use make -B * ci: try triggering extensions build on extensions.yml file changes * Auto-build: Update extensions [skip ci] * Auto-build: Update extensions [skip ci] * ci: remove windows linker flag, use a whitelist for git add * Auto-build: Update extensions [skip ci] * Auto-build: Update extensions [skip ci] * Auto-build: Update extensions [skip ci] * fix path in feature class, minor refactor * Fix code style (php-cs-fixer) --------- Co-authored-by: PHP CS Fixer <phpcsfixer@example.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
106 lines
4.2 KiB
PHP
106 lines
4.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use Illuminate\Database\QueryException;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Facades\Event;
|
|
use Illuminate\Support\Facades\Route;
|
|
use Stancl\JobPipeline\JobPipeline;
|
|
use Stancl\Tenancy\Bootstrappers\DatabaseTenancyBootstrapper;
|
|
use Stancl\Tenancy\Events\TenancyEnded;
|
|
use Stancl\Tenancy\Events\TenancyInitialized;
|
|
use Stancl\Tenancy\Events\TenantCreated;
|
|
use Stancl\Tenancy\Features\DisallowSqliteAttach;
|
|
use Stancl\Tenancy\Jobs\CreateDatabase;
|
|
use Stancl\Tenancy\Jobs\MigrateDatabase;
|
|
use Stancl\Tenancy\Listeners\BootstrapTenancy;
|
|
use Stancl\Tenancy\Listeners\RevertToCentralContext;
|
|
use Stancl\Tenancy\Middleware\InitializeTenancyByPath;
|
|
use Stancl\Tenancy\Tests\Etc\Tenant;
|
|
|
|
test('sqlite ATTACH statements can be blocked', function (bool $disallow) {
|
|
try {
|
|
readlink(base_path('vendor'));
|
|
} catch (\Throwable) {
|
|
symlink(base_path('vendor'), '/var/www/html/vendor');
|
|
}
|
|
|
|
if (php_uname('m') == 'aarch64') {
|
|
// Escape testbench prison. Can't hardcode /var/www/html/extensions/... here
|
|
// since GHA doesn't mount the filesystem on the container's workdir
|
|
DisallowSqliteAttach::$extensionPath = realpath(base_path('../../../../extensions/lib/arm/noattach.so'));
|
|
} else {
|
|
DisallowSqliteAttach::$extensionPath = realpath(base_path('../../../../extensions/lib/noattach.so'));
|
|
}
|
|
|
|
if ($disallow) config(['tenancy.features' => [DisallowSqliteAttach::class]]);
|
|
|
|
config(['tenancy.bootstrappers' => [DatabaseTenancyBootstrapper::class]]);
|
|
Event::listen(TenancyInitialized::class, BootstrapTenancy::class);
|
|
Event::listen(TenancyEnded::class, RevertToCentralContext::class);
|
|
|
|
Event::listen(TenantCreated::class, JobPipeline::make([
|
|
CreateDatabase::class,
|
|
MigrateDatabase::class,
|
|
])->send(function (TenantCreated $event) {
|
|
return $event->tenant;
|
|
})->toListener());
|
|
|
|
$tempdb1 = tempnam(sys_get_temp_dir(), 'tenancy_attach_test');
|
|
$tempdb2 = tempnam(sys_get_temp_dir(), 'tenancy_attach_test');
|
|
register_shutdown_function(fn () => @unlink($tempdb1));
|
|
register_shutdown_function(fn () => @unlink($tempdb2));
|
|
|
|
config(['database.connections.foo' => ['driver' => 'sqlite', 'database' => $tempdb1]]);
|
|
config(['database.connections.bar' => ['driver' => 'sqlite', 'database' => $tempdb2]]);
|
|
|
|
DB::connection('bar')->statement('CREATE TABLE secrets (key, value)');
|
|
DB::connection('bar')->statement('INSERT INTO secrets (key, value) VALUES ("secret_foo", "secret_bar")');
|
|
|
|
Route::post('/central-sqli', function () {
|
|
DB::connection('foo')->select(request('q1'));
|
|
return json_encode(DB::connection('foo')->select(request('q2')));
|
|
});
|
|
|
|
Route::middleware(InitializeTenancyByPath::class)->post('/{tenant}/tenant-sqli', function () {
|
|
DB::select(request('q1'));
|
|
return json_encode(DB::select(request('q2')));
|
|
});
|
|
|
|
tenancy(); // trigger features: todo@samuel remove after feature refactor
|
|
|
|
if ($disallow) {
|
|
expect(fn () => pest()->post('/central-sqli', [
|
|
'q1' => 'ATTACH DATABASE "' . $tempdb2 . '" as bar',
|
|
'q2' => 'SELECT * from bar.secrets',
|
|
])->json())->toThrow(QueryException::class, 'not authorized');
|
|
} else {
|
|
expect(pest()->post('/central-sqli', [
|
|
'q1' => 'ATTACH DATABASE "' . $tempdb2 . '" as bar',
|
|
'q2' => 'SELECT * from bar.secrets',
|
|
])->json()[0])->toBe([
|
|
'key' => 'secret_foo',
|
|
'value' => 'secret_bar',
|
|
]);
|
|
}
|
|
|
|
$tenant = Tenant::create([
|
|
'tenancy_db_connection' => 'sqlite',
|
|
]);
|
|
|
|
if ($disallow) {
|
|
expect(fn () => pest()->post($tenant->id . '/tenant-sqli', [
|
|
'q1' => 'ATTACH DATABASE "' . $tempdb2 . '" as baz',
|
|
'q2' => 'SELECT * from bar.secrets',
|
|
])->json())->toThrow(QueryException::class, 'not authorized');
|
|
} else {
|
|
expect(pest()->post($tenant->id . '/tenant-sqli', [
|
|
'q1' => 'ATTACH DATABASE "' . $tempdb2 . '" as baz',
|
|
'q2' => 'SELECT * from baz.secrets',
|
|
])->json()[0])->toBe([
|
|
'key' => 'secret_foo',
|
|
'value' => 'secret_bar',
|
|
]);
|
|
}
|
|
})->with([true, false]);
|