Fix API application routing (#29303)

API not routed correctly on semi-SEF URLs with index.php in them
such as /api/index.php/v1/content/article

The reason is that the ApiRouter code is incorrectly tied to the
URL rewriting option in Global Configuration and doesn't really
check correctly whether the index.php part is found at the
beginning of the URL.
This commit is contained in:
Nicholas K. Dionysopoulos 2020-05-31 22:22:31 +03:00 committed by GitHub
parent 82eac13730
commit 4563141cd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 28 deletions

View File

@ -77,20 +77,34 @@ Header always set X-Content-Type-Options "nosniff"
# RewriteBase /
## Begin - Joomla! core SEF Section.
#
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
#
# If the requested path and file is not /index.php and the request
# has not already been internally rewritten to the index.php script
RewriteCond %{REQUEST_URI} !^/index\.php
# and the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
# and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
# internally rewrite the request to the index.php script
RewriteRule .* index.php [L]
#
## End - Joomla! core SEF Section.
#
# PHP FastCGI fix for HTTP Authorization, required for the API application
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# -- SEF URLs for the API application
# If the requested path starts with /api, the file is not /api/index.php
# and the request has not already been internally rewritten to the
# api/index.php script
RewriteCond %{REQUEST_URI} ^/api/
RewriteCond %{REQUEST_URI} !^/api/index\.php
# and the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
# and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
# internally rewrite the request to the /api/index.php script
RewriteRule .* api/index.php [L]
# -- SEF URLs for the public frontend application
# If the requested path and file is not /index.php and the request
# has not already been internally rewritten to the index.php script
RewriteCond %{REQUEST_URI} !^/index\.php
# and the requested path and file doesn't directly match a physical file
RewriteCond %{REQUEST_FILENAME} !-f
# and the requested path and file doesn't directly match a physical folder
RewriteCond %{REQUEST_FILENAME} !-d
# internally rewrite the request to the index.php script
RewriteRule .* index.php [L]
#
## End - Joomla! core SEF Section.
</IfModule>
## These directives are only enabled if the Apache mod_rewrite module is disabled

View File

@ -115,18 +115,8 @@ class ApiRouter extends Router
// Remove the base URI path.
$path = substr_replace($path, '', 0, \strlen($baseUri));
if (!$this->app->get('sef_rewrite'))
{
// Transform the route
if ($path === 'index.php')
{
$path = '';
}
else
{
$path = str_replace('index.php/', '', $path);
}
}
// Transform the route
$path = $this->removeIndexPhpFromPath($path);
$query = Uri::getInstance()->getQuery(true);
@ -159,4 +149,34 @@ class ApiRouter extends Router
throw new RouteNotFoundException(sprintf('Unable to handle request for route `%s`.', $path));
}
/**
* Removes the index.php from the route's path.
*
* @param string $path
*
* @return string
*
* @since 4.0.0
*/
private function removeIndexPhpFromPath(string $path): string
{
// Normalize the path
$path = ltrim($path, '/');
// We can only remove index.php if it's present in the beginning of the route
if (strpos($path, 'index.php') !== 0)
{
return $path;
}
// Edge case: the route is index.php without a trailing slash. Bad idea but we can still map it to a null route.
if ($path === 'index.php')
{
return '';
}
// Remove the "index.php/" part of the route and return the result.
return substr($path, 10);
}
}

View File

@ -5,7 +5,7 @@
<directoryBrowse enabled="false" />
<rewrite>
<rules>
<rule name="Joomla! Rule 1" stopProcessing="true">
<rule name="Joomla! Common Exploits Prevention" stopProcessing="true">
<match url="^(.*)$" ignoreCase="false" />
<conditions logicalGrouping="MatchAny">
<add input="{QUERY_STRING}" pattern="base64_encode[^(]*\([^)]*\)" ignoreCase="false" />
@ -15,7 +15,16 @@
</conditions>
<action type="CustomResponse" url="index.php" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
</rule>
<rule name="Joomla! Rule 2">
<rule name="Joomla! API Application SEF URLs">
<match url="^api/(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="^/api/index.php" ignoreCase="true" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
</conditions>
<action type="Rewrite" url="api/index.php" />
</rule>
<rule name="Joomla! Public Frontend SEF URLs">
<match url="(.*)" ignoreCase="false" />
<conditions logicalGrouping="MatchAll">
<add input="{URL}" pattern="^/index.php" ignoreCase="true" negate="true" />