Add NamespaceRestriction class so that BlockRestriction can handle namespaces.
[lhc/web/wiklou.git] / includes / user / User.php
index 1412215..f23c8ee 100644 (file)
@@ -58,7 +58,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * @const int Serialized record version.
         */
-       const VERSION = 12;
+       const VERSION = 13;
 
        /**
         * Exclude user options that are set to their default value.
@@ -1144,8 +1144,8 @@ class User implements IDBAccessObject, UserIdentity {
         * @return bool
         */
        public function isValidPassword( $password ) {
-               // simple boolean wrapper for getPasswordValidity
-               return $this->getPasswordValidity( $password ) === true;
+               // simple boolean wrapper for checkPasswordValidity
+               return $this->checkPasswordValidity( $password )->isGood();
        }
 
        /**
@@ -1153,8 +1153,11 @@ class User implements IDBAccessObject, UserIdentity {
         *
         * @param string $password Desired password
         * @return bool|string|array True on success, string or array of error message on failure
+        * @deprecated since 1.33, use checkPasswordValidity
         */
        public function getPasswordValidity( $password ) {
+               wfDeprecated( __METHOD__, '1.33' );
+
                $result = $this->checkPasswordValidity( $password );
                if ( $result->isGood() ) {
                        return true;
@@ -1176,15 +1179,17 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Check if this is a valid password for this user
         *
-        * Create a Status object based on the password's validity.
-        * The Status should be set to fatal if the user should not
-        * be allowed to log in, and should have any errors that
-        * would block changing the password.
-        *
-        * If the return value of this is not OK, the password
-        * should not be checked. If the return value is not Good,
-        * the password can be checked, but the user should not be
-        * able to set their password to this.
+        * Returns a Status object with a set of messages describing
+        * problems with the password. If the return status is fatal,
+        * the action should be refused and the password should not be
+        * checked at all (this is mainly meant for DoS mitigation).
+        * If the return value is OK but not good, the password can be checked,
+        * but the user should not be able to set their password to this.
+        * The value of the returned Status object will be an array which
+        * can have the following fields:
+        * - forceChange (bool): if set to true, the user should not be
+        *   allowed to log with this password unless they change it during
+        *   the login process (see ResetPasswordSecondaryAuthenticationProvider).
         *
         * @param string $password Desired password
         * @return Status
@@ -1198,7 +1203,7 @@ class User implements IDBAccessObject, UserIdentity {
                        $wgPasswordPolicy['checks']
                );
 
-               $status = Status::newGood();
+               $status = Status::newGood( [] );
                $result = false; // init $result to false for the internal checks
 
                if ( !Hooks::run( 'isValidPassword', [ $password, &$result, $this ] ) ) {
@@ -1207,7 +1212,7 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                if ( $result === false ) {
-                       $status->merge( $upp->checkUserPassword( $this, $password ) );
+                       $status->merge( $upp->checkUserPassword( $this, $password ), true );
                        return $status;
                } elseif ( $result === true ) {
                        return $status;
@@ -1574,7 +1579,7 @@ class User implements IDBAccessObject, UserIdentity {
 
                if ( is_array( $data ) ) {
                        if ( isset( $data['user_groups'] ) && is_array( $data['user_groups'] ) ) {
-                               if ( !count( $data['user_groups'] ) ) {
+                               if ( $data['user_groups'] === [] ) {
                                        $this->mGroupMemberships = [];
                                } else {
                                        $firstGroup = reset( $data['user_groups'] );
@@ -1640,7 +1645,7 @@ class User implements IDBAccessObject, UserIdentity {
                }
 
                $toPromote = Autopromote::getAutopromoteOnceGroups( $this, $event );
-               if ( !count( $toPromote ) ) {
+               if ( $toPromote === [] ) {
                        return [];
                }
 
@@ -2300,14 +2305,25 @@ class User implements IDBAccessObject, UserIdentity {
                                // Special handling for a user's own talk page. The block is not aware
                                // of the user, so this must be done here.
                                if ( $title->equals( $this->getTalkPage() ) ) {
-                                       // If the block is sitewide, then whatever is set is what is honored.
                                        if ( $block->isSitewide() ) {
+                                               // If the block is sitewide, whatever is set is what is honored.
+                                               // This must be checked here, because Block::appliesToPage will
+                                               // return true for a sitewide block.
                                                $blocked = $block->prevents( 'editownusertalk' );
                                        } else {
-                                               // If the block is partial, ignore 'editownusertalk' unless
-                                               // there is a restriction on the user talk namespace.
-                                               // TODO: To be implemented with Namespace restrictions
-                                               $blocked = $block->appliesToTitle( $title );
+                                               // The page restrictions always take precedence over the namespace
+                                               // restrictions. If the user is explicity blocked from their own
+                                               // talk page, nothing can change that.
+                                               $blocked = $block->appliesToPage( $title->getArticleID() );
+
+                                               // If the block applies to the user talk namespace, then whatever is
+                                               // set is what is honored.
+                                               if ( !$blocked && $block->appliesToNamespace( NS_USER_TALK ) ) {
+                                                       $blocked = $block->prevents( 'editownusertalk' );
+                                               }
+
+                                               // If another type of restriction is added, it should be checked
+                                               // here.
                                        }
                                } else {
                                        $blocked = $block->appliesToTitle( $title );
@@ -3598,7 +3614,8 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Get the list of explicit group memberships this user has.
         * The implicit * and user groups are not included.
-        * @return array Array of String internal group names
+        *
+        * @return string[] Array of internal group names (sorted since 1.33)
         */
        public function getGroups() {
                $this->load();
@@ -4539,6 +4556,7 @@ class User implements IDBAccessObject, UserIdentity {
        /**
         * Get whether the user is blocked from using Special:Upload
         *
+        * @since 1.33
         * @return bool
         */
        public function isBlockedFromUpload() {