Skip to content

Commit af2d3fe

Browse files
Merge commit from fork
* fix: Add missing permission check on AJAX actions for composer actions * fix phpstan and tests * fix 403 response * refine response messages --------- Co-authored-by: Patryk Gruszka <patryk.gruszka@mautic.org>
1 parent d03960b commit af2d3fe

File tree

3 files changed

+71
-4
lines changed

3 files changed

+71
-4
lines changed

app/bundles/MarketplaceBundle/Controller/AjaxController.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,23 @@
1515
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
1616
use Mautic\CoreBundle\Service\FlashBag;
1717
use Mautic\CoreBundle\Translation\Translator;
18+
use Mautic\MarketplaceBundle\Security\Permissions\MarketplacePermissions;
19+
use Mautic\MarketplaceBundle\Service\Config;
1820
use Psr\Log\LoggerInterface;
1921
use Symfony\Component\Console\Command\Command;
2022
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2123
use Symfony\Component\HttpFoundation\JsonResponse;
2224
use Symfony\Component\HttpFoundation\Request;
2325
use Symfony\Component\HttpFoundation\RequestStack;
26+
use Symfony\Component\HttpFoundation\Response;
2427

2528
class AjaxController extends CommonAjaxController
2629
{
2730
public function __construct(
2831
private ComposerHelper $composer,
2932
private CacheHelper $cacheHelper,
3033
private LoggerInterface $logger,
34+
private Config $config,
3135
ManagerRegistry $doctrine,
3236
MauticFactory $factory,
3337
ModelFactory $modelFactory,
@@ -44,6 +48,19 @@ public function __construct(
4448

4549
public function installPackageAction(Request $request): JsonResponse
4650
{
51+
if (!$this->config->marketplaceIsEnabled()) {
52+
return $this->sendJsonResponse([
53+
'error' => $this->translator->trans('marketplace.package.request.marketplace_disabled'),
54+
], Response::HTTP_BAD_REQUEST);
55+
}
56+
57+
if (!$this->security->isGranted(MarketplacePermissions::CAN_INSTALL_PACKAGES)
58+
|| !$this->config->isComposerEnabled()) {
59+
return $this->sendJsonResponse([
60+
'error' => $this->translator->trans('marketplace.package.request.no_permissions'),
61+
], Response::HTTP_FORBIDDEN);
62+
}
63+
4764
$data = json_decode($request->getContent(), true);
4865

4966
if (empty($data['vendor']) || empty($data['package'])) {
@@ -82,6 +99,19 @@ public function installPackageAction(Request $request): JsonResponse
8299

83100
public function removePackageAction(Request $request): JsonResponse
84101
{
102+
if (!$this->config->marketplaceIsEnabled()) {
103+
return $this->sendJsonResponse([
104+
'error' => $this->translator->trans('marketplace.package.request.marketplace_disabled'),
105+
], Response::HTTP_BAD_REQUEST);
106+
}
107+
108+
if (!$this->security->isGranted(MarketplacePermissions::CAN_REMOVE_PACKAGES)
109+
|| !$this->config->isComposerEnabled()) {
110+
return $this->sendJsonResponse([
111+
'error' => $this->translator->trans('marketplace.package.request.no_permissions'),
112+
], Response::HTTP_FORBIDDEN);
113+
}
114+
85115
$data = json_decode($request->getContent(), true);
86116

87117
if (empty($data['vendor']) || empty($data['package'])) {

app/bundles/MarketplaceBundle/Tests/Functional/Controller/AjaxControllerTest.php

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,44 @@
1717
use Mautic\CoreBundle\Translation\Translator;
1818
use Mautic\MarketplaceBundle\Controller\AjaxController;
1919
use Mautic\MarketplaceBundle\DTO\ConsoleOutput;
20+
use Mautic\MarketplaceBundle\Security\Permissions\MarketplacePermissions;
21+
use Mautic\MarketplaceBundle\Service\Config;
2022
use PHPUnit\Framework\Assert;
23+
use PHPUnit\Framework\MockObject\MockObject;
2124
use Psr\Log\LoggerInterface;
2225
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
2326
use Symfony\Component\HttpFoundation\Request;
2427
use Symfony\Component\HttpFoundation\RequestStack;
2528

2629
final class AjaxControllerTest extends AbstractMauticTestCase
2730
{
31+
/**
32+
* @var MockObject|CorePermissions
33+
*/
34+
private MockObject $security;
35+
36+
/**
37+
* @var MockObject|Config
38+
*/
39+
private MockObject $marketplaceConfig;
40+
41+
/**
42+
* @var MockObject|RequestStack
43+
*/
44+
private MockObject $requestStack;
45+
2846
public function testInstallPackageAction(): void
2947
{
3048
$request = new Request([], [], [], [], [], [], '{"vendor":"mautic","package":"test-plugin-bundle"}');
3149
$controller = $this->generateController(false);
3250

51+
$this->marketplaceConfig->method('marketplaceIsEnabled')->willReturn(true);
52+
$this->marketplaceConfig->method('isComposerEnabled')->willReturn(true);
53+
$this->security->expects($this->any())
54+
->method('isGranted')
55+
->with(MarketplacePermissions::CAN_INSTALL_PACKAGES)
56+
->willReturn(true);
57+
3358
$response = $controller->installPackageAction($request);
3459

3560
Assert::assertSame('{"success":true}', $response->getContent());
@@ -41,6 +66,13 @@ public function testRemovePackageAction(): void
4166
$request = new Request([], [], [], [], [], [], '{"vendor":"mautic","package":"test-plugin-bundle"}');
4267
$controller = $this->generateController(true);
4368

69+
$this->marketplaceConfig->method('marketplaceIsEnabled')->willReturn(true);
70+
$this->marketplaceConfig->method('isComposerEnabled')->willReturn(true);
71+
$this->security->expects($this->any())
72+
->method('isGranted')
73+
->with(MarketplacePermissions::CAN_REMOVE_PACKAGES)
74+
->willReturn(true);
75+
4476
$response = $controller->removePackageAction($request);
4577

4678
Assert::assertSame('{"success":true}', $response->getContent());
@@ -66,13 +98,16 @@ private function generateController(bool $isPackageInstalled): AjaxController
6698
$dispatcher = $this->createMock(EventDispatcherInterface::class);
6799
$translator = $this->createMock(Translator::class);
68100
$flashBag = $this->createMock(FlashBag::class);
69-
$requestStack = new RequestStack();
70-
$security = $this->createMock(CorePermissions::class);
101+
$this->requestStack = $this->createMock(RequestStack::class);
102+
103+
$this->security = $this->createMock(CorePermissions::class);
104+
$this->marketplaceConfig = $this->createMock(Config::class);
71105

72106
$controller = new AjaxController(
73107
$composer,
74108
$cacheHelper,
75109
$logger,
110+
$this->marketplaceConfig,
76111
$doctrine,
77112
$factory,
78113
$modelFactory,
@@ -81,8 +116,8 @@ private function generateController(bool $isPackageInstalled): AjaxController
81116
$dispatcher,
82117
$translator,
83118
$flashBag,
84-
$requestStack,
85-
$security
119+
$this->requestStack,
120+
$this->security
86121
);
87122
$controller->setContainer(static::getContainer());
88123

app/bundles/MarketplaceBundle/Translations/en_US/messages.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ marketplace.package.install.html.failed="Something went wrong while installing <
4343
marketplace.package.install.html.in.progress="<strong>%packagename%</strong> is being installed. This might take a while..."
4444
marketplace.package.install.html.success="Successfully installed <strong>%packagename%</strong>!"
4545
marketplace.package.install.html.success.continue="Go to the plugin page to activate the plugin"
46+
marketplace.package.request.marketplace_disabled="The marketplace is disabled."
47+
marketplace.package.request.no_permissions="You don't have permission to perform this action."
4648
marketplace.package.request.details.missing="The package vendor or name has not been provided. Please try again."
4749
marketplace.package.cache.clear.failed="Couldn't refresh plugins list. Please ask a developer to check server permissions and try again."
4850
marketplace.package.remove.not.installed="The selected package is not currently installed and can therefore not be removed. Please try again with a different package name."

0 commit comments

Comments
 (0)