r84358 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r84357‎ | r84358 | r84359 >
Date:23:47, 19 March 2011
Author:happy-melon
Status:ok (Comments)
Tags:
Comment:
* Implement an extensible Block::prevents( <action> ) function to replace the plethora of direct member variable accesses This pushes the historic *disable*-createaccount-vs-*allow* usertalk-edit wierdness down to the database layer
* Implement accessors for isHardblock() and getRangeStart()/getRangeEnd() in the same fashion.
* Make the corresponding variables private, removing external accessors. This required updating AbuseFilter with non-B/C code, so I also implemented the rest of the changes I've made to the blocking backend in that extension.
* Move the "get an IP range which encompasses the given IP/range" logic to Block.php; will be needed later... :D
Modified paths:
  • /trunk/extensions/AbuseFilter/AbuseFilter.class.php (modified) (history)
  • /trunk/extensions/AbuseFilter/Views/AbuseFilterViewRevert.php (modified) (history)
  • /trunk/extensions/regexBlock/regexBlockCore.php (modified) (history)
  • /trunk/phase3/includes/Block.php (modified) (history)
  • /trunk/phase3/includes/User.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialBlock.php (modified) (history)
  • /trunk/phase3/includes/specials/SpecialBlockList.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/User.php
@@ -1157,7 +1157,7 @@
11581158 $this->mBlockedby = $this->mBlock->mByName;
11591159 $this->mBlockreason = $this->mBlock->mReason;
11601160 $this->mHideName = $this->mBlock->mHideName;
1161 - $this->mAllowUsertalk = $this->mBlock->mAllowUsertalk;
 1161+ $this->mAllowUsertalk = !$this->mBlock->prevents( 'editusertalk' );
