mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/LoginNotify
synced 2024-11-15 11:11:02 +00:00
f431bc7196
Bug: T163434 Change-Id: I3bd4a725291c8d0d10ff002c481064fe67f8df39 Depends-On: I52cc257e593da3d6c3b01a909e554a950225aec8
320 lines
9.8 KiB
PHP
320 lines
9.8 KiB
PHP
<?php
|
|
|
|
use \Psr\Log\NullLogger;
|
|
use Wikimedia\TestingAccessWrapper;
|
|
|
|
// @codingStandardsIgnoreStart Generic.Files.LineLength.TooLong
|
|
class LoginNotifyTests extends MediaWikiTestCase {
|
|
|
|
/** @var LoginNotify */
|
|
private $inst;
|
|
|
|
public function setUpLoginNotify() {
|
|
$config = new HashConfig( [
|
|
"LoginNotifyAttemptsKnownIP" => 15,
|
|
"LoginNotifyExpiryKnownIP" => 604800,
|
|
"LoginNotifyAttemptsNewIP" => 5,
|
|
"LoginNotifyExpiryNewIP" => 1209600,
|
|
"LoginNotifyCheckKnownIPs" => true,
|
|
"LoginNotifyEnableOnSuccess" => true,
|
|
"LoginNotifyEnableForPriv" => [ "editinterface", "userrights" ],
|
|
"LoginNotifySecretKey" => "Secret Stuff!",
|
|
"LoginNotifyCookieExpire" => 15552000,
|
|
"LoginNotifyCookieDomain" => null,
|
|
"LoginNotifyMaxCookieRecords" => 6,
|
|
"LoginNotifyCacheLoginIPExpiry" => 60*60*24*60
|
|
] );
|
|
$this->inst = TestingAccessWrapper::newFromObject(
|
|
new LoginNotify(
|
|
$config,
|
|
new HashBagOStuff
|
|
)
|
|
);
|
|
$this->inst->setLogger( new NullLogger );
|
|
}
|
|
|
|
public function setUp() {
|
|
parent::setUp();
|
|
$this->setUpLoginNotify();
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideGetIPNetwork
|
|
*/
|
|
public function testGetIPNetwork( $ip, $expected ) {
|
|
$actual = $this->inst->getIPNetwork( $ip );
|
|
$this->assertSame( $expected, $actual );
|
|
}
|
|
|
|
public function provideGetIPNetwork() {
|
|
return [
|
|
[ '127.0.0.1', '127.0.0.' ],
|
|
[ '118.221.191.18', '118.221.191.' ],
|
|
[ '::1', '0:0:0:0:' ],
|
|
[ '1:2:3:4:5:6:7:8', '1:2:3:4:' ],
|
|
[ '1:ffe1::7:8', '1:FFE1:0:0:' ],
|
|
[ 'd3::1:2:3:4:5:6', 'D3:0:1:2:' ]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @expectedException UnexpectedValueException
|
|
*/
|
|
public function testGetIPNetworkInvalid() {
|
|
$this->inst->getIPNetwork( 'localhost' );
|
|
}
|
|
|
|
/**
|
|
* @param $username string
|
|
* @param $year int
|
|
* @param $salt string
|
|
* @param $expected string
|
|
* @dataProvider provideGenerateUserCookieRecord
|
|
*/
|
|
public function testGenerateUserCookieRecord( $username, $year, $salt, $expected ) {
|
|
$actual = $this->inst->generateUserCookieRecord( $username, $year, $salt );
|
|
$this->assertEquals( $expected, $actual );
|
|
}
|
|
|
|
public function provideGenerateUserCookieRecord() {
|
|
return [
|
|
[ 'Foo', 2011, 'a4321f', '2011-a4321f-8oerxg4l59zpiu0by7m2to1b4cjeer4' ],
|
|
[ 'Foo', 2011, 'A4321f', '2011-A4321f-in65gc2i9czojfopkeieijc0ek8j5vu' ],
|
|
[ 'Foo', 2015, 'a4321f', '2015-a4321f-2hf2zh9h3afv79b1u4l474ozc0by2xe' ],
|
|
[ 'FOo', 2011, 'a4321f', '2011-a4321f-d0dhdzxg3te3yd3np6xdfrwdrckop7m' ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideIsUserRecordGivenCookie
|
|
*/
|
|
public function testIsUserRecordGivenCookie( User $user, $cookieRecord, $expected, $desc ) {
|
|
$actual = $this->inst->isUserRecordGivenCookie( $user, $cookieRecord );
|
|
$this->assertEquals( $expected, $actual, "For {$user->getName()} on test $desc." );
|
|
}
|
|
|
|
public function provideIsUserRecordGivenCookie() {
|
|
$this->setUpLoginNotify();
|
|
$u = User::newFromName( 'Foo', false );
|
|
$cookie1 = $this->inst->generateUserCookieRecord( 'Foo2' );
|
|
$cookie2 = $this->inst->generateUserCookieRecord( 'Foo' );
|
|
$cookie3 = $this->inst->generateUserCookieRecord( 'Foo', gmdate( 'Y' ) - 2 );
|
|
$cookie4 = $this->inst->generateUserCookieRecord( 'Foo', gmdate( 'Y' ), 'RAND' );
|
|
$cookie5 = $this->inst->generateUserCookieRecord( 'Foo', gmdate( 'Y' ) - 4 );
|
|
return [
|
|
[ $u, '2015-in65gc2i9czojfopkeieijc0ek8j5vu', false, "no salt" ],
|
|
[ $u, '2011-A4321f-in65gc2i9czojfopkeieijc0ek8j5vu', false, "too old" ],
|
|
[ $u, $cookie1, false, "name mismatch" ],
|
|
[ $u, $cookie2, true, "normal" ],
|
|
[ $u, $cookie3, true, "2 year old" ],
|
|
[ $u, $cookie4, true, "Specific salt" ],
|
|
[ $u, $cookie5, false, "4 year old" ],
|
|
];
|
|
}
|
|
|
|
public function testGetPrevLoginCookie() {
|
|
$req = new FauxRequest();
|
|
$res1 = $this->inst->getPrevLoginCookie( $req );
|
|
$this->assertEquals( '', $res1, "no cookie set" );
|
|
|
|
$req->setCookie( 'loginnotify_prevlogins', 'foo', '' );
|
|
$res2 = $this->inst->getPrevLoginCookie( $req );
|
|
$this->assertEquals( 'foo', $res2, "get dummy cookie" );
|
|
}
|
|
|
|
public function testGetKey() {
|
|
$user1 = User::newFromName( 'Foo_bar' );
|
|
// Make sure proper normalization happens.
|
|
$user2 = User::newFromName( 'Foo__bar' );
|
|
$user3 = User::newFromName( 'Somebody' );
|
|
|
|
$this->assertEquals(
|
|
'global:loginnotify:new:ok2qitd5efi25tzjy2l3el4n57g6l3l',
|
|
$this->inst->getKey( $user1, 'new' )
|
|
);
|
|
$this->assertEquals(
|
|
'global:loginnotify:known:ok2qitd5efi25tzjy2l3el4n57g6l3l',
|
|
$this->inst->getKey( $user1, 'known' )
|
|
);
|
|
$this->assertEquals(
|
|
'global:loginnotify:new:ok2qitd5efi25tzjy2l3el4n57g6l3l',
|
|
$this->inst->getKey( $user2, 'new' )
|
|
);
|
|
$this->assertEquals(
|
|
'global:loginnotify:new:tuwpi7e2h9pidovmaxxswk6aq327ewg',
|
|
$this->inst->getKey( $user3, 'new' )
|
|
);
|
|
}
|
|
|
|
public function testCheckAndIncKey() {
|
|
$key = 'global:loginnotify:new:tuwpi7e2h9pidovmaxxswk6aq327ewg';
|
|
for ( $i = 1; $i < 5; $i++ ) {
|
|
$res = $this->inst->checkAndIncKey( $key, 5, 3600 );
|
|
$this->assertFalse( $res, "key check numb $i" );
|
|
}
|
|
$this->assertEquals( 5, $this->inst->checkAndIncKey( $key, 5, 3600 ) );
|
|
for ( $i = 1; $i < 5; $i++ ) {
|
|
$res = $this->inst->checkAndIncKey( $key, 5, 3600 );
|
|
$this->assertFalse( $res, "key check numb $i+5" );
|
|
}
|
|
$this->assertEquals( 10, $this->inst->checkAndIncKey( $key, 5, 3600 ) );
|
|
|
|
$key2 = 'global:loginnotify:known:tuwpi7e2h9pidovmaxxswk6aq327ewg';
|
|
for ( $i = 1; $i < 5; $i++ ) {
|
|
$res = $this->inst->checkAndIncKey( $key2, 1, 3600 );
|
|
$this->assertEquals( $i, $res, "key check interval 1 numb $i" );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideClearCounters
|
|
*/
|
|
public function testClearCounters( $key ) {
|
|
$user = User::newFromName( "Fred" );
|
|
$key = $this->inst->getKey( $user, $key );
|
|
|
|
$this->inst->checkAndIncKey( $key, 1, 3600 );
|
|
$res = $this->inst->checkAndIncKey( $key, 1, 3600 );
|
|
$this->assertEquals( 2, $res, "prior to clear" );
|
|
$this->inst->clearCounters( $user );
|
|
$res = $this->inst->checkAndIncKey( $key, 1, 3600 );
|
|
$this->assertEquals( 1, $res, "after clear" );
|
|
}
|
|
|
|
public function provideClearCounters() {
|
|
return [
|
|
[ 'new' ],
|
|
[ 'known' ],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @note Expected new cookie does not include first record, as
|
|
* first record depends on random numbers.
|
|
* @dataProvider provideCheckAndGenerateCookie
|
|
*/
|
|
public function testCheckAndGenerateCookie(
|
|
User $user,
|
|
$cookie,
|
|
$expectedSeenBefore,
|
|
$expectedNewCookie,
|
|
$desc
|
|
) {
|
|
list( $actualSeenBefore, $actualNewCookie ) =
|
|
$this->inst->checkAndGenerateCookie( $user, $cookie );
|
|
|
|
$this->assertEquals( $expectedSeenBefore, $actualSeenBefore,
|
|
"[Seen before] $desc"
|
|
);
|
|
$newCookieParts = explode( '.', $actualNewCookie, 2 );
|
|
if ( !isset( $newCookieParts[1] ) ) {
|
|
$newCookieParts[1] = '';
|
|
}
|
|
$this->assertTrue( $this->inst->isUserRecordGivenCookie( $user, $newCookieParts[0] ), "[Cookie new entry] $desc" );
|
|
$this->assertEquals( $expectedNewCookie, $newCookieParts[1], "[Cookie] $desc" );
|
|
}
|
|
|
|
public function provideCheckAndGenerateCookie() {
|
|
$this->setUpLoginNotify();
|
|
$y = gmdate( 'Y' );
|
|
$oldYear = $curYear - 3;
|
|
$u1 = User::newFromName( 'Foo' );
|
|
|
|
$cookie1 = $this->inst->generateUserCookieRecord( 'Foo' );
|
|
$cookie2 = $this->inst->generateUserCookieRecord( 'Foo' );
|
|
$cookieOld = $this->inst->generateUserCookieRecord( 'Foo', 2001 );
|
|
$cookieOtherUser = "$y-1veusdo-tr049njztrrvkkz4tk3kre8rm1zb134";
|
|
return [
|
|
[ $u1, '', false, '', "no cookie" ],
|
|
[
|
|
$u1,
|
|
"$cookieOtherUser",
|
|
false,
|
|
"$cookieOtherUser",
|
|
"not in cookie"
|
|
],
|
|
[
|
|
$u1,
|
|
"$cookieOtherUser.$y-.$y-abcdefg-8oerxg4l59zpiu0by7m2to1b4cjeer4.$oldYear-1234567-tpnsk00419wba6vjh1upif21qtst1cv",
|
|
false,
|
|
"$cookieOtherUser.$y-abcdefg-8oerxg4l59zpiu0by7m2to1b4cjeer4",
|
|
"old values in cookie"
|
|
],
|
|
[
|
|
$u1,
|
|
$cookieOld,
|
|
false,
|
|
"",
|
|
"Only old value"
|
|
],
|
|
[
|
|
$u1,
|
|
$cookie1,
|
|
true,
|
|
"",
|
|
"Normal success"
|
|
],
|
|
[
|
|
$u1,
|
|
"$cookieOtherUser.$cookie1.$cookie2."
|
|
. "$y-1234567-tpnsk00419wba6vjh1upif21qtst1cv.$cookie1",
|
|
true,
|
|
"$cookieOtherUser.$y-1234567-tpnsk00419wba6vjh1upif21qtst1cv",
|
|
"Remove all of current user."
|
|
],
|
|
[
|
|
$u1,
|
|
"$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser",
|
|
false,
|
|
"$cookieOtherUser.$cookieOtherUser.$cookieOtherUser."
|
|
. "$cookieOtherUser.$cookieOtherUser.$cookieOtherUser",
|
|
"Limit max number of records."
|
|
]
|
|
];
|
|
}
|
|
|
|
/**
|
|
* @dataProvider provideValidateCookieRecord
|
|
*/
|
|
public function testValidateCookieRecord( $cookie, $expected ) {
|
|
$this->assertEquals( $expected, $this->inst->validateCookieRecord( $cookie ) );
|
|
}
|
|
|
|
public function provideValidateCookieRecord() {
|
|
$y = gmdate( 'Y' );
|
|
return [
|
|
[ 'fdakslnknfaknasf', false ],
|
|
[ '--', false ],
|
|
[ '91-ffff-fdaskfjlasflasd', false ],
|
|
[ '2011-ffff-fdaskfjlasflasd', false ],
|
|
[ "$y-1veusdo-tr049njztrrvkkz4tk3kre8rm1zb134", true ],
|
|
[ "1991-1veusdo-tr049njztrrvkkz4tk3kre8rm1zb134", false ],
|
|
];
|
|
}
|
|
|
|
public function testIsUserInCache() {
|
|
$u = User::newFromName( 'Xyzzy' );
|
|
$uWrap = TestingAccessWrapper::newFromObject( $u );
|
|
$this->assertEquals( $this->inst->IsUserInCache( $u ), LoginNotify::NO_INFO_AVAILABLE );
|
|
|
|
$this->inst->cacheLoginIP( $u );
|
|
$this->assertTrue( $this->inst->IsUserInCache( $u ) );
|
|
|
|
$uWrap->mRequest = new FauxRequest();
|
|
$uWrap->mRequest->setIP( '10.1.2.3' );
|
|
|
|
$this->assertFalse( $this->inst->IsUserInCache( $u ) );
|
|
}
|
|
}
|
|
// @codingStandardsIgnoreEnd
|