Skip to content

Commit 1216f95

Browse files
committed
Prevent deletion of options that have children attached to them.
closes pterodactyl#562
1 parent d5bf873 commit 1216f95

File tree

4 files changed

+52
-6
lines changed

4 files changed

+52
-6
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/*
3+
* Pterodactyl - Panel
4+
* Copyright (c) 2015 - 2017 Dane Everitt <dane@daneeveritt.com>.
5+
*
6+
* This software is licensed under the terms of the MIT license.
7+
* https://opensource.org/licenses/MIT
8+
*/
9+
10+
namespace Pterodactyl\Exceptions\Service\ServiceOption;
11+
12+
use Pterodactyl\Exceptions\DisplayException;
13+
14+
class HasChildrenException extends DisplayException
15+
{
16+
}

app/Services/Services/Options/OptionDeletionService.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99

1010
namespace Pterodactyl\Services\Services\Options;
1111

12+
use Webmozart\Assert\Assert;
1213
use Pterodactyl\Exceptions\Service\HasActiveServersException;
1314
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
1415
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
16+
use Pterodactyl\Exceptions\Service\ServiceOption\HasChildrenException;
1517

1618
class OptionDeletionService
1719
{
@@ -46,17 +48,22 @@ public function __construct(
4648
* @return int
4749
*
4850
* @throws \Pterodactyl\Exceptions\Service\HasActiveServersException
51+
* @throws \Pterodactyl\Exceptions\Service\ServiceOption\HasChildrenException
4952
*/
5053
public function handle($option)
5154
{
52-
$servers = $this->serverRepository->findCountWhere([
53-
['option_id', '=', $option],
54-
]);
55+
Assert::integerish($option, 'First argument passed to handle must be integer, received %s.');
5556

57+
$servers = $this->serverRepository->findCountWhere([['option_id', '=', $option]]);
5658
if ($servers > 0) {
5759
throw new HasActiveServersException(trans('exceptions.service.options.delete_has_servers'));
5860
}
5961

62+
$children = $this->repository->findCountWhere([['config_from', '=', $option]]);
63+
if ($children > 0) {
64+
throw new HasChildrenException(trans('exceptions.service.options.has_children'));
65+
}
66+
6067
return $this->repository->delete($option);
6168
}
6269
}

resources/lang/en/exceptions.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
'delete_has_servers' => 'A service option with active servers attached to it cannot be deleted from the Panel.',
2525
'invalid_copy_id' => 'The service option selected for copying a script from either does not exist, or is copying a script itself.',
2626
'must_be_child' => 'The "Copy Settings From" directive for this option must be a child option for the selected service.',
27+
'has_children' => 'This service option is a parent to one or more other options. Please delete those options before deleting this option.',
2728
],
2829
'variables' => [
2930
'env_not_unique' => 'The environment variable :name must be unique to this service option.',

tests/Unit/Services/Services/Options/OptionDeletionServiceTest.php

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,20 +11,22 @@
1111

1212
use Mockery as m;
1313
use Tests\TestCase;
14+
use Pterodactyl\Exceptions\DisplayException;
1415
use Pterodactyl\Exceptions\Service\HasActiveServersException;
1516
use Pterodactyl\Contracts\Repository\ServerRepositoryInterface;
1617
use Pterodactyl\Services\Services\Options\OptionDeletionService;
1718
use Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface;
19+
use Pterodactyl\Exceptions\Service\ServiceOption\HasChildrenException;
1820

1921
class OptionDeletionServiceTest extends TestCase
2022
{
2123
/**
22-
* @var \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface
24+
* @var \Pterodactyl\Contracts\Repository\ServiceOptionRepositoryInterface|\Mockery\Mock
2325
*/
2426
protected $repository;
2527

2628
/**
27-
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface
29+
* @var \Pterodactyl\Contracts\Repository\ServerRepositoryInterface|\Mockery\Mock
2830
*/
2931
protected $serverRepository;
3032

@@ -33,6 +35,9 @@ class OptionDeletionServiceTest extends TestCase
3335
*/
3436
protected $service;
3537

38+
/**
39+
* Setup tests.
40+
*/
3641
public function setUp()
3742
{
3843
parent::setUp();
@@ -49,6 +54,7 @@ public function setUp()
4954
public function testOptionIsDeletedIfNoServersAreFound()
5055
{
5156
$this->serverRepository->shouldReceive('findCountWhere')->with([['option_id', '=', 1]])->once()->andReturn(0);
57+
$this->repository->shouldReceive('findCountWhere')->with([['config_from', '=', 1]])->once()->andReturn(0);
5258
$this->repository->shouldReceive('delete')->with(1)->once()->andReturn(1);
5359

5460
$this->assertEquals(1, $this->service->handle(1));
@@ -63,9 +69,25 @@ public function testExceptionIsThrownIfServersAreFound()
6369

6470
try {
6571
$this->service->handle(1);
66-
} catch (\Exception $exception) {
72+
} catch (DisplayException $exception) {
6773
$this->assertInstanceOf(HasActiveServersException::class, $exception);
6874
$this->assertEquals(trans('exceptions.service.options.delete_has_servers'), $exception->getMessage());
6975
}
7076
}
77+
78+
/**
79+
* Test that an exception is thrown if children options exist.
80+
*/
81+
public function testExceptionIsThrownIfChildrenArePresent()
82+
{
83+
$this->serverRepository->shouldReceive('findCountWhere')->with([['option_id', '=', 1]])->once()->andReturn(0);
84+
$this->repository->shouldReceive('findCountWhere')->with([['config_from', '=', 1]])->once()->andReturn(1);
85+
86+
try {
87+
$this->service->handle(1);
88+
} catch (DisplayException $exception) {
89+
$this->assertInstanceOf(HasChildrenException::class, $exception);
90+
$this->assertEquals(trans('exceptions.service.options.has_children'), $exception->getMessage());
91+
}
92+
}
7193
}

0 commit comments

Comments
 (0)