wikimedia/mediawiki-core

View on GitHub
includes/auth/ResetPasswordSecondaryAuthenticationProvider.php

Summary

Maintainability
B
5 hrs
Test Coverage
<?php
/**
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * http://www.gnu.org/copyleft/gpl.html
 *
 * @file
 * @ingroup Auth
 */

namespace MediaWiki\Auth;

use MediaWiki\Message\Message;
use MediaWiki\User\User;

/**
 * Reset the local password, if signalled via $this->manager->setAuthenticationSessionData()
 *
 * The authentication data key is 'reset-pass'; the data is an object with the
 * following properties:
 * - msg: Message object to display to the user
 * - hard: Boolean, if true the reset cannot be skipped.
 * - req: Optional PasswordAuthenticationRequest to use to actually reset the
 *   password. Won't be displayed to the user.
 *
 * @ingroup Auth
 * @since 1.27
 */
class ResetPasswordSecondaryAuthenticationProvider extends AbstractSecondaryAuthenticationProvider {

    public function getAuthenticationRequests( $action, array $options ) {
        return [];
    }

    public function beginSecondaryAuthentication( $user, array $reqs ) {
        return $this->tryReset( $user, $reqs );
    }

    public function continueSecondaryAuthentication( $user, array $reqs ) {
        return $this->tryReset( $user, $reqs );
    }

    public function beginSecondaryAccountCreation( $user, $creator, array $reqs ) {
        return $this->tryReset( $user, $reqs );
    }

    public function continueSecondaryAccountCreation( $user, $creator, array $reqs ) {
        return $this->tryReset( $user, $reqs );
    }

    /**
     * Try to reset the password
     * @param User $user
     * @param AuthenticationRequest[] $reqs
     * @return AuthenticationResponse
     */
    protected function tryReset( User $user, array $reqs ) {
        $data = $this->manager->getAuthenticationSessionData( 'reset-pass' );
        if ( !$data ) {
            return AuthenticationResponse::newAbstain();
        }

        if ( is_array( $data ) ) {
            $data = (object)$data;
        }
        if ( !is_object( $data ) ) {
            throw new \UnexpectedValueException( 'reset-pass is not valid' );
        }

        if ( !isset( $data->msg ) ) {
            throw new \UnexpectedValueException( 'reset-pass msg is missing' );
        } elseif ( !$data->msg instanceof Message ) {
            throw new \UnexpectedValueException( 'reset-pass msg is not valid' );
        } elseif ( !isset( $data->hard ) ) {
            throw new \UnexpectedValueException( 'reset-pass hard is missing' );
        } elseif ( isset( $data->req ) && (
            !$data->req instanceof PasswordAuthenticationRequest ||
            !array_key_exists( 'retype', $data->req->getFieldInfo() )
        ) ) {
            throw new \UnexpectedValueException( 'reset-pass req is not valid' );
        }

        if ( !$data->hard ) {
            $req = ButtonAuthenticationRequest::getRequestByName( $reqs, 'skipReset' );
            if ( $req ) {
                $this->manager->removeAuthenticationSessionData( 'reset-pass' );
                return AuthenticationResponse::newPass();
            }
        }

        /** @var PasswordAuthenticationRequest $needReq */
        $needReq = $data->req ?? new PasswordAuthenticationRequest();
        '@phan-var PasswordAuthenticationRequest $needReq';
        if ( !$needReq->action ) {
            $needReq->action = AuthManager::ACTION_CHANGE;
        }
        $needReq->required = $data->hard ? AuthenticationRequest::REQUIRED
            : AuthenticationRequest::OPTIONAL;
        $needReqs = [ $needReq ];
        if ( !$data->hard ) {
            $needReqs[] = new ButtonAuthenticationRequest(
                'skipReset',
                wfMessage( 'authprovider-resetpass-skip-label' ),
                wfMessage( 'authprovider-resetpass-skip-help' )
            );
        }

        /** @var PasswordAuthenticationRequest $req */
        $req = AuthenticationRequest::getRequestByClass( $reqs, get_class( $needReq ) );
        '@phan-var PasswordAuthenticationRequest $req';
        if ( !$req || !array_key_exists( 'retype', $req->getFieldInfo() ) ) {
            return AuthenticationResponse::newUI( $needReqs, $data->msg, 'warning' );
        }

        if ( $req->password !== $req->retype ) {
            return AuthenticationResponse::newUI( $needReqs, new Message( 'badretype' ), 'error' );
        }

        $req->username = $user->getName();
        $status = $this->manager->allowsAuthenticationDataChange( $req );
        if ( !$status->isGood() ) {
            return AuthenticationResponse::newUI( $needReqs, $status->getMessage(), 'error' );
        }
        $this->manager->changeAuthenticationData( $req );

        $this->manager->removeAuthenticationSessionData( 'reset-pass' );
        return AuthenticationResponse::newPass();
    }
}