46 private $policyCheckFunctions;
53 public function __construct( array $policies, array $checks ) {
54 if ( !isset( $policies[
'default'] ) ) {
55 throw new InvalidArgumentException(
56 'Must include a \'default\' password policy'
59 $this->policies = $policies;
61 foreach ( $checks as $statement => $check ) {
62 if ( !is_callable( $check ) ) {
63 throw new InvalidArgumentException(
64 "Policy check functions must be callable. '$statement' isn't callable."
67 $this->policyCheckFunctions[$statement] = $check;
83 return $this->checkPolicies(
87 $this->policyCheckFunctions
108 $this->policies[
'default']
110 return $this->checkPolicies(
114 $this->policyCheckFunctions
125 private function checkPolicies(
UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
126 $status = Status::newGood( [] );
127 $forceChange =
false;
128 $suggestChangeOnLogin =
false;
129 $legacyUser = MediaWikiServices::getInstance()
131 ->newFromUserIdentity( $user );
132 foreach ( $policies as $policy => $settings ) {
133 if ( !isset( $policyCheckFunctions[$policy] ) ) {
134 throw new DomainException(
"Invalid password policy config. No check defined for '$policy'." );
136 if ( !is_array( $settings ) ) {
138 $settings = [
'value' => $settings ];
140 if ( !array_key_exists(
'value', $settings ) ) {
141 throw new DomainException(
"Invalid password policy config. No value defined for '$policy'." );
143 $value = $settings[
'value'];
145 $policyStatus = call_user_func(
146 $policyCheckFunctions[$policy],
152 if ( !$policyStatus->isGood() ) {
153 if ( !empty( $settings[
'forceChange'] ) ) {
157 if ( !empty( $settings[
'suggestChangeOnLogin'] ) ) {
158 $suggestChangeOnLogin =
true;
161 $status->merge( $policyStatus );
164 if ( $status->isOK() ) {
165 if ( $forceChange ) {
166 $status->value[
'forceChange'] =
true;
167 } elseif ( $suggestChangeOnLogin ) {
168 $status->value[
'suggestChangeOnLogin'] =
true;
182 $services = MediaWikiServices::getInstance();
185 $services->getUserGroupManager()
186 ->getUserEffectiveGroups( $user ),
187 $this->policies[
'default']
190 $legacyUser = $services->getUserFactory()
191 ->newFromUserIdentity( $user );
192 (
new HookRunner( $services->getHookContainer() ) )->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
194 return $effectivePolicy;
208 $effectivePolicy = $defaultPolicy;
209 foreach ( $policies as $group => $policy ) {
210 if ( in_array( $group, $userGroups ) ) {
218 return $effectivePolicy;
231 $keys = array_merge( array_keys( $p1 ), array_keys( $p2 ) );
232 foreach ( $keys as $key ) {
233 if ( !isset( $p1[$key] ) ) {
234 $ret[$key] = $p2[$key];
235 } elseif ( !isset( $p2[$key] ) ) {
236 $ret[$key] = $p1[$key];
237 } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
238 $ret[$key] = max( $p1[$key], $p2[$key] );
240 if ( !is_array( $p1[$key] ) ) {
241 $p1[$key] = [
'value' => $p1[$key] ];
242 } elseif ( !is_array( $p2[$key] ) ) {
243 $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.