Skip to content

Commit

Permalink
Merge pull request #4407 from magento-borg/borg-security-2.2
Browse files Browse the repository at this point in the history
[borg] Bug fixes
  • Loading branch information
nathanjosiah authored Jun 25, 2019
2 parents 2fcb0f9 + ac1a866 commit 5517446
Show file tree
Hide file tree
Showing 25 changed files with 1,193 additions and 79 deletions.
3 changes: 3 additions & 0 deletions app/code/Magento/CatalogImportExport/Model/Import/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ class Product extends \Magento\ImportExport\Model\Import\Entity\AbstractEntity
ValidatorInterface::ERROR_INVALID_WEIGHT => 'Product weight is invalid',
ValidatorInterface::ERROR_DUPLICATE_URL_KEY => 'Url key: \'%s\' was already generated for an item with the SKU: \'%s\'. You need to specify the unique URL key manually',
ValidatorInterface::ERROR_NEW_TO_DATE => 'Make sure new_to_date is later than or the same as new_from_date',
// Can't add new translated strings in patch release
'invalidLayoutUpdate' => 'Invalid format.',
'insufficientPermissions' => 'Invalid format.',
];

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\CatalogImportExport\Model\Import\Product\Validator;

use Magento\Framework\Config\ValidationStateInterface;
use Magento\Framework\View\Model\Layout\Update\ValidatorFactory;

