[!!!][TASK] Implement Salted Passwords against SaltInterface
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Classes / Salt / SaltFactory.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Saltedpasswords\Salt;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Utility\GeneralUtility;
19 use TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility;
20
21 /**
22 * Class that implements Blowfish salted hashing based on PHP's
23 * crypt() function.
24 */
25 class SaltFactory
26 {
27 /**
28 * An instance of the salted hashing method.
29 * This member is set in the getSaltingInstance() function.
30 *
31 * @var SaltInterface
32 */
33 protected static $instance = null;
34
35 /**
36 * Returns list of all registered hashing methods. Used eg. in
37 * extension configuration to select the default hashing method.
38 *
39 * @return array
40 */
41 public static function getRegisteredSaltedHashingMethods(): array
42 {
43 $saltMethods = static::getDefaultSaltMethods();
44 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'])) {
45 $configuredMethods = (array)$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/saltedpasswords']['saltMethods'];
46 if (!empty($configuredMethods)) {
47 if (isset($configuredMethods[0])) {
48 // ensure the key of the array is not numeric, but a class name
49 foreach ($configuredMethods as $method) {
50 $saltMethods[$method] = $method;
51 }
52 } else {
53 $saltMethods = array_merge($saltMethods, $configuredMethods);
54 }
55 }
56 }
57 return $saltMethods;
58 }
59
60 /**
61 * Returns an array with default salt method class names.
62 *
63 * @return array
64 */
65 protected static function getDefaultSaltMethods(): array
66 {
67 return [
68 Md5Salt::class => Md5Salt::class,
69 BlowfishSalt::class => BlowfishSalt::class,
70 PhpassSalt::class => PhpassSalt::class,
71 Pbkdf2Salt::class => Pbkdf2Salt::class
72 ];
73 }
74
75 /**
76 * Obtains a salting hashing method instance.
77 *
78 * This function will return an instance of a class that implements
79 * \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface
80 *
81 * Use parameter NULL to reset the factory!
82 *
83 * @param string|null $saltedHash Salted hashed password to determine the type of used method from or NULL to reset to the default type
84 * @param string $mode The TYPO3 mode (FE or BE) saltedpasswords shall be used for
85 * @return SaltInterface|null An instance of salting hash method class or null if given hash is not supported
86 */
87 public static function getSaltingInstance($saltedHash = '', $mode = TYPO3_MODE)
88 {
89 // Creating new instance when
90 // * no instance existing
91 // * a salted hash given to determine salted hashing method from
92 // * a NULL parameter given to reset instance back to default method
93 if (!is_object(self::$instance) || !empty($saltedHash) || $saltedHash === null) {
94 // Determine method by checking the given hash
95 if (!empty($saltedHash)) {
96 $result = self::determineSaltingHashingMethod($saltedHash, $mode);
97 if (!$result) {
98 self::$instance = null;
99 }
100 } else {
101 $classNameToUse = SaltedPasswordsUtility::getDefaultSaltingHashingMethod($mode);
102 $availableClasses = static::getRegisteredSaltedHashingMethods();
103 self::$instance = GeneralUtility::makeInstance($availableClasses[$classNameToUse]);
104 }
105 }
106 return self::$instance;
107 }
108
109 /**
110 * Method tries to determine the salting hashing method used for given salt.
111 *
112 * Method implicitly sets the instance of the found method object in the class property when found.
113 *
114 * @param string $saltedHash
115 * @param string $mode (optional) The TYPO3 mode (FE or BE) saltedpasswords shall be used for
116 * @return bool TRUE, if salting hashing method has been found, otherwise FALSE
117 */
118 public static function determineSaltingHashingMethod(string $saltedHash, $mode = TYPO3_MODE): bool
119 {
120 $registeredMethods = static::getRegisteredSaltedHashingMethods();
121 $defaultClassName = SaltedPasswordsUtility::getDefaultSaltingHashingMethod($mode);
122 $defaultReference = $registeredMethods[$defaultClassName];
123 unset($registeredMethods[$defaultClassName]);
124 // place the default method first in the order
125 $registeredMethods = [$defaultClassName => $defaultReference] + $registeredMethods;
126 $methodFound = false;
127 foreach ($registeredMethods as $method) {
128 $objectInstance = GeneralUtility::makeInstance($method);
129 if ($objectInstance instanceof SaltInterface) {
130 $methodFound = $objectInstance->isValidSaltedPW($saltedHash);
131 if ($methodFound) {
132 self::$instance = $objectInstance;
133 break;
134 }
135 }
136 }
137 return $methodFound;
138 }
139
140 /**
141 * Method sets a custom salting hashing method class.
142 *
143 * @param string $resource Object resource to use (e.g. \TYPO3\CMS\Saltedpasswords\Salt\BlowfishSalt::class)
144 * @return SaltInterface|null An instance of salting hashing method object or null
145 */
146 public static function setPreferredHashingMethod(string $resource)
147 {
148 self::$instance = null;
149 $objectInstance = GeneralUtility::makeInstance($resource);
150 if ($objectInstance instanceof SaltInterface) {
151 self::$instance = $objectInstance;
152 }
153 return self::$instance;
154 }
155 }