Commit 2f045fbf authored by Susanne Moog's avatar Susanne Moog Committed by Stefan Neufeind
Browse files

[FEATURE] Use new REST API for update checks

A new REST api for get.typo3.org was built and should be used
for update checks and additional information gathering.

With this patch the core upgrade and version service is refactored
to use the new API and an additional information in the system
informatian tool bar is displayed.

With the new API it would be possible to add change logs or
links to news for new versions easily in the future.

Additional info:
https://get.typo3.org/v1/api/doc

Resolves: #84549
Releases: master
Change-Id: I9d9c923605a853e9e676367daaa7074bff68aedb
Reviewed-on: https://review.typo3.org/56468


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Alexander Opitz's avatarAlexander Opitz <opitz.alexander@googlemail.com>
Tested-by: Alexander Opitz's avatarAlexander Opitz <opitz.alexander@googlemail.com>
Reviewed-by: default avatarStefan Neufeind <typo3.neufeind@speedpartner.de>
Tested-by: default avatarStefan Neufeind <typo3.neufeind@speedpartner.de>
parent bfa5caac
.. include:: ../../Includes.txt
=============================================================
Deprecation: #84549 - Deprecate methods in CoreVersionService
=============================================================
See :issue:`84549`
Description
===========
The core version service was refactored to make use of the new REST API available via `https://get.typo3.org/v1/api/doc <https://get.typo3.org/v1/api/doc>`_.
Due to that refactoring multiple methods in class :php:`CoreVersionService` have been deprecated:
* :php:`getDownloadBaseUrl()`
* :php:`isYoungerPatchDevelopmentReleaseAvailable()`
* :php:`getYoungestPatchDevelopmentRelease()`
* :php:`updateVersionMatrix()`
Impact
======
Usage of any of these methods will trigger a PHP :php:`E_USER_DEPRECATED` error.
Affected Installations
======================
Any that use the mentioned methods.
Migration
=========
* For :php:`getDownloadBaseUrl()` use `https://get.typo3.org` directly
* For :php:`isYoungerPatchDevelopmentReleaseAvailable()` use :php:`isYoungerPatchReleaseAvailable()`
as the current releases do not make use of development suffixes (like alpha or rc) anymore
* For :php:`getYoungestPatchDevelopmentRelease()` use :php:`getYoungestPatchRelease()`
* :php:`updateVersionMatrix()` needs no replacement method - instead the necessary information can be
fetched directly via the REST API
.. index:: Backend, PHP-API, PartiallyScanned, ext:install
.. include:: ../../Includes.txt
========================================================
Feature: #84549 - Usage of new REST API on get.typo3.org
========================================================
See :issue:`84549`
Description
===========
Instead of providing only a JSON file, the get.typo3.org website was refactored to
provide a REST web API for information on TYPO3 releases.
The core uses that information to check for available upgrades and download new versions.
With this change the information will be fetched via the new API. Additionally information
about new releases will also be displayed in the system information toolbar, both in Composer
Mode and Classic Mode mode to notify users that TYPO3 might be updated. If the version is
out-of-support or has known security issues, the notification is displayed as an error.
Impact
======
* :php:`CoreVersionService` makes use of the REST API directly - no complete version listing
is stored in the registry anymore as the new API provides direct access to necessary information
* The system information toolbar contains a message hinting at the availability of updates - the
message is purposely also displayed for editors as they are exposed to the system more often
and will be able to quickly notify administrators in case security relevant updates are released.
.. index:: Backend, PHP-API, ext:install
......@@ -22,7 +22,6 @@ use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\FormProtection\FormProtectionFactory;
......@@ -337,20 +336,6 @@ class UpgradeController extends AbstractController
]);
}
/**
* Update available core version list
*
* @return ResponseInterface
*/
public function coreUpdateUpdateVersionMatrixAction(): ResponseInterface
{
$this->coreUpdateInitialize();
return new JsonResponse([
'success' => $this->coreUpdateService->updateVersionMatrix(),
'status' => $this->coreUpdateService->getMessages(),
]);
}
/**
* Verify downloaded core checksum
*
......
......@@ -184,18 +184,19 @@ class InstallStatusReport implements \TYPO3\CMS\Reports\StatusProviderInterface
return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_isDevelopmentVersion'), Status::NOTICE);
}
// If fetching version matrix fails we can not do anything except print out the current version
try {
$coreVersionService->updateVersionMatrix();
} catch (Exception\RemoteFetchException $remoteFetchException) {
return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remoteFetchException'), Status::NOTICE);
}
try {
$isUpdateAvailable = $coreVersionService->isYoungerPatchReleaseAvailable();
$isMaintainedVersion = $coreVersionService->isVersionActivelyMaintained();
} catch (Exception\CoreVersionServiceException $coreVersionServiceException) {
return GeneralUtility::makeInstance(Status::class, 'TYPO3', TYPO3_version, $languageService->sL('LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_patchLevelNotFoundInReleaseMatrix'), Status::WARNING);
} catch (Exception\RemoteFetchException $remoteFetchException) {
return GeneralUtility::makeInstance(
Status::class,
'TYPO3',
TYPO3_version,
$languageService->sL(
'LLL:EXT:install/Resources/Private/Language/Report/locallang.xlf:status_remoteFetchException'
),
Status::NOTICE
);
}
if (!$isUpdateAvailable && $isMaintainedVersion) {
......
......@@ -75,7 +75,7 @@ class CoreUpdateService
$this->coreVersionService = $coreVersionService ?: GeneralUtility::makeInstance(CoreVersionService::class);
$this->setDownloadTargetPath(Environment::getVarPath() . '/transient/');
$this->symlinkToCoreFiles = $this->discoverCurrentCoreSymlink();
$this->downloadBaseUri = $this->coreVersionService->getDownloadBaseUri();
$this->downloadBaseUri = 'https://get.typo3.org';
$this->messages = new FlashMessageQueue('install');
}
......@@ -127,19 +127,24 @@ class CoreUpdateService
/**
* Wrapper method for CoreVersionService
*
* @deprecated Since TYPO3v9 and will be removed in v10 - use REST api directly (see https://get.typo3.org/v1/api/doc)
* @return bool TRUE on success
*/
public function updateVersionMatrix()
{
trigger_error(
'The method updateVersionMatrix() is deprecated since v9 and will be removed in v10, use the REST api directly (see https://get.typo3.org/v1/api/doc).',
\E_USER_DEPRECATED
);
$success = true;
try {
$this->coreVersionService->updateVersionMatrix();
$this->coreVersionService->getYoungestPatchRelease();
} catch (RemoteFetchException $e) {
$success = false;
$this->messages->enqueue(new FlashMessage(
'Current version specification could not be fetched from http://get.typo3.org/json.'
'Current version specification could not be fetched from https://get.typo3.org.'
. ' This is probably a network issue, please fix it.',
'Version matrix could not be fetched from get.typo3.org',
'Version information could not be fetched from get.typo3.org',
FlashMessage::ERROR
));
}
......
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Install\Service;
/*
......@@ -29,48 +31,28 @@ class CoreVersionService
protected $registry;
/**
* Base URI for TYPO3 downloads
* Base URI for TYPO3 Version REST api
*
* @var string
*/
protected $downloadBaseUri;
protected $apiBaseUrl = 'https://get.typo3.org/v1/api/';
/**
* Initialize update URI
*
* @param Registry $registry
* @param Registry $registry Deprecated
*/
public function __construct(Registry $registry = null)
{
$this->downloadBaseUri = 'https://get.typo3.org/';
$this->registry = $registry ?: GeneralUtility::makeInstance(Registry::class);
}
/**
* @return mixed
*/
public function getDownloadBaseUri()
{
return $this->downloadBaseUri;
}
/**
* Update version matrix from remote and store in registry
*
* @throws Exception\RemoteFetchException
*/
public function updateVersionMatrix()
{
$versionArray = $this->fetchVersionMatrixFromRemote();
$installedMajorVersion = (int)$this->getInstalledMajorVersion();
foreach ($versionArray as $versionNumber => $versionDetails) {
if (is_array($versionDetails) && (int)$this->getMajorVersion($versionNumber) < $installedMajorVersion) {
unset($versionArray[$versionNumber]);
}
if (null !== $registry) {
trigger_error(
'The constructor parameter $registry for CoreVersionService is deprecated since v9 and will be removed in v10.',
E_USER_DEPRECATED
);
$this->registry = $registry;
} else {
$this->registry = GeneralUtility::makeInstance(Registry::class);
}
$this->registry->set('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
}
/**
......@@ -79,7 +61,7 @@ class CoreVersionService
*
* @return bool FALSE If some development version is installed
*/
public function isInstalledVersionAReleasedVersion()
public function isInstalledVersionAReleasedVersion(): bool
{
$version = $this->getInstalledVersion();
return substr($version, -4) !== '-dev';
......@@ -92,22 +74,12 @@ class CoreVersionService
* @return string sha1 of version
* @throws Exception\CoreVersionServiceException
*/
public function getTarGzSha1OfVersion($version)
public function getTarGzSha1OfVersion(string $version): string
{
$this->ensureVersionExistsInMatrix($version);
$majorVersion = $this->getMajorVersion($version);
$versionMatrix = $this->getVersionMatrix();
$url = 'release/' . $version;
$result = $this->fetchFromRemote($url);
if (empty($versionMatrix[$majorVersion]['releases'][$version]['checksums']['tar']['sha1'])) {
throw new Exception\CoreVersionServiceException(
'Release sha1 of version ' . $version . ' not found in version matrix.'
. ' This is probably a bug on get.typo3.org.',
1381263173
);
}
return $versionMatrix[$majorVersion]['releases'][$version]['checksums']['tar']['sha1'];
return $result['tar_package']['sha1sum'] ?? '';
}
/**
......@@ -115,7 +87,7 @@ class CoreVersionService
*
* @return string
*/
public function getInstalledVersion()
public function getInstalledVersion(): string
{
return VersionNumberUtility::getCurrentTypo3Version();
}
......@@ -124,116 +96,75 @@ class CoreVersionService
* Checks if TYPO3 version (e.g. 6.2) is an actively maintained version
*
* @return bool TRUE if version is actively maintained
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
public function isVersionActivelyMaintained()
public function isVersionActivelyMaintained(): bool
{
$majorVersion = $this->getInstalledMajorVersion();
$versionMatrix = $this->getVersionMatrix();
return (bool)$versionMatrix[$majorVersion]['active'];
$url = 'major/' . $this->getInstalledMajorVersion();
$result = $this->fetchFromRemote($url);
return !isset($result['maintained_until']) ||
(
new \DateTimeImmutable($result['maintained_until']) >=
new \DateTimeImmutable('now', new \DateTimeZone('UTC'))
);
}
/**
* Returns TRUE if a younger patch level release exists in version matrix.
*
* @return bool TRUE if younger patch release is exists
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
public function isYoungerPatchReleaseAvailable()
public function isYoungerPatchReleaseAvailable(): bool
{
$version = $this->getInstalledVersion();
$youngestVersion = $this->getYoungestPatchRelease();
return $youngestVersion !== $version;
}
/**
* Returns TRUE if a younger patch level release exists in version matrix that may be a development release.
*
* @return bool TRUE if younger patch release is exists
*/
public function isYoungerPatchDevelopmentReleaseAvailable()
{
$result = false;
$version = $this->getInstalledVersion();
$youngestVersion = $this->getYoungestPatchDevelopmentRelease();
if ($youngestVersion !== $version) {
$result = true;
}
return $result;
return version_compare($this->getInstalledVersion(), $this->getYoungestPatchRelease()) === -1;
}
/**
* Returns TRUE if an upgrade from current version is security relevant
*
* @return bool TRUE if there is a pending security update
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
public function isUpdateSecurityRelevant()
public function isUpdateSecurityRelevant(): bool
{
$result = false;
$version = $this->getInstalledVersion();
$youngestVersion = $this->getYoungestReleaseByType(['security']);
if ($youngestVersion !== $version) {
$result = true;
$url = 'major/' . $this->getInstalledMajorVersion() . '/release/latest/security';
$result = $this->fetchFromRemote($url);
if (isset($result['version'])) {
return version_compare($this->getInstalledVersion(), $result['version']) === -1;
}
return $result;
return false;
}
/**
* Youngest patch release, e.g., 6.2.2
*
* @return string Version string of youngest patch level release
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
public function getYoungestPatchRelease()
{
return $this->getYoungestReleaseByType(['release', 'security', 'regular']);
}
/**
* Youngest development patch release, e.g., 6.2.0alpha3 or 6.2-snapshot-20131004
*
* @return string
*/
public function getYoungestPatchDevelopmentRelease()
public function getYoungestPatchRelease(): string
{
return $this->getYoungestReleaseByType(['release', 'security', 'regular', 'development']);
$url = 'major/' . $this->getInstalledMajorVersion() . '/release/latest';
$result = $this->fetchFromRemote($url);
return $result['version'];
}
/**
* Get youngest release version string.
* Returns same version number if no younger release was found.
*
* @param array $types List of allowed types: development, release, security, regular
* @throws Exception\CoreVersionServiceException
* @return string Youngest release, e.g., 7.2.0alpha3 or 7.3.0
* @param string $url
* @return array
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
protected function getYoungestReleaseByType(array $types)
protected function fetchFromRemote(string $url): array
{
$version = $this->getInstalledVersion();
$majorVersion = $this->getMajorVersion($version);
$versionMatrix = $this->getVersionMatrix();
$url = $this->apiBaseUrl . $url;
$json = GeneralUtility::getUrl($url);
$youngestRelease = $version;
$versionReleaseTimestamp = $this->getReleaseTimestampOfVersion($version);
$patchLevelVersions = $versionMatrix[$majorVersion]['releases'];
foreach ($patchLevelVersions as $aVersionNumber => $aVersionDetails) {
if (!array_key_exists('type', $aVersionDetails)) {
throw new Exception\CoreVersionServiceException(
'Release type of version ' . $aVersionNumber . ' not found in version matrix.'
. ' This is probably a bug on get.typo3.org.',
1380909029
);
}
$type = $aVersionDetails['type'];
$aVersionNumberReleaseTimestamp = $this->getReleaseTimestampOfVersion($aVersionNumber);
if (
$aVersionNumberReleaseTimestamp > $versionReleaseTimestamp
&& in_array($type, $types)
) {
$youngestRelease = $aVersionNumber;
$versionReleaseTimestamp = $aVersionNumberReleaseTimestamp;
}
if (!$json) {
$this->throwFetchException($url);
}
return $youngestRelease;
return json_decode($json, true);
}
/**
......@@ -241,7 +172,7 @@ class CoreVersionService
*
* @return string For example 7
*/
protected function getInstalledMajorVersion()
protected function getInstalledMajorVersion(): string
{
return $this->getMajorVersion($this->getInstalledVersion());
}
......@@ -252,93 +183,96 @@ class CoreVersionService
* @param string $version to check
* @return string Major version, e.g., '7'
*/
protected function getMajorVersion($version)
protected function getMajorVersion(string $version): string
{
$explodedVersion = explode('.', $version);
return $explodedVersion[0];
}
/**
* Get version matrix from registry
* Update version matrix from remote and store in registry
*
* @return array
* @throws Exception
* @deprecated Since TYPO3v9 and will be removed in v10 - use new REST API directly (see https://get.typo3.org/v1/api/doc)
* @throws Exception\RemoteFetchException
*/
protected function getVersionMatrix()
public function updateVersionMatrix(): void
{
$versionMatrix = $this->registry->get('TYPO3.CMS.Install', 'coreVersionMatrix');
if (empty($versionMatrix) || !is_array($versionMatrix)) {
throw new Exception\CoreVersionServiceException(
'No version matrix found in registry, call updateVersionMatrix() first.',
1380898792
);
trigger_error('Method updateVersionMatrix is deprecated, use new REST API directly (see https://get.typo3.org/v1/api/doc).', \E_USER_DEPRECATED);
$url = 'https://get.typo3.org/json';
$versionJson = GeneralUtility::getUrl($url);
if (!$versionJson) {
$this->throwFetchException($url);
}
$versionArray = json_decode($versionJson, true);
$installedMajorVersion = (int)$this->getInstalledMajorVersion();
foreach ($versionArray as $versionNumber => $versionDetails) {
if (is_array($versionDetails) && (int)($this->getMajorVersion((string)$versionNumber)) < $installedMajorVersion) {
unset($versionArray[$versionNumber]);
}
}
return $versionMatrix;
$this->registry->set('TYPO3.CMS.Install', 'coreVersionMatrix', $versionArray);
}
/**
* Get available version string from get.typo3.org
* Youngest development patch release, e.g., 6.2.0alpha3 or 6.2-snapshot-20131004
*
* @return array
* @throws Exception\RemoteFetchException
* @deprecated Since TYPO3v9 and will be removed in v10 - TYPO3 release cycles do not contain development releases anymore
* @return string
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
protected function fetchVersionMatrixFromRemote()
public function getYoungestPatchDevelopmentRelease(): string
{
$url = $this->downloadBaseUri . 'json';
$versionJson = GeneralUtility::getUrl($url);
if (!$versionJson) {
throw new Exception\RemoteFetchException(
'Fetching ' . $url . ' failed. Maybe this instance can not connect to the remote system properly.',
1380897593
);
}
return json_decode($versionJson, true);
trigger_error(
'Method getYoungestPatchDevelopmentRelease() is deprecated since v9 and will be removed in v10, use getYoungestPatchRelease() instead.',
\E_USER_DEPRECATED
);
return $this->getYoungestPatchRelease();
}
/**
* Returns release timestamp of a specific version
* Returns TRUE if a younger patch level release exists in version matrix that may be a development release.
*
* @param $version String to check in version matrix, e.g., 7.2.0alpha3 or 7.3.0
* @throws Exception\CoreVersionServiceException
* @return int Timestamp of release
* @deprecated Since TYPO3v9 and will be removed in v10 - TYPO3 release cycles do not contain development releases anymore
* @return bool TRUE if younger patch release is exists
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
protected function getReleaseTimestampOfVersion($version)
public function isYoungerPatchDevelopmentReleaseAvailable(): bool
{
$majorVersion = $this->getMajorVersion($version);
$versionMatrix = $this->getVersionMatrix();
$this->ensureVersionExistsInMatrix($version);
if (!array_key_exists('date', $versionMatrix[$majorVersion]['releases'][$version])) {
throw new Exception\CoreVersionServiceException(
'Release date of version ' . $version . ' not found in version matrix. This is probably a bug on get.typo3.org',
1380905853
);
}
$dateString = $versionMatrix[$majorVersion]['releases'][$version]['date'];
$date = new \DateTime($dateString);
return $date->getTimestamp();
trigger_error(
'Method isYoungerPatchDevelopmentReleaseAvailable() is deprecated since v9 and will be removed in v10, use isYoungerPatchReleaseAvailable() instead.',
\E_USER_DEPRECATED
);
return $this->isYoungerPatchReleaseAvailable();
}
/**
* Throws an exception if specified version does not exist in version matrix
* @deprecated Since TYPO3v9 and will be removed in v10, use 'https://get.typo3.org' directly
* @return string
*/
public function getDownloadBaseUrl(): string
{
trigger_error(
'Method getDownloadBaseUrl() is deprecated since v9 and will be removed in v10, use https://get.typo3.org directly.',
\E_USER_DEPRECATED
);
return $this->apiBaseUrl;
}
/**
* Helper method to throw same exception in multiple places
*
* @param $version String to check in version matrix, e.g., 7.2.0alpha3 or 7.3.0
* @throws Exception\CoreVersionServiceException
* @param string $url
* @throws \TYPO3\CMS\Install\Service\Exception\RemoteFetchException
*/
protected function ensureVersionExistsInMatrix($version)
protected function throwFetchException(string $url): void
{
$majorVersion = $this->getMajorVersion($version);
$versionMatrix = $this->getVersionMatrix();
if (!array_key_exists($majorVersion, $versionMatrix)) {
throw new Exception\CoreVersionServiceException(
'Major release ' . $majorVersion . ' not found in version matrix.', <