diff --git a/app/code/Magento/Integration/Model/AdminTokenService.php b/app/code/Magento/Integration/Model/AdminTokenService.php index 140c3ddc97791..0627007f329a8 100644 --- a/app/code/Magento/Integration/Model/AdminTokenService.php +++ b/app/code/Magento/Integration/Model/AdminTokenService.php @@ -95,7 +95,13 @@ public function createAdminAccessToken($username, $password) } /** - * {@inheritdoc} + * Revoke token by admin id. + * + * The function will delete the token from the oauth_token table. + * + * @param int $adminId + * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ public function revokeAdminAccessToken($adminId) { @@ -105,7 +111,7 @@ public function revokeAdminAccessToken($adminId) } try { foreach ($tokenCollection as $token) { - $token->setRevoked(1)->save(); + $token->delete(); } } catch (\Exception $e) { throw new LocalizedException(__('The tokens could not be revoked.')); diff --git a/app/code/Magento/Integration/Model/CustomerTokenService.php b/app/code/Magento/Integration/Model/CustomerTokenService.php index 5278907192983..97fd8c557c30c 100644 --- a/app/code/Magento/Integration/Model/CustomerTokenService.php +++ b/app/code/Magento/Integration/Model/CustomerTokenService.php @@ -88,7 +88,13 @@ public function createCustomerAccessToken($username, $password) } /** - * {@inheritdoc} + * Revoke token by customer id. + * + * The function will delete the token from the oauth_token table. + * + * @param int $customerId + * @return bool + * @throws \Magento\Framework\Exception\LocalizedException */ public function revokeCustomerAccessToken($customerId) { @@ -98,7 +104,7 @@ public function revokeCustomerAccessToken($customerId) } try { foreach ($tokenCollection as $token) { - $token->setRevoked(1)->save(); + $token->delete(); } } catch (\Exception $e) { throw new LocalizedException(__('The tokens could not be revoked.')); diff --git a/app/code/Magento/Integration/Plugin/Model/AdminUser.php b/app/code/Magento/Integration/Plugin/Model/AdminUser.php new file mode 100644 index 0000000000000..df3766250caa7 --- /dev/null +++ b/app/code/Magento/Integration/Plugin/Model/AdminUser.php @@ -0,0 +1,46 @@ +adminTokenService = $adminTokenService; + } + + /** + * Check if admin is inactive - if so, invalidate their tokens + * + * @param \Magento\User\Model\User $subject + * @param \Magento\Framework\DataObject $object + * @return $this + */ + public function afterSave( + \Magento\User\Model\User $subject, + \Magento\Framework\DataObject $object + ) { + $isActive = $object->getIsActive(); + if (isset($isActive) && $isActive == 0) { + $this->adminTokenService->revokeAdminAccessToken($object->getId()); + } + return $subject; + } +} diff --git a/app/code/Magento/Integration/Plugin/Model/CustomerUser.php b/app/code/Magento/Integration/Plugin/Model/CustomerUser.php new file mode 100644 index 0000000000000..537b66402397f --- /dev/null +++ b/app/code/Magento/Integration/Plugin/Model/CustomerUser.php @@ -0,0 +1,46 @@ +customerTokenService = $customerTokenService; + } + + /** + * Check if customer is inactive - if so, invalidate their tokens + * + * @param \Magento\Customer\Model\Customer $subject + * @param \Magento\Framework\DataObject $object + * @return $this + */ + public function afterSave( + \Magento\Customer\Model\Customer $subject, + \Magento\Framework\DataObject $object + ) { + $isActive = $object->getIsActive(); + if (isset($isActive) && $isActive == 0) { + $this->customerTokenService->revokeCustomerAccessToken($object->getId()); + } + return $subject; + } +} diff --git a/app/code/Magento/Integration/Setup/UpgradeData.php b/app/code/Magento/Integration/Setup/UpgradeData.php new file mode 100644 index 0000000000000..b376d6a2b8b1e --- /dev/null +++ b/app/code/Magento/Integration/Setup/UpgradeData.php @@ -0,0 +1,96 @@ +startSetup(); + + if (version_compare($context->getVersion(), '2.2.0', '<')) { + $this->removeRevokedTokens($setup); + $this->removeTokensFromInactiveAdmins($setup); + $this->removeTokensFromInactiveCustomers($setup); + } + + $setup->endSetup(); + } + + /** + * Remove any revoked tokens from oauth_token table + * + * @param ModuleDataSetupInterface $setup + * @return void + */ + private function removeRevokedTokens($setup) + { + $oauthTokenTable = $setup->getTable('oauth_token'); + + $where = ['revoked = ?' => 1]; + $setup->getConnection()->delete($oauthTokenTable, $where); + } + + /** + * Remove any tokens from oauth_token table where admin is inactive + * + * @param ModuleDataSetupInterface $setup + * @return void + */ + private function removeTokensFromInactiveAdmins($setup) + { + $oauthTokenTable = $setup->getTable('oauth_token'); + $adminUserTable = $setup->getTable('admin_user'); + + $select = $setup->getConnection()->select()->from( + $adminUserTable, + ['user_id', 'is_active'] + ); + + $admins = $setup->getConnection()->fetchAll($select); + foreach ($admins as $admin) { + if ($admin['is_active'] == 0) { + $where = ['admin_id = ?' => (int)$admin['user_id']]; + $setup->getConnection()->delete($oauthTokenTable, $where); + } + } + } + + /** + * Remove any tokens from oauth_token table where customer is inactive + * + * @param ModuleDataSetupInterface $setup + * @return void + */ + private function removeTokensFromInactiveCustomers($setup) + { + $oauthTokenTable = $setup->getTable('oauth_token'); + $adminUserTable = $setup->getTable('customer_entity'); + + $select = $setup->getConnection()->select()->from( + $adminUserTable, + ['entity_id', 'is_active'] + ); + + $admins = $setup->getConnection()->fetchAll($select); + foreach ($admins as $admin) { + if ($admin['is_active'] == 0) { + $where = ['customer_id = ?' => (int)$admin['entity_id']]; + $setup->getConnection()->delete($oauthTokenTable, $where); + } + } + } +} diff --git a/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php index 9a29c647d2295..52bff9ca06864 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/AdminTokenServiceTest.php @@ -13,6 +13,9 @@ use Magento\Integration\Model\Integration; use Magento\Integration\Model\Oauth\Token; +/** + * Test for \Magento\Integration\Model\AdminTokenService + */ class AdminTokenServiceTest extends \PHPUnit_Framework_TestCase { /** \Magento\Integration\Model\AdminTokenService */ @@ -50,7 +53,7 @@ protected function setUp() $this->_tokenMock = $this->getMockBuilder(\Magento\Integration\Model\Oauth\Token::class) ->disableOriginalConstructor() - ->setMethods(['getToken', 'loadByAdminId', 'setRevoked', 'save', '__wakeup'])->getMock(); + ->setMethods(['getToken', 'loadByAdminId', 'delete', '__wakeup'])->getMock(); $this->_tokenModelCollectionMock = $this->getMockBuilder( \Magento\Integration\Model\ResourceModel\Oauth\Token\Collection::class @@ -97,10 +100,8 @@ public function testRevokeAdminAccessToken() ->with(null) ->will($this->returnValue(1)); $this->_tokenMock->expects($this->once()) - ->method('setRevoked') + ->method('delete') ->will($this->returnValue($this->_tokenMock)); - $this->_tokenMock->expects($this->once()) - ->method('save'); $this->assertTrue($this->_tokenService->revokeAdminAccessToken($adminId)); } @@ -116,9 +117,7 @@ public function testRevokeAdminAccessTokenWithoutAdminId() ->with(null) ->will($this->returnValue($this->_tokenModelCollectionMock)); $this->_tokenMock->expects($this->never()) - ->method('save'); - $this->_tokenMock->expects($this->never()) - ->method('setRevoked') + ->method('delete') ->will($this->returnValue($this->_tokenMock)); $this->_tokenService->revokeAdminAccessToken(null); } @@ -142,10 +141,8 @@ public function testRevokeAdminAccessTokenCannotRevoked() ->method('getIterator') ->will($this->returnValue(new \ArrayIterator([$this->_tokenMock]))); - $this->_tokenMock->expects($this->never()) - ->method('save'); $this->_tokenMock->expects($this->once()) - ->method('setRevoked') + ->method('delete') ->will($this->throwException($exception)); $this->_tokenService->revokeAdminAccessToken($adminId); } diff --git a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php index 26e6c29f3be65..9bfe334cd7a99 100644 --- a/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php +++ b/app/code/Magento/Integration/Test/Unit/Model/CustomerTokenServiceTest.php @@ -11,6 +11,9 @@ use Magento\Integration\Model\Integration; use Magento\Integration\Model\Oauth\Token; +/** + * Test for \Magento\Integration\Model\CustomerTokenService + */ class CustomerTokenServiceTest extends \PHPUnit_Framework_TestCase { /** \Magento\Integration\Model\CustomerTokenService */ @@ -49,7 +52,7 @@ protected function setUp() $this->_tokenMock = $this->getMockBuilder(\Magento\Integration\Model\Oauth\Token::class) ->disableOriginalConstructor() - ->setMethods(['getToken', 'loadByCustomerId', 'setRevoked', 'save', '__wakeup'])->getMock(); + ->setMethods(['getToken', 'loadByCustomerId', 'delete', '__wakeup'])->getMock(); $this->_tokenModelCollectionMock = $this->getMockBuilder( \Magento\Integration\Model\ResourceModel\Oauth\Token\Collection::class @@ -95,10 +98,8 @@ public function testRevokeCustomerAccessToken() ->method('_fetchAll') ->will($this->returnValue(1)); $this->_tokenMock->expects($this->once()) - ->method('setRevoked') + ->method('delete') ->will($this->returnValue($this->_tokenMock)); - $this->_tokenMock->expects($this->once()) - ->method('save'); $this->assertTrue($this->_tokenService->revokeCustomerAccessToken($customerId)); } @@ -114,9 +115,7 @@ public function testRevokeCustomerAccessTokenWithoutCustomerId() ->with(null) ->will($this->returnValue($this->_tokenModelCollectionMock)); $this->_tokenMock->expects($this->never()) - ->method('save'); - $this->_tokenMock->expects($this->never()) - ->method('setRevoked') + ->method('delete') ->will($this->returnValue($this->_tokenMock)); $this->_tokenService->revokeCustomerAccessToken(null); } @@ -140,10 +139,8 @@ public function testRevokeCustomerAccessTokenCannotRevoked() ->method('getIterator') ->will($this->returnValue(new \ArrayIterator([$this->_tokenMock]))); - $this->_tokenMock->expects($this->never()) - ->method('save'); $this->_tokenMock->expects($this->once()) - ->method('setRevoked') + ->method('delete') ->will($this->throwException($exception)); $this->_tokenService->revokeCustomerAccessToken($customerId); } diff --git a/app/code/Magento/Integration/etc/di.xml b/app/code/Magento/Integration/etc/di.xml index f1c1e5b4fb7de..387770909bda6 100644 --- a/app/code/Magento/Integration/etc/di.xml +++ b/app/code/Magento/Integration/etc/di.xml @@ -29,4 +29,10 @@ + + + + + + diff --git a/app/code/Magento/Integration/etc/module.xml b/app/code/Magento/Integration/etc/module.xml index 076aaeb22afb5..c5e2aa75a8d05 100644 --- a/app/code/Magento/Integration/etc/module.xml +++ b/app/code/Magento/Integration/etc/module.xml @@ -6,7 +6,7 @@ */ --> - + diff --git a/dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php b/dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php index 4285b76bba772..3fb7b3417b679 100644 --- a/dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php +++ b/dev/tests/api-functional/testsuite/Magento/Integration/Model/AdminTokenServiceTest.php @@ -79,6 +79,19 @@ public function testCreateAdminAccessToken() $this->assertToken($adminUserNameFromFixture, $accessToken); } + /** + * Provider to test input validation + * + * @return array + */ + public function validationDataProvider() + { + return [ + 'Check for empty credentials' => ['', ''], + 'Check for null credentials' => [null, null] + ]; + } + /** * @dataProvider validationDataProvider */ @@ -95,8 +108,8 @@ public function testCreateAdminAccessTokenEmptyOrNullCredentials() $requestData = ['username' => '', 'password' => '']; $this->_webApiCall($serviceInfo, $requestData); $noExceptionOccurred = true; - } catch (\Exception $e) { - $this->assertInputExceptionMessages($e); + } catch (\Exception $exception) { + $this->assertInputExceptionMessages($exception); } if ($noExceptionOccurred) { $this->fail("Exception was expected to be thrown when provided credentials are invalid."); @@ -118,8 +131,8 @@ public function testCreateAdminAccessTokenInvalidCredentials() $requestData = ['username' => $customerUserName, 'password' => $password]; $this->_webApiCall($serviceInfo, $requestData); $noExceptionOccurred = true; - } catch (\Exception $e) { - $this->assertInvalidCredentialsException($e); + } catch (\Exception $exception) { + $this->assertInvalidCredentialsException($exception); } if ($noExceptionOccurred) { $this->fail("Exception was expected to be thrown when provided credentials are invalid."); @@ -127,16 +140,63 @@ public function testCreateAdminAccessTokenInvalidCredentials() } /** - * Provider to test input validation - * - * @return array + * @magentoApiDataFixture Magento/Webapi/_files/webapi_user.php */ - public function validationDataProvider() + public function testUseAdminAccessTokenInactiveAdmin() { - return [ - 'Check for empty credentials' => ['', ''], - 'Check for null credentials' => [null, null] + $adminUserNameFromFixture = 'webapi_user'; + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => self::RESOURCE_PATH_ADMIN_TOKEN, + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST, + ], + ]; + $requestData = [ + 'username' => $adminUserNameFromFixture, + 'password' => \Magento\TestFramework\Bootstrap::ADMIN_PASSWORD, ]; + $accessToken = $this->_webApiCall($serviceInfo, $requestData); + $this->assertToken($adminUserNameFromFixture, $accessToken); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/store/storeConfigs', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'token' => $accessToken + ] + ]; + $requestData = [ + 'storeCodes' => ['default'], + ]; + $storeConfigs = $this->_webApiCall($serviceInfo, $requestData); + $this->assertNotNull($storeConfigs); + + $adminUser = $this->userModel->loadByUsername($adminUserNameFromFixture); + $adminUser->setData("is_active", 0); + $adminUser->save(); + + $serviceInfo = [ + 'rest' => [ + 'resourcePath' => '/V1/store/storeConfigs', + 'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET, + 'token' => $accessToken + ] + ]; + $requestData = [ + 'storeCodes' => ['default'], + ]; + + $noExceptionOccurred = false; + try { + $this->_webApiCall($serviceInfo, $requestData); + $noExceptionOccurred = true; + } catch (\Exception $exception) { + $this->assertUnauthorizedAccessException($exception); + } + if ($noExceptionOccurred) { + $this->fail("Exception was expected to be thrown when provided token is expired."); + } } /** @@ -167,7 +227,7 @@ public function testThrottlingMaxAttempts() try { $this->_webApiCall($serviceInfo, $invalidCredentials); $noExceptionOccurred = true; - } catch (\Exception $e) { + } catch (\Exception $exception) { } } if ($noExceptionOccurred) { @@ -209,8 +269,8 @@ public function testThrottlingAccountLockout() try { $this->_webApiCall($serviceInfo, $invalidCredentials); $noExceptionOccurred = true; - } catch (\Exception $e) { - $this->assertInvalidCredentialsException($e); + } catch (\Exception $exception) { + $this->assertInvalidCredentialsException($exception); } if ($noExceptionOccurred) { $this->fail("Exception was expected to be thrown when provided credentials are invalid."); @@ -221,8 +281,8 @@ public function testThrottlingAccountLockout() try { $this->_webApiCall($serviceInfo, $validCredentials); $noExceptionOccurred = true; - } catch (\Exception $e) { - $this->assertInvalidCredentialsException($e); + } catch (\Exception $exception) { + $this->assertInvalidCredentialsException($exception); } if ($noExceptionOccurred) { $this->fail("Exception was expected to be thrown because account should have been locked at this point."); @@ -232,12 +292,12 @@ public function testThrottlingAccountLockout() /** * Assert for presence of Input exception messages * - * @param \Exception $e + * @param \Exception $exception */ - private function assertInputExceptionMessages($e) + private function assertInputExceptionMessages($exception) { - $this->assertEquals(HTTPExceptionCodes::HTTP_BAD_REQUEST, $e->getCode()); - $exceptionData = $this->processRestExceptionResult($e); + $this->assertEquals(HTTPExceptionCodes::HTTP_BAD_REQUEST, $exception->getCode()); + $exceptionData = $this->processRestExceptionResult($exception); $expectedExceptionData = [ 'message' => 'One or more input exceptions have occurred.', 'errors' => [ @@ -261,18 +321,44 @@ private function assertInputExceptionMessages($e) /** * Make sure that status code and message are correct in case of authentication failure. * - * @param \Exception $e + * @param \Exception $exception */ - private function assertInvalidCredentialsException($e) + private function assertInvalidCredentialsException($exception) { - $this->assertEquals(HTTPExceptionCodes::HTTP_UNAUTHORIZED, $e->getCode(), "Response HTTP code is invalid."); - $exceptionData = $this->processRestExceptionResult($e); + $this->assertEquals( + HTTPExceptionCodes::HTTP_UNAUTHORIZED, + $exception->getCode(), + "Response HTTP code is invalid." + ); + $exceptionData = $this->processRestExceptionResult($exception); $expectedExceptionData = [ 'message' => 'You did not sign in correctly or your account is temporarily disabled.' ]; $this->assertEquals($expectedExceptionData, $exceptionData, "Exception message is invalid."); } + /** + * Make sure that status code and message are correct in case of authentication failure. + * + * @param \Exception $exception + */ + private function assertUnauthorizedAccessException($exception) + { + $this->assertEquals( + HTTPExceptionCodes::HTTP_UNAUTHORIZED, + $exception->getCode(), + "Response HTTP code is invalid." + ); + $exceptionData = $this->processRestExceptionResult($exception); + $expectedExceptionData = [ + 'message' => 'Consumer is not authorized to access %resources', + 'parameters' => [ + 'resources' => 'Magento_Backend::store' + ] + ]; + $this->assertEquals($expectedExceptionData, $exceptionData, "Exception message is invalid."); + } + /** * Make sure provided token is valid and belongs to the specified user. * diff --git a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php index 56fa848930862..fefb0dad0f319 100644 --- a/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Controller/Adminhtml/User/InvalidateTokenTest.php @@ -39,7 +39,7 @@ public function testInvalidateSingleToken() $this->getRequest()->setParam('user_id', $adminUserId); $this->dispatch('backend/admin/user/invalidateToken'); $token = $tokenModel->loadByAdminId($adminUserId); - $this->assertEquals(1, $token->getRevoked()); + $this->assertEquals(null, $token->getId()); } /** diff --git a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php index 32d6f4c352a59..14bbf8af826f4 100644 --- a/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php +++ b/dev/tests/integration/testsuite/Magento/User/Model/UserTest.php @@ -231,6 +231,7 @@ public function testAuthenticateCaseInsensitive() } /** + * @expectedException \Magento\Framework\Exception\LocalizedException * @expectedException \Magento\Framework\Exception\AuthenticationException * @magentoDbIsolation enabled */ diff --git a/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php b/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php index 68962bb2478cb..8aa089f929e65 100644 --- a/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php +++ b/dev/tests/integration/testsuite/Magento/Webapi/_files/webapi_user.php @@ -19,6 +19,6 @@ ->setResourceId('Magento_Backend::all') ->setPrivileges("") ->setAssertId(0) - ->setRoleId(1) + ->setRoleId(2) ->setPermission('allow'); $model->save(); diff --git a/setup/performance-toolkit/benchmark.jmx b/setup/performance-toolkit/benchmark.jmx index aa7a160ebe72f..d9b1a94b12e2d 100644 --- a/setup/performance-toolkit/benchmark.jmx +++ b/setup/performance-toolkit/benchmark.jmx @@ -2305,6 +2305,56 @@ vars.put("testLabel", "CatProdBrows"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -2343,6 +2393,56 @@ vars.put("testLabel", "CatProdBrows"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -2679,6 +2779,56 @@ vars.put("loadType", "Guest"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -2865,6 +3015,56 @@ vars.put("loadType", "Guest"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -3508,6 +3708,56 @@ vars.put("loadType", "Guest"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -3694,6 +3944,56 @@ vars.put("loadType", "Guest"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -4879,6 +5179,56 @@ vars.put("loadType", "Customer"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + + @@ -5065,6 +5415,56 @@ vars.put("loadType", "Customer"); 2 + + + product_id + .//input[@type="hidden" and @name="product"]/@value + false + true + false + + + + + ^\d+$ + + Assertion.response_data + false + 1 + variable + product_id + + + + + + + + + + + + + + ${base_path}review/product/listAjax/id/${product_id}/ + GET + true + false + true + false + false + + + + + + 200 + + Assertion.response_code + false + 2 + +