mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-12-05 17:22:06 +03:00
Merge branch 'development' into release
This commit is contained in:
@@ -26,6 +26,13 @@ DB_DATABASE=database_database
|
|||||||
DB_USERNAME=database_username
|
DB_USERNAME=database_username
|
||||||
DB_PASSWORD=database_user_password
|
DB_PASSWORD=database_user_password
|
||||||
|
|
||||||
|
# Storage system to use
|
||||||
|
# By default files are stored on the local filesystem, with images being placed in
|
||||||
|
# public web space so they can be efficiently served directly by the web-server.
|
||||||
|
# For other options with different security levels & considerations, refer to:
|
||||||
|
# https://www.bookstackapp.com/docs/admin/upload-config/
|
||||||
|
STORAGE_TYPE=local
|
||||||
|
|
||||||
# Mail system to use
|
# Mail system to use
|
||||||
# Can be 'smtp' or 'sendmail'
|
# Can be 'smtp' or 'sendmail'
|
||||||
MAIL_DRIVER=smtp
|
MAIL_DRIVER=smtp
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ class ImageService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->storage->usingSecureImages() && user()->isGuest()) {
|
if ($this->blockedBySecureImages()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,13 +280,24 @@ class ImageService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->storage->usingSecureImages() && user()->isGuest()) {
|
if ($this->blockedBySecureImages()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->imageFileExists($image->path, $image->type);
|
return $this->imageFileExists($image->path, $image->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the current user should be blocked from accessing images based on if secure images are enabled
|
||||||
|
* and if public access is enabled for the application.
|
||||||
|
*/
|
||||||
|
protected function blockedBySecureImages(): bool
|
||||||
|
{
|
||||||
|
$enforced = $this->storage->usingSecureImages() && !setting('app-public');
|
||||||
|
|
||||||
|
return $enforced && user()->isGuest();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the given image path exists for the given image type and that it is likely an image file.
|
* Check if the given image path exists for the given image type and that it is likely an image file.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class ImageStorage
|
|||||||
return 'local';
|
return 'local';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rename local_secure options to get our image specific storage driver which
|
// Rename local_secure options to get our image-specific storage driver, which
|
||||||
// is scoped to the relevant image directories.
|
// is scoped to the relevant image directories.
|
||||||
if ($localSecureInUse) {
|
if ($localSecureInUse) {
|
||||||
return 'local_secure_images';
|
return 'local_secure_images';
|
||||||
|
|||||||
54
composer.lock
generated
54
composer.lock
generated
@@ -62,16 +62,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "aws/aws-sdk-php",
|
"name": "aws/aws-sdk-php",
|
||||||
"version": "3.360.0",
|
"version": "3.362.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/aws/aws-sdk-php.git",
|
"url": "https://github.com/aws/aws-sdk-php.git",
|
||||||
"reference": "a21055795be59f3d7c5ca6e4d52a80930dcf8c20"
|
"reference": "f29a49b74d5ee771f13432e16d58651de91f7e79"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a21055795be59f3d7c5ca6e4d52a80930dcf8c20",
|
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/f29a49b74d5ee771f13432e16d58651de91f7e79",
|
||||||
"reference": "a21055795be59f3d7c5ca6e4d52a80930dcf8c20",
|
"reference": "f29a49b74d5ee771f13432e16d58651de91f7e79",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -153,22 +153,22 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
"forum": "https://github.com/aws/aws-sdk-php/discussions",
|
||||||
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
"issues": "https://github.com/aws/aws-sdk-php/issues",
|
||||||
"source": "https://github.com/aws/aws-sdk-php/tree/3.360.0"
|
"source": "https://github.com/aws/aws-sdk-php/tree/3.362.1"
|
||||||
},
|
},
|
||||||
"time": "2025-11-17T19:46:19+00:00"
|
"time": "2025-11-20T19:10:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "bacon/bacon-qr-code",
|
"name": "bacon/bacon-qr-code",
|
||||||
"version": "v3.0.2",
|
"version": "v3.0.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Bacon/BaconQrCode.git",
|
"url": "https://github.com/Bacon/BaconQrCode.git",
|
||||||
"reference": "fe259c55425b8178f77fb6d1f84ba2473e21ed55"
|
"reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/fe259c55425b8178f77fb6d1f84ba2473e21ed55",
|
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/36a1cb2b81493fa5b82e50bf8068bf84d1542563",
|
||||||
"reference": "fe259c55425b8178f77fb6d1f84ba2473e21ed55",
|
"reference": "36a1cb2b81493fa5b82e50bf8068bf84d1542563",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -208,9 +208,9 @@
|
|||||||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||||
"source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.2"
|
"source": "https://github.com/Bacon/BaconQrCode/tree/v3.0.3"
|
||||||
},
|
},
|
||||||
"time": "2025-11-16T22:59:48+00:00"
|
"time": "2025-11-19T17:15:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -3613,31 +3613,31 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nunomaduro/termwind",
|
"name": "nunomaduro/termwind",
|
||||||
"version": "v2.3.2",
|
"version": "v2.3.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nunomaduro/termwind.git",
|
"url": "https://github.com/nunomaduro/termwind.git",
|
||||||
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0"
|
"reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0",
|
"url": "https://api.github.com/repos/nunomaduro/termwind/zipball/6fb2a640ff502caace8e05fd7be3b503a7e1c017",
|
||||||
"reference": "eb61920a53057a7debd718a5b89c2178032b52c0",
|
"reference": "6fb2a640ff502caace8e05fd7be3b503a7e1c017",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"symfony/console": "^7.3.4"
|
"symfony/console": "^7.3.6"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"illuminate/console": "^11.46.1",
|
"illuminate/console": "^11.46.1",
|
||||||
"laravel/pint": "^1.25.1",
|
"laravel/pint": "^1.25.1",
|
||||||
"mockery/mockery": "^1.6.12",
|
"mockery/mockery": "^1.6.12",
|
||||||
"pestphp/pest": "^2.36.0 || ^3.8.4",
|
"pestphp/pest": "^2.36.0 || ^3.8.4 || ^4.1.3",
|
||||||
"phpstan/phpstan": "^1.12.32",
|
"phpstan/phpstan": "^1.12.32",
|
||||||
"phpstan/phpstan-strict-rules": "^1.6.2",
|
"phpstan/phpstan-strict-rules": "^1.6.2",
|
||||||
"symfony/var-dumper": "^7.3.4",
|
"symfony/var-dumper": "^7.3.5",
|
||||||
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
|
"thecodingmachine/phpstan-strict-rules": "^1.0.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@@ -3680,7 +3680,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nunomaduro/termwind/issues",
|
"issues": "https://github.com/nunomaduro/termwind/issues",
|
||||||
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.2"
|
"source": "https://github.com/nunomaduro/termwind/tree/v2.3.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3696,7 +3696,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-10-18T11:10:27+00:00"
|
"time": "2025-11-20T02:34:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "onelogin/php-saml",
|
"name": "onelogin/php-saml",
|
||||||
@@ -8587,16 +8587,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nunomaduro/collision",
|
"name": "nunomaduro/collision",
|
||||||
"version": "v8.8.2",
|
"version": "v8.8.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nunomaduro/collision.git",
|
"url": "https://github.com/nunomaduro/collision.git",
|
||||||
"reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb"
|
"reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb",
|
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/1dc9e88d105699d0fee8bb18890f41b274f6b4c4",
|
||||||
"reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb",
|
"reference": "1dc9e88d105699d0fee8bb18890f41b274f6b4c4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -8618,7 +8618,7 @@
|
|||||||
"laravel/sanctum": "^4.1.1",
|
"laravel/sanctum": "^4.1.1",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
"orchestra/testbench-core": "^9.12.0 || ^10.4",
|
"orchestra/testbench-core": "^9.12.0 || ^10.4",
|
||||||
"pestphp/pest": "^3.8.2",
|
"pestphp/pest": "^3.8.2 || ^4.0.0",
|
||||||
"sebastian/environment": "^7.2.1 || ^8.0"
|
"sebastian/environment": "^7.2.1 || ^8.0"
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
@@ -8682,7 +8682,7 @@
|
|||||||
"type": "patreon"
|
"type": "patreon"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-25T02:12:12+00:00"
|
"time": "2025-11-20T02:55:25+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ namespace Tests\Uploads;
|
|||||||
use BookStack\Entities\Repos\PageRepo;
|
use BookStack\Entities\Repos\PageRepo;
|
||||||
use BookStack\Uploads\Image;
|
use BookStack\Uploads\Image;
|
||||||
use BookStack\Uploads\ImageService;
|
use BookStack\Uploads\ImageService;
|
||||||
|
use BookStack\Uploads\UserAvatars;
|
||||||
|
use BookStack\Users\Models\Role;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@@ -467,6 +469,26 @@ class ImageTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_avatar_images_visible_only_when_public_access_enabled_with_local_secure_restricted()
|
||||||
|
{
|
||||||
|
config()->set('filesystems.images', 'local_secure_restricted');
|
||||||
|
$user = $this->users->admin();
|
||||||
|
$avatars = $this->app->make(UserAvatars::class);
|
||||||
|
$avatars->assignToUserFromExistingData($user, $this->files->pngImageData(), 'png');
|
||||||
|
|
||||||
|
$avatarUrl = $user->getAvatar();
|
||||||
|
|
||||||
|
$resp = $this->get($avatarUrl);
|
||||||
|
$resp->assertRedirect('/login');
|
||||||
|
|
||||||
|
$this->permissions->makeAppPublic();
|
||||||
|
|
||||||
|
$resp = $this->get($avatarUrl);
|
||||||
|
$resp->assertOk();
|
||||||
|
|
||||||
|
$this->files->deleteAtRelativePath($user->avatar->path);
|
||||||
|
}
|
||||||
|
|
||||||
public function test_secure_restricted_images_inaccessible_without_relation_permission()
|
public function test_secure_restricted_images_inaccessible_without_relation_permission()
|
||||||
{
|
{
|
||||||
config()->set('filesystems.images', 'local_secure_restricted');
|
config()->set('filesystems.images', 'local_secure_restricted');
|
||||||
@@ -491,6 +513,38 @@ class ImageTest extends TestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_secure_restricted_images_accessible_with_public_guest_access()
|
||||||
|
{
|
||||||
|
config()->set('filesystems.images', 'local_secure_restricted');
|
||||||
|
$this->permissions->makeAppPublic();
|
||||||
|
|
||||||
|
$this->asEditor();
|
||||||
|
$page = $this->entities->page();
|
||||||
|
$this->files->uploadGalleryImageToPage($this, $page);
|
||||||
|
$image = Image::query()->where('type', '=', 'gallery')
|
||||||
|
->where('uploaded_to', '=', $page->id)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$expectedUrl = url($image->path);
|
||||||
|
$expectedPath = storage_path($image->path);
|
||||||
|
auth()->logout();
|
||||||
|
|
||||||
|
$this->get($expectedUrl)->assertOk();
|
||||||
|
|
||||||
|
$this->permissions->setEntityPermissions($page, [], []);
|
||||||
|
|
||||||
|
$resp = $this->get($expectedUrl);
|
||||||
|
$resp->assertNotFound();
|
||||||
|
|
||||||
|
$this->permissions->setEntityPermissions($page, ['view'], [Role::getSystemRole('public')]);
|
||||||
|
|
||||||
|
$this->get($expectedUrl)->assertOk();
|
||||||
|
|
||||||
|
if (file_exists($expectedPath)) {
|
||||||
|
unlink($expectedPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function test_thumbnail_path_handled_by_secure_restricted_images()
|
public function test_thumbnail_path_handled_by_secure_restricted_images()
|
||||||
{
|
{
|
||||||
config()->set('filesystems.images', 'local_secure_restricted');
|
config()->set('filesystems.images', 'local_secure_restricted');
|
||||||
|
|||||||
Reference in New Issue
Block a user