MediaWiki master
UserPasswordPolicy.php
Go to the documentation of this file.
1<?php
27
34
38 private $policies;
39
46 private $policyCheckFunctions;
47
53 public function __construct( array $policies, array $checks ) {
54 if ( !isset( $policies['default'] ) ) {
55 throw new InvalidArgumentException(
56 'Must include a \'default\' password policy'
57 );
58 }
59 $this->policies = $policies;
60
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."
65 );
66 }
67 $this->policyCheckFunctions[$statement] = $check;
68 }
69 }
70
81 public function checkUserPassword( UserIdentity $user, $password ) {
82 $effectivePolicy = $this->getPoliciesForUser( $user );
83 return $this->checkPolicies(
84 $user,
85 $password,
86 $effectivePolicy,
87 $this->policyCheckFunctions
88 );
89 }
90
104 public function checkUserPasswordForGroups( UserIdentity $user, $password, array $groups ) {
105 $effectivePolicy = self::getPoliciesForGroups(
106 $this->policies,
107 $groups,
108 $this->policies['default']
109 );
110 return $this->checkPolicies(
111 $user,
112 $password,
113 $effectivePolicy,
114 $this->policyCheckFunctions
115 );
116 }
117
125 private function checkPolicies( UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
126 $status = Status::newGood( [] );
127 $forceChange = false;
128 $suggestChangeOnLogin = false;
129 $legacyUser = MediaWikiServices::getInstance()
130 ->getUserFactory()
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'." );
135 }
136 if ( !is_array( $settings ) ) {
137 // legacy format
138 $settings = [ 'value' => $settings ];
139 }
140 if ( !array_key_exists( 'value', $settings ) ) {
141 throw new DomainException( "Invalid password policy config. No value defined for '$policy'." );
142 }
143 $value = $settings['value'];
145 $policyStatus = call_user_func(
146 $policyCheckFunctions[$policy],
147 $value,
148 $legacyUser,
149 $password
150 );
151
152 if ( !$policyStatus->isGood() ) {
153 if ( !empty( $settings['forceChange'] ) ) {
154 $forceChange = true;
155 }
156
157 if ( !empty( $settings['suggestChangeOnLogin'] ) ) {
158 $suggestChangeOnLogin = true;
159 }
160 }
161 $status->merge( $policyStatus );
162 }
163
164 if ( $status->isOK() ) {
165 if ( $forceChange ) {
166 $status->value['forceChange'] = true;
167 } elseif ( $suggestChangeOnLogin ) {
168 $status->value['suggestChangeOnLogin'] = true;
169 }
170 }
171
172 return $status;
173 }
174
181 public function getPoliciesForUser( UserIdentity $user ) {
182 $services = MediaWikiServices::getInstance();
183 $effectivePolicy = self::getPoliciesForGroups(
184 $this->policies,
185 $services->getUserGroupManager()
186 ->getUserEffectiveGroups( $user ),
187 $this->policies['default']
188 );
189
190 $legacyUser = $services->getUserFactory()
191 ->newFromUserIdentity( $user );
192 ( new HookRunner( $services->getHookContainer() ) )->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
193
194 return $effectivePolicy;
195 }
196
205 public static function getPoliciesForGroups( array $policies, array $userGroups,
206 array $defaultPolicy
207 ) {
208 $effectivePolicy = $defaultPolicy;
209 foreach ( $policies as $group => $policy ) {
210 if ( in_array( $group, $userGroups ) ) {
211 $effectivePolicy = self::maxOfPolicies(
212 $effectivePolicy,
213 $policy
214 );
215 }
216 }
217
218 return $effectivePolicy;
219 }
220
229 public static function maxOfPolicies( array $p1, array $p2 ) {
230 $ret = [];
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] );
239 } else {
240 if ( !is_array( $p1[$key] ) ) {
241 $p1[$key] = [ 'value' => $p1[$key] ];
242 } elseif ( !is_array( $p2[$key] ) ) {
243 $p2[$key] = [ 'value' => $p2[$key] ];
244 }
245 $ret[$key] = self::maxOfPolicies( $p1[$key], $p2[$key] );
246 }
247 }
248 return $ret;
249 }
250
251}
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
Service locator for MediaWiki core services.
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
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.
Interface for objects representing user identity.