diff --git a/bin/phpunit-fast b/bin/phpunit-fast --- a/bin/phpunit-fast +++ b/bin/phpunit-fast @@ -5,6 +5,7 @@ pushd ${cwd}/../src/ php vendor/bin/phpunit \ + --no-coverage \ --stop-on-defect \ --stop-on-error \ --stop-on-failure $* diff --git a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php --- a/src/app/Http/Controllers/API/V4/Admin/DomainsController.php +++ b/src/app/Http/Controllers/API/V4/Admin/DomainsController.php @@ -4,6 +4,7 @@ use App\Domain; use App\User; +use Illuminate\Http\Request; class DomainsController extends \App\Http\Controllers\API\V4\DomainsController { @@ -52,4 +53,52 @@ return response()->json($result); } + + /** + * Suspend the domain + * + * @param \Illuminate\Http\Request $request The API request. + * @params string $id Domain identifier + * + * @return \Illuminate\Http\JsonResponse The response + */ + public function suspend(Request $request, $id) + { + $domain = Domain::find($id); + + if (empty($domain) || $domain->isPublic()) { + return $this->errorResponse(404); + } + + $domain->suspend(); + + return response()->json([ + 'status' => 'success', + 'message' => __('app.domain-suspend-success'), + ]); + } + + /** + * Un-Suspend the domain + * + * @param \Illuminate\Http\Request $request The API request. + * @params string $id Domain identifier + * + * @return \Illuminate\Http\JsonResponse The response + */ + public function unsuspend(Request $request, $id) + { + $domain = Domain::find($id); + + if (empty($domain) || $domain->isPublic()) { + return $this->errorResponse(404); + } + + $domain->unsuspend(); + + return response()->json([ + 'status' => 'success', + 'message' => __('app.domain-unsuspend-success'), + ]); + } } diff --git a/src/resources/lang/en/app.php b/src/resources/lang/en/app.php --- a/src/resources/lang/en/app.php +++ b/src/resources/lang/en/app.php @@ -31,6 +31,8 @@ 'domain-verify-success' => 'Domain verified successfully.', 'domain-verify-error' => 'Domain ownership verification failed.', + 'domain-suspend-success' => 'Domain suspended successfully.', + 'domain-unsuspend-success' => 'Domain unsuspended successfully.', 'user-update-success' => 'User data updated successfully.', 'user-create-success' => 'User created successfully.', diff --git a/src/resources/vue/Admin/Domain.vue b/src/resources/vue/Admin/Domain.vue --- a/src/resources/vue/Admin/Domain.vue +++ b/src/resources/vue/Admin/Domain.vue @@ -22,6 +22,10 @@ +
+ + +
@@ -64,6 +68,24 @@ .catch(this.$root.errorHandler) }, methods: { + suspendDomain() { + axios.post('/api/v4/domains/' + this.domain.id + '/suspend', {}) + .then(response => { + if (response.data.status == 'success') { + this.$toast.success(response.data.message) + this.domain = Object.assign({}, this.domain, { isSuspended: true }) + } + }) + }, + unsuspendDomain() { + axios.post('/api/v4/domains/' + this.domain.id + '/unsuspend', {}) + .then(response => { + if (response.data.status == 'success') { + this.$toast.success(response.data.message) + this.domain = Object.assign({}, this.domain, { isSuspended: false }) + } + }) + } } } diff --git a/src/routes/api.php b/src/routes/api.php --- a/src/routes/api.php +++ b/src/routes/api.php @@ -101,6 +101,8 @@ function () { Route::apiResource('domains', API\V4\Admin\DomainsController::class); Route::get('domains/{id}/confirm', 'API\V4\Admin\DomainsController@confirm'); + Route::post('domains/{id}/suspend', 'API\V4\Admin\DomainsController@suspend'); + Route::post('domains/{id}/unsuspend', 'API\V4\Admin\DomainsController@unsuspend'); Route::apiResource('entitlements', API\V4\Admin\EntitlementsController::class); Route::apiResource('packages', API\V4\Admin\PackagesController::class); diff --git a/src/tests/Browser/Admin/DomainTest.php b/src/tests/Browser/Admin/DomainTest.php --- a/src/tests/Browser/Admin/DomainTest.php +++ b/src/tests/Browser/Admin/DomainTest.php @@ -2,7 +2,7 @@ namespace Tests\Browser\Admin; -use App\Discount; +use App\Domain; use Tests\Browser; use Tests\Browser\Components\Toast; use Tests\Browser\Pages\Admin\Domain as DomainPage; @@ -86,4 +86,34 @@ }); }); } + + /** + * Test suspending/unsuspending a domain + * + * @depends testDomainInfo + */ + public function testSuspendAndUnsuspend(): void + { + $this->browse(function (Browser $browser) { + $domain = $this->getTestDomain('domainscontroller.com', [ + 'status' => Domain::STATUS_NEW | Domain::STATUS_ACTIVE + | Domain::STATUS_LDAP_READY | Domain::STATUS_CONFIRMED + | Domain::STATUS_VERIFIED, + 'type' => Domain::TYPE_EXTERNAL, + ]); + + $browser->visit(new DomainPage($domain->id)) + ->assertVisible('@domain-info #button-suspend') + ->assertMissing('@domain-info #button-unsuspend') + ->click('@domain-info #button-suspend') + ->assertToast(Toast::TYPE_SUCCESS, 'Domain suspended successfully.') + ->assertSeeIn('@domain-info #status span.text-warning', 'Suspended') + ->assertMissing('@domain-info #button-suspend') + ->click('@domain-info #button-unsuspend') + ->assertToast(Toast::TYPE_SUCCESS, 'Domain unsuspended successfully.') + ->assertSeeIn('@domain-info #status span.text-success', 'Active') + ->assertVisible('@domain-info #button-suspend') + ->assertMissing('@domain-info #button-unsuspend'); + }); + } } diff --git a/src/tests/Feature/Controller/Admin/DomainsTest.php b/src/tests/Feature/Controller/Admin/DomainsTest.php --- a/src/tests/Feature/Controller/Admin/DomainsTest.php +++ b/src/tests/Feature/Controller/Admin/DomainsTest.php @@ -2,6 +2,8 @@ namespace Tests\Feature\Controller\Admin; +use App\Domain; +use Illuminate\Support\Facades\Queue; use Tests\TestCase; class DomainsTest extends TestCase @@ -13,6 +15,8 @@ { parent::setUp(); self::useAdminUrl(); + + $this->deleteTestDomain('domainscontroller.com'); } /** @@ -20,6 +24,8 @@ */ public function tearDown(): void { + $this->deleteTestDomain('domainscontroller.com'); + parent::tearDown(); } @@ -84,4 +90,70 @@ $this->assertSame(0, $json['count']); $this->assertCount(0, $json['list']); } + + /** + * Test domain suspending (POST /api/v4/domains//suspend) + */ + public function testSuspend(): void + { + Queue::fake(); // disable jobs + + $domain = $this->getTestDomain('domainscontroller.com', [ + 'status' => Domain::STATUS_NEW, + 'type' => Domain::TYPE_EXTERNAL, + ]); + $user = $this->getTestUser('test@domainscontroller.com'); + $admin = $this->getTestUser('jeroen@jeroen.jeroen'); + + // Test unauthorized access to admin API + $response = $this->actingAs($user)->post("/api/v4/domains/{$domain->id}/suspend", []); + $response->assertStatus(403); + + $this->assertFalse($domain->fresh()->isSuspended()); + + // Test suspending the user + $response = $this->actingAs($admin)->post("/api/v4/domains/{$domain->id}/suspend", []); + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertSame('success', $json['status']); + $this->assertSame("Domain suspended successfully.", $json['message']); + $this->assertCount(2, $json); + + $this->assertTrue($domain->fresh()->isSuspended()); + } + + /** + * Test user un-suspending (POST /api/v4/users//unsuspend) + */ + public function testUnsuspend(): void + { + Queue::fake(); // disable jobs + + $domain = $this->getTestDomain('domainscontroller.com', [ + 'status' => Domain::STATUS_NEW | Domain::STATUS_SUSPENDED, + 'type' => Domain::TYPE_EXTERNAL, + ]); + $user = $this->getTestUser('test@domainscontroller.com'); + $admin = $this->getTestUser('jeroen@jeroen.jeroen'); + + // Test unauthorized access to admin API + $response = $this->actingAs($user)->post("/api/v4/domains/{$domain->id}/unsuspend", []); + $response->assertStatus(403); + + $this->assertTrue($domain->fresh()->isSuspended()); + + // Test suspending the user + $response = $this->actingAs($admin)->post("/api/v4/domains/{$domain->id}/unsuspend", []); + $response->assertStatus(200); + + $json = $response->json(); + + $this->assertSame('success', $json['status']); + $this->assertSame("Domain unsuspended successfully.", $json['message']); + $this->assertCount(2, $json); + + $this->assertFalse($domain->fresh()->isSuspended()); + } }