MediaWiki  master
img_auth.php
Go to the documentation of this file.
1 <?php
42 define( 'MW_NO_OUTPUT_COMPRESSION', 1 );
43 define( 'MW_ENTRY_POINT', 'img_auth' );
44 require __DIR__ . '/includes/WebStart.php';
45 
47 
49 $mediawiki->doPostOutputShutdown();
50 
51 function wfImageAuthMain() {
53 
54  $services = \MediaWiki\MediaWikiServices::getInstance();
55  $permissionManager = $services->getPermissionManager();
56 
57  $request = RequestContext::getMain()->getRequest();
58  $publicWiki = in_array( 'read', $permissionManager->getGroupPermissions( [ '*' ] ), true );
59 
60  // Find the path assuming the request URL is relative to the local public zone URL
61  $baseUrl = $services->getRepoGroup()->getLocalRepo()->getZoneUrl( 'public' );
62  if ( $baseUrl[0] === '/' ) {
63  $basePath = $baseUrl;
64  } else {
65  $basePath = parse_url( $baseUrl, PHP_URL_PATH );
66  }
68 
69  if ( $path === false ) {
70  // Try instead assuming img_auth.php is the base path
71  $basePath = $wgImgAuthPath ?: "$wgScriptPath/img_auth.php";
73  }
74 
75  if ( $path === false ) {
76  wfForbidden( 'img-auth-accessdenied', 'img-auth-notindir' );
77  return;
78  }
79 
80  if ( $path === '' || $path[0] !== '/' ) {
81  // Make sure $path has a leading /
82  $path = "/" . $path;
83  }
84 
85  $user = RequestContext::getMain()->getUser();
86 
87  // Various extensions may have their own backends that need access.
88  // Check if there is a special backend and storage base path for this file.
89  foreach ( $wgImgAuthUrlPathMap as $prefix => $storageDir ) {
90  $prefix = rtrim( $prefix, '/' ) . '/'; // implicit trailing slash
91  if ( strpos( $path, $prefix ) === 0 ) {
92  $be = $services->getFileBackendGroup()->backendFromPath( $storageDir );
93  $filename = $storageDir . substr( $path, strlen( $prefix ) ); // strip prefix
94  // Check basic user authorization
95  $isAllowedUser = $permissionManager->userHasRight( $user, 'read' );
96  if ( !$isAllowedUser ) {
97  wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $path );
98  return;
99  }
100  if ( $be->fileExists( [ 'src' => $filename ] ) ) {
101  wfDebugLog( 'img_auth', "Streaming `" . $filename . "`." );
102  $be->streamFile( [ 'src' => $filename ],
103  [ 'Cache-Control: private', 'Vary: Cookie' ] );
104  } else {
105  wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $path );
106  }
107  return;
108  }
109  }
110 
111  // Get the local file repository
112  $repo = $services->getRepoGroup()->getRepo( 'local' );
113  $zone = strstr( ltrim( $path, '/' ), '/', true );
114 
115  // Get the full file storage path and extract the source file name.
116  // (e.g. 120px-Foo.png => Foo.png or page2-120px-Foo.png => Foo.png).
117  // This only applies to thumbnails/transcoded, and each of them should
118  // be under a folder that has the source file name.
119  if ( $zone === 'thumb' || $zone === 'transcoded' ) {
120  $name = wfBaseName( dirname( $path ) );
121  $filename = $repo->getZonePath( $zone ) . substr( $path, strlen( "/" . $zone ) );
122  // Check to see if the file exists
123  if ( !$repo->fileExists( $filename ) ) {
124  wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $filename );
125  return;
126  }
127  } else {
128  $name = wfBaseName( $path ); // file is a source file
129  $filename = $repo->getZonePath( 'public' ) . $path;
130  // Check to see if the file exists and is not deleted
131  $bits = explode( '!', $name, 2 );
132  if ( substr( $path, 0, 9 ) === '/archive/' && count( $bits ) == 2 ) {
133  $file = $repo->newFromArchiveName( $bits[1], $name );
134  } else {
135  $file = $repo->newFile( $name );
136  }
137  if ( !$file->exists() || $file->isDeleted( File::DELETED_FILE ) ) {
138  wfForbidden( 'img-auth-accessdenied', 'img-auth-nofile', $filename );
139  return;
140  }
141  }
142 
143  $headers = []; // extra HTTP headers to send
144 
145  $title = Title::makeTitleSafe( NS_FILE, $name );
146 
147  if ( !$publicWiki ) {
148  // For private wikis, run extra auth checks and set cache control headers
149  $headers['Cache-Control'] = 'private';
150  $headers['Vary'] = 'Cookie';
151 
152  if ( !$title instanceof Title ) { // files have valid titles
153  wfForbidden( 'img-auth-accessdenied', 'img-auth-badtitle', $name );
154  return;
155  }
156 
157  // Run hook for extension authorization plugins
159  $result = null;
160  if ( !Hooks::run( 'ImgAuthBeforeStream', [ &$title, &$path, &$name, &$result ] ) ) {
161  wfForbidden( $result[0], $result[1], array_slice( $result, 2 ) );
162  return;
163  }
164 
165  // Check user authorization for this title
166  // Checks Whitelist too
167 
168  if ( !$permissionManager->userCan( 'read', $user, $title ) ) {
169  wfForbidden( 'img-auth-accessdenied', 'img-auth-noread', $name );
170  return;
171  }
172  }
173 
174  if ( isset( $_SERVER['HTTP_RANGE'] ) ) {
175  $headers['Range'] = $_SERVER['HTTP_RANGE'];
176  }
177  if ( isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ) {
178  $headers['If-Modified-Since'] = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
179  }
180 
181  if ( $request->getCheck( 'download' ) ) {
182  $headers['Content-Disposition'] = 'attachment';
183  }
184 
185  // Allow modification of headers before streaming a file
186  Hooks::run( 'ImgAuthModifyHeaders', [ $title->getTitleValue(), &$headers ] );
187 
188  // Stream the requested file
189  list( $headers, $options ) = HTTPFileStreamer::preprocessHeaders( $headers );
190  wfDebugLog( 'img_auth', "Streaming `" . $filename . "`." );
191  $repo->streamFileWithStatus( $filename, $headers, $options );
192 }
193 
203 function wfForbidden( $msg1, $msg2, ...$args ) {
204  global $wgImgAuthDetails;
205 
206  $args = ( isset( $args[0] ) && is_array( $args[0] ) ) ? $args[0] : $args;
207 
208  $msgHdr = wfMessage( $msg1 )->text();
209  $detailMsgKey = $wgImgAuthDetails ? $msg2 : 'badaccess-group0';
210  $detailMsg = wfMessage( $detailMsgKey, $args )->text();
211 
212  wfDebugLog( 'img_auth',
213  "wfForbidden Hdr: " . wfMessage( $msg1 )->inLanguage( 'en' )->text() . " Msg: " .
214  wfMessage( $msg2, $args )->inLanguage( 'en' )->text()
215  );
216 
217  HttpStatus::header( 403 );
218  header( 'Cache-Control: no-cache' );
219  header( 'Content-Type: text/html; charset=utf-8' );
221  echo $templateParser->processTemplate( 'ImageAuthForbidden', [
222  'msgHdr' => $msgHdr,
223  'detailMsg' => $detailMsg,
224  ] );
225 }
WebRequest\getRequestPathSuffix
static getRequestPathSuffix( $basePath)
If the request URL matches a given base path, extract the path part of the request URL after that bas...
Definition: WebRequest.php:234
wfBaseName
wfBaseName( $path, $suffix='')
Return the final portion of a pathname.
Definition: GlobalFunctions.php:2315
NS_FILE
const NS_FILE
Definition: Defines.php:75
$file
if(PHP_SAPI !='cli-server') if(!isset( $_SERVER['SCRIPT_FILENAME'])) $file
Item class for a filearchive table row.
Definition: router.php:42
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1198
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:992
HTTPFileStreamer\preprocessHeaders
static preprocessHeaders( $headers)
Takes HTTP headers in a name => value format and converts them to the weird format expected by stream...
Definition: HTTPFileStreamer.php:51
MediaWiki
A helper class for throttling authentication attempts.
$args
if( $line===false) $args
Definition: mcc.php:124
$wgImgAuthDetails
$wgImgAuthDetails
Set this to true if you use img_auth and want the user to see details on why access failed.
Definition: DefaultSettings.php:482
$title
$title
Definition: testCompression.php:38
$wgImgAuthPath
$wgImgAuthPath
The base path for img_auth.php.
Definition: DefaultSettings.php:265
$wgImgAuthUrlPathMap
$wgImgAuthUrlPathMap
Map of relative URL directories to match to internal mwstore:// base storage paths.
Definition: DefaultSettings.php:498
Title\makeTitleSafe
static makeTitleSafe( $ns, $title, $fragment='', $interwiki='')
Create a new Title from a namespace index and a DB key.
Definition: Title.php:621
$templateParser
$templateParser
Definition: NoLocalSettings.php:47
$mediawiki
$mediawiki
Definition: img_auth.php:48
wfImageAuthMain
wfImageAuthMain()
Definition: img_auth.php:51
wfForbidden
wfForbidden( $msg1, $msg2,... $args)
Issue a standard HTTP 403 Forbidden header ($msg1-a message index, not a message) and an error messag...
Definition: img_auth.php:203
RequestContext\getMain
static getMain()
Get the RequestContext object associated with the main request.
Definition: RequestContext.php:451
Title
Represents a title within MediaWiki.
Definition: Title.php:42
HttpStatus\header
static header( $code)
Output an HTTP status code header.
Definition: HttpStatus.php:96
$path
$path
Definition: NoLocalSettings.php:25
$basePath
$basePath
Definition: addSite.php:5
File\DELETED_FILE
const DELETED_FILE
Definition: File.php:63
$wgScriptPath
$wgScriptPath
The path we should point to.
Definition: DefaultSettings.php:137
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:133
TemplateParser
Definition: TemplateParser.php:27