MediaWiki master
BlockUtils.php
Go to the documentation of this file.
1<?php
2
22namespace MediaWiki\Block;
23
31use Wikimedia\IPUtils;
32
49 private $options;
50
52 private $userIdentityLookup;
53
55 private $userNameUtils;
56
60 public const CONSTRUCTOR_OPTIONS = [
62 ];
63
69 public function __construct(
70 ServiceOptions $options,
71 UserIdentityLookup $userIdentityLookup,
72 UserNameUtils $userNameUtils
73 ) {
74 $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
75 $this->options = $options;
76 $this->userIdentityLookup = $userIdentityLookup;
77 $this->userNameUtils = $userNameUtils;
78 }
79
98 public function parseBlockTarget( $target ): array {
99 // We may have been through this before
100 if ( $target instanceof UserIdentity ) {
101 if ( IPUtils::isValid( $target->getName() ) ) {
102 return [ $target, AbstractBlock::TYPE_IP ];
103 } else {
104 return [ $target, AbstractBlock::TYPE_USER ];
105 }
106 } elseif ( $target === null ) {
107 return [ null, null ];
108 }
109
110 $target = trim( $target );
111
112 if ( IPUtils::isValid( $target ) ) {
113 return [
114 UserIdentityValue::newAnonymous( IPUtils::sanitizeIP( $target ) ),
116 ];
117
118 } elseif ( IPUtils::isValidRange( $target ) ) {
119 // Can't create a UserIdentity from an IP range
120 return [ IPUtils::sanitizeRange( $target ), AbstractBlock::TYPE_RANGE ];
121 }
122
123 // Consider the possibility that this is not a username at all
124 // but actually an old subpage (T31797)
125 if ( str_contains( $target, '/' ) ) {
126 // An old subpage, drill down to the user behind it
127 $target = explode( '/', $target )[0];
128 }
129
130 if ( preg_match( '/^#\d+$/', $target ) ) {
131 // Autoblock reference in the form "#12345"
132 return [ substr( $target, 1 ), AbstractBlock::TYPE_AUTO ];
133 }
134
135 $userFromDB = $this->userIdentityLookup->getUserIdentityByName( $target );
136 if ( $userFromDB instanceof UserIdentity ) {
137 // Note that since numbers are valid usernames, a $target of "12345" will be
138 // considered a UserIdentity. If you want to pass a block ID, prepend a hash "#12345",
139 // since hash characters are not valid in usernames or titles generally.
140 return [ $userFromDB, AbstractBlock::TYPE_USER ];
141 }
142
143 // Wrap the invalid user in a UserIdentityValue.
144 // This allows validateTarget() to return a "nosuchusershort" message,
145 // which is needed for Special:Block.
146 $canonicalName = $this->userNameUtils->getCanonical( $target );
147 if ( $canonicalName ) {
148 return [
149 new UserIdentityValue( 0, $canonicalName ),
151 ];
152 }
153
154 return [ null, null ];
155 }
156
165 public function parseBlockTargetRow( $row ) {
166 if ( $row->bt_auto ) {
167 return [ $row->bl_id, AbstractBlock::TYPE_AUTO ];
168 } elseif ( isset( $row->bt_user ) ) {
169 if ( isset( $row->bt_user_text ) ) {
170 $user = new UserIdentityValue( $row->bt_user, $row->bt_user_text );
171 } else {
172 $user = $this->userIdentityLookup->getUserIdentityByUserId( $row->bt_user );
173 }
174 return [ $user, AbstractBlock::TYPE_USER ];
175 } elseif ( $row->bt_address === null ) {
176 return [ null, null ];
177 } elseif ( IPUtils::isValid( $row->bt_address ) ) {
178 return [
179 UserIdentityValue::newAnonymous( IPUtils::sanitizeIP( $row->bt_address ) ),
181 ];
182 } elseif ( IPUtils::isValidRange( $row->bt_address ) ) {
183 // Can't create a UserIdentity from an IP range
184 return [ IPUtils::sanitizeRange( $row->bt_address ), AbstractBlock::TYPE_RANGE ];
185 } else {
186 return [ null, null ];
187 }
188 }
189
197 public function validateTarget( $value ): Status {
198 [ $target, $type ] = $this->parseBlockTarget( $value );
199
200 $status = Status::newGood( $target );
201
202 switch ( $type ) {
204 if ( !$target->isRegistered() ) {
205 $status->fatal(
206 'nosuchusershort',
207 wfEscapeWikiText( $target->getName() )
208 );
209 }
210 break;
211
213 [ $ip, $range ] = explode( '/', $target, 2 );
214
215 if ( IPUtils::isIPv4( $ip ) ) {
216 $status->merge( $this->validateIPv4Range( (int)$range ) );
217 } elseif ( IPUtils::isIPv6( $ip ) ) {
218 $status->merge( $this->validateIPv6Range( (int)$range ) );
219 } else {
220 // Something is FUBAR
221 $status->fatal( 'badipaddress' );
222 }
223 break;
224
226 // All is well
227 break;
228
229 default:
230 $status->fatal( 'badipaddress' );
231 break;
232 }
233
234 return $status;
235 }
236
244 private function validateIPv4Range( int $range ): Status {
245 $status = Status::newGood();
246 $blockCIDRLimit = $this->options->get( MainConfigNames::BlockCIDRLimit );
247
248 if ( $blockCIDRLimit['IPv4'] == 32 ) {
249 // Range block effectively disabled
250 $status->fatal( 'range_block_disabled' );
251 } elseif ( $range > 32 ) {
252 // Such a range cannot exist
253 $status->fatal( 'ip_range_invalid' );
254 } elseif ( $range < $blockCIDRLimit['IPv4'] ) {
255 $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv4'] );
256 }
257
258 return $status;
259 }
260
268 private function validateIPv6Range( int $range ): Status {
269 $status = Status::newGood();
270 $blockCIDRLimit = $this->options->get( MainConfigNames::BlockCIDRLimit );
271
272 if ( $blockCIDRLimit['IPv6'] == 128 ) {
273 // Range block effectively disabled
274 $status->fatal( 'range_block_disabled' );
275 } elseif ( $range > 128 ) {
276 // Dodgy range - such a range cannot exist
277 $status->fatal( 'ip_range_invalid' );
278 } elseif ( $range < $blockCIDRLimit['IPv6'] ) {
279 $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv6'] );
280 }
281
282 return $status;
283 }
284}
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(!defined('MW_SETUP_CALLBACK'))
Definition WebStart.php:88
Backend class for blocking utils.
parseBlockTarget( $target)
From string specification or UserIdentity, get the block target and the type of target.
parseBlockTargetRow( $row)
From a row which must contain bt_auto, bt_user, bt_address and bl_id, and optionally bt_user_text,...
__construct(ServiceOptions $options, UserIdentityLookup $userIdentityLookup, UserNameUtils $userNameUtils)
validateTarget( $value)
Validate block target.
A class for passing options to services.
assertRequiredOptions(array $expectedKeys)
Assert that the list of options provided in this instance exactly match $expectedKeys,...
A class containing constants representing the names of configuration variables.
const BlockCIDRLimit
Name constant for the BlockCIDRLimit setting, for use with Config::get()
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition Status.php:54
Value object representing a user's identity.
UserNameUtils service.
Interface for objects representing user identity.