MediaWiki  master
BlockUtils.php
Go to the documentation of this file.
1 <?php
2 
22 namespace MediaWiki\Block;
23 
30 use Status;
31 use Wikimedia\IPUtils;
32 
46 class BlockUtils {
48  private $options;
49 
52 
54  private $userNameUtils;
55 
59  public const CONSTRUCTOR_OPTIONS = [
61  ];
62 
68  public function __construct(
72  ) {
73  $options->assertRequiredOptions( self::CONSTRUCTOR_OPTIONS );
74  $this->options = $options;
75  $this->userIdentityLookup = $userIdentityLookup;
76  $this->userNameUtils = $userNameUtils;
77  }
78 
92  public function parseBlockTarget( $target ): array {
93  // We may have been through this before
94  if ( $target instanceof UserIdentity ) {
95  if ( IPUtils::isValid( $target->getName() ) ) {
96  return [ $target, AbstractBlock::TYPE_IP ];
97  } else {
98  return [ $target, AbstractBlock::TYPE_USER ];
99  }
100  } elseif ( $target === null ) {
101  return [ null, null ];
102  }
103 
104  $target = trim( $target );
105 
106  if ( IPUtils::isValid( $target ) ) {
107  return [
108  UserIdentityValue::newAnonymous( IPUtils::sanitizeIP( $target ) ),
110  ];
111 
112  } elseif ( IPUtils::isValidRange( $target ) ) {
113  // Can't create a UserIdentity from an IP range
114  return [ IPUtils::sanitizeRange( $target ), AbstractBlock::TYPE_RANGE ];
115  }
116 
117  // Consider the possibility that this is not a username at all
118  // but actually an old subpage (T31797)
119  if ( strpos( $target, '/' ) !== false ) {
120  // An old subpage, drill down to the user behind it
121  $target = explode( '/', $target )[0];
122  }
123 
124  if ( preg_match( '/^#\d+$/', $target ) ) {
125  // Autoblock reference in the form "#12345"
126  return [ substr( $target, 1 ), AbstractBlock::TYPE_AUTO ];
127  }
128 
129  $userFromDB = $this->userIdentityLookup->getUserIdentityByName( $target );
130  if ( $userFromDB instanceof UserIdentity ) {
131  // Note that since numbers are valid usernames, a $target of "12345" will be
132  // considered a UserIdentity. If you want to pass a block ID, prepend a hash "#12345",
133  // since hash characters are not valid in usernames or titles generally.
134  return [ $userFromDB, AbstractBlock::TYPE_USER ];
135  }
136 
137  // TODO: figure out if it makes sense to have users that do not exist in the DB here
138  $canonicalName = $this->userNameUtils->getCanonical( $target );
139  if ( $canonicalName ) {
140  return [
141  new UserIdentityValue( 0, $canonicalName ),
143  ];
144  }
145 
146  return [ null, null ];
147  }
148 
156  public function validateTarget( $value ): Status {
157  list( $target, $type ) = $this->parseBlockTarget( $value );
158 
159  $status = Status::newGood( $target );
160 
161  switch ( $type ) {
163  if ( !$target->isRegistered() ) {
164  $status->fatal(
165  'nosuchusershort',
166  wfEscapeWikiText( $target->getName() )
167  );
168  }
169  break;
170 
172  list( $ip, $range ) = explode( '/', $target, 2 );
173 
174  if ( IPUtils::isIPv4( $ip ) ) {
175  $status->merge( $this->validateIPv4Range( (int)$range ) );
176  } elseif ( IPUtils::isIPv6( $ip ) ) {
177  $status->merge( $this->validateIPv6Range( (int)$range ) );
178  } else {
179  // Something is FUBAR
180  $status->fatal( 'badipaddress' );
181  }
182  break;
183 
185  // All is well
186  break;
187 
188  default:
189  $status->fatal( 'badipaddress' );
190  break;
191  }
192 
193  return $status;
194  }
195 
203  private function validateIPv4Range( int $range ): Status {
204  $status = Status::newGood();
205  $blockCIDRLimit = $this->options->get( MainConfigNames::BlockCIDRLimit );
206 
207  if ( $blockCIDRLimit['IPv4'] == 32 ) {
208  // Range block effectively disabled
209  $status->fatal( 'range_block_disabled' );
210  } elseif ( $range > 32 ) {
211  // Such a range cannot exist
212  $status->fatal( 'ip_range_invalid' );
213  } elseif ( $range < $blockCIDRLimit['IPv4'] ) {
214  $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv4'] );
215  }
216 
217  return $status;
218  }
219 
227  private function validateIPv6Range( int $range ): Status {
228  $status = Status::newGood();
229  $blockCIDRLimit = $this->options->get( MainConfigNames::BlockCIDRLimit );
230 
231  if ( $blockCIDRLimit['IPv6'] == 128 ) {
232  // Range block effectively disabled
233  $status->fatal( 'range_block_disabled' );
234  } elseif ( $range > 128 ) {
235  // Dodgy range - such a range cannot exist
236  $status->fatal( 'ip_range_invalid' );
237  } elseif ( $range < $blockCIDRLimit['IPv6'] ) {
238  $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv6'] );
239  }
240 
241  return $status;
242  }
243 }
wfEscapeWikiText( $text)
Escapes the given text so that it may be output using addWikiText() without any linking,...
if(!defined('MW_SETUP_CALLBACK'))
The persistent session ID (if any) loaded at startup.
Definition: WebStart.php:82
Backend class for blocking utils.
Definition: BlockUtils.php:46
UserNameUtils $userNameUtils
Definition: BlockUtils.php:54
parseBlockTarget( $target)
From an existing block, get the target and the type of target.
Definition: BlockUtils.php:92
ServiceOptions $options
Definition: BlockUtils.php:48
__construct(ServiceOptions $options, UserIdentityLookup $userIdentityLookup, UserNameUtils $userNameUtils)
Definition: BlockUtils.php:68
UserIdentityLookup $userIdentityLookup
Definition: BlockUtils.php:51
validateIPv4Range(int $range)
Validate an IPv4 range.
Definition: BlockUtils.php:203
validateTarget( $value)
Validate block target.
Definition: BlockUtils.php:156
validateIPv6Range(int $range)
Validate an IPv6 range.
Definition: BlockUtils.php:227
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()
Value object representing a user's identity.
static newAnonymous(string $name, $wikiId=self::LOCAL)
Create UserIdentity for an anonymous user.
UserNameUtils service.
fatal( $message,... $parameters)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
static newGood( $value=null)
Factory function for good results.
Definition: StatusValue.php:82
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:44
Interface for objects representing user identity.