[ticket/15851] Automatic update downloader

PHPBB3-15851
This commit is contained in:
Máté Bartus 2023-09-22 09:35:54 +01:00 committed by Marc Alexander
parent 84ec3dc6c3
commit d2c402f038
No known key found for this signature in database
GPG key ID: 50E0D2423696F995
9 changed files with 352 additions and 1 deletions

View file

@ -29,6 +29,7 @@
"php": "^8.1", "php": "^8.1",
"ext-pdo": "*", "ext-pdo": "*",
"ext-zlib": "*", "ext-zlib": "*",
"ext-sodium": "*",
"bantu/ini-get-wrapper": "~1.0", "bantu/ini-get-wrapper": "~1.0",
"carlos-mg89/oauth": "^0.8.15", "carlos-mg89/oauth": "^0.8.15",
"chita/topological_sort": "^3.0", "chita/topological_sort": "^3.0",

View file

@ -20,3 +20,5 @@ parameters:
- passwords.driver.bcrypt - passwords.driver.bcrypt
- passwords.driver.salted_md5 - passwords.driver.salted_md5
- passwords.driver.phpass - passwords.driver.phpass
public_key: 'auJX0pGetfYatE7t/rX5hAkCLZv9s78TwKkLfR3YGuQ='

View file

@ -37,6 +37,7 @@ imports:
- { resource: services_twig.yml } - { resource: services_twig.yml }
- { resource: services_twig_extensions.yml } - { resource: services_twig_extensions.yml }
- { resource: services_ucp.yml } - { resource: services_ucp.yml }
- { resource: services_updater.yml }
- { resource: services_user.yml } - { resource: services_user.yml }
- { resource: tables.yml } - { resource: tables.yml }

View file

@ -0,0 +1,14 @@
services:
updater.get_updates:
class: phpbb\update\get_updates
arguments:
- '@filesystem'
- '%public_key%'
- '%core.root_path%'
updater.controller:
class: phpbb\update\controller
arguments:
- '@filesystem'
- '@updater.get_updates'
- '%core.root_path%'

View file

@ -38,12 +38,32 @@ class acp_update
try try
{ {
$recheck = $request->variable('versioncheck_force', false); $recheck = $request->variable('versioncheck_force', false);
$do_update = $request->variable('do_update', false);
$updates_available = $version_helper->get_update_on_branch($recheck); $updates_available = $version_helper->get_update_on_branch($recheck);
$upgrades_available = $version_helper->get_suggested_updates(); $upgrades_available = $version_helper->get_suggested_updates();
$branch = '';
if (!empty($upgrades_available)) if (!empty($upgrades_available))
{ {
$branch = array_key_last($upgrades_available);
$upgrades_available = array_pop($upgrades_available); $upgrades_available = array_pop($upgrades_available);
} }
if ($do_update && !empty($updates_available))
{
$updater = $phpbb_container->get('updater.controller');
$current_version = $config['version'];
$new_version = $upgrades_available['current'];
$download_url = 'https://download.phpbb.com/pub/release/';
$download_url .= $branch . '/' . $new_version . '/';
$download_url .= 'phpBB-' . $current_version . '_to_' . $new_version . '.zip';
$data = $updater->handle(
$download_url
);
$response = new \phpbb\json_response();
$response->send($data);
}
} }
catch (\RuntimeException $e) catch (\RuntimeException $e)
{ {

View file

@ -221,6 +221,13 @@ $lang = array_merge($lang, array(
<p>We noticed that the last update of your phpBB installation hasnt been completed. Visit the <a href="%1$s" title="%1$s">database updater</a>, ensure <em>Update database only</em> is selected and click on <strong>Submit</strong>. Don\'t forget to delete the "install"-directory after you have updated the database successfully.</p>', <p>We noticed that the last update of your phpBB installation hasnt been completed. Visit the <a href="%1$s" title="%1$s">database updater</a>, ensure <em>Update database only</em> is selected and click on <strong>Submit</strong>. Don\'t forget to delete the "install"-directory after you have updated the database successfully.</p>',
// Auto update
'COULD_NOT_DOWNLOAD_UPDATE_PACKAGE' => 'Failed to download the update package.',
'COULD_NOT_DOWNLOAD_UPDATE_SIGNATURE' => 'Failed to download the update package signature.',
'UPDATE_SIGNATURE_INVALID' => 'The update package is corrupted.',
'COULD_NOT_EXTRACT_UPDATE' => 'Could not extract files from the update package.',
'COULD_NOT_WRITE_UPDATE_FILES' => 'Could not copy files from the update package.',
// //
// Server data // Server data
// //

View file

@ -96,7 +96,7 @@ class check_server_environment extends \phpbb\install\task_base
*/ */
protected function check_php_version() protected function check_php_version()
{ {
if (version_compare(PHP_VERSION, '7.2.0', '<')) if (version_compare(PHP_VERSION, '8.1.0', '<'))
{ {
$this->response_helper->add_error_message('PHP_VERSION_REQD', 'PHP_VERSION_REQD_EXPLAIN'); $this->response_helper->add_error_message('PHP_VERSION_REQD', 'PHP_VERSION_REQD_EXPLAIN');

View file

@ -0,0 +1,137 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\update;
use phpbb\filesystem\filesystem_interface;
use phpbb\language\language;
class controller
{
/** @var filesystem_interface Filesystem manager */
private filesystem_interface $filesystem;
/** @var get_updates Updater class */
private get_updates $updater;
/** @var language Translation handler */
private language $language;
/** @var string phpBB root path */
private string $phpbb_root_path;
/**
* Constructor.
*
* @param filesystem_interface $filesystem
* @param get_updates $updater
* @param language $language
* @param string $phpbb_root_path
*/
public function __construct(
filesystem_interface $filesystem,
get_updates $updater,
language $language,
string $phpbb_root_path)
{
$this->filesystem = $filesystem;
$this->language = $language;
$this->updater = $updater;
$this->phpbb_root_path = $phpbb_root_path;
}
/**
* Handle requests.
*
* @param string $download The download URL.
*
* @return string[] Unencoded json response.
*/
public function handle(string $download): array
{
$update_path = $this->phpbb_root_path . 'store/update.zip';
$status = ['status' => 'continue'];
if (!file_exists($update_path))
{
$result = $this->updater->download($download, $update_path);
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_DOWNLOAD_UPDATE_PACKAGE')
];
}
return $status;
}
if (!file_exists($update_path . '.sig'))
{
$result = $this->updater->download($download . '.sig', $update_path . '.sig');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_DOWNLOAD_UPDATE_SIGNATURE')
];
}
return $status;
}
if (!is_dir($this->phpbb_root_path . 'store/update'))
{
$result = $this->updater->validate($update_path, $update_path . '.sig');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('UPDATE_SIGNATURE_INVALID')
];
}
$result = $this->updater->extract($update_path, $this->phpbb_root_path . 'store/update');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_EXTRACT_UPDATE')
];
}
return $status;
}
if (!is_dir($this->phpbb_root_path . 'install'))
{
$result = $this->updater->copy($this->phpbb_root_path . 'store/update');
if (!$result)
{
return [
'status' => 'error',
'error' => $this->language->lang('COULD_NOT_WRITE_UPDATE_FILES')
];
}
return $status;
}
$this->filesystem->remove([
$this->phpbb_root_path . 'store/update',
$update_path,
$update_path . '.sig'
]);
$status['status'] = 'done';
return $status;
}
}

