Check editor accounts.

A new editor needs a username and a password of at least 12 characters. A persistent login needs an unexpired key and an existing account. Removing an account revokes its persistent logins, and its sessions end.
This commit is contained in:
tim
2026-09-26 15:27:51 +02:00
parent 5d657d7e45
commit 3df88e0b4c
10 changed files with 217 additions and 16 deletions
+36 -4
View File
@@ -1,6 +1,7 @@
<?php
namespace Xxx\Tests\Pages;
use PHPUnit\Framework\Attributes\DataProvider;
use Xxx\Tests\Support\PageTestCase;
// publish/addeditor.php
@@ -14,7 +15,7 @@ class AddEditorTest extends PageTestCase {
}
private function add(array $fields = array()): array {
return $this->post('/publish/addeditor', $fields + array('adduser' => '1', 'username' => 'newbie', 'password' => 'pass word',
return $this->post('/publish/addeditor', $fields + array('adduser' => '1', 'username' => 'newbie', 'password' => 'a long pass phrase',
'email' => 'new@example.org', 'csrf_token' => $this->token));
}
@@ -38,7 +39,7 @@ class AddEditorTest extends PageTestCase {
$this->assertStringContainsString('<p>Added editor newbie</p>', $answer['body']);
$row = $this->tables()['editors'][1];
$this->assertSame(array('newbie', 'new@example.org'), array($row['username'], $row['email']));
$this->assertTrue(password_verify('pass word', $row['password']));
$this->assertTrue(password_verify('a long pass phrase', $row['password']));
}
public function testTheTokenIsChecked(): void {
@@ -54,10 +55,41 @@ class AddEditorTest extends PageTestCase {
}
public function testAHashThatDoesNotFitIsNotKept(): void {
$this->setTables(array('editors' => array()), 40);
$this->setTables(array('editors' => array(array('username' => 'ed', 'password' => 'x', 'email' => ''))), 40);
$answer = $this->add();
$this->assertStringContainsString('the editors.password column is too short', $answer['body']);
$this->assertSame(array(), $this->tables()['editors']);
$this->assertSame(array('ed'), array_column($this->tables()['editors'], 'username'));
$this->assertCount(1, $this->queries('/^DELETE FROM persistent_sessions WHERE username = \?$/'));
}
public static function badAccounts(): array {
return array(
'no username' => array(array('username' => ''), 'Please enter a username.'),
'a blank username' => array(array('username' => ' '), 'Please enter a username.'),
'an array username' => array(array('username' => array('x')), 'Please enter a username.'),
'spaces at the ends' => array(array('username' => ' newbie'), 'no spaces at the ends'),
'a bar' => array(array('username' => 'new|bie'), 'no spaces at the ends'),
'a long username' => array(array('username' => str_repeat('n', 65)), 'at most 64 characters'),
'no password' => array(array('password' => ''), 'A password needs at least 12 characters.'),
'a short password' => array(array('password' => 'eleven char'), 'A password needs at least 12 characters.'),
'an array password' => array(array('password' => array('a long pass phrase')), 'A password needs at least 12 characters.'),
'a huge password' => array(array('password' => str_repeat('p', 1025)), 'A password has at most 1024 characters.'),
'the username' => array(array('username' => 'newbie-editor', 'password' => 'NEWBIE-EDITOR'), 'The password cannot be the username.'),
);
}
#[DataProvider('badAccounts')]
public function testABadAccountIsRefused(array $fields, string $message): void {
$answer = $this->add($fields);
$this->assertSame(200, $answer['status']);
$this->assertStringContainsString(htmlspecialchars($message, ENT_QUOTES), $answer['body']);
$this->assertStringNotContainsString('Added editor', $answer['body']);
$this->assertCount(1, $this->tables()['editors']);
$this->assertCount(0, $this->queries('/^INSERT INTO editors/'));
}
public function testTwelveCharactersAreEnough(): void {
$this->assertStringContainsString('Added editor newbie', $this->add(array('password' => 'twelve chars'))['body']);
}
public function testTheUsernameIsEscaped(): void {
+6
View File
@@ -68,6 +68,12 @@ class FeedAndImageTest extends PageTestCase {
$this->assertSame(200, $this->get('/display?id=3')['status']);
}
public function testARemovedEditorSeesNoHiddenImages(): void {
$this->loginAs();
$this->setTables(array('xxx' => $this->tables()['xxx']));
$this->assertSame(404, $this->get('/display?id=3')['status']);
}
public function testQueuedImagesNeedPermission(): void {
$this->assertSame(401, $this->get('/display?id=9&queue=1')['status']);
$this->startSession(array('allow_queue_image' => '8'));
+28
View File
@@ -181,6 +181,34 @@ class LoginTest extends PageTestCase {
$this->assertStringNotContainsString('/publish/logout', $body);
}
public function testAnExpiredPersistentKeyDoesNotLogIn(): void {
$this->setTables(array('editors' => $this->tables()['editors'], 'persistent_sessions' => array(
array('username' => 'ed', 'session' => hash('sha256', 'key'), 'created' => gmdate('Y-m-d H:i:s', strtotime('-1 year -1 day'))))));
$this->cookies['PersistentAuth'] = 'ed|key';
$body = $this->get('/')['body'];
$this->assertStringNotContainsString('/publish/logout', $body);
$this->assertArrayNotHasKey('editor', $this->session());
// The expired key and its cookie are removed
$this->assertArrayNotHasKey('PersistentAuth', $this->cookies);
$this->assertSame(array(), $this->tables()['persistent_sessions']);
}
public function testAPersistentKeyOfARemovedAccountDoesNotLogIn(): void {
$this->setTables(array('persistent_sessions' => array(
array('username' => 'ed', 'session' => hash('sha256', 'key'), 'created' => gmdate('Y-m-d H:i:s')))));
$this->cookies['PersistentAuth'] = 'ed|key';
$this->assertStringNotContainsString('/publish/logout', $this->get('/')['body']);
$this->assertArrayNotHasKey('editor', $this->session());
$this->assertArrayNotHasKey('PersistentAuth', $this->cookies);
}
public function testARemovedAccountEndsItsSession(): void {
$this->loginAs();
$this->setTables(array());
$this->assertRedirect('/publish/login', $this->get('/publish/edit'));
$this->assertArrayNotHasKey('editor', $this->session());
}
public function testLogoutNeedsTheToken(): void {
$this->loginAs();
$this->assertRedirect('/', $this->get('/publish/logout?token=wrong'));