New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: MediaWiki API integration #7

Open
davidhedlund opened this Issue Jun 7, 2018 · 3 comments

Comments

Projects
None yet
3 participants
@davidhedlund

davidhedlund commented Jun 7, 2018

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

This comment has been minimized.

Show comment
Hide comment
@jpgill86

jpgill86 Jun 21, 2018

Member

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
Member

jpgill86 commented Jun 21, 2018

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

This comment has been minimized.

Show comment
Hide comment
@zhuyifei1999

zhuyifei1999 Jun 22, 2018

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.

zhuyifei1999 commented Jun 22, 2018

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

This comment has been minimized.

Show comment
Hide comment
@davidhedlund

davidhedlund Jun 22, 2018

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:

  • $ sudo ./test.sh

Attempting bot login using password stored in this script ...

Login failed: The supplied credentials could not be authenticated.

Press 'r' to retry, or any other key to quit:

With my user account created with CASAuth temporarily disabled:

  • Disabled CASAuth: 1) Commented this line in LocalSettings.php: require_once( "$IP/extensions/CASAuth/CASAuth.php" ); 2) Verified that CASauth didn't show up in Special:Version
  • Created a new account in Special:CreateAccount. User: Foo, a random 8 character long pass
  • Logged out.
  • Enabled the CASauth and verified it as described in the first step.
  • Modified test.sh: Added BOTNAME=Foo
  • $ sudo ./test.sh

Attempting bot login using password stored in this script ...

Login successful.

davidhedlund commented Jun 22, 2018

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:

  • $ sudo ./test.sh

Attempting bot login using password stored in this script ...

Login failed: The supplied credentials could not be authenticated.

Press 'r' to retry, or any other key to quit:

With my user account created with CASAuth temporarily disabled:

  • Disabled CASAuth: 1) Commented this line in LocalSettings.php: require_once( "$IP/extensions/CASAuth/CASAuth.php" ); 2) Verified that CASauth didn't show up in Special:Version
  • Created a new account in Special:CreateAccount. User: Foo, a random 8 character long pass
  • Logged out.
  • Enabled the CASauth and verified it as described in the first step.
  • Modified test.sh: Added BOTNAME=Foo
  • $ sudo ./test.sh

Attempting bot login using password stored in this script ...

Login successful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment