mirror of
https://github.com/VueFileManager/vuefilemanager.git
synced 2026-04-05 18:23:48 +00:00
- Frontend restriction shared page
This commit is contained in:
@@ -53,7 +53,7 @@
|
||||
"/chunks/invitation.js": "/chunks/invitation.js?id=b3a04d44ea7e75c6203a",
|
||||
"/chunks/invoices.js": "/chunks/invoices.js?id=2032e4f8e4f5ed58b8b4",
|
||||
"/chunks/my-shared-items.js": "/chunks/my-shared-items.js?id=ef0a2dbe808eaee42c1f",
|
||||
"/chunks/not-found.js": "/chunks/not-found.js?id=034287ee0ecb036320d3",
|
||||
"/chunks/not-found.js": "/chunks/not-found.js?id=9b179edd89e9b0544566",
|
||||
"/chunks/page-edit.js": "/chunks/page-edit.js?id=1b8c9a0eee76fcb52f12",
|
||||
"/chunks/pages.js": "/chunks/pages.js?id=8c6772e3224fadca2bf2",
|
||||
"/chunks/plan.js": "/chunks/plan.js?id=f62a5bd64fb706b2f0e2",
|
||||
@@ -75,7 +75,7 @@
|
||||
"/chunks/settings-storage.js": "/chunks/settings-storage.js?id=76b45c336e8e12b23e81",
|
||||
"/chunks/settings~chunks/settings-password.js": "/chunks/settings~chunks/settings-password.js?id=aafc9cd6aa47b01bc25a",
|
||||
"/chunks/setup-wizard.js": "/chunks/setup-wizard.js?id=651d5accf401908724c5",
|
||||
"/chunks/shared.js": "/chunks/shared.js?id=387e9dc2cb6e3e328cf4",
|
||||
"/chunks/shared.js": "/chunks/shared.js?id=db60726012b6d246bd77",
|
||||
"/chunks/shared-with-me.js": "/chunks/shared-with-me.js?id=cf39d503eef93bcc7f1c",
|
||||
"/chunks/shared-with-me~chunks/team-folders.js": "/chunks/shared-with-me~chunks/team-folders.js?id=abf65131397ea2b12355",
|
||||
"/chunks/shared/authenticate.js": "/chunks/shared/authenticate.js?id=e19e444844d1495d900f",
|
||||
@@ -217,5 +217,29 @@
|
||||
"/js/main.fcf019dfae2cac8e0432.hot-update.js": "/js/main.fcf019dfae2cac8e0432.hot-update.js",
|
||||
"/js/main.b99796b9cd80dfca9231.hot-update.js": "/js/main.b99796b9cd80dfca9231.hot-update.js",
|
||||
"/js/main.ff162a01c1b21836ee9f.hot-update.js": "/js/main.ff162a01c1b21836ee9f.hot-update.js",
|
||||
"/js/main.7a46e4ea987d72347892.hot-update.js": "/js/main.7a46e4ea987d72347892.hot-update.js"
|
||||
"/js/main.7a46e4ea987d72347892.hot-update.js": "/js/main.7a46e4ea987d72347892.hot-update.js",
|
||||
"/chunks/shared.e292a21450a2e96c5b77.hot-update.js": "/chunks/shared.e292a21450a2e96c5b77.hot-update.js",
|
||||
"/chunks/shared.ea4e7bbd3ce9f55a2c8e.hot-update.js": "/chunks/shared.ea4e7bbd3ce9f55a2c8e.hot-update.js",
|
||||
"/chunks/shared.836f769daafe640518ac.hot-update.js": "/chunks/shared.836f769daafe640518ac.hot-update.js",
|
||||
"/js/main.d457be736ef017a782ea.hot-update.js": "/js/main.d457be736ef017a782ea.hot-update.js",
|
||||
"/chunks/platform~chunks/shared.d457be736ef017a782ea.hot-update.js": "/chunks/platform~chunks/shared.d457be736ef017a782ea.hot-update.js",
|
||||
"/chunks/shared.d457be736ef017a782ea.hot-update.js": "/chunks/shared.d457be736ef017a782ea.hot-update.js",
|
||||
"/js/main.83361deb74c3e1859144.hot-update.js": "/js/main.83361deb74c3e1859144.hot-update.js",
|
||||
"/chunks/admin~chunks/platform.83361deb74c3e1859144.hot-update.js": "/chunks/admin~chunks/platform.83361deb74c3e1859144.hot-update.js",
|
||||
"/chunks/platform.83361deb74c3e1859144.hot-update.js": "/chunks/platform.83361deb74c3e1859144.hot-update.js",
|
||||
"/chunks/shared.83361deb74c3e1859144.hot-update.js": "/chunks/shared.83361deb74c3e1859144.hot-update.js",
|
||||
"/chunks/shared.3b938ab55dc98ab07bd9.hot-update.js": "/chunks/shared.3b938ab55dc98ab07bd9.hot-update.js",
|
||||
"/js/main.1f3af2652193f43c092d.hot-update.js": "/js/main.1f3af2652193f43c092d.hot-update.js",
|
||||
"/js/main.fef96a82e13da818af79.hot-update.js": "/js/main.fef96a82e13da818af79.hot-update.js",
|
||||
"/chunks/app-setup~chunks/billings-detail~chunks/create-new-password~chunks/database~chunks/email-veri~2c1222b7.js": "/chunks/app-setup~chunks/billings-detail~chunks/create-new-password~chunks/database~chunks/email-veri~2c1222b7.js?id=f8d41224222e2bd2040e",
|
||||
"/chunks/temporary-unavailable.js": "/chunks/temporary-unavailable.js?id=2269ad0b9a010b240838",
|
||||
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~8c6958f5.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~8c6958f5.js?id=b94057ad23d11b238e2b",
|
||||
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~c31998a3.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~c31998a3.js?id=78abc0680fedc29e1146",
|
||||
"/js/main.4e470711fec628ada904.hot-update.js": "/js/main.4e470711fec628ada904.hot-update.js",
|
||||
"/chunks/temporary-unavailable.4e470711fec628ada904.hot-update.js": "/chunks/temporary-unavailable.4e470711fec628ada904.hot-update.js",
|
||||
"/chunks/temporary-unavailable.eb45683c53ba28a1beaa.hot-update.js": "/chunks/temporary-unavailable.eb45683c53ba28a1beaa.hot-update.js",
|
||||
"/js/main.819e5cdfad5c00b05e6a.hot-update.js": "/js/main.819e5cdfad5c00b05e6a.hot-update.js",
|
||||
"/chunks/not-found.819e5cdfad5c00b05e6a.hot-update.js": "/chunks/not-found.819e5cdfad5c00b05e6a.hot-update.js",
|
||||
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~91d4a521.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~91d4a521.js?id=b563ab4d2bf1a17f0fa5",
|
||||
"/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~af91ece5.js": "/vendors~chunks/admin~chunks/admin-account~chunks/app-appearance~chunks/app-billings~chunks/app-email~af91ece5.js?id=9a44c9cf620730dc67d5"
|
||||
}
|
||||
|
||||
11
resources/js/routes/routesIndex.js
vendored
11
resources/js/routes/routesIndex.js
vendored
@@ -30,7 +30,16 @@ const routesIndex = [
|
||||
name: 'NotFound',
|
||||
path: '/not-found',
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "chunks/not-found-shared" */ '../views/NotFound'),
|
||||
import(/* webpackChunkName: "chunks/not-found" */ '../views/NotFound'),
|
||||
meta: {
|
||||
requiresAuth: false
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'TemporaryUnavailable',
|
||||
path: '/temporary-unavailable',
|
||||
component: () =>
|
||||
import(/* webpackChunkName: "chunks/temporary-unavailable" */ '../views/TemporaryUnavailable'),
|
||||
meta: {
|
||||
requiresAuth: false
|
||||
},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<home-icon size="17"/>
|
||||
</div>
|
||||
<div class="label">
|
||||
{{ $t('sidebar.home') }}
|
||||
{{ $t('Home') }}
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
@@ -75,7 +75,13 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
goHome() {
|
||||
this.$router.replace({name: 'Public', params: {token: this.$route.params.token, id: this.sharedDetail.item_id}})
|
||||
this.$router.replace({
|
||||
name: 'Public',
|
||||
params: {
|
||||
token: this.sharedDetail.data.attributes.token,
|
||||
id: this.sharedDetail.data.attributes.item_id
|
||||
}
|
||||
})
|
||||
},
|
||||
dragLeave() {
|
||||
this.area = false
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<template>
|
||||
<AuthContentWrapper ref="auth">
|
||||
|
||||
<AuthContent name="not-found" :visible="true">
|
||||
<AuthContent :visible="true">
|
||||
<Headline
|
||||
:title="$t('page_shared_404.title')"
|
||||
:description="$t('page_shared_404.subtitle')"
|
||||
/>
|
||||
|
||||
<span class="additional-link">{{ $t('page_registration.have_an_account') }}
|
||||
<router-link :to="{name: 'SignIn'}">
|
||||
{{ $t('page_forgotten_password.password_remember_button') }}
|
||||
@@ -18,32 +16,18 @@
|
||||
|
||||
<script>
|
||||
import AuthContentWrapper from '/resources/js/components/Auth/AuthContentWrapper'
|
||||
import {ValidationProvider, ValidationObserver} from 'vee-validate/dist/vee-validate.full'
|
||||
import AuthContent from '/resources/js/components/Auth/AuthContent'
|
||||
import AuthButton from '/resources/js/components/Auth/AuthButton'
|
||||
import {required} from 'vee-validate/dist/rules'
|
||||
import Headline from "./Auth/Headline"
|
||||
import {mapGetters} from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'NotFound',
|
||||
components: {
|
||||
AuthContentWrapper,
|
||||
ValidationProvider,
|
||||
ValidationObserver,
|
||||
AuthContent,
|
||||
AuthButton,
|
||||
Headline,
|
||||
required,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['config']),
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isLoading: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
<MobileToolbar />
|
||||
|
||||
<!--File list & info sidebar-->
|
||||
<!--File list & info sidebar-->
|
||||
<div class="flex space-x-6 md:overflow-hidden md:h-full">
|
||||
|
||||
<router-view
|
||||
|
||||
38
resources/js/views/TemporaryUnavailable.vue
Normal file
38
resources/js/views/TemporaryUnavailable.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<AuthContentWrapper ref="auth">
|
||||
|
||||
<AuthContent :visible="true">
|
||||
<Headline
|
||||
:title="$t('Temporary Unavailable')"
|
||||
:description="$t('Unfortunately, this shared link is temporary unavailable. Please try it later.')"
|
||||
/>
|
||||
|
||||
<span class="additional-link">{{ $t('page_registration.have_an_account') }}
|
||||
<router-link :to="{name: 'SignIn'}">
|
||||
{{ $t('page_forgotten_password.password_remember_button') }}
|
||||
</router-link>
|
||||
</span>
|
||||
</AuthContent>
|
||||
</AuthContentWrapper>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AuthContentWrapper from '/resources/js/components/Auth/AuthContentWrapper'
|
||||
import AuthContent from '/resources/js/components/Auth/AuthContent'
|
||||
import AuthButton from '/resources/js/components/Auth/AuthButton'
|
||||
import Headline from "./Auth/Headline"
|
||||
|
||||
export default {
|
||||
name: 'NotFound',
|
||||
components: {
|
||||
AuthContentWrapper,
|
||||
AuthContent,
|
||||
AuthButton,
|
||||
Headline,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import '/resources/sass/vuefilemanager/_auth';
|
||||
</style>
|
||||
@@ -43,4 +43,9 @@ class DefaultRestrictionsEngine implements RestrictionsEngine
|
||||
{
|
||||
return resolve(CheckMaxTeamMembersLimitAction::class)($user, $newInvites);
|
||||
}
|
||||
|
||||
public function canVisitShared(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,4 +38,9 @@ class FixedBillingRestrictionsEngine implements RestrictionsEngine
|
||||
{
|
||||
return resolve(CheckMaxTeamMembersLimitAction::class)($user, $newInvites);
|
||||
}
|
||||
|
||||
public function canVisitShared(User $user): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,10 @@ class MeteredBillingRestrictionsEngine implements RestrictionsEngine
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canVisitShared(User $user): bool
|
||||
{
|
||||
// Disable share visit when user has more than 3 failed payments
|
||||
return ! ($user->failedPayments()->count() >= 3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,4 +14,6 @@ interface RestrictionsEngine
|
||||
public function canCreateTeamFolder(User $user): bool;
|
||||
|
||||
public function canInviteTeamMembers(User $user, array $newInvites = []): bool;
|
||||
|
||||
public function canVisitShared(User $user): bool;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Domain\Browsing\Controllers;
|
||||
|
||||
use Domain\Files\Models\File;
|
||||
use Domain\Folders\Resources\FolderResource;
|
||||
use Domain\Sharing\Models\Share;
|
||||
use Domain\Folders\Models\Folder;
|
||||
use Domain\Files\Resources\FilesCollection;
|
||||
@@ -30,6 +31,7 @@ class VisitorBrowseFolderController
|
||||
// Check if user can get directory
|
||||
($this->verifyAccessToItem)($id, $shared);
|
||||
|
||||
// Get requested folder
|
||||
$requestedFolder = Folder::findOrFail($id);
|
||||
|
||||
// Get files and folders
|
||||
@@ -49,7 +51,7 @@ class VisitorBrowseFolderController
|
||||
return [
|
||||
'folders' => new FolderCollection($folders),
|
||||
'files' => new FilesCollection($files),
|
||||
'root' => $requestedFolder,
|
||||
'root' => new FolderResource($requestedFolder),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Domain\Sharing\Controllers;
|
||||
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
use Domain\Files\Models\File;
|
||||
use Domain\Sharing\Models\Share;
|
||||
@@ -16,12 +17,16 @@ class SharePublicIndexController extends Controller
|
||||
*/
|
||||
public function __construct(
|
||||
public RecordDownloadAction $recordDownload,
|
||||
) {
|
||||
}
|
||||
) {}
|
||||
|
||||
public function __invoke(
|
||||
Share $share,
|
||||
): View | StreamedResponse {
|
||||
): View | StreamedResponse | RedirectResponse {
|
||||
// Check if user can see shared record
|
||||
if (! $share->user->canVisitShared()) {
|
||||
return redirect('/temporary-unavailable');
|
||||
}
|
||||
|
||||
// Delete share_session if exist
|
||||
if ($share->is_protected) {
|
||||
cookie()->queue('share_session', '', -1);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<?php
|
||||
namespace Domain\Sharing\Controllers;
|
||||
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Domain\Sharing\Models\Share;
|
||||
use App\Http\Controllers\Controller;
|
||||
|
||||
@@ -12,7 +14,8 @@ class WebCrawlerOpenGraphController extends Controller
|
||||
*/
|
||||
public function __invoke(
|
||||
Share $share
|
||||
): View {
|
||||
): Application|Factory|View
|
||||
{
|
||||
$namespace = match ($share->type) {
|
||||
'folder' => 'Domain\\Folders\\Models\\Folder',
|
||||
'file' => 'Domain\\Files\\Models\\File',
|
||||
|
||||
@@ -235,4 +235,23 @@ class DefaultRestrictionsTest extends TestCase
|
||||
->get("file/$file->name/$share->token")
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function it_can_get_share_page()
|
||||
{
|
||||
$user = User::factory()
|
||||
->create();
|
||||
|
||||
$share = Share::factory()
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'type' => 'folder',
|
||||
'is_protected' => false,
|
||||
]);
|
||||
|
||||
$this->get("/share/$share->token")
|
||||
->assertViewIs('index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,4 +196,23 @@ class FixedBillingRestrictionsTest extends TestCase
|
||||
->get("file/$file->name/$share->token")
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function it_can_get_share_page()
|
||||
{
|
||||
$user = User::factory()
|
||||
->create();
|
||||
|
||||
$share = Share::factory()
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'type' => 'folder',
|
||||
'is_protected' => false,
|
||||
]);
|
||||
|
||||
$this->get("/share/$share->token")
|
||||
->assertViewIs('index');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,4 +213,24 @@ class MeteredBillingRestrictionsTest extends TestCase
|
||||
->get("file/$file->name/$share->token")
|
||||
->assertStatus(404);
|
||||
}
|
||||
|
||||
/**
|
||||
* @test
|
||||
*/
|
||||
public function it_cant_get_share_page()
|
||||
{
|
||||
$user = User::factory()
|
||||
->hasFailedpayments(3)
|
||||
->create();
|
||||
|
||||
$share = Share::factory()
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'type' => 'folder',
|
||||
'is_protected' => false,
|
||||
]);
|
||||
|
||||
$this->get("/share/$share->token")
|
||||
->assertRedirect('/temporary-unavailable');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Domain\Sharing;
|
||||
|
||||
use Tests\TestCase;
|
||||
@@ -43,8 +44,12 @@ class VisitorBrowseTest extends TestCase
|
||||
*/
|
||||
public function it_get_share_page()
|
||||
{
|
||||
$user = User::factory()
|
||||
->create();
|
||||
|
||||
$share = Share::factory()
|
||||
->create([
|
||||
'user_id' => $user->id,
|
||||
'type' => 'folder',
|
||||
'is_protected' => false,
|
||||
]);
|
||||
@@ -69,7 +74,7 @@ class VisitorBrowseTest extends TestCase
|
||||
public function it_try_to_get_deleted_share_page()
|
||||
{
|
||||
$this->get('/share/19ZMPNiass4ZqWwQ')
|
||||
->assertNotFound(404);
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -188,7 +193,7 @@ class VisitorBrowseTest extends TestCase
|
||||
}
|
||||
|
||||
// Check public shared item
|
||||
if (! $is_protected) {
|
||||
if (!$is_protected) {
|
||||
$this->getJson("/api/browse/folders/$root->id/$share->token")
|
||||
->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
@@ -303,7 +308,7 @@ class VisitorBrowseTest extends TestCase
|
||||
}
|
||||
|
||||
// Check public shared item
|
||||
if (! $is_protected) {
|
||||
if (!$is_protected) {
|
||||
$this->getJson("/api/browse/navigation/$share->token")
|
||||
->assertStatus(200)
|
||||
->assertExactJson($tree);
|
||||
@@ -355,7 +360,7 @@ class VisitorBrowseTest extends TestCase
|
||||
}
|
||||
|
||||
// Check public shared item
|
||||
if (! $is_protected) {
|
||||
if (!$is_protected) {
|
||||
$this->getJson("/api/browse/search/$share->token?query=doc")
|
||||
->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
@@ -406,7 +411,7 @@ class VisitorBrowseTest extends TestCase
|
||||
}
|
||||
|
||||
// Check public shared item
|
||||
if (! $is_protected) {
|
||||
if (!$is_protected) {
|
||||
$this->getJson("/api/browse/search/$share->token?query=doc")
|
||||
->assertStatus(200)
|
||||
->assertJsonFragment([]);
|
||||
@@ -453,7 +458,7 @@ class VisitorBrowseTest extends TestCase
|
||||
}
|
||||
|
||||
// Check public shared item
|
||||
if (! $is_protected) {
|
||||
if (!$is_protected) {
|
||||
$this->getJson("/api/browse/file/$share->token")
|
||||
->assertStatus(200)
|
||||
->assertJsonFragment([
|
||||
|
||||
Reference in New Issue
Block a user