Skip to content

Commit

Permalink
feat: download and get storage files as private (#5192)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin authored Jun 22, 2021
1 parent c3f3eac commit 7fdc445
Show file tree
Hide file tree
Showing 23 changed files with 678 additions and 48 deletions.
2 changes: 1 addition & 1 deletion app/Console/Commands/OneTime/MoveAvatars.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ private function moveContactAvatars($contact)
}
if (! $this->option('dryrun')) {
$avatarFile = $storage->get($avatarFileName);
$newStorage->put($avatarFileName, $avatarFile, 'public');
$newStorage->put($avatarFileName, $avatarFile, config('filesystems.default_visibility'));
}

$this->line(' File pushed: '.$avatarFileName, null, OutputInterface::VERBOSITY_VERBOSE);
Expand Down
5 changes: 4 additions & 1 deletion app/Http/Controllers/ContactsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,10 @@ public function update(Request $request, Contact $contact)
}
$contact->has_avatar = true;
$contact->avatar_location = config('filesystems.default');
$contact->avatar_file_name = $request->avatar->storePublicly('avatars', $contact->avatar_location);
$contact->avatar_file_name = $request->file('avatar')->store('avatars', [
'disk' => $contact->avatar_location,
'visibility' => config('filesystems.default_visibility'),
]);
$contact->save();
}

Expand Down
136 changes: 136 additions & 0 deletions app/Http/Controllers/StorageController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace App\Http\Controllers;

use Illuminate\Support\Str;
use Illuminate\Http\Request;
use App\Models\Account\Photo;
use App\Helpers\StorageHelper;
use Illuminate\Support\Carbon;
use App\Models\Contact\Contact;
use App\Models\Contact\Document;
use Illuminate\Support\Facades\Response;
use League\Flysystem\FileNotFoundException;

class StorageController extends Controller
{
public function __construct()
{
$this->middleware(['setEtag', 'ifMatch', 'ifNoneMatch']);
}

/**
* Download file with authorization.
*
* @param Request $request
* @param string $file
* @return \Illuminate\Http\Response|\Symfony\Component\HttpFoundation\StreamedResponse|null
*/
public function show(Request $request, string $file)
{
$filename = $this->getFilename($request, $file);

try {
$disk = StorageHelper::disk(config('filesystems.default'));

$lastModified = Carbon::createFromTimestamp($disk->lastModified($file), 'UTC')->locale('en');

$headers = [
'Last-Modified' => $lastModified->isoFormat('ddd\, DD MMM YYYY HH\:mm\:ss \G\M\T'),
'Cache-Control' => config('filesystems.default_cache_control'),
];

if (! $this->checkConditions($request, $lastModified)) {
return Response::noContent(304, $headers)->setNotModified();
}

return $disk->response($file, $filename, $headers);
} catch (FileNotFoundException $e) {
abort(404);
}
}

/**
* Get the filename for this file.
*
* @param Request $request
* @param string $file
* @return string
*/
private function getFilename(Request $request, string $file): string
{
$accountId = $request->user()->account_id;
$folder = Str::before($file, '/');

switch ($folder) {
case 'avatars':
$obj = Contact::where([
'account_id' => $accountId,
['avatar_default_url', 'like', "$file%"],
])->first();
$filename = Str::after($file, '/');
break;

case 'photos':
$obj = Photo::where([
'account_id' => $accountId,
'new_filename' => $file,
])->first();
$filename = $obj ? $obj->original_filename : null;
break;

case 'documents':
$obj = Document::where([
'account_id' => $accountId,
'new_filename' => $file,
])->first();
$filename = $obj ? $obj->original_filename : null;
break;

default:
$obj = false;
$filename = null;
break;
}

if ($obj === false || $obj === null || ! $obj->exists) {
abort(404);
}

return $filename;
}

/**
* Check for If-Modified-Since and If-Unmodified-Since conditions.
* Return true if the condition does not match.
*
* @param Request $request
* @param Carbon $lastModified Last modified date
* @return bool
*/
private function checkConditions(Request $request, Carbon $lastModified): bool
{
if (! $request->header('If-None-Match') && ($ifModifiedSince = $request->header('If-Modified-Since'))) {
// The If-Modified-Since header contains a date. We will only
// return the entity if it has been changed since that date.
$date = Carbon::parse($ifModifiedSince);

if ($lastModified->lessThanOrEqualTo($date)) {
return false;
}
}

if ($ifUnmodifiedSince = $request->header('If-Unmodified-Since')) {
// The If-Unmodified-Since will allow the request if the
// entity has not changed since the specified date.
$date = Carbon::parse($ifUnmodifiedSince);

// We must only check the date if it's valid
if ($lastModified->greaterThan($date)) {
abort(412, 'An If-Unmodified-Since header was specified, but the entity has been changed since the specified date.');
}
}

return true;
}
}
2 changes: 1 addition & 1 deletion app/Jobs/Avatars/MoveContactAvatarToPhotosDirectory.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ private function moveContactAvatars(): ?string

if (! $this->dryrun) {
$avatarFile = $this->storage->get($avatarFileName);
$newStorage->put($newAvatarFilename, $avatarFile, 'public');
$newStorage->put($newAvatarFilename, $avatarFile, config('filesystems.default_visibility'));

$this->contact->avatar_location = config('filesystems.default');
$this->contact->save();
Expand Down
2 changes: 1 addition & 1 deletion app/Jobs/ResizeAvatars.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,6 @@ private function resize($avatarFile, $filename, $extension, $storage, $size)
$avatar = Image::make($avatarFile);
$avatar->fit($size);

$storage->put($avatarFileName, (string) $avatar->stream(), 'public');
$storage->put($avatarFileName, (string) $avatar->stream(), config('filesystems.default_visibility'));
}
}
6 changes: 4 additions & 2 deletions app/Models/Account/Photo.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@ public function contact()
*/
public function url()
{
$url = $this->new_filename;
if (config('filesystems.default_visibility') === 'public') {
return asset(StorageHelper::disk(config('filesystems.default'))->url($this->new_filename));
}

return asset(StorageHelper::disk(config('filesystems.default'))->url($url));
return route('storage', ['file' => $this->new_filename]);
}

