Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F5986306
UserGroupAssignmentServiceTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
UserGroupAssignmentServiceTest.php
View Options
<?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
Details
Attached
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)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment