mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-11-23 17:22:23 +03:00
For #5885 Adds to, uses and cleans-up central permission checking in ImageService to mirror that which would be experienced by users in the UI to result in the same image access conditions. Adds testing to cover.
156 lines
4.8 KiB
PHP
156 lines
4.8 KiB
PHP
<?php
|
|
|
|
namespace BookStack\Uploads;
|
|
|
|
use Illuminate\Filesystem\FilesystemManager;
|
|
use Illuminate\Support\Str;
|
|
|
|
class ImageStorage
|
|
{
|
|
public function __construct(
|
|
protected FilesystemManager $fileSystem,
|
|
) {
|
|
}
|
|
|
|
/**
|
|
* Get the storage disk for the given image type.
|
|
*/
|
|
public function getDisk(string $imageType = ''): ImageStorageDisk
|
|
{
|
|
$diskName = $this->getDiskName($imageType);
|
|
|
|
return new ImageStorageDisk(
|
|
$diskName,
|
|
$this->fileSystem->disk($diskName),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Check if "local secure restricted" (Fetched behind auth, with permissions enforced)
|
|
* is currently active in the instance.
|
|
*/
|
|
public function usingSecureRestrictedImages(): bool
|
|
{
|
|
return config('filesystems.images') === 'local_secure_restricted';
|
|
}
|
|
|
|
/**
|
|
* Check if "local secure" (Fetched behind auth, either with or without permissions enforced)
|
|
* is currently active in the instance.
|
|
*/
|
|
public function usingSecureImages(): bool
|
|
{
|
|
return config('filesystems.images') === 'local_secure' || $this->usingSecureRestrictedImages();
|
|
}
|
|
|
|
/**
|
|
* Clean up an image file name to be both URL and storage safe.
|
|
*/
|
|
public function cleanImageFileName(string $name): string
|
|
{
|
|
$name = str_replace(' ', '-', $name);
|
|
$nameParts = explode('.', $name);
|
|
$extension = array_pop($nameParts);
|
|
$name = implode('-', $nameParts);
|
|
$name = Str::slug($name);
|
|
|
|
if (strlen($name) === 0) {
|
|
$name = Str::random(10);
|
|
}
|
|
|
|
return $name . '.' . $extension;
|
|
}
|
|
|
|
/**
|
|
* Get the name of the storage disk to use.
|
|
*/
|
|
protected function getDiskName(string $imageType): string
|
|
{
|
|
$storageType = strtolower(config('filesystems.images'));
|
|
$localSecureInUse = ($storageType === 'local_secure' || $storageType === 'local_secure_restricted');
|
|
|
|
// Ensure system images (App logo) are uploaded to a public space
|
|
if ($imageType === 'system' && $localSecureInUse) {
|
|
return 'local';
|
|
}
|
|
|
|
// Rename local_secure options to get our image specific storage driver which
|
|
// is scoped to the relevant image directories.
|
|
if ($localSecureInUse) {
|
|
return 'local_secure_images';
|
|
}
|
|
|
|
return $storageType;
|
|
}
|
|
|
|
/**
|
|
* Get a storage path for the given image URL.
|
|
* Ensures the path will start with "uploads/images".
|
|
* Returns null if the url cannot be resolved to a local URL.
|
|
*/
|
|
public function urlToPath(string $url): ?string
|
|
{
|
|
$url = ltrim(trim($url), '/');
|
|
|
|
// Handle potential relative paths
|
|
$isRelative = !str_starts_with($url, 'http');
|
|
if ($isRelative) {
|
|
if (str_starts_with(strtolower($url), 'uploads/images')) {
|
|
return trim($url, '/');
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
// Handle local images based on paths on the same domain
|
|
$potentialHostPaths = [
|
|
url('uploads/images/'),
|
|
$this->getPublicUrl('/uploads/images/'),
|
|
];
|
|
|
|
foreach ($potentialHostPaths as $potentialBasePath) {
|
|
$potentialBasePath = strtolower($potentialBasePath);
|
|
if (str_starts_with(strtolower($url), $potentialBasePath)) {
|
|
return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Gets a public facing url for an image or location at the given path.
|
|
*/
|
|
public static function getPublicUrl(string $filePath): string
|
|
{
|
|
return static::getPublicBaseUrl() . '/' . ltrim($filePath, '/');
|
|
}
|
|
|
|
/**
|
|
* Get the public base URL used for images.
|
|
* Will not include any path element of the image file, just the base part
|
|
* from where the path is then expected to start from.
|
|
* If s3-style store is in use it will default to guessing a public bucket URL.
|
|
*/
|
|
protected static function getPublicBaseUrl(): string
|
|
{
|
|
$storageUrl = config('filesystems.url');
|
|
|
|
// Get the standard public s3 url if s3 is set as storage type
|
|
// Uses the nice, short URL if bucket name has no periods in otherwise the longer
|
|
// region-based url will be used to prevent http issues.
|
|
if (!$storageUrl && config('filesystems.images') === 's3') {
|
|
$storageDetails = config('filesystems.disks.s3');
|
|
if (!str_contains($storageDetails['bucket'], '.')) {
|
|
$storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com';
|
|
} else {
|
|
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
|
|
}
|
|
}
|
|
|
|
$basePath = $storageUrl ?: url('/');
|
|
|
|
return rtrim($basePath, '/');
|
|
}
|
|
}
|