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 = $policyCheckFunctions[$policy](
156 if ( !$policyStatus->isGood() ) {
157 if ( !empty( $settings[
'forceChange'] ) ) {
161 if ( !empty( $settings[
'suggestChangeOnLogin'] ) ) {
162 $suggestChangeOnLogin =
true;
165 $status->merge( $policyStatus );
168 if ( $status->isOK() ) {
169 if ( $forceChange ) {
170 $status->value[
'forceChange'] =
true;
171 } elseif ( $suggestChangeOnLogin ) {
172 $status->value[
'suggestChangeOnLogin'] =
true;
189 $services->getUserGroupManager()
190 ->getUserEffectiveGroups( $user ),
191 $this->policies[
'default']
194 $legacyUser = $services->getUserFactory()
195 ->newFromUserIdentity( $user );
196 (
new HookRunner( $services->getHookContainer() ) )->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
198 return $effectivePolicy;
212 $effectivePolicy = $defaultPolicy;
213 foreach ( $policies as $group => $policy ) {
214 if ( in_array( $group, $userGroups ) ) {
222 return $effectivePolicy;
235 $keys = array_merge( array_keys( $p1 ), array_keys( $p2 ) );
236 foreach ( $keys as $key ) {
237 if ( !isset( $p1[$key] ) ) {
238 $ret[$key] = $p2[$key];
239 } elseif ( !isset( $p2[$key] ) ) {
240 $ret[$key] = $p1[$key];
241 } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
242 $ret[$key] = max( $p1[$key], $p2[$key] );
244 if ( !is_array( $p1[$key] ) ) {
245 $p1[$key] = [
'value' => $p1[$key] ];
246 } elseif ( !is_array( $p2[$key] ) ) {
247 $p2[$key] = [
'value' => $p2[$key] ];
258class_alias( UserPasswordPolicy::class,
'UserPasswordPolicy' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.