Page MenuHomePhabricator

API readapidenied with private wiki on v1.34
Closed, ResolvedPublic

Description

Hi,

I run a wiki instance on my lab network. The wiki is configured with the $wgGroupPermissions['*']['read'] = false; in the LocalSettings.php so the wiki is "private" (an account is needed).

I have a PHP script running on a second server that connect to the wiki to read the content of a page. The script is based on the example of the API documentation (https://www.mediawiki.org/wiki/API:Login). This was working great on the v1.33.1.

Since I upgraded to the v1.34, the script doesn't work anymore. When I try to fetch the token, I got this error from the API : Error requesting API. Error code : readapidenied. Error message : You need read permission to use this module.. I don't even have the opportunity to authenticate.

If I change the $wgGroupPermissions['*']['read'] to true, it's working. The problem occured just after the upgrade to the v1.34.

I found that somebody have the same problem : https://www.mediawiki.org/wiki/Topic:Vdrava0likcnvy6a but I didn't find his/her bug report.

Best regards,
Nulloz

Event Timeline

Hi @Nullozz, thanks for taking the time to report this and welcome to Wikimedia Phabricator!

The script is based on the example of the API documentation (https://www.mediawiki.org/wiki/API:Login).

Which of the auth methods on https://www.mediawiki.org/wiki/API:Login#Two_methods_to_authenticate does your code use?

@Aklapper I use the bot password. Here is my code :

class Wiki {
    private $endpoint;
    private $username;
    private $password;
    private $cookies;
    private $is_logged;

    /**
     * Constructeur.
     *
     * @param string $host l'hôte wikimedia
     * @param string $username le nom d'utilisateur
     * @param string $password le mot de passe
     */
    public function __construct(string $host, string $username, string $password) {
        $this->endpoint = "https://" . $host . "/api.php";
        $this->username = $username;
        $this->password = $password;

        $this->cookies = tempnam('', 'cookies');

        $this->is_logged = false;
    }

    /**
     * Destructeur.
     *
     * Supprime le fichier contenant les cookies
     */
    public function __destruct() {
        unlink($this->cookies);
    }

    /**
     * Authentification.
     *
     * @throws Exception
     */
    public function login() {
        if($this->is_logged)
            return ;

        $url_params = [
            "action" => "query",
            "meta" => "tokens",
            "type" => "login",
            "format" => "json"
        ];

        $result = $this->api_request("GET", $url_params);

        $post_data = [
            "action" => "login",
            "lgname" => $this->username,
            "lgpassword" => $this->password,
            "lgtoken" => $result["query"]["tokens"]["logintoken"],
            "format" => "json"
        ];

        $this->api_request("POST", NULL, $post_data);
        $this->is_logged = true;
    }

    public function get_html(int $page_id, int $section_id=NULL) {
        if(!$this->is_logged)
            $this->login();

        $url_params = [
            "action" => "parse",
            "pageid" => $page_id,
            "format" => "json",
            "disablelimitreport" => true,
        ];

        if($section_id)
            $url_params["section"] = $section_id;

        $result = $this->api_request("GET", $url_params);
        return $result["parse"]["text"]["*"];
    }

    /**
     * Réalise une requête GET ou POST à l'API.
     *
     * Vérifie et traite le code d'erreur.
     *
     * @param string $method la méthode (GET ou POST)
     * @param array $url_params les paramètres de l'URL
     * @param array $post_data les données $POST
     * @return mixed les données jsons
     * @throws Exception si une erreur est retournée par wikimedia
     */
    private function api_request(string $method, array $url_params=NULL, array $post_data=NULL) {
        $ch = curl_init();

        $url = $this->endpoint;
        if($url_params)
            $url .= "?" . http_build_query($url_params);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_COOKIEJAR, $this->cookies);
        curl_setopt($ch, CURLOPT_COOKIEFILE, $this->cookies);
        curl_setopt($ch, CURLOPT_CAINFO, "OwnCA.pem");

        if(strtoupper($method) == "POST") {
            curl_setopt($ch, CURLOPT_POST, true );
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
        }

        $output = curl_exec($ch);
        curl_close($ch);

        if($output === false)
            throw new Exception("CURL failed");

        $result = json_decode($output, true);

        if(key_exists("error", $result))
            throw new Exception("Error requesting API. Error code : ". $result['error']['code'] . ". Error message : " .  $result['error']['info']);

        return $result;
    }
}
Anomie subscribed.

Thanks for posting the code. I've confirmed the bug; seems to be a regression from T130112.

Change 572332 had a related patch set uploaded (by Anomie; owner: Anomie):
[mediawiki/core@master] API: Fix fetching login token from action=query&meta=tokens on private wikis

https://gerrit.wikimedia.org/r/572332

You can use the deprecated NeedToken response field as a tempoerary workaround. It is in the error message you get when calling the login API without a token, and it will contain a token.

@Tgr

Thank you. The day I updated the wiki to the version 1.34 is the same day I updated my server. It turns out that the new version of curl installed suffers "from a cookie parsing bug present in 7.64.0, fixed in
7.64.1, that made curl not accept cookies on domain names without any dots
" (https://curl.haxx.se/mail/archive-2019-06/0011.html) wich is my case. This workaround therefore didn't work either and I didn't understand why until now.

@Anomie

For what it's worth, I applied your patch and it works (great) again (It was after the patch and the fact that it still didn't work that I found the problem with curl 7.64).

Many thanks for the responsiveness and your work

Change 572332 merged by jenkins-bot:
[mediawiki/core@master] API: Fix fetching login token from action=query&meta=tokens on private wikis

https://gerrit.wikimedia.org/r/572332

Change 580097 had a related patch set uploaded (by Anomie; owner: Anomie):
[mediawiki/core@REL1_34] API: Fix fetching login token from action=query&meta=tokens on private wikis

https://gerrit.wikimedia.org/r/580097

Anomie claimed this task.

Change 580097 merged by jenkins-bot:
[mediawiki/core@REL1_34] API: Fix fetching login token from action=query&meta=tokens on private wikis

https://gerrit.wikimedia.org/r/580097