From ec29764c3fa96cf6e6609349e6027d11e8ebbbac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=8Carodej?= Date: Wed, 5 Jan 2022 17:52:08 +0100 Subject: [PATCH] - Restrictions for team folders --- public/mix-manifest.json | 6 +- resources/js/helpers/ValidatorHelpers.js | 5 ++ resources/js/views/Profile.vue | 22 ----- .../Engines/DefaultLimitationEngine.php | 14 ++- .../Engines/FixedBillingLimitationEngine.php | 11 +++ .../MeteredBillingLimitationEngine.php | 11 +++ src/App/Limitations/LimitationEngine.php | 4 + src/Domain/Files/Actions/UploadFileAction.php | 2 +- .../Controllers/UploadFileController.php | 3 +- .../CheckMaxTeamMembersLimitAction.php | 8 +- .../ConvertFolderIntoTeamFolderController.php | 9 +- .../Controllers/TeamFoldersController.php | 24 ++++- .../App/Limitations/DefaultLimitationTest.php | 79 +++++++++++++++++ .../FixedBillingLimitationTest.php | 83 +++++++++++++++++- .../MeteredBillingLimitationTest.php | 44 ++++++++-- tests/Domain/Teams/TeamLimitsTest.php | 87 ------------------- 16 files changed, 275 insertions(+), 137 deletions(-) delete mode 100644 tests/Domain/Teams/TeamLimitsTest.php diff --git a/public/mix-manifest.json b/public/mix-manifest.json index 2f8ffa23..183759b5 100644 --- a/public/mix-manifest.json +++ b/public/mix-manifest.json @@ -70,7 +70,7 @@ "/chunks/profile~chunks/settings-password.js": "/chunks/profile~chunks/settings-password.js?id=dfa4128d68360d5e1b3b", "/chunks/purchase-code.js": "/chunks/purchase-code.js?id=9e948882ae2315eb6132", "/chunks/recent-uploads.js": "/chunks/recent-uploads.js?id=8577d4c771602671b38a", - "/chunks/settings.js": "/chunks/settings.js?id=4466d97a0211f77d9317", + "/chunks/settings.js": "/chunks/settings.js?id=b8e396ca81993a2ac3c9", "/chunks/settings-password.js": "/chunks/settings-password.js?id=b6eb94764cc7b47f835e", "/chunks/settings-storage.js": "/chunks/settings-storage.js?id=76b45c336e8e12b23e81", "/chunks/settings~chunks/settings-password.js": "/chunks/settings~chunks/settings-password.js?id=aafc9cd6aa47b01bc25a", @@ -198,5 +198,7 @@ "/js/main.51906e2db8a6c36f5304.hot-update.js": "/js/main.51906e2db8a6c36f5304.hot-update.js", "/js/main.c09b0f39fe6d0b43515b.hot-update.js": "/js/main.c09b0f39fe6d0b43515b.hot-update.js", "/js/main.de28ca938da3825e62b3.hot-update.js": "/js/main.de28ca938da3825e62b3.hot-update.js", - "/js/main.be91e0ed3d68ecb1af06.hot-update.js": "/js/main.be91e0ed3d68ecb1af06.hot-update.js" + "/js/main.be91e0ed3d68ecb1af06.hot-update.js": "/js/main.be91e0ed3d68ecb1af06.hot-update.js", + "/chunks/settings.91678a5fa053e1ee00a7.hot-update.js": "/chunks/settings.91678a5fa053e1ee00a7.hot-update.js", + "/js/main.000dc4eab65b819bb1e4.hot-update.js": "/js/main.000dc4eab65b819bb1e4.hot-update.js" } diff --git a/resources/js/helpers/ValidatorHelpers.js b/resources/js/helpers/ValidatorHelpers.js index 7d904346..6494ded3 100644 --- a/resources/js/helpers/ValidatorHelpers.js +++ b/resources/js/helpers/ValidatorHelpers.js @@ -4,6 +4,11 @@ const ValidatorHelpers = { install(Vue) { Vue.prototype.$cantInviteMember = function (email, invitations) { + + if (store.getters.config.subscriptionType === 'metered') { + return false + } + // Get max team members limitations let limit = store.getters.user.data.meta.limitations.max_team_members diff --git a/resources/js/views/Profile.vue b/resources/js/views/Profile.vue index 9f8407b0..a9f4be25 100644 --- a/resources/js/views/Profile.vue +++ b/resources/js/views/Profile.vue @@ -42,31 +42,9 @@ {{ user.data.attributes.email }} - - - - - - - - - - diff --git a/src/App/Limitations/Engines/DefaultLimitationEngine.php b/src/App/Limitations/Engines/DefaultLimitationEngine.php index 98f1444e..464c8873 100644 --- a/src/App/Limitations/Engines/DefaultLimitationEngine.php +++ b/src/App/Limitations/Engines/DefaultLimitationEngine.php @@ -3,6 +3,7 @@ namespace App\Limitations\Engines; use App\Users\Models\User; use App\Limitations\LimitationEngine; +use Domain\Teams\Actions\CheckMaxTeamMembersLimitAction; class DefaultLimitationEngine implements LimitationEngine { @@ -20,8 +21,7 @@ class DefaultLimitationEngine implements LimitationEngine ); // 2. Check if storage usage exceed predefined capacity - return ! ($usedPercentage >= 100) - ; + return ! ($usedPercentage >= 100); } public function canDownload(User $user): bool @@ -33,4 +33,14 @@ class DefaultLimitationEngine implements LimitationEngine { return true; } + + public function canCreateTeamFolder(User $user): bool + { + return true; + } + + public function canInviteTeamMembers(User $user, array $newInvites): bool + { + return resolve(CheckMaxTeamMembersLimitAction::class)($user, $newInvites); + } } diff --git a/src/App/Limitations/Engines/FixedBillingLimitationEngine.php b/src/App/Limitations/Engines/FixedBillingLimitationEngine.php index 27603078..943fe03a 100644 --- a/src/App/Limitations/Engines/FixedBillingLimitationEngine.php +++ b/src/App/Limitations/Engines/FixedBillingLimitationEngine.php @@ -3,6 +3,7 @@ namespace App\Limitations\Engines; use App\Users\Models\User; use App\Limitations\LimitationEngine; +use Domain\Teams\Actions\CheckMaxTeamMembersLimitAction; class FixedBillingLimitationEngine implements LimitationEngine { @@ -27,4 +28,14 @@ class FixedBillingLimitationEngine implements LimitationEngine { return true; } + + public function canCreateTeamFolder(User $user): bool + { + return true; + } + + public function canInviteTeamMembers(User $user, array $newInvites): bool + { + return resolve(CheckMaxTeamMembersLimitAction::class)($user, $newInvites); + } } diff --git a/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php b/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php index b3cc26b3..bac7420d 100644 --- a/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php +++ b/src/App/Limitations/Engines/MeteredBillingLimitationEngine.php @@ -23,4 +23,15 @@ class MeteredBillingLimitationEngine implements LimitationEngine // Disable create folder when user has more than 3 failed payments return ! ($user->failedPayments()->count() >= 3); } + + public function canCreateTeamFolder(User $user): bool + { + // Disable create folder when user has more than 3 failed payments + return ! ($user->failedPayments()->count() >= 3); + } + + public function canInviteTeamMembers(User $user, array $newInvites): bool + { + return true; + } } diff --git a/src/App/Limitations/LimitationEngine.php b/src/App/Limitations/LimitationEngine.php index a118c5ff..d63ccbed 100644 --- a/src/App/Limitations/LimitationEngine.php +++ b/src/App/Limitations/LimitationEngine.php @@ -10,4 +10,8 @@ interface LimitationEngine public function canDownload(User $user): bool; public function canCreateFolder(User $user): bool; + + public function canCreateTeamFolder(User $user): bool; + + public function canInviteTeamMembers(User $user, array $newInvites): bool; } diff --git a/src/Domain/Files/Actions/UploadFileAction.php b/src/Domain/Files/Actions/UploadFileAction.php index c52d6760..511b2c4e 100644 --- a/src/Domain/Files/Actions/UploadFileAction.php +++ b/src/Domain/Files/Actions/UploadFileAction.php @@ -1,7 +1,6 @@ uploadFiles)($request); return response(new FileResource($file), 201); diff --git a/src/Domain/Teams/Actions/CheckMaxTeamMembersLimitAction.php b/src/Domain/Teams/Actions/CheckMaxTeamMembersLimitAction.php index 3c75ea5c..bf8ccaf4 100644 --- a/src/Domain/Teams/Actions/CheckMaxTeamMembersLimitAction.php +++ b/src/Domain/Teams/Actions/CheckMaxTeamMembersLimitAction.php @@ -5,7 +5,7 @@ use App\Users\Models\User; class CheckMaxTeamMembersLimitAction { - public function __invoke(array $invitations, User $user) + public function __invoke(User $user, array $newInvites): bool { // Get user limitation summary $limits = $user->limitations->summary(); @@ -14,7 +14,7 @@ class CheckMaxTeamMembersLimitAction $allowedEmails = $limits['max_team_members']['meta']['allowed_emails']; // Get new email invites from request - $invitationEmails = collect($invitations) + $invitationEmails = collect($newInvites) ->pluck('email'); // Count total unique members @@ -24,8 +24,6 @@ class CheckMaxTeamMembersLimitAction ->count(); // Check if there is more unique members than total max team members are allowed - if ($totalMembers > $limits['max_team_members']['total']) { - abort(423, 'You exceed your members limit.'); - } + return ! ($totalMembers > $limits['max_team_members']['total']); } } diff --git a/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php b/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php index 35c4e4d5..ee37dbd5 100644 --- a/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php +++ b/src/Domain/Teams/Controllers/ConvertFolderIntoTeamFolderController.php @@ -7,7 +7,6 @@ use App\Http\Controllers\Controller; use Domain\Teams\Models\TeamFolderMember; use Illuminate\Contracts\Routing\ResponseFactory; use Domain\Teams\Requests\ConvertIntoTeamFolderRequest; -use Domain\Teams\Actions\CheckMaxTeamMembersLimitAction; use Domain\Teams\Actions\InviteMembersIntoTeamFolderAction; use Domain\Teams\Actions\SetTeamFolderPropertyForAllChildrenAction; @@ -15,7 +14,6 @@ class ConvertFolderIntoTeamFolderController extends Controller { public function __construct( public InviteMembersIntoTeamFolderAction $inviteMembers, - public CheckMaxTeamMembersLimitAction $checkMaxTeamMembersLimit, public SetTeamFolderPropertyForAllChildrenAction $setTeamFolderPropertyForAllChildren, ) { } @@ -25,7 +23,12 @@ class ConvertFolderIntoTeamFolderController extends Controller Folder $folder ): ResponseFactory|Response { // Check if user didn't exceed max team members limit - ($this->checkMaxTeamMembersLimit)($request->input('invitations'), $folder->owner); + if (! $folder->owner->canInviteTeamMembers($request->input('invitations'))) { + return response([ + 'type' => 'error', + 'message' => 'You exceed your members limit.', + ], 401); + } // Update root team folder $folder->update([ diff --git a/src/Domain/Teams/Controllers/TeamFoldersController.php b/src/Domain/Teams/Controllers/TeamFoldersController.php index 3ad7fc85..168dd648 100644 --- a/src/Domain/Teams/Controllers/TeamFoldersController.php +++ b/src/Domain/Teams/Controllers/TeamFoldersController.php @@ -17,7 +17,6 @@ use Domain\Folders\Resources\FolderCollection; use Domain\Teams\Actions\UpdateInvitationsAction; use Illuminate\Contracts\Routing\ResponseFactory; use Domain\Teams\Requests\CreateTeamFolderRequest; -use Domain\Teams\Actions\CheckMaxTeamMembersLimitAction; use Domain\Teams\Requests\UpdateTeamFolderMembersRequest; use Domain\Teams\Actions\InviteMembersIntoTeamFolderAction; use Domain\Teams\Actions\SetTeamFolderPropertyForAllChildrenAction; @@ -27,7 +26,6 @@ class TeamFoldersController extends Controller public function __construct( public InviteMembersIntoTeamFolderAction $inviteMembers, public SetTeamFolderPropertyForAllChildrenAction $setTeamFolderPropertyForAllChildren, - public CheckMaxTeamMembersLimitAction $checkMaxTeamMembersLimit, ) { } @@ -61,8 +59,21 @@ class TeamFoldersController extends Controller ): ResponseFactory | Response { $data = CreateTeamFolderData::fromRequest($request); + // Check if user can create team folder + if (! $request->user()->canCreateTeamFolder()) { + return response([ + 'type' => 'error', + 'message' => 'This user action is not allowed.', + ], 401); + } + // Check if user didn't exceed max team members limit - ($this->checkMaxTeamMembersLimit)($data->invitations, $request->user()); + if (! $request->user()->canInviteTeamMembers($data->invitations)) { + return response([ + 'type' => 'error', + 'message' => 'You exceed your members limit.', + ], 401); + } // Create folder $folder = Folder::create([ @@ -93,7 +104,12 @@ class TeamFoldersController extends Controller $this->authorize('owner', $folder); // Check if user didn't exceed max team members limit - ($this->checkMaxTeamMembersLimit)($request->input('invitations'), $request->user()); + if (! $request->user()->canInviteTeamMembers($request->input('invitations'))) { + return response([ + 'type' => 'error', + 'message' => 'You exceed your members limit.', + ], 401); + } $updateInvitations( $folder, diff --git a/tests/App/Limitations/DefaultLimitationTest.php b/tests/App/Limitations/DefaultLimitationTest.php index 69461224..ae4946b1 100644 --- a/tests/App/Limitations/DefaultLimitationTest.php +++ b/tests/App/Limitations/DefaultLimitationTest.php @@ -5,6 +5,7 @@ use Tests\TestCase; use App\Users\Models\User; use Domain\Files\Models\File; use Domain\Settings\Models\Setting; +use Domain\Teams\Models\TeamFolderMember; class DefaultLimitationTest extends TestCase { @@ -105,4 +106,82 @@ class DefaultLimitationTest extends TestCase 'name' => 'New Folder', ]); } + + /** + * @test + */ + public function it_cant_invite_team_members_into_team_folder_because_user_exceeded_members_limit() + { + $user = User::factory() + ->hasFolders([ + 'team_folder' => true, + ]) + ->create(); + + TeamFolderMember::create([ + 'parent_id' => $user->folders[0]->id, + 'user_id' => $user->id, + 'permission' => 'owner', + ]); + + // Create team folder members + $members = User::factory() + ->count(5) + ->create() + ->each( + fn ($member) => TeamFolderMember::factory() + ->create([ + 'parent_id' => $user->folders[0]->id, + 'user_id' => $member->id, + ]) + ); + + // Try invite new members, it has to fail + $this + ->actingAs($user) + ->post('/api/teams/folders', [ + 'name' => 'Company Project', + 'invitations' => [ + [ + 'email' => 'test@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test2@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test3@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test4@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test5@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test6@doe.com', + 'permission' => 'can-edit', + ], + ], + ]) + ->assertStatus(401); + + // Invite existing member, it has to go through + $this + ->actingAs($user) + ->post('/api/teams/folders', [ + 'name' => 'Company Project', + 'invitations' => [ + [ + 'email' => $members[0]->email, + 'permission' => 'can-edit', + ], + ], + ]) + ->assertCreated(); + } } diff --git a/tests/App/Limitations/FixedBillingLimitationTest.php b/tests/App/Limitations/FixedBillingLimitationTest.php index e6903b38..ee1b7022 100644 --- a/tests/App/Limitations/FixedBillingLimitationTest.php +++ b/tests/App/Limitations/FixedBillingLimitationTest.php @@ -5,6 +5,7 @@ use Tests\TestCase; use App\Users\Models\User; use Domain\Files\Models\File; use Domain\Settings\Models\Setting; +use Domain\Teams\Models\TeamFolderMember; class FixedBillingLimitationTest extends TestCase { @@ -13,7 +14,7 @@ class FixedBillingLimitationTest extends TestCase parent::setUp(); Setting::updateOrCreate([ - 'name' => 'subscription_type', + 'name' => 'subscription_type', ], [ 'value' => 'fixed', ]); @@ -58,7 +59,7 @@ class FixedBillingLimitationTest extends TestCase $this ->actingAs($user) ->postJson('/api/create-folder', [ - 'name' => 'New Folder', + 'name' => 'New Folder', ]) ->assertStatus(201); @@ -66,4 +67,82 @@ class FixedBillingLimitationTest extends TestCase 'name' => 'New Folder', ]); } + + /** + * @test + */ + public function it_cant_invite_team_members_into_team_folder_because_user_exceeded_members_limit() + { + $user = User::factory() + ->hasFolders([ + 'team_folder' => true, + ]) + ->create(); + + TeamFolderMember::create([ + 'parent_id' => $user->folders[0]->id, + 'user_id' => $user->id, + 'permission' => 'owner', + ]); + + // Create team folder members + $members = User::factory() + ->count(5) + ->create() + ->each( + fn ($member) => TeamFolderMember::factory() + ->create([ + 'parent_id' => $user->folders[0]->id, + 'user_id' => $member->id, + ]) + ); + + // Try invite new members, it has to fail + $this + ->actingAs($user) + ->post('/api/teams/folders', [ + 'name' => 'Company Project', + 'invitations' => [ + [ + 'email' => 'test@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test2@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test3@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test4@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test5@doe.com', + 'permission' => 'can-edit', + ], + [ + 'email' => 'test6@doe.com', + 'permission' => 'can-edit', + ], + ], + ]) + ->assertStatus(401); + + // Invite existing member, it has to go through + $this + ->actingAs($user) + ->post('/api/teams/folders', [ + 'name' => 'Company Project', + 'invitations' => [ + [ + 'email' => $members[0]->email, + 'permission' => 'can-edit', + ], + ], + ]) + ->assertCreated(); + } } diff --git a/tests/App/Limitations/MeteredBillingLimitationTest.php b/tests/App/Limitations/MeteredBillingLimitationTest.php index 9af044d0..3d1d1052 100644 --- a/tests/App/Limitations/MeteredBillingLimitationTest.php +++ b/tests/App/Limitations/MeteredBillingLimitationTest.php @@ -49,16 +49,30 @@ class MeteredBillingLimitationTest extends TestCase $user = User::factory() ->create(); + // Create basic folder $this ->actingAs($user) ->postJson('/api/create-folder', [ - 'name' => 'New Folder', + 'name' => 'New Folder', ]) ->assertStatus(201); - $this->assertDatabaseHas('folders', [ - 'name' => 'New Folder', - ]); + // Create team folder + $this + ->actingAs($user) + ->postJson('/api/teams/folders', [ + 'name' => 'New Team Folder', + 'invitations' => [ + [ + 'email' => 'john@doe.com', + 'permission' => 'can-edit', + 'type' => 'invitation', + ], + ], + ]) + ->assertStatus(201); + + $this->assertDatabaseCount('folders', 2); } /** @@ -70,15 +84,29 @@ class MeteredBillingLimitationTest extends TestCase ->hasFailedpayments(3) ->create(); + // Create basic folder $this ->actingAs($user) ->postJson('/api/create-folder', [ - 'name' => 'New Folder', + 'name' => 'New Folder', ]) ->assertStatus(401); - $this->assertDatabaseMissing('folders', [ - 'name' => 'New Folder', - ]); + // Create team folder + $this + ->actingAs($user) + ->postJson('/api/teams/folders', [ + 'name' => 'New Folder', + 'invitations' => [ + [ + 'email' => 'john@doe.com', + 'permission' => 'can-edit', + 'type' => 'invitation', + ], + ], + ]) + ->assertStatus(401); + + $this->assertDatabaseCount('folders', 0); } } diff --git a/tests/Domain/Teams/TeamLimitsTest.php b/tests/Domain/Teams/TeamLimitsTest.php deleted file mode 100644 index 51ffb32a..00000000 --- a/tests/Domain/Teams/TeamLimitsTest.php +++ /dev/null @@ -1,87 +0,0 @@ -hasFolders([ - 'team_folder' => true, - ]) - ->create(); - - TeamFolderMember::create([ - 'parent_id' => $user->folders[0]->id, - 'user_id' => $user->id, - 'permission' => 'owner', - ]); - - $members = User::factory() - ->count(5) - ->create(); - - $members->each( - fn ($member) => TeamFolderMember::factory() - ->create([ - 'parent_id' => $user->folders[0]->id, - 'user_id' => $member->id, - ]) - ); - - // Try invite new member - $this - ->actingAs($user) - ->post('/api/teams/folders', [ - 'name' => 'Company Project', - 'invitations' => [ - [ - 'email' => 'test@doe.com', - 'permission' => 'can-edit', - ], - [ - 'email' => 'test2@doe.com', - 'permission' => 'can-edit', - ], - [ - 'email' => 'test3@doe.com', - 'permission' => 'can-edit', - ], - [ - 'email' => 'test4@doe.com', - 'permission' => 'can-edit', - ], - [ - 'email' => 'test5@doe.com', - 'permission' => 'can-edit', - ], - [ - 'email' => 'test6@doe.com', - 'permission' => 'can-edit', - ], - ], - ]) - ->assertStatus(423); - - // Invite existing member - $this - ->actingAs($user) - ->post('/api/teams/folders', [ - 'name' => 'Company Project', - 'invitations' => [ - [ - 'email' => $members[0]->email, - 'permission' => 'can-edit', - ], - ], - ]) - ->assertCreated(); - } -}