Url.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Backend\Model;
  7. use Magento\Framework\Serialize\Serializer\Json;
  8. use Magento\Framework\Url\HostChecker;
  9. use Magento\Framework\App\ObjectManager;
  10. /**
  11. * Class \Magento\Backend\Model\UrlInterface
  12. *
  13. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  14. * @api
  15. * @since 100.0.2
  16. */
  17. class Url extends \Magento\Framework\Url implements \Magento\Backend\Model\UrlInterface
  18. {
  19. /**
  20. * Whether to use a security key in the backend
  21. *
  22. * @bug Currently, this constant is slightly misleading: it says "form key", but in fact it is used by URLs, too
  23. */
  24. const XML_PATH_USE_SECURE_KEY = 'admin/security/use_form_key';
  25. /**
  26. * Authentication session
  27. *
  28. * @var \Magento\Backend\Model\Auth\Session
  29. */
  30. protected $_session;
  31. /**
  32. * @var \Magento\Backend\Model\Menu
  33. */
  34. protected $_menu;
  35. /**
  36. * Startup page url from config
  37. *
  38. * @var string
  39. */
  40. protected $_startupMenuItemId;
  41. /**
  42. * @var \Magento\Backend\Helper\Data
  43. */
  44. protected $_backendHelper;
  45. /**
  46. * Menu config
  47. *
  48. * @var \Magento\Backend\Model\Menu\Config
  49. */
  50. protected $_menuConfig;
  51. /**
  52. * @var \Magento\Framework\App\CacheInterface
  53. */
  54. protected $_cache;
  55. /**
  56. * @var \Magento\Framework\Encryption\EncryptorInterface
  57. */
  58. protected $_encryptor;
  59. /**
  60. * @var \Magento\Store\Model\StoreFactory
  61. */
  62. protected $_storeFactory;
  63. /**
  64. * @var \Magento\Framework\Data\Form\FormKey
  65. */
  66. protected $formKey;
  67. /**
  68. * @var \Magento\Store\Model\Store
  69. */
  70. protected $_scope;
  71. /**
  72. * Constructor
  73. *
  74. * @param \Magento\Framework\App\Route\ConfigInterface $routeConfig
  75. * @param \Magento\Framework\App\RequestInterface $request
  76. * @param \Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo
  77. * @param \Magento\Framework\Url\ScopeResolverInterface $scopeResolver
  78. * @param \Magento\Framework\Session\Generic $session
  79. * @param \Magento\Framework\Session\SidResolverInterface $sidResolver
  80. * @param \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory
  81. * @param \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver
  82. * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
  83. * @param \Magento\Framework\Url\RouteParamsPreprocessorInterface $routeParamsPreprocessor
  84. * @param string $scopeType
  85. * @param \Magento\Backend\Helper\Data $backendHelper
  86. * @param Menu\Config $menuConfig
  87. * @param \Magento\Framework\App\CacheInterface $cache
  88. * @param Auth\Session $authSession
  89. * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
  90. * @param \Magento\Store\Model\StoreFactory $storeFactory
  91. * @param \Magento\Framework\Data\Form\FormKey $formKey
  92. * @param array $data
  93. * @param HostChecker|null $hostChecker
  94. * @param Json $serializer
  95. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  96. */
  97. public function __construct(
  98. \Magento\Framework\App\Route\ConfigInterface $routeConfig,
  99. \Magento\Framework\App\RequestInterface $request,
  100. \Magento\Framework\Url\SecurityInfoInterface $urlSecurityInfo,
  101. \Magento\Framework\Url\ScopeResolverInterface $scopeResolver,
  102. \Magento\Framework\Session\Generic $session,
  103. \Magento\Framework\Session\SidResolverInterface $sidResolver,
  104. \Magento\Framework\Url\RouteParamsResolverFactory $routeParamsResolverFactory,
  105. \Magento\Framework\Url\QueryParamsResolverInterface $queryParamsResolver,
  106. \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
  107. \Magento\Framework\Url\RouteParamsPreprocessorInterface $routeParamsPreprocessor,
  108. $scopeType,
  109. \Magento\Backend\Helper\Data $backendHelper,
  110. \Magento\Backend\Model\Menu\Config $menuConfig,
  111. \Magento\Framework\App\CacheInterface $cache,
  112. \Magento\Backend\Model\Auth\Session $authSession,
  113. \Magento\Framework\Encryption\EncryptorInterface $encryptor,
  114. \Magento\Store\Model\StoreFactory $storeFactory,
  115. \Magento\Framework\Data\Form\FormKey $formKey,
  116. array $data = [],
  117. HostChecker $hostChecker = null,
  118. Json $serializer = null
  119. ) {
  120. $this->_encryptor = $encryptor;
  121. $hostChecker = $hostChecker ?: ObjectManager::getInstance()->get(HostChecker::class);
  122. parent::__construct(
  123. $routeConfig,
  124. $request,
  125. $urlSecurityInfo,
  126. $scopeResolver,
  127. $session,
  128. $sidResolver,
  129. $routeParamsResolverFactory,
  130. $queryParamsResolver,
  131. $scopeConfig,
  132. $routeParamsPreprocessor,
  133. $scopeType,
  134. $data,
  135. $hostChecker,
  136. $serializer
  137. );
  138. $this->_backendHelper = $backendHelper;
  139. $this->_menuConfig = $menuConfig;
  140. $this->_cache = $cache;
  141. $this->_session = $authSession;
  142. $this->formKey = $formKey;
  143. $this->_storeFactory = $storeFactory;
  144. }
  145. /**
  146. * Retrieve is secure mode for ULR logic
  147. *
  148. * @return bool
  149. */
  150. protected function _isSecure()
  151. {
  152. if ($this->hasData('secure_is_forced')) {
  153. return $this->getData('secure');
  154. }
  155. return $this->_scopeConfig->isSetFlag('web/secure/use_in_adminhtml');
  156. }
  157. /**
  158. * Force strip secret key param if _nosecret param specified
  159. *
  160. * @param array $data
  161. * @param bool $unsetOldParams
  162. * @return $this
  163. */
  164. protected function _setRouteParams(array $data, $unsetOldParams = true)
  165. {
  166. if (isset($data['_nosecret'])) {
  167. $this->setNoSecret(true);
  168. unset($data['_nosecret']);
  169. } else {
  170. $this->setNoSecret(false);
  171. }
  172. unset($data['_scope_to_url']);
  173. return parent::_setRouteParams($data, $unsetOldParams);
  174. }
  175. /**
  176. * Custom logic to retrieve Urls
  177. *
  178. * @param string $routePath
  179. * @param array $routeParams
  180. * @return string
  181. */
  182. public function getUrl($routePath = null, $routeParams = null)
  183. {
  184. if (filter_var($routePath, FILTER_VALIDATE_URL)) {
  185. return $routePath;
  186. }
  187. $cacheSecretKey = false;
  188. if (isset($routeParams['_cache_secret_key'])) {
  189. unset($routeParams['_cache_secret_key']);
  190. $cacheSecretKey = true;
  191. }
  192. $result = parent::getUrl($routePath, $routeParams);
  193. if (!$this->useSecretKey()) {
  194. return $result;
  195. }
  196. $this->getRouteParamsResolver()->unsetData('route_params');
  197. $this->_setRoutePath($routePath);
  198. $extraParams = $this->getRouteParamsResolver()->getRouteParams();
  199. $routeName = $this->_getRouteName('*');
  200. $controllerName = $this->_getControllerName(self::DEFAULT_CONTROLLER_NAME);
  201. $actionName = $this->_getActionName(self::DEFAULT_ACTION_NAME);
  202. if (!isset($routeParams[self::SECRET_KEY_PARAM_NAME])) {
  203. if (!is_array($routeParams)) {
  204. $routeParams = [];
  205. }
  206. $secretKey = $cacheSecretKey
  207. ? "\${$routeName}/{$controllerName}/{$actionName}\$"
  208. : $this->getSecretKey($routeName, $controllerName, $actionName);
  209. $routeParams[self::SECRET_KEY_PARAM_NAME] = $secretKey;
  210. }
  211. if (!empty($extraParams)) {
  212. $routeParams = array_merge($extraParams, $routeParams);
  213. }
  214. return parent::getUrl("{$routeName}/{$controllerName}/{$actionName}", $routeParams);
  215. }
  216. /**
  217. * Generate secret key for controller and action based on form key
  218. *
  219. * @param string $routeName
  220. * @param string $controller Controller name
  221. * @param string $action Action name
  222. * @return string
  223. */
  224. public function getSecretKey($routeName = null, $controller = null, $action = null)
  225. {
  226. $salt = $this->formKey->getFormKey();
  227. $request = $this->_getRequest();
  228. if (!$routeName) {
  229. if ($request->getBeforeForwardInfo('route_name') !== null) {
  230. $routeName = $request->getBeforeForwardInfo('route_name');
  231. } else {
  232. $routeName = $request->getRouteName();
  233. }
  234. }
  235. if (!$controller) {
  236. if ($request->getBeforeForwardInfo('controller_name') !== null) {
  237. $controller = $request->getBeforeForwardInfo('controller_name');
  238. } else {
  239. $controller = $request->getControllerName();
  240. }
  241. }
  242. if (!$action) {
  243. if ($request->getBeforeForwardInfo('action_name') !== null) {
  244. $action = $request->getBeforeForwardInfo('action_name');
  245. } else {
  246. $action = $request->getActionName();
  247. }
  248. }
  249. $secret = $routeName . $controller . $action . $salt;
  250. return $this->_encryptor->getHash($secret);
  251. }
  252. /**
  253. * Return secret key settings flag
  254. *
  255. * @return bool
  256. */
  257. public function useSecretKey()
  258. {
  259. return $this->_scopeConfig->isSetFlag(self::XML_PATH_USE_SECURE_KEY) && !$this->getNoSecret();
  260. }
  261. /**
  262. * Enable secret key using
  263. *
  264. * @return $this
  265. */
  266. public function turnOnSecretKey()
  267. {
  268. $this->setNoSecret(false);
  269. return $this;
  270. }
  271. /**
  272. * Disable secret key using
  273. *
  274. * @return $this
  275. */
  276. public function turnOffSecretKey()
  277. {
  278. $this->setNoSecret(true);
  279. return $this;
  280. }
  281. /**
  282. * Refresh admin menu cache etc.
  283. *
  284. * @return void
  285. */
  286. public function renewSecretUrls()
  287. {
  288. $this->_cache->clean([\Magento\Backend\Block\Menu::CACHE_TAGS]);
  289. }
  290. /**
  291. * Find admin start page url
  292. *
  293. * @return string
  294. */
  295. public function getStartupPageUrl()
  296. {
  297. $menuItem = $this->_getMenu()->get(
  298. $this->_scopeConfig->getValue(self::XML_PATH_STARTUP_MENU_ITEM, $this->_scopeType)
  299. );
  300. if ($menuItem !== null) {
  301. if ($menuItem->isAllowed() && $menuItem->getAction()) {
  302. return $menuItem->getAction();
  303. }
  304. }
  305. return $this->findFirstAvailableMenu();
  306. }
  307. /**
  308. * Find first menu item that user is able to access
  309. *
  310. * @return string
  311. */
  312. public function findFirstAvailableMenu()
  313. {
  314. /* @var $menu \Magento\Backend\Model\Menu\Item */
  315. $menu = $this->_getMenu();
  316. $item = $menu->getFirstAvailable();
  317. $action = $item ? $item->getAction() : null;
  318. if (!$item) {
  319. $user = $this->_getSession()->getUser();
  320. if ($user) {
  321. $user->setHasAvailableResources(false);
  322. }
  323. $action = '*/*/denied';
  324. }
  325. return $action;
  326. }
  327. /**
  328. * Get Menu model
  329. *
  330. * @return \Magento\Backend\Model\Menu
  331. */
  332. protected function _getMenu()
  333. {
  334. if ($this->_menu === null) {
  335. $this->_menu = $this->_menuConfig->getMenu();
  336. }
  337. return $this->_menu;
  338. }
  339. /**
  340. * Set custom auth session
  341. *
  342. * @param \Magento\Backend\Model\Auth\Session $session
  343. * @return $this
  344. */
  345. public function setSession(\Magento\Backend\Model\Auth\Session $session)
  346. {
  347. $this->_session = $session;
  348. return $this;
  349. }
  350. /**
  351. * Retrieve auth session
  352. *
  353. * @return \Magento\Backend\Model\Auth\Session
  354. */
  355. protected function _getSession()
  356. {
  357. return $this->_session;
  358. }
  359. /**
  360. * Return backend area front name, defined in configuration
  361. *
  362. * @return string
  363. */
  364. public function getAreaFrontName()
  365. {
  366. if (!$this->_getData('area_front_name')) {
  367. $this->setData('area_front_name', $this->_backendHelper->getAreaFrontName());
  368. }
  369. return $this->_getData('area_front_name');
  370. }
  371. /**
  372. * Retrieve action path.
  373. * Add backend area front name as a prefix to action path
  374. *
  375. * @return string
  376. */
  377. protected function _getActionPath()
  378. {
  379. $path = parent::_getActionPath();
  380. if ($path) {
  381. if ($this->getAreaFrontName()) {
  382. $path = $this->getAreaFrontName() . '/' . $path;
  383. }
  384. }
  385. return $path;
  386. }
  387. /**
  388. * Get scope for the url instance
  389. *
  390. * @return \Magento\Store\Model\Store
  391. */
  392. protected function _getScope()
  393. {
  394. if (!$this->_scope) {
  395. $this->_scope = $this->_storeFactory->create(
  396. [
  397. 'url' => $this,
  398. 'data' => ['code' => 'admin', 'force_disable_rewrites' => false, 'disable_store_in_url' => true],
  399. ]
  400. );
  401. }
  402. return $this->_scope;
  403. }
  404. /**
  405. * Get cache id for config path
  406. *
  407. * @param string $path
  408. * @return string
  409. */
  410. protected function _getConfigCacheId($path)
  411. {
  412. return 'admin/' . $path;
  413. }
  414. /**
  415. * Get config data by path
  416. * Use only global config values for backend
  417. *
  418. * @param string $path
  419. * @return null|string
  420. */
  421. protected function _getConfig($path)
  422. {
  423. return $this->_scopeConfig->getValue($path);
  424. }
  425. }