Skip to content

Commit

Permalink
First commit for clientlogin + 2FA
Browse files Browse the repository at this point in the history
To be able to login with 2FA enabled, we need to use clientlogin instead of login.
In case of 2fa, a second http request is needed to send user, pass, totp (2fa code), fake loginreturnurl, rememberMe boolean and our classical token.

It's the first commit, curently the login form works as usual for normal users. users with 2fa enabled wan't be connected yet.

So this commit is not ready to be merged for now.

Ispirated by https://github.com/commons-app/apps-android-commons/blob/b0e8175003a686789474238dd293aa89d1e925c7/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java#L93

Bug: https://phabricator.wikimedia.org/T180279
  • Loading branch information
framawiki committed Nov 11, 2017
1 parent 2e53b2c commit 5b2bc26
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 33 deletions.
1 change: 1 addition & 0 deletions huggle/Localization/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
<string name="login-ro-title">Switch to read-only</string>
<string name="login-ro-question">One or more projects ($1) do not allow you to login with edit permissions (reason: $2). Do you want to switch to read-only mode instead?</string>
<string name="login-ro-info">Project $1 switched to read-only mode</string>
<string name="not-implemented">This functionality is not yet implemented. Please contact developers at [email protected].</string>
<string name="main-stat">$1 edits per minute, $2 reverts per minute, level $3</string>
<string name="main-menu-provider-stop">Stop provider</string>
<string name="main-menu-provider-resume">Resume provider</string>
Expand Down
5 changes: 5 additions & 0 deletions huggle/apiquery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ void ApiQuery::SetAction(const Action action)
this->ActionPart = "clearhasmsg";
this->UsingPOST = true;
return;
///! \todo ActionQuery still used ?
case ActionQuery:
this->ActionPart = "query";
this->IsContinuous = true;
Expand All @@ -416,6 +417,10 @@ void ApiQuery::SetAction(const Action action)
this->ActionPart = "login";
this->EnforceLogin = false;
return;
case ClientLogin:
this->ActionPart = "clientlogin";
this->EnforceLogin = false;
return;
case ActionLogout:
this->ActionPart = "logout";
return;
Expand Down
1 change: 1 addition & 0 deletions huggle/apiquery.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace Huggle
ActionClearHasMsg,
ActionQuery,
ActionLogin,
ClientLogin,
ActionLogout,
//ActionTokens,
ActionPurge,
Expand Down
95 changes: 62 additions & 33 deletions huggle/login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <QSslSocket>
#include <QDesktopServices>
#include <QMessageBox>
#include <QInputDialog>

#define LOGINFORM_LOGIN 0
#define LOGINFORM_SITEINFO 1
Expand Down Expand Up @@ -554,21 +555,20 @@ void Login::PerformLoginPart2(WikiSite *site)
this->Statuses[site] = WaitingForToken;
this->LoginQueries.remove(site);
query->DecRef();
query = new ApiQuery(ActionLogin, site);
query = new ApiQuery(ClientLogin, site);
this->LoginQueries.insert(site, query);
query->HiddenQuery = true;
//query->HiddenQuery = true;
query->IncRef();
if (hcfg->SystemConfig_BotPassword)
{
query->Parameters = "lgname=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&lgpassword=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&lgtoken=" + QUrl::toPercentEncoding(token);
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password);
} else
{
query->Parameters = "lgname=" + QUrl::toPercentEncoding(hcfg->SystemConfig_Username)
+ "&lgpassword=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&lgtoken=" + QUrl::toPercentEncoding(token);
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_Username)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password);
}
query->Parameters = query->Parameters + "&loginreturnurl=http://example.com/&rememberMe=1&logintoken=" + QUrl::toPercentEncoding(token);
query->UsingPOST = true;
query->Process();
}
Expand Down Expand Up @@ -1269,42 +1269,71 @@ bool Login::ProcessOutput(WikiSite *site)
ApiQuery *query = this->LoginQueries[site];
// Check what the result was
ApiQueryResult *result = query->GetApiQueryResult();
ApiQueryResultNode *ln = result->GetNode("login");
QString result_code = ln->GetAttribute("result");
QString reason = ln->GetAttribute("reason");
if (result_code.isEmpty())
ApiQueryResultNode *ln = result->GetNode("clientlogin");
QString status = ln->GetAttribute("status");
if (status.isEmpty())
{
this->DisplayError(_l("api.php-invalid-response"));
return false;
}
if (result_code == "Success")

if (status == "PASS")
return true;
if (result_code == "EmptyPass")
{
this->DisplayError(_l("login-password-empty"));
if (status == "UI") {
// Need a user interaction like captacha or 2FA
//QString v_id = ln->ChildNodes.at(0)->GetAttribute("id", "unknown");
//if (v_id == "TOTPAuthenticationRequest"){
if (true){
// 2FA is requierd (TOTP code needed)
QString totp = QInputDialog::getText(this, "Two factor authentification", "Please enter the 2FA code from your device:");
query = new ApiQuery(ClientLogin, site);
//query->HiddenQuery = true;
query->IncRef();
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&OATHToken=" + totp + "&loginreturnurl=http://example.com/&rememberMe=1&logintoken=" + QUrl::toPercentEncoding(this->Tokens[site]);
query->UsingPOST = true;
query->Process();
ApiQueryResult *result = query->GetApiQueryResult();
ApiQueryResultNode *ln = result->GetNode("clientlogin");
}
return false;
}
if (result_code == "WrongPass")
{
/// \bug This sometimes doesn't work properly
this->ui->lineEdit_password->setFocus();
this->DisplayError(_l("login-error-password"));
if (status == "REDIRECT")
// Need to login using another web service
this->DisplayError(_l("not-implemented"));
return false;
}
if (result_code == "NoName")
if (status == "FAIL")
{
this->DisplayError(_l("login-fail-wrong-name"));
return false;
}
if (result_code == "NotExists")
{
this->DisplayError(_l("login-username-doesnt-exist"));
QString message = ln->GetAttribute("message");
QString message_code = ln->GetAttribute("messagecode");
if (message_code == "wrongpassword") {
/// \bug This sometimes doesn't work properly
this->ui->lineEdit_password->setFocus();
this->DisplayError(_l("login-error-password"));
return false;
}
/// \todo Verify these error codes
if (message_code == "EmptyPass")
{
this->DisplayError(_l("login-password-empty"));
return false;
}
if (message_code == "NoName")
{
this->DisplayError(_l("login-fail-wrong-name"));
return false;
}
if (message_code == "NotExists")
{
this->DisplayError(_l("login-username-doesnt-exist"));
return false;
}
if (message.isEmpty())
message = message_code;
this->DisplayError(_l("login-api", message));
return false;
}
if (reason.isEmpty())
reason = result_code;
this->DisplayError(_l("login-api", reason));
return false;
}

void Login::on_ButtonOK_clicked()
Expand Down

0 comments on commit 5b2bc26

Please sign in to comment.