Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"license-name": "MIT",
"@note": "Only list specific tested MediaWiki versions given how unstable the extension is",
"requires": {
"MediaWiki": "1.39.15 || 1.43.5"
"MediaWiki": "1.39.17 || 1.43.5"
},
"AutoloadNamespaces": {
"MediaWiki\\Extension\\MixedVisibilityFiles\\": "src/"
Expand All @@ -27,7 +27,9 @@
"visibility": {
"class": "\\MediaWiki\\Extension\\MixedVisibilityFiles\\VisibilityHooks",
"services": [
"PageProps"
"PageProps",
"UserGroupManager",
"MainConfig"
]
}
},
Expand All @@ -36,5 +38,11 @@
"getUserPermissionsErrorsExpensive": "visibility",
"MediaWikiServices": "services"
},
"config": {
"MixedVisibilityFilesPrefixRestrictions": {
"value": {},
"description": "Map of file name prefixes to user group(s). Files whose name starts with a given prefix are restricted to users in the specified group(s). Each key is a prefix string and each value is either a group name (string) or an array of group names. Prefix matching is case-sensitive; note that MediaWiki normalizes the first character of file names to uppercase. Example: {\"Confidential_\": \"sysop\", \"Internal_\": [\"sysop\", \"bureaucrat\"]}"
}
},
"manifest_version": 2
}
43 changes: 39 additions & 4 deletions src/VisibilityHooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace MediaWiki\Extension\MixedVisibilityFiles;

use Config;
use MediaWiki\Hook\GetDoubleUnderscoreIDsHook;
use MediaWiki\Permissions\Hook\GetUserPermissionsErrorsExpensiveHook;
use MediaWiki\User\UserGroupManager;
use PageProps;

class VisibilityHooks implements
Expand All @@ -13,8 +15,19 @@ class VisibilityHooks implements

private PageProps $pageProps;

public function __construct( PageProps $pageProps ) {
private UserGroupManager $userGroupManager;

/** @var array<string,string|string[]> */
private array $prefixRestrictions;

public function __construct(
PageProps $pageProps,
UserGroupManager $userGroupManager,
Config $config
) {
$this->pageProps = $pageProps;
$this->userGroupManager = $userGroupManager;
$this->prefixRestrictions = $config->get( 'MixedVisibilityFilesPrefixRestrictions' );
}

/**
Expand All @@ -33,14 +46,36 @@ public function onGetUserPermissionsErrorsExpensive( $title, $user, $action,
if ( MW_ENTRY_POINT !== 'img_auth' ) {
return;
}
// Only trying to affect file reads by anonymous users
// Only trying to affect file reads
if ( $action !== 'read' ) {
return;
}
if ( $user->isRegistered() ) {
if ( $title->getNamespace() !== NS_FILE ) {
return;
}
if ( $title->getNamespace() !== NS_FILE ) {

// Check prefix-based group restrictions (applies to all users)
$fileName = $title->getDBkey();
Comment thread
jeffw16 marked this conversation as resolved.
foreach ( $this->prefixRestrictions as $prefix => $requiredGroups ) {
if ( str_starts_with( $fileName, $prefix ) ) {
// File matches a prefix restriction
if ( !$user->isRegistered() ) {
$result = false;
return false;
}
$userGroups = $this->userGroupManager->getUserEffectiveGroups( $user );
$allowedGroups = (array)$requiredGroups;
if ( !array_intersect( $allowedGroups, $userGroups ) ) {
$result = false;
return false;
}
// User is in a required group, allow access
return;
}
}

// Default behavior: block anonymous users unless file is marked public
if ( $user->isRegistered() ) {
return;
}

Expand Down