diff --git a/app/AlbumSources/AlbumSourceBase.php b/app/AlbumSources/AlbumSourceBase.php index 374e17f..a5ac38a 100644 --- a/app/AlbumSources/AlbumSourceBase.php +++ b/app/AlbumSources/AlbumSourceBase.php @@ -42,6 +42,11 @@ abstract class AlbumSourceBase return self::$albumSourceCache[$fullClassName]; } + public function getConfiguration() + { + return $this->configuration; + } + public function setAlbum(Album $album) { $this->album = $album; diff --git a/app/AlbumSources/AmazonS3Source.php b/app/AlbumSources/AmazonS3Source.php index 6694f5b..9ac3164 100644 --- a/app/AlbumSources/AmazonS3Source.php +++ b/app/AlbumSources/AmazonS3Source.php @@ -130,7 +130,19 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ */ public function getUrlToPhoto(Photo $photo, $thumbnail = null) { - return $this->getClient()->getObjectUrl($this->configuration->container_name, $this->getPathToPhoto($photo, $thumbnail)); + $client = $this->getClient(); + + if ($this->configuration->s3_signed_urls) + { + $cmd = $client->getCommand('GetObject', [ + 'Bucket' => $this->configuration->container_name, + 'Key' => $this->getPathToPhoto($photo, $thumbnail) + ]); + + return $client->createPresignedRequest($cmd, '+5 minutes')->getUri(); + } + + return $client->getObjectUrl($this->configuration->container_name, $this->getPathToPhoto($photo, $thumbnail)); } /** @@ -144,7 +156,11 @@ class AmazonS3Source extends AlbumSourceBase implements IAlbumSource, IAnalysisQ { $photoPath = $this->getPathToPhoto($photo, $thumbnail); - $this->getClient()->upload($this->configuration->container_name, $photoPath, fopen($tempFilename, 'r+'), 'public-read'); + $uploadAcl = $this->configuration->s3_signed_urls + ? 'private' + : 'public-read'; + + $this->getClient()->upload($this->configuration->container_name, $photoPath, fopen($tempFilename, 'r+'), $uploadAcl); } /** diff --git a/app/AlbumSources/IAlbumSource.php b/app/AlbumSources/IAlbumSource.php index 4e98635..aecce32 100644 --- a/app/AlbumSources/IAlbumSource.php +++ b/app/AlbumSources/IAlbumSource.php @@ -6,7 +6,6 @@ use App\Album; use App\Photo; use App\Storage; use Guzzle\Http\EntityBody; -use Symfony\Component\HttpFoundation\File\File; interface IAlbumSource { @@ -32,6 +31,12 @@ interface IAlbumSource */ function fetchPhotoContent(Photo $photo, $thumbnail = null); + /** + * Gets the configuration of the source. + * @return mixed + */ + function getConfiguration(); + /** * Gets the name of this album source. * @return string diff --git a/app/Http/Controllers/Admin/StorageController.php b/app/Http/Controllers/Admin/StorageController.php index 6a1c775..f4c53c0 100644 --- a/app/Http/Controllers/Admin/StorageController.php +++ b/app/Http/Controllers/Admin/StorageController.php @@ -55,12 +55,14 @@ class StorageController extends Controller $this->authorizeAccessToAdminPanel('admin:manage-storage'); $filesystemDefaultLocation = sprintf('%s/storage/app/albums', dirname(dirname(dirname(dirname(__DIR__))))); + $storage = new Storage(); + $storage->s3_signed_urls = true; return Theme::render('admin.create_storage', [ 'album_sources' => UserConfig::albumSources(), 'filesystem_default_location' => $filesystemDefaultLocation, 'info' => $request->session()->get('info'), - 'storage' => new Storage() + 'storage' => $storage ]); } @@ -94,6 +96,7 @@ class StorageController extends Controller $storage->is_active = true; $storage->is_default = (strtolower($request->get('is_default')) == 'on'); $storage->is_internal = false; + $storage->s3_signed_urls = (strtolower($request->get('s3_signed_urls')) == 'on'); if ($storage->source != 'LocalFilesystemSource' && isset($storage->location)) { @@ -219,10 +222,12 @@ class StorageController extends Controller 'cdn_url', 'access_key', 'secret_key', - 'b2_bucket_type' + 'b2_bucket_type', + 's3_signed_urls' ])); $storage->is_active = (strtolower($request->get('is_active')) == 'on'); $storage->is_default = (strtolower($request->get('is_default')) == 'on'); + $storage->s3_signed_urls = (strtolower($request->get('s3_signed_urls')) == 'on'); if ($storage->is_default && !$storage->is_active) { diff --git a/app/Photo.php b/app/Photo.php index a4fef8b..3a3e892 100644 --- a/app/Photo.php +++ b/app/Photo.php @@ -2,6 +2,7 @@ namespace App; +use App\AlbumSources\IAlbumSource; use Illuminate\Database\Eloquent\Model; use Illuminate\Notifications\Notifiable; @@ -110,9 +111,14 @@ class Photo extends Model public function thumbnailUrl($thumbnailName = null, $cacheBust = true) { - $url = $this->album->getAlbumSource()->getUrlToPhoto($this, $thumbnailName); + /** @var IAlbumSource $source */ + $source = $this->album->getAlbumSource(); + $sourceConfiguration = $source->getConfiguration(); - if ($cacheBust) + $url = $source->getUrlToPhoto($this, $thumbnailName); + + // Cache busting doesn't work with S3 signed URLs + if ($cacheBust && !$sourceConfiguration->s3_signed_urls) { // Append the timestamp of the last update to avoid browser caching $theDate = is_null($this->updated_at) ? $this->created_at : $this->updated_at; diff --git a/database/migrations/2020_04_18_212642_add_s3_signed_urls_column_to_storages_table.php b/database/migrations/2020_04_18_212642_add_s3_signed_urls_column_to_storages_table.php new file mode 100644 index 0000000..af6aff9 --- /dev/null +++ b/database/migrations/2020_04_18_212642_add_s3_signed_urls_column_to_storages_table.php @@ -0,0 +1,32 @@ +boolean('s3_signed_urls'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('storages', function (Blueprint $table) { + $table->dropColumn('s3_signed_urls'); + }); + } +} diff --git a/resources/lang/en/admin.php b/resources/lang/en/admin.php index b844d06..1a8561a 100644 --- a/resources/lang/en/admin.php +++ b/resources/lang/en/admin.php @@ -323,7 +323,10 @@ return [ 'photos' => 'photo|photos', 'users' => 'user|users', ], + 'storage_auth_url_label_help' => 'Leave blank to authenticate with Amazon S3. For an S3-compatible provider, enter your provider\'s authentication URL here.', 'storage_backblaze_access_key_id_help' => 'To use your account\'s master key, enter your account ID here.', + 'storage_s3_signed_urls_help' => 'When enabled, Blue Twilight will upload your photos with a private ACL and will use signed URLs to display the photos to your visitors.', + 'storage_s3_signed_urls_tooltip' => 'This location is set to use private images with signed URLs.', 'storage_title' => 'Storage Locations', 'sysinfo_panel' => 'System information', 'sysinfo_widget' => [ diff --git a/resources/lang/en/forms.php b/resources/lang/en/forms.php index 0e1cc96..14777d8 100644 --- a/resources/lang/en/forms.php +++ b/resources/lang/en/forms.php @@ -113,6 +113,7 @@ return [ 'storage_driver_label' => 'Storage driver:', 'storage_endpoint_url_label' => 'Endpoint URL (leave blank if using Amazon):', 'storage_location_label' => 'Physical location:', + 'storage_s3_signed_urls' => 'Upload files privately and use signed URLs', 'storage_secret_key_label' => 'Secret key:', 'storage_service_name_label' => 'Service name:', 'storage_service_region_label' => 'Region:', diff --git a/resources/views/themes/base/admin/edit_storage.blade.php b/resources/views/themes/base/admin/edit_storage.blade.php index c7d4705..56594cc 100644 --- a/resources/views/themes/base/admin/edit_storage.blade.php +++ b/resources/views/themes/base/admin/edit_storage.blade.php @@ -61,13 +61,13 @@ @include(Theme::viewName('partials.admin_storages_rackspace_options')) @endif -
+ @if ($storage->source == 'BackblazeB2Source')
@include(Theme::viewName('partials.admin_storages_backblaze_b2_options')) -
+ @endif
- @lang('forms.cancel_action') + @lang('forms.cancel_action')
diff --git a/resources/views/themes/base/admin/list_storage.blade.php b/resources/views/themes/base/admin/list_storage.blade.php index 036de62..fe68ddc 100644 --- a/resources/views/themes/base/admin/list_storage.blade.php +++ b/resources/views/themes/base/admin/list_storage.blade.php @@ -39,7 +39,12 @@ @if ($storage->source == 'LocalFilesystemSource'){{ $storage->location }}@endif @if ($storage->source == 'OpenStackSource'){{ $storage->container_name }} - {{ $storage->service_name }}, {{ $storage->service_region }}@endif - @if ($storage->source == 'AmazonS3Source'){{ $storage->container_name }} - {{ $storage->service_region }}@endif + @if ($storage->source == 'AmazonS3Source') + {{ $storage->container_name }} - {{ $storage->service_region }} + @if ($storage->s3_signed_urls) + + @endif + @endif @if ($storage->source == 'RackspaceSource'){{ $storage->container_name }} - {{ $storage->service_region }}@endif @@ -80,4 +85,12 @@ -@endsection \ No newline at end of file +@endsection + +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/themes/base/partials/admin_storages_amazon_s3_options.blade.php b/resources/views/themes/base/partials/admin_storages_amazon_s3_options.blade.php index beadec0..5c92d85 100644 --- a/resources/views/themes/base/partials/admin_storages_amazon_s3_options.blade.php +++ b/resources/views/themes/base/partials/admin_storages_amazon_s3_options.blade.php @@ -55,10 +55,19 @@
+ @lang('admin.storage_auth_url_label_help') @if ($errors->has('auth_url'))
{{ $errors->first('auth_url') }}
@endif +
+ +
+ s3_signed_urls)) checked="checked"@endif> +
\ No newline at end of file