AdminSessionsManager.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Security\Model;
  7. use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress;
  8. use \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory;
  9. /**
  10. * Admin Sessions Manager Model
  11. *
  12. * @api
  13. * @since 100.1.0
  14. */
  15. class AdminSessionsManager
  16. {
  17. /**
  18. * Admin Session lifetime (sec)
  19. */
  20. const ADMIN_SESSION_LIFETIME = 86400;
  21. /**
  22. * Logout reason when current user has been locked out
  23. */
  24. const LOGOUT_REASON_USER_LOCKED = 10;
  25. /**
  26. * @var ConfigInterface
  27. * @since 100.1.0
  28. */
  29. protected $securityConfig;
  30. /**
  31. * @var \Magento\Backend\Model\Auth\Session
  32. * @since 100.1.0
  33. */
  34. protected $authSession;
  35. /**
  36. * @var AdminSessionInfoFactory
  37. * @since 100.1.0
  38. */
  39. protected $adminSessionInfoFactory;
  40. /**
  41. * @var \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory
  42. * @since 100.1.0
  43. */
  44. protected $adminSessionInfoCollectionFactory;
  45. /**
  46. * @var \Magento\Security\Model\AdminSessionInfo
  47. * @since 100.1.0
  48. */
  49. protected $currentSession;
  50. /**
  51. * @var \Magento\Framework\Stdlib\DateTime\DateTime
  52. */
  53. private $dateTime;
  54. /**
  55. * @var RemoteAddress
  56. */
  57. private $remoteAddress;
  58. /**
  59. * Max lifetime for session prolong to be valid (sec)
  60. *
  61. * Means that after session was prolonged
  62. * all other prolongs will be ignored within this period
  63. */
  64. private $maxIntervalBetweenConsecutiveProlongs = 60;
  65. /**
  66. * @param ConfigInterface $securityConfig
  67. * @param \Magento\Backend\Model\Auth\Session $authSession
  68. * @param AdminSessionInfoFactory $adminSessionInfoFactory
  69. * @param CollectionFactory $adminSessionInfoCollectionFactory
  70. * @param \Magento\Framework\Stdlib\DateTime\DateTime $dateTime
  71. * @param RemoteAddress $remoteAddress
  72. */
  73. public function __construct(
  74. ConfigInterface $securityConfig,
  75. \Magento\Backend\Model\Auth\Session $authSession,
  76. \Magento\Security\Model\AdminSessionInfoFactory $adminSessionInfoFactory,
  77. \Magento\Security\Model\ResourceModel\AdminSessionInfo\CollectionFactory $adminSessionInfoCollectionFactory,
  78. \Magento\Framework\Stdlib\DateTime\DateTime $dateTime,
  79. RemoteAddress $remoteAddress
  80. ) {
  81. $this->securityConfig = $securityConfig;
  82. $this->authSession = $authSession;
  83. $this->adminSessionInfoFactory = $adminSessionInfoFactory;
  84. $this->adminSessionInfoCollectionFactory = $adminSessionInfoCollectionFactory;
  85. $this->dateTime = $dateTime;
  86. $this->remoteAddress = $remoteAddress;
  87. }
  88. /**
  89. * Handle all others active sessions according Sharing Account Setting
  90. *
  91. * @return $this
  92. * @since 100.1.0
  93. */
  94. public function processLogin()
  95. {
  96. $this->createNewSession();
  97. $olderThen = $this->dateTime->gmtTimestamp() - $this->securityConfig->getAdminSessionLifetime();
  98. if (!$this->securityConfig->isAdminAccountSharingEnabled()) {
  99. $result = $this->createAdminSessionInfoCollection()->updateActiveSessionsStatus(
  100. AdminSessionInfo::LOGGED_OUT_BY_LOGIN,
  101. $this->getCurrentSession()->getUserId(),
  102. $this->getCurrentSession()->getSessionId(),
  103. $olderThen
  104. );
  105. if ($result) {
  106. $this->getCurrentSession()->setIsOtherSessionsTerminated(true);
  107. }
  108. }
  109. return $this;
  110. }
  111. /**
  112. * Handle Prolong process
  113. *
  114. * @return $this
  115. * @since 100.1.0
  116. */
  117. public function processProlong()
  118. {
  119. if ($this->lastProlongIsOldEnough()) {
  120. $this->getCurrentSession()->setData(
  121. 'updated_at',
  122. date(
  123. \Magento\Framework\Stdlib\DateTime::DATETIME_PHP_FORMAT,
  124. $this->authSession->getUpdatedAt()
  125. )
  126. );
  127. $this->getCurrentSession()->save();
  128. }
  129. return $this;
  130. }
  131. /**
  132. * Handle logout process
  133. *
  134. * @return $this
  135. * @since 100.1.0
  136. */
  137. public function processLogout()
  138. {
  139. $this->getCurrentSession()->setData(
  140. 'status',
  141. AdminSessionInfo::LOGGED_OUT
  142. );
  143. $this->getCurrentSession()->save();
  144. return $this;
  145. }
  146. /**
  147. * Get current session record
  148. *
  149. * @return AdminSessionInfo
  150. * @since 100.1.0
  151. */
  152. public function getCurrentSession()
  153. {
  154. if (!$this->currentSession) {
  155. $this->currentSession = $this->adminSessionInfoFactory->create();
  156. $this->currentSession->load($this->authSession->getSessionId(), 'session_id');
  157. }
  158. return $this->currentSession;
  159. }
  160. /**
  161. * Get logout reason message by status
  162. *
  163. * @param int $statusCode
  164. * @return string
  165. * @since 100.1.0
  166. */
  167. public function getLogoutReasonMessageByStatus($statusCode)
  168. {
  169. switch ((int)$statusCode) {
  170. case AdminSessionInfo::LOGGED_IN:
  171. $reasonMessage = null;
  172. break;
  173. case AdminSessionInfo::LOGGED_OUT_BY_LOGIN:
  174. $reasonMessage = __(
  175. 'Someone logged into this account from another device or browser.'
  176. .' Your current session is terminated.'
  177. );
  178. break;
  179. case AdminSessionInfo::LOGGED_OUT_MANUALLY:
  180. $reasonMessage = __(
  181. 'Your current session is terminated by another user of this account.'
  182. );
  183. break;
  184. case self::LOGOUT_REASON_USER_LOCKED:
  185. $reasonMessage = __(
  186. 'Your account is temporarily disabled. Please try again later.'
  187. );
  188. break;
  189. default:
  190. $reasonMessage = __('Your current session has been expired.');
  191. break;
  192. }
  193. return $reasonMessage;
  194. }
  195. /**
  196. * Get message with explanation of logout reason
  197. *
  198. * @return string
  199. * @since 100.1.0
  200. */
  201. public function getLogoutReasonMessage()
  202. {
  203. return $this->getLogoutReasonMessageByStatus(
  204. $this->getCurrentSession()->getStatus()
  205. );
  206. }
  207. /**
  208. * Get sessions for current user
  209. *
  210. * @return \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection
  211. * @since 100.1.0
  212. */
  213. public function getSessionsForCurrentUser()
  214. {
  215. return $this->createAdminSessionInfoCollection()
  216. ->filterByUser($this->authSession->getUser()->getId(), \Magento\Security\Model\AdminSessionInfo::LOGGED_IN)
  217. ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime())
  218. ->loadData();
  219. }
  220. /**
  221. * Logout another user sessions
  222. *
  223. * @return $this
  224. * @since 100.1.0
  225. */
  226. public function logoutOtherUserSessions()
  227. {
  228. $collection = $this->createAdminSessionInfoCollection()
  229. ->filterByUser(
  230. $this->authSession->getUser()->getId(),
  231. \Magento\Security\Model\AdminSessionInfo::LOGGED_IN,
  232. $this->authSession->getSessionId()
  233. )
  234. ->filterExpiredSessions($this->securityConfig->getAdminSessionLifetime())
  235. ->loadData();
  236. $collection->setDataToAll('status', \Magento\Security\Model\AdminSessionInfo::LOGGED_OUT_MANUALLY)
  237. ->save();
  238. return $this;
  239. }
  240. /**
  241. * Clean expired Admin Sessions
  242. *
  243. * @return $this
  244. * @since 100.1.0
  245. */
  246. public function cleanExpiredSessions()
  247. {
  248. $this->createAdminSessionInfoCollection()->deleteSessionsOlderThen(
  249. $this->dateTime->gmtTimestamp() - self::ADMIN_SESSION_LIFETIME
  250. );
  251. return $this;
  252. }
  253. /**
  254. * Create new record
  255. *
  256. * @return $this
  257. * @since 100.1.0
  258. */
  259. protected function createNewSession()
  260. {
  261. $this->adminSessionInfoFactory
  262. ->create()
  263. ->setData(
  264. [
  265. 'session_id' => $this->authSession->getSessionId(),
  266. 'user_id' => $this->authSession->getUser()->getId(),
  267. 'ip' => $this->remoteAddress->getRemoteAddress(),
  268. 'status' => AdminSessionInfo::LOGGED_IN
  269. ]
  270. )->save();
  271. return $this;
  272. }
  273. /**
  274. * @return \Magento\Security\Model\ResourceModel\AdminSessionInfo\Collection
  275. * @since 100.1.0
  276. */
  277. protected function createAdminSessionInfoCollection()
  278. {
  279. return $this->adminSessionInfoCollectionFactory->create();
  280. }
  281. /**
  282. * Calculates diff between now and last session updated_at
  283. * and decides whether new prolong must be triggered or not
  284. *
  285. * This is done to limit amount of session prolongs and updates to database
  286. * within some period of time - X
  287. * X - is calculated in getIntervalBetweenConsecutiveProlongs()
  288. *
  289. * @see getIntervalBetweenConsecutiveProlongs()
  290. * @return bool
  291. */
  292. private function lastProlongIsOldEnough()
  293. {
  294. $lastProlongTimestamp = strtotime($this->getCurrentSession()->getUpdatedAt());
  295. $nowTimestamp = $this->authSession->getUpdatedAt();
  296. $diff = $nowTimestamp - $lastProlongTimestamp;
  297. return (float) $diff > $this->getIntervalBetweenConsecutiveProlongs();
  298. }
  299. /**
  300. * Calculates lifetime for session prolong to be valid
  301. *
  302. * Calculation is based on admin session lifetime
  303. * Calculated result is in seconds and is in the interval
  304. * between 1 (including) and MAX_INTERVAL_BETWEEN_CONSECUTIVE_PROLONGS (including)
  305. *
  306. * @return float
  307. */
  308. private function getIntervalBetweenConsecutiveProlongs()
  309. {
  310. return (float) max(
  311. 1,
  312. min(
  313. 4 * log((float)$this->securityConfig->getAdminSessionLifetime()),
  314. $this->maxIntervalBetweenConsecutiveProlongs
  315. )
  316. );
  317. }
  318. }