Commit a1583955 authored by Benni Mack's avatar Benni Mack
Browse files

[TASK] Use middleware for SOAP endpoint & remove pi-based plugin

This change adds a PSR-15 middleware to intercept all /ter and /ter?wsdl
calls in favor of a pi-based plugin.

The plugin is removed (so the whole page in TYPO3 can be removed as well later-on).

The previous WSDL delivery endpoint extensions.typo3.org/typo3conf/ext/ter/tx_ter_wsdl.php
is now a redirect to /ter?wsdl, so everything is covered by the middleware.
parent 4310c1ee
Pipeline #9436 passed with stages
in 8 minutes and 34 seconds
...@@ -4104,9 +4104,10 @@ ...@@ -4104,9 +4104,10 @@
"dist": { "dist": {
"type": "path", "type": "path",
"url": "extensions/ter", "url": "extensions/ter",
"reference": "67290f0598857f79d6184fdf9be74209e0dab347" "reference": "6691b4efbb79761f87a4234dd4b9a9a76d50bf15"
}, },
"require": { "require": {
"ext-soap": "*",
"typo3/cms-core": "^9.5 || ^10.4" "typo3/cms-core": "^9.5 || ^10.4"
}, },
"type": "typo3-cms-extension", "type": "typo3-cms-extension",
...@@ -4118,10 +4119,7 @@ ...@@ -4118,10 +4119,7 @@
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"T3o\\Ter\\": "Classes/" "T3o\\Ter\\": "Classes/"
}, }
"classmap": [
"pi1"
]
}, },
"license": [ "license": [
"GPL-2.0-or-later" "GPL-2.0-or-later"
...@@ -5826,13 +5824,13 @@ ...@@ -5826,13 +5824,13 @@
"authors": [ "authors": [
{ {
"name": "The TYPO3 Community", "name": "The TYPO3 Community",
"role": "Contributor", "homepage": "https://typo3.org/community/",
"homepage": "https://typo3.org/community/" "role": "Contributor"
}, },
{ {
"name": "TYPO3 CMS Core Team", "name": "TYPO3 CMS Core Team",
"role": "Developer", "homepage": "https://forge.typo3.org/projects/typo3cms-core",
"homepage": "https://forge.typo3.org/projects/typo3cms-core" "role": "Developer"
} }
], ],
"description": "Minimal required set of TYPO3 extensions", "description": "Minimal required set of TYPO3 extensions",
......
<?php <?php
declare(strict_types = 1);
/* /*
* This file is part of the TYPO3 CMS project. * This file is part of TYPO3 CMS-extension "ter_fe2", created by Benni Mack.
* *
* It is free software; you can redistribute it and/or modify it under * It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2 * the terms of the GNU General Public License, either version 2
* of the License, or any later version. * of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/ */
/** namespace T3o\Ter\Middleware;
* SOAP server for the TYPO3 Extension Repository
* use Psr\Http\Message\ResponseInterface;
* $Id$ use Psr\Http\Message\ServerRequestInterface;
* use Psr\Http\Message\UriInterface;
* @author Robert Lemke <robert@typo3.org> use Psr\Http\Server\MiddlewareInterface;
*/ use Psr\Http\Server\RequestHandlerInterface;
/** use T3o\Ter\Api\Configuration;
* [CLASS/FUNCTION INDEX of SCRIPT] use T3o\Ter\Exception\FailedDependencyException;
* use T3o\Ter\Exception\InternalServerErrorException;
* use T3o\Ter\Exception\NotFoundException;
* use T3o\Ter\Exception\UnauthorizedException;
* 54: class tx_ter_pi1 extends tslib_pibase use T3o\Ter\Soap\TerSoapV1Handler;
* 61: function main ($content, $conf) use T3o\Ter\Soap\Wsdl;
* use TYPO3\CMS\Core\Http\Response;
* TOTAL FUNCTIONS: 1 use TYPO3\CMS\Core\Site\Entity\Site;
* (This index is automatically created/updated by the extension "extdeveval") use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
use TYPO3\CMS\Frontend\Plugin\AbstractPlugin;
/** /**
* TYPO3 Extension Repository, frontend plugin for SOAP service * This middleware actually handles everything that is needed for TER's SOAP API.
* * and reacts on /ter* requests. Until 2020 this was done in a pi-based plugin.
* @author Robert Lemke <robert@typo3.org>
*/ */
class tx_ter_pi1 extends AbstractPlugin class SoapEndpoint implements MiddlewareInterface
{ {
public function main($content, $conf) protected string $endpoint = '/ter';
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{ {
// Always deliver the proper WSDL file if ?wsdl is given $currentSite = $request->getAttribute('site');
if (isset($_GET['wsdl'])) { if ($this->isSoapTerUrl($currentSite, $request->getUri())) {
require_once(\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::extPath('ter') . 'tx_ter_wsdl.php'); // Always deliver the proper WSDL file if ?wsdl is given
exit; if (isset($request->getQueryParams()['wsdl'])) {
$wsdlContents = GeneralUtility::makeInstance(Wsdl::class)->getWsdlContents($request);
$response = new Response('php://temp', 200, [
'Content-type' => 'text/xml',
'Content-length' => strlen($wsdlContents)
]);
$response->getBody()->write($wsdlContents);
return $response;
}
return $this->handleSoapRequest($request);
} }
return $handler->handle($request);
}
/**
* Checks if the site is "TER" and the path is "/ter" (exact match).
*
* @param Site|null $site
* @param UriInterface $currentUri
* @return bool
*/
protected function isSoapTerUrl(?Site $site, UriInterface $currentUri): bool
{
if (!$site instanceof Site) {
return false;
}
if ($site->getIdentifier() !== 'extensions') {
return false;
}
if (strtolower($currentUri->getPath()) !== $this->endpoint) {
return false;
}
return true;
}
/**
* Returns a response object directly (handled by ext-soap) on failure or exits directly,
* as SoapServer works this way unfortunately.
*
* @param ServerRequestInterface $request
* @return ResponseInterface
*/
protected function handleSoapRequest(ServerRequestInterface $request): ResponseInterface
{
try { try {
$server = new \SoapServer(null, ['uri' => \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\T3o\Ter\Api\Configuration::class)->getWsdlNamespace(), 'trace' => true, 'exceptions' => true]); $server = new \SoapServer(
$server->setClass(\T3o\Ter\Soap\TerSoapV1Handler::class); null,
[
'uri' => GeneralUtility::makeInstance(Configuration::class)->getWsdlNamespace(),
'trace' => true,
'exceptions' => true
]
);
$server->setClass(TerSoapV1Handler::class);
$server->handle(file_get_contents('php://input')); $server->handle(file_get_contents('php://input'));
// Due to different behaviour on stage and prod server we need this exit to prevent wrong XML response // Due to different behaviour on stage and prod server we need this exit to prevent wrong XML response
// @todo: make a PSR-7 response out of it
exit(); exit();
} catch (\T3o\Ter\Exception\Exception $e) { } catch (\T3o\Ter\Exception\Exception $e) {
/** /**
...@@ -65,17 +111,16 @@ class tx_ter_pi1 extends AbstractPlugin ...@@ -65,17 +111,16 @@ class tx_ter_pi1 extends AbstractPlugin
* That's why there is an own set of exceptions defined that basically stand for a status code. * That's why there is an own set of exceptions defined that basically stand for a status code.
*/ */
$statusCode = 404; $statusCode = 404;
if ($e instanceof \T3o\Ter\Exception\UnauthorizedException) { if ($e instanceof UnauthorizedException) {
$statusCode = 401; $statusCode = 401;
} elseif ($e instanceof \T3o\Ter\Exception\NotFoundException) { } elseif ($e instanceof NotFoundException) {
$statusCode = 404; $statusCode = 404;
} elseif ($e instanceof \T3o\Ter\Exception\FailedDependencyException) { } elseif ($e instanceof FailedDependencyException) {
$statusCode = 424; $statusCode = 424;
} elseif ($e instanceof \T3o\Ter\Exception\InternalServerErrorException) { } elseif ($e instanceof InternalServerErrorException) {
$statusCode = 500; $statusCode = 500;
error_log(sprintf('TER Server internal error occurred. Error message is: "%s"', $e->getMessage())); error_log(sprintf('TER Server internal error occurred. Error message is: "%s"', $e->getMessage()));
} }
header(' ', true, $statusCode);
/** /**
* Using $server->fault will cause a http 500 status code to be sent which in turn * Using $server->fault will cause a http 500 status code to be sent which in turn
* will trigger the web server's error page to be shown, instead of the SOAP XML * will trigger the web server's error page to be shown, instead of the SOAP XML
...@@ -98,10 +143,11 @@ class tx_ter_pi1 extends AbstractPlugin ...@@ -98,10 +143,11 @@ class tx_ter_pi1 extends AbstractPlugin
</SOAP-ENV:Fault> </SOAP-ENV:Fault>
</SOAP-ENV:Body> </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'; </SOAP-ENV:Envelope>';
echo sprintf($faultStringXmlTemplate, $e->getCode(), $e->getMessage()); return new Response(
exit; sprintf($faultStringXmlTemplate, $e->getCode(), $e->getMessage()),
$statusCode,
['Content-Type' => 'text/xml; charset=utf-8']
);
} }
return '';
} }
} }
<?php
declare(strict_types = 1);
/*
* This file is part of TYPO3 CMS-extension "ter", created by Benni Mack.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*/
namespace T3o\Ter\Soap;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
/**
* WSDL wrapper for the TYPO3 Extension Repository. Returns the contents of the WSDL definition.
*/
class Wsdl
{
public function getWsdlContents(ServerRequestInterface $request): string
{
$serviceLocation = 'https://' . $request->getServerParams()['HTTP_HOST'] . '/ter';
if (Environment::getContext()->isProduction()) {
$wsdlFileName = 'tx_ter.wsdl';
} else {
$wsdlFileName = 'tx_ter.stage.wsdl';
}
$WSDLSource = file_get_contents(ExtensionManagementUtility::extPath('ter') . $wsdlFileName);
$WSDLSource = trim(str_replace('---SERVICE_LOCATION---', $serviceLocation, $WSDLSource));
return $WSDLSource;
}
}
<?php
return [
'frontend' => [
't3o/ter/soap' => [
'target' => \T3o\Ter\Middleware\SoapEndpoint::class,
'after' => [
'typo3/cms-frontend/prepare-tsfe-rendering',
'typo3/cms-frontend/shortcut-and-mountpoint-redirect'
],
]
]
];
<?php <?php
// Remove the old "CODE", "Layout" and the "recursive" fields
$GLOBALS['TCA']['tt_content']['types']['list']['subtypes_excludelist']['ter_pi1'] = 'layout,select_key,pages,recursive';
// Add plugin and datasets
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPlugin(['TER SOAP Server', 'ter_pi1'], 'list_type', 'ter');
// Add static configuration files // Add static configuration files
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile('ter', 'resources/static/', 'TER Server'); \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile('ter', 'resources/static/', 'TER Server');
...@@ -20,15 +20,13 @@ ...@@ -20,15 +20,13 @@
"license": ["GPL-2.0-or-later"], "license": ["GPL-2.0-or-later"],
"version": "2.1.0", "version": "2.1.0",
"require": { "require": {
"ext-soap": "*",
"typo3/cms-core": "^9.5 || ^10.4" "typo3/cms-core": "^9.5 || ^10.4"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {
"T3o\\Ter\\": "Classes/" "T3o\\Ter\\": "Classes/"
}, }
"classmap": [
"pi1"
]
}, },
"extra": { "extra": {
"typo3/cms": { "typo3/cms": {
......
...@@ -3,8 +3,6 @@ if (!defined('TYPO3_MODE')) { ...@@ -3,8 +3,6 @@ if (!defined('TYPO3_MODE')) {
die('Access denied.'); die('Access denied.');
} }
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPItoST43('ter', '', '_pi1', 'list_type', false);
// Register core version update task // Register core version update task
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\T3o\Ter\Task\UpdateCurrentVersionListTask::class] = [ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\T3o\Ter\Task\UpdateCurrentVersionListTask::class] = [
'extension' => 'ter', 'extension' => 'ter',
......
...@@ -12,30 +12,10 @@ ...@@ -12,30 +12,10 @@
* The TYPO3 project - inspiring people to share! * The TYPO3 project - inspiring people to share!
*/ */
/** /**
* WSDL wrapper for the TYPO3 Extension Repository * Previously directly accessible, but now it's a redirect to /ter?wsdl.
*
* Note: We expect that you call this script from a directory "wsdl" in the
* site's main public directory.
*
* $Id$
*
* @author Robert Lemke <robert@typo3.org>
*/ */
error_reporting(E_ALL ^ E_NOTICE); error_reporting(E_ALL ^ E_NOTICE);
$serviceLocation = 'https://' . $_SERVER['HTTP_HOST'] . '/ter'; header('HTTP/1.1 301 Moved Permanently');
header('Location: /ter?wsdl');
if (getenv('TYPO3_CONTEXT') === 'Production') { exit;
$wsdlFileName = 'tx_ter.wsdl';
} else {
$wsdlFileName = 'tx_ter.stage.wsdl';
}
$WSDLSource = file_get_contents(__DIR__ . '/' . $wsdlFileName);
$WSDLSource = trim(str_replace('---SERVICE_LOCATION---', $serviceLocation, $WSDLSource));
if (!headers_sent()) {
header('Content-type: text/xml');
header('Content-Length: ' . strlen($WSDLSource));
}
echo $WSDLSource;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment