X-Git-Url: https://git.cyclocoop.org/%27.WWW_URL.%27admin/?a=blobdiff_plain;f=includes%2FBlock.php;h=66b9ff02d5ac60de90d5a050046f2a029b9e5650;hb=43a7b72ae0ba4c7454d1eaa8d465d965f3590fcd;hp=8663d0328cd75201adccd38e497162e4763fb442;hpb=44a9c1bf59b110e3d04b6a5083242f981f4de832;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/Block.php b/includes/Block.php index 8663d0328c..66b9ff02d5 100644 --- a/includes/Block.php +++ b/includes/Block.php @@ -74,6 +74,9 @@ class Block { /** @var bool */ protected $isAutoblocking; + /** @var string|null */ + protected $systemBlockType; + # TYPE constants const TYPE_USER = 1; const TYPE_IP = 2; @@ -99,6 +102,10 @@ class Block { * blockEmail bool Disallow sending emails * allowUsertalk bool Allow the target to edit its own talk page * byText string Username of the blocker (for foreign users) + * systemBlock string Indicate that this block is automatically + * created by MediaWiki rather than being stored + * in the database. Value is a string to return + * from self::getSystemBlockType(). * * @since 1.26 accepts $options array instead of individual parameters; order * of parameters above reflects the original order @@ -119,6 +126,7 @@ class Block { 'blockEmail' => false, 'allowUsertalk' => false, 'byText' => '', + 'systemBlock' => null, ]; if ( func_num_args() > 1 || !is_array( $options ) ) { @@ -162,6 +170,7 @@ class Block { $this->prevents( 'createaccount', (bool)$options['createAccount'] ); $this->mFromMaster = false; + $this->systemBlockType = $options['systemBlock']; } /** @@ -461,6 +470,11 @@ class Block { */ public function insert( $dbw = null ) { global $wgBlockDisablesLogin; + + if ( $this->getSystemBlockType() !== null ) { + throw new MWException( 'Cannot insert a system block into the database' ); + } + wfDebug( "Block::insert; timestamp {$this->mTimestamp}\n" ); if ( $dbw === null ) { @@ -741,13 +755,20 @@ class Block { return false; } + # Don't autoblock for system blocks + if ( $this->getSystemBlockType() !== null ) { + throw new MWException( 'Cannot autoblock from a system block' ); + } + # Check for presence on the autoblock whitelist. if ( self::isWhitelistedFromAutoblocks( $autoblockIP ) ) { return false; } + // Avoid PHP 7.1 warning of passing $this by reference + $block = $this; # Allow hooks to cancel the autoblock. - if ( !Hooks::run( 'AbortAutoblock', [ $autoblockIP, &$this ] ) ) { + if ( !Hooks::run( 'AbortAutoblock', [ $autoblockIP, &$block ] ) ) { wfDebug( "Autoblock aborted by hook.\n" ); return false; } @@ -934,6 +955,14 @@ class Block { return $this->mId; } + /** + * Get the system block type, if any + * @return string|null + */ + public function getSystemBlockType() { + return $this->systemBlockType; + } + /** * Get/set a flag determining whether the master is used for reads * @@ -1419,8 +1448,7 @@ class Block { /** * Set the 'BlockID' cookie to this block's ID and expiry time. The cookie's expiry will be - * the same as the block's, unless it's greater than $wgCookieExpiration in which case - * $wgCookieExpiration will be used instead (defaults to 30 days). + * the same as the block's, to a maximum of 24 hours. * * An empty value can also be set, in order to retain the cookie but remove the block ID * (e.g. as used in User::getBlockedStatus). @@ -1430,18 +1458,64 @@ class Block { */ public function setCookie( WebResponse $response, $setEmpty = false ) { // Calculate the default expiry time. - $config = RequestContext::getMain()->getConfig(); - $defaultExpiry = wfTimestamp() + $config->get( 'CookieExpiration' ); + $maxExpiryTime = wfTimestamp( TS_MW, wfTimestamp() + ( 24 * 60 * 60 ) ); // Use the Block's expiry time only if it's less than the default. - $expiry = wfTimestamp( TS_UNIX, $this->getExpiry() ); - if ( $expiry > $defaultExpiry ) { - // The *default* default expiry is 30 days. - $expiry = $defaultExpiry; + $expiryTime = $this->getExpiry(); + if ( $expiryTime === 'infinity' || $expiryTime > $maxExpiryTime ) { + $expiryTime = $maxExpiryTime; } - $cookieValue = $setEmpty ? '' : $this->getId(); - $response->setCookie( 'BlockID', $cookieValue, $expiry ); + // Set the cookie. Reformat the MediaWiki datetime as a Unix timestamp for the cookie. + $cookieValue = $setEmpty ? '' : $this->getCookieValue(); + $expiryValue = DateTime::createFromFormat( 'YmdHis', $expiryTime )->format( 'U' ); + $cookieOptions = [ 'httpOnly' => false ]; + $response->setCookie( 'BlockID', $cookieValue, $expiryValue, $cookieOptions ); + } + + /** + * Get the BlockID cookie's value for this block. This is usually the block ID concatenated + * with an HMAC in order to avoid spoofing (T152951), but if wgSecretKey is not set will just + * be the block ID. + * @return string The block ID, probably concatenated with "!" and the HMAC. + */ + public function getCookieValue() { + $config = RequestContext::getMain()->getConfig(); + $id = $this->getId(); + $secretKey = $config->get( 'SecretKey' ); + if ( !$secretKey ) { + // If there's no secret key, don't append a HMAC. + return $id; + } + $hmac = MWCryptHash::hmac( $id, $secretKey, false ); + $cookieValue = $id . '!' . $hmac; + return $cookieValue; + } + + /** + * Get the stored ID from the 'BlockID' cookie. The cookie's value is usually a combination of + * the ID and a HMAC (see Block::setCookie), but will sometimes only be the ID. + * @param string $cookieValue The string in which to find the ID. + * @return integer|null The block ID, or null if the HMAC is present and invalid. + */ + public static function getIdFromCookieValue( $cookieValue ) { + // Extract the ID prefix from the cookie value (may be the whole value, if no bang found). + $bangPos = strpos( $cookieValue, '!' ); + $id = ( $bangPos === false ) ? $cookieValue : substr( $cookieValue, 0, $bangPos ); + // Get the site-wide secret key. + $config = RequestContext::getMain()->getConfig(); + $secretKey = $config->get( 'SecretKey' ); + if ( !$secretKey ) { + // If there's no secret key, just use the ID as given. + return $id; + } + $storedHmac = substr( $cookieValue, $bangPos + 1 ); + $calculatedHmac = MWCryptHash::hmac( $id, $secretKey, false ); + if ( $calculatedHmac === $storedHmac ) { + return $id; + } else { + return null; + } } /** @@ -1469,14 +1543,18 @@ class Block { * This could be a username, an IP range, or a single IP. */ $intended = $this->getTarget(); + $systemBlockType = $this->getSystemBlockType(); + $lang = $context->getLanguage(); return [ - $this->mAuto ? 'autoblockedtext' : 'blockedtext', + $systemBlockType !== null + ? 'systemblockedtext' + : ( $this->mAuto ? 'autoblockedtext' : 'blockedtext' ), $link, $reason, $context->getRequest()->getIP(), $this->getByName(), - $this->getId(), + $systemBlockType !== null ? $systemBlockType : $this->getId(), $lang->formatExpiry( $this->mExpiry ), (string)$intended, $lang->userTimeAndDate( $this->mTimestamp, $context->getUser() ),