assertRequiredOptions( self::CONSTRUCTOR_OPTIONS ); $this->options = $options; $this->userIdentityLookup = $userIdentityLookup; $this->userNameUtils = $userNameUtils; } /** * From an existing block, get the target and the type of target. * * Note that, except for null, it is always safe to treat the target * as a string; for UserIdentityValue objects this will return * UserIdentityValue::__toString() which in turn gives * UserIdentityValue::getName(). * * If the type is not null, it will be an AbstractBlock::TYPE_ constant. * * @param string|UserIdentity|null $target * @return array [ UserIdentity|String|null, int|null ] */ public function parseBlockTarget( $target ): array { // We may have been through this before if ( $target instanceof UserIdentity ) { if ( IPUtils::isValid( $target->getName() ) ) { return [ $target, AbstractBlock::TYPE_IP ]; } else { return [ $target, AbstractBlock::TYPE_USER ]; } } elseif ( $target === null ) { return [ null, null ]; } $target = trim( $target ); if ( IPUtils::isValid( $target ) ) { return [ UserIdentityValue::newAnonymous( IPUtils::sanitizeIP( $target ) ), AbstractBlock::TYPE_IP ]; } elseif ( IPUtils::isValidRange( $target ) ) { // Can't create a UserIdentity from an IP range return [ IPUtils::sanitizeRange( $target ), AbstractBlock::TYPE_RANGE ]; } // Consider the possibility that this is not a username at all // but actually an old subpage (T31797) if ( strpos( $target, '/' ) !== false ) { // An old subpage, drill down to the user behind it $target = explode( '/', $target )[0]; } if ( preg_match( '/^#\d+$/', $target ) ) { // Autoblock reference in the form "#12345" return [ substr( $target, 1 ), AbstractBlock::TYPE_AUTO ]; } $userFromDB = $this->userIdentityLookup->getUserIdentityByName( $target ); if ( $userFromDB instanceof UserIdentity ) { // Note that since numbers are valid usernames, a $target of "12345" will be // considered a UserIdentity. If you want to pass a block ID, prepend a hash "#12345", // since hash characters are not valid in usernames or titles generally. return [ $userFromDB, AbstractBlock::TYPE_USER ]; } // TODO: figure out if it makes sense to have users that do not exist in the DB here $canonicalName = $this->userNameUtils->getCanonical( $target ); if ( $canonicalName ) { return [ new UserIdentityValue( 0, $canonicalName ), AbstractBlock::TYPE_USER ]; } return [ null, null ]; } /** * Validate block target * * @param string|UserIdentity $value * * @return Status */ public function validateTarget( $value ): Status { list( $target, $type ) = $this->parseBlockTarget( $value ); $status = Status::newGood( $target ); switch ( $type ) { case AbstractBlock::TYPE_USER: if ( !$target->isRegistered() ) { $status->fatal( 'nosuchusershort', wfEscapeWikiText( $target->getName() ) ); } break; case AbstractBlock::TYPE_RANGE: list( $ip, $range ) = explode( '/', $target, 2 ); if ( IPUtils::isIPv4( $ip ) ) { $status->merge( $this->validateIPv4Range( (int)$range ) ); } elseif ( IPUtils::isIPv6( $ip ) ) { $status->merge( $this->validateIPv6Range( (int)$range ) ); } else { // Something is FUBAR $status->fatal( 'badipaddress' ); } break; case AbstractBlock::TYPE_IP: // All is well break; default: $status->fatal( 'badipaddress' ); break; } return $status; } /** * Validate an IPv4 range * * @param int $range * * @return Status */ private function validateIPv4Range( int $range ): Status { $status = Status::newGood(); $blockCIDRLimit = $this->options->get( 'BlockCIDRLimit' ); if ( $blockCIDRLimit['IPv4'] == 32 ) { // Range block effectively disabled $status->fatal( 'range_block_disabled' ); } elseif ( $range > 32 ) { // Such a range cannot exist $status->fatal( 'ip_range_invalid' ); } elseif ( $range < $blockCIDRLimit['IPv4'] ) { $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv4'] ); } return $status; } /** * Validate an IPv6 range * * @param int $range * * @return Status */ private function validateIPv6Range( int $range ): Status { $status = Status::newGood(); $blockCIDRLimit = $this->options->get( 'BlockCIDRLimit' ); if ( $blockCIDRLimit['IPv6'] == 128 ) { // Range block effectively disabled $status->fatal( 'range_block_disabled' ); } elseif ( $range > 128 ) { // Dodgy range - such a range cannot exist $status->fatal( 'ip_range_invalid' ); } elseif ( $range < $blockCIDRLimit['IPv6'] ) { $status->fatal( 'ip_range_toolarge', $blockCIDRLimit['IPv6'] ); } return $status; } }