MediaWiki  master
UserPasswordPolicy.php
Go to the documentation of this file.
1 <?php
25 
32 
36  private $policies;
37 
45 
51  public function __construct( array $policies, array $checks ) {
52  if ( !isset( $policies['default'] ) ) {
53  throw new InvalidArgumentException(
54  'Must include a \'default\' password policy'
55  );
56  }
57  $this->policies = $policies;
58 
59  foreach ( $checks as $statement => $check ) {
60  if ( !is_callable( $check ) ) {
61  throw new InvalidArgumentException(
62  "Policy check functions must be callable. '$statement' isn't callable."
63  );
64  }
65  $this->policyCheckFunctions[$statement] = $check;
66  }
67  }
68 
79  public function checkUserPassword( UserIdentity $user, $password ) {
80  $effectivePolicy = $this->getPoliciesForUser( $user );
81  return $this->checkPolicies(
82  $user,
83  $password,
84  $effectivePolicy,
85  $this->policyCheckFunctions
86  );
87  }
88 
102  public function checkUserPasswordForGroups( UserIdentity $user, $password, array $groups ) {
103  $effectivePolicy = self::getPoliciesForGroups(
104  $this->policies,
105  $groups,
106  $this->policies['default']
107  );
108  return $this->checkPolicies(
109  $user,
110  $password,
111  $effectivePolicy,
112  $this->policyCheckFunctions
113  );
114  }
115 
123  private function checkPolicies( UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
124  $status = Status::newGood( [] );
125  $forceChange = false;
126  $suggestChangeOnLogin = false;
127  $legacyUser = MediaWikiServices::getInstance()
128  ->getUserFactory()
129  ->newFromUserIdentity( $user );
130  foreach ( $policies as $policy => $settings ) {
131  if ( !isset( $policyCheckFunctions[$policy] ) ) {
132  throw new DomainException( "Invalid password policy config. No check defined for '$policy'." );
133  }
134  if ( !is_array( $settings ) ) {
135  // legacy format
136  $settings = [ 'value' => $settings ];
137  }
138  if ( !array_key_exists( 'value', $settings ) ) {
139  throw new DomainException( "Invalid password policy config. No value defined for '$policy'." );
140  }
141  $value = $settings['value'];
143  $policyStatus = call_user_func(
144  $policyCheckFunctions[$policy],
145  $value,
146  $legacyUser,
147  $password
148  );
149 
150  if ( !$policyStatus->isGood() ) {
151  if ( !empty( $settings['forceChange'] ) ) {
152  $forceChange = true;
153  }
154 
155  if ( !empty( $settings['suggestChangeOnLogin'] ) ) {
156  $suggestChangeOnLogin = true;
157  }
158  }
159  $status->merge( $policyStatus );
160  }
161 
162  if ( $status->isOK() ) {
163  if ( $forceChange ) {
164  $status->value['forceChange'] = true;
165  } elseif ( $suggestChangeOnLogin ) {
166  $status->value['suggestChangeOnLogin'] = true;
167  }
168  }
169 
170  return $status;
171  }
172 
179  public function getPoliciesForUser( UserIdentity $user ) {
180  $effectivePolicy = self::getPoliciesForGroups(
181  $this->policies,
182  MediaWikiServices::getInstance()
183  ->getUserGroupManager()
184  ->getUserEffectiveGroups( $user ),
185  $this->policies['default']
186  );
187 
188  $legacyUser = MediaWikiServices::getInstance()
189  ->getUserFactory()
190  ->newFromUserIdentity( $user );
191  Hooks::runner()->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
192 
193  return $effectivePolicy;
194  }
195 
204  public static function getPoliciesForGroups( array $policies, array $userGroups,
205  array $defaultPolicy
206  ) {
207  $effectivePolicy = $defaultPolicy;
208  foreach ( $policies as $group => $policy ) {
209  if ( in_array( $group, $userGroups ) ) {
210  $effectivePolicy = self::maxOfPolicies(
211  $effectivePolicy,
212  $policy
213  );
214  }
215  }
216 
217  return $effectivePolicy;
218  }
219 
228  public static function maxOfPolicies( array $p1, array $p2 ) {
229  $ret = [];
230  $keys = array_merge( array_keys( $p1 ), array_keys( $p2 ) );
231  foreach ( $keys as $key ) {
232  if ( !isset( $p1[$key] ) ) {
233  $ret[$key] = $p2[$key];
234  } elseif ( !isset( $p2[$key] ) ) {
235  $ret[$key] = $p1[$key];
236  } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
237  $ret[$key] = max( $p1[$key], $p2[$key] );
238  } else {
239  if ( !is_array( $p1[$key] ) ) {
240  $p1[$key] = [ 'value' => $p1[$key] ];
241  } elseif ( !is_array( $p2[$key] ) ) {
242  $p2[$key] = [ 'value' => $p2[$key] ];
243  }
244  $ret[$key] = self::maxOfPolicies( $p1[$key], $p2[$key] );
245  }
246  }
247  return $ret;
248  }
249 
250 }
UserPasswordPolicy\maxOfPolicies
static maxOfPolicies(array $p1, array $p2)
Utility function to get a policy that is the most restrictive of $p1 and $p2.
Definition: UserPasswordPolicy.php:228
UserPasswordPolicy\checkPolicies
checkPolicies(UserIdentity $user, $password, $policies, $policyCheckFunctions)
Definition: UserPasswordPolicy.php:123
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:193
UserPasswordPolicy\getPoliciesForGroups
static getPoliciesForGroups(array $policies, array $userGroups, array $defaultPolicy)
Utility function to get the effective policy from a list of policies, based on a list of groups.
Definition: UserPasswordPolicy.php:204
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:39
UserPasswordPolicy\__construct
__construct(array $policies, array $checks)
Definition: UserPasswordPolicy.php:51
UserPasswordPolicy
Check if a user's password complies with any password policies that apply to that user,...
Definition: UserPasswordPolicy.php:31
UserPasswordPolicy\checkUserPasswordForGroups
checkUserPasswordForGroups(UserIdentity $user, $password, array $groups)
Check if a password meets the effective password policy for a User, using a set of groups they may or...
Definition: UserPasswordPolicy.php:102
StatusValue\newGood
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
UserPasswordPolicy\getPoliciesForUser
getPoliciesForUser(UserIdentity $user)
Get the policy for a user, based on their group membership.
Definition: UserPasswordPolicy.php:179
Hooks\runner
static runner()
Get a HookRunner instance for calling hooks using the new interfaces.
Definition: Hooks.php:173
$keys
$keys
Definition: testCompression.php:72
UserPasswordPolicy\checkUserPassword
checkUserPassword(UserIdentity $user, $password)
Check if a password meets the effective password policy for a User.
Definition: UserPasswordPolicy.php:79
UserPasswordPolicy\$policies
array $policies
Definition: UserPasswordPolicy.php:36
UserPasswordPolicy\$policyCheckFunctions
array $policyCheckFunctions
Mapping of statements to the function that will test the password for compliance.
Definition: UserPasswordPolicy.php:44