CWRUChielLab / CASAuth

A modified version of the CASAuth plugin found here: http://www.mediawiki.org/wiki/Extension:CASAuthentication
11 stars 5 forks source link

Feature request: MediaWiki API integration #7

Open davidhedlund opened 6 years ago

davidhedlund commented 6 years ago

Cannot login to wiki via MediaWiki API; as a result automated API-based libraries like pywikibot cannot login.

It's theoretically possible to login via API using the hash, but since the MD5 hash is salted the end user has no way of obtaining their own password.

The CASAuth extension is installed in Free Software Directory (aka the Directory). The Free Software Foundation needs pywikibot to occasionally batch upload tens of thousands files to the Directory to sync the wiki with the data provided by the Debian package repository.

jpgill86 commented 6 years ago

Hi @davidhedlund,

I'm afraid I can provide only limited help since I am not currently developing in MediaWiki and have stopped maintaining this project.

I haven't used pywikibot before, but with MediaWiki 1.27.1 I was able to interact with the MediaWiki API using a bot from a Bash command line script. As of 1.27, logins can be facilitated using the clientlogin action or the login action (see these docs). If I recall correctly from my time developing my script, the latter conflicted with CASAuth, but the former definitely worked.

If it's possible to configure pywikibot to use clientlogin rather than login, that may fix your issue. If you find a solution, please post it here in case others have the same issue.

I can provide this excerpt from my Bash script in case that's helpful. Note that the original script has only been tested on MediaWiki 1.27.1, and this short excerpt hasn't been tested at all.

Good luck to you!

EDIT: If I recall correctly, my bot account was created while I had temporarily disabled CASAuth, so the password salting performed by CASAuth wasn't an issue for me.

#!/bin/bash

######################################################################
##                                                                  ##
## Global variables                                                 ##
##                                                                  ##
######################################################################

# The user name and, optionally, the password of a wiki account that
# will be used to interact with the wiki through the MediaWiki API.
# User names and passwords are case-sensitive. If the password is left
# blank here, you will be prompted for it when it is needed.

BOTNAME=MyBot                                       # CHANGE THIS AS NEEDED
BOTPASS=

# MediaWiki provides an API for querying the server. We will use it
# to log into the bot account.

WIKIAPI="https://$(hostname).case.edu/w/api.php"    # CHANGE THIS AS NEEDED

# Maintaining a login session requires that we store an HTTP cookie
# file.

COOKIE="/tmp/cookie.txt"

# The functions below set and use the following additional global
# variables

BOTISLOGGEDIN=false

######################################################################
##                                                                  ##
## Function: loginbot                                               ##
##                                                                  ##
## Logs the bot into the wiki so that it can perform automated      ##
## tasks. Prompts for the bot account password if one was not       ##
## provided as an argument when the function was called. If         ##
## successful, the function saves an HTTP cookie associated with    ##
## the login session and updates the BOTISLOGGEDIN global variable. ##
##                                                                  ##
######################################################################

function loginbot {

    local BOTPASS=$1
    local RESPONSE
    local LOGINTOKEN
    local LOGINSTATUS
    local WARNING
    local ERROR

    # If the bot account password is not passed as a function
    # argument, ask for it now.

    if [ -z "$BOTPASS" ]; then
        read -s -r -p "Enter $BOTNAME's password: " BOTPASS
        echo
        echo
    fi

    # Delete any old cookie files.

    rm -f "$COOKIE"

    # Logging into the wiki is a two-step process. This first step
    # should result in the receipt of an HTTP cookie (saved to a file
    # using -c) and a login token (a random string) that is paired to
    # the cookie.

    RESPONSE=$(curl -s -c "$COOKIE" $WIKIAPI \
        -d "action=query" \
        -d "meta=tokens" \
        -d "type=login" \
        -d "format=json")

    LOGINTOKEN=$(echo $RESPONSE | jq '.query.tokens.logintoken | @uri' | tr -d '"')

    if [ "$LOGINTOKEN" == "null" ]; then
        WARNING=$(echo $RESPONSE | jq '.warnings.tokens | .["*"]' | tr -d '"')
        echo >&2 "Login token retrieval failed: $WARNING"
        return 1
    fi

    # The second step for logging in submits the cookie (submitted
    # from a file using -b) and login token, along with the username
    # and password, and should result in the receipt of a modified
    # HTTP cookie (saved to the same file using -c). A valid return
    # URL is required to log in but is not used by this script.

    RESPONSE=$(curl -s -b "$COOKIE" -c "$COOKIE" $WIKIAPI \
        -d "action=clientlogin" \
        -d "format=json" \
        -d "username=$BOTNAME" \
        -d "password=$BOTPASS" \
        -d "loginreturnurl=http://localhost" \
        -d "logintoken=$LOGINTOKEN")

    LOGINSTATUS=$(echo $RESPONSE | jq '.clientlogin.status' | tr -d '"')

    if [ "$LOGINSTATUS" == "FAIL" ]; then
        ERROR=$(echo $RESPONSE | jq '.clientlogin.message' | tr -d '"')
        echo >&2 "Login failed: $ERROR"
        return 1
    fi

    if [ "$LOGINSTATUS" == "PASS" ]; then
        echo "Login successful."
        BOTISLOGGEDIN=true
        return 0
    else
        echo >&2 "Login failed: Result was expected to be 'PASS' but got '$LOGINSTATUS' instead"
        BOTISLOGGEDIN=false
        return 1
    fi

} # end loginbot

######################################################################
##                                                                  ##
## Function: retryprompt                                            ##
##                                                                  ##
## Asks the user if they want to retry some action that failed.     ##
## Aborts if they press any key other than 'r'.                     ##
##                                                                  ##
######################################################################

function retryprompt {

    local PROMPT

    read -r -n 1 -p "Press 'r' to retry, or any other key to quit: " PROMPT
    echo
    if [ "$PROMPT" != "r" ]; then
        exit 0
    else
        echo
    fi

} # end retryprompt

######################################################################
##                                                                  ##
## Main: Functions are actually called here                         ##
##                                                                  ##
######################################################################

# Since this script is very powerful, require sudo

if [ "$(whoami)" != "root" ]; then
    echo >&2 "Aborted: superuser priveleges needed (rerun with sudo)"
    exit 1
fi

# Log into the account

if [ -n "$BOTPASS" ]; then
    echo "Attempting bot login using password stored in this script ..."
    echo
    loginbot "$BOTPASS"
else
    loginbot
fi

until $BOTISLOGGEDIN; do
    echo
    retryprompt
    loginbot
done
echo
continueprompt

# DO STUFF WITH YOUR BOT HERE
zhuyifei1999 commented 6 years ago

EDIT: If I recall correctly, my bot account was created while I had temporarily disabled CASAuth, so the password salting performed by CASAuth wasn't an issue for me.

Yes, that was exactly the issue. For a normal user there is no way of knowing the hashed salted 'string' MediaWiki recognizes as the account's main password. Wondering if it is possible that CASAuth integrates with CAS so that 'somehow' the passwords that gets sent to MediaWiki API is verified against CAS, instead of the salted hash.

davidhedlund commented 6 years ago

If it's possible to configure pywikibot to use clientlogin rather than login, that may fix your issue. If you find a solution, please post it here in case others have the same issue.

clientlogin (https://www.mediawiki.org/wiki/API:Login) is for those that is more 'interactive' pywikibot is more of a bot framework.

I want to confirm that your script works (even if this cannot be used to solve our problem):

With my user account created while CASAuth enabled:

With my user account created with CASAuth temporarily disabled: