mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/OATHAuth
synced 2024-11-24 00:05:24 +00:00
Make the TOTP disable form only remove that single key
Bug: T242031 Change-Id: Iad07292cc96537e8ebd72da65e8f1e922cba3eca
This commit is contained in:
parent
c09ec34213
commit
c6a621d31c
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
namespace MediaWiki\Extension\OATHAuth\HTMLForm;
|
namespace MediaWiki\Extension\OATHAuth\HTMLForm;
|
||||||
|
|
||||||
use MediaWiki\Extension\OATHAuth\Module\TOTP;
|
use MediaWiki\Extension\OATHAuth\Key\TOTPKey;
|
||||||
use MediaWiki\Logger\LoggerFactory;
|
use MediaWiki\Logger\LoggerFactory;
|
||||||
use Message;
|
use Message;
|
||||||
use MWException;
|
use MWException;
|
||||||
|
@ -52,27 +52,35 @@ class TOTPDisableForm extends OATHAuthOOUIHTMLForm {
|
||||||
return [ 'oathauth-throttled', Message::durationParam( 60 ) ];
|
return [ 'oathauth-throttled', Message::durationParam( 60 ) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
$module = $this->oathUser->getModule();
|
foreach ( $this->oathUser->getKeys() as $key ) {
|
||||||
if (
|
if ( !( $key instanceof TOTPKey ) ) {
|
||||||
( $module instanceof TOTP ) &&
|
continue;
|
||||||
!$module->verify( $this->oathUser, [ 'token' => $formData['token'] ] )
|
}
|
||||||
) {
|
|
||||||
LoggerFactory::getInstance( 'authentication' )->info(
|
if ( !$key->verify( [ 'token' => $formData['token'] ], $this->oathUser ) ) {
|
||||||
'OATHAuth {user} failed to provide a correct token while disabling 2FA from {clientip}', [
|
continue;
|
||||||
'user' => $this->getUser()->getName(),
|
}
|
||||||
'clientip' => $this->getRequest()->getIP(),
|
|
||||||
]
|
$this->oathRepo->removeKey(
|
||||||
|
$this->oathUser,
|
||||||
|
$key,
|
||||||
|
$this->getRequest()->getIP(),
|
||||||
|
true
|
||||||
);
|
);
|
||||||
|
|
||||||
// Increase rate limit counter for failed request
|
return true;
|
||||||
$this->getUser()->pingLimiter( 'badoath' );
|
|
||||||
|
|
||||||
return [ 'oathauth-failedtovalidateoath' ];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->oathUser->setKeys();
|
LoggerFactory::getInstance( 'authentication' )->info(
|
||||||
$this->oathRepo->removeAll( $this->oathUser, $this->getRequest()->getIP(), true );
|
'OATHAuth {user} failed to provide a correct token while disabling 2FA from {clientip}', [
|
||||||
|
'user' => $this->getUser()->getName(),
|
||||||
|
'clientip' => $this->getRequest()->getIP(),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
return true;
|
// Increase rate limit counter for failed request
|
||||||
|
$this->getUser()->pingLimiter( 'badoath' );
|
||||||
|
|
||||||
|
return [ 'oathauth-failedtovalidateoath' ];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,9 +81,9 @@ class OATHUser {
|
||||||
/**
|
/**
|
||||||
* Get the key associated with this user.
|
* Get the key associated with this user.
|
||||||
*
|
*
|
||||||
* @return IAuthKey[]|array
|
* @return IAuthKey[]
|
||||||
*/
|
*/
|
||||||
public function getKeys() {
|
public function getKeys(): array {
|
||||||
return $this->keys;
|
return $this->keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -194,6 +194,61 @@ class OATHUserRepository implements LoggerAwareInterface {
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param OATHUser $user
|
||||||
|
* @param IAuthKey $key
|
||||||
|
* @param string $clientInfo
|
||||||
|
* @param bool $self Whether they disabled it themselves
|
||||||
|
*/
|
||||||
|
public function removeKey( OATHUser $user, IAuthKey $key, string $clientInfo, bool $self ) {
|
||||||
|
$keyId = $key->getId();
|
||||||
|
if ( !$keyId ) {
|
||||||
|
throw new InvalidArgumentException( 'A non-persisted key cannot be removed' );
|
||||||
|
}
|
||||||
|
|
||||||
|
$userId = $this->centralIdLookupFactory->getLookup()
|
||||||
|
->centralIdFromLocalUser( $user->getUser() );
|
||||||
|
$this->dbProvider->getPrimaryDatabase( 'virtual-oathauth' )
|
||||||
|
->newDeleteQueryBuilder()
|
||||||
|
->deleteFrom( 'oathauth_devices' )
|
||||||
|
->where( [ 'oad_user' => $userId, 'oad_id' => $keyId ] )
|
||||||
|
->caller( __METHOD__ )
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
// TODO: figure this out from the key itself
|
||||||
|
// After calling ->disable(), getModule() will return null so this
|
||||||
|
// has to be done before.
|
||||||
|
$keyType = $user->getModule()->getName();
|
||||||
|
|
||||||
|
// Remove the key from the user object
|
||||||
|
$user->setKeys(
|
||||||
|
array_values(
|
||||||
|
array_filter(
|
||||||
|
$user->getKeys(),
|
||||||
|
static function ( IAuthKey $key ) use ( $keyId ) {
|
||||||
|
return $key->getId() !== $keyId;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( !$user->getKeys() ) {
|
||||||
|
$user->setModule( null );
|
||||||
|
}
|
||||||
|
|
||||||
|
$userName = $user->getUser()->getName();
|
||||||
|
$this->cache->delete( $userName );
|
||||||
|
|
||||||
|
$this->logger->info( 'OATHAuth removed {oathtype} key {key} for {user} from {clientip}', [
|
||||||
|
'key' => $keyId,
|
||||||
|
'user' => $userName,
|
||||||
|
'clientip' => $clientInfo,
|
||||||
|
'oathtype' => $keyType,
|
||||||
|
] );
|
||||||
|
|
||||||
|
Manager::notifyDisabled( $user, $self );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param OATHUser $user
|
* @param OATHUser $user
|
||||||
* @param string $clientInfo
|
* @param string $clientInfo
|
||||||
|
|
|
@ -93,8 +93,9 @@ class OATHUserRepositoryTest extends MediaWikiIntegrationTestCase {
|
||||||
// Test looking it up again from the database
|
// Test looking it up again from the database
|
||||||
$this->assertArrayEquals( [ $key ], $repository->findByUser( $user )->getKeys() );
|
$this->assertArrayEquals( [ $key ], $repository->findByUser( $user )->getKeys() );
|
||||||
|
|
||||||
$repository->remove(
|
$repository->removeKey(
|
||||||
$oathUser,
|
$oathUser,
|
||||||
|
$key,
|
||||||
'127.0.0.1',
|
'127.0.0.1',
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue