Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 69 |
|
0.00% |
0 / 4 |
CRAP | |
0.00% |
0 / 1 |
CentralAuthAntiSpoofManager | |
0.00% |
0 / 69 |
|
0.00% |
0 / 4 |
272 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
getSpoofUser | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
testNewAccount | |
0.00% |
0 / 41 |
|
0.00% |
0 / 1 |
110 | |||
getOldRenamedUserName | |
0.00% |
0 / 19 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\CentralAuth\User; |
4 | |
5 | use MediaWiki\Config\ServiceOptions; |
6 | use MediaWiki\Extension\CentralAuth\CentralAuthDatabaseManager; |
7 | use MediaWiki\Language\RawMessage; |
8 | use MediaWiki\User\User; |
9 | use Message; |
10 | use Psr\Log\LoggerInterface; |
11 | use StatusValue; |
12 | use Wikimedia\Rdbms\IConnectionProvider; |
13 | |
14 | class CentralAuthAntiSpoofManager { |
15 | /** @internal Only public for service wiring use. */ |
16 | public const CONSTRUCTOR_OPTIONS = [ |
17 | 'CentralAuthOldNameAntiSpoofWiki', |
18 | ]; |
19 | |
20 | private ServiceOptions $options; |
21 | private LoggerInterface $logger; |
22 | private IConnectionProvider $connectionProvider; |
23 | private CentralAuthDatabaseManager $databaseManager; |
24 | |
25 | /** |
26 | * @param ServiceOptions $options |
27 | * @param LoggerInterface $logger |
28 | * @param IConnectionProvider $connectionProvider |
29 | * @param CentralAuthDatabaseManager $databaseManager |
30 | */ |
31 | public function __construct( |
32 | ServiceOptions $options, |
33 | LoggerInterface $logger, |
34 | IConnectionProvider $connectionProvider, |
35 | CentralAuthDatabaseManager $databaseManager |
36 | ) { |
37 | $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); |
38 | $this->options = $options; |
39 | $this->logger = $logger; |
40 | $this->connectionProvider = $connectionProvider; |
41 | $this->databaseManager = $databaseManager; |
42 | } |
43 | |
44 | /** |
45 | * @param string $name |
46 | * @return CentralAuthSpoofUser |
47 | */ |
48 | public function getSpoofUser( string $name ): CentralAuthSpoofUser { |
49 | return new CentralAuthSpoofUser( |
50 | $name, |
51 | $this->databaseManager |
52 | ); |
53 | } |
54 | |
55 | /** |
56 | * Test if an account is acceptable |
57 | * |
58 | * @param User $user |
59 | * @param User $creator |
60 | * @param bool $enable |
61 | * @param bool $override |
62 | * @param LoggerInterface|null $logger |
63 | * |
64 | * @return StatusValue |
65 | */ |
66 | public function testNewAccount( $user, $creator, $enable, $override, $logger = null ) { |
67 | $logger ??= $this->logger; |
68 | |
69 | if ( !$enable ) { |
70 | $mode = 'LOGGING '; |
71 | $active = false; |
72 | } elseif ( $override && $creator->isAllowed( 'override-antispoof' ) ) { |
73 | $mode = 'OVERRIDE '; |
74 | $active = false; |
75 | } else { |
76 | $mode = ''; |
77 | $active = true; |
78 | } |
79 | |
80 | $name = $user->getName(); |
81 | $spoof = $this->getSpoofUser( $name ); |
82 | if ( $spoof->isLegal() ) { |
83 | $normalized = $spoof->getNormalized(); |
84 | $conflicts = $spoof->getConflicts(); |
85 | $oldUserName = $this->getOldRenamedUserName( $name ); |
86 | if ( $oldUserName !== null ) { |
87 | $conflicts[] = $oldUserName; |
88 | } |
89 | if ( !$conflicts ) { |
90 | $logger->info( "{$mode}PASS new account '$name' [$normalized]" ); |
91 | } else { |
92 | $logger->info( "{$mode}CONFLICT new account '$name' [$normalized] spoofs " . |
93 | implode( ',', $conflicts ) ); |
94 | |
95 | if ( $active ) { |
96 | $list = []; |
97 | foreach ( $conflicts as $simUser ) { |
98 | $list[] = "* " . wfMessage( 'antispoof-conflict-item', $simUser )->plain(); |
99 | } |
100 | $list = implode( "\n", $list ); |
101 | |
102 | return StatusValue::newFatal( |
103 | 'antispoof-conflict', |
104 | $name, |
105 | Message::numParam( count( $conflicts ) ), |
106 | // Avoid forced wikitext escaping for params in the Status class |
107 | new RawMessage( $list ) |
108 | ); |
109 | } |
110 | } |
111 | } else { |
112 | $error = $spoof->getErrorStatus(); |
113 | $logger->info( "{mode}ILLEGAL new account '{name}' {error}", [ |
114 | 'mode' => $mode, |
115 | 'name' => $name, |
116 | 'error' => $error->getMessage( false, false, 'en' )->text(), |
117 | ] ); |
118 | if ( $active ) { |
119 | return StatusValue::newFatal( 'antispoof-name-illegal', $name, $error->getMessage() ); |
120 | } |
121 | } |
122 | return StatusValue::newGood(); |
123 | } |
124 | |
125 | /** |
126 | * Given a username, find the old name |
127 | * |
128 | * @param string $name Name to lookup |
129 | * |
130 | * @return null|string Old username, or null |
131 | */ |
132 | public function getOldRenamedUserName( $name ) { |
133 | $dbrLogWiki = $this->connectionProvider->getReplicaDatabase( |
134 | // If nobody has set this variable, it will be false, |
135 | // which will mean the current wiki, which sounds like as |
136 | // good a default as we can get. |
137 | $this->options->get( 'CentralAuthOldNameAntiSpoofWiki' ) |
138 | ); |
139 | |
140 | $newNameOfUser = $dbrLogWiki->newSelectQueryBuilder() |
141 | ->select( 'log_title' ) |
142 | ->from( 'logging' ) |
143 | ->join( 'log_search', null, 'ls_log_id=log_id' ) |
144 | ->where( [ |
145 | 'ls_field' => 'oldname', |
146 | 'ls_value' => $name, |
147 | 'log_type' => 'gblrename', |
148 | 'log_namespace' => NS_SPECIAL |
149 | ] ) |
150 | ->caller( __METHOD__ ) |
151 | ->fetchField(); |
152 | $slashPos = strpos( $newNameOfUser ?: '', '/' ); |
153 | if ( $newNameOfUser && $slashPos ) { |
154 | // We have to remove the Special:CentralAuth prefix. |
155 | return substr( $newNameOfUser, $slashPos + 1 ); |
156 | } |
157 | return null; |
158 | } |
159 | } |