Merge pull request #9823 from MrPetovan/task/9677-2fa-remember-device
Add "Remember this device" feature to two factor authentication
This commit is contained in:
commit
199f72ee3c
48 changed files with 988 additions and 248 deletions
|
@ -35,22 +35,24 @@ class StaticCookie extends Cookie
|
|||
|
||||
/**
|
||||
* Send a cookie - protected, internal function for test-mocking possibility
|
||||
* @see Cookie::setCookie()
|
||||
*
|
||||
* @link https://php.net/manual/en/function.setcookie.php
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $value [optional]
|
||||
* @param int $expire [optional]
|
||||
* @param bool $secure [optional]
|
||||
* @return bool
|
||||
*
|
||||
* @noinspection PhpMissingParentCallCommonInspection
|
||||
*
|
||||
* @link https://php.net/manual/en/function.setcookie.php
|
||||
*
|
||||
* @see Cookie::setCookie()
|
||||
*/
|
||||
protected function setCookie(string $name, string $value = null, int $expire = null, bool $secure = null)
|
||||
protected function setCookie(string $value = null, int $expire = null, bool $secure = null): bool
|
||||
{
|
||||
self::$_COOKIE[$name] = $value;
|
||||
self::$_COOKIE[self::NAME] = $value;
|
||||
self::$_EXPIRE = $expire;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function clearStatic()
|
||||
|
|
|
@ -128,30 +128,20 @@ class CookieTest extends MockedTest
|
|||
$cookie = new Cookie($this->config, $this->baseUrl, [], $cookieData);
|
||||
self::assertInstanceOf(Cookie::class, $cookie);
|
||||
|
||||
$assertData = $cookie->getData();
|
||||
|
||||
if (!$hasValues) {
|
||||
self::assertEmpty($assertData);
|
||||
if (isset($uid)) {
|
||||
self::assertEquals($uid, $cookie->get('uid'));
|
||||
} else {
|
||||
self::assertNotEmpty($assertData);
|
||||
if (isset($uid)) {
|
||||
self::assertObjectHasAttribute('uid', $assertData);
|
||||
self::assertEquals($uid, $assertData->uid);
|
||||
} else {
|
||||
self::assertObjectNotHasAttribute('uid', $assertData);
|
||||
}
|
||||
if (isset($hash)) {
|
||||
self::assertObjectHasAttribute('hash', $assertData);
|
||||
self::assertEquals($hash, $assertData->hash);
|
||||
} else {
|
||||
self::assertObjectNotHasAttribute('hash', $assertData);
|
||||
}
|
||||
if (isset($ip)) {
|
||||
self::assertObjectHasAttribute('ip', $assertData);
|
||||
self::assertEquals($ip, $assertData->ip);
|
||||
} else {
|
||||
self::assertObjectNotHasAttribute('ip', $assertData);
|
||||
}
|
||||
self::assertNull($cookie->get('uid'));
|
||||
}
|
||||
if (isset($hash)) {
|
||||
self::assertEquals($hash, $cookie->get('hash'));
|
||||
} else {
|
||||
self::assertNull($cookie->get('hash'));
|
||||
}
|
||||
if (isset($ip)) {
|
||||
self::assertEquals($ip, $cookie->get('ip'));
|
||||
} else {
|
||||
self::assertNull($cookie->get('ip'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,7 +186,7 @@ class CookieTest extends MockedTest
|
|||
$cookie = new Cookie($this->config, $this->baseUrl);
|
||||
self::assertInstanceOf(Cookie::class, $cookie);
|
||||
|
||||
self::assertEquals($assertTrue, $cookie->check($assertHash, $password, $userPrivateKey));
|
||||
self::assertEquals($assertTrue, $cookie->comparePrivateDataHash($assertHash, $password, $userPrivateKey));
|
||||
}
|
||||
|
||||
public function dataSet()
|
||||
|
@ -210,7 +200,6 @@ class CookieTest extends MockedTest
|
|||
'assertHash' => 'b657a15cfe7ed1f7289c9aa51af14a9a26c966f4ddd74e495fba103d8e872a39',
|
||||
'remoteIp' => '0.0.0.0',
|
||||
'serverArray' => [],
|
||||
'lifetime' => null,
|
||||
],
|
||||
'withServerArray' => [
|
||||
'serverKey' => 23,
|
||||
|
@ -220,32 +209,11 @@ class CookieTest extends MockedTest
|
|||
'assertHash' => 'b657a15cfe7ed1f7289c9aa51af14a9a26c966f4ddd74e495fba103d8e872a39',
|
||||
'remoteIp' => '1.2.3.4',
|
||||
'serverArray' => ['REMOTE_ADDR' => '1.2.3.4',],
|
||||
'lifetime' => null,
|
||||
],
|
||||
'withLifetime0' => [
|
||||
'serverKey' => 23,
|
||||
'uid' => 0,
|
||||
'password' => '234',
|
||||
'privateKey' => '124',
|
||||
'assertHash' => 'b657a15cfe7ed1f7289c9aa51af14a9a26c966f4ddd74e495fba103d8e872a39',
|
||||
'remoteIp' => '1.2.3.4',
|
||||
'serverArray' => ['REMOTE_ADDR' => '1.2.3.4',],
|
||||
'lifetime' => 0,
|
||||
],
|
||||
'withLifetime' => [
|
||||
'serverKey' => 23,
|
||||
'uid' => 0,
|
||||
'password' => '234',
|
||||
'privateKey' => '124',
|
||||
'assertHash' => 'b657a15cfe7ed1f7289c9aa51af14a9a26c966f4ddd74e495fba103d8e872a39',
|
||||
'remoteIp' => '1.2.3.4',
|
||||
'serverArray' => ['REMOTE_ADDR' => '1.2.3.4',],
|
||||
'lifetime' => 2 * 24 * 60 * 60,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function assertCookie($uid, $hash, $remoteIp, $lifetime)
|
||||
public function assertCookie($uid, $hash, $remoteIp)
|
||||
{
|
||||
self::assertArrayHasKey(Cookie::NAME, StaticCookie::$_COOKIE);
|
||||
|
||||
|
@ -258,11 +226,7 @@ class CookieTest extends MockedTest
|
|||
self::assertObjectHasAttribute('ip', $data);
|
||||
self::assertEquals($remoteIp, $data->ip);
|
||||
|
||||
if (isset($lifetime) && $lifetime !== 0) {
|
||||
self::assertLessThanOrEqual(time() + $lifetime, StaticCookie::$_EXPIRE);
|
||||
} else {
|
||||
self::assertLessThanOrEqual(time() + Cookie::DEFAULT_EXPIRE * 24 * 60 * 60, StaticCookie::$_EXPIRE);
|
||||
}
|
||||
self::assertLessThanOrEqual(time() + Cookie::DEFAULT_EXPIRE * 24 * 60 * 60, StaticCookie::$_EXPIRE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,7 +234,7 @@ class CookieTest extends MockedTest
|
|||
*
|
||||
* @dataProvider dataSet
|
||||
*/
|
||||
public function testSet($serverKey, $uid, $password, $privateKey, $assertHash, $remoteIp, $serverArray, $lifetime)
|
||||
public function testSet($serverKey, $uid, $password, $privateKey, $assertHash, $remoteIp, $serverArray)
|
||||
{
|
||||
$this->baseUrl->shouldReceive('getSSLPolicy')->andReturn(true)->once();
|
||||
$this->config->shouldReceive('get')->with('system', 'site_prvkey')->andReturn($serverKey)->once();
|
||||
|
@ -279,17 +243,20 @@ class CookieTest extends MockedTest
|
|||
$cookie = new StaticCookie($this->config, $this->baseUrl, $serverArray);
|
||||
self::assertInstanceOf(Cookie::class, $cookie);
|
||||
|
||||
$cookie->set($uid, $password, $privateKey, $lifetime);
|
||||
$cookie->setMultiple([
|
||||
'uid' => $uid,
|
||||
'hash' => $assertHash,
|
||||
]);
|
||||
|
||||
self::assertCookie($uid, $assertHash, $remoteIp, $lifetime);
|
||||
self::assertCookie($uid, $assertHash, $remoteIp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test two different set() of the cookie class (first set is invalid)
|
||||
* Test the set() method of the cookie class
|
||||
*
|
||||
* @dataProvider dataSet
|
||||
*/
|
||||
public function testDoubleSet($serverKey, $uid, $password, $privateKey, $assertHash, $remoteIp, $serverArray, $lifetime)
|
||||
public function testDoubleSet($serverKey, $uid, $password, $privateKey, $assertHash, $remoteIp, $serverArray)
|
||||
{
|
||||
$this->baseUrl->shouldReceive('getSSLPolicy')->andReturn(true)->once();
|
||||
$this->config->shouldReceive('get')->with('system', 'site_prvkey')->andReturn($serverKey)->once();
|
||||
|
@ -298,12 +265,10 @@ class CookieTest extends MockedTest
|
|||
$cookie = new StaticCookie($this->config, $this->baseUrl, $serverArray);
|
||||
self::assertInstanceOf(Cookie::class, $cookie);
|
||||
|
||||
// Invalid set, should get overwritten
|
||||
$cookie->set(-1, 'invalid', 'nothing', -234);
|
||||
$cookie->set('uid', $uid);
|
||||
$cookie->set('hash', $assertHash);
|
||||
|
||||
$cookie->set($uid, $password, $privateKey, $lifetime);
|
||||
|
||||
self::assertCookie($uid, $assertHash, $remoteIp, $lifetime);
|
||||
self::assertCookie($uid, $assertHash, $remoteIp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
62
tests/src/Security/TwoFactor/Factory/TrustedBrowserTest.php
Normal file
62
tests/src/Security/TwoFactor/Factory/TrustedBrowserTest.php
Normal file
|
@ -0,0 +1,62 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Test\src\Security\TwoFactor\Factory;
|
||||
|
||||
use Friendica\Security\TwoFactor\Factory\TrustedBrowser;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Logger\VoidLogger;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
class TrustedBrowserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testCreateFromTableRowSuccess()
|
||||
{
|
||||
$factory = new TrustedBrowser(new VoidLogger());
|
||||
|
||||
$row = [
|
||||
'cookie_hash' => Strings::getRandomHex(),
|
||||
'uid' => 42,
|
||||
'user_agent' => 'PHPUnit',
|
||||
'created' => DateTimeFormat::utcNow(),
|
||||
'last_used' => null,
|
||||
];
|
||||
|
||||
$trustedBrowser = $factory->createFromTableRow($row);
|
||||
|
||||
$this->assertEquals($row, $trustedBrowser->toArray());
|
||||
}
|
||||
|
||||
public function testCreateFromTableRowMissingData()
|
||||
{
|
||||
$this->expectException(\TypeError::class);
|
||||
|
||||
$factory = new TrustedBrowser(new VoidLogger());
|
||||
|
||||
$row = [
|
||||
'cookie_hash' => null,
|
||||
'uid' => null,
|
||||
'user_agent' => null,
|
||||
'created' => null,
|
||||
'last_used' => null,
|
||||
];
|
||||
|
||||
$trustedBrowser = $factory->createFromTableRow($row);
|
||||
|
||||
$this->assertEquals($row, $trustedBrowser->toArray());
|
||||
}
|
||||
|
||||
public function testCreateForUserWithUserAgent()
|
||||
{
|
||||
$factory = new TrustedBrowser(new VoidLogger());
|
||||
|
||||
$uid = 42;
|
||||
$userAgent = 'PHPUnit';
|
||||
|
||||
$trustedBrowser = $factory->createForUserWithUserAgent($uid, $userAgent);
|
||||
|
||||
$this->assertNotEmpty($trustedBrowser->cookie_hash);
|
||||
$this->assertEquals($uid, $trustedBrowser->uid);
|
||||
$this->assertEquals($userAgent, $trustedBrowser->user_agent);
|
||||
$this->assertNotEmpty($trustedBrowser->created);
|
||||
}
|
||||
}
|
46
tests/src/Security/TwoFactor/Model/TrustedBrowserTest.php
Normal file
46
tests/src/Security/TwoFactor/Model/TrustedBrowserTest.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Friendica\Test\src\Security\TwoFactor\Model;
|
||||
|
||||
use Friendica\Security\TwoFactor\Model\TrustedBrowser;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
class TrustedBrowserTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function test__construct()
|
||||
{
|
||||
$hash = Strings::getRandomHex();
|
||||
|
||||
$trustedBrowser = new TrustedBrowser(
|
||||
$hash,
|
||||
42,
|
||||
'PHPUnit',
|
||||
DateTimeFormat::utcNow()
|
||||
);
|
||||
|
||||
$this->assertEquals($hash, $trustedBrowser->cookie_hash);
|
||||
$this->assertEquals(42, $trustedBrowser->uid);
|
||||
$this->assertEquals('PHPUnit', $trustedBrowser->user_agent);
|
||||
$this->assertNotEmpty($trustedBrowser->created);
|
||||
}
|
||||
|
||||
public function testRecordUse()
|
||||
{
|
||||
$hash = Strings::getRandomHex();
|
||||
$past = DateTimeFormat::utc('now - 5 minutes');
|
||||
|
||||
$trustedBrowser = new TrustedBrowser(
|
||||
$hash,
|
||||
42,
|
||||
'PHPUnit',
|
||||
$past,
|
||||
$past
|
||||
);
|
||||
|
||||
$trustedBrowser->recordUse();
|
||||
|
||||
$this->assertEquals($past, $trustedBrowser->created);
|
||||
$this->assertGreaterThan($past, $trustedBrowser->last_used);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue