44 private $policyCheckFunctions;
51 public function __construct( array $policies, array $checks ) {
52 if ( !isset( $policies[
'default'] ) ) {
53 throw new InvalidArgumentException(
54 'Must include a \'default\' password policy'
57 $this->policies = $policies;
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."
65 $this->policyCheckFunctions[$statement] = $check;
81 return $this->checkPolicies(
85 $this->policyCheckFunctions
106 $this->policies[
'default']
108 return $this->checkPolicies(
112 $this->policyCheckFunctions
123 private function checkPolicies(
UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
124 $status = Status::newGood( [] );
125 $forceChange =
false;
126 $suggestChangeOnLogin =
false;
127 $legacyUser = MediaWikiServices::getInstance()
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'." );
134 if ( !is_array( $settings ) ) {
136 $settings = [
'value' => $settings ];
138 if ( !array_key_exists(
'value', $settings ) ) {
139 throw new DomainException(
"Invalid password policy config. No value defined for '$policy'." );
141 $value = $settings[
'value'];
143 $policyStatus = call_user_func(
144 $policyCheckFunctions[$policy],
150 if ( !$policyStatus->isGood() ) {
151 if ( !empty( $settings[
'forceChange'] ) ) {
155 if ( !empty( $settings[
'suggestChangeOnLogin'] ) ) {
156 $suggestChangeOnLogin =
true;
159 $status->merge( $policyStatus );
162 if ( $status->isOK() ) {
163 if ( $forceChange ) {
164 $status->value[
'forceChange'] =
true;
165 } elseif ( $suggestChangeOnLogin ) {
166 $status->value[
'suggestChangeOnLogin'] =
true;
182 MediaWikiServices::getInstance()
183 ->getUserGroupManager()
184 ->getUserEffectiveGroups( $user ),
185 $this->policies[
'default']
188 $legacyUser = MediaWikiServices::getInstance()
190 ->newFromUserIdentity( $user );
191 Hooks::runner()->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
193 return $effectivePolicy;
207 $effectivePolicy = $defaultPolicy;
208 foreach ( $policies as $group => $policy ) {
209 if ( in_array( $group, $userGroups ) ) {
217 return $effectivePolicy;
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] );
239 if ( !is_array( $p1[$key] ) ) {
240 $p1[$key] = [
'value' => $p1[$key] ];
241 } elseif ( !is_array( $p2[$key] ) ) {
242 $p2[$key] = [
'value' => $p2[$key] ];
Check if a user's password complies with any password policies that apply to that user,...
getPoliciesForUser(UserIdentity $user)
Get the policy for a user, based on their group membership.
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...
checkUserPassword(UserIdentity $user, $password)
Check if a password meets the effective password policy for a User.
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.
__construct(array $policies, array $checks)
static maxOfPolicies(array $p1, array $p2)
Utility function to get a policy that is the most restrictive of $p1 and $p2.