11621162 if ( $this->isLoggedIn() && $wgUser->getID() == $this->getID() ) {
11631163 $this->spreadBlock();
11641164 }
@@ -2801,7 +2801,7 @@
28022802 */
28032803 function isBlockedFromCreateAccount() {
28042804 $this->getBlockedStatus();
2805 - return $this->mBlock && $this->mBlock->mCreateAccount;
 2805+ return $this->mBlock && $this->mBlock->prevents( 'createaccount' );
28062806 }
28072807
28082808 /**
@@ -2810,7 +2810,7 @@
28112811 */
28122812 function isBlockedFromEmailuser() {
28132813 $this->getBlockedStatus();
2814 - return $this->mBlock && $this->mBlock->mBlockEmail;
 2814+ return $this->mBlock && $this->mBlock->prevents( 'sendemail' );
28152815 }
28162816
28172817 /**
Index: trunk/phase3/includes/specials/SpecialBlockList.php
@@ -90,26 +90,6 @@
9191 $this->showList();
9292 }
9393
94 - /**
95 - * Get the component of an IP address which is certain to be the same between an IP
96 - * address and a rangeblock containing that IP address.
97 - * @todo: should be in IP.php??
98 - * @param $ip String
99 - * @return String
100 - */
101 - protected static function getIpFragment( $ip ){
102 - global $wgBlockCIDRLimit;
103 - if( IP::isIPv4( $ip ) ){
104 - $hexAddress = IP::toHex( $ip );
105 - return substr( $hexAddress, 0, wfBaseconvert( $wgBlockCIDRLimit['IPv4'], 10, 16 ) );
106 - } elseif( IP::isIPv6( $ip ) ) {
107 - $hexAddress = substr( IP::toHex( $ip ), 2 );
108 - return 'v6-' . substr( $hexAddress, 0, wfBaseconvert( $wgBlockCIDRLimit['IPv6'], 10, 16 ) );
109 - } else {
110 - return null;
111 - }
112 - }
113 -
11494 function showList() {
11595 global $wgOut, $wgUser;
11696
@@ -133,25 +113,16 @@
134114 break;
135115
136116 case Block::TYPE_IP:
137 - case BLock::TYPE_RANGE:
 117+ case Block::TYPE_RANGE:
138118 list( $start, $end ) = IP::parseRange( $target );
139 - # Per bug 14634, we want to include relevant active rangeblocks; for
140 - # rangeblocks, we want to include larger ranges which enclose the given
141 - # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
142 - # so we can improve performance by filtering on a LIKE clause
143 - $chunk = self::getIpFragment( $start );
144119 $dbr = wfGetDB( DB_SLAVE );
145 - $like = $dbr->buildLike( $chunk, $dbr->anyString() );
146 -
147 - # Fairly hard to make a malicious SQL statement out of hex characters,
148 - # but stranger things have happened...
149 - $safeStart = $dbr->addQuotes( IP::toHex( $start ) );
150 - $safeEnd = $dbr->addQuotes( IP::toHex( $end ) );
151 - $safeTarget = $dbr->addQuotes( IP::toHex( $target ) );
152 -
153 - # TODO: abstract this away
154 - $conds[] = "(ipb_address = $safeTarget) OR
155 - (ipb_range_start $like AND ipb_range_start <= $safeStart AND ipb_range_end >= $safeEnd)";
 120+ $conds[] = $dbr->makeList(
 121+ array(
 122+ 'ipb_address' => $target,
 123+ Block::getRangeCond( $start, $end )
 124+ ),
 125+ LIST_OR
 126+ );
156127 $conds['ipb_auto'] = 0;
157128 break;
158129
Index: trunk/phase3/includes/specials/SpecialBlock.php
@@ -208,17 +208,17 @@
209209 || $block->mAddress == $this->target ) # or if it is, the range is what we're about to block
210210 )
211211 {
212 - $fields['HardBlock']['default'] = !$block->mAnonOnly;
213 - $fields['CreateAccount']['default'] = $block->mCreateAccount;
 212+ $fields['HardBlock']['default'] = $block->isHardblock();
 213+ $fields['CreateAccount']['default'] = $block->prevents( 'createaccount' );
214214 $fields['AutoBlock']['default'] = $block->mEnableAutoblock;
215215 if( isset( $fields['DisableEmail'] ) ){
216 - $fields['DisableEmail']['default'] = $block->mBlockEmail;
 216+ $fields['DisableEmail']['default'] = $block->prevents( 'sendemail' );
217217 }
218218 if( isset( $fields['HideUser'] ) ){
219219 $fields['HideUser']['default'] = $block->mHideName;
220220 }
221221 if( isset( $fields['DisableUTEdit'] ) ){
222 - $fields['DisableUTEdit']['default'] = !$block->mAllowUsertalk;
 222+ $fields['DisableUTEdit']['default'] = $block->prevents( 'editusertalk' );
223223 }
224224 $fields['Reason']['default'] = $block->mReason;
225225 $fields['AlreadyBlocked']['default'] = true;
@@ -502,10 +502,6 @@
503503 return array( 'ipb_expiry_invalid' );
504504 }
505505
506 - if( !$wgBlockAllowsUTEdit ){
507 - $data['DisableUTEdit'] = true;
508 - }
509 -
510506 # If the user has done the form 'properly', they won't even have been given the
511507 # option to suppress-block unless they have the 'hideuser' permission
512508 if( !isset( $data['HideUser'] ) ){
@@ -548,10 +544,11 @@
549545 !$data['HardBlock'], # Block anon only
550546 $data['CreateAccount'],
551547 $data['AutoBlock'],
552 - $data['HideUser'],
553 - $data['DisableEmail'],
554 - !$data['DisableUTEdit'] # *Allow* UTEdit
 548+ $data['HideUser']
555549 );
 550+
 551+ $block->prevents( 'editusertalk', ( !$wgBlockAllowsUTEdit || $data['DisableUTEdit'] ) );
 552+ $block->prevents( 'sendemail', $data['DisableEmail'] );
