Page MenuHomeWickedGov Phorge

UserGroupAssignmentServiceTest.php
No OneTemporary

Size
13 KB
Referenced Files
None
Subscribers
None

UserGroupAssignmentServiceTest.php

<?php
/**
* @license GPL-2.0-or-later
* @file
*/
use MediaWiki\MainConfigNames;
use MediaWiki\Tests\Unit\Permissions\MockAuthorityTrait;
use MediaWiki\Tests\User\TempUser\TempUserTestTrait;
use MediaWiki\User\UserGroupAssignmentService;
use MediaWiki\User\UserGroupManager;
use MediaWiki\User\UserGroupManagerFactory;
use MediaWiki\User\UserGroupMembership;
use MediaWiki\User\UserIdentity;
use MediaWiki\User\UserIdentityValue;
/**
* @covers \MediaWiki\User\UserGroupAssignmentService
* @group Database
*/
class UserGroupAssignmentServiceTest extends MediaWikiIntegrationTestCase {
use MockAuthorityTrait;
use TempUserTestTrait;
/** @dataProvider provideTargetCanHaveGroups */
public function testTargetCanHaveGroups( UserIdentity $target, bool $expected ): void {
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$this->assertEquals( $expected, $service->targetCanHaveUserGroups( $target ) );
}
public static function provideTargetCanHaveGroups(): array {
return [
'Registered user' => [ new UserIdentityValue( 1, 'Test user' ), true ],
'Remote registered user' => [ new UserIdentityValue( 1, 'Test user', 'otherwiki' ), true ],
'Anonymous user' => [ new UserIdentityValue( 0, '127.0.0.1' ), false ],
'Remote anonymous user' => [ new UserIdentityValue( 0, '127.0.0.1', 'otherwiki' ), false ],
'Temporary user' => [ new UserIdentityValue( 2, '~2025-1' ), false ],
'Remote temporary user' => [ new UserIdentityValue( 2, '~2025-1', 'otherwiki' ), false ],
];
}
/** @dataProvider provideTargetCanHaveGroups_tempAccountsNotKnown */
public function testTargetCanHaveGroups_tempAccountsNotKnown( UserIdentity $target, bool $expected ): void {
$this->disableAutoCreateTempUser();
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$this->assertEquals( $expected, $service->targetCanHaveUserGroups( $target ) );
}
public static function provideTargetCanHaveGroups_tempAccountsNotKnown(): array {
return [
'Anonymous user' => [ new UserIdentityValue( 0, '127.0.0.1' ), false ],
'Remote anonymous user' => [ new UserIdentityValue( 0, '127.0.0.1', 'otherwiki' ), false ],
'Temporary (unknown) user' => [ new UserIdentityValue( 2, '~2025-1' ), true ],
'Remote temporary user' => [ new UserIdentityValue( 2, '~2025-1', 'otherwiki' ), false ],
];
}
/** @dataProvider provideUserCanChangeRights */
public function testUserCanChangeRights( UserIdentity $target, bool $hasInterwikiRight, bool $expected ): void {
$ugmMock = $this->createMock( UserGroupManager::class );
$ugmMock->method( 'getGroupsChangeableBy' )
->willReturn( [
'add' => [ 'add-group' ],
'remove' => [],
'add-self' => [],
'remove-self' => [],
] );
$ugmFactoryMock = $this->createMock( UserGroupManagerFactory::class );
$ugmFactoryMock->method( 'getUserGroupManager' )
->willReturn( $ugmMock );
$this->setService( 'UserGroupManagerFactory', $ugmFactoryMock );
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$performer = $this->mockRegisteredAuthorityWithPermissions(
$hasInterwikiRight ? [ 'userrights-interwiki' ] : []
);
$canChange = $service->userCanChangeRights( $performer, $target );
$this->assertSame( $expected, $canChange );
}
public static function provideUserCanChangeRights(): array {
return [
'Registered target' => [
'target' => new UserIdentityValue( 1, 'Test user' ),
'hasInterwikiRight' => false,
'expected' => true,
],
'Anonymous target' => [
'target' => new UserIdentityValue( 0, '127.0.0.1' ),
'hasInterwikiRight' => false,
'expected' => false,
],
'Temporary user target' => [
'target' => new UserIdentityValue( 2, '~2025-1' ),
'hasInterwikiRight' => false,
'expected' => false,
],
'Registered remote target, no interwiki right' => [
'target' => new UserIdentityValue( 1, 'Test user', 'otherwiki' ),
'hasInterwikiRight' => false,
'expected' => false,
],
'Registered remote target, with interwiki right' => [
'target' => new UserIdentityValue( 1, 'Test user', 'otherwiki' ),
'hasInterwikiRight' => true,
'expected' => true,
],
];
}
/** @dataProvider provideIsSelf */
public function testUserCanChangeRightsOnlySelf( bool $isSelf ): void {
$ugmMock = $this->createMock( UserGroupManager::class );
$ugmMock->method( 'getGroupsChangeableBy' )
->willReturn( [
'add' => [],
'remove' => [],
'add-self' => [ 'add-group' ],
'remove-self' => [],
] );
$ugmFactoryMock = $this->createMock( UserGroupManagerFactory::class );
$ugmFactoryMock->method( 'getUserGroupManager' )
->willReturn( $ugmMock );
$this->setService( 'UserGroupManagerFactory', $ugmFactoryMock );
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$performer = $this->mockRegisteredNullAuthority();
$target = $isSelf ? $performer->getUser() : new UserIdentityValue( 1, 'Test user' );
$canChange = $service->userCanChangeRights( $performer, $target );
$this->assertSame( $isSelf, $canChange );
}
/** @dataProvider provideIsSelf */
public function testGetChangeableGroups( bool $isSelf ): void {
$ugmMock = $this->createMock( UserGroupManager::class );
$ugmMock->method( 'getGroupsChangeableBy' )
->willReturn( [
'add' => [ 'add-group1' ],
'remove' => [ 'remove-group1' ],
'add-self' => [ 'add-group2' ],
'remove-self' => [ 'remove-group2' ],
] );
$ugmFactoryMock = $this->createMock( UserGroupManagerFactory::class );
$ugmFactoryMock->method( 'getUserGroupManager' )
->willReturn( $ugmMock );
$this->setService( 'UserGroupManagerFactory', $ugmFactoryMock );
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$performer = $this->mockAnonNullAuthority();
$target = $isSelf ? $performer->getUser() : new UserIdentityValue( 1, 'Test user' );
$groups = $service->getChangeableGroups( $performer, $target );
if ( $isSelf ) {
$this->assertSame( [
'add' => [ 'add-group1', 'add-group2' ],
'remove' => [ 'remove-group1', 'remove-group2' ],
'restricted' => [],
], $groups );
} else {
$this->assertSame( [
'add' => [ 'add-group1' ],
'remove' => [ 'remove-group1' ],
'restricted' => [],
], $groups );
}
}
public static function provideIsSelf(): array {
return [
'Not self' => [ false ],
'Self' => [ true ],
];
}
/** @dataProvider provideGetPageTitleForTargetUser */
public function testGetPageTitleForTargetUser( UserIdentity $target, string $expected ): void {
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
$title = $service->getPageTitleForTargetUser( $target );
$this->assertSame( $expected, $title );
}
public static function provideGetPageTitleForTargetUser(): array {
return [
'Local user' => [ new UserIdentityValue( 1, 'LocalUser' ), 'LocalUser' ],
'Remote user' => [ new UserIdentityValue( 2, 'RemoteUser', 'otherwiki' ), 'RemoteUser@otherwiki' ],
];
}
/** @dataProvider provideEnforcePermissions */
public function testEnforcePermissions(
array &$add,
array &$remove,
array &$newExpiries,
array $existingUGMs,
array $permittedChanges,
array $expectedAdd,
array $expectedRemove,
array $expectedNewExpiries
): void {
foreach ( $existingUGMs as $group => $expiry ) {
$existingUGMs[$group] = new UserGroupMembership( 1, $group, $expiry );
}
UserGroupAssignmentService::enforceChangeGroupPermissions(
$add,
$remove,
$newExpiries,
$existingUGMs,
$permittedChanges
);
$this->assertSame( $expectedAdd, $add );
$this->assertSame( $expectedRemove, $remove );
$this->assertSame( $expectedNewExpiries, $newExpiries );
}
public static function provideEnforcePermissions(): array {
return [
'All changes are permitted' => [
'add' => [ 'added-group1', 'added-group2', 'prolonged-group', 'shortened-group' ],
'remove' => [ 'removed-group1', 'removed-group2' ],
'newExpiries' => [
'added-group1' => null,
'added-group2' => '20290101000000',
'prolonged-group' => '20300101000000',
'shortened-group' => '20250101000000',
],
'existingUGMs' => [
'prolonged-group' => '20280101000000',
'shortened-group' => '20270101000000',
'removed-group1' => null,
'removed-group2' => '20260101000000',
],
'permittedChanges' => [
'add' => [ 'added-group1', 'added-group2', 'prolonged-group' ],
'remove' => [ 'removed-group1', 'removed-group2', 'shortened-group' ],
],
'expectedAdd' => [ 'added-group1', 'added-group2', 'prolonged-group', 'shortened-group' ],
'expectedRemove' => [ 'removed-group1', 'removed-group2' ],
'expectedNewExpiries' => [
'added-group1' => null,
'added-group2' => '20290101000000',
'prolonged-group' => '20300101000000',
'shortened-group' => '20250101000000',
],
],
'No changes permitted' => [
'add' => [ 'added-group1', 'added-group2', 'prolonged-group', 'shortened-group' ],
'remove' => [ 'removed-group1', 'removed-group2' ],
'newExpiries' => [
'added-group1' => null,
'added-group2' => '20290101000000',
'prolonged-group' => '20300101000000',
'shortened-group' => '20250101000000',
],
'existingUGMs' => [
'prolonged-group' => '20280101000000',
'shortened-group' => '20270101000000',
'removed-group1' => null,
'removed-group2' => '20260101000000',
],
'permittedChanges' => [
'add' => [],
'remove' => [],
],
'expectedAdd' => [],
'expectedRemove' => [],
'expectedNewExpiries' => [],
],
'Shortening fails without remove right' => [
'add' => [ 'shortened-group1', 'shortened-group2' ],
'remove' => [],
'newExpiries' => [
'shortened-group1' => '20250101000000',
'shortened-group2' => '20260101000000',
],
'existingUGMs' => [
'shortened-group1' => '20270101000000',
'shortened-group2' => null,
],
'permittedChanges' => [
'add' => [ 'shortened-group1', 'shortened-group2' ],
'remove' => [],
],
'expectedAdd' => [],
'expectedRemove' => [],
'expectedNewExpiries' => [],
],
'Shortening succeeds with just remove right' => [
'add' => [ 'shortened-group1', 'shortened-group2' ],
'remove' => [],
'newExpiries' => [
'shortened-group1' => '20250101000000',
'shortened-group2' => '20260101000000',
],
'existingUGMs' => [
'shortened-group1' => '20270101000000',
'shortened-group2' => null,
],
'permittedChanges' => [
'add' => [],
'remove' => [ 'shortened-group1', 'shortened-group2' ],
],
'expectedAdd' => [ 'shortened-group1', 'shortened-group2' ],
'expectedRemove' => [],
'expectedNewExpiries' => [
'shortened-group1' => '20250101000000',
'shortened-group2' => '20260101000000',
],
],
'User wants to remove non-existing group' => [
'add' => [],
'remove' => [ 'non-existing-group' ],
'newExpiries' => [],
'existingUGMs' => [],
'permittedChanges' => [
'add' => [],
'remove' => [ 'non-existing-group' ],
],
'expectedAdd' => [],
'expectedRemove' => [],
'expectedNewExpiries' => [],
],
'User wants to both add and remove the same group' => [
'add' => [ 'group1', 'group2' ],
'remove' => [ 'group1', 'group2' ],
'newExpiries' => [
'group1' => null,
'group2' => '20290101000000',
],
'existingUGMs' => [
'group2' => null,
],
'permittedChanges' => [
'add' => [ 'group1', 'group2' ],
'remove' => [ 'group1', 'group2' ],
],
'expectedAdd' => [],
'expectedRemove' => [],
'expectedNewExpiries' => [],
],
];
}
public function testSaveUserGroups(): void {
$add = [ 'added-group1', 'added-group2' ];
$remove = [ 'removed-group' ];
$newExpiries = [
'added-group1' => null,
'added-group2' => '20290101000000',
];
// Using bureaucrat as a performer won't work because these groups aren't otherwise
// known to software and won't be allowed to be assigned.
$this->overrideConfigValue( MainConfigNames::AddGroups,
[ 'sysop' => [ 'added-group1', 'added-group2', 'hook-added-group' ] ] );
$this->overrideConfigValue( MainConfigNames::RemoveGroups,
[ 'sysop' => [ 'removed-group', 'hook-removed-group' ] ] );
$performer = $this->getTestUser( [ 'sysop' ] )->getUser();
$target = $this->getTestUser( [ 'static-group', 'removed-group', 'hook-removed-group' ] )->getUser();
$hookBeforeCalled = false;
$hookAfterCalled = false;
$this->setTemporaryHook(
'ChangeUserGroups',
function (
$performer, $hookUser, array &$addGroups, array &$removeGroups
) use ( &$hookBeforeCalled ) {
$this->assertSame( [ 'added-group1', 'added-group2' ], $addGroups );
$this->assertSame( [ 'removed-group' ], $removeGroups );
$hookBeforeCalled = true;
$addGroups[] = 'hook-added-group';
$removeGroups[] = 'hook-removed-group';
}
);
$this->setTemporaryHook(
'UserGroupsChanged',
function (
$hookUser, array $addGroups, array $removeGroups, $performer, $reason, $oldUGMs, $newUGMs
) use ( &$hookAfterCalled ) {
$this->assertSame( [ 'added-group1', 'added-group2', 'hook-added-group' ], $addGroups );
$this->assertSame( [ 'removed-group', 'hook-removed-group' ], $removeGroups );
$this->assertSame(
[ 'static-group', 'added-group1', 'added-group2', 'hook-added-group' ],
array_keys( $newUGMs )
);
$hookAfterCalled = true;
}
);
$service = $this->getServiceContainer()->getUserGroupAssignmentService();
[ $added, $removed ] = $service->saveChangesToUserGroups( $performer, $target, $add, $remove, $newExpiries );
$this->assertEquals( [ 'added-group1', 'added-group2', 'hook-added-group' ], $added );
$this->assertEquals( [ 'removed-group', 'hook-removed-group' ], $removed );
$this->assertTrue( $hookBeforeCalled, 'ChangeUserGroups hook was not called' );
$this->assertTrue( $hookAfterCalled, 'UserGroupsChanged hook was not called' );
}
}

File Metadata

Mime Type
text/x-php
Expires
Sun, Oct 4, 00:04 (2 w, 3 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
22/0f/6daea0d5bf186e82b1578bca8de5
Default Alt Text
UserGroupAssignmentServiceTest.php (13 KB)

Event Timeline