vendor/pimcore/pimcore/lib/Tool/Authentication.php line 63

Open in your IDE?
  1. <?php
  2. /**
  3. * Pimcore
  4. *
  5. * This source file is available under two different licenses:
  6. * - GNU General Public License version 3 (GPLv3)
  7. * - Pimcore Commercial License (PCL)
  8. * Full copyright and license information is available in
  9. * LICENSE.md which is distributed with this source code.
  10. *
  11. * @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
  12. * @license http://www.pimcore.org/license GPLv3 and PCL
  13. */
  14. namespace Pimcore\Tool;
  15. use Defuse\Crypto\Crypto;
  16. use Defuse\Crypto\Exception\CryptoException;
  17. use Pimcore\Logger;
  18. use Pimcore\Model\User;
  19. use Pimcore\Tool;
  20. use Symfony\Component\HttpFoundation\Request;
  21. class Authentication
  22. {
  23. /**
  24. * @deprecated
  25. *
  26. * @param string $username
  27. * @param string $password
  28. *
  29. * @return null|User
  30. */
  31. public static function authenticatePlaintext($username, $password)
  32. {
  33. trigger_deprecation(
  34. 'pimcore/pimcore',
  35. '10.6',
  36. sprintf('%s is deprecated and will be removed in Pimcore 11', __METHOD__),
  37. );
  38. /** @var User $user */
  39. $user = User::getByName($username);
  40. // user needs to be active, needs a password and an ID (do not allow system user to login, ...)
  41. if (self::isValidUser($user)) {
  42. if (self::verifyPassword($user, $password)) {
  43. $user->setLastLoginDate(); //set user current login date
  44. return $user;
  45. }
  46. }
  47. return null;
  48. }
  49. /**
  50. * @param Request|null $request
  51. *
  52. * @return User|null
  53. */
  54. public static function authenticateSession(Request $request = null)
  55. {
  56. if (null === $request) {
  57. $request = \Pimcore::getContainer()->get('request_stack')->getCurrentRequest();
  58. if (null === $request) {
  59. return null;
  60. }
  61. }
  62. if (!Session::requestHasSessionId($request, true)) {
  63. // if no session cookie / ID no authentication possible, we don't need to start a session
  64. return null;
  65. }
  66. $session = Session::getReadOnly();
  67. $user = $session->get('user');
  68. if ($user instanceof User) {
  69. // renew user
  70. $user = User::getById($user->getId());
  71. if (self::isValidUser($user)) {
  72. return $user;
  73. }
  74. }
  75. return null;
  76. }
  77. /**
  78. * @deprecated
  79. *
  80. * @throws \Exception
  81. *
  82. * @return User
  83. */
  84. public static function authenticateHttpBasic()
  85. {
  86. trigger_deprecation(
  87. 'pimcore/pimcore',
  88. '10.6',
  89. sprintf('%s is deprecated and will be removed in Pimcore 11', __METHOD__),
  90. );
  91. // we're using Sabre\HTTP for basic auth
  92. $request = \Sabre\HTTP\Sapi::getRequest();
  93. $response = new \Sabre\HTTP\Response();
  94. $auth = new \Sabre\HTTP\Auth\Basic(Tool::getHostname(), $request, $response);
  95. $result = $auth->getCredentials();
  96. if (is_array($result)) {
  97. list($username, $password) = $result;
  98. $user = self::authenticatePlaintext($username, $password);
  99. if ($user) {
  100. return $user;
  101. }
  102. }
  103. $auth->requireLogin();
  104. $response->setBody('Authentication required');
  105. Logger::error('Authentication Basic (WebDAV) required');
  106. \Sabre\HTTP\Sapi::sendResponse($response);
  107. die();
  108. }
  109. /**
  110. * @param string $token
  111. * @param bool $adminRequired
  112. *
  113. * @return null|User
  114. */
  115. public static function authenticateToken($token, $adminRequired = false)
  116. {
  117. $username = null;
  118. $timestamp = null;
  119. try {
  120. $decrypted = self::tokenDecrypt($token);
  121. list($timestamp, $username) = $decrypted;
  122. } catch (CryptoException $e) {
  123. return null;
  124. }
  125. $user = User::getByName($username);
  126. if (self::isValidUser($user)) {
  127. if ($adminRequired && !$user->isAdmin()) {
  128. return null;
  129. }
  130. try {
  131. $timeZone = date_default_timezone_get();
  132. date_default_timezone_set('UTC');
  133. if ($timestamp > time() || $timestamp < (time() - (60 * 60 * 24))) {
  134. return null;
  135. }
  136. } finally {
  137. date_default_timezone_set($timeZone);
  138. }
  139. return $user;
  140. }
  141. return null;
  142. }
  143. /**
  144. * @param User $user
  145. * @param string $password
  146. *
  147. * @return bool
  148. */
  149. public static function verifyPassword($user, $password)
  150. {
  151. if (!$user->getPassword()) {
  152. // do not allow logins for users without a password
  153. return false;
  154. }
  155. $password = self::preparePlainTextPassword($user->getName(), $password);
  156. if (!password_verify($password, $user->getPassword())) {
  157. return false;
  158. }
  159. $config = \Pimcore::getContainer()->getParameter('pimcore.config')['security']['password'];
  160. if (password_needs_rehash($user->getPassword(), $config['algorithm'], $config['options'])) {
  161. $user->setPassword(self::getPasswordHash($user->getName(), $password));
  162. $user->save();
  163. }
  164. return true;
  165. }
  166. /**
  167. * @param User|null $user
  168. *
  169. * @return bool
  170. */
  171. public static function isValidUser($user)
  172. {
  173. if ($user instanceof User && $user->isActive() && $user->getId() && $user->getPassword()) {
  174. return true;
  175. }
  176. return false;
  177. }
  178. /**
  179. * @internal
  180. *
  181. * @param string $username
  182. * @param string $plainTextPassword
  183. *
  184. * @return string
  185. *
  186. * @throws \Exception
  187. */
  188. public static function getPasswordHash($username, $plainTextPassword)
  189. {
  190. $password = self::preparePlainTextPassword($username, $plainTextPassword);
  191. $config = \Pimcore::getContainer()->getParameter('pimcore.config')['security']['password'];
  192. if ($hash = password_hash($password, $config['algorithm'], $config['options'])) {
  193. return $hash;
  194. }
  195. throw new \Exception('Unable to create password hash for user: ' . $username);
  196. }
  197. /**
  198. * @param string $username
  199. * @param string $plainTextPassword
  200. *
  201. * @return string
  202. */
  203. private static function preparePlainTextPassword($username, $plainTextPassword)
  204. {
  205. // plaintext password is prepared as digest A1 hash, this is to be backward compatible because this was
  206. // the former hashing algorithm in pimcore (< version 2.1.1)
  207. return md5($username . ':pimcore:' . $plainTextPassword);
  208. }
  209. /**
  210. * @internal
  211. *
  212. * @param string $username
  213. *
  214. * @return string
  215. */
  216. public static function generateToken($username)
  217. {
  218. $secret = \Pimcore::getContainer()->getParameter('secret');
  219. $data = time() - 1 . '|' . $username;
  220. $token = Crypto::encryptWithPassword($data, $secret);
  221. return $token;
  222. }
  223. /**
  224. * @param string $token
  225. *
  226. * @return array
  227. */
  228. private static function tokenDecrypt($token)
  229. {
  230. $secret = \Pimcore::getContainer()->getParameter('secret');
  231. $decrypted = Crypto::decryptWithPassword($token, $secret);
  232. return explode('|', $decrypted);
  233. }
  234. }