/**
* Validates layout and custom layout update fields
*/
class LayoutUpdate extends AbstractImportValidator
{
/**
* @var ValidatorFactory
*/
private $layoutValidatorFactory;

/**
* @var ValidationStateInterface
*/
private $validationState;

/**
* @param ValidatorFactory $layoutValidatorFactory
* @param ValidationStateInterface $validationState
*/
public function __construct(
ValidatorFactory $layoutValidatorFactory,
ValidationStateInterface $validationState
) {
$this->layoutValidatorFactory = $layoutValidatorFactory;
$this->validationState = $validationState;
}

/**
* @inheritdoc
*/
public function isValid($value): bool
{
if (!empty($value['custom_layout_update']) && !$this->validateXml($value['custom_layout_update'])) {
$this->_addMessages(
[
$this->context->retrieveMessageTemplate('invalidLayoutUpdate')
]
);
return false;
}

return true;
}

/**
* Validate XML layout update
*
* @param string $xml
* @return bool
*/
private function validateXml(string $xml): bool
{
/** @var $layoutXmlValidator \Magento\Framework\View\Model\Layout\Update\Validator */
$layoutXmlValidator = $this->layoutValidatorFactory->create(
[
'validationState' => $this->validationState,
]
);

try {
if (!$layoutXmlValidator->isValid($xml)) {
return false;
}
} catch (\Exception $e) {
return false;
}

return true;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\CatalogImportExport\Model\Import\Product\Validator;

use Magento\Authorization\Model\UserContextInterface;
use Magento\Framework\AuthorizationInterface;
use Magento\CatalogImportExport\Model\Import\Product\Validator\AbstractImportValidator;

/**
* Validator to assert that the current user is allowed to make design updates if a layout is provided in the import
*/
class LayoutUpdatePermissions extends AbstractImportValidator
{
/**
* @var UserContextInterface
*/
private $userContext;

/**
* @var AuthorizationInterface
*/
private $authorization;

/**
* @var array
*/
private $allowedUserTypes = [
UserContextInterface::USER_TYPE_ADMIN,
UserContextInterface::USER_TYPE_INTEGRATION
];

/**
* @param UserContextInterface $userContext
* @param AuthorizationInterface $authorization
*/
public function __construct(
UserContextInterface $userContext,
AuthorizationInterface $authorization
) {
$this->userContext = $userContext;
$this->authorization = $authorization;
}

/**
* Validate that the current user is allowed to make design updates
*
* @param array $data
* @return boolean
*/
public function isValid($data): bool
{
if (empty($data['custom_layout_update'])) {
return true;
}

$userType = $this->userContext->getUserType();
$isValid = in_array($userType, $this->allowedUserTypes)
&& $this->authorization->isAllowed('Magento_Catalog::edit_product_design');

if (!$isValid) {
$this->_addMessages(
[
$this->context->retrieveMessageTemplate('insufficientPermissions'),
]
);
}

return $isValid;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product\Validator;

use Magento\CatalogImportExport\Model\Import\Product;
use Magento\Authorization\Model\UserContextInterface;
use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions;
use Magento\Framework\AuthorizationInterface;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject as MockObject;

/**
* Test validation for layout update permissions
*/
class LayoutUpdatePermissionsTest extends TestCase
{
/**
* @var LayoutUpdatePermissions|MockObject
*/
private $validator;

/**
* @var UserContextInterface|MockObject
*/
private $userContext;

/**
* @var AuthorizationInterface|MockObject
*/
private $authorization;

/**
* @var Product
*/
private $context;

protected function setUp()
{
$this->userContext = $this->createMock(UserContextInterface::class);
$this->authorization = $this->createMock(AuthorizationInterface::class);
$this->context = $this->createMock(Product::class);
$this->context
->method('retrieveMessageTemplate')
->with('insufficientPermissions')
->willReturn('oh no');
$this->validator = new LayoutUpdatePermissions(
$this->userContext,
$this->authorization
);
$this->validator->init($this->context);
}

/**
* @param $value
* @param $userContext
* @param $isAllowed
* @param $isValid
* @dataProvider configurationsProvider
*/
public function testValidationConfiguration($value, $userContext, $isAllowed, $isValid)
{
$this->userContext
->method('getUserType')
->willReturn($userContext);

$this->authorization
->method('isAllowed')
->with('Magento_Catalog::edit_product_design')
->willReturn($isAllowed);

$result = $this->validator->isValid(['custom_layout_update' => $value]);
$messages = $this->validator->getMessages();

self::assertSame($isValid, $result);

if ($isValid) {
self::assertSame([], $messages);
} else {
self::assertSame(['oh no'], $messages);
}
}

public function configurationsProvider()
{
return [
['', null, null, true],
[null, null, null, true],
['foo', UserContextInterface::USER_TYPE_ADMIN, true, true],
['foo', UserContextInterface::USER_TYPE_INTEGRATION, true, true],
['foo', UserContextInterface::USER_TYPE_ADMIN, false, false],
['foo', UserContextInterface::USER_TYPE_INTEGRATION, false, false],
['foo', 'something', null, false],
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

declare(strict_types=1);

namespace Magento\CatalogImportExport\Test\Unit\Model\Import\Product\Validator;

use Magento\CatalogImportExport\Model\Import\Product;
use Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate;
use Magento\Framework\Config\ValidationStateInterface;
use Magento\Framework\View\Model\Layout\Update\Validator;
use Magento\Framework\View\Model\Layout\Update\ValidatorFactory;
use PHPUnit\Framework\TestCase;
use PHPUnit_Framework_MockObject_MockObject as MockObject;

/**
* Test validation for layout update
*/
class LayoutUpdateTest extends TestCase
{
/**
* @var LayoutUpdate|MockObject
*/
private $validator;

/**
* @var Validator|MockObject
*/
private $layoutValidator;

protected function setUp()
{
$validatorFactory = $this->createMock(ValidatorFactory::class);
$validationState = $this->createMock(ValidationStateInterface::class);
$this->layoutValidator = $this->createMock(Validator::class);
$validatorFactory->method('create')
->with(['validationState' => $validationState])
->willReturn($this->layoutValidator);

$this->validator = new LayoutUpdate(
$validatorFactory,
$validationState
);
}

public function testValidationIsSkippedWithDataNotPresent()
{
$this->layoutValidator
->expects($this->never())
->method('isValid');

$result = $this->validator->isValid([]);
self::assertTrue($result);
}

public function testValidationFailsProperly()
{
$this->layoutValidator
->method('isValid')
->with('foo')
->willReturn(false);

$contextMock = $this->createMock(Product::class);
$contextMock
->method('retrieveMessageTemplate')
->with('invalidLayoutUpdate')
->willReturn('oh no');
$this->validator->init($contextMock);

$result = $this->validator->isValid(['custom_layout_update' => 'foo']);
$messages = $this->validator->getMessages();
self::assertFalse($result);
self::assertSame(['oh no'], $messages);
}

public function testInvalidDataException()
{
$this->layoutValidator
->method('isValid')
->willThrowException(new \Exception('foo'));

$contextMock = $this->createMock(Product::class);
$contextMock
->method('retrieveMessageTemplate')
->with('invalidLayoutUpdate')
->willReturn('oh no');
$this->validator->init($contextMock);

$result = $this->validator->isValid(['custom_layout_update' => 'foo']);
$messages = $this->validator->getMessages();
self::assertFalse($result);
self::assertSame(['oh no'], $messages);
}
}
1 change: 1 addition & 0 deletions app/code/Magento/CatalogImportExport/composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"magento/module-catalog-inventory": "100.2.*",
"magento/module-media-storage": "100.2.*",
"magento/module-customer": "101.0.*",
"magento/module-authorization": "*",
"magento/framework": "101.0.*",
"ext-ctype": "*"
},
Expand Down
7 changes: 7 additions & 0 deletions app/code/Magento/CatalogImportExport/etc/di.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@
<item name="website" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Website</item>
<item name="weight" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Weight</item>
<item name="quantity" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\Quantity</item>
<item name="layout_update" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate</item>
<item name="layout_update_permissions" xsi:type="object">Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdatePermissions</item>
</argument>
</arguments>
</type>
<type name="Magento\CatalogImportExport\Model\Import\Product\Validator\LayoutUpdate">
<arguments>
<argument name="validationState" xsi:type="object">Magento\Framework\Config\ValidationState\Required</argument>
</arguments>
</type>
</config>
Loading

0 comments on commit 5517446

Please sign in to comment.