123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- <?php
- namespace Elgg;
- class PersistentLoginTest extends \PHPUnit_Framework_TestCase {
- /**
- * @var \ElggSession
- */
- protected $session;
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- protected $dbMock;
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- protected $cryptoMock;
- /**
- * @var \Elgg\PersistentLoginService
- */
- protected $svc;
- /**
- * @var \PHPUnit_Framework_MockObject_MockObject
- */
- protected $user123;
- /**
- * @var \ElggCookie
- */
- protected $lastCookieSet;
- /**
- * @var string
- */
- protected $mockToken;
- /**
- * @var string
- */
- protected $mockHash;
- /**
- * @var int
- */
- protected $timeSlept;
- /**
- * @var int
- */
- protected $thirtyDaysAgo;
- function setUp() {
- $this->thirtyDaysAgo = strtotime("-30 days");
- $this->mockToken = 'z' . str_repeat('a', 31);
- $this->mockHash = md5($this->mockToken);
- $this->user123 = $this->getMockElggUser(123);
- $this->session = \ElggSession::getMock();
- // mock DB
- $this->dbMock = $this->getMockBuilder('\Elgg\Database')
- ->disableOriginalConstructor()
- ->getMock();
- // use addslashes as ->sanitizeString (my local CLI doesn't have MySQL)
- $this->dbMock->expects($this->any())
- ->method('sanitizeString')
- ->will($this->returnCallback(array($this, 'mock_sanitizeString')));
- $this->cryptoMock = $this->getMockBuilder('\ElggCrypto')->getMock();
- $this->cryptoMock->expects($this->any())
- ->method('getRandomString')
- ->will($this->returnValue(str_repeat('a', 31)));
- $this->svc = $this->getSvcWithCookie("");
- }
- function testLoginSavesHashAndPutsTokenInCookieAndSession() {
- $this->dbMock->expects($this->once())
- ->method('insertData')
- ->will($this->returnCallback(array($this, 'mock_insertData')));
- $this->svc->makeLoginPersistent($this->user123);
- $this->assertSame($this->mockToken, $this->lastCookieSet->value);
- $this->assertSame($this->mockToken, $this->session->get('code'));
- }
- function testRemoveDeletesHashAndDeletesTokenFromCookieAndSession() {
- $this->svc = $this->getSvcWithCookie($this->mockToken);
- $this->dbMock->expects($this->once())
- ->method('deleteData')
- ->will($this->returnCallback(array($this, 'mock_deleteData')));
- $this->svc->removePersistentLogin();
- $this->assertSame('', $this->lastCookieSet->value);
- $this->assertSame($this->thirtyDaysAgo, $this->lastCookieSet->expire);
- $this->assertNull($this->session->get('code'));
- }
- function testRemoveWithoutCookieCantDeleteHash() {
- $this->dbMock->expects($this->never())
- ->method('deleteData');
- $this->svc->removePersistentLogin();
- $this->assertSame('', $this->lastCookieSet->value);
- $this->assertSame($this->thirtyDaysAgo, $this->lastCookieSet->expire);
- $this->assertNull($this->session->get('code'));
- }
- function testGettingUserFromKnownHashReturnsUser() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnCallback(array($this, 'mock_getDataRow')));
- $user = $this->svc->getUserFromHash($this->mockHash);
- $this->assertSame($this->user123, $user);
- }
- function testGettingUserFromMissingHashReturnsNull() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnValue(array()));
- $user = $this->svc->getUserFromHash($this->mockHash);
- $this->assertNull($user);
- }
- function testGettingMissingUserFromKnownHashReturnsNull() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnValue((object)array('guid' => 234)));
- $user = $this->svc->getUserFromHash($this->mockHash);
- $this->assertNull($user);
- }
- function testChangingOwnPasswordDeletesAllHashesAndMakesPersistent() {
- $subject = $this->user123;
- $modifier = $this->user123;
- $this->dbMock->expects($this->exactly(2))
- ->method('deleteData');
- // Here we can't make an expectation on mock_deleteAll because one
- // of the calls deletes all, and another deletes only a single hash.
- // We'd have to fix mock_deleteAll to handle it.
- // @todo replace this with a real DB test
- $this->dbMock->expects($this->once())
- ->method('insertData')
- ->will($this->returnCallback(array($this, 'mock_insertData')));
- $this->svc = $this->getSvcWithCookie('notempty');
- $this->svc->handlePasswordChange($subject, $modifier);
- $this->assertSame($this->mockToken, $this->lastCookieSet->value);
- $this->assertSame($this->mockToken, $this->session->get('code'));
- }
- function testChangingOwnPasswordWithNoCookieDoesntMakePersistent() {
- $subject = $this->user123;
- $modifier = $this->user123;
- $this->dbMock->expects($this->once())
- ->method('deleteData')
- ->will($this->returnCallback(array($this, 'mock_deleteAll')));
- $this->dbMock->expects($this->never())
- ->method('insertData');
- $this->svc->handlePasswordChange($subject, $modifier);
- $this->assertNull($this->lastCookieSet);
- $this->assertNull($this->session->get('code'));
- }
- function testChangingSomeoneElsesPasswordDoesntMakePersistent() {
- $subject = $this->user123;
- $modifier = $this->getMockElggUser(234);
- $this->dbMock->expects($this->atLeastOnce())
- ->method('deleteData')
- ->will($this->returnCallback(array($this, 'mock_deleteAll')));
- $this->dbMock->expects($this->never())
- ->method('insertData');
- $this->svc->handlePasswordChange($subject, $modifier);
- $this->assertNull($this->lastCookieSet);
- $this->assertNull($this->session->get('code'));
- }
- function testGettingUserFromValidClientReturnsUser() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnValue((object)array('guid' => 123)));
- $this->svc = $this->getSvcWithCookie($this->mockToken);
- $user = $this->svc->bootSession();
- $this->assertSame($this->user123, $user);
- }
- function testGetPersistedUser_invalidModernToken() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnValue(array()));
- $this->svc = $this->getSvcWithCookie('z' . str_repeat('b', 31));
- $user = $this->svc->bootSession();
- $this->assertNull($this->timeSlept);
- $this->assertSame('', $this->lastCookieSet->value);
- $this->assertSame($this->thirtyDaysAgo, $this->lastCookieSet->expire);
- $this->assertNull($user);
- }
- function testBootSessionWithInvalidLegacyTokenCausesDelayAndFailure() {
- $this->dbMock->expects($this->once())
- ->method('getDataRow')
- ->will($this->returnValue(array()));
- $this->svc = $this->getSvcWithCookie(str_repeat('b', 32));
- $user = $this->svc->bootSession();
- $this->assertSame(1, $this->timeSlept);
- $this->assertSame('', $this->lastCookieSet->value);
- $this->assertSame($this->thirtyDaysAgo, $this->lastCookieSet->expire);
- $this->assertNull($user);
- }
- function testReplaceLegacyTokenWithNoCookieDoesNothing() {
- $this->svc = $this->getSvcWithCookie('');
- $this->dbMock->expects($this->never())
- ->method('deleteData');
- $this->svc->replaceLegacyToken($this->user123);
- $this->assertNull($this->lastCookieSet);
- $this->assertNull($this->session->get('code'));
- }
- function testModernTokenCookiesAreNotReplaced() {
- $this->dbMock->expects($this->never())
- ->method('deleteData');
- $this->svc->replaceLegacyToken($this->user123);
- $this->assertNull($this->lastCookieSet);
- $this->assertNull($this->session->get('code'));
- }
- function testLegacyCookiesAreReplacedInDbCookieAndSession() {
- $this->svc = $this->getSvcWithCookie(str_repeat('a', 32));
- $this->dbMock->expects($this->atLeastOnce())
- ->method('deleteData');
- $this->dbMock->expects($this->once())
- ->method('insertData');
- $this->svc->replaceLegacyToken($this->user123);
- $this->assertSame($this->mockToken, $this->lastCookieSet->value);
- $this->assertSame($this->mockToken, $this->session->get('code'));
- }
- // mock \ElggUser which will return the GUID on ->guid reads
- function getMockElggUser($guid) {
- $user = $this->getMockBuilder('\ElggUser')
- ->disableOriginalConstructor()
- ->getMock();
- $user->expects($this->any())
- ->method('__get')
- ->with('guid')
- ->will($this->returnValue((int)$guid));
- return $user;
- }
- function mock_get_user($guid) {
- if ((int)$guid === 123) {
- return $this->user123;
- }
- return null;
- }
- function mock_elgg_set_cookie(\ElggCookie $cookie) {
- $this->lastCookieSet = $cookie;
- }
- function mock_sleep($seconds) {
- $this->timeSlept = $seconds;
- }
- function mock_sanitizeString($string) {
- // no need for dependence on MySQL here
- return addslashes($string);
- }
- /**
- * @param string $cookie_token
- * @return \Elgg\PersistentLoginService
- */
- protected function getSvcWithCookie($cookie_token = '') {
- $cookie_config = array(
- 'lifetime' => 0,
- 'path' => '/',
- 'domain' => '',
- 'secure' => false,
- 'httponly' => false,
- 'name' => 'elggperm',
- 'expire' => time() + (30 * 86400),
- );
- $time = $this->thirtyDaysAgo + (30 * 86400);
- $svc = new \Elgg\PersistentLoginService(
- $this->dbMock, $this->session, $this->cryptoMock,
- $cookie_config, $cookie_token, $time);
- $svc->_callable_get_user = array($this, 'mock_get_user');
- $svc->_callable_generateToken = array($this, 'mock_generateToken');
- $svc->_callable_elgg_set_cookie = array($this, 'mock_elgg_set_cookie');
- $svc->_callable_sleep = array($this, 'mock_sleep');
- return $svc;
- }
- function mock_insertData($sql) {
- $this->assertContains("INSERT INTO users_remember_me_cookies", $sql);
- $this->assertContains("VALUES ('{$this->mockHash}', 123,", $sql);
- }
- function mock_deleteData($sql) {
- $pattern = "~DELETE FROM users_remember_me_cookies\\s+WHERE code = '{$this->mockHash}'~";
- $this->assertSame(1, preg_match($pattern, $sql));
- }
- function mock_getDataRow($sql) {
- $pattern = "~SELECT guid FROM users_remember_me_cookies\\s+WHERE code = '{$this->mockHash}'~";
- $this->assertSame(1, preg_match($pattern, $sql));
- return (object)array('guid' => 123);
- }
- function mock_deleteAll($sql) {
- $pattern = "~DELETE FROM users_remember_me_cookies\\s+WHERE guid = '123'+~";
- $this->assertSame(1, preg_match($pattern, $sql));
- }
- }
|