Add lazy loading on Mediawiki powered-by icon
[mediawiki.git] / includes / password / PasswordFactory.php
blob42af8de75411afb27de165ebb342d8345665199a
1 <?php
2 /**
3 * Implements the Password class for the MediaWiki software.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * http://www.gnu.org/copyleft/gpl.html
20 * @file
23 declare( strict_types = 1 );
25 /**
26 * Factory class for creating and checking Password objects
28 * @since 1.24
30 final class PasswordFactory {
31 /**
32 * The default PasswordHash type
34 * @var string
35 * @see PasswordFactory::setDefaultType
37 private $default = '';
39 /**
40 * Mapping of password types to classes
42 * @var array
43 * @see PasswordFactory::register
44 * @see Setup.php
46 private $types = [
47 '' => [ 'type' => '', 'class' => InvalidPassword::class ],
50 /**
51 * Most of the time you'll want to use MediaWikiServices::getInstance()->getPasswordFactory
52 * instead.
53 * @param array $config Mapping of password type => config
54 * @param string $default Default password type
55 * @see PasswordFactory::register
56 * @see PasswordFactory::setDefaultType
58 public function __construct( array $config = [], string $default = '' ) {
59 foreach ( $config as $type => $options ) {
60 $this->register( $type, $options );
63 if ( $default !== '' ) {
64 $this->setDefaultType( $default );
68 /**
69 * Register a new type of password hash
71 * @param string $type Unique type name for the hash. Will be prefixed to the password hashes
72 * to identify what hashing method was used.
73 * @param array $config Array of configuration options. 'class' is required (the Password
74 * subclass name), everything else is passed to the constructor of that class.
76 public function register( string $type, array $config ) : void {
77 $config['type'] = $type;
78 $this->types[$type] = $config;
81 /**
82 * Set the default password type
84 * This type will be used for creating new passwords when the type is not specified.
85 * Passwords of a different type will be considered outdated and in need of update.
87 * @param string $type Password hash type
88 * @throws InvalidArgumentException If the type is not registered
90 public function setDefaultType( string $type ) : void {
91 if ( !isset( $this->types[$type] ) ) {
92 throw new InvalidArgumentException( "Invalid password type $type." );
94 $this->default = $type;
97 /**
98 * Get the default password type
100 * @return string
102 public function getDefaultType() : string {
103 return $this->default;
107 * @deprecated since 1.32 Initialize settings using the constructor
109 * Initialize the internal static variables using the global variables
111 * @param Config $config Configuration object to load data from
113 public function init( Config $config ) : void {
114 foreach ( $config->get( 'PasswordConfig' ) as $type => $options ) {
115 $this->register( $type, $options );
118 $this->setDefaultType( $config->get( 'PasswordDefault' ) );
122 * Get the list of types of passwords
124 * @return array
126 public function getTypes() : array {
127 return $this->types;
131 * Create a new Hash object from an existing string hash
133 * Parse the type of a hash and create a new hash object based on the parsed type.
134 * Pass the raw hash to the constructor of the new object. Use InvalidPassword type
135 * if a null hash is given.
137 * @param string|null $hash Existing hash or null for an invalid password
138 * @return Password
139 * @throws PasswordError If hash is invalid or type is not recognized
141 public function newFromCiphertext( ?string $hash ) : Password {
142 if ( $hash === null || $hash === false || $hash === '' ) {
143 return new InvalidPassword( $this, [ 'type' => '' ], null );
144 } elseif ( $hash[0] !== ':' ) {
145 throw new PasswordError( 'Invalid hash given' );
148 $type = substr( $hash, 1, strpos( $hash, ':', 1 ) - 1 );
149 if ( !isset( $this->types[$type] ) ) {
150 throw new PasswordError( "Unrecognized password hash type $type." );
153 $config = $this->types[$type];
155 return new $config['class']( $this, $config, $hash );
159 * Make a new default password of the given type.
161 * @param string $type Existing type
162 * @return Password
163 * @throws PasswordError If hash is invalid or type is not recognized
165 public function newFromType( string $type ) : Password {
166 if ( !isset( $this->types[$type] ) ) {
167 throw new PasswordError( "Unrecognized password hash type $type." );
170 $config = $this->types[$type];
172 return new $config['class']( $this, $config );
176 * Create a new Hash object from a plaintext password
178 * If no existing object is given, make a new default object. If one is given, clone that
179 * object. Then pass the plaintext to Password::crypt().
181 * @param string|null $password Plaintext password, or null for an invalid password
182 * @param Password|null $existing Optional existing hash to get options from
183 * @return Password
185 public function newFromPlaintext( ?string $password, Password $existing = null ) : Password {
186 if ( $password === null ) {
187 return new InvalidPassword( $this, [ 'type' => '' ], null );
190 if ( $existing === null ) {
191 $config = $this->types[$this->default];
192 $obj = new $config['class']( $this, $config );
193 } else {
194 $obj = clone $existing;
197 $obj->crypt( $password );
199 return $obj;
203 * Determine whether a password object needs updating
205 * Check whether the given password is of the default type. If it is,
206 * pass off further needsUpdate checks to Password::needsUpdate.
208 * @param Password $password
210 * @return bool True if needs update, false otherwise
212 public function needsUpdate( Password $password ) : bool {
213 if ( $password->getType() !== $this->default ) {
214 return true;
215 } else {
216 return $password->needsUpdate();
221 * Generate a random string suitable for a password
223 * @param int $minLength Minimum length of password to generate
224 * @return string
226 public static function generateRandomPasswordString( int $minLength = 10 ) : string {
227 // Decide the final password length based on our min password length,
228 // stopping at a minimum of 10 chars.
229 $length = max( 10, $minLength );
230 // Multiply by 1.25 to get the number of hex characters we need
231 // Generate random hex chars
232 $hex = MWCryptRand::generateHex( ceil( $length * 1.25 ) );
233 // Convert from base 16 to base 32 to get a proper password like string
234 return substr( Wikimedia\base_convert( $hex, 16, 32, $length ), -$length );
238 * Create an InvalidPassword
240 * @return InvalidPassword
242 public static function newInvalidPassword() : InvalidPassword {
243 static $password = null;
245 if ( $password === null ) {
246 $factory = new self();
247 $password = new InvalidPassword( $factory, [ 'type' => '' ], null );
250 return $password;