Skip to content

Commit 4563141

Browse files
author
Nicholas K. Dionysopoulos
authored
Fix API application routing (joomla#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.
1 parent 82eac13 commit 4563141

File tree

3 files changed

+71
-28
lines changed

3 files changed

+71
-28
lines changed

htaccess.txt

+28-14
Original file line numberDiff line numberDiff line change
@@ -77,20 +77,34 @@ Header always set X-Content-Type-Options "nosniff"
7777
# RewriteBase /
7878

7979
## Begin - Joomla! core SEF Section.
80-
#
81-
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
82-
#
83-
# If the requested path and file is not /index.php and the request
84-
# has not already been internally rewritten to the index.php script
85-
RewriteCond %{REQUEST_URI} !^/index\.php
86-
# and the requested path and file doesn't directly match a physical file
87-
RewriteCond %{REQUEST_FILENAME} !-f
88-
# and the requested path and file doesn't directly match a physical folder
89-
RewriteCond %{REQUEST_FILENAME} !-d
90-
# internally rewrite the request to the index.php script
91-
RewriteRule .* index.php [L]
92-
#
93-
## End - Joomla! core SEF Section.
80+
#
81+
# PHP FastCGI fix for HTTP Authorization, required for the API application
82+
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
83+
# -- SEF URLs for the API application
84+
# If the requested path starts with /api, the file is not /api/index.php
85+
# and the request has not already been internally rewritten to the
86+
# api/index.php script
87+
RewriteCond %{REQUEST_URI} ^/api/
88+
RewriteCond %{REQUEST_URI} !^/api/index\.php
89+
# and the requested path and file doesn't directly match a physical file
90+
RewriteCond %{REQUEST_FILENAME} !-f
91+
# and the requested path and file doesn't directly match a physical folder
92+
RewriteCond %{REQUEST_FILENAME} !-d
93+
# internally rewrite the request to the /api/index.php script
94+
RewriteRule .* api/index.php [L]
95+
# -- SEF URLs for the public frontend application
96+
# If the requested path and file is not /index.php and the request
97+
# has not already been internally rewritten to the index.php script
98+
RewriteCond %{REQUEST_URI} !^/index\.php
99+
# and the requested path and file doesn't directly match a physical file
100+
RewriteCond %{REQUEST_FILENAME} !-f
101+
# and the requested path and file doesn't directly match a physical folder
102+
RewriteCond %{REQUEST_FILENAME} !-d
103+
# internally rewrite the request to the index.php script
104+
RewriteRule .* index.php [L]
105+
#
106+
## End - Joomla! core SEF Section.
107+
94108
</IfModule>
95109

96110
## These directives are only enabled if the Apache mod_rewrite module is disabled

libraries/src/Router/ApiRouter.php

+32-12
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,8 @@ public function parseApiRoute($method = 'GET')
115115
// Remove the base URI path.
116116
$path = substr_replace($path, '', 0, \strlen($baseUri));
117117

118-
if (!$this->app->get('sef_rewrite'))
119-
{
120-
// Transform the route
121-
if ($path === 'index.php')
122-
{
123-
$path = '';
124-
}
125-
else
126-
{
127-
$path = str_replace('index.php/', '', $path);
128-
}
129-
}
118+
// Transform the route
119+
$path = $this->removeIndexPhpFromPath($path);
130120

131121
$query = Uri::getInstance()->getQuery(true);
132122

@@ -159,4 +149,34 @@ public function parseApiRoute($method = 'GET')
159149

160150
throw new RouteNotFoundException(sprintf('Unable to handle request for route `%s`.', $path));
161151
}
152+
153+
/**
154+
* Removes the index.php from the route's path.
155+
*
156+
* @param string $path
157+
*
158+
* @return string
159+
*
160+
* @since 4.0.0
161+
*/
162+
private function removeIndexPhpFromPath(string $path): string
163+
{
164+
// Normalize the path
165+
$path = ltrim($path, '/');
166+
167+
// We can only remove index.php if it's present in the beginning of the route
168+
if (strpos($path, 'index.php') !== 0)
169+
{
170+
return $path;
171+
}
172+
173+
// Edge case: the route is index.php without a trailing slash. Bad idea but we can still map it to a null route.
174+
if ($path === 'index.php')
175+
{
176+
return '';
177+
}
178+
179+
// Remove the "index.php/" part of the route and return the result.
180+
return substr($path, 10);
181+
}
162182
}

web.config.txt

+11-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<directoryBrowse enabled="false" />
66
<rewrite>
77
<rules>
8-
<rule name="Joomla! Rule 1" stopProcessing="true">
8+
<rule name="Joomla! Common Exploits Prevention" stopProcessing="true">
99
<match url="^(.*)$" ignoreCase="false" />
1010
<conditions logicalGrouping="MatchAny">
1111
<add input="{QUERY_STRING}" pattern="base64_encode[^(]*\([^)]*\)" ignoreCase="false" />
@@ -15,7 +15,16 @@
1515
</conditions>
1616
<action type="CustomResponse" url="index.php" statusCode="403" statusReason="Forbidden" statusDescription="Forbidden" />
1717
</rule>
18-
<rule name="Joomla! Rule 2">
18+
<rule name="Joomla! API Application SEF URLs">
19+
<match url="^api/(.*)" ignoreCase="false" />
20+
<conditions logicalGrouping="MatchAll">
21+
<add input="{URL}" pattern="^/api/index.php" ignoreCase="true" negate="true" />
22+
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
23+
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
24+
</conditions>
25+
<action type="Rewrite" url="api/index.php" />
26+
</rule>
27+
<rule name="Joomla! Public Frontend SEF URLs">
1928
<match url="(.*)" ignoreCase="false" />
2029
<conditions logicalGrouping="MatchAll">
2130
<add input="{URL}" pattern="^/index.php" ignoreCase="true" negate="true" />

0 commit comments

Comments
 (0)