Reputation: 4650
I am writing a custom PrimaryAuthenticationProvider using MediaWiki for authorizing against my API instead of a MediaWiki database. But when I turn off the autocreation in LocalSettings.php the following way:
$wgGroupPermissions['*']['createaccount'] = false;
$wgGroupPermissions['*']['edit'] = false;
$wgGroupPermissions['*']['read'] = false;
when I try to login against my API with the correct credentials, the request fails with the error: Auto-creation of a local account failed: Automatic account creation is not allowed.
I suppose that I didn't stub one of my functions that should return the correct value that MediaWiki shouldn't create the user, but I can't find which one. When I enable $wgGroupPermissions['*']['createaccount'] = false;
, it starts working, but I want to disable creating accounts at all. How can I achieve this?
Here's my core for my authentication provider:
<?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
*/
use \MediaWiki\Auth\AuthenticationRequest;
use \MediaWiki\Auth\ButtonAuthenticationRequest;
use \MediaWiki\Auth\AbstractPrimaryAuthenticationProvider;
use \MediaWiki\Auth\AbstractPasswordPrimaryAuthenticationProvider;
use \MediaWiki\Auth\AuthManager;
use \MediaWiki\Auth\AuthenticationResponse;
/**
* A primary authentication provider that uses the password field in the 'user' table.
* @ingroup Auth
* @since 1.27
*/
class MyAEGEEApiPrimaryAuthenticationProvider
extends AbstractPasswordPrimaryAuthenticationProvider
{
/**
* @param array $params Settings
* the local password will be invalidated when authentication is changed
* and new users will not have a valid local password set.
*/
public function __construct( $params = [] ) {
parent::__construct( $params );
$this->baseHost = 'https://my.aegee.eu';
$this->loginUrl = $this->baseHost.'/services/oms-core-elixir/api/login';
$this->getUserUrl = $this->baseHost.'/services/oms-core-elixir/api/members/me';
}
/**
* Check if the password has expired and needs a reset
*
* @param string $username
* @param \stdClass $row A row from the user table
* @return \stdClass|null
*/
protected function getPasswordResetData( $username, $row ) {
return null;
}
/**
* Makes a /login request to core.
* @param{string} $username
* @param{string} $password
* @return $access_token
*/
private function tryLogin($username, $password) {
$ch = curl_init($this->loginUrl);
$body = json_encode(array(
'username'=>$username,
'password'=>$password
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Accept: application/json',
'Content-Type: application/json'
));
$response = curl_exec($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
if (is_resource($ch)) {
curl_close($ch);
}
if (0 !== $errno) {
wfDebugLog( 'MyAEGEEApi', 'Auth request returned error, failing.' );
return null;
}
$response_parsed = json_decode($response);
if (!$response_parsed->success) {
wfDebugLog( 'MyAEGEEApi', 'Auth request not successful, failing.' );
return null;
}
return $response_parsed->access_token;
}
/**
* Fetches user from core.
* @param{string} $access_token
* @return $user
*/
private function tryGetUser($access_token) {
$ch = curl_init($this->getUserUrl);
$body = json_encode(array(
'username'=>$username,
'password'=>$password
));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Accept: application/json',
'Content-Type: application/json',
'X-Auth-Token: '.$access_token
));
$response = curl_exec($ch);
$error = curl_error($ch);
$errno = curl_errno($ch);
if (is_resource($ch)) {
curl_close($ch);
}
if (0 !== $errno) {
wfDebugLog( 'MyAEGEEApi', 'User request returned error, failing.' );
return null;
}
$response_parsed = json_decode($response);
if (!$response_parsed->success) {
wfDebugLog( 'MyAEGEEApi', 'User request not successful, failing.' );
return null;
}
return $response_parsed->data;
}
/**
* All fun starts here.
*/
public function beginPrimaryAuthentication( array $reqs ) {
if ( !$reqs[0] ) {
wfDebugLog( 'MyAEGEEApi', 'No req, failing' );
return AuthenticationResponse::newAbstain();
}
$username = $reqs[0]->username;
$password = $reqs[0]->password;
if ( $username === null || $password === null ) {
wfDebugLog( 'MyAEGEEApi', 'Empty password or username, failing' );
return AuthenticationResponse::newAbstain();
}
$username = User::getCanonicalName( $username, 'usable' );
if ( $username === false ) {
wfDebugLog( 'MyAEGEEApi', 'Username not usable, failing' );
return AuthenticationResponse::newAbstain();
}
$access_token = $this->tryLogin($username, $password);
wfDebugLog( 'MyAEGEEApi', 'Got access token');
if (!$access_token) {
wfDebugLog( 'MyAEGEEApi', 'Access token failed, failing.');
return AuthenticationResponse::newAbstain();
}
wfDebugLog( 'MyAEGEEApi', 'Auth succeeded.');
$user = $this->tryGetUser($access_token);
if (!$user) {
wfDebugLog( 'MyAEGEEApi', 'User failed, failing.');
return AuthenticationResponse::newAbstain();
}
$username = $user->first_name.' '.$user->last_name;
wfDebugLog( 'MyAEGEEApi', 'User succeeded: '.$username);
return AuthenticationResponse::newPass( $username );
}
public function testUserCanAuthenticate( $username ) {
wfDebugLog( 'MyAEGEEApi', 'testUserCanAuthenticate start');
return true;
}
public function testUserExists( $username, $flags = User::READ_NORMAL ) {
wfDebugLog( 'MyAEGEEApi', 'testUserExists called');
return false;
}
/**
* A stub to just implement something.
*/
public function providerAllowsAuthenticationDataChange(
AuthenticationRequest $req, $checkData = true
) {
wfDebugLog( 'MyAEGEEApi', 'providerAllowsAuthenticationDataChange called');
return \StatusValue::newGood( 'ignored' );
}
/**
* A stub to just implement something.
*/
public function providerChangeAuthenticationData( AuthenticationRequest $req ) {
wfDebugLog( 'MyAEGEEApi', 'providerChangeAuthenticationData start');
}
/**
* A stub to just implement something.
*/
public function accountCreationType() {
wfDebugLog( 'MyAEGEEApi', 'accountCreationType called start');
return self::TYPE_NONE;
}
/**
* A stub to just implement something.
*/
public function testForAccountCreation( $user, $creator, array $reqs ) {
wfDebugLog( 'MyAEGEEApi', 'testForAccountCreation called');
}
/**
* A stub to just implement something.
*/
public function beginPrimaryAccountCreation( $user, $creator, array $reqs ) {
wfDebugLog( 'MyAEGEEApi', 'beginPrimaryAccountCreation called');
return AuthenticationResponse::newAbstain();
}
/**
* A stub to just implement something.
*/
public function finishAccountCreation( $user, $creator, AuthenticationResponse $res ) {
wfDebugLog( 'MyAEGEEApi', 'finishAccountCreation called');
return null;
}
}
And I get the following log in my debug console:
IP: 172.18.0.2
Start request POST /index.php/Special:UserLogin
HTTP HEADERS:
HOST: localhost
USER-AGENT: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko/20100101 Firefox/70.0
CONTENT-LENGTH: 208
ACCEPT: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
ACCEPT-ENCODING: gzip, deflate
ACCEPT-LANGUAGE: en-US,en;q=0.5
CONTENT-TYPE: application/x-www-form-urlencoded
COOKIE: __test=1; Webstorm-87597fa=870153af-a572-4e74-a5d9-c7709900917d; sails.sid=s%3ASAu438xerbZoNUyYmK9AIxJCEBE_KkV0.%2FEydN2aTQPEaxd%2BhxAq3dkfHy1YDlnv2joc82HyQ%2Bi8; mediawiki_session=s0i4jb61ka5ckdhnfqoids1mjb9rl520
DNT: 1
ORIGIN: http://localhost
REFERER: http://localhost/index.php/Special:UserLogin
UPGRADE-INSECURE-REQUESTS: 1
X-FORWARDED-FOR: <IP>
X-FORWARDED-HOST: localhost
X-FORWARDED-PORT: 80
X-FORWARDED-PROTO: http
X-FORWARDED-SERVER: 28ef83ab1502
X-REAL-IP: <IP>
[caches] cluster: APCUBagOStuff, WAN: mediawiki-main-default, stash: db-replicated, message: APCUBagOStuff, session: APCUBagOStuff
[caches] LocalisationCache: using store LCStoreDB
[DBConnection] Wikimedia\Rdbms\LoadBalancer::openConnection: calling initLB() before first connection.
[DBReplication] Cannot use ChronologyProtector with EmptyBagOStuff.
[DBReplication] Wikimedia\Rdbms\LBFactory::getChronologyProtector: using request info {
"IPAddress": "172.18.0.2",
"UserAgent": "Mozilla\/5.0 (Macintosh; Intel Mac OS X 10.15; rv:70.0) Gecko\/20100101 Firefox\/70.0",
"ChronologyProtection": false,
"ChronologyPositionIndex": 0,
"ChronologyClientId": null
}
[DBConnection] Wikimedia\Rdbms\LoadBalancer::openLocalConnection: connected to database 0 at 'maria-mediawiki'.
[session] Session "s0i4jb61ka5ckdhnfqoids1mjb9rl520" requested without UserID cookie
[MessageCache] MessageCache::load: Loading en-gb... local cache is empty, global cache is expired/volatile, loading from database
Unstubbing $wgParser on call of $wgParser::firstCallInit from MessageCache->transform
Parser: using preprocessor: Preprocessor_DOM
Unstubbing $wgLang on call of $wgLang::_unstub from ParserOptions->__construct
[MyAEGEEApi] providerAllowsAuthenticationDataChange called
User::getBlockedStatus: checking...
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): MediaWiki\Auth\AuthManager->setAuthenticationSessionData/MediaWiki\Session\Session->setSecret/MediaWiki\Session\Session->getSecretKeys/MediaWiki\Session\Session->set/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): MediaWiki\Auth\AuthManager->setAuthenticationSessionData/MediaWiki\Session\Session->setSecret/MediaWiki\Session\Session->getSecretKeys/MediaWiki\Session\Session->set/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): MediaWiki\Auth\ThrottlePreAuthenticationProvider->testForAuthentication/MediaWiki\Auth\AuthManager->setAuthenticationSessionData/MediaWiki\Session\Session->setSecret/MediaWiki\Session\Session->set/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" save: dataDirty=1 metaDirty=0 forcePersist=0
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): AuthManagerSpecialPage->performAuthenticationStep/MediaWiki\Auth\AuthManager->beginAuthentication/MediaWiki\Session\Session->setSecret/MediaWiki\Session\Session->set/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" save: dataDirty=1 metaDirty=0 forcePersist=0
[MyAEGEEApi] Got access token
[MyAEGEEApi] Auth succeeded.
[MyAEGEEApi] User succeeded: <user name>
[authentication] Primary login with MyAEGEEApiPrimaryAuthenticationProvider succeeded
[authentication] Auto-creating <user name> on login
[authentication] MediaWiki\Auth\AuthManager::autoCreateUser: IP lacks the ability to create or autocreate accounts
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): MediaWiki\Auth\AuthManager->beginAuthentication/MediaWiki\Auth\AuthManager->continueAuthentication/MediaWiki\Auth\AuthManager->autoCreateUser/MediaWiki\Session\Session->set/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" save: dataDirty=1 metaDirty=0 forcePersist=0
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" data dirty due to dirty(): AuthManagerSpecialPage->performAuthenticationStep/MediaWiki\Auth\AuthManager->beginAuthentication/MediaWiki\Auth\AuthManager->continueAuthentication/MediaWiki\Session\Session->remove/MediaWiki\Session\SessionBackend->dirty
[session] SessionBackend "s0i4jb61ka5ckdhnfqoids1mjb9rl520" save: dataDirty=1 metaDirty=0 forcePersist=0
[authevents] Login attempt
[MyAEGEEApi] providerAllowsAuthenticationDataChange called
MediaWiki::preOutputCommit: primary transaction round committed
MediaWiki::preOutputCommit: pre-send deferred updates completed
MediaWiki::preOutputCommit: LBFactory shutdown completed
Thanks for your answer in advance.
Upvotes: 1
Views: 1109
Reputation: 28200
You should probably disable the login providers you don't want instead of removing account creation permissions. That said, $wgGroupPermissions['*']['autocreateaccount'] = true;
should be enough to make your provider work while still disallowing normal account creation.
Upvotes: 4