/**
Expand Down
12 changes: 6 additions & 6 deletions app/Models/Contact/Contact.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use App\Helpers\LocaleHelper;
use App\Models\Account\Photo;
use App\Models\Journal\Entry;
use function Safe\preg_split;
use App\Helpers\StorageHelper;
use App\Helpers\WeatherHelper;
use Illuminate\Support\Carbon;
Expand Down Expand Up @@ -1049,17 +1048,18 @@ public function getAvatarDefaultURL()
return '';
}

try {
$matches = preg_split('/\?/', $this->avatar_default_url);
if (config('filesystems.default_visibility') === 'public') {
$matches = Str::of($this->avatar_default_url)->split('/\?/');

$url = asset(StorageHelper::disk(config('filesystems.default'))->url($matches[0]));
if (count($matches) > 1) {
if ($matches->count() > 1) {
$url .= '?'.$matches[1];
}

return $url;
} catch (\Exception $e) {
return '';
}

return route('storage', ['file' => $this->avatar_default_url]);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions app/Models/Contact/Document.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@ public function contact()
*/
public function getDownloadLink(): string
{
$url = $this->new_filename;
if (config('filesystems.default_visibility') === 'public') {
return asset(StorageHelper::disk(config('filesystems.default'))->url($this->new_filename));
}

return asset(StorageHelper::disk(config('filesystems.default'))->url($url));
return route('storage', ['file' => $this->new_filename]);
}
}
10 changes: 10 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\View;
use App\Notifications\EmailMessaging;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Auth\Notifications\VerifyEmail;
use Werk365\EtagConditionals\EtagConditionals;
use Illuminate\Auth\Notifications\ResetPassword;

class AppServiceProvider extends ServiceProvider
Expand Down Expand Up @@ -74,6 +76,14 @@ public function boot()
Limit::perDay(5000),
];
});

EtagConditionals::etagGenerateUsing(function (\Illuminate\Http\Request $request, \Symfony\Component\HttpFoundation\Response $response) {
$url = $request->getRequestUri();

return Cache::rememberForever('etag.'.$url, function () use ($url) {
return md5($url);
});
});
}

/**
Expand Down
7 changes: 5 additions & 2 deletions app/Services/Account/Photo/UploadPhoto.php
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ private function importPhoto($data): array
'original_filename' => $photo->getClientOriginalName(),
'filesize' => $photo->getSize(),
'mime_type' => (new \Mimey\MimeTypes)->getMimeType($photo->guessClientExtension()),
'new_filename' => $photo->storePublicly('photos', config('filesystems.default')),
'new_filename' => $photo->store('photos', [
'disk' => config('filesystems.default'),
'visibility' => config('filesystems.default_visibility'),
]),
];
}

Expand Down Expand Up @@ -146,7 +149,7 @@ private function importFile(array $data): ?array
private function storeImage(string $disk, $image, string $filename): ?string
{
$result = Storage::disk($disk)
->put($path = $filename, (string) $image->stream(), 'public');
->put($path = $filename, (string) $image->stream(), config('filesystems.default_visibility'));

return $result ? $path : null;
}
Expand Down
5 changes: 4 additions & 1 deletion app/Services/Contact/Avatar/GenerateDefaultAvatar.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\Str;
use App\Services\BaseService;
use App\Models\Contact\Contact;
use Illuminate\Support\Facades\Cache;
use Laravolt\Avatar\Facade as Avatar;
use Illuminate\Support\Facades\Storage;
use Illuminate\Contracts\Filesystem\FileNotFoundException;
Expand Down Expand Up @@ -47,6 +48,8 @@ public function execute(array $data)
$contact->avatar_default_url = $filename;
$contact->save();

Cache::forget('etag'.Str::before('?', $filename));

return $contact;
}

Expand Down Expand Up @@ -83,7 +86,7 @@ private function createNewAvatar(Contact $contact)

$filename = 'avatars/'.$contact->uuid.'.jpg';
Storage::disk(config('filesystems.default'))
->put($filename, $img, 'public');
->put($filename, $img, config('filesystems.default_visibility'));

// This will force the browser to reload the new avatar
return $filename.'?'.now()->format('U');
Expand Down
5 changes: 4 additions & 1 deletion app/Services/Contact/Document/UploadDocument.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ private function populateData($data)
'mime_type' => (new \Mimey\MimeTypes)->getMimeType($document->guessClientExtension()),
];

$filename = $document->storePublicly('documents', config('filesystems.default'));
$filename = $document->store('documents', [
'disk' => config('filesystems.default'),
'visibility' => config('filesystems.default_visibility'),
]);

return array_merge($data, [
'new_filename' => $filename,
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"web-token/jwt-signature-algorithm-ecdsa": "^2.1",
"web-token/jwt-signature-algorithm-eddsa": "^2.1",
"web-token/jwt-signature-algorithm-rsa": "^2.1",
"werk365/etagconditionals": "^1.2",
"xantios/mimey": "^2.0"
},
"require-dev": {
Expand Down
Loading

0 comments on commit 7fdc445

Please sign in to comment.