From a82bbb38c015ae8a99ecfa6fb6d15f1654a853ab Mon Sep 17 00:00:00 2001 From: Alexander Paliarush Date: Thu, 23 Jul 2015 08:45:21 -0500 Subject: [PATCH 01/31] MAGETWO-40641: Make Data Object Encoding-Decoding Reusable --- lib/internal/Magento/Framework/Reflection/TypeProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php index c4d0bc217eb6e..c05d6ba81c988 100644 --- a/lib/internal/Magento/Framework/Reflection/TypeProcessor.php +++ b/lib/internal/Magento/Framework/Reflection/TypeProcessor.php @@ -591,9 +591,9 @@ protected function findAccessorMethodName( $methodName = $boolAccessorName; return $methodName; } else { - throw new \Exception( + throw new \LogicException( sprintf( - 'Property :"%s" does not exist in the provided class: "%s".', + 'Property "%s" does not have corresponding setter in class "%s".', $camelCaseProperty, $class->getName() ) From 72b904b3a236a9fdae6e7220d0e9b869dd308292 Mon Sep 17 00:00:00 2001 From: Bryant Luk Date: Fri, 31 Jul 2015 10:44:45 -0500 Subject: [PATCH 02/31] MAGETWO-39994: Configure connection with the RabbitMQ in env.php - Change word for phpdoc --- .../Magento/Framework/Setup/ConfigOptionsListInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/Magento/Framework/Setup/ConfigOptionsListInterface.php b/lib/internal/Magento/Framework/Setup/ConfigOptionsListInterface.php index 40251a31bb616..5a7e2adffcd30 100644 --- a/lib/internal/Magento/Framework/Setup/ConfigOptionsListInterface.php +++ b/lib/internal/Magento/Framework/Setup/ConfigOptionsListInterface.php @@ -21,7 +21,7 @@ interface ConfigOptionsListInterface public function getOptions(); /** - * Creates array of ConfigData objects from user inputted data. + * Creates array of ConfigData objects from user input data. * Data in these objects will be stored in array form in deployment config file. * * @param array $options From 4d290a0af0bb3faffa24a26ac0cce0814ae3b91e Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Mon, 3 Aug 2015 18:36:26 +0300 Subject: [PATCH 03/31] MAGETWO-40260: Merge Branches and Stabilize Builds - fixes for L3,L4 builds --- .../Webapi/ServiceInputProcessor.php | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index de651a1539176..15ad2b9e8f99f 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -111,17 +111,7 @@ public function process($serviceClassName, $serviceMethodName, array $inputArray } } } - - if (!empty($inputError)) { - $exception = new InputException(); - foreach ($inputError as $errorParamField) { - $exception->addError(new Phrase(InputException::REQUIRED_FIELD, ['fieldName' => $errorParamField])); - } - if ($exception->wasErrorAdded()) { - throw $exception; - } - } - + $this->processInputError($inputError); return $inputData; } @@ -250,7 +240,7 @@ protected function _createDataObjectForTypeAndArrayValue($type, $customAttribute * @param mixed $value * @param string $type Convert given value to the this type * @return mixed - * @throws \Magento\Framework\LogicException + * @throws \Magento\Framework\Exception\LocalizedException */ public function convertValue($value, $type) { @@ -336,4 +326,23 @@ protected function getMethodParams($serviceClassName, $serviceMethodName) $this->cache->save(serialize($params), $cacheId, [WebapiCache::CACHE_TAG]); return $params; } + + /** + * Process an input error + * + * @param array $inputError + * @throws InputException + */ + protected function processInputError($inputError) + { + if (!empty($inputError)) { + $exception = new InputException(); + foreach ($inputError as $errorParamField) { + $exception->addError(new Phrase(InputException::REQUIRED_FIELD, ['fieldName' => $errorParamField])); + } + if ($exception->wasErrorAdded()) { + throw $exception; + } + } + } } From 75fa1d46c486a039e74853f78dcf86078a0a6378 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Mon, 3 Aug 2015 19:51:18 +0300 Subject: [PATCH 04/31] MAGETWO-40260: Merge Branches and Stabilize Builds - fixes for L3,L4 builds --- lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php index 15ad2b9e8f99f..6dd7f20908a28 100644 --- a/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php +++ b/lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php @@ -331,6 +331,7 @@ protected function getMethodParams($serviceClassName, $serviceMethodName) * Process an input error * * @param array $inputError + * @return void * @throws InputException */ protected function processInputError($inputError) From a54cad00b795db6eb30d3ce0fef796b45847b743 Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Tue, 18 Aug 2015 17:17:20 -0500 Subject: [PATCH 05/31] MAGETWO-38568: Move PCI module code to new CE module - moved Pci module from EE to CE and renamed PasswordManagement --- .../Block/Adminhtml/Crypt/Key/Edit.php | 58 +++ .../Block/Adminhtml/Crypt/Key/Form.php | 55 +++ .../Block/Adminhtml/Locks.php | 24 ++ .../Controller/Adminhtml/Crypt/Key.php | 24 ++ .../Controller/Adminhtml/Crypt/Key/Index.php | 42 ++ .../Controller/Adminhtml/Crypt/Key/Save.php | 86 ++++ .../Controller/Adminhtml/Locks.php | 24 ++ .../Controller/Adminhtml/Locks/Grid.php | 21 + .../Controller/Adminhtml/Locks/Index.php | 23 ++ .../Controller/Adminhtml/Locks/MassUnlock.php | 35 ++ .../Magento/PasswordManagement/LICENSE.txt | 48 +++ .../PasswordManagement/LICENSE_AFL.txt | 48 +++ .../Model/Backend/Observer.php | 377 ++++++++++++++++++ .../PasswordManagement/Model/Observer.php | 46 +++ .../Model/Resource/Admin/User.php | 166 ++++++++ .../Model/Resource/Key/Change.php | 209 ++++++++++ .../Model/System/Config/Source/Password.php | 25 ++ app/code/Magento/PasswordManagement/README.md | 2 + .../Setup/InstallSchema.php | 103 +++++ .../Test/Unit/Model/ObserverTest.php | 88 ++++ .../Magento/PasswordManagement/composer.json | 26 ++ .../Magento/PasswordManagement/etc/acl.xml | 23 ++ .../PasswordManagement/etc/adminhtml/di.xml | 14 + .../etc/adminhtml/events.xml | 21 + .../PasswordManagement/etc/adminhtml/menu.xml | 13 + .../etc/adminhtml/routes.xml | 14 + .../etc/adminhtml/system.xml | 31 ++ .../Magento/PasswordManagement/etc/config.xml | 20 + .../etc/frontend/events.xml | 12 + .../PasswordManagement/etc/logging.xml | 28 ++ .../Magento/PasswordManagement/etc/module.xml | 15 + .../Magento/PasswordManagement/i18n/de_DE.csv | 39 ++ .../Magento/PasswordManagement/i18n/en_US.csv | 39 ++ .../Magento/PasswordManagement/i18n/es_ES.csv | 39 ++ .../Magento/PasswordManagement/i18n/fr_FR.csv | 39 ++ .../Magento/PasswordManagement/i18n/nl_NL.csv | 39 ++ .../Magento/PasswordManagement/i18n/pt_BR.csv | 39 ++ .../Magento/PasswordManagement/i18n/zh_CN.csv | 39 ++ .../layout/adminhtml_crypt_key_index.xml | 16 + .../layout/adminhtml_locks_block.xml | 94 +++++ .../adminhtml/layout/adminhtml_locks_grid.xml | 14 + .../layout/adminhtml_locks_index.xml | 16 + composer.json | 1 + .../Block/Adminhtml/Crypt/Key/EditTest.php | 26 ++ .../Block/Adminhtml/Crypt/Key/FormTest.php | 54 +++ .../Adminhtml/Crypt/Key/IndexTest.php | 20 + .../Adminhtml/Crypt/Key/SaveTest.php | 105 +++++ .../Controller/Adminhtml/Locks/GridTest.php | 35 ++ .../Controller/Adminhtml/Locks/IndexTest.php | 31 ++ .../Adminhtml/Locks/MassUnlockTest.php | 38 ++ .../Model/Resource/Key/ChangeTest.php | 98 +++++ .../_files/locked_users.php | 51 +++ .../_files/payment_info.php | 20 + .../_files/dependency_test/tables_ce.php | 1 + .../connection/blacklist/files_list.php | 2 + 55 files changed, 2616 insertions(+) create mode 100644 app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php create mode 100644 app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php create mode 100644 app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php create mode 100644 app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php create mode 100644 app/code/Magento/PasswordManagement/LICENSE.txt create mode 100644 app/code/Magento/PasswordManagement/LICENSE_AFL.txt create mode 100644 app/code/Magento/PasswordManagement/Model/Backend/Observer.php create mode 100644 app/code/Magento/PasswordManagement/Model/Observer.php create mode 100644 app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php create mode 100644 app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php create mode 100644 app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php create mode 100644 app/code/Magento/PasswordManagement/README.md create mode 100644 app/code/Magento/PasswordManagement/Setup/InstallSchema.php create mode 100644 app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php create mode 100644 app/code/Magento/PasswordManagement/composer.json create mode 100644 app/code/Magento/PasswordManagement/etc/acl.xml create mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/di.xml create mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/events.xml create mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml create mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml create mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/system.xml create mode 100644 app/code/Magento/PasswordManagement/etc/config.xml create mode 100644 app/code/Magento/PasswordManagement/etc/frontend/events.xml create mode 100644 app/code/Magento/PasswordManagement/etc/logging.xml create mode 100644 app/code/Magento/PasswordManagement/etc/module.xml create mode 100644 app/code/Magento/PasswordManagement/i18n/de_DE.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/en_US.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/es_ES.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/fr_FR.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/nl_NL.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/pt_BR.csv create mode 100644 app/code/Magento/PasswordManagement/i18n/zh_CN.csv create mode 100644 app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml create mode 100644 app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml create mode 100644 app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml create mode 100644 app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php create mode 100644 dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php b/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php new file mode 100644 index 0000000000000..3c0b7663cef12 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php @@ -0,0 +1,58 @@ +buttonList->add( + 'save', + [ + 'label' => __('Change Encryption Key'), + 'class' => 'save primary save-encryption-key', + 'data_attribute' => [ + 'mage-init' => ['button' => ['event' => 'save', 'target' => '#edit_form']], + ] + ], + 1 + ); + } + + /** + * Header text getter + * + * @return \Magento\Framework\Phrase + */ + public function getHeaderText() + { + return __('Encryption Key'); + } +} diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php b/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php new file mode 100644 index 0000000000000..f94ed96f1f436 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php @@ -0,0 +1,55 @@ +_formFactory->create( + ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']] + ); + $fieldset = $form->addFieldset('main_fieldset', ['legend' => __('New Encryption Key')]); + $fieldset->addField( + 'enc_key_note', + 'note', + ['text' => __('The encryption key is used to protect passwords and other sensitive data.')] + ); + $fieldset->addField( + 'generate_random', + 'select', + [ + 'name' => 'generate_random', + 'label' => __('Auto-generate a Key'), + 'options' => [0 => __('No'), 1 => __('Yes')], + 'onclick' => "var cryptKey = $('crypt_key'); cryptKey.disabled = this.value == 1; if (cryptKey.disabled) {cryptKey.parentNode.parentNode.hide();} else {cryptKey.parentNode.parentNode.show();}", + 'note' => __('The generated key will be displayed after changing.') + ] + ); + $fieldset->addField( + 'crypt_key', + 'text', + ['name' => 'crypt_key', 'label' => __('New Key'), 'style' => 'width:32em;', 'maxlength' => 32] + ); + $form->setUseContainer(true); + if ($data = $this->getFormData()) { + $form->addValues($data); + } + $this->setForm($form); + return parent::_prepareForm(); + } +} diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php b/app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php new file mode 100644 index 0000000000000..98f253adba442 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php @@ -0,0 +1,24 @@ +buttonList->remove('add'); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php new file mode 100644 index 0000000000000..47ef3865ba62e --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php @@ -0,0 +1,24 @@ +_authorization->isAllowed('Magento_PasswordManagement::crypt_key'); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php new file mode 100644 index 0000000000000..99ced594c1199 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php @@ -0,0 +1,42 @@ +_objectManager->get('Magento\Framework\App\DeploymentConfig\Writer'); + if (!$writer->checkIfWritable()) { + $this->messageManager->addError(__('Deployment configuration file is not writable.')); + } + + $this->_view->loadLayout(); + $this->_setActiveMenu('Magento_PasswordManagement::system_crypt_key'); + $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Encryption Key')); + + if (($formBlock = $this->_view->getLayout()->getBlock( + 'pm.crypt.key.form' + )) && ($data = $this->_objectManager->get( + 'Magento\Backend\Model\Session' + )->getFormData( + true + )) + ) { + /* @var \Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form $formBlock */ + $formBlock->setFormData($data); + } + + $this->_view->renderLayout(); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php new file mode 100644 index 0000000000000..4d9f618fdaf6f --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php @@ -0,0 +1,86 @@ +encryptor = $encryptor; + $this->change = $change; + $this->cache = $cache; + parent::__construct($context); + } + + /** + * Process saving new encryption key + * + * @return void + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + */ + public function execute() + { + try { + $key = null; + + if (0 == $this->getRequest()->getPost('generate_random')) { + $key = $this->getRequest()->getPost('crypt_key'); + if (empty($key)) { + throw new \Exception(__('Please enter an encryption key.')); + } + $this->encryptor->validateKey($key); + } + + $newKey = $this->change->changeEncryptionKey($key); + $this->messageManager->addSuccess(__('The encryption key has been changed.')); + + if (!$key) { + $this->messageManager->addNotice( + __( + 'This is your new encryption key: %1. Be sure to write it down and take good care of it!', + $newKey + ) + ); + } + $this->cache->clean(); + } catch (\Exception $e) { + if ($message = $e->getMessage()) { + $this->messageManager->addError($e->getMessage()); + } + $this->_session->setFormData(['crypt_key' => $key]); + } + $this->_redirect('adminhtml/*/'); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php new file mode 100644 index 0000000000000..0d9891cc51e44 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php @@ -0,0 +1,24 @@ +_authorization->isAllowed('Magento_PasswordManagement::locks'); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php new file mode 100644 index 0000000000000..e917922494555 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php @@ -0,0 +1,21 @@ +_view->loadLayout(false); + $this->_view->renderLayout(); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php new file mode 100644 index 0000000000000..4536dad76a528 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php @@ -0,0 +1,23 @@ +_view->loadLayout(); + $this->_setActiveMenu('Magento_PasswordManagement::system_acl_locks'); + $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Locked Users')); + $this->_view->renderLayout(); + } +} diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php new file mode 100644 index 0000000000000..4314db27f60ea --- /dev/null +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php @@ -0,0 +1,35 @@ +getRequest()->getPost('unlock'); + if ($userIds && is_array($userIds)) { + $affectedUsers = $this->_objectManager->get('Magento\PasswordManagement\Model\Resource\Admin\User')->unlock($userIds); + $this->getMessageManager()->addSuccess(__('Unlocked %1 user(s).', $affectedUsers)); + } + } catch (\Exception $e) { + $this->messageManager->addError($e->getMessage()); + } + + /** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */ + $resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT); + return $resultRedirect->setPath('adminhtml/*/'); + } +} diff --git a/app/code/Magento/PasswordManagement/LICENSE.txt b/app/code/Magento/PasswordManagement/LICENSE.txt new file mode 100644 index 0000000000000..49525fd99da9c --- /dev/null +++ b/app/code/Magento/PasswordManagement/LICENSE.txt @@ -0,0 +1,48 @@ + +Open Software License ("OSL") v. 3.0 + +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including 'fair use' or 'fair dealing'). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright (C) 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/PasswordManagement/LICENSE_AFL.txt b/app/code/Magento/PasswordManagement/LICENSE_AFL.txt new file mode 100644 index 0000000000000..87943b95d43a5 --- /dev/null +++ b/app/code/Magento/PasswordManagement/LICENSE_AFL.txt @@ -0,0 +1,48 @@ + +Academic Free License ("AFL") v. 3.0 + +This Academic Free License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Academic Free License version 3.0 + + 1. Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + + 1. to reproduce the Original Work in copies, either alone or as part of a collective work; + + 2. to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + + 3. to distribute or communicate copies of the Original Work and Derivative Works to the public, under any license of your choice that does not contradict the terms and conditions, including Licensor's reserved rights and remedies, in this Academic Free License; + + 4. to perform the Original Work publicly; and + + 5. to display the Original Work publicly. + + 2. Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + + 3. Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + + 4. Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + + 5. External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + + 6. Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + + 7. Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + + 8. Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + + 9. Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + + 10. Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + + 11. Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + + 12. Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + + 13. Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + + 14. Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + 15. Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + + 16. Modification of This License. This License is Copyright 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Academic Free License" or "AFL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. \ No newline at end of file diff --git a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php new file mode 100644 index 0000000000000..b480df5481298 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php @@ -0,0 +1,377 @@ +_authorization = $authorization; + $this->_backendConfig = $backendConfig; + $this->_userResource = $userResource; + $this->_url = $url; + $this->_session = $session; + $this->_authSession = $authSession; + $this->_userFactory = $userFactory; + $this->_encryptor = $encryptor; + $this->_actionFlag = $actionFlag; + $this->messageManager = $messageManager; + } + + /** + * Admin locking and password hashing upgrade logic implementation + * + * @param EventObserver $observer + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function adminAuthenticate($observer) + { + $password = $observer->getEvent()->getPassword(); + $user = $observer->getEvent()->getUser(); + $resource = $this->_userResource; + $authResult = $observer->getEvent()->getResult(); + + // update locking information regardless whether user locked or not + if (!$authResult && $user->getId()) { + $now = new \DateTime(); + $lockThreshold = $this->getAdminLockThreshold(); + $maxFailures = (int)$this->_backendConfig->getValue('admin/security/lockout_failures'); + if (!($lockThreshold && $maxFailures)) { + return; + } + $failuresNum = (int)$user->getFailuresNum() + 1; + $firstFailureDate = $user->getFirstFailure(); + if ($firstFailureDate) { + $firstFailureDate = new \DateTime($firstFailureDate); + } + + $updateFirstFailureDate = false; + $updateLockExpires = false; + $lockThresholdInterval = new \DateInterval('PT' . $lockThreshold.'S'); + // set first failure date when this is first failure or last first failure expired + if (1 === $failuresNum || !$firstFailureDate || $now->diff($firstFailureDate) > $lockThresholdInterval) { + $updateFirstFailureDate = $now; + // otherwise lock user + } elseif ($failuresNum >= $maxFailures) { + $updateLockExpires = $now->add($lockThresholdInterval); + } + $resource->updateFailure($user, $updateLockExpires, $updateFirstFailureDate); + } + + // check whether user is locked + $lockExpires = $user->getLockExpires(); + if ($lockExpires) { + $lockExpires = new \DateTime($lockExpires); + if ($lockExpires > new \DateTime()) { + throw new UserLockedException( + __('You did not sign in correctly or your account is temporarily disabled.') + ); + } + } + + if (!$authResult) { + return; + } + + $resource->unlock($user->getId()); + + /** + * Check whether the latest password is expired + * Side-effect can be when passwords were changed with different lifetime configuration settings + */ + $latestPassword = $this->_userResource->getLatestPassword($user->getId()); + if ($latestPassword) { + if ($this->_isLatestPasswordExpired($latestPassword)) { + if ($this->isPasswordChangeForced()) { + $message = __('It\'s time to change your password.'); + } else { + $myAccountUrl = $this->_url->getUrl('adminhtml/system_account/'); + $message = __('It\'s time to change your password.', $myAccountUrl); + } + $this->messageManager->addNotice($message); + $message = $this->messageManager->getMessages()->getLastAddedMessage(); + if ($message) { + $message->setIdentifier('magento_pm_password_expired')->setIsSticky(true); + $this->_authSession->setPciAdminUserIsPasswordExpired(true); + } + } + } + + // upgrade admin password + if (!$this->_encryptor->validateHashByVersion($password, $user->getPassword())) { + $this->_userFactory->create()->load( + $user->getId() + )->setNewPassword( + $password + )->setForceNewPassword( + true + )->save(); + } + } + + /** + * Check if latest password is expired + * + * @param array $latestPassword + * @return bool + */ + protected function _isLatestPasswordExpired($latestPassword) + { + if (!isset($latestPassword['expires'])) { + return false; + } + + if ($this->getAdminPasswordLifetime() == 0) { + return false; + } + + return (int)$latestPassword['expires'] < time(); + } + + /** + * Harden admin password change. + * + * New password must be minimum 7 chars length and include alphanumeric characters + * The password is compared to at least last 4 previous passwords to prevent setting them again + * + * @param EventObserver $observer + * @return void + * @throws \Magento\Framework\Exception\LocalizedException + */ + public function checkAdminPasswordChange($observer) + { + /* @var $user \Magento\User\Model\User */ + $user = $observer->getEvent()->getObject(); + + if ($user->getNewPassword()) { + $password = $user->getNewPassword(); + } else { + $password = $user->getPassword(); + } + + if ($password && !$user->getForceNewPassword() && $user->getId()) { + if ($this->_encryptor->validateHash($password, $user->getOrigData('password'))) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Sorry, but this password has already been used. Please create another.') + ); + } + + // check whether password was used before + $resource = $this->_userResource; + $passwordHash = $this->_encryptor->getHash($password, false); + foreach ($resource->getOldPasswords($user) as $oldPasswordHash) { + if ($passwordHash === $oldPasswordHash) { + throw new \Magento\Framework\Exception\LocalizedException( + __('Sorry, but this password has already been used. Please create another.') + ); + } + } + } + } + + /** + * Save new admin password + * + * @param EventObserver $observer + * @return void + */ + public function trackAdminNewPassword($observer) + { + /* @var $user \Magento\User\Model\User */ + $user = $observer->getEvent()->getObject(); + if ($user->getId()) { + $password = $user->getNewPassword(); + $passwordLifetime = $this->getAdminPasswordLifetime(); + if ($passwordLifetime && $password && !$user->getForceNewPassword()) { + $resource = $this->_userResource; + $passwordHash = $this->_encryptor->getHash($password, false); + $resource->trackPassword($user, $passwordHash, $passwordLifetime); + $this->messageManager->getMessages()->deleteMessageByIdentifier('magento_pm_password_expired'); + $this->_authSession->unsPciAdminUserIsPasswordExpired(); + } + } + } + + /** + * Get admin lock threshold from configuration + * + * @return int + */ + public function getAdminLockThreshold() + { + return 60 * (int)$this->_backendConfig->getValue('admin/security/lockout_threshold'); + } + + /** + * Get admin password lifetime + * + * @return int + */ + public function getAdminPasswordLifetime() + { + return 86400 * (int)$this->_backendConfig->getValue('admin/security/password_lifetime'); + } + + /** + * Force admin to change password + * + * @param EventObserver $observer + * @return void + */ + public function forceAdminPasswordChange($observer) + { + if (!$this->isPasswordChangeForced()) { + return; + } + $session = $this->_authSession; + if (!$session->isLoggedIn()) { + return; + } + $actionList = [ + 'adminhtml_system_account_index', + 'adminhtml_system_account_save', + 'adminhtml_auth_logout', + ]; + $controller = $observer->getEvent()->getControllerAction(); + /** @var \Magento\Framework\App\RequestInterface $request */ + $request = $observer->getEvent()->getRequest(); + if ($this->_authSession->getPciAdminUserIsPasswordExpired()) { + if (!in_array($request->getFullActionName(), $actionList)) { + if ($this->_authorization->isAllowed('Magento_Backend::myaccount')) { + $controller->getResponse()->setRedirect($this->_url->getUrl('adminhtml/system_account/')); + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_POST_DISPATCH, true); + } else { + /* + * if admin password is expired and access to 'My Account' page is denied + * than we need to do force logout with error message + */ + $this->_authSession->clearStorage(); + $this->_session->clearStorage(); + $this->messageManager->addError( + __('Your password has expired; please contact your administrator.') + ); + $controller->getRequest()->setDispatched(false); + } + } + } + } + + /** + * Check whether password change is forced + * + * @return bool + */ + public function isPasswordChangeForced() + { + return (bool)(int)$this->_backendConfig->getValue('admin/security/password_is_forced'); + } +} diff --git a/app/code/Magento/PasswordManagement/Model/Observer.php b/app/code/Magento/PasswordManagement/Model/Observer.php new file mode 100644 index 0000000000000..c608749d7f5bb --- /dev/null +++ b/app/code/Magento/PasswordManagement/Model/Observer.php @@ -0,0 +1,46 @@ +_encryptor = $encryptor; + } + + /** + * Upgrade customer password hash when customer has logged in + * + * @param EventObserver $observer + * @return void + */ + public function upgradeCustomerPassword($observer) + { + $password = $observer->getEvent()->getPassword(); + $model = $observer->getEvent()->getModel(); + if (!$this->_encryptor->validateHashByVersion($password, $model->getPasswordHash())) { + $model->changePassword($password); + } + } +} diff --git a/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php b/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php new file mode 100644 index 0000000000000..c4682fef8cc85 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php @@ -0,0 +1,166 @@ +getConnection()->update( + $this->getMainTable(), + ['failures_num' => 0, 'first_failure' => null, 'lock_expires' => null], + $this->getIdFieldName() . ' IN (' . $this->getConnection()->quote($userIds) . ')' + ); + } + + /** + * Lock specified user record(s) + * + * @param int|int[] $userIds + * @param int $exceptId + * @param int $lifetime + * @return int number of affected rows + */ + public function lock($userIds, $exceptId, $lifetime) + { + if (!is_array($userIds)) { + $userIds = [$userIds]; + } + $exceptId = (int)$exceptId; + return $this->getConnection()->update( + $this->getMainTable(), + ['lock_expires' => $this->dateTime->formatDate(time() + $lifetime)], + "{$this->getIdFieldName()} IN (" . $this->getConnection()->quote( + $userIds + ) . ")\n AND {$this->getIdFieldName()} <> {$exceptId}" + ); + } + + /** + * Increment failures count along with updating lock expire and first failure dates + * + * @param ModelUser $user + * @param int|false $setLockExpires + * @param int|false $setFirstFailure + * @return void + */ + public function updateFailure($user, $setLockExpires = false, $setFirstFailure = false) + { + $update = ['failures_num' => new \Zend_Db_Expr('failures_num + 1')]; + if (false !== $setFirstFailure) { + $update['first_failure'] = $this->dateTime->formatDate($setFirstFailure); + $update['failures_num'] = 1; + } + if (false !== $setLockExpires) { + $update['lock_expires'] = $this->dateTime->formatDate($setLockExpires); + } + $this->getConnection()->update( + $this->getMainTable(), + $update, + $this->getConnection()->quoteInto("{$this->getIdFieldName()} = ?", $user->getId()) + ); + } + + /** + * Purge and get remaining old password hashes + * + * @param ModelUser $user + * @param int $retainLimit + * @return array + */ + public function getOldPasswords($user, $retainLimit = 4) + { + $userId = (int)$user->getId(); + $table = $this->getTable('admin_passwords'); + + // purge expired passwords, except that should retain + $retainPasswordIds = $this->getConnection()->fetchCol( + $this->getConnection()->select()->from( + $table, + 'password_id' + )->where( + 'user_id = :user_id' + )->order( + 'expires ' . \Magento\Framework\DB\Select::SQL_DESC + )->order( + 'password_id ' . \Magento\Framework\DB\Select::SQL_DESC + )->limit( + $retainLimit + ), + [':user_id' => $userId] + ); + $where = ['user_id = ?' => $userId, 'expires <= ?' => time()]; + if ($retainPasswordIds) { + $where['password_id NOT IN (?)'] = $retainPasswordIds; + } + $this->getConnection()->delete($table, $where); + + // now get all remained passwords + return $this->getConnection()->fetchCol( + $this->getConnection()->select()->from($table, 'password_hash')->where('user_id = :user_id'), + [':user_id' => $userId] + ); + } + + /** + * Remember a password hash for further usage + * + * @param ModelUser $user + * @param string $passwordHash + * @param int $lifetime + * @return void + */ + public function trackPassword($user, $passwordHash, $lifetime) + { + $now = time(); + $this->getConnection()->insert( + $this->getTable('admin_passwords'), + [ + 'user_id' => $user->getId(), + 'password_hash' => $passwordHash, + 'expires' => $now + $lifetime, + 'last_updated' => $now + ] + ); + } + + /** + * Get latest password for specified user id + * Possible false positive when password was changed several times with different lifetime configuration + * + * @param int $userId + * @return array + */ + public function getLatestPassword($userId) + { + return $this->getConnection()->fetchRow( + $this->getConnection()->select()->from( + $this->getTable('admin_passwords') + )->where( + 'user_id = :user_id' + )->order( + 'password_id ' . \Magento\Framework\DB\Select::SQL_DESC + )->limit( + 1 + ), + [':user_id' => $userId] + ); + } +} diff --git a/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php b/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php new file mode 100644 index 0000000000000..ebb759b90b486 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php @@ -0,0 +1,209 @@ + + */ +class Change extends \Magento\Framework\Model\Resource\Db\AbstractDb +{ + /** + * Encryptor interface + * + * @var \Magento\Framework\Encryption\EncryptorInterface + */ + protected $_encryptor; + + /** + * Filesystem directory write interface + * + * @var \Magento\Framework\Filesystem\Directory\WriteInterface + */ + protected $_directory; + + /** + * System configuration structure + * + * @var \Magento\Config\Model\Config\Structure + */ + protected $_structure; + + /** + * Configuration writer + * + * @var \Magento\Framework\App\DeploymentConfig\Writer + */ + protected $_writer; + + /** + * @param \Magento\Framework\Model\Resource\Db\Context $context + * @param \Magento\Framework\Filesystem $filesystem + * @param \Magento\Config\Model\Config\Structure $structure + * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor + * @param \Magento\Framework\App\DeploymentConfig\Writer $writer + * @param string $connectionName + */ + public function __construct( + \Magento\Framework\Model\Resource\Db\Context $context, + \Magento\Framework\Filesystem $filesystem, + \Magento\Config\Model\Config\Structure $structure, + \Magento\Framework\Encryption\EncryptorInterface $encryptor, + \Magento\Framework\App\DeploymentConfig\Writer $writer, + $connectionName = null + ) { + $this->_encryptor = clone $encryptor; + parent::__construct($context, $connectionName); + $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::CONFIG); + $this->_structure = $structure; + $this->_writer = $writer; + } + + /** + * Initialize + * + * @return void + */ + protected function _construct() + { + $this->_init('core_config_data', 'config_id'); + } + + /** + * Re-encrypt all encrypted data in the database + * + * TODO: seems not used + * + * @param bool $safe Specifies whether wrapping re-encryption into the database transaction or not + * @return void + * @throws \Exception + */ + public function reEncryptDatabaseValues($safe = true) + { + // update database only + if ($safe) { + $this->beginTransaction(); + } + try { + $this->_reEncryptSystemConfigurationValues(); + $this->_reEncryptCreditCardNumbers(); + if ($safe) { + $this->commit(); + } + } catch (\Exception $e) { + if ($safe) { + $this->rollBack(); + } + throw $e; + } + } + + /** + * Change encryption key + * + * @param string|null $key + * @return null|string + * @throws \Exception + */ + public function changeEncryptionKey($key = null) + { + // prepare new key, encryptor and new configuration segment + if (!$this->_writer->checkIfWritable()) { + throw new \Exception(__('Deployment configuration file is not writable.')); + } + + if (null === $key) { + $key = md5(time()); + } + $this->_encryptor->setNewKey($key); + + $encryptSegment = new ConfigData(ConfigFilePool::APP_ENV); + $encryptSegment->set(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, $this->_encryptor->exportKeys()); + + $configData = [$encryptSegment->getFileKey() => $encryptSegment->getData()]; + + // update database and config.php + $this->beginTransaction(); + try { + $this->_reEncryptSystemConfigurationValues(); + $this->_reEncryptCreditCardNumbers(); + $this->_writer->saveConfig($configData); + $this->commit(); + return $key; + } catch (\Exception $e) { + $this->rollBack(); + throw $e; + } + } + + /** + * Gather all encrypted system config values and re-encrypt them + * + * @return void + */ + protected function _reEncryptSystemConfigurationValues() + { + // look for encrypted node entries in all system.xml files + /** @var \Magento\Config\Model\Config\Structure $configStructure */ + $configStructure = $this->_structure; + $paths = $configStructure->getFieldPathsByAttribute( + 'backend_model', + 'Magento\Config\Model\Config\Backend\Encrypted' + ); + + // walk through found data and re-encrypt it + if ($paths) { + $table = $this->getTable('core_config_data'); + $values = $this->getConnection()->fetchPairs( + $this->getConnection()->select()->from( + $table, + ['config_id', 'value'] + )->where( + 'path IN (?)', + $paths + )->where( + 'value NOT LIKE ?', + '' + ) + ); + foreach ($values as $configId => $value) { + $this->getConnection()->update( + $table, + ['value' => $this->_encryptor->encrypt($this->_encryptor->decrypt($value))], + ['config_id = ?' => (int)$configId] + ); + } + } + } + + /** + * Gather saved credit card numbers from sales order payments and re-encrypt them + * + * @return void + */ + protected function _reEncryptCreditCardNumbers() + { + $table = $this->getTable('sales_order_payment'); + $select = $this->getConnection()->select()->from($table, ['entity_id', 'cc_number_enc']); + + $attributeValues = $this->getConnection()->fetchPairs($select); + // save new values + foreach ($attributeValues as $valueId => $value) { + $this->getConnection()->update( + $table, + ['cc_number_enc' => $this->_encryptor->encrypt($this->_encryptor->decrypt($value))], + ['entity_id = ?' => (int)$valueId] + ); + } + } +} diff --git a/app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php b/app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php new file mode 100644 index 0000000000000..aff2ad3a1fa50 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php @@ -0,0 +1,25 @@ + 0, 'label' => __('Recommended')], ['value' => 1, 'label' => __('Forced')]]; + } +} diff --git a/app/code/Magento/PasswordManagement/README.md b/app/code/Magento/PasswordManagement/README.md new file mode 100644 index 0000000000000..9d13ada892e43 --- /dev/null +++ b/app/code/Magento/PasswordManagement/README.md @@ -0,0 +1,2 @@ +The Magento_PasswordManagement module provides an advanced encryption model to protect passwords and other sensitive data. +For customer passwords, the module implements upgrading hashes. For admin passwords, it enables setting lifetimes and locking them when expired or when a specified numbers of failures have occurred. It allows preventing password brute force attacks for system backend. diff --git a/app/code/Magento/PasswordManagement/Setup/InstallSchema.php b/app/code/Magento/PasswordManagement/Setup/InstallSchema.php new file mode 100644 index 0000000000000..bd28a1368c0d4 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Setup/InstallSchema.php @@ -0,0 +1,103 @@ +startSetup(); + + /** + * Create table 'admin_passwords' + */ + $table = $installer->getConnection()->newTable( + $installer->getTable('admin_passwords') + )->addColumn( + 'password_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true], + 'Password Id' + )->addColumn( + 'user_id', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['unsigned' => true, 'nullable' => false, 'default' => '0'], + 'User Id' + )->addColumn( + 'password_hash', + \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, + 100, + [], + 'Password Hash' + )->addColumn( + 'expires', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['unsigned' => true, 'nullable' => false, 'default' => '0'], + 'Expires' + )->addColumn( + 'last_updated', + \Magento\Framework\DB\Ddl\Table::TYPE_INTEGER, + null, + ['unsigned' => true, 'nullable' => false, 'default' => '0'], + 'Last Updated' + )->addIndex( + $installer->getIdxName('admin_passwords', ['user_id']), + ['user_id'] + )->addForeignKey( + $installer->getFkName('admin_passwords', 'user_id', 'admin_user', 'user_id'), + 'user_id', + $installer->getTable('admin_user'), + 'user_id', + \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE + )->setComment( + 'Enterprise Admin Passwords' + ); + $installer->getConnection()->createTable($table); + + $tableAdmins = $installer->getTable('admin_user'); + + $installer->getConnection()->addColumn( + $tableAdmins, + 'failures_num', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + 'nullable' => true, + 'default' => 0, + 'comment' => 'Failure Number' + ] + ); + + $installer->getConnection()->addColumn( + $tableAdmins, + 'first_failure', + ['type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, 'comment' => 'First Failure'] + ); + + $installer->getConnection()->addColumn( + $tableAdmins, + 'lock_expires', + ['type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, 'comment' => 'Expiration Lock Dates'] + ); + + $installer->endSetup(); + + } +} diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php new file mode 100644 index 0000000000000..3373fa7323b80 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php @@ -0,0 +1,88 @@ +encryptorMock = $this->getMockBuilder( + '\Magento\Framework\Encryption\Encryptor' + )->disableOriginalConstructor()->getMock(); + $this->encryptorMock->expects($this->any())->method('validateHashByVersion')->will( + $this->returnCallback( + function ($arg1, $arg2) { + return $arg1 == $arg2; + } + ) + ); + $this->observer = new \Magento\PasswordManagement\Model\Observer($this->encryptorMock); + $this->customerMock = $this->getMockBuilder( + '\Magento\Customer\Model\Customer' + )->disableOriginalConstructor()->setMethods( + ['getPasswordHash', 'changePassword', '__wakeup'] + )->getMock(); + } + + /** + * Create Observer with custom data structure and fill password + * + * @param $password + * @param $passwordHash + * @return \Magento\Framework\DataObject + */ + protected function getObserverMock($password, $passwordHash) + { + $this->customerMock->expects( + $this->once() + )->method( + 'getPasswordHash' + )->will( + $this->returnValue($passwordHash) + ); + + $event = new \Magento\Framework\DataObject(); + $event->setData(['password' => $password, 'model' => $this->customerMock]); + + $observerMock = new \Magento\Framework\DataObject(); + $observerMock->setData('event', $event); + + return $observerMock; + } + + /** + * Test successfully password change if new password doesn't match old one + */ + public function testUpgradeCustomerPassword() + { + $this->customerMock->expects($this->once())->method('changePassword')->will($this->returnSelf()); + $this->observer->upgradeCustomerPassword($this->getObserverMock('different password', 'old password')); + } + + /** + * Test failure password change if new password matches old one + */ + public function testUpgradeCustomerPasswordNotChanged() + { + $this->customerMock->expects($this->never())->method('changePassword'); + $this->observer->upgradeCustomerPassword($this->getObserverMock('same password', 'same password')); + } +} diff --git a/app/code/Magento/PasswordManagement/composer.json b/app/code/Magento/PasswordManagement/composer.json new file mode 100644 index 0000000000000..63317efde084e --- /dev/null +++ b/app/code/Magento/PasswordManagement/composer.json @@ -0,0 +1,26 @@ +{ + "name": "magento/module-password-management", + "description": "N/A", + "require": { + "php": "~5.5.0|~5.6.0", + "magento/module-config": "1.0.0-beta", + "magento/module-sales": "1.0.0-beta", + "magento/module-backend": "1.0.0-beta", + "magento/module-user": "1.0.0-beta", + "magento/framework": "1.0.0-beta", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-module", + "version": "1.0.0-beta", + "license": [ + "proprietary" + ], + "extra": { + "map": [ + [ + "*", + "Magento/PasswordManagement" + ] + ] + } +} diff --git a/app/code/Magento/PasswordManagement/etc/acl.xml b/app/code/Magento/PasswordManagement/etc/acl.xml new file mode 100644 index 0000000000000..8c4a1615dd60f --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/acl.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml new file mode 100644 index 0000000000000..b05f508f04db3 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml @@ -0,0 +1,14 @@ + + + + + + Magento\Framework\Authorization + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/events.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/events.xml new file mode 100644 index 0000000000000..60df1cc8b8691 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/adminhtml/events.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml new file mode 100644 index 0000000000000..176c28dfd5383 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml new file mode 100644 index 0000000000000..569d27551d1fc --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml new file mode 100644 index 0000000000000..e1de692512bcf --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml @@ -0,0 +1,31 @@ + + + + +
+ advanced + + + + We will disable this feature if the value is empty. + + + + + + + We will disable this feature if the value is empty. + + + + Magento\PasswordManagement\Model\System\Config\Source\Password + + +
+
+
diff --git a/app/code/Magento/PasswordManagement/etc/config.xml b/app/code/Magento/PasswordManagement/etc/config.xml new file mode 100644 index 0000000000000..da09926c09976 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/config.xml @@ -0,0 +1,20 @@ + + + + + + + 900 + 6 + 30 + 90 + 1 + + + + diff --git a/app/code/Magento/PasswordManagement/etc/frontend/events.xml b/app/code/Magento/PasswordManagement/etc/frontend/events.xml new file mode 100644 index 0000000000000..3a7f933559f0f --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/frontend/events.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/logging.xml b/app/code/Magento/PasswordManagement/etc/logging.xml new file mode 100644 index 0000000000000..46fe78b7e2658 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/logging.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/module.xml b/app/code/Magento/PasswordManagement/etc/module.xml new file mode 100644 index 0000000000000..cdf727a55c802 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/module.xml @@ -0,0 +1,15 @@ + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/i18n/de_DE.csv b/app/code/Magento/PasswordManagement/i18n/de_DE.csv new file mode 100644 index 0000000000000..3612fc47ae503 --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/de_DE.csv @@ -0,0 +1,39 @@ +ID,Benutzerkennung +No,No +Yes,Yes +Username,Benutzername +"Encryption Key","Encryption Key" +"Change Encryption Key","Verschlüsselungscode ändern" +"New Encryption Key","Neuer Verschlüsselungscode" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Automatische Schlüsselgenerierung" +"The generated key will be displayed after changing.","Der generierte Schlüssel wird nach seiner Änderung angezeigt." +"New Key","Neuer Schlüssel" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Bitte einen Verschlüsselungscode eingeben." +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Gesperrte Benutzer" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Das Konto ist gesperrt." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Empfohlen +Forced,Dechiffriert +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Änderung des Verschlüsselungscodes" +"Admin Accounts Locks","Admin Kontosperrungen" +Unlock,Entsperren +"Last login","Letzter Login" +Failures,Fehler +Unlocked,"Gesperrt bis" diff --git a/app/code/Magento/PasswordManagement/i18n/en_US.csv b/app/code/Magento/PasswordManagement/i18n/en_US.csv new file mode 100644 index 0000000000000..106d8edf541bc --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/en_US.csv @@ -0,0 +1,39 @@ +ID,ID +No,No +Yes,Yes +Username,Username +"Encryption Key","Encryption Key" +"Change Encryption Key","Change Encryption Key" +"New Encryption Key","New Encryption Key" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Auto-generate a Key" +"The generated key will be displayed after changing.","The generated key will be displayed after changing." +"New Key","New Key" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Please enter an encryption key." +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Locked Users" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","This account is locked." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Recommended +Forced,Forced +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Encryption Key Change" +"Admin Accounts Locks","Admin Accounts Locks" +Unlock,Unlock +"Last login","Last login" +Failures,Failures +Unlocked,Unlocked diff --git a/app/code/Magento/PasswordManagement/i18n/es_ES.csv b/app/code/Magento/PasswordManagement/i18n/es_ES.csv new file mode 100644 index 0000000000000..0033414380e78 --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/es_ES.csv @@ -0,0 +1,39 @@ +ID,Identificación +No,No +Yes,Yes +Username,"Nombre de Usuario" +"Encryption Key","Encryption Key" +"Change Encryption Key","Cambiar clave de encriptación" +"New Encryption Key","Nueva clave de encriptación" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Auto generar una clave" +"The generated key will be displayed after changing.","La clave generada se mostrará después de cambiarla." +"New Key","Nueva clave" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Por favor introduzca una clave de encriptación" +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Usuarios bloqueados" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Esta cuenta está bloqueada." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Recomendado +Forced,Forzado +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Cambio de clave de encriptación" +"Admin Accounts Locks","Bloqueo de cuentas de administración" +Unlock,Desbloquear +"Last login","Ultimo acceso" +Failures,Fallos +Unlocked,"Bloqueado hasta" diff --git a/app/code/Magento/PasswordManagement/i18n/fr_FR.csv b/app/code/Magento/PasswordManagement/i18n/fr_FR.csv new file mode 100644 index 0000000000000..2f2fe83e365de --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/fr_FR.csv @@ -0,0 +1,39 @@ +ID,Identifiant +No,No +Yes,Yes +Username,"Nom d'utilisateur" +"Encryption Key","Encryption Key" +"Change Encryption Key","Modifier Clé de Chiffrement" +"New Encryption Key","Nouvelle Clé de Chiffrement" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Générer automatiquement une Clé" +"The generated key will be displayed after changing.","La clé générée apparaîtra après changement." +"New Key","Nouvelle Clé" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Merci d'entrer une clé de chiffrement" +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Utilisateurs verrouillés" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Le compte est verrouillé." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Recommandé +Forced,Forcé +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Modification Clé de Chiffrement" +"Admin Accounts Locks","Verrous Comptes Administrateurs" +Unlock,Déverrouiller +"Last login","Dernière connexion" +Failures,Echecs +Unlocked,"Verrouillé jusqu'" diff --git a/app/code/Magento/PasswordManagement/i18n/nl_NL.csv b/app/code/Magento/PasswordManagement/i18n/nl_NL.csv new file mode 100644 index 0000000000000..4c52dbd60266e --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/nl_NL.csv @@ -0,0 +1,39 @@ +ID,identiteit +No,No +Yes,Yes +Username,Gebruikersnaam +"Encryption Key","Encryption Key" +"Change Encryption Key","Verander Encryptie Code" +"New Encryption Key","Nieuwe Encryptie Code" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Automatisch een sleutel genereren." +"The generated key will be displayed after changing.","De gegenereerde sleutel wordt weergegeven na het veranderen." +"New Key","Nieuwe Code" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Vul een encryptie sleutel in." +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Gesloten Gebruikers" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Dit account is geblokkeerd." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Aanbevolen +Forced,Gedwongen +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Encryptie Code Veranderen" +"Admin Accounts Locks","Beheerder Accounts Sloten" +Unlock,Openen +"Last login","Laatste login" +Failures,Mislukkingen +Unlocked,"Gesloten tot" diff --git a/app/code/Magento/PasswordManagement/i18n/pt_BR.csv b/app/code/Magento/PasswordManagement/i18n/pt_BR.csv new file mode 100644 index 0000000000000..790b051e9e9f1 --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/pt_BR.csv @@ -0,0 +1,39 @@ +ID,Identidade +No,No +Yes,Yes +Username,"Nome do usuário" +"Encryption Key","Encryption Key" +"Change Encryption Key","Mudar Chave de Criptografia" +"New Encryption Key","Nova Chave de Criptografia" +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key","Autogerar Chave" +"The generated key will be displayed after changing.","A chave gerada será exibida após a mudança." +"New Key","Nova Chave" +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.","Por favor introduza uma chave de encriptação." +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users","Usuários Bloqueados" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Esta conta está bloqueada." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,Recomendado +Forced,Forçado +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Mudança na Chave de Criptografia" +"Admin Accounts Locks","Bloqueios para Contas de Administrador" +Unlock,Desbloquear +"Last login","Último login" +Failures,Falhas +Unlocked,"Bloqueado até" diff --git a/app/code/Magento/PasswordManagement/i18n/zh_CN.csv b/app/code/Magento/PasswordManagement/i18n/zh_CN.csv new file mode 100644 index 0000000000000..08650721ba2b3 --- /dev/null +++ b/app/code/Magento/PasswordManagement/i18n/zh_CN.csv @@ -0,0 +1,39 @@ +ID,ID +No,No +Yes,Yes +Username,用户名 +"Encryption Key","Encryption Key" +"Change Encryption Key",更改加密密钥 +"New Encryption Key",新建加密密钥 +"The encryption key is used to protect passwords and other sensitive data.","The encryption key is used to protect passwords and other sensitive data." +"Auto-generate a Key",自动生成密钥 +"The generated key will be displayed after changing.",生成的密钥会在改动后显示。 +"New Key",新密钥 +"To enable a key change this file must be writable: %1.","To enable a key change this file must be writable: %1." +"Please enter an encryption key.",请输入加密密钥。 +"The encryption key has been changed.","The encryption key has been changed." +"This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" +"Locked Users",锁定的用户 +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.",该帐户已被锁定。 +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +"Not supported cipher version","Not supported cipher version" +"The encryption key format is invalid.","The encryption key format is invalid." +"File %1 is not writeable.","File %1 is not writeable." +Recommended,推荐 +Forced,强制 +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change",加密密钥已更改 +"Admin Accounts Locks",管理帐户锁定 +Unlock,解锁 +"Last login",上一次登录 +Failures,失败 +Unlocked,锁定直到 diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml new file mode 100644 index 0000000000000..df4fef540efcc --- /dev/null +++ b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml new file mode 100644 index 0000000000000..f40b8ef38ef9b --- /dev/null +++ b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml @@ -0,0 +1,94 @@ + + + + + + + + lockedAdminsGrid + Magento\User\Model\Resource\User\Locked\Collection + user_id + 1 + + 1 + + + + + user_id + unlock + 1 + + + Unlock + */*/massUnlock + 1 + + + + + + + lockedAdminsGrid + + + + ID + number + 0 + user_id + user_id + col-id + col-id + + + + + Username + text + username + username + col-name + col-name + + + + + Last login + datetime + 0 + last_login + logdate + col-date + col-date + + + + + Failures + 0 + failures_num + failures_num + + + + + Unlocked + datetime + 0 + lock_expires + lock_expires + col-date + col-date + + + + + + + diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml new file mode 100644 index 0000000000000..d6d830e926887 --- /dev/null +++ b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml new file mode 100644 index 0000000000000..76d602577db62 --- /dev/null +++ b/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/composer.json b/composer.json index 1704c488c4410..cacb3edc2e5ec 100644 --- a/composer.json +++ b/composer.json @@ -123,6 +123,7 @@ "magento/module-offline-payments": "self.version", "magento/module-offline-shipping": "self.version", "magento/module-page-cache": "self.version", + "magento/module-password-management": "self.version", "magento/module-payment": "self.version", "magento/module-paypal": "self.version", "magento/module-persistent": "self.version", diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php new file mode 100644 index 0000000000000..5bdca032fc4c2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php @@ -0,0 +1,26 @@ +get( + 'Magento\Framework\View\LayoutInterface' + )->createBlock( + 'Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Edit' + ); + + $this->assertEquals('Encryption Key', $block->getHeaderText()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php new file mode 100644 index 0000000000000..0ce5ee847a20c --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php @@ -0,0 +1,54 @@ +get('Magento\Framework\View\DesignInterface') + ->setArea(\Magento\Backend\App\Area\FrontNameResolver::AREA_CODE) + ->setDefaultDesignTheme(); + + $block = $objectManager->get('Magento\Framework\View\LayoutInterface') + ->createBlock('Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form'); + + $prepareFormMethod = new \ReflectionMethod( + 'Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form', + '_prepareForm' + ); + $prepareFormMethod->setAccessible(true); + $prepareFormMethod->invoke($block); + + $form = $block->getForm(); + + $this->assertEquals('edit_form', $form->getId()); + $this->assertEquals('post', $form->getMethod()); + + foreach (['enc_key_note', 'generate_random', 'crypt_key', 'main_fieldset'] as $id) { + $element = $form->getElement($id); + $this->assertNotNull($element); + } + + $generateRandomField = $form->getElement('generate_random'); + $this->assertEquals('select', $generateRandomField->getType()); + $this->assertEquals([ 0 => 'No', 1 => 'Yes'], $generateRandomField->getOptions()); + + $cryptKeyField = $form->getElement('crypt_key'); + $this->assertEquals('text', $cryptKeyField->getType()); + $this->assertEquals('crypt_key', $cryptKeyField->getName()); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php new file mode 100644 index 0000000000000..44f5728cf0324 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php @@ -0,0 +1,20 @@ +dispatch('backend/admin/crypt_key/index'); + + $body = $this->getResponse()->getBody(); + $this->assertContains('

Encryption Key

', $body); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php new file mode 100644 index 0000000000000..c87f1c7038ab8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php @@ -0,0 +1,105 @@ +getRequest(); + $request + ->setPostValue('generate_random', $ifGenerateRandom) + ->setPostValue('crypt_key', $encryptionKey); + $this->dispatch('backend/admin/crypt_key/save'); + $this->assertSessionMessages( + $this->contains('Please enter an encryption key.'), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + } + + /** + * Test save action with provided encryption key + * + * @magentoDbIsolation enabled + */ + public function testSaveActionWithProvidedKey() + { + $this->markTestSkipped('Test is blocked by MAGETWO-33612.'); + + // data set with provided encryption key + $ifGenerateRandom = '0'; + $encryptionKey = 'foo_encryption_key'; + + $request = $this->getRequest(); + $request + ->setPostValue('generate_random', $ifGenerateRandom) + ->setPostValue('crypt_key', $encryptionKey); + $this->dispatch('backend/admin/crypt_key/save'); + + $this->assertRedirect(); + $this->assertSessionMessages( + $this->contains('The encryption key has been changed.'), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + } + + /** + * Test save action with invalid encryption key + */ + public function testSaveActionWithInvalidKey() + { + // data set with provided encryption key + $ifGenerateRandom = '0'; + $encryptionKey = 'invalid key'; + + $params = [ + 'generate_random' => $ifGenerateRandom, + 'crypt_key' => $encryptionKey, + ]; + $this->getRequest()->setPostValue($params); + $this->dispatch('backend/admin/crypt_key/save'); + + $this->assertRedirect(); + $this->assertSessionMessages( + $this->contains('The encryption key format is invalid.'), + \Magento\Framework\Message\MessageInterface::TYPE_ERROR + ); + } + + /** + * Test save action with randomly generated key + * + * @magentoDbIsolation enabled + */ + public function testSaveActionWithRandomKey() + { + $this->markTestSkipped('Test is blocked by MAGETWO-33612.'); + + // data set with random encryption key + $ifGenerateRandom = '1'; + $encryptionKey = ''; + + $request = $this->getRequest(); + $request + ->setPostValue('generate_random', $ifGenerateRandom) + ->setPostValue('crypt_key', $encryptionKey); + $this->dispatch('backend/admin/crypt_key/save'); + + $this->assertRedirect(); + $this->assertSessionMessages( + $this->contains('The encryption key has been changed.'), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php new file mode 100644 index 0000000000000..653145dadc4b2 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php @@ -0,0 +1,35 @@ +dispatch('backend/admin/locks/grid'); + + $body = $this->getResponse()->getBody(); + $this->assertContains('data-column="username"', $body); + $this->assertContains('data-column="last_login"', $body); + $this->assertContains('data-column="last_login"', $body); + $this->assertContains('data-column="failures_num"', $body); + $this->assertContains('data-column="lock_expires"', $body); + $this->assertRegExp( + '/\s*adminUser1\s*<\/td>/', + $body + ); + $this->assertRegExp( + '/\s*adminUser2\s*<\/td>/', + $body + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php new file mode 100644 index 0000000000000..8018fb80711b3 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php @@ -0,0 +1,31 @@ +dispatch('backend/admin/locks/index'); + + $body = $this->getResponse()->getBody(); + $this->assertContains('

Locked Users

', $body); + $this->assertRegExp( + '/\s*adminUser1\s*<\/td>/', + $body + ); + $this->assertRegExp( + '/\s*adminUser2\s*<\/td>/', + $body + ); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php new file mode 100644 index 0000000000000..37ee60a81647b --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php @@ -0,0 +1,38 @@ +create('Magento\User\Model\User'); + $userIds[] = $model->loadByUsername('adminUser1')->getId(); + $userIds[] = $model->loadByUsername('adminUser2')->getId(); + + $request = $this->getRequest(); + $request->setPostValue( + 'unlock', + $userIds + ); + $this->dispatch('backend/admin/locks/massunlock'); + + $this->assertSessionMessages( + $this->contains((string)__('Unlocked %1 user(s).', count($userIds))), + \Magento\Framework\Message\MessageInterface::TYPE_SUCCESS + ); + $this->assertRedirect(); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php new file mode 100644 index 0000000000000..034e3f0db1b23 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php @@ -0,0 +1,98 @@ +objectManager = \Magento\TestFramework\Helper\Bootstrap::getObjectManager(); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage Deployment configuration file is not writable + */ + public function testChangeEncryptionKeyConfigNotWritable() + { + $writerMock = $this->getMock('Magento\Framework\App\DeploymentConfig\Writer', [], [], '', false); + $writerMock->expects($this->once())->method('checkIfWritable')->will($this->returnValue(false)); + + /** @var \Magento\PasswordManagement\Model\Resource\Key\Change $keyChangeModel */ + $keyChangeModel = $this->objectManager->create( + 'Magento\PasswordManagement\Model\Resource\Key\Change', + ['writer' => $writerMock] + ); + $keyChangeModel->changeEncryptionKey(); + } + + /** + * @magentoDbIsolation enabled + * @magentoDataFixture Magento/PasswordManagement/_files/payment_info.php + */ + public function testChangeEncryptionKey() + { + $testPath = 'test/config'; + $testValue = 'test'; + + $writerMock = $this->getMock('Magento\Framework\App\DeploymentConfig\Writer', [], [], '', false); + $writerMock->expects($this->once())->method('checkIfWritable')->will($this->returnValue(true)); + + $structureMock = $this->getMock('Magento\Config\Model\Config\Structure', [], [], '', false); + $structureMock->expects($this->once()) + ->method('getFieldPathsByAttribute') + ->will($this->returnValue([$testPath])); + + /** @var \Magento\PasswordManagement\Model\Resource\Key\Change $keyChangeModel */ + $keyChangeModel = $this->objectManager->create( + 'Magento\PasswordManagement\Model\Resource\Key\Change', + ['structure' => $structureMock, 'writer' => $writerMock] + ); + + $configModel = $this->objectManager->create( + 'Magento\Config\Model\Resource\Config' + ); + $configModel->saveConfig($testPath, 'test', 'default', 0); + $this->assertNotNull($keyChangeModel->changeEncryptionKey()); + + $connection = $keyChangeModel->getConnection(); + // Verify that the config value has been encrypted + $values1 = $connection->fetchPairs( + $connection->select()->from( + $keyChangeModel->getTable('core_config_data'), + ['config_id', 'value'] + )->where( + 'path IN (?)', + [$testPath] + )->where( + 'value NOT LIKE ?', + '' + ) + ); + $this->assertNotContains($testValue, $values1); + + // Verify that the credit card number has been encrypted + $values2 = $connection->fetchPairs( + $connection->select()->from( + $keyChangeModel->getTable('sales_order_payment'), + ['entity_id', 'cc_number_enc'] + ) + ); + $this->assertNotContains('1111111111', $values2); + + /** clean up */ + $select = $connection->select()->from($configModel->getMainTable())->where('path=?', $testPath); + $this->assertNotEmpty($connection->fetchRow($select)); + $configModel->deleteConfig($testPath, 'default', 0); + $this->assertEmpty($connection->fetchRow($select)); + } +} diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php new file mode 100644 index 0000000000000..a75f95e98f8d6 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php @@ -0,0 +1,51 @@ +create('Magento\User\Model\User'); +$model->setFirstname("John") + ->setLastname("Doe") + ->setUsername('adminUser1') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUser1@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow'); +$model->save(); +$userIds[] = $model->getDataByKey('user_id'); + +/** @var $model \Magento\User\Model\User */ +$model = $objectManager->create('Magento\User\Model\User'); +$model->setFirstname("John") + ->setLastname("Doe") + ->setUsername('adminUser2') + ->setPassword(\Magento\TestFramework\Bootstrap::ADMIN_PASSWORD) + ->setEmail('adminUser2@example.com') + ->setRoleType('G') + ->setResourceId('Magento_Adminhtml::all') + ->setPrivileges("") + ->setAssertId(0) + ->setRoleId(1) + ->setPermission('allow'); +$model->save(); +$userIds[] = $model->getDataByKey('user_id'); + +$defaultAdminUserId = 1; +$lockLifetime = 86400; + +/** @var $modelLockedUsers \Magento\PasswordManagement\Model\Resource\Admin\User */ +$modelLockedUsers = $objectManager->create('Magento\PasswordManagement\Model\Resource\Admin\User'); +$modelLockedUsers->lock($userIds, $defaultAdminUserId, $lockLifetime); diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php b/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php new file mode 100644 index 0000000000000..3edd7cc9c1da8 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php @@ -0,0 +1,20 @@ +create('Magento\Sales\Model\Order\Payment'); +$paymentInfo->setMethod('Cc')->setData('cc_number_enc', '1111111111'); + +/** @var \Magento\Sales\Model\Order $order */ +$order = $objectManager->create('Magento\Sales\Model\Order'); +$order->setIncrementId( + '100000001' +)->setPayment( + $paymentInfo +); +$order->save(); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php index 789052def185a..f6acee1d24130 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php @@ -193,6 +193,7 @@ 'oauth_nonce' => 'Magento\Integration', 'oauth_token' => 'Magento\Integration', 'authorizenet_debug' => 'Magento\Authorizenet', + 'admin_passwords' => 'Magento\PasswordManagement', 'paypal_cert' => 'Magento\Paypal', 'paypal_payment_transaction' => 'Magento\Paypal', 'paypal_settlement_report' => 'Magento\Paypal', diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php index 1e9233cee49d8..baf146629a788 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php @@ -12,5 +12,7 @@ '/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php', '/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php', '/app/code/Magento/CatalogImportExport/Model/Export/Product.php', + '/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php', + '/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php', //example '/app/code/Magento/Backend/Model/View.php', ]; From 5dd735795ca99f50dfe032ed82a3797404ab3770 Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Wed, 19 Aug 2015 12:32:29 -0500 Subject: [PATCH 06/31] MAGETWO-38568: Move PCI module code to new CE module - fixed code formatting error --- .../Controller/Adminhtml/Locks/MassUnlock.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php index 4314db27f60ea..bbb28ba889476 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php @@ -21,7 +21,9 @@ public function execute() // unlock users $userIds = $this->getRequest()->getPost('unlock'); if ($userIds && is_array($userIds)) { - $affectedUsers = $this->_objectManager->get('Magento\PasswordManagement\Model\Resource\Admin\User')->unlock($userIds); + $affectedUsers = $this->_objectManager + ->get('Magento\PasswordManagement\Model\Resource\Admin\User') + ->unlock($userIds); $this->getMessageManager()->addSuccess(__('Unlocked %1 user(s).', $affectedUsers)); } } catch (\Exception $e) { From 66369585597f5702114b6c420843e279655a9da6 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Thu, 20 Aug 2015 16:01:27 +0300 Subject: [PATCH 07/31] MAGETWO-38569: Create command line utility in existing bin/magento tool to unlock account --- .../Console/UnlockAdminAccountCommand.php | 82 +++++++++++++++++++ .../Console/UnlockAdminAccountCommandTest.php | 48 +++++++++++ .../Magento/PasswordManagement/etc/di.xml | 16 ++++ 3 files changed, 146 insertions(+) create mode 100644 app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php create mode 100644 app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php create mode 100644 app/code/Magento/PasswordManagement/etc/di.xml diff --git a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php new file mode 100644 index 0000000000000..ae76186671c84 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php @@ -0,0 +1,82 @@ +adminUser = $adminUser; + parent::__construct($name); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $adminUserName = $input->getArgument(self::ARGUMENT_ADMIN_USERNAME); + $userData = $this->adminUser->loadByUsername($adminUserName); + $outputMessage = sprintf('Couldn\'t find the user "%s"', $adminUserName); + if ($userData) { + if (isset($userData[self::USER_ID]) && $this->adminUser->unlock($userData[self::USER_ID])) { + $outputMessage = sprintf('The user "%s" has been unlocked', $adminUserName); + } else { + $outputMessage = sprintf('Couldn\'t unlock the user "%s"', $adminUserName); + } + } + $output->writeln('' . $outputMessage . ''); + } + + /** + * {@inheritdoc} + */ + protected function configure() + { + $this->setName(self::COMMAND_ADMIN_ACCOUT_UNLOCK); + $this->setDescription(self::COMMAND_DESCRIPTION); + $this->addArgument( + self::ARGUMENT_ADMIN_USERNAME, + InputArgument::REQUIRED, + self::ARGUMENT_ADMIN_USERNAME_DESCRIPTION + ); + $this->setHelp( + <<%command.full_name% user_name +HELP + ); + parent::configure(); + } +} diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php new file mode 100644 index 0000000000000..770d11b331bb8 --- /dev/null +++ b/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php @@ -0,0 +1,48 @@ +objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + parent::setUp(); + } + + /** + * Test configure() method implicitly via construct invocation. + * + * @return void + */ + public function testConfigure() + { + $this->command = $this->objectManager + ->getObject('Magento\PasswordManagement\Console\UnlockAdminAccountCommand'); + + $this->assertEquals(UnlockAdminAccountCommand::COMMAND_ADMIN_ACCOUT_UNLOCK, $this->command->getName()); + $this->assertEquals(UnlockAdminAccountCommand::COMMAND_DESCRIPTION, $this->command->getDescription()); + $this->command->getDefinition()->getArgument(UnlockAdminAccountCommand::ARGUMENT_ADMIN_USERNAME); + $this->assertContains('This command unlocks admin by its username', $this->command->getHelp()); + } +} diff --git a/app/code/Magento/PasswordManagement/etc/di.xml b/app/code/Magento/PasswordManagement/etc/di.xml new file mode 100644 index 0000000000000..152d0244b9455 --- /dev/null +++ b/app/code/Magento/PasswordManagement/etc/di.xml @@ -0,0 +1,16 @@ + + + + + + + Magento\PasswordManagement\Console\UnlockAdminAccountCommand + + + + From b3ecce668ea636d5a3cf67386212a3d6d836965c Mon Sep 17 00:00:00 2001 From: Bryant Luk Date: Thu, 20 Aug 2015 17:07:08 -0500 Subject: [PATCH 08/31] MAGETWO-41965: Remove use Rule for QuoteConfigProductAttributes - Remove the shorthand use Magento\SalesRule\Model\Resource\Rule because there is a new Rule class in the same namespace --- .../SalesRule/Model/Plugin/QuoteConfigProductAttributes.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php index 7ad693c280c26..5356a8e953483 100644 --- a/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php +++ b/app/code/Magento/SalesRule/Model/Plugin/QuoteConfigProductAttributes.php @@ -7,7 +7,6 @@ use Magento\Store\Model\StoreManagerInterface; use Magento\Customer\Model\Session; -use Magento\SalesRule\Model\Resource\Rule; class QuoteConfigProductAttributes { @@ -19,7 +18,7 @@ class QuoteConfigProductAttributes /** * @param Rule $ruleResource */ - public function __construct(Rule $ruleResource) + public function __construct(\Magento\SalesRule\Model\Resource\Rule $ruleResource) { $this->_ruleResource = $ruleResource; } From 11fb4e99c7aae1a7be9d2c2dcae5f9c200e0f7c4 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Fri, 21 Aug 2015 11:38:08 +0300 Subject: [PATCH 09/31] MAGETWO-38569: Create command line utility in existing bin/magento tool to unlock account --- .../Console/UnlockAdminAccountCommand.php | 14 +++++++------- .../Unit/Console/UnlockAdminAccountCommandTest.php | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php index ae76186671c84..c59c3d31040ef 100644 --- a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php +++ b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php @@ -18,7 +18,7 @@ class UnlockAdminAccountCommand extends Command { const ARGUMENT_ADMIN_USERNAME = 'username'; const ARGUMENT_ADMIN_USERNAME_DESCRIPTION = 'The admin username to unlock'; - const COMMAND_ADMIN_ACCOUT_UNLOCK = 'admin:user:unlock'; + const COMMAND_ADMIN_ACCOUNT_UNLOCK = 'admin:user:unlock'; const COMMAND_DESCRIPTION = 'Unlock Admin Account'; const USER_ID = 'user_id'; @@ -47,12 +47,12 @@ protected function execute(InputInterface $input, OutputInterface $output) { $adminUserName = $input->getArgument(self::ARGUMENT_ADMIN_USERNAME); $userData = $this->adminUser->loadByUsername($adminUserName); - $outputMessage = sprintf('Couldn\'t find the user "%s"', $adminUserName); + $outputMessage = sprintf('Couldn\'t find the user account "%s"', $adminUserName); if ($userData) { if (isset($userData[self::USER_ID]) && $this->adminUser->unlock($userData[self::USER_ID])) { - $outputMessage = sprintf('The user "%s" has been unlocked', $adminUserName); + $outputMessage = sprintf('The user account "%s" has been unlocked', $adminUserName); } else { - $outputMessage = sprintf('Couldn\'t unlock the user "%s"', $adminUserName); + $outputMessage = sprintf('Couldn\'t unlock the user account "%s"', $adminUserName); } } $output->writeln('' . $outputMessage . ''); @@ -63,7 +63,7 @@ protected function execute(InputInterface $input, OutputInterface $output) */ protected function configure() { - $this->setName(self::COMMAND_ADMIN_ACCOUT_UNLOCK); + $this->setName(self::COMMAND_ADMIN_ACCOUNT_UNLOCK); $this->setDescription(self::COMMAND_DESCRIPTION); $this->addArgument( self::ARGUMENT_ADMIN_USERNAME, @@ -72,9 +72,9 @@ protected function configure() ); $this->setHelp( <<%command.full_name% user_name + %command.full_name% username HELP ); parent::configure(); diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php index 770d11b331bb8..1e5ee3527bf0f 100644 --- a/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php +++ b/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php @@ -40,7 +40,7 @@ public function testConfigure() $this->command = $this->objectManager ->getObject('Magento\PasswordManagement\Console\UnlockAdminAccountCommand'); - $this->assertEquals(UnlockAdminAccountCommand::COMMAND_ADMIN_ACCOUT_UNLOCK, $this->command->getName()); + $this->assertEquals(UnlockAdminAccountCommand::COMMAND_ADMIN_ACCOUNT_UNLOCK, $this->command->getName()); $this->assertEquals(UnlockAdminAccountCommand::COMMAND_DESCRIPTION, $this->command->getDescription()); $this->command->getDefinition()->getArgument(UnlockAdminAccountCommand::ARGUMENT_ADMIN_USERNAME); $this->assertContains('This command unlocks admin by its username', $this->command->getHelp()); From 7c2e382b3f04935f610a3a0242c56c8ca6561380 Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Fri, 21 Aug 2015 11:42:02 -0500 Subject: [PATCH 10/31] MAGETWO-38568: Move PCI module code to new CE module - fixed code formatting issues, removed unused variable --- .../Controller/Adminhtml/Crypt/Key/Save.php | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php index 4d9f618fdaf6f..d46e83bf30c9d 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php +++ b/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php @@ -5,8 +5,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key; class Save extends \Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key @@ -48,7 +46,6 @@ public function __construct( * Process saving new encryption key * * @return void - * @SuppressWarnings(PHPMD.UnusedLocalVariable) */ public function execute() { @@ -69,16 +66,15 @@ public function execute() if (!$key) { $this->messageManager->addNotice( __( - 'This is your new encryption key: %1. Be sure to write it down and take good care of it!', + 'This is your new encryption key: %1. ' . + 'Be sure to write it down and take good care of it!', $newKey ) ); } $this->cache->clean(); } catch (\Exception $e) { - if ($message = $e->getMessage()) { - $this->messageManager->addError($e->getMessage()); - } + $this->messageManager->addError($e->getMessage()); $this->_session->setFormData(['crypt_key' => $key]); } $this->_redirect('adminhtml/*/'); From 87364d36ff6086f06a2c9adfa0089acfcd5a7ce3 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Tue, 25 Aug 2015 18:04:32 +0300 Subject: [PATCH 11/31] MAGETWO-41519: Method Mage_Core_Model_Email_Template_Filter:: configDirective allows access to any config value --- .../Magento/Email/Model/Source/Variables.php | 11 ++++ .../Magento/Email/Model/Template/Filter.php | 28 ++++++++-- .../Test/Unit/Model/Template/FilterTest.php | 52 +++++++++++++++++++ .../Magento/Widget/Model/Template/Filter.php | 5 +- 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/app/code/Magento/Email/Model/Source/Variables.php b/app/code/Magento/Email/Model/Source/Variables.php index e734627f770b2..d18c3ba433c42 100644 --- a/app/code/Magento/Email/Model/Source/Variables.php +++ b/app/code/Magento/Email/Model/Source/Variables.php @@ -72,4 +72,15 @@ public function toOptionArray($withGroup = false) } return $optionArray; } + + /** + * Return available config variables + * + * @return array + * @codeCoverageIgnore + */ + public function getData() + { + return $this->_configVariables; + } } diff --git a/app/code/Magento/Email/Model/Template/Filter.php b/app/code/Magento/Email/Model/Template/Filter.php index 39adadcb41e57..fc58ed0e223a5 100644 --- a/app/code/Magento/Email/Model/Template/Filter.php +++ b/app/code/Magento/Email/Model/Template/Filter.php @@ -145,6 +145,11 @@ class Filter extends \Magento\Framework\Filter\Template */ protected $emogrifier; + /** + * @var \Magento\Email\Model\Source\Variables + */ + protected $configVariables; + /** * @param \Magento\Framework\Stdlib\StringUtils $string * @param \Psr\Log\LoggerInterface $logger @@ -158,6 +163,7 @@ class Filter extends \Magento\Framework\Filter\Template * @param \Magento\Framework\App\State $appState * @param \Magento\Framework\UrlInterface $urlModel * @param \Pelago\Emogrifier $emogrifier + * @param \Magento\Email\Model\Source\Variables $configVariables * @param array $variables * * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -175,6 +181,7 @@ public function __construct( \Magento\Framework\App\State $appState, \Magento\Framework\UrlInterface $urlModel, \Pelago\Emogrifier $emogrifier, + \Magento\Email\Model\Source\Variables $configVariables, $variables = [] ) { $this->_escaper = $escaper; @@ -189,6 +196,7 @@ public function __construct( $this->_appState = $appState; $this->urlModel = $urlModel; $this->emogrifier = $emogrifier; + $this->configVariables = $configVariables; parent::__construct($string, $variables); } @@ -225,7 +233,7 @@ public function setUseSessionInUrl($flag) */ public function setPlainTemplateMode($plainTemplateMode) { - $this->plainTemplateMode = (bool) $plainTemplateMode; + $this->plainTemplateMode = (bool)$plainTemplateMode; return $this; } @@ -247,7 +255,7 @@ public function isPlainTemplateMode() */ public function setIsChildTemplate($isChildTemplate) { - $this->isChildTemplate = (bool) $isChildTemplate; + $this->isChildTemplate = (bool)$isChildTemplate; return $this; } @@ -703,7 +711,7 @@ public function configDirective($construction) $configValue = ''; $params = $this->getParameters($construction[2]); $storeId = $this->getStoreId(); - if (isset($params['path'])) { + if (isset($params['path']) && $this->isAvailableConfigVariable($params['path'])) { $configValue = $this->_scopeConfig->getValue( $params['path'], \Magento\Store\Model\ScopeInterface::SCOPE_STORE, @@ -713,6 +721,20 @@ public function configDirective($construction) return $configValue; } + /** + * Check if given variable is available for directive "Config" + * + * @param string $variable + * @return bool + */ + private function isAvailableConfigVariable($variable) + { + return in_array( + $variable, + array_column($this->configVariables->getData(), 'value') + ); + } + /** * Custom Variable directive * diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php index 764b9cff0e593..59f1cdf4ed2ed 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php @@ -70,6 +70,11 @@ class FilterTest extends \PHPUnit_Framework_TestCase */ private $backendUrlBuilder; + /** + * @var \Magento\Email\Model\Source\Variables|\PHPUnit_Framework_MockObject_MockObject + */ + private $configVariables; + /** * @var \Pelago\Emogrifier */ @@ -125,6 +130,10 @@ protected function setUp() ->getMock(); $this->emogrifier = $this->objectManager->getObject('\Pelago\Emogrifier'); + + $this->configVariables = $this->getMockBuilder('Magento\Email\Model\Source\Variables') + ->disableOriginalConstructor() + ->getMock(); } /** @@ -147,6 +156,7 @@ protected function getModel($mockedMethods = null) $this->appState, $this->backendUrlBuilder, $this->emogrifier, + $this->configVariables, [], ]) ->setMethods($mockedMethods) @@ -330,4 +340,46 @@ public function testAfterFilterCallbackGetsResetWhenExceptionTriggered() $this->assertEquals($exceptionResult, $filter->filter($value)); } + + public function testConfigDirectiveAvailable() + { + $path = "web/unsecure/base_url"; + $availableConfigs = [['value' => $path]]; + $construction = ["{{config path={$path}}}", 'config', " path={$path}"]; + $scopeConfigValue = 'value'; + + $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface',[], [], '', false); + $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); + $storeMock->expects($this->once())->method('getId')->willReturn(1); + + $this->configVariables->expects($this->once()) + ->method('getData') + ->willReturn($availableConfigs); + $this->scopeConfig->expects($this->once()) + ->method('getValue') + ->willReturn($scopeConfigValue); + + $this->assertEquals($scopeConfigValue, $this->getModel()->configDirective($construction)); + } + + public function testConfigDirectiveUnavailable() + { + $path = "web/unsecure/base_url"; + $availableConfigs = []; + $construction = ["{{config path={$path}}}", 'config', " path={$path}"]; + $scopeConfigValue = ''; + + $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface',[], [], '', false); + $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); + $storeMock->expects($this->once())->method('getId')->willReturn(1); + + $this->configVariables->expects($this->once()) + ->method('getData') + ->willReturn($availableConfigs); + $this->scopeConfig->expects($this->never()) + ->method('getValue') + ->willReturn($scopeConfigValue); + + $this->assertEquals($scopeConfigValue, $this->getModel()->configDirective($construction)); + } } diff --git a/app/code/Magento/Widget/Model/Template/Filter.php b/app/code/Magento/Widget/Model/Template/Filter.php index a07bdd85de474..f066c61037781 100644 --- a/app/code/Magento/Widget/Model/Template/Filter.php +++ b/app/code/Magento/Widget/Model/Template/Filter.php @@ -34,6 +34,7 @@ class Filter extends \Magento\Cms\Model\Template\Filter * @param \Magento\Framework\App\State $appState * @param \Magento\Framework\UrlInterface $urlModel * @param \Pelago\Emogrifier $emogrifier + * @param \Magento\Email\Model\Source\Variables $configVariables * @param \Magento\Widget\Model\Resource\Widget $widgetResource * @param \Magento\Widget\Model\Widget $widget * @SuppressWarnings(PHPMD.ExcessiveParameterList) @@ -51,6 +52,7 @@ public function __construct( \Magento\Framework\App\State $appState, \Magento\Framework\UrlInterface $urlModel, \Pelago\Emogrifier $emogrifier, + \Magento\Email\Model\Source\Variables $configVariables, \Magento\Widget\Model\Resource\Widget $widgetResource, \Magento\Widget\Model\Widget $widget ) { @@ -68,7 +70,8 @@ public function __construct( $layoutFactory, $appState, $urlModel, - $emogrifier + $emogrifier, + $configVariables ); } From 66031c16ff599db266f882839363bc03438a2fc3 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 11:45:31 +0300 Subject: [PATCH 12/31] MAGETWO-41519: Method Mage_Core_Model_Email_Template_Filter:: configDirective allows access to any config value - removed overabundant a fragment of code, related to config variables --- app/code/Magento/Cms/Setup/InstallData.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/app/code/Magento/Cms/Setup/InstallData.php b/app/code/Magento/Cms/Setup/InstallData.php index e73fe3d3e6c0d..d85f872e1e285 100644 --- a/app/code/Magento/Cms/Setup/InstallData.php +++ b/app/code/Magento/Cms/Setup/InstallData.php @@ -377,24 +377,6 @@ public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $installer->doUpdateClassAliases(); $setup->endSetup(); - - $cookieRestriction = $this->createPage()->load('privacy-policy-cookie-restriction-mode', 'identifier'); - - if ($cookieRestriction->getId()) { - $content = $cookieRestriction->getContent(); - $replacment = '{{config path="general/store_information/street_line1"}} ' . - '{{config path="general/store_information/street_line2"}} ' . - '{{config path="general/store_information/city"}} ' . - '{{config path="general/store_information/postcode"}} ' . - '{{config path="general/store_information/region_id"}} ' . - '{{config path="general/store_information/country_id"}}'; - $content = preg_replace( - '/{{config path="general\\/store_information\\/address"}}/ims', - $replacment, - $content - ); - $cookieRestriction->setContent($content)->save(); - } } /** From 082f234899ffea8bcef110bdef8fc7530be53958 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 14:46:48 +0300 Subject: [PATCH 13/31] MAGETWO-41240: Update AccountManagement to dispatch events consistently --- app/code/Magento/Captcha/Model/Observer.php | 4 +++- app/code/Magento/Customer/Model/AccountManagement.php | 3 ++- app/code/Magento/Customer/Model/Customer.php | 1 - .../Magento/PasswordManagement/Model/Backend/Observer.php | 2 +- app/code/Magento/PasswordManagement/Model/Observer.php | 3 ++- .../PasswordManagement/Test/Unit/Model/ObserverTest.php | 2 +- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/code/Magento/Captcha/Model/Observer.php b/app/code/Magento/Captcha/Model/Observer.php index 30417fd063a19..69d63b64c3370 100644 --- a/app/code/Magento/Captcha/Model/Observer.php +++ b/app/code/Magento/Captcha/Model/Observer.php @@ -321,7 +321,9 @@ public function checkUserForgotPasswordBackend($observer) */ public function resetAttemptForFrontend($observer) { - return $this->_getResourceModel()->deleteUserAttempts($observer->getModel()->getEmail()); + /** @var \Magento\Customer\Model\Customer $model */ + $model = $observer->getModel(); + return $this->_getResourceModel()->deleteUserAttempts($model->getEmail()); } /** diff --git a/app/code/Magento/Customer/Model/AccountManagement.php b/app/code/Magento/Customer/Model/AccountManagement.php index 4ab68d8778326..032d2f244f7ca 100644 --- a/app/code/Magento/Customer/Model/AccountManagement.php +++ b/app/code/Magento/Customer/Model/AccountManagement.php @@ -370,9 +370,10 @@ public function authenticate($username, $password) throw new EmailNotConfirmedException(__('This account is not confirmed.')); } + $customerModel = $this->customerFactory->create()->updateData($customer); $this->eventManager->dispatch( 'customer_customer_authenticated', - ['model' => $this->getFullCustomerObject($customer), 'password' => $password] + ['model' => $customerModel, 'password' => $password] ); $this->eventManager->dispatch('customer_data_object_login', ['customer' => $customer]); diff --git a/app/code/Magento/Customer/Model/Customer.php b/app/code/Magento/Customer/Model/Customer.php index c607dcbd62f46..d4a2e9ba3d2fc 100644 --- a/app/code/Magento/Customer/Model/Customer.php +++ b/app/code/Magento/Customer/Model/Customer.php @@ -210,7 +210,6 @@ class Customer extends \Magento\Framework\Model\AbstractModel * @param CollectionFactory $addressesFactory * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder * @param GroupRepositoryInterface $groupRepository - * @param AttributeFactory $attributeFactory * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor * @param \Magento\Framework\Stdlib\DateTime $dateTime * @param CustomerInterfaceFactory $customerDataFactory diff --git a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php index b480df5481298..451372dc8697f 100644 --- a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php @@ -206,7 +206,7 @@ public function adminAuthenticate($observer) } // upgrade admin password - if (!$this->_encryptor->validateHashByVersion($password, $user->getPassword())) { + if (!$this->_encryptor->validateHash($password, $user->getPassword())) { $this->_userFactory->create()->load( $user->getId() )->setNewPassword( diff --git a/app/code/Magento/PasswordManagement/Model/Observer.php b/app/code/Magento/PasswordManagement/Model/Observer.php index c608749d7f5bb..332161c545693 100644 --- a/app/code/Magento/PasswordManagement/Model/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Observer.php @@ -38,8 +38,9 @@ public function __construct(\Magento\Framework\Encryption\EncryptorInterface $en public function upgradeCustomerPassword($observer) { $password = $observer->getEvent()->getPassword(); + /** @var \Magento\Customer\Model\Customer $model */ $model = $observer->getEvent()->getModel(); - if (!$this->_encryptor->validateHashByVersion($password, $model->getPasswordHash())) { + if (!$this->_encryptor->validateHash($password, $model->getPasswordHash())) { $model->changePassword($password); } } diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php index 3373fa7323b80..5454694d62e13 100644 --- a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php @@ -27,7 +27,7 @@ protected function setUp() $this->encryptorMock = $this->getMockBuilder( '\Magento\Framework\Encryption\Encryptor' )->disableOriginalConstructor()->getMock(); - $this->encryptorMock->expects($this->any())->method('validateHashByVersion')->will( + $this->encryptorMock->expects($this->any())->method('validateHash')->will( $this->returnCallback( function ($arg1, $arg2) { return $arg1 == $arg2; From e100850a2044589165e51fb1b4012044bdd26e00 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 14:55:29 +0300 Subject: [PATCH 14/31] MAGETWO-38569: Create command line utility in existing bin/magento tool to unlock account - updated a message for user --- .../PasswordManagement/Console/UnlockAdminAccountCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php index c59c3d31040ef..ca20bb6fc13f8 100644 --- a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php +++ b/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php @@ -52,7 +52,10 @@ protected function execute(InputInterface $input, OutputInterface $output) if (isset($userData[self::USER_ID]) && $this->adminUser->unlock($userData[self::USER_ID])) { $outputMessage = sprintf('The user account "%s" has been unlocked', $adminUserName); } else { - $outputMessage = sprintf('Couldn\'t unlock the user account "%s"', $adminUserName); + $outputMessage = sprintf( + 'The user account "%s" was not locked or could not be unlocked', + $adminUserName + ); } } $output->writeln('' . $outputMessage . ''); From f7c97872021ac4e27f420257cc1134c3e4846773 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 16:49:31 +0300 Subject: [PATCH 15/31] MAGETWO-38929: Add unit test coverage for \Magento\User\Helper\Data --- .../User/Test/Unit/Helper/DataTest.php | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 app/code/Magento/User/Test/Unit/Helper/DataTest.php diff --git a/app/code/Magento/User/Test/Unit/Helper/DataTest.php b/app/code/Magento/User/Test/Unit/Helper/DataTest.php new file mode 100644 index 0000000000000..2b6af00a2f5e1 --- /dev/null +++ b/app/code/Magento/User/Test/Unit/Helper/DataTest.php @@ -0,0 +1,67 @@ +mathRandomMock = $this->getMockBuilder('Magento\Framework\Math\Random') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->configMock = $this->getMockBuilder('Magento\Backend\App\ConfigInterface') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + 'Magento\User\Helper\Data', + [ + 'config' => $this->configMock, + 'mathRandom' => $this->mathRandomMock + ] + ); + } + + public function testGenerateResetPasswordLinkToken() + { + $hash = 'hashString'; + $this->mathRandomMock->expects($this->once())->method('getUniqueHash')->willReturn($hash); + $this->assertEquals($hash, $this->model->generateResetPasswordLinkToken()); + } + + public function testGetResetPasswordLinkExpirationPeriod() + { + $value = '123'; + $this->configMock->expects($this->once()) + ->method('getValue') + ->with( \Magento\User\Helper\Data::XML_PATH_ADMIN_RESET_PASSWORD_LINK_EXPIRATION_PERIOD) + ->willReturn($value); + $this->assertEquals((int) $value, $this->model->getResetPasswordLinkExpirationPeriod()); + } +} From 3f21844f38ea844357a6287f2db5e3f8a34313ac Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 19:46:15 +0300 Subject: [PATCH 16/31] MAGETWO-41240: Update AccountManagement to dispatch events consistently - added validateHashByVersion to interface --- .../Model/Backend/Observer.php | 22 +++++++++++-------- .../PasswordManagement/Model/Observer.php | 8 ++++++- .../Test/Unit/Model/ObserverTest.php | 2 +- .../Framework/Encryption/Encryptor.php | 2 +- .../Encryption/EncryptorInterface.php | 10 +++++++++ 5 files changed, 32 insertions(+), 12 deletions(-) diff --git a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php index 451372dc8697f..1a8b4a4f72f90 100644 --- a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php @@ -7,6 +7,7 @@ use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Exception\State\UserLockedException; +use Magento\Framework\Encryption\Encryptor; /** * PasswordManagement backend observer model @@ -155,7 +156,7 @@ public function adminAuthenticate($observer) $updateFirstFailureDate = false; $updateLockExpires = false; - $lockThresholdInterval = new \DateInterval('PT' . $lockThreshold.'S'); + $lockThresholdInterval = new \DateInterval('PT' . $lockThreshold . 'S'); // set first failure date when this is first failure or last first failure expired if (1 === $failuresNum || !$firstFailureDate || $now->diff($firstFailureDate) > $lockThresholdInterval) { $updateFirstFailureDate = $now; @@ -206,14 +207,17 @@ public function adminAuthenticate($observer) } // upgrade admin password - if (!$this->_encryptor->validateHash($password, $user->getPassword())) { - $this->_userFactory->create()->load( - $user->getId() - )->setNewPassword( - $password - )->setForceNewPassword( - true - )->save(); + $isValidHash = $this->_encryptor->validateHashByVersion( + $password, + $user->getPassword(), + Encryptor::HASH_VERSION_LATEST + ); + if (!$isValidHash) { + $this->_userFactory->create() + ->load($user->getId()) + ->setNewPassword($password) + ->setForceNewPassword(true) + ->save(); } } diff --git a/app/code/Magento/PasswordManagement/Model/Observer.php b/app/code/Magento/PasswordManagement/Model/Observer.php index 332161c545693..3f9b19c2107b9 100644 --- a/app/code/Magento/PasswordManagement/Model/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Observer.php @@ -6,6 +6,7 @@ namespace Magento\PasswordManagement\Model; use Magento\Framework\Event\Observer as EventObserver; +use Magento\Framework\Encryption\Encryptor; /** * PasswordManagement backend observer model @@ -40,7 +41,12 @@ public function upgradeCustomerPassword($observer) $password = $observer->getEvent()->getPassword(); /** @var \Magento\Customer\Model\Customer $model */ $model = $observer->getEvent()->getModel(); - if (!$this->_encryptor->validateHash($password, $model->getPasswordHash())) { + $isValidHash = $this->_encryptor->validateHashByVersion( + $password, + $model->getPasswordHash(), + Encryptor::HASH_VERSION_LATEST + ); + if (!$isValidHash) { $model->changePassword($password); } } diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php index 5454694d62e13..3373fa7323b80 100644 --- a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php @@ -27,7 +27,7 @@ protected function setUp() $this->encryptorMock = $this->getMockBuilder( '\Magento\Framework\Encryption\Encryptor' )->disableOriginalConstructor()->getMock(); - $this->encryptorMock->expects($this->any())->method('validateHash')->will( + $this->encryptorMock->expects($this->any())->method('validateHashByVersion')->will( $this->returnCallback( function ($arg1, $arg2) { return $arg1 == $arg2; diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index 9a5204b02f17d..df3adf9e9f6b7 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -171,7 +171,7 @@ public function validateHash($password, $hash) * @param int $version * @return bool */ - public function validateHashByVersion($password, $hash, $version = self::HASH_VERSION_LATEST) + public function validateHashByVersion($password, $hash, $version) { // look for salt $hashArr = explode(':', $hash, 2); diff --git a/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php b/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php index b9d98dc2e845b..dfdfb79535f04 100644 --- a/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php +++ b/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php @@ -43,6 +43,16 @@ public function hash($data); */ public function validateHash($password, $hash); + /** + * Validate hash by specified version + * + * @param string $password + * @param string $hash + * @param int $version + * @return bool + */ + public function validateHashByVersion($password, $hash, $version); + /** * Encrypt a string * From 463894fb96792493d26ad8c22aedcb90e9f73564 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 26 Aug 2015 19:57:24 +0300 Subject: [PATCH 17/31] MAGETWO-41240: Update AccountManagement to dispatch events consistently - refactored methods --- .../Model/Backend/Observer.php | 2 +- .../PasswordManagement/Model/Observer.php | 2 +- .../Test/Unit/Model/ObserverTest.php | 2 +- .../Framework/Encryption/Encryptor.php | 19 ++++++++++++++++--- .../Encryption/EncryptorInterface.php | 13 ++++++++++++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php index 1a8b4a4f72f90..ed5686c16579e 100644 --- a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Backend/Observer.php @@ -207,7 +207,7 @@ public function adminAuthenticate($observer) } // upgrade admin password - $isValidHash = $this->_encryptor->validateHashByVersion( + $isValidHash = $this->_encryptor->isValidHashByVersion( $password, $user->getPassword(), Encryptor::HASH_VERSION_LATEST diff --git a/app/code/Magento/PasswordManagement/Model/Observer.php b/app/code/Magento/PasswordManagement/Model/Observer.php index 3f9b19c2107b9..6227978912eef 100644 --- a/app/code/Magento/PasswordManagement/Model/Observer.php +++ b/app/code/Magento/PasswordManagement/Model/Observer.php @@ -41,7 +41,7 @@ public function upgradeCustomerPassword($observer) $password = $observer->getEvent()->getPassword(); /** @var \Magento\Customer\Model\Customer $model */ $model = $observer->getEvent()->getModel(); - $isValidHash = $this->_encryptor->validateHashByVersion( + $isValidHash = $this->_encryptor->isValidHashByVersion( $password, $model->getPasswordHash(), Encryptor::HASH_VERSION_LATEST diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php index 3373fa7323b80..f80bce9c4b678 100644 --- a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php @@ -27,7 +27,7 @@ protected function setUp() $this->encryptorMock = $this->getMockBuilder( '\Magento\Framework\Encryption\Encryptor' )->disableOriginalConstructor()->getMock(); - $this->encryptorMock->expects($this->any())->method('validateHashByVersion')->will( + $this->encryptorMock->expects($this->any())->method('isValidHashByVersion')->will( $this->returnCallback( function ($arg1, $arg2) { return $arg1 == $arg2; diff --git a/lib/internal/Magento/Framework/Encryption/Encryptor.php b/lib/internal/Magento/Framework/Encryption/Encryptor.php index df3adf9e9f6b7..cc8c28feae760 100644 --- a/lib/internal/Magento/Framework/Encryption/Encryptor.php +++ b/lib/internal/Magento/Framework/Encryption/Encryptor.php @@ -149,14 +149,27 @@ public function hash($data, $version = self::HASH_VERSION_LATEST) * @param string $password * @param string $hash * @return bool + * @deprecated */ public function validateHash($password, $hash) { - return $this->validateHashByVersion( + return $this->isValidHash($password, $hash); + } + + /** + * Validate hash against hashing method (with or without salt) + * + * @param string $password + * @param string $hash + * @return bool + */ + public function isValidHash($password, $hash) + { + return $this->isValidHashByVersion( $password, $hash, self::HASH_VERSION_SHA256 - ) || $this->validateHashByVersion( + ) || $this->isValidHashByVersion( $password, $hash, self::HASH_VERSION_MD5 @@ -171,7 +184,7 @@ public function validateHash($password, $hash) * @param int $version * @return bool */ - public function validateHashByVersion($password, $hash, $version) + public function isValidHashByVersion($password, $hash, $version) { // look for salt $hashArr = explode(':', $hash, 2); diff --git a/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php b/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php index dfdfb79535f04..1e72deaed4502 100644 --- a/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php +++ b/lib/internal/Magento/Framework/Encryption/EncryptorInterface.php @@ -40,9 +40,20 @@ public function hash($data); * @param string $hash * @return bool * @throws \Exception + * @deprecated */ public function validateHash($password, $hash); + /** + * Validate hash against hashing method (with or without salt) + * + * @param string $password + * @param string $hash + * @return bool + * @throws \Exception + */ + public function isValidHash($password, $hash); + /** * Validate hash by specified version * @@ -51,7 +62,7 @@ public function validateHash($password, $hash); * @param int $version * @return bool */ - public function validateHashByVersion($password, $hash, $version); + public function isValidHashByVersion($password, $hash, $version); /** * Encrypt a string From 20722201014e1f33981af13f7850d0bfdabbd77f Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Thu, 27 Aug 2015 15:31:42 -0500 Subject: [PATCH 18/31] MAGETWO-38570: Refactor as much functionality from PCI module to rest of core/library modules - moved admin user password and locking functionality from PasswordManagement to User module - moved customer password upgrade from PasswordManagement to Customer module - renamed PasswordManagement module to EncryptionKey to contain the encryption key management code --- app/code/Magento/Customer/Model/Observer.php | 40 +++- app/code/Magento/Customer/README.md | 3 +- .../Customer/Test/Unit/Model/ObserverTest.php | 65 +++++- .../Magento/Customer/etc/frontend/events.xml | 3 + .../Block/Adminhtml/Crypt/Key/Edit.php | 3 +- .../Block/Adminhtml/Crypt/Key/Form.php | 16 +- .../Controller/Adminhtml/Crypt/Key.php | 5 +- .../Controller/Adminhtml/Crypt/Key/Index.php | 21 +- .../Controller/Adminhtml/Crypt/Key/Save.php | 13 +- .../LICENSE.txt | 0 .../LICENSE_AFL.txt | 0 .../Model/Resource/Key/Change.php | 49 ++--- app/code/Magento/EncryptionKey/README.md | 1 + .../composer.json | 5 +- .../etc/acl.xml | 5 +- .../EncryptionKey/etc/adminhtml/menu.xml | 12 + .../etc/adminhtml/routes.xml | 2 +- .../etc/config.xml | 4 - .../Magento/EncryptionKey/etc/logging.xml | 20 ++ .../etc/module.xml | 2 +- .../i18n/de_DE.csv | 20 -- .../i18n/en_US.csv | 20 -- .../i18n/es_ES.csv | 20 -- .../i18n/fr_FR.csv | 20 -- .../i18n/nl_NL.csv | 20 -- .../i18n/pt_BR.csv | 20 -- .../i18n/zh_CN.csv | 20 -- .../layout/adminhtml_crypt_key_index.xml | 4 +- .../PasswordManagement/Model/Observer.php | 53 ----- .../Model/Resource/Admin/User.php | 166 -------------- app/code/Magento/PasswordManagement/README.md | 2 - .../Test/Unit/Model/ObserverTest.php | 88 -------- .../PasswordManagement/etc/adminhtml/di.xml | 14 -- .../PasswordManagement/etc/adminhtml/menu.xml | 13 -- .../etc/adminhtml/system.xml | 31 --- .../Magento/PasswordManagement/etc/di.xml | 16 -- .../etc/frontend/events.xml | 12 - .../Block/Adminhtml/Locks.php | 2 +- .../Console/UnlockAdminAccountCommand.php | 4 +- .../Controller/Adminhtml/Locks.php | 5 +- .../Controller/Adminhtml/Locks/Grid.php | 7 +- .../Controller/Adminhtml/Locks/Index.php | 9 +- .../Controller/Adminhtml/Locks/MassUnlock.php | 9 +- .../Model/Backend/Observer.php | 208 +++++++++--------- app/code/Magento/User/Model/Resource/User.php | 149 ++++++++++++- .../Model/System/Config/Source/Password.php | 2 +- app/code/Magento/User/README.md | 1 + .../Setup/UpgradeSchema.php} | 63 +++--- .../Console/UnlockAdminAccountCommandTest.php | 8 +- app/code/Magento/User/etc/acl.xml | 1 + app/code/Magento/User/etc/adminhtml/di.xml | 5 + .../etc/adminhtml/events.xml | 8 +- app/code/Magento/User/etc/adminhtml/menu.xml | 1 + .../Magento/User/etc/adminhtml/system.xml | 17 ++ app/code/Magento/User/etc/config.xml | 6 + app/code/Magento/User/etc/di.xml | 7 + .../etc/logging.xml | 10 +- app/code/Magento/User/i18n/de_DE.csv | 23 ++ app/code/Magento/User/i18n/en_US.csv | 23 ++ app/code/Magento/User/i18n/es_ES.csv | 24 ++ app/code/Magento/User/i18n/fr_FR.csv | 24 ++ app/code/Magento/User/i18n/nl_NL.csv | 24 ++ app/code/Magento/User/i18n/pt_BR.csv | 24 ++ app/code/Magento/User/i18n/zh_CN.csv | 24 ++ .../layout/adminhtml_locks_block.xml | 2 +- .../adminhtml/layout/adminhtml_locks_grid.xml | 2 +- .../layout/adminhtml_locks_index.xml | 2 +- composer.json | 2 +- .../Block/Adminhtml/Crypt/Key/EditTest.php | 6 +- .../Block/Adminhtml/Crypt/Key/FormTest.php | 8 +- .../Adminhtml/Crypt/Key/IndexTest.php | 2 +- .../Adminhtml/Crypt/Key/SaveTest.php | 2 +- .../Model/Resource/Key/ChangeTest.php | 12 +- .../_files/payment_info.php | 0 .../Controller/Adminhtml/Locks/GridTest.php | 4 +- .../Controller/Adminhtml/Locks/IndexTest.php | 4 +- .../Adminhtml/Locks/MassUnlockTest.php | 4 +- .../_files/locked_users.php | 4 +- .../_files/dependency_test/tables_ce.php | 4 +- .../connection/blacklist/files_list.php | 4 +- 80 files changed, 752 insertions(+), 806 deletions(-) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Block/Adminhtml/Crypt/Key/Edit.php (94%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Block/Adminhtml/Crypt/Key/Form.php (76%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Controller/Adminhtml/Crypt/Key.php (70%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Controller/Adminhtml/Crypt/Key/Index.php (57%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Controller/Adminhtml/Crypt/Key/Save.php (85%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/LICENSE.txt (100%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/LICENSE_AFL.txt (100%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/Model/Resource/Key/Change.php (81%) create mode 100644 app/code/Magento/EncryptionKey/README.md rename app/code/Magento/{PasswordManagement => EncryptionKey}/composer.json (78%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/etc/acl.xml (65%) create mode 100644 app/code/Magento/EncryptionKey/etc/adminhtml/menu.xml rename app/code/Magento/{PasswordManagement => EncryptionKey}/etc/adminhtml/routes.xml (82%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/etc/config.xml (65%) create mode 100644 app/code/Magento/EncryptionKey/etc/logging.xml rename app/code/Magento/{PasswordManagement => EncryptionKey}/etc/module.xml (86%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/de_DE.csv (52%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/en_US.csv (51%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/es_ES.csv (52%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/fr_FR.csv (51%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/nl_NL.csv (51%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/pt_BR.csv (51%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/i18n/zh_CN.csv (50%) rename app/code/Magento/{PasswordManagement => EncryptionKey}/view/adminhtml/layout/adminhtml_crypt_key_index.xml (58%) delete mode 100644 app/code/Magento/PasswordManagement/Model/Observer.php delete mode 100644 app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php delete mode 100644 app/code/Magento/PasswordManagement/README.md delete mode 100644 app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php delete mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/di.xml delete mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml delete mode 100644 app/code/Magento/PasswordManagement/etc/adminhtml/system.xml delete mode 100644 app/code/Magento/PasswordManagement/etc/di.xml delete mode 100644 app/code/Magento/PasswordManagement/etc/frontend/events.xml rename app/code/Magento/{PasswordManagement => User}/Block/Adminhtml/Locks.php (88%) rename app/code/Magento/{PasswordManagement => User}/Console/UnlockAdminAccountCommand.php (95%) rename app/code/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks.php (71%) rename app/code/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/Grid.php (68%) rename app/code/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/Index.php (64%) rename app/code/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/MassUnlock.php (81%) rename app/code/Magento/{PasswordManagement => User}/Model/Backend/Observer.php (61%) rename app/code/Magento/{PasswordManagement => User}/Model/System/Config/Source/Password.php (88%) rename app/code/Magento/{PasswordManagement/Setup/InstallSchema.php => User/Setup/UpgradeSchema.php} (83%) rename app/code/Magento/{PasswordManagement => User}/Test/Unit/Console/UnlockAdminAccountCommandTest.php (78%) rename app/code/Magento/{PasswordManagement => User}/etc/adminhtml/events.xml (50%) rename app/code/Magento/{PasswordManagement => User}/etc/logging.xml (62%) rename app/code/Magento/{PasswordManagement => User}/view/adminhtml/layout/adminhtml_locks_block.xml (99%) rename app/code/Magento/{PasswordManagement => User}/view/adminhtml/layout/adminhtml_locks_grid.xml (84%) rename app/code/Magento/{PasswordManagement => User}/view/adminhtml/layout/adminhtml_locks_index.xml (79%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/Block/Adminhtml/Crypt/Key/EditTest.php (70%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/Block/Adminhtml/Crypt/Key/FormTest.php (84%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/Controller/Adminhtml/Crypt/Key/IndexTest.php (87%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/Controller/Adminhtml/Crypt/Key/SaveTest.php (97%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/Model/Resource/Key/ChangeTest.php (87%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => EncryptionKey}/_files/payment_info.php (100%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/GridTest.php (88%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/IndexTest.php (85%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => User}/Controller/Adminhtml/Locks/MassUnlockTest.php (88%) rename dev/tests/integration/testsuite/Magento/{PasswordManagement => User}/_files/locked_users.php (88%) diff --git a/app/code/Magento/Customer/Model/Observer.php b/app/code/Magento/Customer/Model/Observer.php index 5763043f7aa49..c85c8b84af8c6 100644 --- a/app/code/Magento/Customer/Model/Observer.php +++ b/app/code/Magento/Customer/Model/Observer.php @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\Customer\Model; use Magento\Customer\Api\GroupManagementInterface; @@ -14,12 +12,15 @@ use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\State as AppState; +use Magento\Framework\Encryption\Encryptor; +use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Escaper; +use Magento\Framework\Message\ManagerInterface; use Magento\Framework\Registry; use Magento\Store\Model\ScopeInterface; -use Magento\Framework\Message\ManagerInterface; /** + * Customer Observer Model * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class Observer @@ -74,6 +75,13 @@ class Observer */ protected $appState; + /** + * Encryption model + * + * @var EncryptorInterface + */ + protected $encryptor; + /** * @param Vat $customerVat * @param HelperAddress $customerAddress @@ -83,6 +91,7 @@ class Observer * @param ManagerInterface $messageManager * @param Escaper $escaper * @param AppState $appState + * @param EncryptorInterface $encryptor */ public function __construct( Vat $customerVat, @@ -92,7 +101,8 @@ public function __construct( ScopeConfigInterface $scopeConfig, ManagerInterface $messageManager, Escaper $escaper, - AppState $appState + AppState $appState, + EncryptorInterface $encryptor ) { $this->_customerVat = $customerVat; $this->_customerAddress = $customerAddress; @@ -102,6 +112,7 @@ public function __construct( $this->messageManager = $messageManager; $this->escaper = $escaper; $this->appState = $appState; + $this->encryptor = $encryptor; } /** @@ -326,4 +337,25 @@ protected function addErrorMessage($customerAddress) $this->messageManager->addError(implode(' ', $message)); return $this; } + + /** + * Upgrade customer password hash when customer has logged in + * + * @param EventObserver $observer + * @return void + */ + public function upgradeCustomerPassword($observer) + { + $password = $observer->getEvent()->getPassword(); + /** @var \Magento\Customer\Model\Customer $model */ + $model = $observer->getEvent()->getModel(); + $isValidHash = $this->encryptor->isValidHashByVersion( + $password, + $model->getPasswordHash(), + Encryptor::HASH_VERSION_LATEST + ); + if (!$isValidHash) { + $model->changePassword($password); + } + } } diff --git a/app/code/Magento/Customer/README.md b/app/code/Magento/Customer/README.md index f3de8700173ce..d0463d847b74b 100644 --- a/app/code/Magento/Customer/README.md +++ b/app/code/Magento/Customer/README.md @@ -1 +1,2 @@ -The Magento_Customer module serves to handle the customer data (Customer, Customer Address and Customer Group entities) both in the admin panel and the storefront. +The Magento_Customer module serves to handle the customer data (Customer, Customer Address and Customer Group entities) both in the admin panel and the storefront. +For customer passwords, the module implements upgrading hashes. diff --git a/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php index d02337fe6bc81..305046b2d32d5 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php @@ -68,6 +68,16 @@ class ObserverTest extends \PHPUnit_Framework_TestCase */ protected $appState; + /** + * @var \Magento\Framework\Encryption\Encryptor|\PHPUnit_Framework_MockObject_MockObject + */ + protected $encryptorMock; + + /** + * @var \Magento\Customer\Model\Customer|\PHPUnit_Framework_MockObject_MockObject + */ + protected $customerMock; + protected function setUp() { $this->vat = $this->getMockBuilder('Magento\Customer\Model\Vat') @@ -99,6 +109,17 @@ protected function setUp() ->disableOriginalConstructor() ->getMock(); + $this->encryptorMock = $this->getMockBuilder('\Magento\Framework\Encryption\Encryptor') + ->disableOriginalConstructor() + ->getMock(); + $this->encryptorMock->expects($this->any()) + ->method('isValidHashByVersion') + ->will($this->returnCallback( + function ($arg1, $arg2) { + return $arg1 == $arg2; + } + ) + ); $this->model = new Observer( $this->vat, $this->helperAddress, @@ -107,7 +128,8 @@ protected function setUp() $this->scopeConfig, $this->messageManager, $this->escaper, - $this->appState + $this->appState, + $this->encryptorMock ); } @@ -746,4 +768,45 @@ public function dataProviderAfterAddressSaveNewGroup() ], ]; } + + /** + * Test successfully password change if new password doesn't match old one + */ + public function testUpgradeCustomerPassword() + { + $customerMock = $this->getMockBuilder('\Magento\Customer\Model\Customer') + ->disableOriginalConstructor() + ->setMethods(['getPasswordHash', 'changePassword', '__wakeup']) + ->getMock(); + $customerMock->expects($this->once())->method('changePassword')->will($this->returnSelf()); + $customerMock->expects($this->once())->method('getPasswordHash')->will($this->returnValue('old password')); + + $event = new \Magento\Framework\DataObject(); + $event->setData(['password' => 'different password', 'model' => $customerMock]); + + $observerMock = new \Magento\Framework\DataObject(); + $observerMock->setData('event', $event); + + $this->model->upgradeCustomerPassword($observerMock); + } + + /** + * Test failure password change if new password matches old one + */ + public function testUpgradeCustomerPasswordNotChanged() + { + $customerMock = $this->getMockBuilder('\Magento\Customer\Model\Customer') + ->disableOriginalConstructor() + ->setMethods(['getPasswordHash', 'changePassword', '__wakeup']) + ->getMock(); + $customerMock->expects($this->never())->method('changePassword'); + $customerMock->expects($this->once())->method('getPasswordHash')->will($this->returnValue('same password')); + + $event = new \Magento\Framework\DataObject(); + $event->setData(['password' => 'same password', 'model' => $customerMock]); + + $observerMock = new \Magento\Framework\DataObject(); + $observerMock->setData('event', $event); + $this->model->upgradeCustomerPassword($observerMock); + } } diff --git a/app/code/Magento/Customer/etc/frontend/events.xml b/app/code/Magento/Customer/etc/frontend/events.xml index 51e6bec7b9c7e..826f91980b4f1 100644 --- a/app/code/Magento/Customer/etc/frontend/events.xml +++ b/app/code/Magento/Customer/etc/frontend/events.xml @@ -25,4 +25,7 @@ + + + diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php b/app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Edit.php similarity index 94% rename from app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php rename to app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Edit.php index 3c0b7663cef12..5ce0c2da40a5f 100644 --- a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Edit.php +++ b/app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Edit.php @@ -3,11 +3,10 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Block\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Block\Adminhtml\Crypt\Key; /** * Encryption key change edit page block - * */ class Edit extends \Magento\Backend\Block\Widget\Form\Container { diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php b/app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Form.php similarity index 76% rename from app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php rename to app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Form.php index f94ed96f1f436..7cce8004d0acc 100644 --- a/app/code/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/Form.php +++ b/app/code/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/Form.php @@ -3,9 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Block\Adminhtml\Crypt\Key; - -// @codingStandardsIgnoreFile +namespace Magento\EncryptionKey\Block\Adminhtml\Crypt\Key; /** * Encryption key change form block @@ -21,7 +19,13 @@ protected function _prepareForm() { /** @var \Magento\Framework\Data\Form $form */ $form = $this->_formFactory->create( - ['data' => ['id' => 'edit_form', 'action' => $this->getData('action'), 'method' => 'post']] + [ + 'data' => [ + 'id' => 'edit_form', + 'action' => $this->getData('action'), + 'method' => 'post' + ] + ] ); $fieldset = $form->addFieldset('main_fieldset', ['legend' => __('New Encryption Key')]); $fieldset->addField( @@ -36,7 +40,9 @@ protected function _prepareForm() 'name' => 'generate_random', 'label' => __('Auto-generate a Key'), 'options' => [0 => __('No'), 1 => __('Yes')], - 'onclick' => "var cryptKey = $('crypt_key'); cryptKey.disabled = this.value == 1; if (cryptKey.disabled) {cryptKey.parentNode.parentNode.hide();} else {cryptKey.parentNode.parentNode.show();}", + 'onclick' => "var cryptKey = $('crypt_key'); cryptKey.disabled = this.value == 1; " . + "if (cryptKey.disabled) {cryptKey.parentNode.parentNode.hide();} " . + "else {cryptKey.parentNode.parentNode.show();}", 'note' => __('The generated key will be displayed after changing.') ] ); diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key.php similarity index 70% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php rename to app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key.php index 47ef3865ba62e..8c02fcacecb6e 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key.php +++ b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key.php @@ -6,9 +6,8 @@ /** * Encryption key changer controller - * */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt; +namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt; abstract class Key extends \Magento\Backend\App\Action { @@ -19,6 +18,6 @@ abstract class Key extends \Magento\Backend\App\Action */ protected function _isAllowed() { - return $this->_authorization->isAllowed('Magento_PasswordManagement::crypt_key'); + return $this->_authorization->isAllowed('Magento_EncryptionKey::crypt_key'); } } diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php similarity index 57% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php rename to app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php index 99ced594c1199..0c2ad5395a93e 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php +++ b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php @@ -4,9 +4,12 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; -class Index extends \Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key +/** + * Key Index action + */ +class Index extends \Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key { /** * Render main page with form @@ -22,18 +25,12 @@ public function execute() } $this->_view->loadLayout(); - $this->_setActiveMenu('Magento_PasswordManagement::system_crypt_key'); + $this->_setActiveMenu('Magento_EncryptionKey::system_crypt_key'); $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Encryption Key')); - if (($formBlock = $this->_view->getLayout()->getBlock( - 'pm.crypt.key.form' - )) && ($data = $this->_objectManager->get( - 'Magento\Backend\Model\Session' - )->getFormData( - true - )) - ) { - /* @var \Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form $formBlock */ + if (($formBlock = $this->_view->getLayout()->getBlock('crypt.key.form')) && + ($data = $this->_objectManager->get('Magento\Backend\Model\Session')->getFormData(true))) { + /* @var \Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form $formBlock */ $formBlock->setFormData($data); } diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Save.php similarity index 85% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php rename to app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Save.php index d46e83bf30c9d..b294d98df9c1c 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Save.php +++ b/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Save.php @@ -5,9 +5,12 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; -class Save extends \Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key +/** + * Encryption Key Save Controller + */ +class Save extends \Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key { /** * @var \Magento\Framework\Encryption\EncryptorInterface @@ -15,7 +18,7 @@ class Save extends \Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key protected $encryptor; /** - * @var \Magento\PasswordManagement\Model\Resource\Key\Change + * @var \Magento\EncryptionKey\Model\Resource\Key\Change */ protected $change; @@ -27,13 +30,13 @@ class Save extends \Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key /** * @param \Magento\Backend\App\Action\Context $context * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor - * @param \Magento\PasswordManagement\Model\Resource\Key\Change $change + * @param \Magento\EncryptionKey\Model\Resource\Key\Change $change * @param \Magento\Framework\App\CacheInterface $cache */ public function __construct( \Magento\Backend\App\Action\Context $context, \Magento\Framework\Encryption\EncryptorInterface $encryptor, - \Magento\PasswordManagement\Model\Resource\Key\Change $change, + \Magento\EncryptionKey\Model\Resource\Key\Change $change, \Magento\Framework\App\CacheInterface $cache ) { $this->encryptor = $encryptor; diff --git a/app/code/Magento/PasswordManagement/LICENSE.txt b/app/code/Magento/EncryptionKey/LICENSE.txt similarity index 100% rename from app/code/Magento/PasswordManagement/LICENSE.txt rename to app/code/Magento/EncryptionKey/LICENSE.txt diff --git a/app/code/Magento/PasswordManagement/LICENSE_AFL.txt b/app/code/Magento/EncryptionKey/LICENSE_AFL.txt similarity index 100% rename from app/code/Magento/PasswordManagement/LICENSE_AFL.txt rename to app/code/Magento/EncryptionKey/LICENSE_AFL.txt diff --git a/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php b/app/code/Magento/EncryptionKey/Model/Resource/Key/Change.php similarity index 81% rename from app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php rename to app/code/Magento/EncryptionKey/Model/Resource/Key/Change.php index ebb759b90b486..2ebb4e2cc3f8e 100644 --- a/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php +++ b/app/code/Magento/EncryptionKey/Model/Resource/Key/Change.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Model\Resource\Key; +namespace Magento\EncryptionKey\Model\Resource\Key; use Magento\Framework\App\Filesystem\DirectoryList; use Magento\Framework\Config\ConfigOptionsListConstants; @@ -13,8 +13,6 @@ /** * Encryption key changer resource model * The operation must be done in one transaction - * - * @author Magento Core Team */ class Change extends \Magento\Framework\Model\Resource\Db\AbstractDb { @@ -23,28 +21,28 @@ class Change extends \Magento\Framework\Model\Resource\Db\AbstractDb * * @var \Magento\Framework\Encryption\EncryptorInterface */ - protected $_encryptor; + protected $encryptor; /** * Filesystem directory write interface * * @var \Magento\Framework\Filesystem\Directory\WriteInterface */ - protected $_directory; + protected $directory; /** * System configuration structure * * @var \Magento\Config\Model\Config\Structure */ - protected $_structure; + protected $structure; /** * Configuration writer * * @var \Magento\Framework\App\DeploymentConfig\Writer */ - protected $_writer; + protected $writer; /** * @param \Magento\Framework\Model\Resource\Db\Context $context @@ -62,11 +60,11 @@ public function __construct( \Magento\Framework\App\DeploymentConfig\Writer $writer, $connectionName = null ) { - $this->_encryptor = clone $encryptor; + $this->encryptor = clone $encryptor; parent::__construct($context, $connectionName); - $this->_directory = $filesystem->getDirectoryWrite(DirectoryList::CONFIG); - $this->_structure = $structure; - $this->_writer = $writer; + $this->directory = $filesystem->getDirectoryWrite(DirectoryList::CONFIG); + $this->structure = $structure; + $this->writer = $writer; } /** @@ -118,17 +116,17 @@ public function reEncryptDatabaseValues($safe = true) public function changeEncryptionKey($key = null) { // prepare new key, encryptor and new configuration segment - if (!$this->_writer->checkIfWritable()) { + if (!$this->writer->checkIfWritable()) { throw new \Exception(__('Deployment configuration file is not writable.')); } if (null === $key) { $key = md5(time()); } - $this->_encryptor->setNewKey($key); + $this->encryptor->setNewKey($key); $encryptSegment = new ConfigData(ConfigFilePool::APP_ENV); - $encryptSegment->set(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, $this->_encryptor->exportKeys()); + $encryptSegment->set(ConfigOptionsListConstants::CONFIG_PATH_CRYPT_KEY, $this->encryptor->exportKeys()); $configData = [$encryptSegment->getFileKey() => $encryptSegment->getData()]; @@ -137,7 +135,7 @@ public function changeEncryptionKey($key = null) try { $this->_reEncryptSystemConfigurationValues(); $this->_reEncryptCreditCardNumbers(); - $this->_writer->saveConfig($configData); + $this->writer->saveConfig($configData); $this->commit(); return $key; } catch (\Exception $e) { @@ -155,7 +153,7 @@ protected function _reEncryptSystemConfigurationValues() { // look for encrypted node entries in all system.xml files /** @var \Magento\Config\Model\Config\Structure $configStructure */ - $configStructure = $this->_structure; + $configStructure = $this->structure; $paths = $configStructure->getFieldPathsByAttribute( 'backend_model', 'Magento\Config\Model\Config\Backend\Encrypted' @@ -165,21 +163,16 @@ protected function _reEncryptSystemConfigurationValues() if ($paths) { $table = $this->getTable('core_config_data'); $values = $this->getConnection()->fetchPairs( - $this->getConnection()->select()->from( - $table, - ['config_id', 'value'] - )->where( - 'path IN (?)', - $paths - )->where( - 'value NOT LIKE ?', - '' - ) + $this->getConnection() + ->select() + ->from($table, ['config_id', 'value']) + ->where('path IN (?)', $paths) + ->where('value NOT LIKE ?', '') ); foreach ($values as $configId => $value) { $this->getConnection()->update( $table, - ['value' => $this->_encryptor->encrypt($this->_encryptor->decrypt($value))], + ['value' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], ['config_id = ?' => (int)$configId] ); } @@ -201,7 +194,7 @@ protected function _reEncryptCreditCardNumbers() foreach ($attributeValues as $valueId => $value) { $this->getConnection()->update( $table, - ['cc_number_enc' => $this->_encryptor->encrypt($this->_encryptor->decrypt($value))], + ['cc_number_enc' => $this->encryptor->encrypt($this->encryptor->decrypt($value))], ['entity_id = ?' => (int)$valueId] ); } diff --git a/app/code/Magento/EncryptionKey/README.md b/app/code/Magento/EncryptionKey/README.md new file mode 100644 index 0000000000000..b90ea34fdf478 --- /dev/null +++ b/app/code/Magento/EncryptionKey/README.md @@ -0,0 +1 @@ +The Magento_EncryptionKey module provides an advanced encryption model to protect passwords and other sensitive data. diff --git a/app/code/Magento/PasswordManagement/composer.json b/app/code/Magento/EncryptionKey/composer.json similarity index 78% rename from app/code/Magento/PasswordManagement/composer.json rename to app/code/Magento/EncryptionKey/composer.json index 63317efde084e..968d9155e504f 100644 --- a/app/code/Magento/PasswordManagement/composer.json +++ b/app/code/Magento/EncryptionKey/composer.json @@ -1,12 +1,11 @@ { - "name": "magento/module-password-management", + "name": "magento/module-encryption-key", "description": "N/A", "require": { "php": "~5.5.0|~5.6.0", "magento/module-config": "1.0.0-beta", "magento/module-sales": "1.0.0-beta", "magento/module-backend": "1.0.0-beta", - "magento/module-user": "1.0.0-beta", "magento/framework": "1.0.0-beta", "magento/magento-composer-installer": "*" }, @@ -19,7 +18,7 @@ "map": [ [ "*", - "Magento/PasswordManagement" + "Magento/EncryptionKey" ] ] } diff --git a/app/code/Magento/PasswordManagement/etc/acl.xml b/app/code/Magento/EncryptionKey/etc/acl.xml similarity index 65% rename from app/code/Magento/PasswordManagement/etc/acl.xml rename to app/code/Magento/EncryptionKey/etc/acl.xml index 8c4a1615dd60f..15dcb594e635b 100644 --- a/app/code/Magento/PasswordManagement/etc/acl.xml +++ b/app/code/Magento/EncryptionKey/etc/acl.xml @@ -10,11 +10,8 @@ - - - - + diff --git a/app/code/Magento/EncryptionKey/etc/adminhtml/menu.xml b/app/code/Magento/EncryptionKey/etc/adminhtml/menu.xml new file mode 100644 index 0000000000000..df50e3ce95718 --- /dev/null +++ b/app/code/Magento/EncryptionKey/etc/adminhtml/menu.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml b/app/code/Magento/EncryptionKey/etc/adminhtml/routes.xml similarity index 82% rename from app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml rename to app/code/Magento/EncryptionKey/etc/adminhtml/routes.xml index 569d27551d1fc..4269e609c50cd 100644 --- a/app/code/Magento/PasswordManagement/etc/adminhtml/routes.xml +++ b/app/code/Magento/EncryptionKey/etc/adminhtml/routes.xml @@ -8,7 +8,7 @@ - + diff --git a/app/code/Magento/PasswordManagement/etc/config.xml b/app/code/Magento/EncryptionKey/etc/config.xml similarity index 65% rename from app/code/Magento/PasswordManagement/etc/config.xml rename to app/code/Magento/EncryptionKey/etc/config.xml index da09926c09976..204937c0b0ae5 100644 --- a/app/code/Magento/PasswordManagement/etc/config.xml +++ b/app/code/Magento/EncryptionKey/etc/config.xml @@ -10,10 +10,6 @@ 900 - 6 - 30 - 90 - 1 diff --git a/app/code/Magento/EncryptionKey/etc/logging.xml b/app/code/Magento/EncryptionKey/etc/logging.xml new file mode 100644 index 0000000000000..9dec7bed63e38 --- /dev/null +++ b/app/code/Magento/EncryptionKey/etc/logging.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/app/code/Magento/PasswordManagement/etc/module.xml b/app/code/Magento/EncryptionKey/etc/module.xml similarity index 86% rename from app/code/Magento/PasswordManagement/etc/module.xml rename to app/code/Magento/EncryptionKey/etc/module.xml index cdf727a55c802..b30faf3e0646e 100644 --- a/app/code/Magento/PasswordManagement/etc/module.xml +++ b/app/code/Magento/EncryptionKey/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/app/code/Magento/PasswordManagement/i18n/de_DE.csv b/app/code/Magento/EncryptionKey/i18n/de_DE.csv similarity index 52% rename from app/code/Magento/PasswordManagement/i18n/de_DE.csv rename to app/code/Magento/EncryptionKey/i18n/de_DE.csv index 3612fc47ae503..6f364baa1ff9f 100644 --- a/app/code/Magento/PasswordManagement/i18n/de_DE.csv +++ b/app/code/Magento/EncryptionKey/i18n/de_DE.csv @@ -13,27 +13,7 @@ Username,Benutzername "Please enter an encryption key.","Bitte einen Verschlüsselungscode eingeben." "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Gesperrte Benutzer" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","Das Konto ist gesperrt." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Empfohlen -Forced,Dechiffriert -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Änderung des Verschlüsselungscodes" -"Admin Accounts Locks","Admin Kontosperrungen" -Unlock,Entsperren -"Last login","Letzter Login" -Failures,Fehler -Unlocked,"Gesperrt bis" diff --git a/app/code/Magento/PasswordManagement/i18n/en_US.csv b/app/code/Magento/EncryptionKey/i18n/en_US.csv similarity index 51% rename from app/code/Magento/PasswordManagement/i18n/en_US.csv rename to app/code/Magento/EncryptionKey/i18n/en_US.csv index 106d8edf541bc..792bc2f60f5a8 100644 --- a/app/code/Magento/PasswordManagement/i18n/en_US.csv +++ b/app/code/Magento/EncryptionKey/i18n/en_US.csv @@ -13,27 +13,7 @@ Username,Username "Please enter an encryption key.","Please enter an encryption key." "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Locked Users" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","This account is locked." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Recommended -Forced,Forced -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Encryption Key Change" -"Admin Accounts Locks","Admin Accounts Locks" -Unlock,Unlock -"Last login","Last login" -Failures,Failures -Unlocked,Unlocked diff --git a/app/code/Magento/PasswordManagement/i18n/es_ES.csv b/app/code/Magento/EncryptionKey/i18n/es_ES.csv similarity index 52% rename from app/code/Magento/PasswordManagement/i18n/es_ES.csv rename to app/code/Magento/EncryptionKey/i18n/es_ES.csv index 0033414380e78..0840d0cb0b5b2 100644 --- a/app/code/Magento/PasswordManagement/i18n/es_ES.csv +++ b/app/code/Magento/EncryptionKey/i18n/es_ES.csv @@ -13,27 +13,7 @@ Username,"Nombre de Usuario" "Please enter an encryption key.","Por favor introduzca una clave de encriptación" "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Usuarios bloqueados" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","Esta cuenta está bloqueada." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Recomendado -Forced,Forzado -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Cambio de clave de encriptación" -"Admin Accounts Locks","Bloqueo de cuentas de administración" -Unlock,Desbloquear -"Last login","Ultimo acceso" -Failures,Fallos -Unlocked,"Bloqueado hasta" diff --git a/app/code/Magento/PasswordManagement/i18n/fr_FR.csv b/app/code/Magento/EncryptionKey/i18n/fr_FR.csv similarity index 51% rename from app/code/Magento/PasswordManagement/i18n/fr_FR.csv rename to app/code/Magento/EncryptionKey/i18n/fr_FR.csv index 2f2fe83e365de..0604b96989aef 100644 --- a/app/code/Magento/PasswordManagement/i18n/fr_FR.csv +++ b/app/code/Magento/EncryptionKey/i18n/fr_FR.csv @@ -13,27 +13,7 @@ Username,"Nom d'utilisateur" "Please enter an encryption key.","Merci d'entrer une clé de chiffrement" "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Utilisateurs verrouillés" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","Le compte est verrouillé." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Recommandé -Forced,Forcé -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Modification Clé de Chiffrement" -"Admin Accounts Locks","Verrous Comptes Administrateurs" -Unlock,Déverrouiller -"Last login","Dernière connexion" -Failures,Echecs -Unlocked,"Verrouillé jusqu'" diff --git a/app/code/Magento/PasswordManagement/i18n/nl_NL.csv b/app/code/Magento/EncryptionKey/i18n/nl_NL.csv similarity index 51% rename from app/code/Magento/PasswordManagement/i18n/nl_NL.csv rename to app/code/Magento/EncryptionKey/i18n/nl_NL.csv index 4c52dbd60266e..cffae21b4184b 100644 --- a/app/code/Magento/PasswordManagement/i18n/nl_NL.csv +++ b/app/code/Magento/EncryptionKey/i18n/nl_NL.csv @@ -13,27 +13,7 @@ Username,Gebruikersnaam "Please enter an encryption key.","Vul een encryptie sleutel in." "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Gesloten Gebruikers" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","Dit account is geblokkeerd." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Aanbevolen -Forced,Gedwongen -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Encryptie Code Veranderen" -"Admin Accounts Locks","Beheerder Accounts Sloten" -Unlock,Openen -"Last login","Laatste login" -Failures,Mislukkingen -Unlocked,"Gesloten tot" diff --git a/app/code/Magento/PasswordManagement/i18n/pt_BR.csv b/app/code/Magento/EncryptionKey/i18n/pt_BR.csv similarity index 51% rename from app/code/Magento/PasswordManagement/i18n/pt_BR.csv rename to app/code/Magento/EncryptionKey/i18n/pt_BR.csv index 790b051e9e9f1..495e0c38d96cf 100644 --- a/app/code/Magento/PasswordManagement/i18n/pt_BR.csv +++ b/app/code/Magento/EncryptionKey/i18n/pt_BR.csv @@ -13,27 +13,7 @@ Username,"Nome do usuário" "Please enter an encryption key.","Por favor introduza uma chave de encriptação." "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users","Usuários Bloqueados" -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.","Esta conta está bloqueada." -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,Recomendado -Forced,Forçado -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change","Mudança na Chave de Criptografia" -"Admin Accounts Locks","Bloqueios para Contas de Administrador" -Unlock,Desbloquear -"Last login","Último login" -Failures,Falhas -Unlocked,"Bloqueado até" diff --git a/app/code/Magento/PasswordManagement/i18n/zh_CN.csv b/app/code/Magento/EncryptionKey/i18n/zh_CN.csv similarity index 50% rename from app/code/Magento/PasswordManagement/i18n/zh_CN.csv rename to app/code/Magento/EncryptionKey/i18n/zh_CN.csv index 08650721ba2b3..3b5711dc0af9b 100644 --- a/app/code/Magento/PasswordManagement/i18n/zh_CN.csv +++ b/app/code/Magento/EncryptionKey/i18n/zh_CN.csv @@ -13,27 +13,7 @@ Username,用户名 "Please enter an encryption key.",请输入加密密钥。 "The encryption key has been changed.","The encryption key has been changed." "This is your new encryption key: %1. Be sure to write it down and take good care of it!","This is your new encryption key: %1. Be sure to write it down and take good care of it!" -"Locked Users",锁定的用户 -"Unlocked %1 user(s).","Unlocked %1 user(s)." -"This account is locked.",该帐户已被锁定。 -"It's time to change your password.","It's time to change your password." -"It's time to change your password.","It's time to change your password." -"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." -"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." "Not supported cipher version","Not supported cipher version" "The encryption key format is invalid.","The encryption key format is invalid." "File %1 is not writeable.","File %1 is not writeable." -Recommended,推荐 -Forced,强制 -"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" -"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." -"Lockout Time (minutes)","Lockout Time (minutes)" -"Password Lifetime (days)","Password Lifetime (days)" -"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " -"Password Change","Password Change" "Encryption Key Change",加密密钥已更改 -"Admin Accounts Locks",管理帐户锁定 -Unlock,解锁 -"Last login",上一次登录 -Failures,失败 -Unlocked,锁定直到 diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml b/app/code/Magento/EncryptionKey/view/adminhtml/layout/adminhtml_crypt_key_index.xml similarity index 58% rename from app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml rename to app/code/Magento/EncryptionKey/view/adminhtml/layout/adminhtml_crypt_key_index.xml index df4fef540efcc..7180d426aceb8 100644 --- a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_crypt_key_index.xml +++ b/app/code/Magento/EncryptionKey/view/adminhtml/layout/adminhtml_crypt_key_index.xml @@ -8,8 +8,8 @@ - - + + diff --git a/app/code/Magento/PasswordManagement/Model/Observer.php b/app/code/Magento/PasswordManagement/Model/Observer.php deleted file mode 100644 index 6227978912eef..0000000000000 --- a/app/code/Magento/PasswordManagement/Model/Observer.php +++ /dev/null @@ -1,53 +0,0 @@ -_encryptor = $encryptor; - } - - /** - * Upgrade customer password hash when customer has logged in - * - * @param EventObserver $observer - * @return void - */ - public function upgradeCustomerPassword($observer) - { - $password = $observer->getEvent()->getPassword(); - /** @var \Magento\Customer\Model\Customer $model */ - $model = $observer->getEvent()->getModel(); - $isValidHash = $this->_encryptor->isValidHashByVersion( - $password, - $model->getPasswordHash(), - Encryptor::HASH_VERSION_LATEST - ); - if (!$isValidHash) { - $model->changePassword($password); - } - } -} diff --git a/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php b/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php deleted file mode 100644 index c4682fef8cc85..0000000000000 --- a/app/code/Magento/PasswordManagement/Model/Resource/Admin/User.php +++ /dev/null @@ -1,166 +0,0 @@ -getConnection()->update( - $this->getMainTable(), - ['failures_num' => 0, 'first_failure' => null, 'lock_expires' => null], - $this->getIdFieldName() . ' IN (' . $this->getConnection()->quote($userIds) . ')' - ); - } - - /** - * Lock specified user record(s) - * - * @param int|int[] $userIds - * @param int $exceptId - * @param int $lifetime - * @return int number of affected rows - */ - public function lock($userIds, $exceptId, $lifetime) - { - if (!is_array($userIds)) { - $userIds = [$userIds]; - } - $exceptId = (int)$exceptId; - return $this->getConnection()->update( - $this->getMainTable(), - ['lock_expires' => $this->dateTime->formatDate(time() + $lifetime)], - "{$this->getIdFieldName()} IN (" . $this->getConnection()->quote( - $userIds - ) . ")\n AND {$this->getIdFieldName()} <> {$exceptId}" - ); - } - - /** - * Increment failures count along with updating lock expire and first failure dates - * - * @param ModelUser $user - * @param int|false $setLockExpires - * @param int|false $setFirstFailure - * @return void - */ - public function updateFailure($user, $setLockExpires = false, $setFirstFailure = false) - { - $update = ['failures_num' => new \Zend_Db_Expr('failures_num + 1')]; - if (false !== $setFirstFailure) { - $update['first_failure'] = $this->dateTime->formatDate($setFirstFailure); - $update['failures_num'] = 1; - } - if (false !== $setLockExpires) { - $update['lock_expires'] = $this->dateTime->formatDate($setLockExpires); - } - $this->getConnection()->update( - $this->getMainTable(), - $update, - $this->getConnection()->quoteInto("{$this->getIdFieldName()} = ?", $user->getId()) - ); - } - - /** - * Purge and get remaining old password hashes - * - * @param ModelUser $user - * @param int $retainLimit - * @return array - */ - public function getOldPasswords($user, $retainLimit = 4) - { - $userId = (int)$user->getId(); - $table = $this->getTable('admin_passwords'); - - // purge expired passwords, except that should retain - $retainPasswordIds = $this->getConnection()->fetchCol( - $this->getConnection()->select()->from( - $table, - 'password_id' - )->where( - 'user_id = :user_id' - )->order( - 'expires ' . \Magento\Framework\DB\Select::SQL_DESC - )->order( - 'password_id ' . \Magento\Framework\DB\Select::SQL_DESC - )->limit( - $retainLimit - ), - [':user_id' => $userId] - ); - $where = ['user_id = ?' => $userId, 'expires <= ?' => time()]; - if ($retainPasswordIds) { - $where['password_id NOT IN (?)'] = $retainPasswordIds; - } - $this->getConnection()->delete($table, $where); - - // now get all remained passwords - return $this->getConnection()->fetchCol( - $this->getConnection()->select()->from($table, 'password_hash')->where('user_id = :user_id'), - [':user_id' => $userId] - ); - } - - /** - * Remember a password hash for further usage - * - * @param ModelUser $user - * @param string $passwordHash - * @param int $lifetime - * @return void - */ - public function trackPassword($user, $passwordHash, $lifetime) - { - $now = time(); - $this->getConnection()->insert( - $this->getTable('admin_passwords'), - [ - 'user_id' => $user->getId(), - 'password_hash' => $passwordHash, - 'expires' => $now + $lifetime, - 'last_updated' => $now - ] - ); - } - - /** - * Get latest password for specified user id - * Possible false positive when password was changed several times with different lifetime configuration - * - * @param int $userId - * @return array - */ - public function getLatestPassword($userId) - { - return $this->getConnection()->fetchRow( - $this->getConnection()->select()->from( - $this->getTable('admin_passwords') - )->where( - 'user_id = :user_id' - )->order( - 'password_id ' . \Magento\Framework\DB\Select::SQL_DESC - )->limit( - 1 - ), - [':user_id' => $userId] - ); - } -} diff --git a/app/code/Magento/PasswordManagement/README.md b/app/code/Magento/PasswordManagement/README.md deleted file mode 100644 index 9d13ada892e43..0000000000000 --- a/app/code/Magento/PasswordManagement/README.md +++ /dev/null @@ -1,2 +0,0 @@ -The Magento_PasswordManagement module provides an advanced encryption model to protect passwords and other sensitive data. -For customer passwords, the module implements upgrading hashes. For admin passwords, it enables setting lifetimes and locking them when expired or when a specified numbers of failures have occurred. It allows preventing password brute force attacks for system backend. diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php b/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php deleted file mode 100644 index f80bce9c4b678..0000000000000 --- a/app/code/Magento/PasswordManagement/Test/Unit/Model/ObserverTest.php +++ /dev/null @@ -1,88 +0,0 @@ -encryptorMock = $this->getMockBuilder( - '\Magento\Framework\Encryption\Encryptor' - )->disableOriginalConstructor()->getMock(); - $this->encryptorMock->expects($this->any())->method('isValidHashByVersion')->will( - $this->returnCallback( - function ($arg1, $arg2) { - return $arg1 == $arg2; - } - ) - ); - $this->observer = new \Magento\PasswordManagement\Model\Observer($this->encryptorMock); - $this->customerMock = $this->getMockBuilder( - '\Magento\Customer\Model\Customer' - )->disableOriginalConstructor()->setMethods( - ['getPasswordHash', 'changePassword', '__wakeup'] - )->getMock(); - } - - /** - * Create Observer with custom data structure and fill password - * - * @param $password - * @param $passwordHash - * @return \Magento\Framework\DataObject - */ - protected function getObserverMock($password, $passwordHash) - { - $this->customerMock->expects( - $this->once() - )->method( - 'getPasswordHash' - )->will( - $this->returnValue($passwordHash) - ); - - $event = new \Magento\Framework\DataObject(); - $event->setData(['password' => $password, 'model' => $this->customerMock]); - - $observerMock = new \Magento\Framework\DataObject(); - $observerMock->setData('event', $event); - - return $observerMock; - } - - /** - * Test successfully password change if new password doesn't match old one - */ - public function testUpgradeCustomerPassword() - { - $this->customerMock->expects($this->once())->method('changePassword')->will($this->returnSelf()); - $this->observer->upgradeCustomerPassword($this->getObserverMock('different password', 'old password')); - } - - /** - * Test failure password change if new password matches old one - */ - public function testUpgradeCustomerPasswordNotChanged() - { - $this->customerMock->expects($this->never())->method('changePassword'); - $this->observer->upgradeCustomerPassword($this->getObserverMock('same password', 'same password')); - } -} diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml deleted file mode 100644 index b05f508f04db3..0000000000000 --- a/app/code/Magento/PasswordManagement/etc/adminhtml/di.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Magento\Framework\Authorization - - - diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml deleted file mode 100644 index 176c28dfd5383..0000000000000 --- a/app/code/Magento/PasswordManagement/etc/adminhtml/menu.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml b/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml deleted file mode 100644 index e1de692512bcf..0000000000000 --- a/app/code/Magento/PasswordManagement/etc/adminhtml/system.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - -
- advanced - - - - We will disable this feature if the value is empty. - - - - - - - We will disable this feature if the value is empty. - - - - Magento\PasswordManagement\Model\System\Config\Source\Password - - -
-
-
diff --git a/app/code/Magento/PasswordManagement/etc/di.xml b/app/code/Magento/PasswordManagement/etc/di.xml deleted file mode 100644 index 152d0244b9455..0000000000000 --- a/app/code/Magento/PasswordManagement/etc/di.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - Magento\PasswordManagement\Console\UnlockAdminAccountCommand - - - - diff --git a/app/code/Magento/PasswordManagement/etc/frontend/events.xml b/app/code/Magento/PasswordManagement/etc/frontend/events.xml deleted file mode 100644 index 3a7f933559f0f..0000000000000 --- a/app/code/Magento/PasswordManagement/etc/frontend/events.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - diff --git a/app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php b/app/code/Magento/User/Block/Adminhtml/Locks.php similarity index 88% rename from app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php rename to app/code/Magento/User/Block/Adminhtml/Locks.php index 98f253adba442..40001b80f054f 100644 --- a/app/code/Magento/PasswordManagement/Block/Adminhtml/Locks.php +++ b/app/code/Magento/User/Block/Adminhtml/Locks.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Block\Adminhtml; +namespace Magento\User\Block\Adminhtml; /** * Locked administrators page diff --git a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php b/app/code/Magento/User/Console/UnlockAdminAccountCommand.php similarity index 95% rename from app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php rename to app/code/Magento/User/Console/UnlockAdminAccountCommand.php index ca20bb6fc13f8..30c3a59956596 100644 --- a/app/code/Magento/PasswordManagement/Console/UnlockAdminAccountCommand.php +++ b/app/code/Magento/User/Console/UnlockAdminAccountCommand.php @@ -3,13 +3,13 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Console; +namespace Magento\User\Console; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Magento\PasswordManagement\Model\Resource\Admin\User as AdminUser; +use Magento\User\Model\Resource\User as AdminUser; /** * Command for unlocking an account. diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php b/app/code/Magento/User/Controller/Adminhtml/Locks.php similarity index 71% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php rename to app/code/Magento/User/Controller/Adminhtml/Locks.php index 0d9891cc51e44..5317287a97c05 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks.php +++ b/app/code/Magento/User/Controller/Adminhtml/Locks.php @@ -6,9 +6,8 @@ /** * Locked administrators controller - * */ -namespace Magento\PasswordManagement\Controller\Adminhtml; +namespace Magento\User\Controller\Adminhtml; abstract class Locks extends \Magento\Backend\App\Action { @@ -19,6 +18,6 @@ abstract class Locks extends \Magento\Backend\App\Action */ protected function _isAllowed() { - return $this->_authorization->isAllowed('Magento_PasswordManagement::locks'); + return $this->_authorization->isAllowed('Magento_User::locks'); } } diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php b/app/code/Magento/User/Controller/Adminhtml/Locks/Grid.php similarity index 68% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php rename to app/code/Magento/User/Controller/Adminhtml/Locks/Grid.php index e917922494555..971b87791f613 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Grid.php +++ b/app/code/Magento/User/Controller/Adminhtml/Locks/Grid.php @@ -4,9 +4,12 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; -class Grid extends \Magento\PasswordManagement\Controller\Adminhtml\Locks +/** + * Locked users grid + */ +class Grid extends \Magento\User\Controller\Adminhtml\Locks { /** * Render AJAX-grid only diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php b/app/code/Magento/User/Controller/Adminhtml/Locks/Index.php similarity index 64% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php rename to app/code/Magento/User/Controller/Adminhtml/Locks/Index.php index 4536dad76a528..b3e9f092fd1b1 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/Index.php +++ b/app/code/Magento/User/Controller/Adminhtml/Locks/Index.php @@ -4,9 +4,12 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; -class Index extends \Magento\PasswordManagement\Controller\Adminhtml\Locks +/** + * Locks Index action + */ +class Index extends \Magento\User\Controller\Adminhtml\Locks { /** * Render page with grid @@ -16,7 +19,7 @@ class Index extends \Magento\PasswordManagement\Controller\Adminhtml\Locks public function execute() { $this->_view->loadLayout(); - $this->_setActiveMenu('Magento_PasswordManagement::system_acl_locks'); + $this->_setActiveMenu('Magento_User::system_acl_locks'); $this->_view->getPage()->getConfig()->getTitle()->prepend(__('Locked Users')); $this->_view->renderLayout(); } diff --git a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php b/app/code/Magento/User/Controller/Adminhtml/Locks/MassUnlock.php similarity index 81% rename from app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php rename to app/code/Magento/User/Controller/Adminhtml/Locks/MassUnlock.php index bbb28ba889476..b87e13d7781ae 100644 --- a/app/code/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlock.php +++ b/app/code/Magento/User/Controller/Adminhtml/Locks/MassUnlock.php @@ -4,11 +4,14 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; use Magento\Framework\Controller\ResultFactory; -class MassUnlock extends \Magento\PasswordManagement\Controller\Adminhtml\Locks +/** + * Mass Unlock Controller + */ +class MassUnlock extends \Magento\User\Controller\Adminhtml\Locks { /** * Unlock specified users @@ -22,7 +25,7 @@ public function execute() $userIds = $this->getRequest()->getPost('unlock'); if ($userIds && is_array($userIds)) { $affectedUsers = $this->_objectManager - ->get('Magento\PasswordManagement\Model\Resource\Admin\User') + ->get('Magento\User\Model\Resource\User') ->unlock($userIds); $this->getMessageManager()->addSuccess(__('Unlocked %1 user(s).', $affectedUsers)); } diff --git a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php b/app/code/Magento/User/Model/Backend/Observer.php similarity index 61% rename from app/code/Magento/PasswordManagement/Model/Backend/Observer.php rename to app/code/Magento/User/Model/Backend/Observer.php index ed5686c16579e..7dac4a423a440 100644 --- a/app/code/Magento/PasswordManagement/Model/Backend/Observer.php +++ b/app/code/Magento/User/Model/Backend/Observer.php @@ -3,14 +3,14 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Model\Backend; +namespace Magento\User\Model\Backend; use Magento\Framework\Event\Observer as EventObserver; use Magento\Framework\Exception\State\UserLockedException; use Magento\Framework\Encryption\Encryptor; /** - * PasswordManagement backend observer model + * User backend observer model * * Implements hashes upgrading * @SuppressWarnings(PHPMD.CouplingBetweenObjects) @@ -22,63 +22,63 @@ class Observer * * @var \Magento\Framework\AuthorizationInterface */ - protected $_authorization; + protected $authorization; /** * Backend configuration interface * * @var \Magento\Backend\App\ConfigInterface */ - protected $_backendConfig; + protected $backendConfig; /** * Admin user resource model * - * @var \Magento\PasswordManagement\Model\Resource\Admin\User + * @var \Magento\User\Model\Resource\User */ - protected $_userResource; + protected $userResource; /** * Backend url interface * * @var \Magento\Backend\Model\UrlInterface */ - protected $_url; + protected $url; /** * Backend session * * @var \Magento\Backend\Model\Session */ - protected $_session; + protected $session; /** * Backend authorization session * * @var \Magento\Backend\Model\Auth\Session */ - protected $_authSession; + protected $authSession; /** * Factory class for user model * * @var \Magento\User\Model\UserFactory */ - protected $_userFactory; + protected $userFactory; /** - * PasswordManagement encryption model + * Encryption model * * @var \Magento\Framework\Encryption\EncryptorInterface */ - protected $_encryptor; + protected $encryptor; /** * Action flag * * @var \Magento\Framework\App\ActionFlag */ - protected $_actionFlag; + protected $actionFlag; /** * Message manager interface @@ -90,7 +90,7 @@ class Observer /** * @param \Magento\Framework\AuthorizationInterface $authorization * @param \Magento\Backend\App\ConfigInterface $backendConfig - * @param \Magento\PasswordManagement\Model\Resource\Admin\User $userResource + * @param \Magento\User\Model\Resource\User $userResource * @param \Magento\Backend\Model\UrlInterface $url * @param \Magento\Backend\Model\Session $session * @param \Magento\Backend\Model\Auth\Session $authSession @@ -103,7 +103,7 @@ class Observer public function __construct( \Magento\Framework\AuthorizationInterface $authorization, \Magento\Backend\App\ConfigInterface $backendConfig, - \Magento\PasswordManagement\Model\Resource\Admin\User $userResource, + \Magento\User\Model\Resource\User $userResource, \Magento\Backend\Model\UrlInterface $url, \Magento\Backend\Model\Session $session, \Magento\Backend\Model\Auth\Session $authSession, @@ -112,15 +112,15 @@ public function __construct( \Magento\Framework\App\ActionFlag $actionFlag, \Magento\Framework\Message\ManagerInterface $messageManager ) { - $this->_authorization = $authorization; - $this->_backendConfig = $backendConfig; - $this->_userResource = $userResource; - $this->_url = $url; - $this->_session = $session; - $this->_authSession = $authSession; - $this->_userFactory = $userFactory; - $this->_encryptor = $encryptor; - $this->_actionFlag = $actionFlag; + $this->authorization = $authorization; + $this->backendConfig = $backendConfig; + $this->userResource = $userResource; + $this->url = $url; + $this->session = $session; + $this->authSession = $authSession; + $this->userFactory = $userFactory; + $this->encryptor = $encryptor; + $this->actionFlag = $actionFlag; $this->messageManager = $messageManager; } @@ -137,34 +137,11 @@ public function adminAuthenticate($observer) { $password = $observer->getEvent()->getPassword(); $user = $observer->getEvent()->getUser(); - $resource = $this->_userResource; $authResult = $observer->getEvent()->getResult(); - // update locking information regardless whether user locked or not if (!$authResult && $user->getId()) { - $now = new \DateTime(); - $lockThreshold = $this->getAdminLockThreshold(); - $maxFailures = (int)$this->_backendConfig->getValue('admin/security/lockout_failures'); - if (!($lockThreshold && $maxFailures)) { - return; - } - $failuresNum = (int)$user->getFailuresNum() + 1; - $firstFailureDate = $user->getFirstFailure(); - if ($firstFailureDate) { - $firstFailureDate = new \DateTime($firstFailureDate); - } - - $updateFirstFailureDate = false; - $updateLockExpires = false; - $lockThresholdInterval = new \DateInterval('PT' . $lockThreshold . 'S'); - // set first failure date when this is first failure or last first failure expired - if (1 === $failuresNum || !$firstFailureDate || $now->diff($firstFailureDate) > $lockThresholdInterval) { - $updateFirstFailureDate = $now; - // otherwise lock user - } elseif ($failuresNum >= $maxFailures) { - $updateLockExpires = $now->add($lockThresholdInterval); - } - $resource->updateFailure($user, $updateLockExpires, $updateFirstFailureDate); + // update locking information regardless whether user locked or not + $this->_updateLockingInformation($user); } // check whether user is locked @@ -182,38 +159,19 @@ public function adminAuthenticate($observer) return; } - $resource->unlock($user->getId()); - - /** - * Check whether the latest password is expired - * Side-effect can be when passwords were changed with different lifetime configuration settings - */ - $latestPassword = $this->_userResource->getLatestPassword($user->getId()); - if ($latestPassword) { - if ($this->_isLatestPasswordExpired($latestPassword)) { - if ($this->isPasswordChangeForced()) { - $message = __('It\'s time to change your password.'); - } else { - $myAccountUrl = $this->_url->getUrl('adminhtml/system_account/'); - $message = __('It\'s time to change your password.', $myAccountUrl); - } - $this->messageManager->addNotice($message); - $message = $this->messageManager->getMessages()->getLastAddedMessage(); - if ($message) { - $message->setIdentifier('magento_pm_password_expired')->setIsSticky(true); - $this->_authSession->setPciAdminUserIsPasswordExpired(true); - } - } - } + $this->userResource->unlock($user->getId()); + + $latestPassword = $this->userResource->getLatestPassword($user->getId()); + $this->_checkExpiredPassword($latestPassword); // upgrade admin password - $isValidHash = $this->_encryptor->isValidHashByVersion( + $isValidHash = $this->encryptor->isValidHashByVersion( $password, $user->getPassword(), Encryptor::HASH_VERSION_LATEST ); if (!$isValidHash) { - $this->_userFactory->create() + $this->userFactory->create() ->load($user->getId()) ->setNewPassword($password) ->setForceNewPassword(true) @@ -221,6 +179,61 @@ public function adminAuthenticate($observer) } } + /** + * Update locking information for the user + * + * @param \Magento\User\Model\User $user + */ + private function _updateLockingInformation($user) + { + $now = new \DateTime(); + $lockThreshold = $this->getAdminLockThreshold(); + $maxFailures = (int)$this->backendConfig->getValue('admin/security/lockout_failures'); + if (!($lockThreshold && $maxFailures)) { + return; + } + $failuresNum = (int)$user->getFailuresNum() + 1; + if ($firstFailureDate = $user->getFirstFailure()) { + $firstFailureDate = new \DateTime($firstFailureDate); + } + + $updateFirstFailureDate = false; + $updateLockExpires = false; + $lockThresholdInterval = new \DateInterval('PT' . $lockThreshold.'S'); + // set first failure date when this is first failure or last first failure expired + if (1 === $failuresNum || !$firstFailureDate || $now->diff($firstFailureDate) > $lockThresholdInterval) { + $updateFirstFailureDate = $now; + // otherwise lock user + } elseif ($failuresNum >= $maxFailures) { + $updateLockExpires = $now->add($lockThresholdInterval); + } + $this->userResource->updateFailure($user, $updateLockExpires, $updateFirstFailureDate); + } + + /** + * Check whether the latest password is expired + * Side-effect can be when passwords were changed with different lifetime configuration settings + * + * @param array $latestPassword + */ + private function _checkExpiredPassword($latestPassword) + { + if ($latestPassword && $this->_isLatestPasswordExpired($latestPassword)) { + if ($this->isPasswordChangeForced()) { + $message = __('It\'s time to change your password.'); + } else { + $myAccountUrl = $this->url->getUrl('adminhtml/system_account/'); + $message = __('It\'s time to change your password.', $myAccountUrl); + } + $this->messageManager->addNotice($message); + $message = $this->messageManager->getMessages()->getLastAddedMessage(); + if ($message) { + $message->setIdentifier('magento_user_password_expired')->setIsSticky(true); + $this->authSession->setPciAdminUserIsPasswordExpired(true); + } + } + } + /** * Check if latest password is expired * @@ -229,15 +242,11 @@ public function adminAuthenticate($observer) */ protected function _isLatestPasswordExpired($latestPassword) { - if (!isset($latestPassword['expires'])) { - return false; - } - - if ($this->getAdminPasswordLifetime() == 0) { + if (!isset($latestPassword['expires']) || $this->getAdminPasswordLifetime() == 0) { return false; + } else { + return (int)$latestPassword['expires'] < time(); } - - return (int)$latestPassword['expires'] < time(); } /** @@ -262,15 +271,15 @@ public function checkAdminPasswordChange($observer) } if ($password && !$user->getForceNewPassword() && $user->getId()) { - if ($this->_encryptor->validateHash($password, $user->getOrigData('password'))) { + if ($this->encryptor->validateHash($password, $user->getOrigData('password'))) { throw new \Magento\Framework\Exception\LocalizedException( __('Sorry, but this password has already been used. Please create another.') ); } // check whether password was used before - $resource = $this->_userResource; - $passwordHash = $this->_encryptor->getHash($password, false); + $resource = $this->userResource; + $passwordHash = $this->encryptor->getHash($password, false); foreach ($resource->getOldPasswords($user) as $oldPasswordHash) { if ($passwordHash === $oldPasswordHash) { throw new \Magento\Framework\Exception\LocalizedException( @@ -295,23 +304,22 @@ public function trackAdminNewPassword($observer) $password = $user->getNewPassword(); $passwordLifetime = $this->getAdminPasswordLifetime(); if ($passwordLifetime && $password && !$user->getForceNewPassword()) { - $resource = $this->_userResource; - $passwordHash = $this->_encryptor->getHash($password, false); + $resource = $this->userResource; + $passwordHash = $this->encryptor->getHash($password, false); $resource->trackPassword($user, $passwordHash, $passwordLifetime); - $this->messageManager->getMessages()->deleteMessageByIdentifier('magento_pm_password_expired'); - $this->_authSession->unsPciAdminUserIsPasswordExpired(); + $this->messageManager->getMessages()->deleteMessageByIdentifier('magento_user_password_expired'); + $this->authSession->unsPciAdminUserIsPasswordExpired(); } } } /** * Get admin lock threshold from configuration - * * @return int */ public function getAdminLockThreshold() { - return 60 * (int)$this->_backendConfig->getValue('admin/security/lockout_threshold'); + return 60 * (int)$this->backendConfig->getValue('admin/security/lockout_threshold'); } /** @@ -321,7 +329,7 @@ public function getAdminLockThreshold() */ public function getAdminPasswordLifetime() { - return 86400 * (int)$this->_backendConfig->getValue('admin/security/password_lifetime'); + return 86400 * (int)$this->backendConfig->getValue('admin/security/password_lifetime'); } /** @@ -335,7 +343,7 @@ public function forceAdminPasswordChange($observer) if (!$this->isPasswordChangeForced()) { return; } - $session = $this->_authSession; + $session = $this->authSession; if (!$session->isLoggedIn()) { return; } @@ -347,19 +355,19 @@ public function forceAdminPasswordChange($observer) $controller = $observer->getEvent()->getControllerAction(); /** @var \Magento\Framework\App\RequestInterface $request */ $request = $observer->getEvent()->getRequest(); - if ($this->_authSession->getPciAdminUserIsPasswordExpired()) { + if ($this->authSession->getPciAdminUserIsPasswordExpired()) { if (!in_array($request->getFullActionName(), $actionList)) { - if ($this->_authorization->isAllowed('Magento_Backend::myaccount')) { - $controller->getResponse()->setRedirect($this->_url->getUrl('adminhtml/system_account/')); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); - $this->_actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_POST_DISPATCH, true); + if ($this->authorization->isAllowed('Magento_Backend::myaccount')) { + $controller->getResponse()->setRedirect($this->url->getUrl('adminhtml/system_account/')); + $this->actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_DISPATCH, true); + $this->actionFlag->set('', \Magento\Framework\App\Action\Action::FLAG_NO_POST_DISPATCH, true); } else { /* * if admin password is expired and access to 'My Account' page is denied * than we need to do force logout with error message */ - $this->_authSession->clearStorage(); - $this->_session->clearStorage(); + $this->authSession->clearStorage(); + $this->session->clearStorage(); $this->messageManager->addError( __('Your password has expired; please contact your administrator.') ); @@ -376,6 +384,6 @@ public function forceAdminPasswordChange($observer) */ public function isPasswordChangeForced() { - return (bool)(int)$this->_backendConfig->getValue('admin/security/password_is_forced'); + return (bool)(int)$this->backendConfig->getValue('admin/security/password_is_forced'); } } diff --git a/app/code/Magento/User/Model/Resource/User.php b/app/code/Magento/User/Model/Resource/User.php index 5d8d64b7706de..6ec973c65ac07 100644 --- a/app/code/Magento/User/Model/Resource/User.php +++ b/app/code/Magento/User/Model/Resource/User.php @@ -4,8 +4,6 @@ * See COPYING.txt for license details. */ -// @codingStandardsIgnoreFile - namespace Magento\User\Model\Resource; use Magento\Authorization\Model\Acl\Role\Group as RoleGroup; @@ -476,4 +474,151 @@ public function updateRoleUsersAcl(\Magento\Authorization\Model\Role $role) return $rowsCount > 0; } + + /** + * Unlock specified user record(s) + * + * @param int|int[] $userIds + * @return int number of affected rows + */ + public function unlock($userIds) + { + if (!is_array($userIds)) { + $userIds = [$userIds]; + } + return $this->getConnection()->update( + $this->getMainTable(), + ['failures_num' => 0, 'first_failure' => null, 'lock_expires' => null], + $this->getIdFieldName() . ' IN (' . $this->getConnection()->quote($userIds) . ')' + ); + } + + /** + * Lock specified user record(s) + * + * @param int|int[] $userIds + * @param int $exceptId + * @param int $lifetime + * @return int number of affected rows + */ + public function lock($userIds, $exceptId, $lifetime) + { + if (!is_array($userIds)) { + $userIds = [$userIds]; + } + $exceptId = (int)$exceptId; + return $this->getConnection()->update( + $this->getMainTable(), + ['lock_expires' => $this->dateTime->formatDate(time() + $lifetime)], + "{$this->getIdFieldName()} IN (" . $this->getConnection()->quote( + $userIds + ) . ")\n AND {$this->getIdFieldName()} <> {$exceptId}" + ); + } + + /** + * Increment failures count along with updating lock expire and first failure dates + * + * @param ModelUser $user + * @param int|false $setLockExpires + * @param int|false $setFirstFailure + * @return void + */ + public function updateFailure($user, $setLockExpires = false, $setFirstFailure = false) + { + $update = ['failures_num' => new \Zend_Db_Expr('failures_num + 1')]; + if (false !== $setFirstFailure) { + $update['first_failure'] = $this->dateTime->formatDate($setFirstFailure); + $update['failures_num'] = 1; + } + if (false !== $setLockExpires) { + $update['lock_expires'] = $this->dateTime->formatDate($setLockExpires); + } + $this->getConnection()->update( + $this->getMainTable(), + $update, + $this->getConnection()->quoteInto("{$this->getIdFieldName()} = ?", $user->getId()) + ); + } + + /** + * Purge and get remaining old password hashes + * + * @param ModelUser $user + * @param int $retainLimit + * @return array + */ + public function getOldPasswords($user, $retainLimit = 4) + { + $userId = (int)$user->getId(); + $table = $this->getTable('admin_passwords'); + + // purge expired passwords, except that should retain + $retainPasswordIds = $this->getConnection()->fetchCol( + $this->getConnection() + ->select() + ->from($table, 'password_id') + ->where('user_id = :user_id') + ->order('expires ' . \Magento\Framework\DB\Select::SQL_DESC) + ->order('password_id ' . \Magento\Framework\DB\Select::SQL_DESC) + ->limit($retainLimit), + [':user_id' => $userId] + ); + $where = ['user_id = ?' => $userId, 'expires <= ?' => time()]; + if ($retainPasswordIds) { + $where['password_id NOT IN (?)'] = $retainPasswordIds; + } + $this->getConnection()->delete($table, $where); + + // now get all remained passwords + return $this->getConnection()->fetchCol( + $this->getConnection() + ->select() + ->from($table, 'password_hash') + ->where('user_id = :user_id'), + [':user_id' => $userId] + ); + } + + /** + * Remember a password hash for further usage + * + * @param ModelUser $user + * @param string $passwordHash + * @param int $lifetime + * @return void + */ + public function trackPassword($user, $passwordHash, $lifetime) + { + $now = time(); + $this->getConnection()->insert( + $this->getTable('admin_passwords'), + [ + 'user_id' => $user->getId(), + 'password_hash' => $passwordHash, + 'expires' => $now + $lifetime, + 'last_updated' => $now + ] + ); + } + + /** + * Get latest password for specified user id + * Possible false positive when password was changed several times with different lifetime configuration + * + * @param int $userId + * @return array + */ + public function getLatestPassword($userId) + { + return $this->getConnection()->fetchRow( + $this->getConnection() + ->select() + ->from($this->getTable('admin_passwords')) + ->where('user_id = :user_id') + ->order('password_id ' . \Magento\Framework\DB\Select::SQL_DESC) + ->limit(1), + [':user_id' => $userId] + ); + } } diff --git a/app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php b/app/code/Magento/User/Model/System/Config/Source/Password.php similarity index 88% rename from app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php rename to app/code/Magento/User/Model/System/Config/Source/Password.php index aff2ad3a1fa50..a444a91780318 100644 --- a/app/code/Magento/PasswordManagement/Model/System/Config/Source/Password.php +++ b/app/code/Magento/User/Model/System/Config/Source/Password.php @@ -9,7 +9,7 @@ * * @codeCoverageIgnore */ -namespace Magento\PasswordManagement\Model\System\Config\Source; +namespace Magento\User\Model\System\Config\Source; class Password extends \Magento\Framework\DataObject implements \Magento\Framework\Option\ArrayInterface { diff --git a/app/code/Magento/User/README.md b/app/code/Magento/User/README.md index 7795016d23674..cbefec77d1258 100644 --- a/app/code/Magento/User/README.md +++ b/app/code/Magento/User/README.md @@ -3,3 +3,4 @@ **User** enables admin users to manage and assign roles to administrators and other non-customer users, reset user passwords, and invalidate access tokens. Different roles can be assigned to different users to define their permissions. +For admin passwords, it enables setting lifetimes and locking them when expired or when a specified numbers of failures have occurred. It allows preventing password brute force attacks for system backend. diff --git a/app/code/Magento/PasswordManagement/Setup/InstallSchema.php b/app/code/Magento/User/Setup/UpgradeSchema.php similarity index 83% rename from app/code/Magento/PasswordManagement/Setup/InstallSchema.php rename to app/code/Magento/User/Setup/UpgradeSchema.php index bd28a1368c0d4..8700458bb7206 100644 --- a/app/code/Magento/PasswordManagement/Setup/InstallSchema.php +++ b/app/code/Magento/User/Setup/UpgradeSchema.php @@ -4,24 +4,53 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Setup; +namespace Magento\User\Setup; -use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; +use Magento\Framework\Setup\UpgradeSchemaInterface; /** * @codeCoverageIgnore */ -class InstallSchema implements InstallSchemaInterface +class UpgradeSchema implements UpgradeSchemaInterface { /** * {@inheritdoc} + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) + public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $context) { $installer = $setup; $installer->startSetup(); + $tableAdmins = $installer->getTable('admin_user'); + + $installer->getConnection()->addColumn( + $tableAdmins, + 'failures_num', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, + 'nullable' => true, + 'default' => 0, + 'comment' => 'Failure Number' + ] + ); + $installer->getConnection()->addColumn( + $tableAdmins, + 'first_failure', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, + 'comment' => 'First Failure' + ] + ); + $installer->getConnection()->addColumn( + $tableAdmins, + 'lock_expires', + [ + 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, + 'comment' => 'Expiration Lock Dates' + ] + ); /** * Create table 'admin_passwords' @@ -72,32 +101,6 @@ public function install(SchemaSetupInterface $setup, ModuleContextInterface $con ); $installer->getConnection()->createTable($table); - $tableAdmins = $installer->getTable('admin_user'); - - $installer->getConnection()->addColumn( - $tableAdmins, - 'failures_num', - [ - 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_SMALLINT, - 'nullable' => true, - 'default' => 0, - 'comment' => 'Failure Number' - ] - ); - - $installer->getConnection()->addColumn( - $tableAdmins, - 'first_failure', - ['type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, 'comment' => 'First Failure'] - ); - - $installer->getConnection()->addColumn( - $tableAdmins, - 'lock_expires', - ['type' => \Magento\Framework\DB\Ddl\Table::TYPE_TIMESTAMP, 'comment' => 'Expiration Lock Dates'] - ); - $installer->endSetup(); - } } diff --git a/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php b/app/code/Magento/User/Test/Unit/Console/UnlockAdminAccountCommandTest.php similarity index 78% rename from app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php rename to app/code/Magento/User/Test/Unit/Console/UnlockAdminAccountCommandTest.php index 1e5ee3527bf0f..bf1a7cf8806fa 100644 --- a/app/code/Magento/PasswordManagement/Test/Unit/Console/UnlockAdminAccountCommandTest.php +++ b/app/code/Magento/User/Test/Unit/Console/UnlockAdminAccountCommandTest.php @@ -4,9 +4,9 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Test\Unit\Console; +namespace Magento\User\Test\Unit\Console; -use Magento\PasswordManagement\Console\UnlockAdminAccountCommand; +use Magento\User\Console\UnlockAdminAccountCommand; /** * Unit tests for StartConsumerCommand @@ -38,11 +38,11 @@ protected function setUp() public function testConfigure() { $this->command = $this->objectManager - ->getObject('Magento\PasswordManagement\Console\UnlockAdminAccountCommand'); + ->getObject('Magento\User\Console\UnlockAdminAccountCommand'); $this->assertEquals(UnlockAdminAccountCommand::COMMAND_ADMIN_ACCOUNT_UNLOCK, $this->command->getName()); $this->assertEquals(UnlockAdminAccountCommand::COMMAND_DESCRIPTION, $this->command->getDescription()); $this->command->getDefinition()->getArgument(UnlockAdminAccountCommand::ARGUMENT_ADMIN_USERNAME); - $this->assertContains('This command unlocks admin by its username', $this->command->getHelp()); + $this->assertContains('This command unlocks an admin account by its username', $this->command->getHelp()); } } diff --git a/app/code/Magento/User/etc/acl.xml b/app/code/Magento/User/etc/acl.xml index 502b706e13856..261a998abc757 100644 --- a/app/code/Magento/User/etc/acl.xml +++ b/app/code/Magento/User/etc/acl.xml @@ -12,6 +12,7 @@ + diff --git a/app/code/Magento/User/etc/adminhtml/di.xml b/app/code/Magento/User/etc/adminhtml/di.xml index 26f2ed8ba4485..ad550cbf5058d 100644 --- a/app/code/Magento/User/etc/adminhtml/di.xml +++ b/app/code/Magento/User/etc/adminhtml/di.xml @@ -7,4 +7,9 @@ --> + + + Magento\Framework\Authorization + + diff --git a/app/code/Magento/PasswordManagement/etc/adminhtml/events.xml b/app/code/Magento/User/etc/adminhtml/events.xml similarity index 50% rename from app/code/Magento/PasswordManagement/etc/adminhtml/events.xml rename to app/code/Magento/User/etc/adminhtml/events.xml index 60df1cc8b8691..01f8142a549d0 100644 --- a/app/code/Magento/PasswordManagement/etc/adminhtml/events.xml +++ b/app/code/Magento/User/etc/adminhtml/events.xml @@ -7,15 +7,15 @@ --> - + - + - + - + diff --git a/app/code/Magento/User/etc/adminhtml/menu.xml b/app/code/Magento/User/etc/adminhtml/menu.xml index bac4265e50aba..9e9d9ded06c0c 100644 --- a/app/code/Magento/User/etc/adminhtml/menu.xml +++ b/app/code/Magento/User/etc/adminhtml/menu.xml @@ -10,5 +10,6 @@ +
diff --git a/app/code/Magento/User/etc/adminhtml/system.xml b/app/code/Magento/User/etc/adminhtml/system.xml index c49f8f81f1ddf..8fecae6a8f95b 100644 --- a/app/code/Magento/User/etc/adminhtml/system.xml +++ b/app/code/Magento/User/etc/adminhtml/system.xml @@ -15,6 +15,23 @@ Magento\Config\Model\Config\Source\Email\Template + + + + We will disable this feature if the value is empty. + + + + + + + We will disable this feature if the value is empty. + + + + Magento\User\Model\System\Config\Source\Password + + diff --git a/app/code/Magento/User/etc/config.xml b/app/code/Magento/User/etc/config.xml index 6ef863af41a76..d8736de1b56ec 100644 --- a/app/code/Magento/User/etc/config.xml +++ b/app/code/Magento/User/etc/config.xml @@ -14,6 +14,12 @@ 1 admin_emails_reset_password_template + + 6 + 30 + 90 + 1 + diff --git a/app/code/Magento/User/etc/di.xml b/app/code/Magento/User/etc/di.xml index 962ccc2aa632a..1973befe228e6 100644 --- a/app/code/Magento/User/etc/di.xml +++ b/app/code/Magento/User/etc/di.xml @@ -15,4 +15,11 @@ + + + + Magento\User\Console\UnlockAdminAccountCommand + + + diff --git a/app/code/Magento/PasswordManagement/etc/logging.xml b/app/code/Magento/User/etc/logging.xml similarity index 62% rename from app/code/Magento/PasswordManagement/etc/logging.xml rename to app/code/Magento/User/etc/logging.xml index 46fe78b7e2658..9cdb19e72f458 100644 --- a/app/code/Magento/PasswordManagement/etc/logging.xml +++ b/app/code/Magento/User/etc/logging.xml @@ -8,15 +8,7 @@ - - - - - - - + change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Empfohlen +Forced,Dechiffriert +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Admin Accounts Locks","Admin Kontosperrungen" +Unlock,Entsperren +"Last login","Letzter Login" +Failures,Fehler +Unlocked,"Gesperrt bis" diff --git a/app/code/Magento/User/i18n/en_US.csv b/app/code/Magento/User/i18n/en_US.csv index 3f27379d04352..1f56f067006fc 100644 --- a/app/code/Magento/User/i18n/en_US.csv +++ b/app/code/Magento/User/i18n/en_US.csv @@ -95,3 +95,26 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,Username +"Locked Users","Locked Users" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","This account is locked." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Recommended +Forced,Forced +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Admin Accounts Locks","Admin Accounts Locks" +Unlock,Unlock +"Last login","Last login" +Failures,Failures +Unlocked,Unlocked diff --git a/app/code/Magento/User/i18n/es_ES.csv b/app/code/Magento/User/i18n/es_ES.csv index 3f27379d04352..4dd4f371b4af5 100644 --- a/app/code/Magento/User/i18n/es_ES.csv +++ b/app/code/Magento/User/i18n/es_ES.csv @@ -95,3 +95,27 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,"Nombre de Usuario" +"Locked Users","Usuarios bloqueados" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Esta cuenta está bloqueada." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Recomendado +Forced,Forzado +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Cambio de clave de encriptación" +"Admin Accounts Locks","Bloqueo de cuentas de administración" +Unlock,Desbloquear +"Last login","Ultimo acceso" +Failures,Fallos +Unlocked,"Bloqueado hasta" diff --git a/app/code/Magento/User/i18n/fr_FR.csv b/app/code/Magento/User/i18n/fr_FR.csv index 3f27379d04352..bbe1e63a96cbd 100644 --- a/app/code/Magento/User/i18n/fr_FR.csv +++ b/app/code/Magento/User/i18n/fr_FR.csv @@ -95,3 +95,27 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,"Nom d'utilisateur" +"Locked Users","Utilisateurs verrouillés" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Le compte est verrouillé." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Recommandé +Forced,Forcé +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Modification Clé de Chiffrement" +"Admin Accounts Locks","Verrous Comptes Administrateurs" +Unlock,Déverrouiller +"Last login","Dernière connexion" +Failures,Echecs +Unlocked,"Verrouillé jusqu'" diff --git a/app/code/Magento/User/i18n/nl_NL.csv b/app/code/Magento/User/i18n/nl_NL.csv index 3f27379d04352..91813e3a13311 100644 --- a/app/code/Magento/User/i18n/nl_NL.csv +++ b/app/code/Magento/User/i18n/nl_NL.csv @@ -95,3 +95,27 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,Gebruikersnaam +"Locked Users","Gesloten Gebruikers" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Dit account is geblokkeerd." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Aanbevolen +Forced,Gedwongen +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Encryptie Code Veranderen" +"Admin Accounts Locks","Beheerder Accounts Sloten" +Unlock,Openen +"Last login","Laatste login" +Failures,Mislukkingen +Unlocked,"Gesloten tot" diff --git a/app/code/Magento/User/i18n/pt_BR.csv b/app/code/Magento/User/i18n/pt_BR.csv index 3f27379d04352..d8d16b8cd04f7 100644 --- a/app/code/Magento/User/i18n/pt_BR.csv +++ b/app/code/Magento/User/i18n/pt_BR.csv @@ -95,3 +95,27 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,"Nome do usuário" +"Locked Users","Usuários Bloqueados" +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.","Esta conta está bloqueada." +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,Recomendado +Forced,Forçado +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change","Mudança na Chave de Criptografia" +"Admin Accounts Locks","Bloqueios para Contas de Administrador" +Unlock,Desbloquear +"Last login","Último login" +Failures,Falhas +Unlocked,"Bloqueado até" diff --git a/app/code/Magento/User/i18n/zh_CN.csv b/app/code/Magento/User/i18n/zh_CN.csv index 3f27379d04352..1c10afb0b649b 100644 --- a/app/code/Magento/User/i18n/zh_CN.csv +++ b/app/code/Magento/User/i18n/zh_CN.csv @@ -95,3 +95,27 @@ Resources,Resources "Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?","Warning!\r\nThis action will remove this user from already assigned role\r\nAre you sure?" "Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?","Warning!\r\nThis action will remove those users from already assigned roles\r\nAre you sure?" "Reset Password Template","Reset Password Template" +No,No +Yes,Yes +Username,用户名 +"Locked Users",锁定的用户 +"Unlocked %1 user(s).","Unlocked %1 user(s)." +"This account is locked.",该帐户已被锁定。 +"It's time to change your password.","It's time to change your password." +"It's time to change your password.","It's time to change your password." +"Sorry, but this password has already been used. Please create another.","Sorry, but this password has already been used. Please create another." +"Your password has expired; please contact your administrator.","Your password has expired; please contact your administrator." +Recommended,推荐 +Forced,强制 +"Maximum Login Failures to Lockout Account","Maximum Login Failures to Lockout Account" +"We will disable this feature if the value is empty.","We will disable this feature if the value is empty." +"Lockout Time (minutes)","Lockout Time (minutes)" +"Password Lifetime (days)","Password Lifetime (days)" +"We will disable this feature if the value is empty. ","We will disable this feature if the value is empty. " +"Password Change","Password Change" +"Encryption Key Change",加密密钥已更改 +"Admin Accounts Locks",管理帐户锁定 +Unlock,解锁 +"Last login",上一次登录 +Failures,失败 +Unlocked,锁定直到 diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_block.xml similarity index 99% rename from app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml rename to app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_block.xml index f40b8ef38ef9b..2098d0a7e7760 100644 --- a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_block.xml +++ b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_block.xml @@ -7,7 +7,7 @@ --> - + lockedAdminsGrid diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_grid.xml similarity index 84% rename from app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml rename to app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_grid.xml index d6d830e926887..bce6ee8a55c66 100644 --- a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_grid.xml +++ b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_grid.xml @@ -9,6 +9,6 @@ - + diff --git a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_index.xml similarity index 79% rename from app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml rename to app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_index.xml index 76d602577db62..ac9e34a19610d 100644 --- a/app/code/Magento/PasswordManagement/view/adminhtml/layout/adminhtml_locks_index.xml +++ b/app/code/Magento/User/view/adminhtml/layout/adminhtml_locks_index.xml @@ -10,7 +10,7 @@ - + diff --git a/composer.json b/composer.json index cacb3edc2e5ec..29c47c2741691 100644 --- a/composer.json +++ b/composer.json @@ -104,6 +104,7 @@ "magento/module-downloadable": "self.version", "magento/module-eav": "self.version", "magento/module-email": "self.version", + "magento/module-encryption-key": "self.version", "magento/module-fedex": "self.version", "magento/module-gift-message": "self.version", "magento/module-google-adwords": "self.version", @@ -123,7 +124,6 @@ "magento/module-offline-payments": "self.version", "magento/module-offline-shipping": "self.version", "magento/module-page-cache": "self.version", - "magento/module-password-management": "self.version", "magento/module-payment": "self.version", "magento/module-paypal": "self.version", "magento/module-persistent": "self.version", diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/EditTest.php similarity index 70% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/EditTest.php index 5bdca032fc4c2..76905b3097756 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/EditTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/EditTest.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Block\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Block\Adminhtml\Crypt\Key; class EditTest extends \PHPUnit_Framework_TestCase { @@ -13,12 +13,12 @@ class EditTest extends \PHPUnit_Framework_TestCase public function testEditBlock() { /** - * @var \Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Edit + * @var \Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Edit */ $block = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()->get( 'Magento\Framework\View\LayoutInterface' )->createBlock( - 'Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Edit' + 'Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Edit' ); $this->assertEquals('Encryption Key', $block->getHeaderText()); diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/FormTest.php similarity index 84% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/FormTest.php index 0ce5ee847a20c..7315cd9f10f3e 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Block/Adminhtml/Crypt/Key/FormTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Block/Adminhtml/Crypt/Key/FormTest.php @@ -3,10 +3,10 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Block\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Block\Adminhtml\Crypt\Key; /** - * Test class for \Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form + * Test class for \Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form * @magentoAppArea adminhtml */ class FormTest extends \PHPUnit_Framework_TestCase @@ -24,10 +24,10 @@ public function testPrepareForm() ->setDefaultDesignTheme(); $block = $objectManager->get('Magento\Framework\View\LayoutInterface') - ->createBlock('Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form'); + ->createBlock('Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form'); $prepareFormMethod = new \ReflectionMethod( - 'Magento\PasswordManagement\Block\Adminhtml\Crypt\Key\Form', + 'Magento\EncryptionKey\Block\Adminhtml\Crypt\Key\Form', '_prepareForm' ); $prepareFormMethod->setAccessible(true); diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/IndexTest.php similarity index 87% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/IndexTest.php index 44f5728cf0324..6d892bb9d8861 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/IndexTest.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController { diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/SaveTest.php similarity index 97% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/SaveTest.php index c87f1c7038ab8..e0b0a11cd72f8 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/SaveTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/SaveTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Crypt\Key; +namespace Magento\EncryptionKey\Controller\Adminhtml\Crypt\Key; class SaveTest extends \Magento\TestFramework\TestCase\AbstractBackendController { diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/Resource/Key/ChangeTest.php similarity index 87% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/Model/Resource/Key/ChangeTest.php index 034e3f0db1b23..494b050594c39 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Model/Resource/Key/ChangeTest.php +++ b/dev/tests/integration/testsuite/Magento/EncryptionKey/Model/Resource/Key/ChangeTest.php @@ -4,7 +4,7 @@ * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Model\Resource\Key; +namespace Magento\EncryptionKey\Model\Resource\Key; class ChangeTest extends \PHPUnit_Framework_TestCase { @@ -27,9 +27,9 @@ public function testChangeEncryptionKeyConfigNotWritable() $writerMock = $this->getMock('Magento\Framework\App\DeploymentConfig\Writer', [], [], '', false); $writerMock->expects($this->once())->method('checkIfWritable')->will($this->returnValue(false)); - /** @var \Magento\PasswordManagement\Model\Resource\Key\Change $keyChangeModel */ + /** @var \Magento\EncryptionKey\Model\Resource\Key\Change $keyChangeModel */ $keyChangeModel = $this->objectManager->create( - 'Magento\PasswordManagement\Model\Resource\Key\Change', + 'Magento\EncryptionKey\Model\Resource\Key\Change', ['writer' => $writerMock] ); $keyChangeModel->changeEncryptionKey(); @@ -37,7 +37,7 @@ public function testChangeEncryptionKeyConfigNotWritable() /** * @magentoDbIsolation enabled - * @magentoDataFixture Magento/PasswordManagement/_files/payment_info.php + * @magentoDataFixture Magento/EncryptionKey/_files/payment_info.php */ public function testChangeEncryptionKey() { @@ -52,9 +52,9 @@ public function testChangeEncryptionKey() ->method('getFieldPathsByAttribute') ->will($this->returnValue([$testPath])); - /** @var \Magento\PasswordManagement\Model\Resource\Key\Change $keyChangeModel */ + /** @var \Magento\EncryptionKey\Model\Resource\Key\Change $keyChangeModel */ $keyChangeModel = $this->objectManager->create( - 'Magento\PasswordManagement\Model\Resource\Key\Change', + 'Magento\EncryptionKey\Model\Resource\Key\Change', ['structure' => $structureMock, 'writer' => $writerMock] ); diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php b/dev/tests/integration/testsuite/Magento/EncryptionKey/_files/payment_info.php similarity index 100% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/_files/payment_info.php rename to dev/tests/integration/testsuite/Magento/EncryptionKey/_files/payment_info.php diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/GridTest.php similarity index 88% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php rename to dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/GridTest.php index 653145dadc4b2..766d95a7a1bb2 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/GridTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/GridTest.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; class GridTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -11,7 +11,7 @@ class GridTest extends \Magento\TestFramework\TestCase\AbstractBackendController * Test index action * * @magentoDbIsolation enabled - * @magentoDataFixture Magento/PasswordManagement/_files/locked_users.php + * @magentoDataFixture Magento/User/_files/locked_users.php */ public function testGridAction() { diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/IndexTest.php similarity index 85% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php rename to dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/IndexTest.php index 8018fb80711b3..c11802cc69c5b 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/IndexTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/IndexTest.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -11,7 +11,7 @@ class IndexTest extends \Magento\TestFramework\TestCase\AbstractBackendControlle * Test index action * * @magentoDbIsolation enabled - * @magentoDataFixture Magento/PasswordManagement/_files/locked_users.php + * @magentoDataFixture Magento/User/_files/locked_users.php */ public function testIndexAction() { diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/MassUnlockTest.php similarity index 88% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php rename to dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/MassUnlockTest.php index 37ee60a81647b..a0bb3e09004d5 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/Controller/Adminhtml/Locks/MassUnlockTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/Locks/MassUnlockTest.php @@ -3,7 +3,7 @@ * Copyright © 2015 Magento. All rights reserved. * See COPYING.txt for license details. */ -namespace Magento\PasswordManagement\Controller\Adminhtml\Locks; +namespace Magento\User\Controller\Adminhtml\Locks; class MassUnlockTest extends \Magento\TestFramework\TestCase\AbstractBackendController { @@ -11,7 +11,7 @@ class MassUnlockTest extends \Magento\TestFramework\TestCase\AbstractBackendCont * Test index action * * @magentoDbIsolation enabled - * @magentoDataFixture Magento/PasswordManagement/_files/locked_users.php + * @magentoDataFixture Magento/User/_files/locked_users.php */ public function testMassUnlockAction() { diff --git a/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php b/dev/tests/integration/testsuite/Magento/User/_files/locked_users.php similarity index 88% rename from dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php rename to dev/tests/integration/testsuite/Magento/User/_files/locked_users.php index a75f95e98f8d6..51a2f82c36873 100644 --- a/dev/tests/integration/testsuite/Magento/PasswordManagement/_files/locked_users.php +++ b/dev/tests/integration/testsuite/Magento/User/_files/locked_users.php @@ -46,6 +46,6 @@ $defaultAdminUserId = 1; $lockLifetime = 86400; -/** @var $modelLockedUsers \Magento\PasswordManagement\Model\Resource\Admin\User */ -$modelLockedUsers = $objectManager->create('Magento\PasswordManagement\Model\Resource\Admin\User'); +/** @var $modelLockedUsers \Magento\User\Model\Resource\User */ +$modelLockedUsers = $objectManager->create('Magento\User\Model\Resource\User'); $modelLockedUsers->lock($userIds, $defaultAdminUserId, $lockLifetime); diff --git a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php index f6acee1d24130..18751eef9757c 100644 --- a/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php +++ b/dev/tests/static/testsuite/Magento/Test/Integrity/_files/dependency_test/tables_ce.php @@ -7,7 +7,7 @@ 'admin_assert' => 'Magento\Backend', 'authorization_role' => 'Magento\Authorization', 'authorization_rule' => 'Magento\Authorization', - 'admin_user' => 'Magento\Backend', + 'admin_user' => 'Magento\User', 'adminnotification_inbox' => 'Magento\AdminNotification', 'catalog_category_entity_datetime' => 'Magento\Catalog', 'catalog_category_entity_decimal' => 'Magento\Catalog', @@ -193,7 +193,7 @@ 'oauth_nonce' => 'Magento\Integration', 'oauth_token' => 'Magento\Integration', 'authorizenet_debug' => 'Magento\Authorizenet', - 'admin_passwords' => 'Magento\PasswordManagement', + 'admin_passwords' => 'Magento\User', 'paypal_cert' => 'Magento\Paypal', 'paypal_payment_transaction' => 'Magento\Paypal', 'paypal_settlement_report' => 'Magento\Paypal', diff --git a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php index baf146629a788..7b4ddc53272ae 100644 --- a/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php +++ b/dev/tests/static/testsuite/Magento/Test/Legacy/_files/connection/blacklist/files_list.php @@ -12,7 +12,7 @@ '/app/code/Magento/AdvancedPricingImportExport/Model/Export/AdvancedPricing.php', '/app/code/Magento/CatalogImportExport/Test/Unit/Model/Export/ProductTest.php', '/app/code/Magento/CatalogImportExport/Model/Export/Product.php', - '/app/code/Magento/PasswordManagement/Controller/Adminhtml/Crypt/Key/Index.php', - '/app/code/Magento/PasswordManagement/Model/Resource/Key/Change.php', + '/app/code/Magento/EncryptionKey/Controller/Adminhtml/Crypt/Key/Index.php', + '/app/code/Magento/EncryptionKey/Model/Resource/Key/Change.php', //example '/app/code/Magento/Backend/Model/View.php', ]; From 22fb2276fcbcc38bfa34ed1107b38f9e4f4eec3e Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Thu, 27 Aug 2015 15:44:54 -0500 Subject: [PATCH 19/31] MAGETWO-38570: Refactor as much functionality from PCI module to rest of core/library modules - code style and doc updates --- app/code/Magento/Customer/Model/Observer.php | 3 ++- .../Magento/Customer/Test/Unit/Model/ObserverTest.php | 11 ++++++----- app/code/Magento/User/Model/Backend/Observer.php | 2 ++ app/code/Magento/User/Model/Resource/User.php | 4 +++- 4 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/code/Magento/Customer/Model/Observer.php b/app/code/Magento/Customer/Model/Observer.php index c85c8b84af8c6..c709130edb9de 100644 --- a/app/code/Magento/Customer/Model/Observer.php +++ b/app/code/Magento/Customer/Model/Observer.php @@ -12,6 +12,7 @@ use Magento\Framework\App\Area; use Magento\Framework\App\Config\ScopeConfigInterface; use Magento\Framework\App\State as AppState; +use Magento\Framework\DataObject; use Magento\Framework\Encryption\Encryptor; use Magento\Framework\Encryption\EncryptorInterface; use Magento\Framework\Escaper; @@ -262,7 +263,7 @@ public function afterAddressSave($observer) * Add success message for valid VAT ID * * @param Address $customerAddress - * @param $validationResult + * @param DataObject $validationResult * @return $this */ protected function addValidMessage($customerAddress, $validationResult) diff --git a/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php b/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php index 305046b2d32d5..6e50fdaf1f7df 100644 --- a/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php +++ b/app/code/Magento/Customer/Test/Unit/Model/ObserverTest.php @@ -114,11 +114,12 @@ protected function setUp() ->getMock(); $this->encryptorMock->expects($this->any()) ->method('isValidHashByVersion') - ->will($this->returnCallback( - function ($arg1, $arg2) { - return $arg1 == $arg2; - } - ) + ->will( + $this->returnCallback( + function ($arg1, $arg2) { + return $arg1 == $arg2; + } + ) ); $this->model = new Observer( $this->vat, diff --git a/app/code/Magento/User/Model/Backend/Observer.php b/app/code/Magento/User/Model/Backend/Observer.php index 7dac4a423a440..f04c35b9bd7ef 100644 --- a/app/code/Magento/User/Model/Backend/Observer.php +++ b/app/code/Magento/User/Model/Backend/Observer.php @@ -183,6 +183,7 @@ public function adminAuthenticate($observer) * Update locking information for the user * * @param \Magento\User\Model\User $user + * @return void */ private function _updateLockingInformation($user) { @@ -215,6 +216,7 @@ private function _updateLockingInformation($user) * Side-effect can be when passwords were changed with different lifetime configuration settings * * @param array $latestPassword + * @return void */ private function _checkExpiredPassword($latestPassword) { diff --git a/app/code/Magento/User/Model/Resource/User.php b/app/code/Magento/User/Model/Resource/User.php index 6ec973c65ac07..9f2bf7f3d3659 100644 --- a/app/code/Magento/User/Model/Resource/User.php +++ b/app/code/Magento/User/Model/Resource/User.php @@ -147,7 +147,9 @@ public function hasAssigned2Role($user) $connection = $this->getConnection(); $select = $connection->select(); - $select->from($this->getTable('authorization_role'))->where('parent_id > :parent_id')->where('user_id = :user_id'); + $select->from($this->getTable('authorization_role')) + ->where('parent_id > :parent_id') + ->where('user_id = :user_id'); $binds = ['parent_id' => 0, 'user_id' => $userId]; From db7d2f19f4a2571529af51329653f678b0037f4e Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Fri, 28 Aug 2015 16:48:53 +0300 Subject: [PATCH 20/31] MAGETWO-38927: Add unit test coverage for \Magento\User\Model --- .../Model/Plugin/AuthorizationRoleTest.php | 55 +++ .../Magento/User/Test/Unit/Model/UserTest.php | 430 ++++++++++++++---- 2 files changed, 389 insertions(+), 96 deletions(-) create mode 100644 app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php diff --git a/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php b/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php new file mode 100644 index 0000000000000..be819b6e8c9bc --- /dev/null +++ b/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php @@ -0,0 +1,55 @@ +userResourceModelMock = $this->getMockBuilder('Magento\User\Model\Resource\User') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $this->roleMock = $this->getMockBuilder('Magento\Authorization\Model\Role') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + + $objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); + $this->model = $objectManager->getObject( + 'Magento\User\Model\Plugin\AuthorizationRole', + [ + 'userResourceModel' => $this->userResourceModelMock + ] + ); + } + + public function testAfterSave() + { + $this->userResourceModelMock->expects($this->once())->method('updateRoleUsersAcl')->with($this->roleMock); + $this->assertInstanceOf( + '\Magento\Authorization\Model\Role', + $this->model->afterSave($this->roleMock, $this->roleMock) + ); + } +} diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php index 05e2f75ad8be1..9d8cc52ac2296 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php @@ -16,59 +16,71 @@ class UserTest extends \PHPUnit_Framework_TestCase { /** @var \Magento\User\Model\User */ - protected $_model; + protected $model; - /** @var \Magento\User\Helper\Data */ - protected $_userData; + /** @var \Magento\User\Helper\Data|\PHPUnit_Framework_MockObject_MockObject */ + protected $userDataMock; /** @var \Magento\Framework\Mail\Template\TransportBuilder|\PHPUnit_Framework_MockObject_MockObject */ - protected $_transportBuilderMock; + protected $transportBuilderMock; /** @var \Magento\Framework\Model\Context|\PHPUnit_Framework_MockObject_MockObject */ - protected $_contextMock; + protected $contextMock; /** @var \Magento\User\Model\Resource\User|\PHPUnit_Framework_MockObject_MockObject */ - protected $_resourceMock; + protected $resourceMock; /** @var \Magento\Framework\Data\Collection\AbstractDb|\PHPUnit_Framework_MockObject_MockObject */ - protected $_collectionMock; + protected $collectionMock; /** @var \Magento\Framework\Mail\TransportInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_transportMock; + protected $transportMock; /** @var \Magento\Store\Model\StoreManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_storeManagerMock; + protected $storeManagerMock; /** @var \Magento\Store\Model\Store|\PHPUnit_Framework_MockObject_MockObject */ - protected $_storetMock; + protected $storetMock; - /** @var \Magento\Backend\App\ConfigInterface */ - protected $_configMock; + /** @var \Magento\Backend\App\ConfigInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $configMock; /** @var \Magento\Framework\Encryption\EncryptorInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $_encryptorMock; + protected $encryptorMock; + + /** @var \Magento\Framework\Event\ManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ + protected $eventManagerMock; + + /** @var \Magento\Framework\Validator\DataObjectFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $validatorObjectFactoryMock; + + /** @var \Magento\User\Model\UserValidationRules|\PHPUnit_Framework_MockObject_MockObject */ + protected $validationRulesMock; + + /** @var \Magento\Authorization\Model\RoleFactory|\PHPUnit_Framework_MockObject_MockObject */ + protected $roleFactoryMock; /** * Set required values */ protected function setUp() { - $this->_userData = $this->getMockBuilder( + $this->userDataMock = $this->getMockBuilder( 'Magento\User\Helper\Data' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_contextMock = $this->getMockBuilder( + $this->contextMock = $this->getMockBuilder( 'Magento\Framework\Model\Context' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_resourceMock = $this->getMockBuilder( + $this->resourceMock = $this->getMockBuilder( 'Magento\User\Model\Resource\User' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_collectionMock = $this->getMockBuilder( + $this->collectionMock = $this->getMockBuilder( 'Magento\Framework\Data\Collection\AbstractDb' )->disableOriginalConstructor()->setMethods( [] @@ -78,69 +90,74 @@ protected function setUp() )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $eventManagerMock = $this->getMockBuilder( + $this->eventManagerMock = $this->getMockBuilder( 'Magento\Framework\Event\ManagerInterface' )->disableOriginalConstructor()->setMethods( - [] - )->getMock(); - $objectFactoryMock = $this->getMockBuilder( + ['dispatch'] + )->getMockForAbstractClass(); + $this->validatorObjectFactoryMock = $this->getMockBuilder( 'Magento\Framework\Validator\DataObjectFactory' )->disableOriginalConstructor()->setMethods( ['create'] )->getMock(); - $roleFactoryMock = $this->getMockBuilder( + $this->roleFactoryMock = $this->getMockBuilder( 'Magento\Authorization\Model\RoleFactory' )->disableOriginalConstructor()->setMethods( ['create'] )->getMock(); - $this->_transportMock = $this->getMockBuilder( + $this->transportMock = $this->getMockBuilder( 'Magento\Framework\Mail\TransportInterface' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_transportBuilderMock = $this->getMockBuilder( - '\Magento\Framework\Mail\Template\TransportBuilder' + $this->transportBuilderMock = $this->getMockBuilder( + 'Magento\Framework\Mail\Template\TransportBuilder' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_storetMock = $this->getMockBuilder( - '\Magento\Store\Model\Store' + $this->storetMock = $this->getMockBuilder( + 'Magento\Store\Model\Store' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_storeManagerMock = $this->getMockBuilder( - '\Magento\Store\Model\StoreManagerInterface' + $this->storeManagerMock = $this->getMockBuilder( + 'Magento\Store\Model\StoreManagerInterface' + )->disableOriginalConstructor()->setMethods( + [] + )->getMock(); + + $this->configMock = $this->getMockBuilder( + 'Magento\Backend\App\ConfigInterface' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_configMock = $this->getMockBuilder( - '\Magento\Backend\App\ConfigInterface' + $this->validationRulesMock = $this->getMockBuilder( + 'Magento\User\Model\UserValidationRules' )->disableOriginalConstructor()->setMethods( [] )->getMock(); - $this->_encryptorMock = $this->getMockBuilder('Magento\Framework\Encryption\EncryptorInterface') + $this->encryptorMock = $this->getMockBuilder('Magento\Framework\Encryption\EncryptorInterface') ->setMethods(['validateHash']) ->getMockForAbstractClass(); $helper = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this); - $this->_model = $helper->getObject( + $this->model = $helper->getObject( 'Magento\User\Model\User', [ - 'eventManager' => $eventManagerMock, - 'userData' => $this->_userData, - 'context' => $this->_contextMock, + 'eventManager' => $this->eventManagerMock, + 'userData' => $this->userDataMock, 'registry' => $coreRegistry, - 'resource' => $this->_resourceMock, - 'resourceCollection' => $this->_collectionMock, - 'validatorObjectFactory' => $objectFactoryMock, - 'roleFactory' => $roleFactoryMock, - 'transportBuilder' => $this->_transportBuilderMock, - 'storeManager' => $this->_storeManagerMock, - 'validationRules' => new UserValidationRules(), - 'config' => $this->_configMock, - 'encryptor' => $this->_encryptorMock + 'resource' => $this->resourceMock, + 'resourceCollection' => $this->collectionMock, + 'validatorObjectFactory' => $this->validatorObjectFactoryMock, + 'roleFactory' => $this->roleFactoryMock, + 'transportBuilder' => $this->transportBuilderMock, + 'storeManager' => $this->storeManagerMock, + 'validationRules' => $this->validationRulesMock, + 'config' => $this->configMock, + 'encryptor' => $this->encryptorMock ] ); } @@ -152,11 +169,11 @@ public function testSendPasswordResetNotificationEmail() $firstName = 'Foo'; $lastName = 'Bar'; - $this->_model->setEmail($email); - $this->_model->setFirstname($firstName); - $this->_model->setLastname($lastName); + $this->model->setEmail($email); + $this->model->setFirstname($firstName); + $this->model->setLastname($lastName); - $this->_configMock->expects( + $this->configMock->expects( $this->at(0) )->method( 'getValue' @@ -165,7 +182,7 @@ public function testSendPasswordResetNotificationEmail() )->will( $this->returnValue('templateId') ); - $this->_configMock->expects( + $this->configMock->expects( $this->at(1) )->method( 'getValue' @@ -174,17 +191,17 @@ public function testSendPasswordResetNotificationEmail() )->will( $this->returnValue('sender') ); - $this->_transportBuilderMock->expects($this->once())->method('setTemplateOptions')->will($this->returnSelf()); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects($this->once())->method('setTemplateOptions')->will($this->returnSelf()); + $this->transportBuilderMock->expects( $this->once() )->method( 'setTemplateVars' )->with( - ['user' => $this->_model, 'store' => $this->_storetMock] + ['user' => $this->model, 'store' => $this->storetMock] )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'addTo' @@ -194,7 +211,7 @@ public function testSendPasswordResetNotificationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'setFrom' @@ -203,7 +220,7 @@ public function testSendPasswordResetNotificationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'setTemplateIdentifier' @@ -212,26 +229,26 @@ public function testSendPasswordResetNotificationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'getTransport' )->will( - $this->returnValue($this->_transportMock) + $this->returnValue($this->transportMock) ); - $this->_transportMock->expects($this->once())->method('sendMessage'); + $this->transportMock->expects($this->once())->method('sendMessage'); - $this->_storeManagerMock->expects( + $this->storeManagerMock->expects( $this->once() )->method( 'getStore' )->with( $storeId )->will( - $this->returnValue($this->_storetMock) + $this->returnValue($this->storetMock) ); - $this->assertInstanceOf('\Magento\User\Model\User', $this->_model->sendPasswordResetNotificationEmail()); + $this->assertInstanceOf('\Magento\User\Model\User', $this->model->sendPasswordResetNotificationEmail()); } public function testSendPasswordResetConfirmationEmail() @@ -241,11 +258,11 @@ public function testSendPasswordResetConfirmationEmail() $firstName = 'Foo'; $lastName = 'Bar'; - $this->_model->setEmail($email); - $this->_model->setFirstname($firstName); - $this->_model->setLastname($lastName); + $this->model->setEmail($email); + $this->model->setFirstname($firstName); + $this->model->setLastname($lastName); - $this->_configMock->expects( + $this->configMock->expects( $this->at(0) )->method( 'getValue' @@ -254,7 +271,7 @@ public function testSendPasswordResetConfirmationEmail() )->will( $this->returnValue('templateId') ); - $this->_configMock->expects( + $this->configMock->expects( $this->at(1) )->method( 'getValue' @@ -263,17 +280,17 @@ public function testSendPasswordResetConfirmationEmail() )->will( $this->returnValue('sender') ); - $this->_transportBuilderMock->expects($this->once())->method('setTemplateOptions')->will($this->returnSelf()); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects($this->once())->method('setTemplateOptions')->will($this->returnSelf()); + $this->transportBuilderMock->expects( $this->once() )->method( 'setTemplateVars' )->with( - ['user' => $this->_model, 'store' => $this->_storetMock] + ['user' => $this->model, 'store' => $this->storetMock] )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'addTo' @@ -283,7 +300,7 @@ public function testSendPasswordResetConfirmationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'setFrom' @@ -292,7 +309,7 @@ public function testSendPasswordResetConfirmationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'setTemplateIdentifier' @@ -301,40 +318,40 @@ public function testSendPasswordResetConfirmationEmail() )->will( $this->returnSelf() ); - $this->_transportBuilderMock->expects( + $this->transportBuilderMock->expects( $this->once() )->method( 'getTransport' )->will( - $this->returnValue($this->_transportMock) + $this->returnValue($this->transportMock) ); - $this->_transportMock->expects($this->once())->method('sendMessage'); + $this->transportMock->expects($this->once())->method('sendMessage'); - $this->_storeManagerMock->expects( + $this->storeManagerMock->expects( $this->once() )->method( 'getStore' )->with( $storeId )->will( - $this->returnValue($this->_storetMock) + $this->returnValue($this->storetMock) ); - $this->assertInstanceOf('\Magento\User\Model\User', $this->_model->sendPasswordResetConfirmationEmail()); + $this->assertInstanceOf('\Magento\User\Model\User', $this->model->sendPasswordResetConfirmationEmail()); } public function testVerifyIdentity() { $password = 'password'; - $this->_encryptorMock + $this->encryptorMock ->expects($this->once()) ->method('validateHash') - ->with($password, $this->_model->getPassword()) + ->with($password, $this->model->getPassword()) ->will($this->returnValue(true)); - $this->_model->setIsActive(true); - $this->_resourceMock->expects($this->once())->method('hasAssigned2Role')->will($this->returnValue(true)); + $this->model->setIsActive(true); + $this->resourceMock->expects($this->once())->method('hasAssigned2Role')->will($this->returnValue(true)); $this->assertTrue( - $this->_model->verifyIdentity($password), + $this->model->verifyIdentity($password), 'Identity verification failed while should have passed.' ); } @@ -342,13 +359,13 @@ public function testVerifyIdentity() public function testVerifyIdentityFailure() { $password = 'password'; - $this->_encryptorMock + $this->encryptorMock ->expects($this->once()) ->method('validateHash') - ->with($password, $this->_model->getPassword()) + ->with($password, $this->model->getPassword()) ->will($this->returnValue(false)); $this->assertFalse( - $this->_model->verifyIdentity($password), + $this->model->verifyIdentity($password), 'Identity verification passed while should have failed.' ); } @@ -356,33 +373,254 @@ public function testVerifyIdentityFailure() public function testVerifyIdentityInactiveRecord() { $password = 'password'; - $this->_encryptorMock + $this->encryptorMock ->expects($this->once()) ->method('validateHash') - ->with($password, $this->_model->getPassword()) + ->with($password, $this->model->getPassword()) ->will($this->returnValue(true)); - $this->_model->setIsActive(false); + $this->model->setIsActive(false); $this->setExpectedException( 'Magento\\Framework\\Exception\\AuthenticationException', 'You did not sign in correctly or your account is temporarily disabled.' ); - $this->_model->verifyIdentity($password); + $this->model->verifyIdentity($password); } public function testVerifyIdentityNoAssignedRoles() { $password = 'password'; - $this->_encryptorMock + $this->encryptorMock ->expects($this->once()) ->method('validateHash') - ->with($password, $this->_model->getPassword()) + ->with($password, $this->model->getPassword()) ->will($this->returnValue(true)); - $this->_model->setIsActive(true); - $this->_resourceMock->expects($this->once())->method('hasAssigned2Role')->will($this->returnValue(false)); + $this->model->setIsActive(true); + $this->resourceMock->expects($this->once())->method('hasAssigned2Role')->will($this->returnValue(false)); $this->setExpectedException( 'Magento\\Framework\\Exception\\AuthenticationException', 'You need more permissions to access this.' ); - $this->_model->verifyIdentity($password); + $this->model->verifyIdentity($password); + } + + public function testSleep() + { + $excludedProperties = [ + '_eventManager', + '_cacheManager', + '_registry', + '_appState', + '_userData', + '_config', + '_validatorObject', + '_roleFactory', + '_encryptor', + '_transportBuilder', + '_storeManager', + '_validatorBeforeSave' + ]; + $actualResult = $this->model->__sleep(); + $this->assertNotEmpty($actualResult); + $expectedResult = array_intersect($actualResult, $excludedProperties); + $this->assertEmpty($expectedResult); + } + + public function testBeforeSave() + { + $this->eventManagerMock->expects($this->any())->method('dispatch'); + $this->model->setIsActive(1); + $actualData = $this->model->beforeSave()->getData(); + $this->assertArrayHasKey('modified', $actualData); + $this->assertArrayHasKey('extra', $actualData); + $this->assertArrayHasKey('password', $actualData); + $this->assertArrayHasKey('is_active', $actualData); + } + + public function testValidateOk() + { + /** @var $validatorMock \Magento\Framework\Validator\DataObject|\PHPUnit_Framework_MockObject_MockObject */ + $validatorMock = $this->getMockBuilder('Magento\Framework\Validator\DataObject') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->validatorObjectFactoryMock->expects($this->once())->method('create')->willReturn($validatorMock); + $this->validationRulesMock->expects($this->once()) + ->method('addUserInfoRules') + ->with($validatorMock); + $validatorMock->expects($this->once())->method('isValid')->willReturn(true); + $this->assertTrue($this->model->validate()); + } + + public function testValidateInvalid() + { + $messages = ['Invalid username']; + /** @var $validatorMock \Magento\Framework\Validator\DataObject|\PHPUnit_Framework_MockObject_MockObject */ + $validatorMock = $this->getMockBuilder('Magento\Framework\Validator\DataObject') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->validatorObjectFactoryMock->expects($this->once())->method('create')->willReturn($validatorMock); + $this->validationRulesMock->expects($this->once()) + ->method('addUserInfoRules') + ->with($validatorMock); + $validatorMock->expects($this->once())->method('isValid')->willReturn(false); + $validatorMock->expects($this->once())->method('getMessages')->willReturn($messages); + $this->assertEquals($messages, $this->model->validate()); + } + + public function testSaveExtra() + { + $data = [1, 2, 3]; + $this->resourceMock->expects($this->once())->method('saveExtra')->with($this->model, serialize($data)); + $this->assertInstanceOf('Magento\User\Model\User', $this->model->saveExtra($data)); + } + + public function testGetRoles() + { + $this->resourceMock->expects($this->once())->method('getRoles')->with($this->model)->willReturn([]); + $this->assertInternalType('array', $this->model->getRoles()); + } + + public function testGetRole() + { + $roles = ['role']; + $roleMock = $this->getMockBuilder('Magento\Authorization\Model\Role') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->roleFactoryMock->expects($this->once())->method('create')->willReturn($roleMock); + $this->resourceMock->expects($this->once())->method('getRoles')->with($this->model)->willReturn($roles); + $roleMock->expects($this->once())->method('load')->with($roles[0]); + $this->assertInstanceOf('Magento\Authorization\Model\Role', $this->model->getRole()); + } + + public function testDeleteFromRole() + { + $this->resourceMock->expects($this->once())->method('deleteFromRole')->with($this->model); + $this->assertInstanceOf('Magento\User\Model\User', $this->model->deleteFromRole()); + } + + public function testRoleUserExistsTrue() + { + $result = ['role']; + $this->resourceMock->expects($this->once())->method('roleUserExists')->with($this->model)->willReturn($result); + $this->assertTrue($this->model->roleUserExists()); + } + + public function testRoleUserExistsFalse() + { + $result = []; + $this->resourceMock->expects($this->once())->method('roleUserExists')->with($this->model)->willReturn($result); + $this->assertFalse($this->model->roleUserExists()); + } + + public function testGetAclRole() + { + $roles = ['role']; + $result = 1; + $roleMock = $this->getMockBuilder('Magento\Authorization\Model\Role') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->roleFactoryMock->expects($this->once())->method('create')->willReturn($roleMock); + $this->resourceMock->expects($this->once())->method('getRoles')->with($this->model)->willReturn($roles); + $roleMock->expects($this->once())->method('load')->with($roles[0]); + $roleMock->expects($this->once())->method('getId')->willReturn($result); + $this->assertEquals($result, $this->model->getAclRole()); + } + + /** + * @dataProvider authenticateDataProvider + */ + public function testAuthenticate($usernameIn, $usernameOut, $expectedResult) + { + $password = 'password'; + $config = 'config'; + + $data = ['id' => 1, 'is_active' => 1, 'username' => $usernameOut]; + + $this->configMock->expects($this->once()) + ->method('isSetFlag') + ->with('admin/security/use_case_sensitive_login') + ->willReturn($config); + $this->eventManagerMock->expects($this->any())->method('dispatch'); + + $this->resourceMock->expects($this->any())->method('loadByUsername')->willReturn($data); + $this->model->setIdFieldName('id'); + + $this->encryptorMock->expects($this->any())->method('validateHash')->willReturn(true); + $this->resourceMock->expects($this->any())->method('hasAssigned2Role')->willReturn(true); + $this->assertEquals($expectedResult, $this->model->authenticate($usernameIn, $password)); + } + + public function authenticateDataProvider() + { + return [ + 'success' => [ + 'usernameIn' => 'username', + 'usernameOut' => 'username', + 'expectedResult' => true + ], + 'failedUsername' => [ + 'usernameIn' => 'username1', + 'usernameOut' => 'username2', + 'expectedResult' => false + ] + ]; + } + + /** + * @expectedException \Magento\Framework\Exception\LocalizedException + */ + public function testAuthenticateException() + { + $username = 'username'; + $password = 'password'; + $config = 'config'; + + $this->configMock->expects($this->once()) + ->method('isSetFlag') + ->with('admin/security/use_case_sensitive_login') + ->willReturn($config); + + $this->eventManagerMock->expects($this->any())->method('dispatch'); + $this->resourceMock->expects($this->once()) + ->method('loadByUsername') + ->willThrowException(new \Magento\Framework\Exception\LocalizedException(__())); + $this->model->authenticate($username, $password); + } + + public function testChangeResetPasswordLinkToken() + { + $token = '1'; + $this->assertInstanceOf('Magento\User\Model\User', $this->model->changeResetPasswordLinkToken($token)); + $this->assertEquals($token, $this->model->getRpToken()); + $this->assertInternalType('string', $this->model->getRpTokenCreatedAt()); + } + + public function testIsResetPasswordLinkTokenExpiredEmptyToken() + { + $this->assertTrue($this->model->isResetPasswordLinkTokenExpired()); + } + + public function testIsResetPasswordLinkTokenExpiredIsExpiredToken() + { + $this->model->setRpToken('1'); + $this->model->setRpTokenCreatedAt( + (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT) + ); + $this->userDataMock->expects($this->once())->method('getResetPasswordLinkExpirationPeriod')->willReturn(0); + $this->assertTrue($this->model->isResetPasswordLinkTokenExpired()); + } + + public function testIsResetPasswordLinkTokenExpiredIsNotExpiredToken() + { + $this->model->setRpToken('1'); + $this->model->setRpTokenCreatedAt( + (new \DateTime())->format(\Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT) + ); + $this->userDataMock->expects($this->once())->method('getResetPasswordLinkExpirationPeriod')->willReturn(1); + $this->assertFalse($this->model->isResetPasswordLinkTokenExpired()); } } + From 51fecdd22140cd3666899d7469c9f245380362aa Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Fri, 28 Aug 2015 11:44:46 -0500 Subject: [PATCH 21/31] MAGETWO-41679: Create and Stabilize Bamboo Builds - fix dependency issues --- app/code/Magento/ImportExport/composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/app/code/Magento/ImportExport/composer.json b/app/code/Magento/ImportExport/composer.json index 8a19a4779907e..0c2cdcf2a66b6 100644 --- a/app/code/Magento/ImportExport/composer.json +++ b/app/code/Magento/ImportExport/composer.json @@ -8,6 +8,7 @@ "magento/module-eav": "1.0.0-beta", "magento/module-indexer": "1.0.0-beta", "magento/module-media-storage": "1.0.0-beta", + "magento/module-user": "1.0.0-beta", "magento/framework": "1.0.0-beta", "ext-ctype": "*", "magento/magento-composer-installer": "*" From 5edd86f6db48d3634f307a168cd7bac6fda132ad Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Mon, 31 Aug 2015 08:46:45 -0500 Subject: [PATCH 22/31] MAGETWO-38570: Refactor as much functionality from PCI module to rest of core/library modules - remove unnecessary suppressions --- app/code/Magento/User/Model/Backend/Observer.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/code/Magento/User/Model/Backend/Observer.php b/app/code/Magento/User/Model/Backend/Observer.php index f04c35b9bd7ef..07fb1a45fd42c 100644 --- a/app/code/Magento/User/Model/Backend/Observer.php +++ b/app/code/Magento/User/Model/Backend/Observer.php @@ -130,8 +130,6 @@ public function __construct( * @param EventObserver $observer * @return void * @throws \Magento\Framework\Exception\LocalizedException - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) */ public function adminAuthenticate($observer) { From 917533350fd5eeaa16940063fe64b40daab3813c Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Mon, 31 Aug 2015 20:00:14 +0300 Subject: [PATCH 23/31] MAGETWO-41575: Refactor ComposerInformation class to reduce coupling between objects --- .../Composer/ComposerInformationTest.php | 55 +---- .../Setup/Controller/ComponentGridTest.php | 25 ++- .../Setup/Model/UpdatePackagesCacheTest.php | 121 +++++++++++ .../Composer/ComposerInformation.php | 157 +------------- .../Setup/Controller/ComponentGrid.php | 16 +- .../Setup/Model/UpdatePackagesCache.php | 203 ++++++++++++++++++ 6 files changed, 362 insertions(+), 215 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php create mode 100644 setup/src/Magento/Setup/Model/UpdatePackagesCache.php diff --git a/dev/tests/integration/testsuite/Magento/Framework/Composer/ComposerInformationTest.php b/dev/tests/integration/testsuite/Magento/Framework/Composer/ComposerInformationTest.php index 75e7b277f9308..590a553274d7d 100644 --- a/dev/tests/integration/testsuite/Magento/Framework/Composer/ComposerInformationTest.php +++ b/dev/tests/integration/testsuite/Magento/Framework/Composer/ComposerInformationTest.php @@ -29,11 +29,6 @@ class ComposerInformationTest extends \PHPUnit_Framework_TestCase */ private $composerJsonFinder; - /** - * @var \Magento\Framework\Filesystem - */ - private $filesystem; - public function setUp() { $this->objectManager = Bootstrap::getObjectManager(); @@ -81,8 +76,7 @@ public function testGetRequiredPhpVersion($composerDir) 'applicationFactory' => new MagentoComposerApplicationFactory( $this->composerJsonFinder, $this->directoryList - ), - 'filesystem' => $this->filesystem, + ) ] ); @@ -106,8 +100,7 @@ public function testGetRequiredExtensions($composerDir) 'applicationFactory' => new MagentoComposerApplicationFactory( $this->composerJsonFinder, $this->directoryList - ), - 'filesystem' => $this->filesystem, + ) ] ); @@ -133,8 +126,7 @@ public function testGetRootRequiredPackagesAndTypes($composerDir) 'applicationFactory' => new MagentoComposerApplicationFactory( $this->composerJsonFinder, $this->directoryList - ), - 'filesystem' => $this->filesystem, + ) ] ); @@ -144,41 +136,6 @@ public function testGetRootRequiredPackagesAndTypes($composerDir) $this->assertEquals('library', $requiredPackagesAndTypes['composer/composer']); } - public function testGetPackagesForUpdate() - { - $packageName = 'magento/language-de_de'; - - $this->setupDirectory('testSkeleton'); - - /** @var \Magento\Framework\Composer\ComposerInformation $composerInfo */ - $composerInfo = $this->objectManager->create( - 'Magento\Framework\Composer\ComposerInformation', - [ - 'applicationFactory' => new MagentoComposerApplicationFactory( - $this->composerJsonFinder, - $this->directoryList - ), - 'filesystem' => $this->filesystem, - ] - ); - - $requiredPackages = $composerInfo->getInstalledMagentoPackages(); - $this->assertArrayHasKey($packageName, $requiredPackages); - - $this->assertTrue($composerInfo->syncPackagesForUpdate()); - - $packagesForUpdate = $composerInfo->getPackagesForUpdate(); - $this->assertArrayHasKey('packages', $packagesForUpdate); - $this->assertArrayHasKey($packageName, $packagesForUpdate['packages']); - $this->assertTrue( - version_compare( - $packagesForUpdate['packages'][$packageName]['latestVersion'], - $requiredPackages[$packageName]['version'], - '>' - ) - ); - } - /** * Data provider that returns directories containing different types of composer files. * @@ -206,8 +163,7 @@ public function testNoLock() 'applicationFactory' => new MagentoComposerApplicationFactory( $this->composerJsonFinder, $this->directoryList - ), - 'filesystem' => $this->filesystem, + ) ] ); } @@ -223,8 +179,7 @@ public function testIsPackageInComposerJson() 'applicationFactory' => new MagentoComposerApplicationFactory( $this->composerJsonFinder, $this->directoryList - ), - 'filesystem' => $this->filesystem, + ) ] ); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Controller/ComponentGridTest.php b/dev/tests/integration/testsuite/Magento/Setup/Controller/ComponentGridTest.php index f9032e83e2e52..7fe1f34ec5c00 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Controller/ComponentGridTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Controller/ComponentGridTest.php @@ -8,14 +8,20 @@ use Magento\Framework\Composer\ComposerInformation; use Magento\Framework\Module\PackageInfo; +use Magento\Setup\Model\UpdatePackagesCache; class ComponentGridTest extends \PHPUnit_Framework_TestCase { /** - * @var ComposerInformation + * @var ComposerInformation|\PHPUnit_Framework_MockObject_MockObject */ private $composerInformationMock; + /** + * @var UpdatePackagesCache|\PHPUnit_Framework_MockObject_MockObject + */ + private $updatePackagesCacheMock; + /** * Module package info * @@ -101,12 +107,21 @@ public function __construct() '', false ); + $this->updatePackagesCacheMock = $this->getMock( + 'Magento\Setup\Model\UpdatePackagesCache', + [], + [], + '', + false + ); + $packageInfoFactory->expects($this->once()) ->method('create') ->willReturn($this->packageInfo); $this->controller = new ComponentGrid( $this->composerInformationMock, - $objectManagerProvider + $objectManagerProvider, + $this->updatePackagesCacheMock ); } @@ -128,7 +143,7 @@ public function testComponentsAction() $this->composerInformationMock->expects($this->once()) ->method('isPackageInComposerJson') ->willReturn(true); - $this->composerInformationMock->expects($this->once()) + $this->updatePackagesCacheMock->expects($this->once()) ->method('getPackagesForUpdate') ->willReturn($this->lastSyncData); $jsonModel = $this->controller->componentsAction(); @@ -153,9 +168,9 @@ public function testComponentsAction() public function testSyncAction() { - $this->composerInformationMock->expects($this->once()) + $this->updatePackagesCacheMock->expects($this->once()) ->method('syncPackagesForUpdate'); - $this->composerInformationMock->expects($this->once()) + $this->updatePackagesCacheMock->expects($this->once()) ->method('getPackagesForUpdate') ->willReturn($this->lastSyncData); $jsonModel = $this->controller->syncAction(); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php new file mode 100644 index 0000000000000..b1184946b7f32 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php @@ -0,0 +1,121 @@ +objectManager = Bootstrap::getObjectManager(); + } + + /** + * Setup DirectoryList, Filesystem, and ComposerJsonFinder to use a specified directory for reading composer files + * + * @param $composerDir string Directory under _files that contains composer files + */ + private function setupDirectory($composerDir) + { + $directories = [ + DirectoryList::CONFIG => [DirectoryList::PATH => __DIR__ . '/_files/'], + DirectoryList::ROOT => [DirectoryList::PATH => __DIR__ . '/_files/' . $composerDir], + DirectoryList::COMPOSER_HOME => [DirectoryList::PATH => __DIR__ . '/_files/' . $composerDir], + ]; + + $this->directoryList = $this->objectManager->create( + 'Magento\Framework\App\Filesystem\DirectoryList', + ['root' => __DIR__ . '/_files/' . $composerDir, 'config' => $directories] + ); + + $this->filesystem = $this->objectManager->create( + 'Magento\Framework\Filesystem', + ['directoryList' => $this->directoryList] + ); + + /** @var \Magento\Framework\Composer\ComposerInformation $composerInfo */ + $this->composerInformation = $this->objectManager->create( + 'Magento\Framework\Composer\ComposerInformation', + [ + 'applicationFactory' => new MagentoComposerApplicationFactory( + $this->composerJsonFinder, + $this->directoryList + ) + ] + ); + + $this->composerJsonFinder = new ComposerJsonFinder($this->directoryList); + } + + public function testGetPackagesForUpdate() + { + $packageName = 'magento/language-de_de'; + + $this->setupDirectory('testSkeleton'); + + /** @var \Magento\Setup\Model\UpdatePackagesCache $updatePackagesCache */ + $updatePackagesCache = $this->objectManager->create( + 'Magento\Setup\Model\UpdatePackagesCache', + [ + 'applicationFactory' => new MagentoComposerApplicationFactory( + $this->composerJsonFinder, + $this->directoryList + ), + 'filesystem' => $this->filesystem, + 'composerInfo' => $this->composerInformation + ] + ); + + $requiredPackages = $this->composerInformation->getInstalledMagentoPackages(); + $this->assertArrayHasKey($packageName, $requiredPackages); + + $this->assertTrue($updatePackagesCache->syncPackagesForUpdate()); + + $packagesForUpdate = $updatePackagesCache->getPackagesForUpdate(); + $this->assertArrayHasKey('packages', $packagesForUpdate); + $this->assertArrayHasKey($packageName, $packagesForUpdate['packages']); + $this->assertTrue( + version_compare( + $packagesForUpdate['packages'][$packageName]['latestVersion'], + $requiredPackages[$packageName]['version'], + '>' + ) + ); + } +} diff --git a/lib/internal/Magento/Framework/Composer/ComposerInformation.php b/lib/internal/Magento/Framework/Composer/ComposerInformation.php index 39d87457b90e6..40af109990d5b 100755 --- a/lib/internal/Magento/Framework/Composer/ComposerInformation.php +++ b/lib/internal/Magento/Framework/Composer/ComposerInformation.php @@ -6,17 +6,11 @@ namespace Magento\Framework\Composer; -use Composer\Factory as ComposerFactory; use Composer\Package\Link; use Composer\Package\CompletePackageInterface; -use Composer\Package\Version\VersionParser; -use Magento\Framework\App\Filesystem\DirectoryList; -use Magento\Framework\Filesystem; -use Magento\Framework\Stdlib\DateTime; /** * Class ComposerInformation uses Composer to determine dependency information. - * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ComposerInformation { @@ -79,21 +73,6 @@ class ComposerInformation */ private $locker; - /** - * @var \Magento\Framework\Filesystem\Directory\Write - */ - private $directory; - - /** - * @var \Magento\Framework\Stdlib\DateTime - */ - private $dateTime; - - /** - * @var string - */ - private $pathToCacheFile = 'update_composer_packages.json'; - /** @var array */ private static $packageTypes = [ self::THEME_PACKAGE_TYPE, @@ -108,20 +87,14 @@ class ComposerInformation * Constructor * * @param MagentoComposerApplicationFactory $applicationFactory - * @param \Magento\Framework\Filesystem $filesystem - * @param \Magento\Framework\Stdlib\DateTime $dateTime * @throws \Exception */ public function __construct( - MagentoComposerApplicationFactory $applicationFactory, - Filesystem $filesystem, - DateTime $dateTime + MagentoComposerApplicationFactory $applicationFactory ) { $this->application = $applicationFactory->create(); $this->composer = $this->application->createComposer(); $this->locker = $this->composer->getLocker(); - $this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); - $this->dateTime = $dateTime; } /** @@ -238,53 +211,6 @@ public function getInstalledMagentoPackages() return $packages; } - /** - * Sync and cache list of available for update versions for packages - * - * @return bool - */ - public function syncPackagesForUpdate() - { - $availableVersions = []; - foreach ($this->getInstalledMagentoPackages() as $package) { - $latestProductVersion = $this->getLatestNonDevVersion($package['name']); - if ($latestProductVersion && version_compare($latestProductVersion, $package['version'], '>')) { - $packageName = $package['name']; - $availableVersions[$packageName] = [ - 'name' => $packageName, - 'latestVersion' => $latestProductVersion - ]; - } - } - return $this->savePackagesForUpdateToCache($availableVersions) ? true : false; - } - - /** - * Sync and cache list of available for update versions for packages - * - * @return bool|array - */ - public function getPackagesForUpdate() - { - $actualUpdatePackages = []; - $updatePackagesInfo = $this->loadPackagesForUpdateFromCache(); - if (!$updatePackagesInfo) { - return false; - } - $updatePackages = $updatePackagesInfo['packages']; - $availablePackages = $this->getInstalledMagentoPackages(); - foreach ($updatePackages as $package) { - $packageName = $package['name']; - if (array_key_exists($packageName, $availablePackages)) { - if (version_compare($availablePackages[$packageName]['version'], $package['latestVersion'], '<')) { - $actualUpdatePackages[$packageName] = $package; - } - } - } - $updatePackagesInfo['packages'] = $actualUpdatePackages; - return $updatePackagesInfo; - } - /** * Checks if the passed packaged is system package * @@ -299,50 +225,6 @@ public function isSystemPackage($packageName = '') return false; } - /** - * Retrieve the latest available stable version for a package - * - * @param string $package - * @return string - */ - private function getLatestNonDevVersion($package) - { - $versionParser = new VersionParser(); - foreach ($this->getPackageAvailableVersions($package) as $version) { - if ($versionParser->parseStability($version) != 'dev') { - return $version; - } - } - return ''; - } - - /** - * Retrieve all available versions for a package - * - * @param string $package - * @return array - * @throws \RuntimeException - */ - private function getPackageAvailableVersions($package) - { - $versionsPattern = '/^versions\s*\:\s(.+)$/m'; - - $commandParams = [ - self::PARAM_COMMAND => self::COMPOSER_SHOW, - self::PARAM_PACKAGE => $package, - self::PARAM_AVAILABLE => true - ]; - $result = $this->application->runComposerCommand($commandParams); - $matches = []; - preg_match($versionsPattern, $result, $matches); - if (!isset($matches[1])) { - throw new \RuntimeException( - sprintf('Couldn\'t get available versions for package %s', $commandParams[self::PARAM_PACKAGE]) - ); - } - return explode(', ', $matches[1]); - } - /** * Determines if Magento is the root package or it is included as a requirement. * @@ -355,43 +237,6 @@ private function isMagentoRoot() return preg_match('/magento\/magento2.e/', $rootPackage->getName()); } - /** - * Save composer packages available for update to cache - * - * @param array $availableVersions - * @return bool|string - */ - private function savePackagesForUpdateToCache($availableVersions) - { - $syncInfo = []; - $syncInfo['lastSyncDate'] = str_replace('-', '/', $this->dateTime->formatDate(true)); - $syncInfo['packages'] = $availableVersions; - $data = json_encode($syncInfo, JSON_UNESCAPED_SLASHES); - try { - $this->directory->writeFile($this->pathToCacheFile, $data); - } catch (\Magento\Framework\Exception\FileSystemException $e) { - return false; - } - return $data; - } - - /** - * Load composer packages available for update from cache - * - * @return bool|string - */ - private function loadPackagesForUpdateFromCache() - { - if ($this->directory->isExist($this->pathToCacheFile) && $this->directory->isReadable($this->pathToCacheFile)) { - try { - $data = $this->directory->readFile($this->pathToCacheFile); - return json_decode($data, true); - } catch (\Magento\Framework\Exception\FileSystemException $e) { - } - } - return false; - } - /** * Check if a package is inside the root composer or not * diff --git a/setup/src/Magento/Setup/Controller/ComponentGrid.php b/setup/src/Magento/Setup/Controller/ComponentGrid.php index 426a096951e39..0baf08414898a 100644 --- a/setup/src/Magento/Setup/Controller/ComponentGrid.php +++ b/setup/src/Magento/Setup/Controller/ComponentGrid.php @@ -12,6 +12,7 @@ use Zend\Mvc\Controller\AbstractActionController; use Zend\View\Model\JsonModel; use Zend\View\Model\ViewModel; +use Magento\Setup\Model\UpdatePackagesCache; /** * Controller for component grid tasks @@ -30,17 +31,24 @@ class ComponentGrid extends AbstractActionController */ private $packageInfo; + /** + * @var UpdatePackagesCache + */ + private $updatePackagesCache; + /** * @param ComposerInformation $composerInformation * @param ObjectManagerProvider $objectManagerProvider */ public function __construct( ComposerInformation $composerInformation, - ObjectManagerProvider $objectManagerProvider + ObjectManagerProvider $objectManagerProvider, + UpdatePackagesCache $updatePackagesCache ) { $this->composerInformation = $composerInformation; $this->packageInfo = $objectManagerProvider->get() ->get('Magento\Framework\Module\PackageInfoFactory')->create(); + $this->updatePackagesCache = $updatePackagesCache; } /** @@ -63,7 +71,7 @@ public function indexAction() */ public function componentsAction() { - $lastSyncData = $this->composerInformation->getPackagesForUpdate(); + $lastSyncData = $this->updatePackagesCache->getPackagesForUpdate(); $components = $this->composerInformation->getInstalledMagentoPackages(); foreach ($components as $component) { $components[$component['name']]['update'] = false; @@ -103,8 +111,8 @@ public function componentsAction() */ public function syncAction() { - $this->composerInformation->syncPackagesForUpdate(); - $lastSyncData = $this->composerInformation->getPackagesForUpdate(); + $this->updatePackagesCache->syncPackagesForUpdate(); + $lastSyncData = $this->updatePackagesCache->getPackagesForUpdate(); return new JsonModel( [ 'success' => true, diff --git a/setup/src/Magento/Setup/Model/UpdatePackagesCache.php b/setup/src/Magento/Setup/Model/UpdatePackagesCache.php new file mode 100644 index 0000000000000..2d7f68694a47f --- /dev/null +++ b/setup/src/Magento/Setup/Model/UpdatePackagesCache.php @@ -0,0 +1,203 @@ +application = $applicationFactory->create(); + $this->directory = $filesystem->getDirectoryWrite(DirectoryList::VAR_DIR); + $this->dateTime = $dateTime; + $this->composerInformation = $composerInformation; + } + + /** + * Sync and cache list of available for update versions for packages + * + * @return bool + */ + public function syncPackagesForUpdate() + { + $availableVersions = []; + foreach ($this->composerInformation->getInstalledMagentoPackages() as $package) { + $latestProductVersion = $this->getLatestNonDevVersion($package['name']); + if ($latestProductVersion && version_compare($latestProductVersion, $package['version'], '>')) { + $packageName = $package['name']; + $availableVersions[$packageName] = [ + 'name' => $packageName, + 'latestVersion' => $latestProductVersion + ]; + } + } + return $this->savePackagesForUpdateToCache($availableVersions) ? true : false; + } + + /** + * Sync and cache list of available for update versions for packages + * + * @return bool|array + */ + public function getPackagesForUpdate() + { + $actualUpdatePackages = []; + $updatePackagesInfo = $this->loadPackagesForUpdateFromCache(); + if (!$updatePackagesInfo) { + return false; + } + $updatePackages = $updatePackagesInfo['packages']; + $availablePackages = $this->composerInformation->getInstalledMagentoPackages(); + foreach ($updatePackages as $package) { + $packageName = $package['name']; + if (array_key_exists($packageName, $availablePackages)) { + if (version_compare($availablePackages[$packageName]['version'], $package['latestVersion'], '<')) { + $actualUpdatePackages[$packageName] = $package; + } + } + } + $updatePackagesInfo['packages'] = $actualUpdatePackages; + return $updatePackagesInfo; + } + + /** + * Retrieve the latest available stable version for a package + * + * @param string $package + * @return string + */ + private function getLatestNonDevVersion($package) + { + $versionParser = new VersionParser(); + foreach ($this->getPackageAvailableVersions($package) as $version) { + if ($versionParser->parseStability($version) != 'dev') { + return $version; + } + } + return ''; + } + + /** + * Retrieve all available versions for a package + * + * @param string $package + * @return array + * @throws \RuntimeException + */ + private function getPackageAvailableVersions($package) + { + $versionsPattern = '/^versions\s*\:\s(.+)$/m'; + + $commandParams = [ + self::PARAM_COMMAND => self::COMPOSER_SHOW, + self::PARAM_PACKAGE => $package, + self::PARAM_AVAILABLE => true + ]; + $result = $this->application->runComposerCommand($commandParams); + $matches = []; + preg_match($versionsPattern, $result, $matches); + if (!isset($matches[1])) { + throw new \RuntimeException( + sprintf('Couldn\'t get available versions for package %s', $commandParams[self::PARAM_PACKAGE]) + ); + } + return explode(', ', $matches[1]); + } + + /** + * Save composer packages available for update to cache + * + * @param array $availableVersions + * @return bool|string + */ + private function savePackagesForUpdateToCache($availableVersions) + { + $syncInfo = []; + $syncInfo['lastSyncDate'] = str_replace('-', '/', $this->dateTime->formatDate(true)); + $syncInfo['packages'] = $availableVersions; + $data = json_encode($syncInfo, JSON_UNESCAPED_SLASHES); + try { + $this->directory->writeFile($this->pathToCacheFile, $data); + } catch (\Magento\Framework\Exception\FileSystemException $e) { + return false; + } + return $data; + } + + /** + * Load composer packages available for update from cache + * + * @return bool|string + */ + private function loadPackagesForUpdateFromCache() + { + if ($this->directory->isExist($this->pathToCacheFile) && $this->directory->isReadable($this->pathToCacheFile)) { + try { + $data = $this->directory->readFile($this->pathToCacheFile); + return json_decode($data, true); + } catch (\Magento\Framework\Exception\FileSystemException $e) { + } + } + return false; + } +} From bf14e2375ea23efe6d2047338692ae834a49e3fb Mon Sep 17 00:00:00 2001 From: Cari Spruiell Date: Mon, 31 Aug 2015 14:14:16 -0500 Subject: [PATCH 24/31] MAGETWO-41679: Create and Stabilize Bamboo Builds - remove references to Logging and enterprise --- .../Magento/EncryptionKey/etc/logging.xml | 20 ------------------- app/code/Magento/User/Setup/UpgradeSchema.php | 2 +- app/code/Magento/User/etc/logging.xml | 20 ------------------- 3 files changed, 1 insertion(+), 41 deletions(-) delete mode 100644 app/code/Magento/EncryptionKey/etc/logging.xml delete mode 100644 app/code/Magento/User/etc/logging.xml diff --git a/app/code/Magento/EncryptionKey/etc/logging.xml b/app/code/Magento/EncryptionKey/etc/logging.xml deleted file mode 100644 index 9dec7bed63e38..0000000000000 --- a/app/code/Magento/EncryptionKey/etc/logging.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/app/code/Magento/User/Setup/UpgradeSchema.php b/app/code/Magento/User/Setup/UpgradeSchema.php index 8700458bb7206..27aec4b0283ea 100644 --- a/app/code/Magento/User/Setup/UpgradeSchema.php +++ b/app/code/Magento/User/Setup/UpgradeSchema.php @@ -97,7 +97,7 @@ public function upgrade(SchemaSetupInterface $setup, ModuleContextInterface $con 'user_id', \Magento\Framework\DB\Ddl\Table::ACTION_CASCADE )->setComment( - 'Enterprise Admin Passwords' + 'Admin Passwords' ); $installer->getConnection()->createTable($table); diff --git a/app/code/Magento/User/etc/logging.xml b/app/code/Magento/User/etc/logging.xml deleted file mode 100644 index 9cdb19e72f458..0000000000000 --- a/app/code/Magento/User/etc/logging.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - From e88a8b573765c2c6c343b6a97da2123117c23ac9 Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Thu, 27 Aug 2015 14:02:28 -0500 Subject: [PATCH 25/31] MAGETWO-41858: Added functional tests for integration module --- .../Backend/Test/Block/Widget/Grid.php | 44 +++++- .../Integration/Edit/IntegrationForm.php | 14 +- .../Edit/IntegrationFormPageActions.php | 35 ++++- .../AssertEmailValidationErrorGenerated.php | 52 +++++++ .../AssertIntegrationErrorMessage.php | 51 +++++++ .../Test/Constraint/AssertNoAlertPopup.php | 45 ++++++ .../AssertNoIntegrationSuccessSaveMessage.php | 51 +++++++ .../Integration/Test/Fixture/Integration.xml | 2 +- .../TestCase/CreateIntegrationEntityTest.php | 2 +- .../TestCase/CreateIntegrationEntityTest.xml | 133 +++++++++++++++++- ...reateIntegrationWithDuplicatedNameTest.php | 83 +++++++++++ ...reateIntegrationWithDuplicatedNameTest.xml | 16 +++ 12 files changed, 517 insertions(+), 11 deletions(-) create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertEmailValidationErrorGenerated.php create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoAlertPopup.php create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php create mode 100644 dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php index 41da1ae272f38..2626242253560 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php @@ -137,14 +137,14 @@ abstract class Grid extends Block * * @var string */ - protected $rowTemplate = 'td[contains(.,normalize-space("%s"))]'; + protected $rowTemplate = 'td[contains(.,normalize-space(%s))]'; /** * Secondary part of row locator template for getRow() method with strict option * * @var string */ - protected $rowTemplateStrict = 'td[text()[normalize-space()="%s"]]'; + protected $rowTemplateStrict = 'td[text()[normalize-space()=%s]]'; /** * Magento grid loader @@ -356,7 +356,7 @@ protected function getRow(array $filter, $isStrict = true) $rowTemplate = ($isStrict) ? $this->rowTemplateStrict : $this->rowTemplate; $rows = []; foreach ($filter as $value) { - $rows[] = sprintf($rowTemplate, $value); + $rows[] = sprintf($rowTemplate, $this->xpathEscape($value)); } $location = sprintf($this->rowPattern, implode(' and ', $rows)); return $this->_rootElement->find($location, Locator::SELECTOR_XPATH); @@ -453,4 +453,42 @@ public function openFirstRow() { $this->_rootElement->find($this->firstRowSelector, Locator::SELECTOR_XPATH)->click(); } + + /** + * Escape single and/or double quotes in XPath selector by concat() + * + * @param string $query + * @param string $delim [optional] + * @return string + */ + protected function xpathEscape($query, $delim = '"') + { + if ((strpos($query, '\'') !== false) || + (strpos($query, '"') !== false)) { + $quoteChars[] = '\''; + $quoteChars[] = '"'; + $parts = []; + $currentPart = ''; + foreach (str_split($query) as $character) { + if (in_array($character, $quoteChars)) { + $parts[] = '\''.$currentPart.'\''; + if ($character == '\'') { + $parts[] = '"'.$character.'"'; + } else { + $parts[] = '\''.$character.'\''; + } + $currentPart = ''; + } else { + $currentPart .= $character; + } + } + if ($currentPart) { + $parts[] = '\''.$currentPart.'\''; + } + $ret = 'concat('.implode(',', $parts).')'; + } else { + $ret = $delim.$query.$delim; + } + return $ret; + } } diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php index 875cf0fefabef..7b406553826ae 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php @@ -7,11 +7,23 @@ namespace Magento\Integration\Test\Block\Adminhtml\Integration\Edit; use Magento\Backend\Test\Block\Widget\FormTabs; +use Magento\Backend\Block\Widget\Tab; /** * Integration form block. */ class IntegrationForm extends FormTabs { - // + /** + * Get array of label => js error text. + * + * @param string $tabName + * @return array + */ + public function getJsErrors($tabName) + { + $tab = $this->getTab($tabName); + $this->openTab($tabName); + return $tab->getJsErrors(); + } } diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationFormPageActions.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationFormPageActions.php index 6afacd6b0afab..48b34514e43d0 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationFormPageActions.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationFormPageActions.php @@ -15,14 +15,14 @@ class IntegrationFormPageActions extends FormPageActions { /** - * Save button + * Save button. * * @var string */ protected $saveNewButton = '[data-ui-id="integration-edit-content-save-split-button-button"]'; /** - * Click on "Save" with split button + * Click on "Save" with split button. * * @return void */ @@ -30,4 +30,35 @@ public function saveNew() { $this->_rootElement->find($this->saveNewButton)->click(); } + + /** + * Check if alert is present. + * + * @return bool + */ + public function isAlertPresent() + { + try { + $this->browser->getAlertText(); + return true; + } catch (\Exception $e) { + return false; + } + } + + /** + * Accept alert. + * + * @return void + */ + public function acceptAlert() + { + try { + while (true) { + $this->browser->acceptAlert(); + } + } catch (\Exception $e) { + return; + } + } } diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertEmailValidationErrorGenerated.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertEmailValidationErrorGenerated.php new file mode 100644 index 0000000000000..e23e2a88a4e8e --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertEmailValidationErrorGenerated.php @@ -0,0 +1,52 @@ +getIntegrationForm()->getJsErrors("integration_info"); + $emailJsError = false; + foreach ($errors as $error) { + if (strpos($error, 'Please enter a valid email address') !== false) { + $emailJsError = true; + break; + } + } + \PHPUnit_Framework_Assert::assertTrue( + $emailJsError, + 'Failed to validate email address (' . $integration->getEmail() . ') when saving integration.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Email address is properly validated when saving integration.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php new file mode 100644 index 0000000000000..8635ed94349ad --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php @@ -0,0 +1,51 @@ +getName()); + $actualMessage = $integrationIndexPage->getMessagesBlock()->getErrorMessages(); + \PHPUnit_Framework_Assert::assertEquals( + $expectedMessage, + $actualMessage, + 'Wrong error message is displayed.' + . "\nExpected: " . $expectedMessage + . "\nActual: " . $actualMessage + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Integration error message is correct.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoAlertPopup.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoAlertPopup.php new file mode 100644 index 0000000000000..9dddeed18e9e1 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoAlertPopup.php @@ -0,0 +1,45 @@ +getFormPageActions()->isAlertPresent(); + if ($isAlertPresent) { + $integrationNew->getFormPageActions()->acceptAlert(); + } + \PHPUnit_Framework_Assert::assertFalse( + $isAlertPresent, + 'Saving an integration should not cause alert.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Integration is saved with no alert.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php new file mode 100644 index 0000000000000..8be30d0d35964 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php @@ -0,0 +1,51 @@ +getMessagesBlock()->isVisible()) { + try { + $integrationIndex->getMessagesBlock()->getSuccessMessages(); + } catch (\PHPUnit_Extensions_Selenium2TestCase_WebDriverException $e) { + $noSuccessMessage = true; + } + } else { + $noSuccessMessage = true; + } + \PHPUnit_Framework_Assert::assertTrue( + $noSuccessMessage, + 'Integration is not saved.' + ); + } + + /** + * Returns a string representation of successful assertion. + * + * @return string + */ + public function toString() + { + return 'Integration is not saved.'; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Fixture/Integration.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/Fixture/Integration.xml index f2ed1bfc1e24d..2712dcbae41d7 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Fixture/Integration.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Fixture/Integration.xml @@ -14,7 +14,7 @@ repository_class="Magento\Integration\Test\Repository\Integration" handler_interface="Magento\Integration\Test\Handler\Integration\IntegrationInterface" class="Magento\Integration\Test\Fixture\Integration"> - + diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.php b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.php index b57e65919f29f..ed72ad49b1013 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.php @@ -21,7 +21,7 @@ * 6. Perform all assertions. * * @group Web_API_Framework_(PS) - * @ZephyrId MAGETWO-26009 + * @ZephyrId MAGETWO-26009, MAGETWO-16755, MAGETWO-16819, MAGETWO-16820 */ class CreateIntegrationEntityTest extends Injectable { diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml index 1ac9ad966192f..8bf7715b377d3 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml @@ -7,7 +7,7 @@ --> - + Integration%isolation% test@example.com https://endpoint.com @@ -17,7 +17,7 @@ - + Integration%isolation% Custom Sales/Operations/Invoices @@ -26,12 +26,139 @@ - + Integration%isolation% All + + <script>alert('XSS-%isolation%')</script> + - + <script>alert('XSS')</script> + <script>alert('XSS')</script> + All + + + + + + + <IMG SRC=javascript:alert('XSS-%isolation%')> + - + <IMG SRC=javascript:alert('XSS')> + <IMG SRC=javascript:alert('XSS')> + All + + + + + + + name-%isolation%' OR 'a'='a + - + endpoint' OR 'a'='a + link' OR 'a'='a + All + + + + + + + name-%isolation%" OR "a"="a + - + endpoint" OR "a"="a + link" OR "a"="a + All + + + + + + + name-%isolation%" OR 'a"='a + - + endpoint" OR 'a"='a + link" OR 'a"='a + All + + + + + + + Integration%isolation% + abc.example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + abc.@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + abc..123@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + a@b@c@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + a"b(c)d,e:f;g<h>i[j\k]l@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + this\ still\"not\\allowed@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + just"not"right@example.com + https://endpoint.com + https://testlink.com + All + + + + + Integration%isolation% + this is"not\allowed@example.com + https://endpoint.com + https://testlink.com + All + + + diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php new file mode 100644 index 0000000000000..8c296cb973475 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php @@ -0,0 +1,83 @@ + Extensions > Integrations + * 3. Click 'Add New Integration' + * 4. Fill in all required data + * 5. Click "Save" button to save Integration1 + * 6. Click 'Add New Integration' + * 7. Fill in all required data and use the same name as for Integration1 + * 8. Click "Save" button + * 9. Perform all assertions + * + * @group Web_API_Framework_(PS) + * @ZephyrId MAGETWO-16756 + */ +class CreateIntegrationWithDuplicatedNameTest extends Injectable +{ + /* tags */ + const MVP = 'yes'; + const DOMAIN = 'PS'; + /* end tags */ + + /** + * Integration grid page. + * + * @var IntegrationIndex + */ + protected $integrationIndexPage; + + /** + * Integration new page. + * + * @var IntegrationNew + */ + protected $integrationNewPage; + + /** + * Injection data. + * + * @param IntegrationIndex $integrationIndex + * @param IntegrationNew $integrationNew + * @return void + */ + public function __inject( + IntegrationIndex $integrationIndex, + IntegrationNew $integrationNew + ) { + $this->integrationIndexPage = $integrationIndex; + $this->integrationNewPage = $integrationNew; + } + + /** + * Create Integration Entity with existing name test. + * + * @param Integration $integration + * @return Integration + */ + public function test(Integration $integration) + { + // Precondition + $integration->persist(); + + // Steps + $this->integrationIndexPage->open(); + $this->integrationIndexPage->getGridPageActions()->addNew(); + $this->integrationNewPage->getIntegrationForm()->fill($integration); + $this->integrationNewPage->getFormPageActions()->saveNew(); + return ['integration' => $integration]; + } +} diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml new file mode 100644 index 0000000000000..43b4aa4b0a782 --- /dev/null +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml @@ -0,0 +1,16 @@ + + + + + + default + Integration with name '%s' exists. + + + + From 376a579b5c2c89b0ff6001a55c26c4839b93e8d0 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Tue, 1 Sep 2015 15:42:58 +0300 Subject: [PATCH 26/31] MAGETWO-41679: Create and Stabilize Bamboo Builds --- composer.lock | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/composer.lock b/composer.lock index d2fd58a42d594..8471102b1fbba 100644 --- a/composer.lock +++ b/composer.lock @@ -1,10 +1,10 @@ { "_readme": [ "This file locks the dependencies of your project to a known state", - "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "b06fa6ec127580416633b88e4ea6a6d0", + "hash": "8f9d5aa4519b2cc672be39e318a14719", "packages": [ { "name": "braintree/braintree_php", @@ -94,7 +94,7 @@ "Composer": "src/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -223,7 +223,7 @@ "MagentoHackathon\\Composer\\Magento": "src/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "OSL-3.0" ], @@ -293,7 +293,7 @@ "Zend_": "library/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "include-path": [ "library/" ], @@ -409,7 +409,7 @@ "lessc.inc.php" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "Apache-2.0" ], @@ -613,7 +613,7 @@ "Psr\\Log\\": "" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -657,7 +657,7 @@ "Seld\\JsonLint\\": "src/Seld/JsonLint/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -902,7 +902,7 @@ "cssmin.php" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -2552,7 +2552,7 @@ "League\\CLImate\\": "src/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2613,7 +2613,7 @@ "OAuth\\Unit": "tests" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2668,7 +2668,7 @@ "PDepend\\": "src/main/php/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -2709,7 +2709,7 @@ "PHPMD\\": "src/main/php" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -2822,7 +2822,7 @@ "File/" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "include-path": [ "" ], @@ -3025,7 +3025,7 @@ "src/" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "include-path": [ "", "../../symfony/yaml/" @@ -3200,7 +3200,7 @@ "src/" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -3464,7 +3464,7 @@ "StaticReview\\": "src/" } }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3535,7 +3535,7 @@ "CodeSniffer/Standards/Zend/Sniffs/" ] }, - "notification-url": "http://packagist.org/downloads/", + "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], From 282b5cb86f70ad1a6c630e41378187cbb437ebe6 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Wed, 2 Sep 2015 12:46:19 +0300 Subject: [PATCH 27/31] MAGETWO-41575: Refactor ComposerInformation class to reduce coupling between objects - fix tests --- .../testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php | 4 ++-- setup/src/Magento/Setup/Controller/ComponentGrid.php | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php index b1184946b7f32..dc061b78b44c1 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php @@ -69,6 +69,8 @@ private function setupDirectory($composerDir) ['directoryList' => $this->directoryList] ); + $this->composerJsonFinder = new ComposerJsonFinder($this->directoryList); + /** @var \Magento\Framework\Composer\ComposerInformation $composerInfo */ $this->composerInformation = $this->objectManager->create( 'Magento\Framework\Composer\ComposerInformation', @@ -79,8 +81,6 @@ private function setupDirectory($composerDir) ) ] ); - - $this->composerJsonFinder = new ComposerJsonFinder($this->directoryList); } public function testGetPackagesForUpdate() diff --git a/setup/src/Magento/Setup/Controller/ComponentGrid.php b/setup/src/Magento/Setup/Controller/ComponentGrid.php index 0baf08414898a..e94f06c941833 100644 --- a/setup/src/Magento/Setup/Controller/ComponentGrid.php +++ b/setup/src/Magento/Setup/Controller/ComponentGrid.php @@ -39,6 +39,7 @@ class ComponentGrid extends AbstractActionController /** * @param ComposerInformation $composerInformation * @param ObjectManagerProvider $objectManagerProvider + * @param UpdatePackagesCache $updatePackagesCache */ public function __construct( ComposerInformation $composerInformation, From 50c480d47bdff5238598bf7b5dace74bc7c89629 Mon Sep 17 00:00:00 2001 From: Hayder Sharhan Date: Tue, 1 Sep 2015 00:49:22 +0300 Subject: [PATCH 28/31] MAGETWO-40229: Custom Email Template can't be used on Store View level only - added the Send From form in the customer admin web front so the merchant is able to choose where the customer is created. --- .../Customer/Controller/Adminhtml/Index/Save.php | 2 ++ .../Test/Unit/Controller/Adminhtml/Index/SaveTest.php | 1 + .../Customer/view/base/ui_component/customer_form.xml | 10 ++++++++++ 3 files changed, 13 insertions(+) diff --git a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php index 8cc817d34b0de..2cbe283339dfe 100644 --- a/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php +++ b/app/code/Magento/Customer/Controller/Adminhtml/Index/Save.php @@ -32,6 +32,7 @@ protected function _extractCustomerData() CustomerInterface::DEFAULT_BILLING, CustomerInterface::DEFAULT_SHIPPING, 'confirmation', + 'sendemail_store_id', ]; $customerData = $this->_extractData( @@ -226,6 +227,7 @@ public function execute() ['customer' => $customer, 'request' => $request] ); $customer->setAddresses($addresses); + $customer->setStoreId($customerData['sendemail_store_id']); // Save customer if ($isExistingCustomer) { diff --git a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php index 372b54adc304e..95c875393ed6a 100644 --- a/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php +++ b/app/code/Magento/Customer/Test/Unit/Controller/Adminhtml/Index/SaveTest.php @@ -299,6 +299,7 @@ public function testExecuteWithExistentCustomer() \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_BILLING => $addressId, \Magento\Customer\Api\Data\CustomerInterface::DEFAULT_SHIPPING => $addressId, 'confirmation' => false, + 'sendemail_store_id' => '1', 'id' => $customerId, ]; $mergedAddressData = [ diff --git a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml index a32b3dee3716b..4a4305c82fd4d 100644 --- a/app/code/Magento/Customer/view/base/ui_component/customer_form.xml +++ b/app/code/Magento/Customer/view/base/ui_component/customer_form.xml @@ -245,6 +245,16 @@ + + + Magento\Store\Model\System\Store + + Send Welcome Email From + number + select + + +
From fa92df89c282615277557615066d922e3bdd858c Mon Sep 17 00:00:00 2001 From: Ji Lu Date: Tue, 1 Sep 2015 22:19:24 +0300 Subject: [PATCH 29/31] MAGETWO-41858: Added functional tests for integration module --- .../Backend/Test/Block/Widget/Grid.php | 54 ++++++++----------- .../Integration/Edit/IntegrationForm.php | 1 - ...ntegrationNameDuplicationErrorMessage.php} | 12 ++--- ...tegrationSuccessSaveMessageNotPresent.php} | 4 +- .../TestCase/CreateIntegrationEntityTest.xml | 16 +++--- ...reateIntegrationWithDuplicatedNameTest.php | 2 +- ...reateIntegrationWithDuplicatedNameTest.xml | 3 +- 7 files changed, 41 insertions(+), 51 deletions(-) rename dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/{AssertIntegrationErrorMessage.php => AssertIntegrationNameDuplicationErrorMessage.php} (76%) rename dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/{AssertNoIntegrationSuccessSaveMessage.php => AssertIntegrationSuccessSaveMessageNotPresent.php} (89%) diff --git a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php index 2626242253560..8e1e7be037137 100644 --- a/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php +++ b/dev/tests/functional/tests/app/Magento/Backend/Test/Block/Widget/Grid.php @@ -137,14 +137,14 @@ abstract class Grid extends Block * * @var string */ - protected $rowTemplate = 'td[contains(.,normalize-space(%s))]'; + protected $rowTemplate = 'td[contains(.,normalize-space("%s"))]'; /** * Secondary part of row locator template for getRow() method with strict option * * @var string */ - protected $rowTemplateStrict = 'td[text()[normalize-space()=%s]]'; + protected $rowTemplateStrict = 'td[text()[normalize-space()="%s"]]'; /** * Magento grid loader @@ -356,7 +356,11 @@ protected function getRow(array $filter, $isStrict = true) $rowTemplate = ($isStrict) ? $this->rowTemplateStrict : $this->rowTemplate; $rows = []; foreach ($filter as $value) { - $rows[] = sprintf($rowTemplate, $this->xpathEscape($value)); + if (strpos($value, '"') !== false) { + $rowTemplate = str_replace('"', '', $rowTemplate); + $value = $this->xpathEscape($value); + } + $rows[] = sprintf($rowTemplate, $value); } $location = sprintf($this->rowPattern, implode(' and ', $rows)); return $this->_rootElement->find($location, Locator::SELECTOR_XPATH); @@ -458,37 +462,25 @@ public function openFirstRow() * Escape single and/or double quotes in XPath selector by concat() * * @param string $query - * @param string $delim [optional] + * @param string $defaultDelim [optional] * @return string */ - protected function xpathEscape($query, $delim = '"') + protected function xpathEscape($query, $defaultDelim = '"') { - if ((strpos($query, '\'') !== false) || - (strpos($query, '"') !== false)) { - $quoteChars[] = '\''; - $quoteChars[] = '"'; - $parts = []; - $currentPart = ''; - foreach (str_split($query) as $character) { - if (in_array($character, $quoteChars)) { - $parts[] = '\''.$currentPart.'\''; - if ($character == '\'') { - $parts[] = '"'.$character.'"'; - } else { - $parts[] = '\''.$character.'\''; - } - $currentPart = ''; - } else { - $currentPart .= $character; - } - } - if ($currentPart) { - $parts[] = '\''.$currentPart.'\''; - } - $ret = 'concat('.implode(',', $parts).')'; - } else { - $ret = $delim.$query.$delim; + if (strpos($query, $defaultDelim) === false) { + return $defaultDelim . $query . $defaultDelim; + } + preg_match_all("#(?:('+)|[^']+)#", $query, $matches); + list($parts, $apos) = $matches; + $delim = ''; + foreach ($parts as $i => &$part) { + $delim = $apos[$i] ? '"' : "'"; + $part = $delim . $part . $delim; } - return $ret; + if (count($parts) == 1) { + $parts[] = $delim . $delim; + } + + return 'concat(' . implode(',', $parts) . ')'; } } diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php index 7b406553826ae..a73cfc3bd98fa 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Block/Adminhtml/Integration/Edit/IntegrationForm.php @@ -7,7 +7,6 @@ namespace Magento\Integration\Test\Block\Adminhtml\Integration\Edit; use Magento\Backend\Test\Block\Widget\FormTabs; -use Magento\Backend\Block\Widget\Tab; /** * Integration form block. diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationNameDuplicationErrorMessage.php similarity index 76% rename from dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php rename to dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationNameDuplicationErrorMessage.php index 8635ed94349ad..99613bf462381 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationErrorMessage.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationNameDuplicationErrorMessage.php @@ -13,22 +13,22 @@ /** * Assert error message is displayed in message block. */ -class AssertIntegrationErrorMessage extends AbstractConstraint +class AssertIntegrationNameDuplicationErrorMessage extends AbstractConstraint { + const ERROR_DUPLICATE_INTEGRATION_NAME = "Integration with name '%s' exists."; + /** * Assert error message is displayed in message block. * * @param IntegrationIndex $integrationIndexPage * @param Integration $integration - * @param string $errorMessage * @return void */ public function processAssert( IntegrationIndex $integrationIndexPage, - Integration $integration, - $errorMessage + Integration $integration ) { - $expectedMessage = sprintf($errorMessage, $integration->getName()); + $expectedMessage = sprintf(self::ERROR_DUPLICATE_INTEGRATION_NAME, $integration->getName()); $actualMessage = $integrationIndexPage->getMessagesBlock()->getErrorMessages(); \PHPUnit_Framework_Assert::assertEquals( $expectedMessage, @@ -46,6 +46,6 @@ public function processAssert( */ public function toString() { - return 'Integration error message is correct.'; + return 'Duplicated integration name error message is correct.'; } } diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationSuccessSaveMessageNotPresent.php similarity index 89% rename from dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php rename to dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationSuccessSaveMessageNotPresent.php index 8be30d0d35964..ac23f60261388 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertNoIntegrationSuccessSaveMessage.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/Constraint/AssertIntegrationSuccessSaveMessageNotPresent.php @@ -10,9 +10,9 @@ use Magento\Mtf\Constraint\AbstractConstraint; /** - * Assert that there is no integration's been saved message + * Assert that there is no integration's been saved message. */ -class AssertNoIntegrationSuccessSaveMessage extends AbstractConstraint +class AssertIntegrationSuccessSaveMessageNotPresent extends AbstractConstraint { /** * Assert that there is no integration's been saved message. diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml index 8bf7715b377d3..a908c02ec1d8e 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationEntityTest.xml @@ -95,7 +95,7 @@ https://testlink.com All - + Integration%isolation% @@ -104,7 +104,7 @@ https://testlink.com All - + Integration%isolation% @@ -113,7 +113,7 @@ https://testlink.com All - + Integration%isolation% @@ -122,7 +122,7 @@ https://testlink.com All - + Integration%isolation% @@ -131,7 +131,7 @@ https://testlink.com All - + Integration%isolation% @@ -140,7 +140,7 @@ https://testlink.com All - + Integration%isolation% @@ -149,7 +149,7 @@ https://testlink.com All - + Integration%isolation% @@ -158,7 +158,7 @@ https://testlink.com All - + diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php index 8c296cb973475..e2f1411b45af1 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.php @@ -66,7 +66,7 @@ public function __inject( * Create Integration Entity with existing name test. * * @param Integration $integration - * @return Integration + * @return array */ public function test(Integration $integration) { diff --git a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml index 43b4aa4b0a782..e7f54bb994ca2 100644 --- a/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml +++ b/dev/tests/functional/tests/app/Magento/Integration/Test/TestCase/CreateIntegrationWithDuplicatedNameTest.xml @@ -9,8 +9,7 @@ default - Integration with name '%s' exists. - + From f2b73bf570d9baff9f10ee1b1bc65cc23c44e255 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Fri, 4 Sep 2015 17:25:32 +0300 Subject: [PATCH 30/31] MAGETWO-41679: Create and Stabilize Bamboo Builds --- composer.lock | 2 +- .../Setup/Model/UpdatePackagesCacheTest.php | 34 +- .../Model/_files/testSkeleton/composer.json | 24 + .../Model/_files/testSkeleton/composer.lock | 6174 +++++++++++++++++ 4 files changed, 6213 insertions(+), 21 deletions(-) create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.json create mode 100644 dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock diff --git a/composer.lock b/composer.lock index 16141689d88af..878cafac4f2e4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "20c37ec30731e0b8d0cd52a33c9dec89", + "hash": "3c94391144a4c2de663dfa465b3a2e8a", "packages": [ { "name": "braintree/braintree_php", diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php index dc061b78b44c1..2e94d3898f3d5 100644 --- a/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/UpdatePackagesCacheTest.php @@ -10,6 +10,7 @@ use Magento\TestFramework\Helper\Bootstrap; use Magento\Framework\Composer\ComposerJsonFinder; use Magento\Framework\Composer\MagentoComposerApplicationFactory; +use Magento\Framework\Stdlib\DateTime; /** * Tests Magento\Framework\ComposerInformation @@ -22,12 +23,12 @@ class UpdatePackagesCacheTest extends \PHPUnit_Framework_TestCase private $objectManager; /** - * @var \PHPUnit_Framework_MockObject_MockObject|\Magento\Framework\App\Filesystem\DirectoryList + * @var \Magento\Framework\App\Filesystem\DirectoryList */ private $directoryList; /** - * @var ComposerJsonFinder + * @var ComposerJsonFinder|\PHPUnit_Framework_MockObject_MockObject */ private $composerJsonFinder; @@ -53,23 +54,15 @@ public function setUp() */ private function setupDirectory($composerDir) { - $directories = [ - DirectoryList::CONFIG => [DirectoryList::PATH => __DIR__ . '/_files/'], - DirectoryList::ROOT => [DirectoryList::PATH => __DIR__ . '/_files/' . $composerDir], - DirectoryList::COMPOSER_HOME => [DirectoryList::PATH => __DIR__ . '/_files/' . $composerDir], - ]; - - $this->directoryList = $this->objectManager->create( - 'Magento\Framework\App\Filesystem\DirectoryList', - ['root' => __DIR__ . '/_files/' . $composerDir, 'config' => $directories] - ); - - $this->filesystem = $this->objectManager->create( - 'Magento\Framework\Filesystem', - ['directoryList' => $this->directoryList] - ); + $absoluteComposerDir = realpath(__DIR__ . '/_files/' . $composerDir . '/composer.json'); + $this->composerJsonFinder = $this->getMockBuilder('Magento\Framework\Composer\ComposerJsonFinder') + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + $this->composerJsonFinder->expects($this->any())->method('findComposerJson')->willReturn($absoluteComposerDir); - $this->composerJsonFinder = new ComposerJsonFinder($this->directoryList); + $this->directoryList = $this->objectManager->get('Magento\Framework\App\Filesystem\DirectoryList'); + $this->filesystem = $this->objectManager->get('Magento\Framework\Filesystem'); /** @var \Magento\Framework\Composer\ComposerInformation $composerInfo */ $this->composerInformation = $this->objectManager->create( @@ -85,7 +78,7 @@ private function setupDirectory($composerDir) public function testGetPackagesForUpdate() { - $packageName = 'magento/language-de_de'; + $packageName = 'magento/module-store'; $this->setupDirectory('testSkeleton'); @@ -98,7 +91,8 @@ public function testGetPackagesForUpdate() $this->directoryList ), 'filesystem' => $this->filesystem, - 'composerInfo' => $this->composerInformation + 'dateTime' => new DateTime(), + 'composerInformation' => $this->composerInformation ] ); diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.json b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.json new file mode 100644 index 0000000000000..945bccecd52ed --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.json @@ -0,0 +1,24 @@ +{ + "version": "0.74.0-beta9", + "require": { + "magento/product-community-edition": "0.74.0-beta2", + "magento/sample-module-minimal" : "*" + }, + "repositories": [ + { + "type": "composer", + "url": "http://packages.magento.com/" + } + ], + "autoload": { + "psr-4": { + "Magento\\Framework\\": "htdocs/lib/internal/Magento/Framework/", + "Magento\\Setup\\": "htdocs/setup/src/Magento/Setup/" + } + }, + "extra": { + "magento-root-dir": "htdocs", + "magento-deploystrategy": "copy" + }, + "minimum-stability": "dev" +} diff --git a/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock new file mode 100644 index 0000000000000..0a8ab77a1d894 --- /dev/null +++ b/dev/tests/integration/testsuite/Magento/Setup/Model/_files/testSkeleton/composer.lock @@ -0,0 +1,6174 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "8601c8b464025318475d28dbabf9ee67", + "packages": [ + { + "name": "composer/composer", + "version": "1.0.0-alpha8", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "1eb1df44a97fb2daca1bb8b007f3bee012f0aa46" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/1eb1df44a97fb2daca1bb8b007f3bee012f0aa46", + "reference": "1eb1df44a97fb2daca1bb8b007f3bee012f0aa46", + "shasum": "" + }, + "require": { + "justinrainbow/json-schema": "1.1.*", + "php": ">=5.3.2", + "seld/jsonlint": "1.*", + "symfony/console": "~2.3", + "symfony/finder": "~2.2", + "symfony/process": "~2.1" + }, + "require-dev": { + "phpunit/phpunit": "~3.7.10" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives, and allows gzip compression of all internet traffic" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "psr-0": { + "Composer": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be", + "role": "Developer" + }, + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de", + "role": "Developer" + } + ], + "description": "Dependency Manager", + "homepage": "http://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "time": "2014-01-06 18:39:59" + }, + { + "name": "justinrainbow/json-schema", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "05ff6d8d79fe3ad190b0663d80d3f9deee79416c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/05ff6d8d79fe3ad190b0663d80d3f9deee79416c", + "reference": "05ff6d8d79fe3ad190b0663d80d3f9deee79416c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonSchema": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "NewBSD" + ], + "authors": [ + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch", + "homepage": "http://wiedler.ch/igor/" + }, + { + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Robert Schönthal", + "email": "robert.schoenthal@gmail.com", + "homepage": "http://digitalkaoz.net" + } + ], + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", + "keywords": [ + "json", + "schema" + ], + "time": "2012-01-03 00:33:17" + }, + { + "name": "magento/framework", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_framework-0.74.0-beta2.zip", + "reference": null, + "shasum": "ebb865d9aae1b48e41706a7d2fe080b7735a1a0c" + }, + "require": { + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-hash": "*", + "ext-iconv": "*", + "ext-mcrypt": "*", + "ext-simplexml": "*", + "ext-spl": "*", + "lib-libxml": "*", + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "ext-imagick": "Use Image Magick >=3.0.0 as an optional alternative image processing library" + }, + "type": "magento2-library", + "extra": { + "map": [ + [ + "*", + "Magento/Framework" + ] + ] + }, + "autoload": { + "psr-4": { + "Magento\\Framework\\": "" + } + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/language-de_de", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-de_de-0.74.0-beta2.zip", + "reference": null, + "shasum": "574416a036e55513a6437b287b77426c291d9051" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/de_de" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "German (Germany) language" + }, + { + "name": "magento/language-en_us", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-en_us-0.74.0-beta2.zip", + "reference": null, + "shasum": "6246a6a773d86c078bb4f0bc5cee50e2eeab639f" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/en_us" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "English (United States) language" + }, + { + "name": "magento/language-es_es", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-es_es-0.74.0-beta2.zip", + "reference": null, + "shasum": "da8197a083474993b935bd16d8b001160475b97e" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/es_es" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Spanish (Spain) language" + }, + { + "name": "magento/language-fr_fr", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-fr_fr-0.74.0-beta2.zip", + "reference": null, + "shasum": "e3d58180b96a709e0b2fb8dccaee95b3be5dadd4" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/fr_fr" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "French (France) language" + }, + { + "name": "magento/language-nl_nl", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-nl_nl-0.74.0-beta2.zip", + "reference": null, + "shasum": "d7b29b6275aefa26e7a6d9fb4bcdb62a8ee34332" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/nl_nl" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Dutch (Netherlands) language" + }, + { + "name": "magento/language-pt_br", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-pt_br-0.74.0-beta2.zip", + "reference": null, + "shasum": "7b6bc50f18adc85d524cc549accd05752beb8858" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/pt_br" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Portuguese (Brazil) language" + }, + { + "name": "magento/language-zh_cn", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_language-zh_cn-0.74.0-beta2.zip", + "reference": null, + "shasum": "f9a151b4d1bbc6620289e9e91fb8bee023d14c5f" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*" + }, + "type": "magento2-language", + "extra": { + "map": [ + [ + "*", + "magento/zh_cn" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Chinese (China) language" + }, + { + "name": "magento/magento-composer-installer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/magento/magento-composer-installer.git", + "reference": "7f03451f71e55d52c2bb07325d56a4e6df322f30" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/magento-composer-installer/zipball/7f03451f71e55d52c2bb07325d56a4e6df322f30", + "reference": "7f03451f71e55d52c2bb07325d56a4e6df322f30", + "shasum": "" + }, + "require": { + "composer-plugin-api": "1.0.0" + }, + "require-dev": { + "composer/composer": "*@dev", + "firegento/phpcs": "dev-patch-1", + "mikey179/vfsstream": "*", + "phpunit/phpunit": "*", + "phpunit/phpunit-mock-objects": "dev-master", + "squizlabs/php_codesniffer": "1.4.7", + "symfony/process": "*" + }, + "type": "composer-plugin", + "extra": { + "composer-command-registry": [ + "MagentoHackathon\\Composer\\Magento\\Command\\DeployCommand" + ], + "class": "MagentoHackathon\\Composer\\Magento\\Plugin" + }, + "autoload": { + "psr-0": { + "MagentoHackathon\\Composer\\Magento": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0" + ], + "authors": [ + { + "name": "Vinai Kopp", + "email": "vinai@netzarbeiter.com" + }, + { + "name": "Daniel Fahlke aka Flyingmana", + "email": "flyingmana@googlemail.com" + }, + { + "name": "Jörg Weller", + "email": "weller@flagbit.de" + }, + { + "name": "Karl Spies", + "email": "karl.spies@gmx.net" + }, + { + "name": "Tobias Vogt", + "email": "tobi@webguys.de" + }, + { + "name": "David Fuhr", + "email": "fuhr@flagbit.de" + } + ], + "description": "Composer installer for Magento modules", + "homepage": "https://github.com/magento/magento-composer-installer", + "keywords": [ + "composer-installer", + "magento" + ], + "time": "2015-03-05 21:40:30" + }, + { + "name": "magento/magento2-base", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_magento2-base-0.74.0-beta2.zip", + "reference": null, + "shasum": "be10c634940670ff0cecf5f952f57891c626c652" + }, + "require": { + "magento/magento-composer-installer": "*" + }, + "replace": { + "blueimp/jquery-file-upload": "5.6.14", + "colinmollenhour/cache-backend-redis": "dev-master#193d377b7fb2e88595578b282fa01a62d1185abc", + "colinmollenhour/credis": "dev-master#f07bbfd4117294f462f0fb19c49221d350bf396f", + "components/jquery": "1.11.0", + "components/jqueryui": "1.10.4", + "linkorb/jsmin-php": "1.1.2", + "tinymce/tinymce": "3.4.7", + "trentrichardson/jquery-timepicker-addon": "1.4.3", + "twbs/bootstrap": "3.1.0" + }, + "type": "magento2-component", + "extra": { + "component_paths": { + "trentrichardson/jquery-timepicker-addon": "lib/web/jquery/jquery-ui-timepicker-addon.js", + "colinmollenhour/cache-backend-redis": "lib/internal/Cm/Cache/Backend/Redis.php", + "colinmollenhour/credis": "lib/internal/Credis", + "linkorb/jsmin-php": "lib/internal/JSMin", + "components/jquery": [ + "lib/web/jquery.js", + "lib/web/jquery/jquery.min.js", + "lib/web/jquery/jquery-migrate.js", + "lib/web/jquery/jquery-migrate.min.js" + ], + "blueimp/jquery-file-upload": "lib/web/jquery/fileUploader", + "components/jqueryui": [ + "lib/web/jquery/jquery-ui.js", + "lib/web/jquery/jquery-ui.min.js" + ], + "twbs/bootstrap": [ + "lib/web/jquery/jquery.tabs.js" + ], + "tinymce/tinymce": "lib/web/tiny_mce" + }, + "map": [ + [ + "lib/internal/Cm", + "lib/internal/Cm" + ], + [ + "lib/internal/LinLibertineFont", + "lib/internal/LinLibertineFont" + ], + [ + "lib/internal/Credis", + "lib/internal/Credis" + ], + [ + "lib/internal/CardinalCommerce", + "lib/internal/CardinalCommerce" + ], + [ + "lib/internal/JSMin", + "lib/internal/JSMin" + ], + [ + "lib/.htaccess", + "lib/.htaccess" + ], + [ + "lib/web/lib", + "lib/web/lib" + ], + [ + "lib/web/requirejs", + "lib/web/requirejs" + ], + [ + "lib/web/prototype", + "lib/web/prototype" + ], + [ + "lib/web/moment.js", + "lib/web/moment.js" + ], + [ + "lib/web/i18n", + "lib/web/i18n" + ], + [ + "lib/web/varien", + "lib/web/varien" + ], + [ + "lib/web/blank.html", + "lib/web/blank.html" + ], + [ + "lib/web/scriptaculous", + "lib/web/scriptaculous" + ], + [ + "lib/web/ko", + "lib/web/ko" + ], + [ + "lib/web/modernizr", + "lib/web/modernizr" + ], + [ + "lib/web/date-format-normalizer.js", + "lib/web/date-format-normalizer.js" + ], + [ + "lib/web/spacer.gif", + "lib/web/spacer.gif" + ], + [ + "lib/web/images", + "lib/web/images" + ], + [ + "lib/web/matchMedia.js", + "lib/web/matchMedia.js" + ], + [ + "lib/web/extjs", + "lib/web/extjs" + ], + [ + "lib/web/css", + "lib/web/css" + ], + [ + "lib/web/fonts", + "lib/web/fonts" + ], + [ + "lib/web/jquery", + "lib/web/jquery" + ], + [ + "lib/web/less", + "lib/web/less" + ], + [ + "lib/web/jquery.js", + "lib/web/jquery.js" + ], + [ + "lib/web/underscore.js", + "lib/web/underscore.js" + ], + [ + "lib/web/legacy-build.min.js", + "lib/web/legacy-build.min.js" + ], + [ + "lib/web/mage", + "lib/web/mage" + ], + [ + "lib/web/tiny_mce", + "lib/web/tiny_mce" + ], + [ + ".htaccess.sample", + ".htaccess.sample" + ], + [ + "CONTRIBUTING.md", + "CONTRIBUTING.md" + ], + [ + "package.json", + "package.json" + ], + [ + "nginx.conf.sample", + "nginx.conf.sample" + ], + [ + "var/.htaccess", + "var/.htaccess" + ], + [ + "pub/media/downloadable/.htaccess", + "pub/media/downloadable/.htaccess" + ], + [ + "pub/media/theme_customization/.htaccess", + "pub/media/theme_customization/.htaccess" + ], + [ + "pub/media/customer/.htaccess", + "pub/media/customer/.htaccess" + ], + [ + "pub/media/.htaccess", + "pub/media/.htaccess" + ], + [ + "pub/errors", + "pub/errors" + ], + [ + "pub/opt", + "pub/opt" + ], + [ + "pub/get.php", + "pub/get.php" + ], + [ + "pub/static/.htaccess", + "pub/static/.htaccess" + ], + [ + "pub/.htaccess", + "pub/.htaccess" + ], + [ + "pub/static.php", + "pub/static.php" + ], + [ + "pub/cron.php", + "pub/cron.php" + ], + [ + "pub/index.php", + "pub/index.php" + ], + [ + ".php_cs", + ".php_cs" + ], + [ + "COPYING.txt", + "COPYING.txt" + ], + [ + "dev/shell", + "dev/shell" + ], + [ + "dev/tools", + "dev/tools" + ], + [ + "dev/tests/integration/phpunit.xml.dist", + "dev/tests/integration/phpunit.xml.dist" + ], + [ + "dev/tests/integration/etc", + "dev/tests/integration/etc" + ], + [ + "dev/tests/integration/tmp", + "dev/tests/integration/tmp" + ], + [ + "dev/tests/integration/framework", + "dev/tests/integration/framework" + ], + [ + "dev/tests/integration/testsuite/Magento", + "dev/tests/integration/testsuite/Magento" + ], + [ + "dev/tests/integration/.gitignore", + "dev/tests/integration/.gitignore" + ], + [ + "dev/tests/functional/composer.json", + "dev/tests/functional/composer.json" + ], + [ + "dev/tests/functional/lib", + "dev/tests/functional/lib" + ], + [ + "dev/tests/functional/phpunit.xml.dist", + "dev/tests/functional/phpunit.xml.dist" + ], + [ + "dev/tests/functional/composer.json.dist", + "dev/tests/functional/composer.json.dist" + ], + [ + "dev/tests/functional/bootstrap.php", + "dev/tests/functional/bootstrap.php" + ], + [ + "dev/tests/functional/credentials.xml.dist", + "dev/tests/functional/credentials.xml.dist" + ], + [ + "dev/tests/functional/tests", + "dev/tests/functional/tests" + ], + [ + "dev/tests/functional/utils", + "dev/tests/functional/utils" + ], + [ + "dev/tests/functional/isolation.php", + "dev/tests/functional/isolation.php" + ], + [ + "dev/tests/functional/etc", + "dev/tests/functional/etc" + ], + [ + "dev/tests/functional/.htaccess", + "dev/tests/functional/.htaccess" + ], + [ + "dev/tests/functional/testsuites/Magento", + "dev/tests/functional/testsuites/Magento" + ], + [ + "dev/tests/functional/.gitignore", + "dev/tests/functional/.gitignore" + ], + [ + "dev/tests/js/run_js_tests.php", + "dev/tests/js/run_js_tests.php" + ], + [ + "dev/tests/js/jsTestDriverOrder.php", + "dev/tests/js/jsTestDriverOrder.php" + ], + [ + "dev/tests/js/jsTestDriver.php.dist", + "dev/tests/js/jsTestDriver.php.dist" + ], + [ + "dev/tests/js/spec", + "dev/tests/js/spec" + ], + [ + "dev/tests/js/framework", + "dev/tests/js/framework" + ], + [ + "dev/tests/js/testsuite/lib", + "dev/tests/js/testsuite/lib" + ], + [ + "dev/tests/js/testsuite/mage", + "dev/tests/js/testsuite/mage" + ], + [ + "dev/tests/js/.gitignore", + "dev/tests/js/.gitignore" + ], + [ + "dev/tests/api-functional/phpunit.xml.dist", + "dev/tests/api-functional/phpunit.xml.dist" + ], + [ + "dev/tests/api-functional/config", + "dev/tests/api-functional/config" + ], + [ + "dev/tests/api-functional/framework", + "dev/tests/api-functional/framework" + ], + [ + "dev/tests/api-functional/_files", + "dev/tests/api-functional/_files" + ], + [ + "dev/tests/api-functional/testsuite/Magento", + "dev/tests/api-functional/testsuite/Magento" + ], + [ + "dev/tests/api-functional/.gitignore", + "dev/tests/api-functional/.gitignore" + ], + [ + "dev/tests/unit/phpunit.xml.dist", + "dev/tests/unit/phpunit.xml.dist" + ], + [ + "dev/tests/unit/tmp", + "dev/tests/unit/tmp" + ], + [ + "dev/tests/unit/framework", + "dev/tests/unit/framework" + ], + [ + "dev/tests/unit/.gitignore", + "dev/tests/unit/.gitignore" + ], + [ + "dev/tests/static/phpunit-all.xml.dist", + "dev/tests/static/phpunit-all.xml.dist" + ], + [ + "dev/tests/static/phpunit.xml.dist", + "dev/tests/static/phpunit.xml.dist" + ], + [ + "dev/tests/static/framework", + "dev/tests/static/framework" + ], + [ + "dev/tests/static/testsuite/Magento", + "dev/tests/static/testsuite/Magento" + ], + [ + "dev/tests/static/.gitignore", + "dev/tests/static/.gitignore" + ], + [ + "dev/tests/performance/benchmark.jmx", + "dev/tests/performance/benchmark.jmx" + ], + [ + "dev/tests/performance/compare_reports.php", + "dev/tests/performance/compare_reports.php" + ], + [ + "dev/tests/performance/run_scenarios.php", + "dev/tests/performance/run_scenarios.php" + ], + [ + "dev/tests/performance/framework", + "dev/tests/performance/framework" + ], + [ + "dev/tests/performance/testsuite/backend.jmx", + "dev/tests/performance/testsuite/backend.jmx" + ], + [ + "dev/tests/performance/testsuite/product_edit.jmx", + "dev/tests/performance/testsuite/product_edit.jmx" + ], + [ + "dev/tests/performance/testsuite/add_to_cart.jmx", + "dev/tests/performance/testsuite/add_to_cart.jmx" + ], + [ + "dev/tests/performance/testsuite/product_view.jmx", + "dev/tests/performance/testsuite/product_view.jmx" + ], + [ + "dev/tests/performance/testsuite/home_page.jmx", + "dev/tests/performance/testsuite/home_page.jmx" + ], + [ + "dev/tests/performance/testsuite/reusable", + "dev/tests/performance/testsuite/reusable" + ], + [ + "dev/tests/performance/testsuite/quick_search.jmx", + "dev/tests/performance/testsuite/quick_search.jmx" + ], + [ + "dev/tests/performance/testsuite/fixtures", + "dev/tests/performance/testsuite/fixtures" + ], + [ + "dev/tests/performance/testsuite/advanced_search.jmx", + "dev/tests/performance/testsuite/advanced_search.jmx" + ], + [ + "dev/tests/performance/testsuite/checkout.jmx", + "dev/tests/performance/testsuite/checkout.jmx" + ], + [ + "dev/tests/performance/testsuite/category_view.jmx", + "dev/tests/performance/testsuite/category_view.jmx" + ], + [ + "dev/tests/performance/testsuite/_samples", + "dev/tests/performance/testsuite/_samples" + ], + [ + "dev/tests/performance/config.php.dist", + "dev/tests/performance/config.php.dist" + ], + [ + "dev/tests/performance/.gitignore", + "dev/tests/performance/.gitignore" + ], + [ + "dev/.htaccess", + "dev/.htaccess" + ], + [ + "Gruntfile.js", + "Gruntfile.js" + ], + [ + "setup", + "setup" + ], + [ + "LICENSE.txt", + "LICENSE.txt" + ], + [ + ".travis.yml", + ".travis.yml" + ], + [ + "app/bootstrap.php", + "app/bootstrap.php" + ], + [ + "app/etc/di.xml", + "app/etc/di.xml" + ], + [ + "app/.htaccess", + "app/.htaccess" + ], + [ + "app/functions.php", + "app/functions.php" + ], + [ + "app/autoload.php", + "app/autoload.php" + ], + [ + ".htaccess", + ".htaccess" + ], + [ + "php.ini.sample", + "php.ini.sample" + ], + [ + "CONTRIBUTOR_LICENSE_AGREEMENT.html", + "CONTRIBUTOR_LICENSE_AGREEMENT.html" + ], + [ + "index.php", + "index.php" + ], + [ + "LICENSE_AFL.txt", + "LICENSE_AFL.txt" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Magento 2 Base" + }, + { + "name": "magento/module-admin-notification", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-admin-notification-0.74.0-beta2.zip", + "reference": null, + "shasum": "740186bdf97b2161994c10a883211eae76b285e9" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/AdminNotification" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-authorization", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-authorization-0.74.0-beta2.zip", + "reference": null, + "shasum": "045f8440fe82b38ce228b0be52b011bb02773214" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Authorization" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "Authorization module provides access to Magento ACL functionality." + }, + { + "name": "magento/module-backend", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-backend-0.74.0-beta2.zip", + "reference": null, + "shasum": "39ddbf62a11d9be1b16539733c0f66f46a503033" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backup": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-cron": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-developer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-reports": "0.74.0-beta2", + "magento/module-require-js": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-translation": "0.74.0-beta2", + "magento/module-user": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Backend" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-backup", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-backup-0.74.0-beta2.zip", + "reference": null, + "shasum": "7188d9a41bd9d4294dfd90c7053c301876d82c48" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-cron": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Backup" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-bundle", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-bundle-0.74.0-beta2.zip", + "reference": null, + "shasum": "849f7fc43f7f9b6a37a922272656c6218c681693" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-catalog-rule": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-gift-message": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-webapi": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Bundle" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-captcha", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-captcha-0.74.0-beta2.zip", + "reference": null, + "shasum": "a80050a22bad3399e146b242be33f6717fe15d26" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Captcha" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-0.74.0-beta2.zip", + "reference": null, + "shasum": "d4259a0113a3b8eece9bdf88ad24b5cdee349de0" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-catalog-rule": "0.74.0-beta2", + "magento/module-catalog-url-rewrite": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-msrp": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "magento/module-product-alert": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-url-rewrite": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-cookie": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Catalog" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "3a26206abf71888d300c9d72b2190ff0f66eafd2" + }, + "require": { + "ext-ctype": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-inventory", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-inventory-0.74.0-beta2.zip", + "reference": null, + "shasum": "805c2bb2fb2c2f038dd73e4030a02e1208ea23da" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogInventory" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-rule", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-rule-0.74.0-beta2.zip", + "reference": null, + "shasum": "e9fe8b0f65dea1f8feba125e7d2974d48d1e0eef" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-rule": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogRule" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-search", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-search-0.74.0-beta2.zip", + "reference": null, + "shasum": "e8239e3dbbdccda266347d1ca5f286c1e9a2cb4b" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-search": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogSearch" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-url-rewrite", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-url-rewrite-0.74.0-beta2.zip", + "reference": null, + "shasum": "c3cc582396cc97970e2d0dd92e5315190cde0971" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-import-export": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-url-rewrite": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogUrlRewrite" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-catalog-widget", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-catalog-widget-0.74.0-beta2.zip", + "reference": null, + "shasum": "7dad3ad909bc97610211f4f008fa1817f1aac22a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-rule": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CatalogWidget" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-centinel", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-centinel-0.74.0-beta2.zip", + "reference": null, + "shasum": "069e94cd124c0d078eeb3174afdc1e49031a7288" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Centinel" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-checkout", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-checkout-0.74.0-beta2.zip", + "reference": null, + "shasum": "8161dcc6c2bba44981cb63cd24cf137900d37db7" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-gift-message": "0.74.0-beta2", + "magento/module-msrp": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-cookie": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Checkout" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-checkout-agreements", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-checkout-agreements-0.74.0-beta2.zip", + "reference": null, + "shasum": "be00ed9e3c1e3e4bacf7efc00b67c7cbb2065715" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CheckoutAgreements" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-cms", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-cms-0.74.0-beta2.zip", + "reference": null, + "shasum": "15ae95a27b5ff0c82fbdb43ca8570e0cee4b21f5" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-email": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "magento/module-variable": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Cms" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-cms-url-rewrite", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-cms-url-rewrite-0.74.0-beta2.zip", + "reference": null, + "shasum": "6a3775d7705efcf5b43bfbe327d6ef1db2a1c09c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-cms": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-url-rewrite": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CmsUrlRewrite" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-config", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-config-0.74.0-beta2.zip", + "reference": null, + "shasum": "a4be4800b197ab3a73bbfc0d23d5a1f753a07a57" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-cron": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-email": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Config" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-configurable-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-configurable-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "bb6d242228bcbe7c35253dc244c7977994b24b8e" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-import-export": "0.74.0-beta2", + "magento/module-configurable-product": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/ConfigurableImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-configurable-product", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-configurable-product-0.74.0-beta2.zip", + "reference": null, + "shasum": "fe8e4536a59f60be79211fc0072e640102d3be06" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-catalog-rule": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-webapi": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/ConfigurableProduct" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-contact", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-contact-0.74.0-beta2.zip", + "reference": null, + "shasum": "b53d65b91dd1f596d0a4f7b8bacf73e6eaff98c9" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-cms": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Contact" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-cookie", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-cookie-0.74.0-beta2.zip", + "reference": null, + "shasum": "a8a4e29967524214fb6af5c12681d7b410a63bc6" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-store": "0.74.0-beta2", + "php": "~5.4.11|~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-backend": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Cookie" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-cron", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-cron-0.74.0-beta2.zip", + "reference": null, + "shasum": "0e91ec4ade457797e7db0fe1aa4794747817fa6a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-config": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Cron" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-currency-symbol", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-currency-symbol-0.74.0-beta2.zip", + "reference": null, + "shasum": "261c0a4e05295f852a7986ef2d15509e4c2ad26c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CurrencySymbol" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-customer", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-customer-0.74.0-beta2.zip", + "reference": null, + "shasum": "135623f9517f12f4dc17e062280eaf5f5071e7b7" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-integration": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-newsletter": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-review": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-cookie": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Customer" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-customer-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-customer-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "9b4f6b059fecce79c7f8ce9fc163858b8a47c7ca" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/CustomerImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-developer", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-developer-0.74.0-beta2.zip", + "reference": null, + "shasum": "1a1077d963ec6310df7080defaadc9bd87ed6e21" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Developer" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-dhl", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-dhl-0.74.0-beta2.zip", + "reference": null, + "shasum": "f525d0b48189b60e2c0def9a8804933133995104" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Dhl" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-directory", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-directory-0.74.0-beta2.zip", + "reference": null, + "shasum": "3cb3ed72d237966f9deab3ac0386c3b6701cf66e" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Directory" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-downloadable", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-downloadable-0.74.0-beta2.zip", + "reference": null, + "shasum": "5bd1cabf95a5c44e329f1263f40e165b3aa6070e" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-gift-message": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-msrp": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Downloadable" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-eav", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-eav-0.74.0-beta2.zip", + "reference": null, + "shasum": "5594f375105dc1921f38772fbb069ff57c2e8f01" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Eav" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-email", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-email-0.74.0-beta2.zip", + "reference": null, + "shasum": "8ff7affa3d22107bd5e519dc24c098acccc44bf8" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-variable": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Email" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-fedex", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-fedex-0.74.0-beta2.zip", + "reference": null, + "shasum": "cdc61b901b080b14e42b10889c8fb6f639f520af" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Fedex" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-gift-message", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-gift-message-0.74.0-beta2.zip", + "reference": null, + "shasum": "76ea03167970205295c95d88f871f7ce0547cdbf" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-multishipping": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GiftMessage" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-google-adwords", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-google-adwords-0.74.0-beta2.zip", + "reference": null, + "shasum": "45dfa690b1f183b0a87e6db39e6399ff0024bc66" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GoogleAdwords" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-google-analytics", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-google-analytics-0.74.0-beta2.zip", + "reference": null, + "shasum": "7460860110d6942c3375db4a05cf079134e8ab6a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-cookie": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GoogleAnalytics" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-google-optimizer", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-google-optimizer-0.74.0-beta2.zip", + "reference": null, + "shasum": "90565d6ecc18276ef08c324caae5178ca8ba82ed" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-google-analytics": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GoogleOptimizer" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-google-shopping", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-google-shopping-0.74.0-beta2.zip", + "reference": null, + "shasum": "f86c5696c215900805df27dcc6f8d6df5bd8453a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GoogleShopping" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-grouped-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-grouped-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "3cbf803098dd03d361b51f730930c9c3e6a9292c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-import-export": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-grouped-product": "0.74.0-beta2", + "magento/module-import-export": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GroupedImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-grouped-product", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-grouped-product-0.74.0-beta2.zip", + "reference": null, + "shasum": "8ad968c974e321d99a9bccfd04721ccfb4bfb86f" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-msrp": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/GroupedProduct" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "c16b13bdf9efb2a9d52b6423ee641ca47d60da36" + }, + "require": { + "ext-ctype": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-indexer": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/ImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-indexer", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-indexer-0.74.0-beta2.zip", + "reference": null, + "shasum": "d1c6e17c92d766f8f3f578d290da957deb5d9a7c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Indexer" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-integration", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-integration-0.74.0-beta2.zip", + "reference": null, + "shasum": "8e9a7d4df606513a67a649f63abdd31557061665" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-user": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Integration" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-layered-navigation", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-layered-navigation-0.74.0-beta2.zip", + "reference": null, + "shasum": "a20af59f1ffcf9565e2fab9ead134ee129bdcc8d" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/LayeredNavigation" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-media-storage", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-media-storage-0.74.0-beta2.zip", + "reference": null, + "shasum": "7c1b9232bf83b123c2a3c6b03973bd637f34e0f6" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/MediaStorage" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-msrp", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-msrp-0.74.0-beta2.zip", + "reference": null, + "shasum": "16bda583d7a876262fdc740c9513a4fb1739c3eb" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-bundle": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-downloadable": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-grouped-product": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Msrp" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-multishipping", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-multishipping-0.74.0-beta2.zip", + "reference": null, + "shasum": "53b01c1acc039dec9e603681264a450f708d5fb6" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Multishipping" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-newsletter", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-newsletter-0.74.0-beta2.zip", + "reference": null, + "shasum": "a9e11c0ab106ea0aec8709a5a17169be826680c3" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-cron": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-email": "0.74.0-beta2", + "magento/module-require-js": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Newsletter" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-offline-payments", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-offline-payments-0.74.0-beta2.zip", + "reference": null, + "shasum": "d3b7cd008994bef7456496094f397c82a9f3a8ef" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-payment": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/OfflinePayments" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-offline-shipping", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-offline-shipping-0.74.0-beta2.zip", + "reference": null, + "shasum": "94f097652ea87bc0504837366a51b7065ca864d4" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-sales-rule": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/OfflineShipping" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-page-cache", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-page-cache-0.74.0-beta2.zip", + "reference": null, + "shasum": "9db7b9c8d035e0eb3f619d37c8ff439b9f225e8f" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/PageCache" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-payment", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-payment-0.74.0-beta2.zip", + "reference": null, + "shasum": "9da0894c984887cf2e3c6c5a32f84616867495ae" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-centinel": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Payment" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-persistent", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-persistent-0.74.0-beta2.zip", + "reference": null, + "shasum": "891f5f3f5c23dd19e2f815754f4c5e8b4da6af1c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-cron": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-page-cache": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Persistent" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-product-alert", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-product-alert-0.74.0-beta2.zip", + "reference": null, + "shasum": "43292b34d4078dddc73b7bb4bcdb889292b578a6" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/ProductAlert" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-quote", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-quote-0.74.0-beta2.zip", + "reference": null, + "shasum": "8c3033317c359e5fc20e9308ed796ae6760fa22f" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-catalog-rule": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Quote" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-reports", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-reports-0.74.0-beta2.zip", + "reference": null, + "shasum": "38b1c0ddfbd756666cb0bc05e687d01ced515464" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-downloadable": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-review": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-sales-rule": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Reports" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-require-js", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-require-js-0.74.0-beta2.zip", + "reference": null, + "shasum": "6e94b63070fd1c174ab55c109d384d5b3bb04131" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/RequireJs" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-review", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-review-0.74.0-beta2.zip", + "reference": null, + "shasum": "45385eac2767e9165bfd88bcbc6a69dccca29f38" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-newsletter": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-cookie": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Review" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-rss", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-rss-0.74.0-beta2.zip", + "reference": null, + "shasum": "0706c2102a0003e2f214c35b77c1e628519b727a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Rss" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-rule", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-rule-0.74.0-beta2.zip", + "reference": null, + "shasum": "762edf4eabfe082b589b09bcf47a3ef2ac8a7f35" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Rule" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-sales", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-sales-0.74.0-beta2.zip", + "reference": null, + "shasum": "bf591f1f3033aa2fd3fe8bfb2732a6e7b81a499c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-email": "0.74.0-beta2", + "magento/module-gift-message": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-reports": "0.74.0-beta2", + "magento/module-sales-rule": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "magento/module-wishlist": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Sales" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-sales-rule", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-sales-rule-0.74.0-beta2.zip", + "reference": null, + "shasum": "fdb556db9915f50b0bbbc3fd5f004c21fdf144ac" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-rule": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-reports": "0.74.0-beta2", + "magento/module-rule": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/SalesRule" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-search", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-search-0.74.0-beta2.zip", + "reference": null, + "shasum": "a9605f79153be5e6206e269161ec6b0fe27b4b1a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog-search": "0.74.0-beta2", + "magento/module-reports": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Search" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-sendfriend", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-sendfriend-0.74.0-beta2.zip", + "reference": null, + "shasum": "3830393608a4c34ef05eb26de6543ffe3739e1f4" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Sendfriend" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-shipping", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-shipping-0.74.0-beta2.zip", + "reference": null, + "shasum": "0793baf12096494d141c3e6419f53a9c57aef0df" + }, + "require": { + "ext-gd": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-contact": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-payment": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-fedex": "0.74.0-beta2", + "magento/module-ups": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Shipping" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-sitemap", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-sitemap-0.74.0-beta2.zip", + "reference": null, + "shasum": "55e734ce8228a6e0b39eccb1829ae617845f9e53" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-url-rewrite": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Sitemap" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-store", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-store-0.74.0-beta2.zip", + "reference": null, + "shasum": "b53f0f39a5eceb85e0b28bcf32d5bb68314b3027" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Store" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-tax", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-tax-0.74.0-beta2.zip", + "reference": null, + "shasum": "ed22d2db556957de4ab5406d4d0edce811855b9b" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-reports": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Tax" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-tax-import-export", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-tax-import-export-0.74.0-beta2.zip", + "reference": null, + "shasum": "90716decbf4c0eb8fb29523530471fc2f8fbb426" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/TaxImportExport" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-theme", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-theme-0.74.0-beta2.zip", + "reference": null, + "shasum": "4754e71f25664ca7d010351e63c8c017e3c649dd" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-media-storage": "0.74.0-beta2", + "magento/module-require-js": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-widget": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-translation": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Theme" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-translation", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-translation-0.74.0-beta2.zip", + "reference": null, + "shasum": "68e633ff0380bb3d9660c17149935a0cdeda4edb" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-developer": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Translation" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-ui", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-ui-0.74.0-beta2.zip", + "reference": null, + "shasum": "6edb28f5c771ccb9e747ea71922ecbea80a32391" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Ui" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-ups", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-ups-0.74.0-beta2.zip", + "reference": null, + "shasum": "70a8139468124067bfb07bf1c071638484136883" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Ups" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-url-rewrite", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-url-rewrite-0.74.0-beta2.zip", + "reference": null, + "shasum": "8e781525600ff9d43383816283807ebcff07a8bf" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-url-rewrite": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-cms-url-rewrite": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/UrlRewrite" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-user", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-user-0.74.0-beta2.zip", + "reference": null, + "shasum": "023375251bc6104b31984e4509d01907f6661c6b" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-integration": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/User" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-usps", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-usps-0.74.0-beta2.zip", + "reference": null, + "shasum": "22eecac7075ca9c6ecaa08e482b59f5c29ba465c" + }, + "require": { + "lib-libxml": "*", + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-config": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-shipping": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Usps" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-variable", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-variable-0.74.0-beta2.zip", + "reference": null, + "shasum": "73cb1fdc0ff84722f854736a4016e3f5fa331ec1" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-email": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.4.11|~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Variable" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-version", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-version-0.74.0-beta2.zip", + "reference": null, + "shasum": "de77cb875f16d6eec9c785df117eddf469bec459" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Version" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-webapi", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-webapi-0.74.0-beta2.zip", + "reference": null, + "shasum": "c189f264c1bb186e052a2ed375b4b9e00295f3a6" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-authorization": "0.74.0-beta2", + "magento/module-backend": "0.74.0-beta2", + "magento/module-integration": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-user": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Webapi" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-weee", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-weee-0.74.0-beta2.zip", + "reference": null, + "shasum": "cc524f7211673263beb1f4c6b96bf32ffa04527b" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-directory": "0.74.0-beta2", + "magento/module-eav": "0.74.0-beta2", + "magento/module-quote": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-tax": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Weee" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-widget", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-widget-0.74.0-beta2.zip", + "reference": null, + "shasum": "f23668007563d5b7f9122e3eb453359fab9d1249" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-cms": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-variable": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Widget" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/module-wishlist", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_module-wishlist-0.74.0-beta2.zip", + "reference": null, + "shasum": "fca56783501ad2b8f2c583350cb038f8191634e1" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/module-backend": "0.74.0-beta2", + "magento/module-catalog": "0.74.0-beta2", + "magento/module-catalog-inventory": "0.74.0-beta2", + "magento/module-checkout": "0.74.0-beta2", + "magento/module-customer": "0.74.0-beta2", + "magento/module-grouped-product": "0.74.0-beta2", + "magento/module-rss": "0.74.0-beta2", + "magento/module-sales": "0.74.0-beta2", + "magento/module-store": "0.74.0-beta2", + "magento/module-theme": "0.74.0-beta2", + "magento/module-ui": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "suggest": { + "magento/module-bundle": "0.74.0-beta2", + "magento/module-configurable-product": "0.74.0-beta2", + "magento/module-cookie": "0.74.0-beta2", + "magento/module-downloadable": "0.74.0-beta2" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/Wishlist" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/product-community-edition", + "version": "0.74.0-beta2", + "source": { + "type": "git", + "url": "https://github.com/magento/magento2-community-edition.git", + "reference": "830bba759a180a97375275013b974c4606805466" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/magento2-community-edition/zipball/830bba759a180a97375275013b974c4606805466", + "reference": "830bba759a180a97375275013b974c4606805466", + "shasum": "" + }, + "require": { + "composer/composer": "1.0.0-alpha8", + "magento/framework": "self.version", + "magento/language-de_de": "self.version", + "magento/language-en_us": "self.version", + "magento/language-es_es": "self.version", + "magento/language-fr_fr": "self.version", + "magento/language-nl_nl": "self.version", + "magento/language-pt_br": "self.version", + "magento/language-zh_cn": "self.version", + "magento/magento-composer-installer": "*", + "magento/magento2-base": "self.version", + "magento/module-admin-notification": "self.version", + "magento/module-authorization": "self.version", + "magento/module-backend": "self.version", + "magento/module-backup": "self.version", + "magento/module-bundle": "self.version", + "magento/module-captcha": "self.version", + "magento/module-catalog": "self.version", + "magento/module-catalog-import-export": "self.version", + "magento/module-catalog-inventory": "self.version", + "magento/module-catalog-rule": "self.version", + "magento/module-catalog-search": "self.version", + "magento/module-catalog-url-rewrite": "self.version", + "magento/module-catalog-widget": "self.version", + "magento/module-centinel": "self.version", + "magento/module-checkout": "self.version", + "magento/module-checkout-agreements": "self.version", + "magento/module-cms": "self.version", + "magento/module-cms-url-rewrite": "self.version", + "magento/module-config": "self.version", + "magento/module-configurable-import-export": "self.version", + "magento/module-configurable-product": "self.version", + "magento/module-contact": "self.version", + "magento/module-cookie": "self.version", + "magento/module-cron": "self.version", + "magento/module-currency-symbol": "self.version", + "magento/module-customer": "self.version", + "magento/module-customer-import-export": "self.version", + "magento/module-developer": "self.version", + "magento/module-dhl": "self.version", + "magento/module-directory": "self.version", + "magento/module-downloadable": "self.version", + "magento/module-eav": "self.version", + "magento/module-email": "self.version", + "magento/module-fedex": "self.version", + "magento/module-gift-message": "self.version", + "magento/module-google-adwords": "self.version", + "magento/module-google-analytics": "self.version", + "magento/module-google-optimizer": "self.version", + "magento/module-google-shopping": "self.version", + "magento/module-grouped-import-export": "self.version", + "magento/module-grouped-product": "self.version", + "magento/module-import-export": "self.version", + "magento/module-indexer": "self.version", + "magento/module-integration": "self.version", + "magento/module-layered-navigation": "self.version", + "magento/module-media-storage": "self.version", + "magento/module-msrp": "self.version", + "magento/module-multishipping": "self.version", + "magento/module-newsletter": "self.version", + "magento/module-offline-payments": "self.version", + "magento/module-offline-shipping": "self.version", + "magento/module-page-cache": "self.version", + "magento/module-payment": "self.version", + "magento/module-persistent": "self.version", + "magento/module-product-alert": "self.version", + "magento/module-quote": "self.version", + "magento/module-reports": "self.version", + "magento/module-require-js": "self.version", + "magento/module-review": "self.version", + "magento/module-rss": "self.version", + "magento/module-rule": "self.version", + "magento/module-sales": "self.version", + "magento/module-sales-rule": "self.version", + "magento/module-search": "self.version", + "magento/module-sendfriend": "self.version", + "magento/module-shipping": "self.version", + "magento/module-sitemap": "self.version", + "magento/module-store": "self.version", + "magento/module-tax": "self.version", + "magento/module-tax-import-export": "self.version", + "magento/module-theme": "self.version", + "magento/module-translation": "self.version", + "magento/module-ui": "self.version", + "magento/module-ups": "self.version", + "magento/module-url-rewrite": "self.version", + "magento/module-user": "self.version", + "magento/module-usps": "self.version", + "magento/module-variable": "self.version", + "magento/module-version": "self.version", + "magento/module-webapi": "self.version", + "magento/module-weee": "self.version", + "magento/module-widget": "self.version", + "magento/module-wishlist": "self.version", + "magento/theme-adminhtml-backend": "self.version", + "magento/theme-frontend-blank": "self.version", + "magento/theme-frontend-luma": "self.version", + "magento/zendframework1": "1.12.10", + "monolog/monolog": "1.11.0", + "oyejorge/less.php": "1.7.0.3", + "php": "~5.5.0|~5.6.0|~7.0.0", + "tubalmartin/cssmin": "2.4.8-p4", + "zendframework/zend-code": "2.3.1", + "zendframework/zend-config": "2.3.1", + "zendframework/zend-console": "2.3.1", + "zendframework/zend-di": "2.3.1", + "zendframework/zend-eventmanager": "2.3.1", + "zendframework/zend-form": "2.3.1", + "zendframework/zend-http": "2.3.1", + "zendframework/zend-json": "2.3.1", + "zendframework/zend-log": "2.3.1", + "zendframework/zend-modulemanager": "2.3.1", + "zendframework/zend-mvc": "2.3.1", + "zendframework/zend-serializer": "2.3.1", + "zendframework/zend-server": "2.3.1", + "zendframework/zend-servicemanager": "2.3.1", + "zendframework/zend-soap": "2.3.1", + "zendframework/zend-stdlib": "2.3.1", + "zendframework/zend-text": "2.3.1", + "zendframework/zend-uri": "2.3.1", + "zendframework/zend-validator": "2.3.1", + "zendframework/zend-view": "2.3.1" + }, + "require-dev": { + "ext-ctype": "*", + "ext-curl": "*", + "ext-dom": "*", + "ext-gd": "*", + "ext-hash": "*", + "ext-iconv": "*", + "ext-intl": "*", + "ext-mcrypt": "*", + "ext-simplexml": "*", + "ext-spl": "*", + "fabpot/php-cs-fixer": "~1.2", + "lib-libxml": "*", + "lusitanian/oauth": "~0.3", + "pdepend/pdepend": "2.0.6", + "phpmd/phpmd": "@stable", + "phpunit/phpunit": "4.1.0", + "sjparkinson/static-review": "~4.1", + "squizlabs/php_codesniffer": "1.5.3" + }, + "type": "project", + "autoload": { + "psr-4": { + "Magento\\Framework\\": "lib/internal/Magento/Framework/", + "Magento\\Setup\\": "setup/src/Magento/Setup/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "eCommerce Platform for Growth (Community Edition)", + "time": "2015-03-27 15:47:02" + }, + { + "name": "magento/sample-module-minimal", + "version": "1.0.0", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_sample-module-minimal-1.0.0.zip", + "reference": null, + "shasum": "8c8bd3ee4907a6a91bffeac177628bd4f238d6e4" + }, + "require": { + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-module", + "extra": { + "map": [ + [ + "*", + "Magento/SampleMinimal" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "A minimal skeleton Magento 2 module" + }, + { + "name": "magento/theme-adminhtml-backend", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_theme-adminhtml-backend-0.74.0-beta2.zip", + "reference": null, + "shasum": "d8bd8d6b70d65d6f38751f7e9f9cbbebf751c97c" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-theme", + "extra": { + "map": [ + [ + "*", + "adminhtml/Magento/backend" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/theme-frontend-blank", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_theme-frontend-blank-0.74.0-beta2.zip", + "reference": null, + "shasum": "e298ae05894e97ba3d94a87b77086dfe0c2d974a" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-theme", + "extra": { + "map": [ + [ + "*", + "frontend/Magento/blank" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/theme-frontend-luma", + "version": "0.74.0-beta2", + "dist": { + "type": "zip", + "url": "https://packages.magento.com/_packages/magento_theme-frontend-luma-0.74.0-beta2.zip", + "reference": null, + "shasum": "82e29e8fd8d928c33f064fb431d06f3968f37327" + }, + "require": { + "magento/framework": "0.74.0-beta2", + "magento/magento-composer-installer": "*", + "magento/theme-frontend-blank": "0.74.0-beta2", + "php": "~5.5.0|~5.6.0" + }, + "type": "magento2-theme", + "extra": { + "map": [ + [ + "*", + "frontend/Magento/luma" + ] + ] + }, + "license": [ + "OSL-3.0", + "AFL-3.0" + ], + "description": "N/A" + }, + { + "name": "magento/zendframework1", + "version": "1.12.10", + "source": { + "type": "git", + "url": "https://github.com/magento/zf1.git", + "reference": "d1e5cd8c9f83229bcdd9bb485c3ce25259c77884" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/magento/zf1/zipball/d1e5cd8c9f83229bcdd9bb485c3ce25259c77884", + "reference": "d1e5cd8c9f83229bcdd9bb485c3ce25259c77884", + "shasum": "" + }, + "require": { + "php": ">=5.2.11" + }, + "require-dev": { + "phpunit/dbunit": "1.3.*", + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.12.x-dev" + } + }, + "autoload": { + "psr-0": { + "Zend_": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "include-path": [ + "library/" + ], + "license": [ + "BSD-3-Clause" + ], + "description": "Magento Zend Framework 1", + "homepage": "http://framework.zend.com/", + "keywords": [ + "ZF1", + "framework" + ], + "time": "2015-02-06 17:25:45" + }, + { + "name": "monolog/monolog", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "ec3961874c43840e96da3a8a1ed20d8c73d7e5aa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/ec3961874c43840e96da3a8a1ed20d8c73d7e5aa", + "reference": "ec3961874c43840e96da3a8a1ed20d8c73d7e5aa", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "~2.4, >2.4.8", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "phpunit/phpunit": "~3.7.0", + "raven/raven": "~0.5", + "ruflin/elastica": "0.90.*", + "videlalvaro/php-amqplib": "~2.4" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.11.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2014-09-30 13:30:58" + }, + { + "name": "oyejorge/less.php", + "version": "v1.7.0.3", + "source": { + "type": "git", + "url": "https://github.com/oyejorge/less.php.git", + "reference": "6e08ecb07e6f6d9170c23e8744c58fdd822ad0de" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/oyejorge/less.php/zipball/6e08ecb07e6f6d9170c23e8744c58fdd822ad0de", + "reference": "6e08ecb07e6f6d9170c23e8744c58fdd822ad0de", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "bin": [ + "bin/lessc" + ], + "type": "library", + "autoload": { + "psr-0": { + "Less": "lib/" + }, + "classmap": [ + "lessc.inc.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Matt Agar", + "homepage": "https://github.com/agar" + }, + { + "name": "Martin Jantošovič", + "homepage": "https://github.com/Mordred" + }, + { + "name": "Josh Schmidt", + "homepage": "https://github.com/oyejorge" + } + ], + "description": "PHP port of the Javascript version of LESS http://lesscss.org", + "homepage": "http://lessphp.gpeasy.com", + "keywords": [ + "css", + "less", + "less.js", + "lesscss", + "php", + "stylesheet" + ], + "time": "2015-03-10 18:12:59" + }, + { + "name": "psr/log", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "bf2c13de4300e227d7b2fd08027673a79c519987" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/bf2c13de4300e227d7b2fd08027673a79c519987", + "reference": "bf2c13de4300e227d7b2fd08027673a79c519987", + "shasum": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "Psr/Log/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2015-03-26 14:39:45" + }, + { + "name": "seld/jsonlint", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/863ae85c6d3ef60ca49cb12bd051c4a0648c40c4", + "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "time": "2015-01-04 21:18:15" + }, + { + "name": "symfony/console", + "version": "2.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/Console.git", + "reference": "7e857a2b52b5833ed27d78a6b1b846bd440ee8bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Console/zipball/7e857a2b52b5833ed27d78a6b1b846bd440ee8bd", + "reference": "7e857a2b52b5833ed27d78a6b1b846bd440ee8bd", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "psr/log": "~1.0", + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/phpunit-bridge": "~2.7|~3.0.0", + "symfony/process": "~2.1|~3.0.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/process": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Console Component", + "homepage": "https://symfony.com", + "time": "2015-05-15 14:11:12" + }, + { + "name": "symfony/finder", + "version": "2.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/Finder.git", + "reference": "fd26bdbb67bc8753884eff5767a4f1ee90e2284b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Finder/zipball/fd26bdbb67bc8753884eff5767a4f1ee90e2284b", + "reference": "fd26bdbb67bc8753884eff5767a4f1ee90e2284b", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7|~3.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Finder Component", + "homepage": "https://symfony.com", + "time": "2015-05-15 14:11:12" + }, + { + "name": "symfony/process", + "version": "2.8.x-dev", + "source": { + "type": "git", + "url": "https://github.com/symfony/Process.git", + "reference": "daf96f1491cf0b8d8de60e6d3b5fda8e502b3798" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/Process/zipball/daf96f1491cf0b8d8de60e6d3b5fda8e502b3798", + "reference": "daf96f1491cf0b8d8de60e6d3b5fda8e502b3798", + "shasum": "" + }, + "require": { + "php": ">=5.3.9" + }, + "require-dev": { + "symfony/phpunit-bridge": "~2.7|~3.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Process Component", + "homepage": "https://symfony.com", + "time": "2015-05-15 14:11:12" + }, + { + "name": "tubalmartin/cssmin", + "version": "v2.4.8-p4", + "source": { + "type": "git", + "url": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port.git", + "reference": "fe84d71e8420243544c0ce3bd0f5d7c1936b0f90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tubalmartin/YUI-CSS-compressor-PHP-port/zipball/fe84d71e8420243544c0ce3bd0f5d7c1936b0f90", + "reference": "fe84d71e8420243544c0ce3bd0f5d7c1936b0f90", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "cssmin.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Túbal Martín", + "homepage": "http://tubalmartin.me/" + } + ], + "description": "A PHP port of the YUI CSS compressor", + "homepage": "https://github.com/tubalmartin/YUI-CSS-compressor-PHP-port", + "keywords": [ + "compress", + "compressor", + "css", + "minify", + "yui" + ], + "time": "2014-09-22 08:08:50" + }, + { + "name": "zendframework/zend-code", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-code.git", + "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-code/zipball/a64e90c9ee8c939335ee7e21e39e3342c0e54526", + "reference": "a64e90c9ee8c939335ee7e21e39e3342c0e54526", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-eventmanager": "self.version" + }, + "require-dev": { + "doctrine/common": ">=2.1", + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-stdlib": "self.version" + }, + "suggest": { + "doctrine/common": "Doctrine\\Common >=2.1 for annotation features", + "zendframework/zend-stdlib": "Zend\\Stdlib component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Code\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides facilities to generate arbitrary code using an object oriented interface", + "homepage": "https://github.com/zendframework/zend-code", + "keywords": [ + "code", + "zf2" + ], + "time": "2014-04-15 14:47:18" + }, + { + "name": "zendframework/zend-config", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-config.git", + "reference": "698c139707380b29fd09791ec1c21b837e9a3f15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/698c139707380b29fd09791ec1c21b837e9a3f15", + "reference": "698c139707380b29fd09791ec1c21b837e9a3f15", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-filter": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-json": "self.version", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-json": "Zend\\Json to use the Json reader or writer classes", + "zendframework/zend-servicemanager": "Zend\\ServiceManager for use with the Config Factory to retrieve reader and writer instances" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Config\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a nested object property based user interface for accessing this configuration data within application code", + "homepage": "https://github.com/zendframework/zend-config", + "keywords": [ + "config", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-console", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-console.git", + "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-console/zipball/4253efd75a022d97ef326eac38a06c8eebb48a37", + "reference": "4253efd75a022d97ef326eac38a06c8eebb48a37", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "suggest": { + "zendframework/zend-filter": "To support DefaultRouteMatcher usage", + "zendframework/zend-validator": "To support DefaultRouteMatcher usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Console\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-console", + "keywords": [ + "console", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-di", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-di.git", + "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-di/zipball/7502db10f8023bfd4e860ce83c9cdeda0db42c31", + "reference": "7502db10f8023bfd4e860ce83c9cdeda0db42c31", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-code": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Di\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-di", + "keywords": [ + "di", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-escaper", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-escaper.git", + "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-escaper/zipball/2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", + "reference": "2b2fcb6141cc2a2c6cc0c596e82771c72ef4ddc1", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Escaper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-escaper", + "keywords": [ + "escaper", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-eventmanager", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-eventmanager.git", + "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/ec158e89d0290f988a29ccd6bf179b561efbb702", + "reference": "ec158e89d0290f988a29ccd6bf179b561efbb702", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\EventManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-event-manager", + "keywords": [ + "eventmanager", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-filter", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-filter.git", + "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/307fe694659e08ffd710c70e4db8bc60187bcc84", + "reference": "307fe694659e08ffd710c70e4db8bc60187bcc84", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-crypt": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-uri": "self.version" + }, + "suggest": { + "zendframework/zend-crypt": "Zend\\Crypt component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-uri": "Zend\\Uri component for UriNormalize filter" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Filter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a set of commonly needed data filters", + "homepage": "https://github.com/zendframework/zend-filter", + "keywords": [ + "filter", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-form", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-form.git", + "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-form/zipball/d7a1f5bc4626b1df990391502a868b28c37ba65d", + "reference": "d7a1f5bc4626b1df990391502a868b28c37ba65d", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-inputfilter": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-captcha": "self.version", + "zendframework/zend-code": "self.version", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-validator": "self.version", + "zendframework/zend-view": "self.version", + "zendframework/zendservice-recaptcha": "*" + }, + "suggest": { + "zendframework/zend-captcha": "Zend\\Captcha component", + "zendframework/zend-code": "Zend\\Code component", + "zendframework/zend-eventmanager": "Zend\\EventManager component", + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-validator": "Zend\\Validator component", + "zendframework/zend-view": "Zend\\View component", + "zendframework/zendservice-recaptcha": "ZendService\\ReCaptcha component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Form\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-form", + "keywords": [ + "form", + "zf2" + ], + "time": "2014-04-15 14:36:41" + }, + { + "name": "zendframework/zend-http", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-http.git", + "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-http/zipball/869ce7bdf60727e14d85c305d2948fbe831c3534", + "reference": "869ce7bdf60727e14d85c305d2948fbe831c3534", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-loader": "self.version", + "zendframework/zend-stdlib": "self.version", + "zendframework/zend-uri": "self.version", + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Http\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides an easy interface for performing Hyper-Text Transfer Protocol (HTTP) requests", + "homepage": "https://github.com/zendframework/zend-http", + "keywords": [ + "http", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-inputfilter", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-inputfilter.git", + "reference": "abca740015a856d03542f5b6c535b8874f84b622" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-inputfilter/zipball/abca740015a856d03542f5b6c535b8874f84b622", + "reference": "abca740015a856d03542f5b6c535b8874f84b622", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-filter": "self.version", + "zendframework/zend-stdlib": "self.version", + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-servicemanager": "To support plugin manager support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\InputFilter\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-input-filter", + "keywords": [ + "inputfilter", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-json", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-json.git", + "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/5284687fc3aeab27961d2e17ada08973ae6daafe", + "reference": "5284687fc3aeab27961d2e17ada08973ae6daafe", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-http": "self.version", + "zendframework/zend-server": "self.version" + }, + "suggest": { + "zendframework/zend-http": "Zend\\Http component", + "zendframework/zend-server": "Zend\\Server component", + "zendframework/zendxml": "To support Zend\\Json\\Json::fromXml() usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Json\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides convenience methods for serializing native PHP to JSON and decoding JSON to native PHP", + "homepage": "https://github.com/zendframework/zend-json", + "keywords": [ + "json", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-loader", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-loader.git", + "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-loader/zipball/5e0bd7e28c644078685f525cf8ae03d9a01ae292", + "reference": "5e0bd7e28c644078685f525cf8ae03d9a01ae292", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Loader\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-loader", + "keywords": [ + "loader", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-log", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-log.git", + "reference": "217611433f5cb56d4420a1db8f164e5db85d815d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-log/zipball/217611433f5cb56d4420a1db8f164e5db85d815d", + "reference": "217611433f5cb56d4420a1db8f164e5db85d815d", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-console": "self.version", + "zendframework/zend-db": "self.version", + "zendframework/zend-escaper": "self.version", + "zendframework/zend-mail": "self.version", + "zendframework/zend-validator": "self.version" + }, + "suggest": { + "ext-mongo": "*", + "zendframework/zend-console": "Zend\\Console component", + "zendframework/zend-db": "Zend\\Db component", + "zendframework/zend-escaper": "Zend\\Escaper component, for use in the XML formatter", + "zendframework/zend-mail": "Zend\\Mail component", + "zendframework/zend-validator": "Zend\\Validator component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Log\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "component for general purpose logging", + "homepage": "https://github.com/zendframework/zend-log", + "keywords": [ + "log", + "logging", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-math", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-math.git", + "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-math/zipball/63225fcebb196fc6e20094f5f01e9354779ec31e", + "reference": "63225fcebb196fc6e20094f5f01e9354779ec31e", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "suggest": { + "ext-bcmath": "If using the bcmath functionality", + "ext-gmp": "If using the gmp functionality", + "ircmaxell/random-lib": "Fallback random byte generator for Zend\\Math\\Rand if OpenSSL/Mcrypt extensions are unavailable", + "zendframework/zend-servicemanager": ">= current version, if using the BigInteger::factory functionality" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Math\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-math", + "keywords": [ + "math", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-modulemanager", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-modulemanager.git", + "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-modulemanager/zipball/d4591b958e40b8f5ae8110d9b203331437aa19f2", + "reference": "d4591b958e40b8f5ae8110d9b203331437aa19f2", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-config": "self.version", + "zendframework/zend-console": "self.version", + "zendframework/zend-loader": "self.version", + "zendframework/zend-mvc": "self.version", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-config": "Zend\\Config component", + "zendframework/zend-console": "Zend\\Console component", + "zendframework/zend-loader": "Zend\\Loader component", + "zendframework/zend-mvc": "Zend\\Mvc component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\ModuleManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-module-manager", + "keywords": [ + "modulemanager", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-mvc", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-mvc.git", + "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-mvc/zipball/ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", + "reference": "ee76ddd009ecb0c507bb8ab396fbe719aea8f1ff", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-form": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-authentication": "self.version", + "zendframework/zend-console": "self.version", + "zendframework/zend-di": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-form": "self.version", + "zendframework/zend-http": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-inputfilter": "self.version", + "zendframework/zend-json": "self.version", + "zendframework/zend-log": "self.version", + "zendframework/zend-modulemanager": "self.version", + "zendframework/zend-serializer": "self.version", + "zendframework/zend-session": "self.version", + "zendframework/zend-text": "self.version", + "zendframework/zend-uri": "self.version", + "zendframework/zend-validator": "self.version", + "zendframework/zend-version": "self.version", + "zendframework/zend-view": "self.version" + }, + "suggest": { + "zendframework/zend-authentication": "Zend\\Authentication component for Identity plugin", + "zendframework/zend-config": "Zend\\Config component", + "zendframework/zend-console": "Zend\\Console component", + "zendframework/zend-di": "Zend\\Di component", + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-form": "Zend\\Form component", + "zendframework/zend-http": "Zend\\Http component", + "zendframework/zend-i18n": "Zend\\I18n component for translatable segments", + "zendframework/zend-inputfilter": "Zend\\Inputfilter component", + "zendframework/zend-json": "Zend\\Json component", + "zendframework/zend-log": "Zend\\Log component", + "zendframework/zend-modulemanager": "Zend\\ModuleManager component", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-session": "Zend\\Session component for FlashMessenger, PRG, and FPRG plugins", + "zendframework/zend-stdlib": "Zend\\Stdlib component", + "zendframework/zend-text": "Zend\\Text component", + "zendframework/zend-uri": "Zend\\Uri component", + "zendframework/zend-validator": "Zend\\Validator component", + "zendframework/zend-version": "Zend\\Version component", + "zendframework/zend-view": "Zend\\View component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Mvc\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-mvc", + "keywords": [ + "mvc", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-serializer", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-serializer.git", + "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", + "reference": "22f73b0d0ff1158216bd5bcacf6bd00f7be1a0f6", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-json": "self.version", + "zendframework/zend-math": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-servicemanager": "To support plugin manager support" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Serializer\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides an adapter based interface to simply generate storable representation of PHP types by different facilities, and recover", + "homepage": "https://github.com/zendframework/zend-serializer", + "keywords": [ + "serializer", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-server", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-server.git", + "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-server/zipball/bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", + "reference": "bc5fb97f4ac48a5dc54bd18dded21a3e1eea552c", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-code": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Server\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-server", + "keywords": [ + "server", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-servicemanager", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-servicemanager.git", + "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/7a428b595a1fcd4c2a8026ee5d5f89a56036f682", + "reference": "7a428b595a1fcd4c2a8026ee5d5f89a56036f682", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-di": "self.version" + }, + "suggest": { + "zendframework/zend-di": "Zend\\Di component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\ServiceManager\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-service-manager", + "keywords": [ + "servicemanager", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-soap", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-soap.git", + "reference": "fab9f5309be04cacf01af54f694aed5102592c5c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-soap/zipball/fab9f5309be04cacf01af54f694aed5102592c5c", + "reference": "fab9f5309be04cacf01af54f694aed5102592c5c", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-server": "self.version", + "zendframework/zend-stdlib": "self.version", + "zendframework/zend-uri": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-http": "self.version" + }, + "suggest": { + "zendframework/zend-http": "Zend\\Http component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Soap\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-soap", + "keywords": [ + "soap", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-stdlib", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-stdlib.git", + "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/6079302963d57f36a9ba92ed3f38b992997aa78d", + "reference": "6079302963d57f36a9ba92ed3f38b992997aa78d", + "shasum": "" + }, + "require": { + "php": ">=5.3.23" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-serializer": "self.version", + "zendframework/zend-servicemanager": "self.version" + }, + "suggest": { + "zendframework/zend-eventmanager": "To support aggregate hydrator usage", + "zendframework/zend-filter": "To support naming strategy hydrator usage", + "zendframework/zend-serializer": "Zend\\Serializer component", + "zendframework/zend-servicemanager": "To support hydrator plugin manager usage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Stdlib\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-stdlib", + "keywords": [ + "stdlib", + "zf2" + ], + "time": "2014-04-15 13:59:53" + }, + { + "name": "zendframework/zend-text", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-text.git", + "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-text/zipball/e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", + "reference": "e9b3fffcc6241f7cfdb33282ed10979cd8ba9b90", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Text\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "homepage": "https://github.com/zendframework/zend-text", + "keywords": [ + "text", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-uri", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-uri.git", + "reference": "0de6ba8c166a235588783ff8e88a19b364630d89" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-uri/zipball/0de6ba8c166a235588783ff8e88a19b364630d89", + "reference": "0de6ba8c166a235588783ff8e88a19b364630d89", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-escaper": "self.version", + "zendframework/zend-validator": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Uri\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "a component that aids in manipulating and validating » Uniform Resource Identifiers (URIs)", + "homepage": "https://github.com/zendframework/zend-uri", + "keywords": [ + "uri", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-validator", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-validator.git", + "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/a5f97f6c3a27a27b1235f724ad0048715459f013", + "reference": "a5f97f6c3a27a27b1235f724ad0048715459f013", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-db": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-math": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-session": "self.version", + "zendframework/zend-uri": "self.version" + }, + "suggest": { + "zendframework/zend-db": "Zend\\Db component", + "zendframework/zend-filter": "Zend\\Filter component, required by the Digits validator", + "zendframework/zend-i18n": "Zend\\I18n component to allow translation of validation error messages as well as to use the various Date validators", + "zendframework/zend-math": "Zend\\Math component", + "zendframework/zend-resources": "Translations of validator messages", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component to allow using the ValidatorPluginManager and validator chains", + "zendframework/zend-session": "Zend\\Session component", + "zendframework/zend-uri": "Zend\\Uri component, required by the Uri and Sitemap\\Loc validators" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\Validator\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a set of commonly needed validators", + "homepage": "https://github.com/zendframework/zend-validator", + "keywords": [ + "validator", + "zf2" + ], + "time": "2014-04-14 19:50:30" + }, + { + "name": "zendframework/zend-view", + "version": "2.3.1", + "source": { + "type": "git", + "url": "https://github.com/zendframework/zend-view.git", + "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/zendframework/zend-view/zipball/e308d498fa7851f26bd639bfe3ebbfba397c47bc", + "reference": "e308d498fa7851f26bd639bfe3ebbfba397c47bc", + "shasum": "" + }, + "require": { + "php": ">=5.3.23", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-loader": "self.version", + "zendframework/zend-stdlib": "self.version" + }, + "require-dev": { + "fabpot/php-cs-fixer": "1.7.*", + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-authentication": "self.version", + "zendframework/zend-escaper": "self.version", + "zendframework/zend-feed": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-http": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-json": "self.version", + "zendframework/zend-mvc": "self.version", + "zendframework/zend-navigation": "self.version", + "zendframework/zend-paginator": "self.version", + "zendframework/zend-permissions-acl": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-uri": "self.version" + }, + "suggest": { + "zendframework/zend-authentication": "Zend\\Authentication component", + "zendframework/zend-escaper": "Zend\\Escaper component", + "zendframework/zend-feed": "Zend\\Feed component", + "zendframework/zend-filter": "Zend\\Filter component", + "zendframework/zend-http": "Zend\\Http component", + "zendframework/zend-i18n": "Zend\\I18n component", + "zendframework/zend-json": "Zend\\Json component", + "zendframework/zend-mvc": "Zend\\Mvc component", + "zendframework/zend-navigation": "Zend\\Navigation component", + "zendframework/zend-paginator": "Zend\\Paginator component", + "zendframework/zend-permissions-acl": "Zend\\Permissions\\Acl component", + "zendframework/zend-servicemanager": "Zend\\ServiceManager component", + "zendframework/zend-uri": "Zend\\Uri component" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.3-dev", + "dev-develop": "2.4-dev" + } + }, + "autoload": { + "psr-4": { + "Zend\\View\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "provides a system of helpers, output filters, and variable escaping", + "homepage": "https://github.com/zendframework/zend-view", + "keywords": [ + "view", + "zf2" + ], + "time": "2014-04-15 14:36:41" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} From 30a213536b2a795095710535a0c922c2081e7f57 Mon Sep 17 00:00:00 2001 From: Arkadii Chyzhov Date: Tue, 1 Sep 2015 19:52:56 +0300 Subject: [PATCH 31/31] MAGETWO-40268: Merge Branches and Stabilize Builds --- .../Magento/Email/Test/Unit/Model/Template/FilterTest.php | 4 ++-- app/code/Magento/User/Test/Unit/Helper/DataTest.php | 2 +- .../User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php | 2 +- app/code/Magento/User/Test/Unit/Model/UserTest.php | 1 - app/code/Magento/Widget/composer.json | 1 + 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php index 0a3b0cc94433f..a21a6dbd30bf1 100644 --- a/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php +++ b/app/code/Magento/Email/Test/Unit/Model/Template/FilterTest.php @@ -348,7 +348,7 @@ public function testConfigDirectiveAvailable() $construction = ["{{config path={$path}}}", 'config', " path={$path}"]; $scopeConfigValue = 'value'; - $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface',[], [], '', false); + $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface', [], [], '', false); $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); $storeMock->expects($this->once())->method('getId')->willReturn(1); @@ -369,7 +369,7 @@ public function testConfigDirectiveUnavailable() $construction = ["{{config path={$path}}}", 'config', " path={$path}"]; $scopeConfigValue = ''; - $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface',[], [], '', false); + $storeMock = $this->getMock('Magento\Store\Api\Data\StoreInterface', [], [], '', false); $this->storeManager->expects($this->once())->method('getStore')->willReturn($storeMock); $storeMock->expects($this->once())->method('getId')->willReturn(1); diff --git a/app/code/Magento/User/Test/Unit/Helper/DataTest.php b/app/code/Magento/User/Test/Unit/Helper/DataTest.php index 2b6af00a2f5e1..28f9fec77f164 100644 --- a/app/code/Magento/User/Test/Unit/Helper/DataTest.php +++ b/app/code/Magento/User/Test/Unit/Helper/DataTest.php @@ -60,7 +60,7 @@ public function testGetResetPasswordLinkExpirationPeriod() $value = '123'; $this->configMock->expects($this->once()) ->method('getValue') - ->with( \Magento\User\Helper\Data::XML_PATH_ADMIN_RESET_PASSWORD_LINK_EXPIRATION_PERIOD) + ->with(\Magento\User\Helper\Data::XML_PATH_ADMIN_RESET_PASSWORD_LINK_EXPIRATION_PERIOD) ->willReturn($value); $this->assertEquals((int) $value, $this->model->getResetPasswordLinkExpirationPeriod()); } diff --git a/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php b/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php index be819b6e8c9bc..8a486e4a745b7 100644 --- a/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php +++ b/app/code/Magento/User/Test/Unit/Model/Plugin/AuthorizationRoleTest.php @@ -46,7 +46,7 @@ protected function setUp() public function testAfterSave() { - $this->userResourceModelMock->expects($this->once())->method('updateRoleUsersAcl')->with($this->roleMock); + $this->userResourceModelMock->expects($this->once())->method('updateRoleUsersAcl')->with($this->roleMock); $this->assertInstanceOf( '\Magento\Authorization\Model\Role', $this->model->afterSave($this->roleMock, $this->roleMock) diff --git a/app/code/Magento/User/Test/Unit/Model/UserTest.php b/app/code/Magento/User/Test/Unit/Model/UserTest.php index 9d8cc52ac2296..1f37bd9a15524 100644 --- a/app/code/Magento/User/Test/Unit/Model/UserTest.php +++ b/app/code/Magento/User/Test/Unit/Model/UserTest.php @@ -623,4 +623,3 @@ public function testIsResetPasswordLinkTokenExpiredIsNotExpiredToken() $this->assertFalse($this->model->isResetPasswordLinkTokenExpired()); } } - diff --git a/app/code/Magento/Widget/composer.json b/app/code/Magento/Widget/composer.json index 1a9190debb3a5..936d461a2a7f8 100644 --- a/app/code/Magento/Widget/composer.json +++ b/app/code/Magento/Widget/composer.json @@ -7,6 +7,7 @@ "magento/module-cms": "1.0.0-beta", "magento/module-backend": "1.0.0-beta", "magento/module-catalog": "1.0.0-beta", + "magento/module-email": "1.0.0-beta", "magento/module-theme": "1.0.0-beta", "magento/framework": "1.0.0-beta", "magento/module-variable": "1.0.0-beta",