26use InvalidArgumentException;
51 private $policyCheckFunctions;
58 public function __construct( array $policies, array $checks ) {
59 if ( !isset( $policies[
'default'] ) ) {
60 throw new InvalidArgumentException(
61 'Must include a \'default\' password policy'
64 $this->policies = $policies;
66 foreach ( $checks as $statement => $check ) {
67 if ( !is_callable( $check ) ) {
68 throw new InvalidArgumentException(
69 "Policy check functions must be callable. '$statement' isn't callable."
72 $this->policyCheckFunctions[$statement] = $check;
88 return $this->checkPolicies(
92 $this->policyCheckFunctions
113 $this->policies[
'default']
115 return $this->checkPolicies(
119 $this->policyCheckFunctions
130 private function checkPolicies(
UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
131 $status = Status::newGood( [] );
132 $forceChange =
false;
133 $suggestChangeOnLogin =
false;
136 ->newFromUserIdentity( $user );
137 foreach ( $policies as $policy => $settings ) {
138 if ( !isset( $policyCheckFunctions[$policy] ) ) {
139 throw new DomainException(
"Invalid password policy config. No check defined for '$policy'." );
141 if ( !is_array( $settings ) ) {
143 $settings = [
'value' => $settings ];
145 if ( !array_key_exists(
'value', $settings ) ) {
146 throw new DomainException(
"Invalid password policy config. No value defined for '$policy'." );
148 $value = $settings[
'value'];
150 $policyStatus = call_user_func(
151 $policyCheckFunctions[$policy],
157 if ( !$policyStatus->isGood() ) {
158 if ( !empty( $settings[
'forceChange'] ) ) {
162 if ( !empty( $settings[
'suggestChangeOnLogin'] ) ) {
163 $suggestChangeOnLogin =
true;
166 $status->merge( $policyStatus );
169 if ( $status->isOK() ) {
170 if ( $forceChange ) {
171 $status->value[
'forceChange'] =
true;
172 } elseif ( $suggestChangeOnLogin ) {
173 $status->value[
'suggestChangeOnLogin'] =
true;
190 $services->getUserGroupManager()
191 ->getUserEffectiveGroups( $user ),
192 $this->policies[
'default']
195 $legacyUser = $services->getUserFactory()
196 ->newFromUserIdentity( $user );
197 (
new HookRunner( $services->getHookContainer() ) )->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
199 return $effectivePolicy;
213 $effectivePolicy = $defaultPolicy;
214 foreach ( $policies as $group => $policy ) {
215 if ( in_array( $group, $userGroups ) ) {
223 return $effectivePolicy;
236 $keys = array_merge( array_keys( $p1 ), array_keys( $p2 ) );
237 foreach ( $keys as $key ) {
238 if ( !isset( $p1[$key] ) ) {
239 $ret[$key] = $p2[$key];
240 } elseif ( !isset( $p2[$key] ) ) {
241 $ret[$key] = $p1[$key];
242 } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
243 $ret[$key] = max( $p1[$key], $p2[$key] );
245 if ( !is_array( $p1[$key] ) ) {
246 $p1[$key] = [
'value' => $p1[$key] ];
247 } elseif ( !is_array( $p2[$key] ) ) {
248 $p2[$key] = [
'value' => $p2[$key] ];
259class_alias( UserPasswordPolicy::class,
'UserPasswordPolicy' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.