View file

@ -0,0 +1,169 @@
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\update;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use phpbb\filesystem\exception\filesystem_exception;
use phpbb\filesystem\filesystem_interface;
use SodiumException;
use ZipArchive;
class get_updates
{
/** @var filesystem_interface Filesystem managerr */
private filesystem_interface $filesystem;
/** @var Client HTTP client */
private Client $http_client;
/** @var string Public key to verify package */
private string $public_key;
/** @var string phpBB root path */
private string $phpbb_root_path;
/** @var ZipArchive Zip extractor */
private ZipArchive $zipper;
/**
* Constructor
*
* @param filesystem_interface $filesystem
* @param string $public_key
* @param string $phpbb_root_path
*/
public function __construct(
filesystem_interface $filesystem,
string $public_key,
string $phpbb_root_path)
{
$this->filesystem = $filesystem;
$this->http_client = new Client();
$this->public_key = base64_decode($public_key);
$this->phpbb_root_path = $phpbb_root_path;
$this->zipper = new ZipArchive();
}
/**
* Download the update package.
*
* @param string $url Download link to the update.
* @param string $storage_path Location for the download.
*
* @return bool Whether the download completed successfully.
*/
public function download(string $url, string $storage_path): bool
{
try
{
$this->http_client->request('GET', $url, [
'sink' => $storage_path,
'allow_redirects' => false
]);
}
catch (GuzzleException)
{
return false;
}
return true;
}
/**
* Validate the downloaded file.
*
* @param string $file_path Path to the download.
* @param string $signature_path The signature file.
*
* @return bool Whether the signature is correct or not.
*/
public function validate(string $file_path, string $signature_path): bool
{
if (file_exists($file_path) === false)
{
return false;
}
if (file_exists($signature_path) === false)
{
return false;
}
$raw_signature = file_get_contents($signature_path);
$hash = hash_file('sha384', $file_path, true);
if ($hash === false)
{
return false;
}
$signature = base64_decode($raw_signature);
if ($signature === false)
{
return false;
}
try
{
return sodium_crypto_sign_verify_detached($signature, $hash, $this->public_key);
}
catch (SodiumException)
{
return false;
}
}
/**
* Extract the downloaded archive.
*
* @param string $zip_file Path to the archive.
* @param string $to Path to where to extract the archive to.
*
* @return bool Whether the extraction completed successfully.
*/
public function extract(string $zip_file, string $to): bool
{
if ($this->zipper->open($zip_file) === false)
{
return false;
}
$result = $this->zipper->extractTo($to);
$this->zipper->close();
return $result;
}
/**
* Copy the update package to the root folder.
*
* @param string $src_dir Where to copy from.
*
* @return bool Whether the files were copied successfully.
*/
public function copy(string $src_dir): bool
{
try
{
$this->filesystem->mirror($src_dir, $this->phpbb_root_path);
}
catch (filesystem_exception)
{
return false;
}
return true;
}
}