12use InvalidArgumentException;
37 private $policyCheckFunctions;
44 public function __construct( array $policies, array $checks ) {
45 if ( !isset( $policies[
'default'] ) ) {
46 throw new InvalidArgumentException(
47 'Must include a \'default\' password policy'
50 $this->policies = $policies;
52 foreach ( $checks as $statement => $check ) {
53 if ( !is_callable( $check ) ) {
54 throw new InvalidArgumentException(
55 "Policy check functions must be callable. '$statement' isn't callable."
58 $this->policyCheckFunctions[$statement] = $check;
74 return $this->checkPolicies(
78 $this->policyCheckFunctions
99 $this->policies[
'default']
101 return $this->checkPolicies(
105 $this->policyCheckFunctions
116 private function checkPolicies(
UserIdentity $user, $password, $policies, $policyCheckFunctions ) {
117 $status = Status::newGood( [] );
118 $forceChange =
false;
119 $suggestChangeOnLogin =
false;
122 ->newFromUserIdentity( $user );
123 foreach ( $policies as $policy => $settings ) {
124 if ( !isset( $policyCheckFunctions[$policy] ) ) {
125 throw new DomainException(
"Invalid password policy config. No check defined for '$policy'." );
127 if ( !is_array( $settings ) ) {
129 $settings = [
'value' => $settings ];
131 if ( !array_key_exists(
'value', $settings ) ) {
132 throw new DomainException(
"Invalid password policy config. No value defined for '$policy'." );
134 $value = $settings[
'value'];
136 $policyStatus = $policyCheckFunctions[$policy](
142 if ( !$policyStatus->isGood() ) {
143 if ( !empty( $settings[
'forceChange'] ) ) {
147 if ( !empty( $settings[
'suggestChangeOnLogin'] ) ) {
148 $suggestChangeOnLogin =
true;
151 $status->merge( $policyStatus );
154 if ( $status->isOK() ) {
155 if ( $forceChange ) {
156 $status->value[
'forceChange'] =
true;
157 } elseif ( $suggestChangeOnLogin ) {
158 $status->value[
'suggestChangeOnLogin'] =
true;
175 $services->getUserGroupManager()
176 ->getUserEffectiveGroups( $user ),
177 $this->policies[
'default']
180 $legacyUser = $services->getUserFactory()
181 ->newFromUserIdentity( $user );
182 (
new HookRunner( $services->getHookContainer() ) )->onPasswordPoliciesForUser( $legacyUser, $effectivePolicy );
184 return $effectivePolicy;
198 $effectivePolicy = $defaultPolicy;
199 foreach ( $policies as $group => $policy ) {
200 if ( in_array( $group, $userGroups ) ) {
208 return $effectivePolicy;
221 $keys = array_merge( array_keys( $p1 ), array_keys( $p2 ) );
222 foreach ( $keys as $key ) {
223 if ( !isset( $p1[$key] ) ) {
224 $ret[$key] = $p2[$key];
225 } elseif ( !isset( $p2[$key] ) ) {
226 $ret[$key] = $p1[$key];
227 } elseif ( !is_array( $p1[$key] ) && !is_array( $p2[$key] ) ) {
228 $ret[$key] = max( $p1[$key], $p2[$key] );
230 if ( !is_array( $p1[$key] ) ) {
231 $p1[$key] = [
'value' => $p1[$key] ];
232 } elseif ( !is_array( $p2[$key] ) ) {
233 $p2[$key] = [
'value' => $p2[$key] ];
244class_alias( UserPasswordPolicy::class,
'UserPasswordPolicy' );
Generic operation result class Has warning/error list, boolean status and arbitrary value.