diff --git a/app/Http/Controllers/App/AppFunctionsController.php b/app/Http/Controllers/App/AppFunctionsController.php index dc910c14..030cc03a 100644 --- a/app/Http/Controllers/App/AppFunctionsController.php +++ b/app/Http/Controllers/App/AppFunctionsController.php @@ -88,7 +88,7 @@ class AppFunctionsController extends Controller return view("vuefilemanager.crawler.og-view") ->with('settings', get_settings_in_json()) ->with('metadata', [ - 'url' => url('/shared', ['token' => $shared->token]), + 'url' => url('/share', ['token' => $shared->token]), 'is_protected' => $shared->is_protected, 'user' => $shared->user->settings->name, 'name' => $item->name, diff --git a/app/Http/Controllers/FileManager/BrowseController.php b/app/Http/Controllers/FileManager/BrowseController.php index 57d69ddc..7775ce60 100644 --- a/app/Http/Controllers/FileManager/BrowseController.php +++ b/app/Http/Controllers/FileManager/BrowseController.php @@ -199,7 +199,7 @@ class BrowseController extends Controller { $user_id = Auth::id(); - $query = remove_accents($request->input('query')); + $query = remove_accents($request->query); // Search files id db $searched_files = File::search($query) diff --git a/app/Http/Controllers/Sharing/BrowseShareController.php b/app/Http/Controllers/Sharing/BrowseShareController.php index 8861b6b4..ab2d2510 100644 --- a/app/Http/Controllers/Sharing/BrowseShareController.php +++ b/app/Http/Controllers/Sharing/BrowseShareController.php @@ -3,6 +3,9 @@ namespace App\Http\Controllers\Sharing; use App\Http\Controllers\Controller; +use App\Http\Requests\Share\AuthenticateShareRequest; +use App\Http\Resources\FileResource; +use App\Http\Resources\ShareResource; use App\Models\File; use App\Models\Folder; use App\Models\Share; @@ -10,6 +13,8 @@ use App\Services\HelperService; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Storage; class BrowseShareController extends Controller { @@ -20,6 +25,64 @@ class BrowseShareController extends Controller $this->helper = resolve(HelperService::class); } + /** + * Show page index and delete access_token & shared_token cookie + * @param Share $shared + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Symfony\Component\HttpFoundation\StreamedResponse + */ + public function index(Share $shared) + { + // Delete share_session if exist + if ($shared->is_protected) { + cookie()->queue('share_session', '', -1); + } + + // Check if shared is image file and then show it + if ($shared->type === 'file' && !$shared->is_protected) { + + $image = File::whereUserId($shared->user_id) + ->whereType('image') + ->whereId($shared->item_id) + ->firstOrFail(); + + // Store user download size + $shared + ->user + ->record_download( + (int)$image->getRawOriginal('filesize') + ); + + return $this->get_single_image($image, $shared->user_id); + } + + return view("index") + ->with('settings', get_settings_in_json() ?? null); + } + + /** + * Check Password for protected item + * @param AuthenticateShareRequest $request + * @param Share $shared + * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response + */ + public function authenticate(AuthenticateShareRequest $request, Share $shared) + { + // Check password + if (Hash::check($request->password, $shared->password)) { + + $cookie = json_encode([ + 'token' => $shared->token, + 'authenticated' => true, + ]); + + // Return authorize token with shared options + return response(new ShareResource($shared), 200) + ->cookie('share_session', $cookie, 43200); + } + + abort(401, __('vuefilemanager.incorrect_password')); + } + /** * Browse public folders * @@ -60,11 +123,15 @@ class BrowseShareController extends Controller // Check ability to access protected share record $this->helper->check_protected_share_record($shared); + $query = remove_accents( + $request->input('query') + ); + // Search files id db - $searched_files = File::search($request->input('query')) + $searched_files = File::search($query) ->where('user_id', $shared->user_id) ->get(); - $searched_folders = Folder::search($request->input('query')) + $searched_folders = Folder::search($query) ->where('user_id', $shared->user_id) ->get(); @@ -95,7 +162,8 @@ class BrowseShareController extends Controller }); // Collect folders and files to single array - return collect([$folders, $files])->collapse(); + return collect([$folders, $files]) + ->collapse(); } /** @@ -114,8 +182,8 @@ class BrowseShareController extends Controller // Get folders $folders = Folder::with('folders:id,parent_id,name') - ->where('parent_id', $shared->item_id) - ->where('user_id', $shared->user_id) + ->whereParentId($shared->item_id) + ->whereUserId($shared->user_id) ->sortable() ->get(['id', 'parent_id', 'id', 'name']); @@ -128,4 +196,52 @@ class BrowseShareController extends Controller ] ]; } + + /** + * Get shared public file record + * + * @param Share $shared + * @return mixed + */ + public function get_single_file(Share $shared) + { + // Check ability to access protected share files + $this->helper->check_protected_share_record($shared); + + // Get file + $file = File::whereUserId($shared->user_id) + ->whereId($shared->item_id) + ->firstOrFail(); + + // Set access urls + $file->setPublicUrl($shared->token); + + return response(new FileResource($file), 200); + } + + /** + * Get image from storage and show it + * + * @param $file + * @param $user_id + * @return \Symfony\Component\HttpFoundation\StreamedResponse + */ + private function get_single_image($file, $user_id) + { + // Format pretty filename + $file_pretty_name = $file->name . '.' . $file->mimetype; + + // Get file path + $path = "/files/$user_id/$file->basename"; + + // Check if file exist + if (!Storage::exists($path)) abort(404); + + return Storage::response($path, $file_pretty_name, [ + "Content-Type" => Storage::mimeType($path), + "Content-Length" => Storage::size($path), + "Accept-Ranges" => "bytes", + "Content-Range" => "bytes 0-600/" . Storage::size($path), + ]); + } } diff --git a/app/Http/Controllers/Sharing/EditShareItemsController.php b/app/Http/Controllers/Sharing/EditShareItemsController.php index aa25a95b..cf9ab5e0 100644 --- a/app/Http/Controllers/Sharing/EditShareItemsController.php +++ b/app/Http/Controllers/Sharing/EditShareItemsController.php @@ -228,6 +228,9 @@ class EditShareItemsController extends Controller */ public function zip_folder($id, Share $shared) { + // Check ability to access protected share record + $this->helper->check_protected_share_record($shared); + // Check access to requested folder $this->helper->check_item_access($id, $shared); @@ -261,6 +264,9 @@ class EditShareItemsController extends Controller */ public function zip_multiple_files(Request $request, Share $shared) { + // Check ability to access protected share record + $this->helper->check_protected_share_record($shared); + $file_parent_folders = File::whereUserId($shared->user_id) ->whereIn('id', $request->items) ->get() diff --git a/app/Http/Controllers/Sharing/ServeSharedController.php b/app/Http/Controllers/Sharing/ServeSharedController.php deleted file mode 100644 index 0e9869e9..00000000 --- a/app/Http/Controllers/Sharing/ServeSharedController.php +++ /dev/null @@ -1,135 +0,0 @@ -helper = resolve(HelperService::class); - } - - /** - * Show page index and delete access_token & shared_token cookie - * - * @param Share $shared - * @return \Illuminate\Http\Response - */ - public function index(Share $shared) - { - // Delete share_session if exist - if ($shared->is_protected) { - cookie()->queue('share_session', '', -1); - } - - // Check if shared is image file and then show it - if ($shared->type === 'file' && !$shared->is_protected) { - - $image = File::whereUserId($shared->user_id) - ->whereType('image') - ->whereId($shared->item_id) - ->firstOrFail(); - - // Store user download size - $shared - ->user - ->record_download( - (int)$image->getRawOriginal('filesize') - ); - - return $this->show_image($image, $shared->user_id); - } - - return view("index") - ->with('settings', get_settings_in_json() ?? null); - } - - /** - * Check Password for protected item - * - * @param AuthenticateShareRequest $request - * @param Share $shared - * @return array|\Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response - */ - public function authenticate(AuthenticateShareRequest $request, Share $shared) - { - // Check password - if (!Hash::check($request->password, $shared->password)) { - abort(401, __('vuefilemanager.incorrect_password')); - } - - // Return authorize token with shared options - return response(new ShareResource($shared), 200) - ->cookie('share_session', json_encode([ - 'token' => $shared->token, - 'authenticated' => true, - ]), 43200); - } - - /** - * Get image from storage and show it - * - * @param $file - * @param $user_id - * @return \Symfony\Component\HttpFoundation\StreamedResponse - */ - private function show_image($file, $user_id) - { - // Format pretty filename - $file_pretty_name = $file->name . '.' . $file->mimetype; - - // Get file path - $path = "/files/$user_id/$file->basename"; - - // Check if file exist - if (!Storage::exists($path)) abort(404); - - return Storage::response($path, $file_pretty_name, [ - "Content-Type" => Storage::mimeType($path), - "Content-Length" => Storage::size($path), - "Accept-Ranges" => "bytes", - "Content-Range" => "bytes 0-600/" . Storage::size($path), - ]); - } - - /** - * Get shared public file record - * - * @param Share $shared - * @return mixed - */ - public function file_public(Share $shared) - { - // Check ability to access protected share files - $this->helper->check_protected_share_record($shared); - - // Get file - $file = File::where('user_id', $shared->user_id) - ->where('id', $shared->item_id) - ->firstOrFail(); - - // Set access urls - $file->setPublicUrl($shared->token); - - return response(new FileResource($file), 200); - } -} diff --git a/app/Models/File.php b/app/Models/File.php index 0db56450..0a816d00 100644 --- a/app/Models/File.php +++ b/app/Models/File.php @@ -12,6 +12,10 @@ use TeamTNT\TNTSearch\Indexer\TNTIndexer; use \Illuminate\Database\Eloquent\SoftDeletes; use Kyslik\ColumnSortable\Sortable; +/** + * @method static whereUserId($user_id) + * @method static whereId($id) + */ class File extends Model { use Searchable, SoftDeletes, Sortable, HasFactory; diff --git a/app/Models/Share.php b/app/Models/Share.php index fec8904c..a521ac92 100644 --- a/app/Models/Share.php +++ b/app/Models/Share.php @@ -32,7 +32,7 @@ class Share extends Model */ public function getLinkAttribute() { - return url('/shared', ['token' => $this->attributes['token']]); + return url('/share', ['token' => $this->attributes['token']]); } public function user() diff --git a/app/Services/HelperService.php b/app/Services/HelperService.php index 42d8d9ba..beb52c7d 100644 --- a/app/Services/HelperService.php +++ b/app/Services/HelperService.php @@ -317,7 +317,7 @@ class HelperService abort(403, $abort_message); } - // Check if share record was authenticated previously via ServeSharedController@authenticate + // Check if share record was authenticated previously via ShareController@authenticate if (!$share_session->authenticated) { abort(403, $abort_message); } diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 1964ef38..cc1981a2 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -96,5 +96,6 @@ "/js/main.7e6541d1cbf9901a3e5a.hot-update.js": "/js/main.7e6541d1cbf9901a3e5a.hot-update.js", "/js/main.51418924870a080afea3.hot-update.js": "/js/main.51418924870a080afea3.hot-update.js", "/js/main.869d3fad73dd6fe243f9.hot-update.js": "/js/main.869d3fad73dd6fe243f9.hot-update.js", - "/js/main.d7f2bec845fc64b7d5e4.hot-update.js": "/js/main.d7f2bec845fc64b7d5e4.hot-update.js" + "/js/main.d7f2bec845fc64b7d5e4.hot-update.js": "/js/main.d7f2bec845fc64b7d5e4.hot-update.js", + "/js/main.c788f898c265d85a0089.hot-update.js": "/js/main.c788f898c265d85a0089.hot-update.js" } diff --git a/resources/js/router.js b/resources/js/router.js index 7d7e4102..c5726afb 100644 --- a/resources/js/router.js +++ b/resources/js/router.js @@ -309,7 +309,7 @@ const routesAdmin = [ const routesShared = [ { name: 'Shared', - path: '/shared/:token', + path: '/share/:token', component: () => import(/* webpackChunkName: "chunks/shared" */ './views/Shared'), meta: { @@ -318,7 +318,7 @@ const routesShared = [ children: [ { name: 'SharedFileBrowser', - path: '/shared/:token/files', + path: '/share/:token/files', component: () => import(/* webpackChunkName: "chunks/shared/file-browser" */ './views/Shared/SharedFileBrowser'), meta: { @@ -327,7 +327,7 @@ const routesShared = [ }, { name: 'SharedSingleFile', - path: '/shared/:token/file', + path: '/share/:token/file', component: () => import(/* webpackChunkName: "chunks/shared/single-file" */ './views/Shared/SharedSingleFile'), meta: { @@ -336,7 +336,7 @@ const routesShared = [ }, { name: 'SharedAuthentication', - path: '/shared/:token/authenticate', + path: '/share/:token/authenticate', component: () => import(/* webpackChunkName: "chunks/shared/authenticate" */ './views/Shared/SharedAuthentication'), meta: { diff --git a/resources/js/store/modules/fileBrowser.js b/resources/js/store/modules/fileBrowser.js index 8ee33f3f..f3b1f07a 100644 --- a/resources/js/store/modules/fileBrowser.js +++ b/resources/js/store/modules/fileBrowser.js @@ -97,7 +97,7 @@ const actions = { commit('STORE_CURRENT_FOLDER', currentFolder) axios - .get(getters.api + '/browse/shared' + getters.sorting.URI) + .get(getters.api + '/browse/share' + getters.sorting.URI) .then(response => { commit('LOADING_STATE', {loading: false, data: response.data}) commit('STORE_PREVIOUS_FOLDER', currentFolder) diff --git a/routes/api.php b/routes/api.php index 633e5f62..5bd3561c 100644 --- a/routes/api.php +++ b/routes/api.php @@ -29,7 +29,7 @@ Route::group(['middleware' => ['auth:sanctum']], function () { Route::get('/participants', [BrowseController::class, 'participant_uploads']); Route::get('/navigation', [BrowseController::class, 'navigation_tree']); Route::get('/folders/{id}', [BrowseController::class, 'folder']); - Route::get('/shared', [BrowseController::class, 'shared']); + Route::get('/share', [BrowseController::class, 'shared']); Route::get('/latest', [BrowseController::class, 'latest']); Route::get('/search', [BrowseController::class, 'search']); Route::get('/trash', [BrowseController::class, 'trash']); diff --git a/routes/share.php b/routes/share.php index 76f8175c..3b02864f 100644 --- a/routes/share.php +++ b/routes/share.php @@ -4,7 +4,6 @@ use App\Http\Controllers\App\AppFunctionsController; use App\Http\Controllers\Sharing\BrowseShareController; use App\Http\Controllers\Sharing\EditShareItemsController; use App\Http\Controllers\FileManager\ShareController; -use App\Http\Controllers\Sharing\ServeSharedController; // Browse functions Route::group(['prefix' => 'editor'], function () { @@ -23,12 +22,11 @@ Route::group(['prefix' => 'zip'], function () { // Browse share content Route::group(['prefix' => 'browse'], function () { + Route::post('/authenticate/{shared}', [BrowseShareController::class, 'authenticate']); Route::get('/navigation/{shared}', [BrowseShareController::class, 'navigation_tree']); Route::get('/folders/{id}/{shared}', [BrowseShareController::class, 'browse_folder']); + Route::get('/file/{shared}', [BrowseShareController::class, 'get_single_file']); Route::get('/search/{shared}', [BrowseShareController::class, 'search']); - - Route::post('/authenticate/{shared}', [ServeSharedController::class, 'authenticate']); - Route::get('/file/{shared}', [ServeSharedController::class, 'file_public']); Route::get('/share/{shared}', [ShareController::class, 'show']); }); diff --git a/routes/web.php b/routes/web.php index be325564..735667a6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,7 +3,7 @@ use App\Http\Controllers\Admin\InvoiceController; use App\Http\Controllers\App\SetupWizardController; use App\Http\Controllers\App\AppFunctionsController; -use App\Http\Controllers\Sharing\ServeSharedController; +use App\Http\Controllers\Sharing\BrowseShareController; use App\Http\Controllers\Subscription\StripeWebhookController; Route::post('/stripe/webhook', [StripeWebhookController::class, 'handleWebhook']); @@ -14,9 +14,9 @@ Route::get('/invoice/{customer}/{token}', [InvoiceController::class, 'show'])->m // Get og site for web crawlers if (Crawler::isCrawler()) { - Route::get('/shared/{shared}', [AppFunctionsController::class, 'og_site']); + Route::get('/share/{shared}', [AppFunctionsController::class, 'og_site']); } else { - Route::get('/shared/{shared}', [ServeSharedController::class, 'index']); + Route::get('/share/{shared}', [BrowseShareController::class, 'index']); } // Show index.blade diff --git a/tests/Feature/FileManager/BrowseTest.php b/tests/Feature/FileManager/BrowseTest.php index 85f62779..30305f79 100644 --- a/tests/Feature/FileManager/BrowseTest.php +++ b/tests/Feature/FileManager/BrowseTest.php @@ -433,7 +433,7 @@ class BrowseTest extends TestCase collect([$folder, $file]) ->each(function ($item) use ($user) { - $this->getJson("/api/browse/shared") + $this->getJson("/api/browse/share") ->assertStatus(200) ->assertJsonFragment([ 'id' => $item->id diff --git a/tests/Feature/Share/PrivateFilesAccessTest.php b/tests/Feature/Share/PrivateFilesAccessTest.php deleted file mode 100644 index 5ab9e67e..00000000 --- a/tests/Feature/Share/PrivateFilesAccessTest.php +++ /dev/null @@ -1,71 +0,0 @@ -setup = app()->make(SetupService::class); - } - - /** - * @test - */ - public function it_authenticate_protected_file_with_correct_password() - { - $file = File::factory(File::class) - ->create(); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $file->id, - 'user_id' => $file->user_id, - 'type' => 'file', - 'is_protected' => true, - 'password' => \Hash::make('secret'), - ]); - - $this->postJson("/api/browse/authenticate/$share->token", [ - 'password' => 'secret' - ]) - ->assertStatus(200) - ->assertCookie('share_session', json_encode([ - 'token' => $share->token, - 'authenticated' => true, - ]), false); - } - - /** - * @test - */ - public function it_authenticate_protected_file_with_incorrect_password() - { - $file = File::factory(File::class) - ->create(); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $file->id, - 'user_id' => $file->user_id, - 'type' => 'file', - 'is_protected' => true, - 'password' => \Hash::make('secret'), - ]); - - $this->postJson("/api/browse/authenticate/$share->token", [ - 'password' => 'bad-password' - ]) - ->assertStatus(401) - ->assertCookieMissing('share_session'); - } -} diff --git a/tests/Feature/Share/PrivateVisitorTest.php b/tests/Feature/Share/PrivateVisitorTest.php deleted file mode 100644 index 0b6a7a9c..00000000 --- a/tests/Feature/Share/PrivateVisitorTest.php +++ /dev/null @@ -1,106 +0,0 @@ -setup = app()->make(SetupService::class); - } - - /** - * @test - */ - public function authenticated_visitor_get_folder_content() - { - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'name' => 'root', - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $root->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => true, - 'permission' => 'editor', - ]); - - $folder = Folder::factory(Folder::class) - ->create([ - 'parent_id' => $root->id, - 'name' => 'Documents', - "user_scope" => "master", - 'user_id' => $user->id, - ]); - - $file = File::factory(File::class) - ->create([ - 'folder_id' => $root->id, - 'name' => 'Document', - 'basename' => 'document.pdf', - "mimetype" => "application/pdf", - "user_scope" => "master", - "type" => "file", - 'user_id' => $user->id, - ]); - - $this->withUnencryptedCookie('share_session', json_encode([ - 'token' => $share->token, - 'authenticated' => true, - ])) - ->get("/api/browse/folders/$root->id/private/$share->token") - ->assertStatus(200) - ->assertExactJson([ - [ - "id" => $folder->id, - "user_id" => $user->id, - "parent_id" => $root->id, - "name" => "Documents", - "color" => null, - "emoji" => null, - "user_scope" => "master", - "deleted_at" => null, - "created_at" => $folder->created_at, - "updated_at" => $folder->updated_at->toJson(), - "items" => 0, - "trashed_items" => 0, - "type" => "folder", - ], - [ - "id" => $file->id, - "user_id" => $user->id, - "folder_id" => $root->id, - "thumbnail" => null, - "name" => "Document", - "basename" => "document.pdf", - "mimetype" => "application/pdf", - "filesize" => $file->filesize, - "type" => "file", - "metadata" => null, - "user_scope" => "master", - "deleted_at" => null, - "created_at" => $file->created_at, - "updated_at" => $file->updated_at->toJson(), - "file_url" => "http://localhost/file/document.pdf/private/$share->token", - ] - ]); - } -} diff --git a/tests/Feature/Share/PublicVisitorTest.php b/tests/Feature/Share/PublicVisitorTest.php deleted file mode 100644 index b26dde67..00000000 --- a/tests/Feature/Share/PublicVisitorTest.php +++ /dev/null @@ -1,784 +0,0 @@ -setup = app()->make(SetupService::class); - } - - /** - * @test - */ - public function editor_rename_shared_file() - { - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $file = File::factory(File::class) - ->create([ - 'folder_id' => $folder->id - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $this->patchJson("/api/editor/rename/{$file->id}/public/$share->token", [ - 'name' => 'Renamed Item', - 'type' => 'file', - ]) - ->assertStatus(201) - ->assertJsonFragment([ - 'name' => 'Renamed Item', - ]); - - $this->assertDatabaseHas('files', [ - 'name' => 'Renamed Item' - ]); - } - - /** - * @test - */ - public function editor_rename_shared_folder() - { - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $children = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'parent_id' => $root->id - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $root->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $this->patchJson("/api/editor/rename/{$children->id}/public/$share->token", [ - 'name' => 'Renamed Folder', - 'type' => 'folder', - ]) - ->assertStatus(201) - ->assertJsonFragment([ - 'name' => 'Renamed Folder', - ]); - - $this->assertDatabaseHas('folders', [ - 'name' => 'Renamed Folder' - ]); - } - - /** - * @test - */ - public function editor_create_new_folder_in_shared_folder() - { - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $this->postJson("/api/editor/create-folder/public/$share->token", [ - 'name' => 'Awesome New Folder', - 'parent_id' => $folder->id, - ]) - ->assertStatus(201) - ->assertJsonFragment([ - 'name' => 'Awesome New Folder', - ]); - - $this->assertDatabaseHas('folders', [ - 'name' => 'Awesome New Folder', - 'parent_id' => $folder->id, - 'user_scope' => 'editor', - ]); - } - - /** - * @test - */ - public function editor_delete_multiple_files_in_shared_folder() - { - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $files = File::factory(File::class) - ->count(2) - ->create([ - 'folder_id' => $folder->id - ]); - - $this->postJson("/api/editor/remove/public/$share->token", [ - 'items' => [ - [ - 'id' => $files[0]->id, - 'type' => 'file', - 'force_delete' => false, - ], - [ - 'id' => $files[1]->id, - 'type' => 'file', - 'force_delete' => false, - ], - ], - ])->assertStatus(204); - - $files - ->each(function ($file) { - $this->assertSoftDeleted('files', [ - 'id' => $file->id, - ]); - }); - } - - /** - * @test - */ - public function editor_upload_file_into_shared_folder() - { - Storage::fake('local'); - - $this->setup->create_directories(); - - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'user_scope' => 'master', - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $file = UploadedFile::fake() - ->create('fake-file.pdf', 1000, 'application/pdf'); - - $this->postJson("/api/editor/upload/public/$share->token", [ - 'file' => $file, - 'folder_id' => $folder->id, - 'is_last' => true, - ])->assertStatus(201); - - $this->assertDatabaseHas('traffic', [ - 'user_id' => $user->id, - ]); - - $this->assertDatabaseHas('files', [ - 'user_scope' => 'editor', - ]); - - Storage::disk('local') - ->assertExists( - "files/$user->id/fake-file.pdf" - ); - } - - /** - * @test - */ - public function editor_move_file_to_another_folder() - { - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $children = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'parent_id' => $root->id, - ]); - - $file = File::factory(File::class) - ->create([ - 'user_id' => $user->id, - 'folder_id' => $root->id - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $root->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $this->postJson("/api/editor/move/public/$share->token", [ - 'to_id' => $children->id, - 'items' => [ - [ - 'type' => 'file', - 'id' => $file->id, - ] - ], - ])->assertStatus(204); - - $this->assertDatabaseHas('files', [ - 'id' => $file->id, - 'folder_id' => $children->id, - ]); - } - - /** - * @test - */ - public function editor_move_folder_to_another_folder() - { - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $brother = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'parent_id' => $root->id, - ]); - - $sister = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'parent_id' => $root->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $root->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $this->postJson("/api/editor/move/public/$share->token", [ - 'to_id' => $brother->id, - 'items' => [ - [ - 'type' => 'folder', - 'id' => $sister->id, - ] - ], - ])->assertStatus(204); - - $this->assertDatabaseHas('folders', [ - 'id' => $sister->id, - 'parent_id' => $brother->id, - ]); - } - - /** - * @test - */ - public function guest_zip_shared_multiple_files() - { - Storage::fake('local'); - - $this->setup->create_directories(); - - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - collect([0, 1]) - ->each(function ($index) use ($folder, $user) { - - $file = UploadedFile::fake() - ->create(Str::random() . "-fake-file-$index.pdf", 1000, 'application/pdf'); - - Storage::putFileAs("files/$user->id", $file, $file->name); - - File::factory(File::class) - ->create([ - 'filesize' => $file->getSize(), - 'folder_id' => $folder->id, - 'user_id' => $user->id, - 'basename' => $file->name, - 'name' => "fake-file-$index.pdf", - ]); - }); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - ]); - - $this->postJson("/api/zip/files/public/$share->token", [ - 'items' => File::all()->pluck('id') - ])->assertStatus(201); - - $this->assertDatabaseHas('zips', [ - 'user_id' => $user->id, - 'shared_token' => $share->token, - ]); - - Storage::assertExists("zip/" . Zip::first()->basename); - } - - /** - * @test - */ - public function guest_try_zip_non_shared_file_with_already_shared_multiple_files() - { - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - File::factory(File::class) - ->create([ - 'folder_id' => $folder->id, - 'user_id' => $user->id, - ]); - - File::factory(File::class) - ->create([ - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - ]); - - $this->postJson("/api/zip/files/public/$share->token", [ - 'items' => File::all()->pluck('id') - ])->assertStatus(403); - } - - /** - * @test - */ - public function guest_zip_shared_folder() - { - Storage::fake('local'); - - $this->setup->create_directories(); - - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $children = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id, - 'parent_id' => $root->id - ]); - - collect([0, 1]) - ->each(function ($index) use ($children, $user) { - - $file = UploadedFile::fake() - ->create(Str::random() . "-fake-file-$index.pdf", 1000, 'application/pdf'); - - Storage::putFileAs("files/$user->id", $file, $file->name); - - File::factory(File::class) - ->create([ - 'filesize' => $file->getSize(), - 'folder_id' => $children->id, - 'user_id' => $user->id, - 'basename' => $file->name, - 'name' => "fake-file-$index.pdf", - ]); - }); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $children->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - ]); - - $this->getJson("/api/zip/folder/$children->id/public/$share->token") - ->assertStatus(201); - - $this->assertDatabaseHas('zips', [ - 'user_id' => $user->id, - 'shared_token' => $share->token, - ]); - - Storage::assertExists("zip/" . Zip::first()->basename); - } - - /** - * @test - */ - public function guest_try_zip_non_shared_folder() - { - Storage::fake('local'); - - $this->setup->create_directories(); - - $user = User::factory(User::class) - ->create(); - - $folder = Folder::factory(Folder::class) - ->create([ - 'user_id' => $user->id - ]); - - $share = Share::factory(Share::class) - ->create([ - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - ]); - - $this->getJson("/api/zip/folder/$folder->id/public/$share->token") - ->assertStatus(403); - } - - /** - * @test - */ - public function guest_get_folder_content() - { - $user = User::factory(User::class) - ->create(); - - $root = Folder::factory(Folder::class) - ->create([ - 'name' => 'root', - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $root->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $folder = Folder::factory(Folder::class) - ->create([ - 'parent_id' => $root->id, - 'name' => 'Documents', - "user_scope" => "master", - 'user_id' => $user->id, - ]); - - $file = File::factory(File::class) - ->create([ - 'folder_id' => $root->id, - 'name' => 'Document', - 'basename' => 'document.pdf', - "mimetype" => "application/pdf", - "user_scope" => "master", - "type" => "file", - 'user_id' => $user->id, - ]); - - $this->getJson("/api/browse/folders/$root->id/public/$share->token") - ->assertStatus(200) - ->assertExactJson([ - [ - "id" => $folder->id, - "user_id" => $user->id, - "parent_id" => $root->id, - "name" => "Documents", - "color" => null, - "emoji" => null, - "user_scope" => "master", - "deleted_at" => null, - "created_at" => $folder->created_at, - "updated_at" => $folder->updated_at->toJson(), - "items" => 0, - "trashed_items" => 0, - "type" => "folder", - ], - [ - "id" => $file->id, - "user_id" => $user->id, - "folder_id" => $root->id, - "thumbnail" => null, - "name" => "Document", - "basename" => "document.pdf", - "mimetype" => "application/pdf", - "filesize" => $file->filesize, - "type" => "file", - "metadata" => null, - "user_scope" => "master", - "deleted_at" => null, - "created_at" => $file->created_at, - "updated_at" => $file->updated_at->toJson(), - "file_url" => "http://localhost/file/document.pdf/public/$share->token", - ] - ]); - } - - /** - * @test - */ - public function guest_get_navigator_tree() - { - $user = User::factory(User::class) - ->create(); - - $folder_level_1 = Folder::factory(Folder::class) - ->create([ - 'name' => 'level 1', - 'user_scope' => 'master', - 'user_id' => $user->id, - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder_level_1->id, - 'user_id' => $user->id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $folder_level_2 = Folder::factory(Folder::class) - ->create([ - 'name' => 'level 2', - 'parent_id' => $folder_level_1->id, - 'user_scope' => 'master', - 'user_id' => $user->id, - ]); - - $folder_level_3 = Folder::factory(Folder::class) - ->create([ - 'name' => 'level 3', - 'parent_id' => $folder_level_2->id, - 'user_scope' => 'master', - 'user_id' => $user->id, - ]); - - $folder_level_2_sibling = Folder::factory(Folder::class) - ->create([ - 'name' => 'level 2 Sibling', - 'parent_id' => $folder_level_1->id, - 'user_scope' => 'master', - 'user_id' => $user->id, - ]); - - $this->getJson("/api/browse/navigation/public/$share->token") - ->assertStatus(200) - ->assertExactJson([ - [ - 'id' => $share->item_id, - "name" => "Home", - "location" => "public", - "folders" => [ - [ - "id" => $folder_level_2->id, - "parent_id" => $folder_level_1->id, - "name" => "level 2", - "items" => 1, - "trashed_items" => 1, - "type" => "folder", - "folders" => [ - [ - "id" => $folder_level_3->id, - "parent_id" => $folder_level_2->id, - "name" => "level 3", - "items" => 0, - "trashed_items" => 0, - "type" => "folder", - "folders" => [], - ], - ], - ], - [ - "id" => $folder_level_2_sibling->id, - "parent_id" => $folder_level_1->id, - "name" => "level 2 Sibling", - "items" => 0, - "trashed_items" => 0, - "type" => "folder", - "folders" => [] - ] - ] - ] - ]); - } - - /** - * @test - */ - public function guest_search_file() - { - $folder = Folder::factory(Folder::class) - ->create(); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $folder->user_id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - $file = File::factory(File::class) - ->create([ - 'name' => 'Document', - 'folder_id' => $folder->id, - 'user_id' => $folder->user_id, - ]); - - $this->getJson("/api/browse/search/public/$share->token?query=doc") - ->assertStatus(200) - ->assertJsonFragment([ - 'id' => $file->id - ]); - } - - /** - * @test - */ - public function guest_try_search_non_shared_user_file() - { - $folder = Folder::factory(Folder::class) - ->create(); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $folder->id, - 'user_id' => $folder->user_id, - 'type' => 'folder', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - File::factory(File::class) - ->create([ - 'name' => 'Document', - 'user_id' => $folder->user_id, - ]); - - $this->getJson("/api/browse/search/public/$share->token?query=doc") - ->assertStatus(200) - ->assertJsonFragment([]); - } - - /** - * @test - */ - public function guest_get_file_detail() - { - $file = File::factory(File::class) - ->create([ - 'name' => 'Document', - ]); - - $share = Share::factory(Share::class) - ->create([ - 'item_id' => $file->id, - 'user_id' => $file->user_id, - 'type' => 'file', - 'is_protected' => false, - 'permission' => 'editor', - ]); - - - $this->getJson("/api/browse/file/$share->token/public") - ->assertStatus(200) - ->assertJsonFragment([ - 'name' => 'Document' - ]); - } -} diff --git a/tests/Feature/Share/ShareTest.php b/tests/Feature/Share/UserShareTest.php similarity index 75% rename from tests/Feature/Share/ShareTest.php rename to tests/Feature/Share/UserShareTest.php index 27082b58..a20e6210 100644 --- a/tests/Feature/Share/ShareTest.php +++ b/tests/Feature/Share/UserShareTest.php @@ -14,7 +14,7 @@ use Illuminate\Support\Facades\Notification; use Laravel\Sanctum\Sanctum; use Tests\TestCase; -class ShareTest extends TestCase +class UserShareTest extends TestCase { use DatabaseMigrations; @@ -149,7 +149,7 @@ class ShareTest extends TestCase /** * @test */ - public function it_share_folder_for_multiple_email() + public function it_share_folder_and_send_link_for_multiple_email() { Notification::fake(); @@ -210,7 +210,7 @@ class ShareTest extends TestCase * * TODO: pridat test na zmazanie zip */ - public function it_revoke_single_sharing() + public function it_revoke_single_share_record() { $folder = Folder::factory(Folder::class) ->create(); @@ -236,69 +236,4 @@ class ShareTest extends TestCase 'item_id' => $folder->id ]); } - - /** - * @test - */ - public function it_get_shared_record() - { - $share = Share::factory(Share::class) - ->create([ - 'is_protected' => 0, - ]); - - $this->get("/api/browse/shared/$share->token") - ->assertStatus(200) - ->assertExactJson([ - 'data' => [ - 'id' => $share->id, - 'type' => 'shares', - 'attributes' => [ - 'permission' => $share->permission, - 'is_protected' => false, - 'item_id' => $share->item_id, - 'expire_in' => $share->expire_in, - 'token' => $share->token, - 'link' => $share->link, - 'type' => $share->type, - 'created_at' => $share->created_at->toJson(), - 'updated_at' => $share->updated_at->toJson(), - ], - ] - ]); - } - - /** - * @test - */ - public function it_get_deleted_shared_record() - { - $this->get("/api/browse/shared/19ZMPNiass4ZqWwQ") - ->assertNotFound(); - } - - /** - * @test - */ - public function it_get_shared_page() - { - $share = Share::factory(Share::class) - ->create([ - 'type' => 'folder', - 'is_protected' => false, - ]); - - $this->get("/shared/$share->token") - ->assertViewIs('index') - ->assertStatus(200); - } - - /** - * @test - */ - public function it_get_deleted_shared_page() - { - $this->get('/shared/19ZMPNiass4ZqWwQ') - ->assertNotFound(); - } } diff --git a/tests/Feature/Share/PublicFilesAccessTest.php b/tests/Feature/Share/VisitorAccessToItemsTest.php similarity index 98% rename from tests/Feature/Share/PublicFilesAccessTest.php rename to tests/Feature/Share/VisitorAccessToItemsTest.php index 22cafed3..2e00cbf0 100644 --- a/tests/Feature/Share/PublicFilesAccessTest.php +++ b/tests/Feature/Share/VisitorAccessToItemsTest.php @@ -14,7 +14,7 @@ use Illuminate\Support\Str; use Storage; use Tests\TestCase; -class PublicFilesAccessTest extends TestCase +class VisitorAccessToItemsTest extends TestCase { use DatabaseMigrations; @@ -151,7 +151,7 @@ class PublicFilesAccessTest extends TestCase 'is_protected' => false, ]); - $this->get("/shared/$share->token") + $this->get("/share/$share->token") ->assertStatus(200); } diff --git a/tests/Feature/Share/VisitorBrowseTest.php b/tests/Feature/Share/VisitorBrowseTest.php new file mode 100644 index 00000000..ae783f58 --- /dev/null +++ b/tests/Feature/Share/VisitorBrowseTest.php @@ -0,0 +1,797 @@ +setup = app()->make(SetupService::class); + } + + /** + * @test + */ + public function it_get_share_record() + { + $share = Share::factory(Share::class) + ->create([ + 'is_protected' => 0, + ]); + + $this->get("/api/browse/share/$share->token") + ->assertStatus(200) + ->assertExactJson([ + 'data' => [ + 'id' => $share->id, + 'type' => 'shares', + 'attributes' => [ + 'permission' => $share->permission, + 'is_protected' => false, + 'item_id' => $share->item_id, + 'expire_in' => $share->expire_in, + 'token' => $share->token, + 'link' => $share->link, + 'type' => $share->type, + 'created_at' => $share->created_at->toJson(), + 'updated_at' => $share->updated_at->toJson(), + ], + ] + ]); + } + + /** + * @test + */ + public function it_get_share_page() + { + $share = Share::factory(Share::class) + ->create([ + 'type' => 'folder', + 'is_protected' => false, + ]); + + $this->get("/share/$share->token") + ->assertViewIs('index') + ->assertStatus(200); + } + + /** + * @test + */ + public function it_try_to_get_deleted_share_record() + { + $this->get("/api/browse/share/19ZMPNiass4ZqWwQ") + ->assertNotFound(); + } + + /** + * @test + */ + public function it_try_to_get_deleted_share_page() + { + $this->get('/share/19ZMPNiass4ZqWwQ') + ->assertNotFound(404); + } + + /** + * @test + */ + public function it_authenticate_protected_file_with_correct_password() + { + $file = File::factory(File::class) + ->create(); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $file->id, + 'user_id' => $file->user_id, + 'type' => 'file', + 'is_protected' => true, + 'password' => Hash::make('secret'), + ]); + + $this->postJson("/api/browse/authenticate/$share->token", [ + 'password' => 'secret' + ]) + ->assertStatus(200) + ->assertCookie('share_session', json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ]), false); + } + + /** + * @test + */ + public function it_authenticate_protected_file_with_incorrect_password() + { + $file = File::factory(File::class) + ->create(); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $file->id, + 'user_id' => $file->user_id, + 'type' => 'file', + 'is_protected' => true, + 'password' => Hash::make('secret'), + ]); + + $this->postJson("/api/browse/authenticate/$share->token", [ + 'password' => 'bad-password' + ]) + ->assertStatus(401) + ->assertCookieMissing('share_session'); + } + + /** + * @test + */ + public function visitor_zip_shared_multiple_files() + { + Storage::fake('local'); + + $this->setup->create_directories(); + + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + collect([0, 1]) + ->each(function ($index) use ($folder, $user) { + + $file = UploadedFile::fake() + ->create(Str::random() . "-fake-file-$index.pdf", 1000, 'application/pdf'); + + Storage::putFileAs("files/$user->id", $file, $file->name); + + File::factory(File::class) + ->create([ + 'filesize' => $file->getSize(), + 'folder_id' => $folder->id, + 'user_id' => $user->id, + 'basename' => $file->name, + 'name' => "fake-file-$index.pdf", + ]); + }); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => $is_protected, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->post("/api/zip/files/$share->token", [ + 'items' => File::all()->pluck('id') + ])->assertStatus(201); + } + + // Check public shared item + if (!$is_protected) { + $this->postJson("/api/zip/files/$share->token", [ + 'items' => File::all()->pluck('id') + ])->assertStatus(201); + } + + $this->assertDatabaseHas('zips', [ + 'user_id' => $user->id, + 'shared_token' => $share->token, + ]); + + Storage::assertExists("zip/" . Zip::first()->basename); + }); + } + + /** + * @test + */ + public function visitor_try_zip_not_shared_file_with_already_shared_multiple_files() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + File::factory(File::class) + ->create([ + 'folder_id' => $folder->id, + 'user_id' => $user->id, + ]); + + File::factory(File::class) + ->create([ + 'user_id' => $user->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => $is_protected, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->post("/api/zip/files/$share->token", [ + 'items' => File::all()->pluck('id') + ])->assertStatus(403); + } + + // Check public shared item + if (!$is_protected) { + $this->postJson("/api/zip/files/$share->token", [ + 'items' => File::all()->pluck('id') + ])->assertStatus(403); + } + }); + } + + /** + * @test + */ + public function visitor_zip_shared_folder() + { + Storage::fake('local'); + + $this->setup->create_directories(); + + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $root = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $children = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'parent_id' => $root->id + ]); + + collect([0, 1]) + ->each(function ($index) use ($children, $user) { + + $file = UploadedFile::fake() + ->create(Str::random() . "-fake-file-$index.pdf", 1000, 'application/pdf'); + + Storage::putFileAs("files/$user->id", $file, $file->name); + + File::factory(File::class) + ->create([ + 'filesize' => $file->getSize(), + 'folder_id' => $children->id, + 'user_id' => $user->id, + 'basename' => $file->name, + 'name' => "fake-file-$index.pdf", + ]); + }); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $children->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => $is_protected, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->get("/api/zip/folder/$children->id/$share->token") + ->assertStatus(201); + } + + // Check public shared item + if (!$is_protected) { + $this->getJson("/api/zip/folder/$children->id/$share->token") + ->assertStatus(201); + } + + $this->assertDatabaseHas('zips', [ + 'user_id' => $user->id, + 'shared_token' => $share->token, + ]); + + Zip::all() + ->each(function ($zip) { + Storage::assertExists("zip/$zip->basename"); + }); + }); + } + + /** + * @test + */ + public function visitor_try_zip_not_shared_folder() + { + Storage::fake('local'); + + $this->setup->create_directories(); + + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $share = Share::factory(Share::class) + ->create([ + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => $is_protected, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->get("/api/zip/folder/$folder->id/$share->token") + ->assertStatus(403); + } + + // Check public shared item + if (!$is_protected) { + $this->getJson("/api/zip/folder/$folder->id/$share->token") + ->assertStatus(403); + } + }); + } + + /** + * @test + */ + public function visitor_get_folder_content() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $root = Folder::factory(Folder::class) + ->create([ + 'name' => 'root', + 'user_id' => $user->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $root->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => $is_protected, + 'permission' => 'editor', + ]); + + $folder = Folder::factory(Folder::class) + ->create([ + 'parent_id' => $root->id, + 'name' => 'Documents', + "user_scope" => "master", + 'user_id' => $user->id, + ]); + + $file = File::factory(File::class) + ->create([ + 'folder_id' => $root->id, + 'name' => 'Document', + 'basename' => 'document.pdf', + "mimetype" => "application/pdf", + "user_scope" => "master", + "type" => "file", + 'user_id' => $user->id, + ]); + + $json = [ + [ + "id" => $folder->id, + "user_id" => $user->id, + "parent_id" => $root->id, + "name" => "Documents", + "color" => null, + "emoji" => null, + "user_scope" => "master", + "deleted_at" => null, + "created_at" => $folder->created_at, + "updated_at" => $folder->updated_at->toJson(), + "items" => 0, + "trashed_items" => 0, + "type" => "folder", + ], + [ + "id" => $file->id, + "user_id" => $user->id, + "folder_id" => $root->id, + "thumbnail" => null, + "name" => "Document", + "basename" => "document.pdf", + "mimetype" => "application/pdf", + "filesize" => $file->filesize, + "type" => "file", + "metadata" => null, + "user_scope" => "master", + "deleted_at" => null, + "created_at" => $file->created_at, + "updated_at" => $file->updated_at->toJson(), + "file_url" => "http://localhost/file/document.pdf/$share->token", + ] + ]; + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->get("/api/browse/folders/$root->id/$share->token") + ->assertStatus(200) + ->assertExactJson($json); + } + + // Check public shared item + if (!$is_protected) { + $this->getJson("/api/browse/folders/$root->id/$share->token") + ->assertStatus(200) + ->assertExactJson($json); + } + }); + } + + /** + * @test + */ + public function visitor_get_navigator_tree() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $user = User::factory(User::class) + ->create(); + + $folder_level_1 = Folder::factory(Folder::class) + ->create([ + 'name' => 'level 1', + 'user_scope' => 'master', + 'user_id' => $user->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder_level_1->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'permission' => 'editor', + 'is_protected' => $is_protected, + 'password' => Hash::make('secret'), + ]); + + $folder_level_2 = Folder::factory(Folder::class) + ->create([ + 'name' => 'level 2', + 'parent_id' => $folder_level_1->id, + 'user_scope' => 'master', + 'user_id' => $user->id, + ]); + + $folder_level_3 = Folder::factory(Folder::class) + ->create([ + 'name' => 'level 3', + 'parent_id' => $folder_level_2->id, + 'user_scope' => 'master', + 'user_id' => $user->id, + ]); + + $folder_level_2_sibling = Folder::factory(Folder::class) + ->create([ + 'name' => 'level 2 Sibling', + 'parent_id' => $folder_level_1->id, + 'user_scope' => 'master', + 'user_id' => $user->id, + ]); + + $tree = [ + [ + 'id' => $share->item_id, + "name" => "Home", + "location" => "public", + "folders" => [ + [ + "id" => $folder_level_2->id, + "parent_id" => $folder_level_1->id, + "name" => "level 2", + "items" => 1, + "trashed_items" => 1, + "type" => "folder", + "folders" => [ + [ + "id" => $folder_level_3->id, + "parent_id" => $folder_level_2->id, + "name" => "level 3", + "items" => 0, + "trashed_items" => 0, + "type" => "folder", + "folders" => [], + ], + ], + ], + [ + "id" => $folder_level_2_sibling->id, + "parent_id" => $folder_level_1->id, + "name" => "level 2 Sibling", + "items" => 0, + "trashed_items" => 0, + "type" => "folder", + "folders" => [] + ] + ] + ] + ]; + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this + ->withUnencryptedCookies($cookie) + ->get("/api/browse/navigation/$share->token") + ->assertStatus(200) + ->assertExactJson($tree); + } + + // Check public shared item + if (!$is_protected) { + + $this->getJson("/api/browse/navigation/$share->token") + ->assertStatus(200) + ->assertExactJson($tree); + } + }); + } + + /** + * @test + */ + public function visitor_search_file() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $folder = Folder::factory(Folder::class) + ->create(); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $folder->user_id, + 'type' => 'folder', + 'permission' => 'editor', + 'is_protected' => $is_protected, + 'password' => Hash::make('secret'), + ]); + + $file = File::factory(File::class) + ->create([ + 'name' => 'Document', + 'folder_id' => $folder->id, + 'user_id' => $folder->user_id, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this->withUnencryptedCookies($cookie) + ->get("/api/browse/search/$share->token?query=doc") + ->assertStatus(200) + ->assertJsonFragment([ + 'id' => $file->id + ]); + } + + // Check public shared item + if (!$is_protected) { + + $this->getJson("/api/browse/search/$share->token?query=doc") + ->assertStatus(200) + ->assertJsonFragment([ + 'id' => $file->id + ]); + } + }); + } + + /** + * @test + */ + public function visitor_try_search_not_shared_user_file() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $folder = Folder::factory(Folder::class) + ->create(); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $folder->user_id, + 'type' => 'folder', + 'permission' => 'editor', + 'is_protected' => $is_protected, + 'password' => Hash::make('secret'), + ]); + + File::factory(File::class) + ->create([ + 'name' => 'Document', + 'user_id' => $folder->user_id, + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this->withUnencryptedCookies($cookie) + ->get("/api/browse/search/$share->token?query=doc") + ->assertStatus(200) + ->assertJsonFragment([]); + } + + // Check public shared item + if (!$is_protected) { + + $this->getJson("/api/browse/search/$share->token?query=doc") + ->assertStatus(200) + ->assertJsonFragment([]); + } + }); + } + + /** + * @test + */ + public function visitor_get_file_detail() + { + // check private or public share record + collect([true, false]) + ->each(function ($is_protected) { + + $file = File::factory(File::class) + ->create([ + 'name' => 'Document', + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $file->id, + 'user_id' => $file->user_id, + 'type' => 'file', + 'permission' => 'editor', + 'is_protected' => $is_protected, + 'password' => Hash::make('secret'), + ]); + + // Check shared item protected by password + if ($is_protected) { + + $cookie = ['share_session' => json_encode([ + 'token' => $share->token, + 'authenticated' => true, + ])]; + + $this->withUnencryptedCookies($cookie) + ->get("/api/browse/file/$share->token") + ->assertStatus(200) + ->assertJsonFragment([ + 'name' => 'Document' + ]); + } + + // Check public shared item + if (!$is_protected) { + $this->getJson("/api/browse/file/$share->token") + ->assertStatus(200) + ->assertJsonFragment([ + 'name' => 'Document' + ]); + } + }); + } +} diff --git a/tests/Feature/Share/VisitorManipulatingTest.php b/tests/Feature/Share/VisitorManipulatingTest.php new file mode 100644 index 00000000..6c017656 --- /dev/null +++ b/tests/Feature/Share/VisitorManipulatingTest.php @@ -0,0 +1,345 @@ +setup = app()->make(SetupService::class); + } + + /** + * @test + */ + public function editor_rename_shared_file() + { + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $file = File::factory(File::class) + ->create([ + 'folder_id' => $folder->id + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $this->patchJson("/api/editor/rename/{$file->id}/public/$share->token", [ + 'name' => 'Renamed Item', + 'type' => 'file', + ]) + ->assertStatus(201) + ->assertJsonFragment([ + 'name' => 'Renamed Item', + ]); + + $this->assertDatabaseHas('files', [ + 'name' => 'Renamed Item' + ]); + } + + /** + * @test + */ + public function editor_rename_shared_folder() + { + $user = User::factory(User::class) + ->create(); + + $root = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $children = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'parent_id' => $root->id + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $root->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $this->patchJson("/api/editor/rename/{$children->id}/public/$share->token", [ + 'name' => 'Renamed Folder', + 'type' => 'folder', + ]) + ->assertStatus(201) + ->assertJsonFragment([ + 'name' => 'Renamed Folder', + ]); + + $this->assertDatabaseHas('folders', [ + 'name' => 'Renamed Folder' + ]); + } + + /** + * @test + */ + public function editor_create_new_folder_in_shared_folder() + { + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $this->postJson("/api/editor/create-folder/public/$share->token", [ + 'name' => 'Awesome New Folder', + 'parent_id' => $folder->id, + ]) + ->assertStatus(201) + ->assertJsonFragment([ + 'name' => 'Awesome New Folder', + ]); + + $this->assertDatabaseHas('folders', [ + 'name' => 'Awesome New Folder', + 'parent_id' => $folder->id, + 'user_scope' => 'editor', + ]); + } + + /** + * @test + */ + public function editor_delete_multiple_files_in_shared_folder() + { + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $files = File::factory(File::class) + ->count(2) + ->create([ + 'folder_id' => $folder->id + ]); + + $this->postJson("/api/editor/remove/public/$share->token", [ + 'items' => [ + [ + 'id' => $files[0]->id, + 'type' => 'file', + 'force_delete' => false, + ], + [ + 'id' => $files[1]->id, + 'type' => 'file', + 'force_delete' => false, + ], + ], + ])->assertStatus(204); + + $files + ->each(function ($file) { + $this->assertSoftDeleted('files', [ + 'id' => $file->id, + ]); + }); + } + + /** + * @test + */ + public function editor_upload_file_into_shared_folder() + { + Storage::fake('local'); + + $this->setup->create_directories(); + + $user = User::factory(User::class) + ->create(); + + $folder = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'user_scope' => 'master', + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $folder->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $file = UploadedFile::fake() + ->create('fake-file.pdf', 1000, 'application/pdf'); + + $this->postJson("/api/editor/upload/public/$share->token", [ + 'file' => $file, + 'folder_id' => $folder->id, + 'is_last' => true, + ])->assertStatus(201); + + $this->assertDatabaseHas('traffic', [ + 'user_id' => $user->id, + ]); + + $this->assertDatabaseHas('files', [ + 'user_scope' => 'editor', + ]); + + Storage::disk('local') + ->assertExists( + "files/$user->id/fake-file.pdf" + ); + } + + /** + * @test + */ + public function editor_move_file_to_another_folder() + { + $user = User::factory(User::class) + ->create(); + + $root = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $children = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'parent_id' => $root->id, + ]); + + $file = File::factory(File::class) + ->create([ + 'user_id' => $user->id, + 'folder_id' => $root->id + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $root->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $this->postJson("/api/editor/move/public/$share->token", [ + 'to_id' => $children->id, + 'items' => [ + [ + 'type' => 'file', + 'id' => $file->id, + ] + ], + ])->assertStatus(204); + + $this->assertDatabaseHas('files', [ + 'id' => $file->id, + 'folder_id' => $children->id, + ]); + } + + /** + * @test + */ + public function editor_move_folder_to_another_folder() + { + $user = User::factory(User::class) + ->create(); + + $root = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id + ]); + + $brother = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'parent_id' => $root->id, + ]); + + $sister = Folder::factory(Folder::class) + ->create([ + 'user_id' => $user->id, + 'parent_id' => $root->id, + ]); + + $share = Share::factory(Share::class) + ->create([ + 'item_id' => $root->id, + 'user_id' => $user->id, + 'type' => 'folder', + 'is_protected' => false, + 'permission' => 'editor', + ]); + + $this->postJson("/api/editor/move/public/$share->token", [ + 'to_id' => $brother->id, + 'items' => [ + [ + 'type' => 'folder', + 'id' => $sister->id, + ] + ], + ])->assertStatus(204); + + $this->assertDatabaseHas('folders', [ + 'id' => $sister->id, + 'parent_id' => $brother->id, + ]); + } +}