Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.53% |
67 / 68 |
|
75.00% |
3 / 4 |
CRAP | |
0.00% |
0 / 1 |
Degroup | |
98.53% |
67 / 68 |
|
75.00% |
3 / 4 |
13 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
execute | |
96.88% |
31 / 32 |
|
0.00% |
0 / 1 |
7 | |||
revert | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
4 | |||
getMessage | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\AbuseFilter\Consequences\Consequence; |
4 | |
5 | use ManualLogEntry; |
6 | use MediaWiki\Extension\AbuseFilter\Consequences\Parameters; |
7 | use MediaWiki\Extension\AbuseFilter\FilterUser; |
8 | use MediaWiki\Extension\AbuseFilter\GlobalNameUtils; |
9 | use MediaWiki\Extension\AbuseFilter\Variables\LazyLoadedVariable; |
10 | use MediaWiki\Extension\AbuseFilter\Variables\UnsetVariableException; |
11 | use MediaWiki\Extension\AbuseFilter\Variables\VariableHolder; |
12 | use MediaWiki\Title\TitleValue; |
13 | use MediaWiki\User\UserGroupManager; |
14 | use MediaWiki\User\UserIdentity; |
15 | use MediaWiki\User\UserIdentityUtils; |
16 | use MessageLocalizer; |
17 | |
18 | /** |
19 | * Consequence that removes all user groups from a user. |
20 | */ |
21 | class Degroup extends Consequence implements HookAborterConsequence, ReversibleConsequence { |
22 | /** |
23 | * @var VariableHolder |
24 | * @todo This dependency is subpar |
25 | */ |
26 | private $vars; |
27 | |
28 | /** @var UserGroupManager */ |
29 | private $userGroupManager; |
30 | |
31 | /** @var UserIdentityUtils */ |
32 | private $userIdentityUtils; |
33 | |
34 | /** @var FilterUser */ |
35 | private $filterUser; |
36 | |
37 | /** @var MessageLocalizer */ |
38 | private $messageLocalizer; |
39 | |
40 | /** |
41 | * @param Parameters $params |
42 | * @param VariableHolder $vars |
43 | * @param UserGroupManager $userGroupManager |
44 | * @param UserIdentityUtils $userIdentityUtils |
45 | * @param FilterUser $filterUser |
46 | * @param MessageLocalizer $messageLocalizer |
47 | */ |
48 | public function __construct( |
49 | Parameters $params, |
50 | VariableHolder $vars, |
51 | UserGroupManager $userGroupManager, |
52 | UserIdentityUtils $userIdentityUtils, |
53 | FilterUser $filterUser, |
54 | MessageLocalizer $messageLocalizer |
55 | ) { |
56 | parent::__construct( $params ); |
57 | $this->vars = $vars; |
58 | $this->userGroupManager = $userGroupManager; |
59 | $this->userIdentityUtils = $userIdentityUtils; |
60 | $this->filterUser = $filterUser; |
61 | $this->messageLocalizer = $messageLocalizer; |
62 | } |
63 | |
64 | /** |
65 | * @inheritDoc |
66 | */ |
67 | public function execute(): bool { |
68 | $user = $this->parameters->getUser(); |
69 | |
70 | if ( !$this->userIdentityUtils->isNamed( $user ) ) { |
71 | return false; |
72 | } |
73 | |
74 | // Pull the groups from the VariableHolder, so that they will always be computed. |
75 | // This allow us to pull the groups from the VariableHolder to undo the degroup |
76 | // via Special:AbuseFilter/revert. |
77 | try { |
78 | // No point in triggering a lazy-load, instead we compute it here if necessary |
79 | $groupsVar = $this->vars->getVarThrow( 'user_groups' ); |
80 | } catch ( UnsetVariableException $_ ) { |
81 | $groupsVar = null; |
82 | } |
83 | if ( $groupsVar === null || $groupsVar instanceof LazyLoadedVariable ) { |
84 | // The variable is unset or not computed. Compute it and update the holder so we can use it for reverts |
85 | $groups = $this->userGroupManager->getUserEffectiveGroups( $user ); |
86 | $this->vars->setVar( 'user_groups', $groups ); |
87 | } else { |
88 | $groups = $groupsVar->toNative(); |
89 | } |
90 | |
91 | $implicitGroups = $this->userGroupManager->listAllImplicitGroups(); |
92 | $removeGroups = array_diff( $groups, $implicitGroups ); |
93 | if ( !count( $removeGroups ) ) { |
94 | return false; |
95 | } |
96 | |
97 | foreach ( $removeGroups as $group ) { |
98 | $this->userGroupManager->removeUserFromGroup( $user, $group ); |
99 | } |
100 | |
101 | // TODO Core should provide a logging method |
102 | $logEntry = new ManualLogEntry( 'rights', 'rights' ); |
103 | $logEntry->setPerformer( $this->filterUser->getUserIdentity() ); |
104 | $logEntry->setTarget( new TitleValue( NS_USER, $user->getName() ) ); |
105 | $logEntry->setComment( |
106 | $this->messageLocalizer->msg( |
107 | 'abusefilter-degroupreason', |
108 | $this->parameters->getFilter()->getName(), |
109 | $this->parameters->getFilter()->getID() |
110 | )->inContentLanguage()->text() |
111 | ); |
112 | $logEntry->setParameters( [ |
113 | '4::oldgroups' => $removeGroups, |
114 | '5::newgroups' => [] |
115 | ] ); |
116 | $logEntry->publish( $logEntry->insert() ); |
117 | return true; |
118 | } |
119 | |
120 | /** |
121 | * @inheritDoc |
122 | */ |
123 | public function revert( UserIdentity $performer, string $reason ): bool { |
124 | $user = $this->parameters->getUser(); |
125 | $currentGroups = $this->userGroupManager->getUserGroups( $user ); |
126 | // Pull the user's original groups from the vars. This is guaranteed to be set, because we |
127 | // enforce it when performing a degroup. |
128 | $removedGroups = $this->vars->getComputedVariable( 'user_groups' )->toNative(); |
129 | $removedGroups = array_diff( |
130 | $removedGroups, |
131 | $this->userGroupManager->listAllImplicitGroups(), |
132 | $currentGroups |
133 | ); |
134 | |
135 | $addedGroups = []; |
136 | foreach ( $removedGroups as $group ) { |
137 | // TODO An addUserToGroups method with bulk updates would be nice |
138 | if ( $this->userGroupManager->addUserToGroup( $user, $group ) ) { |
139 | $addedGroups[] = $group; |
140 | } |
141 | } |
142 | |
143 | // Don't log if no groups were added. |
144 | if ( !$addedGroups ) { |
145 | return false; |
146 | } |
147 | |
148 | // TODO Core should provide a logging method |
149 | $logEntry = new ManualLogEntry( 'rights', 'rights' ); |
150 | $logEntry->setTarget( new TitleValue( NS_USER, $user->getName() ) ); |
151 | $logEntry->setPerformer( $performer ); |
152 | $logEntry->setComment( $reason ); |
153 | $logEntry->setParameters( [ |
154 | '4::oldgroups' => $currentGroups, |
155 | '5::newgroups' => array_merge( $currentGroups, $addedGroups ) |
156 | ] ); |
157 | $logEntry->publish( $logEntry->insert() ); |
158 | |
159 | return true; |
160 | } |
161 | |
162 | /** |
163 | * @inheritDoc |
164 | */ |
165 | public function getMessage(): array { |
166 | $filter = $this->parameters->getFilter(); |
167 | return [ |
168 | 'abusefilter-degrouped', |
169 | $filter->getName(), |
170 | GlobalNameUtils::buildGlobalName( $filter->getID(), $this->parameters->getIsGlobalFilter() ) |
171 | ]; |
172 | } |
173 | } |