mirror of
https://github.com/projectsend/projectsend.git
synced 2026-09-17 17:15:08 +00:00
85b1650ef0
The dashboard's System card now says so when no scanner is configured at all, not only when a configured one is failing: "Anything uploaded here — by staff, by clients, or through an upload link — is passed on unchecked", with a link to set it up. Said only where somebody can act on it. Connecting a scanner is a new capability, scanning.connect, community only — on a hosted installation the scanner is infrastructure the platform runs, so its address is not a tenant's to set and its absence is not a tenant's to fix. The two policies stay on both editions, because what to do with a file nobody could scan is a decision about somebody's own files. An edition difference through the registry, never an edition check. Also: PROJECTSEND_SCANNER_DEFAULT_ADDRESS, seeded into the settings on first boot by the command that already does this for two-factor enforcement. It is the opposite of PROJECTSEND_SCANNER_ADDRESS — a starting value rather than a policy, so a Docker install that brings up the optional scanner container arrives configured while the address and the switch stay on the settings screen. Both are seeded together or neither: an address with scanning off would look configured and check nothing. Nothing changes for an existing installation on upgrade: scanning stays off, existing files are marked "never scanned", and the scanner container is still opt-in.
145 lines
6.3 KiB
PHP
145 lines
6.3 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\User;
|
|
use App\Modules\Platform\Settings\Setting;
|
|
use App\Modules\Platform\Settings\Settings;
|
|
use App\Modules\Platform\Settings\StoredSetting;
|
|
|
|
/**
|
|
* A policy that has to exist before the account it protects.
|
|
*
|
|
* On a managed installation the only writers of a setting are whoever
|
|
* administers it and this command — and the administrator is created in
|
|
* the same first boot, one line below. So enforcement seeded here covers
|
|
* the first seat; seeded by anything calling in afterwards does not.
|
|
*/
|
|
beforeEach(function () {
|
|
// Settings are cached across tests (Cache::rememberForever survives the
|
|
// per-test rollback), so the starting point is set rather than assumed.
|
|
app(Settings::class)->set(Setting::TwoFactorEnforcement, 'none');
|
|
StoredSetting::query()->where('key', Setting::TwoFactorEnforcement->value)->delete();
|
|
app(Settings::class)->flush();
|
|
});
|
|
|
|
test('a setting nobody has stored is seeded from the environment', function () {
|
|
config(['projectsend.platform.two_factor_enforcement' => 'staff']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
expect(app(Settings::class)->get(Setting::TwoFactorEnforcement))->toBe('staff');
|
|
});
|
|
|
|
test('a setting somebody has already chosen is left alone', function () {
|
|
// The whole design. An environment value that won every boot would
|
|
// take the setting away from the administrator it belongs to, and
|
|
// somebody who tightened it would find it loosened by a restart.
|
|
app(Settings::class)->set(Setting::TwoFactorEnforcement, 'all');
|
|
config(['projectsend.platform.two_factor_enforcement' => 'staff']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
expect(app(Settings::class)->get(Setting::TwoFactorEnforcement))->toBe('all');
|
|
});
|
|
|
|
test('a stored value equal to the default still counts as chosen', function () {
|
|
// 'none' is the enum's default, so Settings::get() cannot tell it apart
|
|
// from nothing stored. An administrator who deliberately set 'none'
|
|
// must not have it overwritten on the next restart, which is why the
|
|
// command asks the table rather than the accessor.
|
|
app(Settings::class)->set(Setting::TwoFactorEnforcement, 'none');
|
|
config(['projectsend.platform.two_factor_enforcement' => 'all']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
expect(app(Settings::class)->get(Setting::TwoFactorEnforcement))->toBe('none');
|
|
});
|
|
|
|
test('an unset variable seeds nothing and says nothing', function () {
|
|
config(['projectsend.platform.two_factor_enforcement' => null]);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
expect(StoredSetting::query()->where('key', Setting::TwoFactorEnforcement->value)->exists())->toBeFalse();
|
|
});
|
|
|
|
test('a value that is not one of the four is named rather than ignored', function () {
|
|
// A typo here means a tenant provisioned without the policy it was
|
|
// meant to have. Silence would make that look like success.
|
|
config(['projectsend.platform.two_factor_enforcement' => 'stafff']);
|
|
|
|
$this->artisan('projectsend:seed-settings')
|
|
->expectsOutputToContain('is not one of none, staff, clients, all')
|
|
->assertSuccessful();
|
|
|
|
expect(app(Settings::class)->get(Setting::TwoFactorEnforcement))->toBe('none');
|
|
});
|
|
|
|
test('the seeded policy is in force for the first account the same boot creates', function () {
|
|
// The point of the ordering, end to end: the entrypoint seeds and then
|
|
// creates the administrator, so that account is born under the policy
|
|
// rather than ahead of it.
|
|
config(['projectsend.platform.two_factor_enforcement' => 'staff']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
$this->artisan('projectsend:admin', [
|
|
'--name' => 'First',
|
|
'--email' => 'first@example.test',
|
|
'--password' => 'a-strong-password-1',
|
|
])->assertSuccessful();
|
|
|
|
$admin = User::query()->where('email', 'first@example.test')->sole();
|
|
|
|
expect(app(Settings::class)->get(Setting::TwoFactorEnforcement))->toBe('staff')
|
|
->and($admin->hasTwoFactorEnabled())->toBeFalse();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| The virus scanner
|
|
|--------------------------------------------------------------------------
|
|
|
|
|
| The opposite of PROJECTSEND_SCANNER_ADDRESS, which is a policy the
|
|
| platform keeps. This is a starting value for an operator who brought up
|
|
| the optional scanner container beside the application: it arrives
|
|
| configured, and stays theirs to change.
|
|
|
|
|
*/
|
|
|
|
test('a first boot points the installation at the scanner named in its environment', function () {
|
|
config(['projectsend.scanning.default_address' => 'tcp://clamav:3310']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
$settings = app(App\Modules\Platform\Settings\Settings::class);
|
|
|
|
expect($settings->get(App\Modules\Platform\Settings\Setting::VirusScannerAddress))->toBe('tcp://clamav:3310')
|
|
// Both together: an address with scanning off would look
|
|
// configured and check nothing.
|
|
->and($settings->get(App\Modules\Platform\Settings\Setting::VirusScanningEnabled))->toBeTrue();
|
|
});
|
|
|
|
test('it never argues with an administrator who has already chosen', function () {
|
|
$settings = app(App\Modules\Platform\Settings\Settings::class);
|
|
$settings->set(App\Modules\Platform\Settings\Setting::VirusScannerAddress, '');
|
|
$settings->set(App\Modules\Platform\Settings\Setting::VirusScanningEnabled, false);
|
|
|
|
config(['projectsend.scanning.default_address' => 'tcp://clamav:3310']);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
// Cleared on purpose is a decision, and a restart must not undo it.
|
|
expect($settings->get(App\Modules\Platform\Settings\Setting::VirusScannerAddress))->toBe('')
|
|
->and($settings->get(App\Modules\Platform\Settings\Setting::VirusScanningEnabled))->toBeFalse();
|
|
});
|
|
|
|
test('an installation with no scanner named in its environment is left alone', function () {
|
|
config(['projectsend.scanning.default_address' => null]);
|
|
|
|
$this->artisan('projectsend:seed-settings')->assertSuccessful();
|
|
|
|
expect(app(App\Modules\Platform\Settings\Settings::class)->get(App\Modules\Platform\Settings\Setting::VirusScanningEnabled))->toBeFalse();
|
|
});
|