Skip to content

Commit

Permalink
Add CSS class to restricted pages if they are shown on Special:Search (
Browse files Browse the repository at this point in the history
…#5)

* Update mediawiki-codesniffer

* Add CSS class to restricted pages if they are shown on Special:Search
  • Loading branch information
edwardspec authored Oct 5, 2024
1 parent 4ea1237 commit 632ee15
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,9 @@
<arg name="extensions" value="php,php5,inc" />
<arg name="encoding" value="utf8" />
<exclude-pattern>vendor</exclude-pattern>

<rule ref="./vendor/mediawiki/mediawiki-codesniffer/MediaWiki">
<!-- FIXME: changing lines with this warning requires rigorous testing -->
<exclude name="Generic.CodeAnalysis.RequireExplicitBooleanOperatorPrecedence.MissingParentheses" />
</rule>
</ruleset>
7 changes: 6 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"require-dev": {
"mediawiki/mediawiki-codesniffer": "31.0.0",
"mediawiki/mediawiki-codesniffer": "44.0.0",
"mediawiki/minus-x": "1.1.0",
"php-parallel-lint/php-console-highlighter": "0.5.0",
"php-parallel-lint/php-parallel-lint": "1.2.0"
Expand All @@ -15,5 +15,10 @@
"minus-x fix .",
"phpcbf"
]
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true
}
}
}
3 changes: 2 additions & 1 deletion extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@
"ParserFirstCallInit": "AccessControlHooks::accessControlExtension",
"OutputPageParserOutput": "AccessControlHooks::onOutputPageParserOutput",
"LinksUpdate": "AccessControlHooks::onLinksUpdate",
"LoadExtensionSchemaUpdates": "AccessControlHooks::onLoadExtensionSchemaUpdates"
"LoadExtensionSchemaUpdates": "AccessControlHooks::onLoadExtensionSchemaUpdates",
"ShowSearchHit": "AccessControlHooks::onShowSearchHit"
},
"manifest_version": 2
}
105 changes: 83 additions & 22 deletions src/AccessControlHooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,25 @@ class AccessControlHooks {
private const C_PAGE = 'ac_page_id';
private const C_TAG_CONTENT = 'ac_tag_content';

/**
* @var array
* @phan-var array<string,mixed>
*/
private static $cache = [];

/**
* @var array
* @phan-var array<string,bool>
*
* Format: [ 'pageName1' => true, ... ]
* This is only used of $wgAccessControlAllowTextSnippetInSearchResultsForAll is true,
* which allows restricted pages to appear in search results.
*
* This array will contain the list of all restricted pages (which current user can't read)
* that were just shown to current user in the search results.
*/
private static $restrictedSearchResults = [];

/**
* @see https://www.mediawiki.org/wiki/Manual:Hooks/ParserFirstCallInit
*
Expand Down Expand Up @@ -50,14 +67,9 @@ public static function doControlUserAccess( string $input, array $args, Parser $
* @throws MWException
*/
private static function canUserDoAction( User $user, ?array $tagContentArray, string $actionName ): Status {
static $allowReadForAllInSearchResult = null;

if ( $allowReadForAllInSearchResult === null ) {
$allowReadForAllInSearchResult = (bool)self::getConfigValue( 'AccessControlAllowTextSnippetInSearchResultsForAll' );
}

// Return true by default
$return = Status::newGood( true );
$nosearch = false;

if ( $tagContentArray ) {
// For backward compatibility
Expand All @@ -68,9 +80,8 @@ private static function canUserDoAction( User $user, ?array $tagContentArray, st

$i = array_search( '(nosearch)', $tagContentArray, true );
if ( $i !== false ) {
$nosearch = true;
array_splice( $tagContentArray, $i, 1 );
} elseif ( $actionName === 'search' && $allowReadForAllInSearchResult ) {
return $return;
}
}

Expand Down Expand Up @@ -113,8 +124,17 @@ private static function canUserDoAction( User $user, ?array $tagContentArray, st
return $return;
}

if ( $actionName === 'search' && $searchAccess ) {
return $return;
if ( $actionName === 'search' ) {
if ( $searchAccess ) {
// Allowed.
return $return;
}

if ( $nosearch ) {
// Inform the caller that $wgAccessControlAllowTextSnippetInSearchResultsForAll
// should be ignored for this page.
$return->warning( 'accesscontrol-nosearch' );
}
}

if ( $readAccess ) {
Expand Down Expand Up @@ -351,14 +371,56 @@ public static function onGetUserPermissionsErrors( $title, $user, $action, &$res
}

$tagContentArray = self::getRestrictionForTitle( $title, $user );
$isAllowed = self::canUserDoAction( $user, $tagContentArray, $action )->getValue();
$status = self::canUserDoAction( $user, $tagContentArray, $action );
$isAllowed = $status->getValue();

// Special handling for search.
if ( !$isAllowed && $action === 'search' &&
self::getConfigValue( 'AccessControlAllowTextSnippetInSearchResultsForAll' ) &&
!$status->hasMessage( 'accesscontrol-nosearch' )
) {
// If $wgAccessControlAllowTextSnippetInSearchResultsForAll is true (default: false),
// then permission errors won't prevent this page from being shown in search results.
// However, we might want to style these restricted results differently (in ShowSearchHit hook).
self::$restrictedSearchResults[$title->getFullText()] = true;
return true;
}

if ( !$isAllowed ) {
$result = [ 'accesscontrol-info-box', $title->getRootText() ];
}

return $isAllowed;
}

/**
* @param SpecialSearch $searchPage
* @param SearchResult $result
* @param string[] $terms
* @param string &$link
* @param string &$redirect
* @param string &$section
* @param string &$extract
* @param string &$score
* @param string &$size
* @param string &$date
* @param string &$related
* @param string &$html
* @return bool|void
*/
public static function onShowSearchHit( $searchPage, $result, $terms, &$link,
&$redirect, &$section, &$extract, &$score, &$size, &$date, &$related, &$html
) {
$pageName = $result->getTitle()->getFullText();
if ( isset( self::$restrictedSearchResults[$pageName] ) ) {
// User can see this page in search results, but is not allowed to read it.
// Add a CSS class, so that these restricted results can be styled differently.
$link = Xml::tags( 'span', [ 'class' => 'mw-ac-restricted-search-result' ], $link );
}

return true;
}

/**
* @param LinksUpdate $linksUpdate
*/
Expand Down Expand Up @@ -434,23 +496,22 @@ private static function updateRestrictionInDatabase( int $pageId, ?array $tagCon

$db = wfGetDB( DB_MASTER );
$index = [
self::C_PAGE => $pageId,
self::C_PAGE => $pageId,
];
$row = [
self::C_TAG_CONTENT => $tagContentArray,
self::C_TAG_CONTENT => $tagContentArray,
];
try {
$db->upsert(
self::TABLE,
[ $index + $row ],
[[ self::C_PAGE ]],
$row,
__METHOD__
);
$db->upsert(
self::TABLE,
[ $index + $row ],
[ [ self::C_PAGE ] ],
$row,
__METHOD__
);
} catch ( Exception $e ) {
MWDebug::warning( $e->getMessage() );
MWDebug::warning( $e->getMessage() );
}

}

/**
Expand Down

0 comments on commit 632ee15

Please sign in to comment.