556553
557554 if( !wfRunHooks( 'BlockIp', array( &$block, &$wgUser ) ) ) {
558555 return array( 'hookaborted' );
@@ -569,7 +566,7 @@
570567
571568 # This returns direct blocks before autoblocks/rangeblocks, since we should
572569 # be sure the user is blocked by now it should work for our purposes
573 - $currentBlock = Block::newFromDB( $target, $userId );
 570+ $currentBlock = Block::newFromTargetAndType( $target, $type );
574571
575572 if( $block->equals( $currentBlock ) ) {
576573 return array( 'ipb_already_blocked' );
@@ -613,7 +610,7 @@
614611 }
615612
616613 # Block constructor sanitizes certain block options on insert
617 - $data['BlockEmail'] = $block->mBlockEmail;
 614+ $data['BlockEmail'] = $block->prevents( 'sendemail' );
618615 $data['AutoBlock'] = $block->mEnableAutoblock;
619616
620617 # Prepare log parameters
Index: trunk/phase3/includes/Block.php
@@ -16,10 +16,18 @@
1717 */
1818 class Block {
1919 /* public*/ var $mAddress, $mUser, $mBy, $mReason, $mTimestamp, $mAuto, $mId, $mExpiry,
20 - $mRangeStart, $mRangeEnd, $mAnonOnly, $mEnableAutoblock, $mHideName,
21 - $mBlockEmail, $mByName, $mAngryAutoblock, $mAllowUsertalk;
22 - private $mFromMaster;
 20+ $mEnableAutoblock, $mHideName,
 21+ $mByName, $mAngryAutoblock;
 22+ private
 23+ $mFromMaster,
 24+ $mRangeStart,
 25+ $mRangeEnd,
 26+ $mAnonOnly,
 27+ $mBlockEmail,
 28+ $mAllowUsertalk,
 29+ $mCreateAccount;
2330
 31+ /// TYPE constants
2432 const TYPE_USER = 1;
2533 const TYPE_IP = 2;
2634 const TYPE_RANGE = 3;
@@ -227,6 +235,54 @@
228236 }
229237
230238 /**
 239+ * Get a set of SQL conditions which will select rangeblocks encompasing a given range
 240+ * @param $start String Hexadecimal IP representation
 241+ * @param $end String Hexadecimal IP represenation, or null to use $start = $end
 242+ * @return String
 243+ */
 244+ public static function getRangeCond( $start, $end = null ){
 245+ if( $end === null ){
 246+ $end = $start;
 247+ }
 248+ # Per bug 14634, we want to include relevant active rangeblocks; for
 249+ # rangeblocks, we want to include larger ranges which enclose the given
 250+ # range. We know that all blocks must be smaller than $wgBlockCIDRLimit,
 251+ # so we can improve performance by filtering on a LIKE clause
 252+ $chunk = self::getIpFragment( $start );
 253+ $dbr = wfGetDB( DB_SLAVE );
 254+ $like = $dbr->buildLike( $chunk, $dbr->anyString() );
 255+
 256+ # Fairly hard to make a malicious SQL statement out of hex characters,
 257+ # but stranger things have happened...
 258+ $safeStart = $dbr->addQuotes( $start );
 259+ $safeEnd = $dbr->addQuotes( $end );
 260+
 261+ return $dbr->makeList(
 262+ array(
 263+ "ipb_range_start $like",
 264+ "ipb_range_start <= $safeStart",
 265+ "ipb_range_end >= $safeEnd",
 266+ ),
 267+ LIST_AND
 268+ );
 269+ }
 270+
 271+ /**
 272+ * Get the component of an IP address which is certain to be the same between an IP
 273+ * address and a rangeblock containing that IP address.
 274+ * @param $hex String Hexadecimal IP representation
 275+ * @return String
 276+ */
 277+ protected static function getIpFragment( $hex ){
 278+ global $wgBlockCIDRLimit;
 279+ if( substr( $hex, 0, 3 ) == 'v6-' ){
 280+ return 'v6-' . substr( substr( $hex, 3 ), 0, floor( $wgBlockCIDRLimit['IPv6'] / 4 ) );
 281+ } else {
 282+ return substr( $hex, 0, floor( $wgBlockCIDRLimit['IPv4'] / 4 ) );
 283+ }
 284+ }
 285+
 286+ /**
231287 * Fill in member variables from a result wrapper
232288 *
233289 * @param $res ResultWrapper: row from the ipblocks table
@@ -707,6 +763,38 @@
708764 }
709765
710766 /**
 767+ * Get the IP address at the start of the range in Hex form
 768+ * @return String IP in Hex form
 769+ */
 770+ public function getRangeStart(){
 771+ switch( $this->type ){
 772+ case self::TYPE_USER:
 773+ return null;
 774+ case self::TYPE_IP:
 775+ return IP::toHex( $this->target );
 776+ case self::TYPE_RANGE:
 777+ return $this->mRangeStart;
 778+ default: throw new MWException( "Block with invalid type" );
 779+ }
 780+ }
 781+
 782+ /**
 783+ * Get the IP address at the start of the range in Hex form
 784+ * @return String IP in Hex form
 785+ */
 786+ public function getRangeEnd(){
 787+ switch( $this->type ){
 788+ case self::TYPE_USER:
 789+ return null;
 790+ case self::TYPE_IP:
 791+ return IP::toHex( $this->target );
 792+ case self::TYPE_RANGE:
 793+ return $this->mRangeEnd;
 794+ default: throw new MWException( "Block with invalid type" );
 795+ }
 796+ }
 797+
 798+ /**
711799 * Get the user id of the blocking sysop
712800 *
713801 * @return Integer
@@ -740,6 +828,49 @@
741829 }
742830
743831 /**
 832+ * Get/set whether the Block is a hardblock (affects logged-in users on a given IP/range
 833+ * @param $x Bool
 834+ * @return Bool
 835+ */
 836+ public function isHardblock( $x = null ){
 837+ $y = $this->mAnonOnly;
 838+ if( $x !== null){
 839+ $this->mAnonOnly = !$x;
 840+ }
 841+ return !$y;
 842+ }
 843+
 844+ /**
 845+ * Get/set whether the Block prevents a given action
 846+ * @param $action String
 847+ * @param $x Bool
 848+ * @return Bool
 849+ */
 850+ public function prevents( $action, $x = null ){
 851+ switch( $action ){
 852+ case 'edit':
 853+ # TODO Not actually quite this simple (bug 13611 etc)
 854+ return true;
 855+
 856+ case 'createaccount':
 857+ return wfSetVar( $this->mCreateAccount, $x );
 858+
 859+ case 'sendemail':
 860+ return wfSetVar( $this->mBlockEmail, $x );
 861+
 862+ case 'editusertalk':
 863+ $y = $this->mAllowUsertalk;
 864+ if( $x !== null){
 865+ $this->mAllowUsertalk = !$x;
 866+ }
 867+ return !$y;
 868+
 869+ default:
 870+ return null;
 871+ }
 872+ }
 873+
 874+ /**
744875 * Get the block name, but with autoblocked IPs hidden as per standard privacy policy
745876 * @return String, text is escaped
746877 */
Index: trunk/extensions/AbuseFilter/Views/AbuseFilterViewRevert.php
@@ -188,7 +188,7 @@
189189 function revertAction( $action, $result ) {
190190 switch( $action ) {
191191 case 'block':
192 - $block = Block::newFromDB( '', $result['userid'], false );
 192+ $block = Block::newFromTarget( User::whoIs( $result['userid'] ) );
193193 if ( !$block || $block->getBy() != AbuseFilter::getFilterUser()->getId() ) {
194194 return false; // Not blocked by abuse filter.
195195 }
Index: trunk/extensions/AbuseFilter/AbuseFilter.class.php
@@ -1013,8 +1013,8 @@
10141014 $block->mByName = $filterUser->getName();
10151015 $block->mReason = wfMsgForContent( 'abusefilter-blockreason', $rule_desc );
10161016 $block->mTimestamp = wfTimestampNow();
1017 - $block->mAnonOnly = 1;
1018 - $block->mCreateAccount = 1;
 1017+ $block->isHardblock( false );
 1018+ $block->prevents( 'createaccount', true );
10191019 $block->mExpiry = SpecialBlock::parseExpiryInput( $wgAbuseFilterBlockDuration );
10201020
10211021 $block->insert();
@@ -1042,11 +1042,7 @@
10431043 case 'rangeblock':
10441044 $filterUser = AbuseFilter::getFilterUser();
10451045
1046 - $range = IP::toHex( wfGetIP() );
1047 - $range = substr( $range, 0, 4 ) . '0000';
1048 - $range = long2ip( hexdec( $range ) );
1049 - $range .= '/16';
1050 - $range = Block::normaliseRange( $range );
 1046+ $range = IP::sanitizeRange( wfGetIP() . '/16' );
10511047
10521048 // Create a block.
10531049 $block = new Block;
@@ -1056,8 +1052,8 @@
10571053 $block->mByName = $filterUser->getName();
10581054 $block->mReason = wfMsgForContent( 'abusefilter-blockreason', $rule_desc );
10591055 $block->mTimestamp = wfTimestampNow();
1060 - $block->mAnonOnly = 0;
1061 - $block->mCreateAccount = 1;
 1056+ $block->isHardblock( false );
 1057+ $block->prevents( 'createaccount', true );
10621058 $block->mExpiry = SpecialBlock::parseExpiryInput( '1 week' );
10631059
10641060 $block->insert();
Index: trunk/extensions/regexBlock/regexBlockCore.php
@@ -573,7 +573,7 @@
574574 /* account creation check goes through the same hook... */
575575 if ( $valid['create'] == 1 ) {
576576 if ( $user->mBlock ) {
577 - $user->mBlock->mCreateAccount = 1;
 577+ $user->mBlock->prevents( 'createaccount', true );
578578 }
579579 }
580580 /* set expiry information */

Sign-offs

UserFlagDate
Nikerabbitinspected09:21, 20 March 2011

Follow-up revisions

RevisionCommit summaryAuthorDate
r84406Follow-up r84358 CR: rename 'editusertalk' to 'editownusertalk', private --> ...happy-melon17:43, 20 March 2011

Comments

#Comment by Nikerabbit (talk | contribs)   09:21, 20 March 2011

I don't like the accessor isAutoblock, because the name says it is a getter. If it is really too much effort to implement getter and setter, it should at least follow the pattern used in ParserOptions, ie setIsFoo().

Shouldn't you use protected visibility by default, unless there is a pressing need to use private?

I wonder where is the list of actions that can be used with prevents(). Would editusertalk be better named editowntalkpage?

And then some minor nitpicking about style:

if( $x !== null){

Missing space after null. There should be space between ) and { everywhere.

#Comment by Happy-melon (talk | contribs)   11:36, 20 March 2011

It's not a case of "effort" to implement both getter and setter, it's more effort to have a combined function. I think it's nicer code to have

$foo = $block->isHardblock();
$block->isHardblock( false );

Than

$foo = $block->getIsHardblock();
$block->setIsHardblock( false );

And both behaviours are 'sensible' given the function name. $foo = $block->setIsHardblock(); doesn't make sense.

I originally had the actions as class constants before deciding that it wasn't worth the effort. In a strongly-typed language they'd be an enum or somesuch. I probably won't drill as far down as to change the schema representation of that data, but if I did it would be to create an ipblocks_permissions table which keyed permissions to the block id, allowing arbitrary extension of permissions. It might also be worth implementing a hook in prevents() to allow extensions to do interesting things with the block. That probably is a clearer name, yes.

#Comment by Nikerabbit (talk | contribs)   12:54, 20 March 2011

Probably you are right that isHardblock() makes sense for both.

#Comment by Aaron Schulz (talk | contribs)   22:31, 17 June 2011
$range = IP::sanitizeRange( wfGetIP() . '/16' );

This problem was pre-existing...but that doesn't make much sense for IPv6.

Status & tagging log