From df52af620c9f13a73c47cefc373dc7c939bfc6af Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:27:12 +0000 Subject: [PATCH 1/3] Add prefix-based file access restrictions (RBAC) Introduce MixedVisibilityFilesPrefixRestrictions config that maps file name prefixes to required user group(s). Files matching a prefix are restricted to users in the specified group(s), enforced via the getUserPermissionsErrorsExpensive hook. Config example in LocalSettings.php: $wgMixedVisibilityFilesPrefixRestrictions = [ 'Confidential_' => 'sysop', 'Internal_' => ['sysop', 'bureaucrat'], ]; Co-authored-by: jeffw16 <11380894+jeffw16@users.noreply.github.com> Agent-Logs-Url: https://github.com/mywikis/MixedVisibilityFiles/sessions/ccc440fc-ea21-44b8-8881-495abcb9bcd6 --- extension.json | 10 +++++++++- src/VisibilityHooks.php | 43 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/extension.json b/extension.json index 421ca57..8e76705 100644 --- a/extension.json +++ b/extension.json @@ -27,7 +27,9 @@ "visibility": { "class": "\\MediaWiki\\Extension\\MixedVisibilityFiles\\VisibilityHooks", "services": [ - "PageProps" + "PageProps", + "UserGroupManager", + "MainConfig" ] } }, @@ -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. Example: {\"Confidential_\": \"sysop\", \"Internal_\": [\"sysop\", \"bureaucrat\"]}" + } + }, "manifest_version": 2 } diff --git a/src/VisibilityHooks.php b/src/VisibilityHooks.php index 1da3d7d..9ae05d1 100644 --- a/src/VisibilityHooks.php +++ b/src/VisibilityHooks.php @@ -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 @@ -13,8 +15,19 @@ class VisibilityHooks implements private PageProps $pageProps; - public function __construct( PageProps $pageProps ) { + private UserGroupManager $userGroupManager; + + /** @var array */ + private array $prefixRestrictions; + + public function __construct( + PageProps $pageProps, + UserGroupManager $userGroupManager, + Config $config + ) { $this->pageProps = $pageProps; + $this->userGroupManager = $userGroupManager; + $this->prefixRestrictions = $config->get( 'MixedVisibilityFilesPrefixRestrictions' ); } /** @@ -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(); + 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; } From c78b1f55cffab62a87c05b5774af51e0c416eee7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 25 Mar 2026 22:28:02 +0000 Subject: [PATCH 2/3] Document case-sensitivity in prefix restriction config description Co-authored-by: jeffw16 <11380894+jeffw16@users.noreply.github.com> Agent-Logs-Url: https://github.com/mywikis/MixedVisibilityFiles/sessions/ccc440fc-ea21-44b8-8881-495abcb9bcd6 --- extension.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.json b/extension.json index 8e76705..626ed02 100644 --- a/extension.json +++ b/extension.json @@ -41,7 +41,7 @@ "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. Example: {\"Confidential_\": \"sysop\", \"Internal_\": [\"sysop\", \"bureaucrat\"]}" + "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 From ebf8c1868ad0e18df86ed58cda1aa6e0db5a4494 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:52:27 +0000 Subject: [PATCH 3/3] Update MediaWiki 1.39 version requirement to 1.39.17 Agent-Logs-Url: https://github.com/mywikis/MixedVisibilityFiles/sessions/cf6dc173-df89-4e2d-982a-bf7cce0761ad Co-authored-by: jeffw16 <11380894+jeffw16@users.noreply.github.com> --- extension.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension.json b/extension.json index 626ed02..bccf6db 100644 --- a/extension.json +++ b/extension.json @@ -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/"