Skip to content

Commit

Permalink
Merge pull request #13 from joomla-projects/feature/available-versions
Browse files Browse the repository at this point in the history
Added service to fetch available versions from TUF #11
  • Loading branch information
HLeithner authored Nov 17, 2024
2 parents b10410e + 2e37632 commit d8e690e
Show file tree
Hide file tree
Showing 19 changed files with 944 additions and 22 deletions.
4 changes: 4 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,9 @@ AWS_USE_PATH_STYLE_ENDPOINT=false

VITE_APP_NAME="${APP_NAME}"

# Health interval in hours
HEALTH_CHECK_INTERVAL=24
# Orphan site storage period in days
CLEANUP_SITE_DELAY=7
# Cache time in minutes
TUF_REPO_CACHETIME=5
2 changes: 1 addition & 1 deletion app/Http/Controllers/Api/V1/SiteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
namespace App\Http\Controllers\Api\V1;

use App\Http\Controllers\Controller;
use App\Http\Traits\ApiResponse;
use App\Jobs\CheckSiteHealth;
use App\Models\Site;
use App\RemoteSite\Connection;
use App\Traits\ApiResponse;
use Carbon\Carbon;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ServerException;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace App\Traits;
namespace App\Http\Traits;

use Illuminate\Http\JsonResponse;

Expand Down
12 changes: 12 additions & 0 deletions app/Models/TufMetadata.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

declare(strict_types=1);

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class TufMetadata extends Model
{
public $timestamps = false;
}
52 changes: 52 additions & 0 deletions app/Providers/TUFServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace App\Providers;

use App\Models\TufMetadata;
use App\TUF\EloquentModelStorage;
use App\TUF\HttpLoader;
use GuzzleHttp\Client;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
use Tuf\Client\Updater;
use Tuf\Loader\SizeCheckingLoader;
use Tuf\Metadata\StorageInterface;

class TUFServiceProvider extends ServiceProvider
{
public const REPO_PATH = "https://update.joomla.org/cms/";

/**
* Register services.
*
* @return void
*/
public function register()
{
$this->app->singleton(StorageInterface::class, function ($app) {
// Setup loader
$httpLoader = new HttpLoader(
self::REPO_PATH,
App::make(Client::class)
);

$sizeCheckingLoader = new SizeCheckingLoader($httpLoader);

// Setup storage
$storage = new EloquentModelStorage(TufMetadata::findOrFail(1));

// Create updater
$updater = new Updater(
$sizeCheckingLoader,
$storage
);

// Fetch Updates
$updater->refresh();

$storage->persist();

return $storage;
});
}
}
3 changes: 1 addition & 2 deletions app/RemoteSite/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@
namespace App\RemoteSite;

use App\Enum\HttpMethod;
use App\Enum\WebserviceEndpoint;
use App\RemoteSite\Responses\FinalizeUpdate as FinalizeUpdateResponse;
use App\RemoteSite\Responses\HealthCheck as HealthCheckResponse;
use App\RemoteSite\Responses\GetUpdate as GetUpdateResponse;
use App\RemoteSite\Responses\HealthCheck as HealthCheckResponse;
use App\RemoteSite\Responses\PrepareUpdate as PrepareUpdateResponse;
use App\RemoteSite\Responses\ResponseInterface;
use GuzzleHttp\Client;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?php

namespace App\Enum;
namespace App\RemoteSite;

use App\Enum\HttpMethod;
use App\RemoteSite\Responses\FinalizeUpdate;
use App\RemoteSite\Responses\GetUpdate;
use App\RemoteSite\Responses\HealthCheck;
Expand Down
59 changes: 59 additions & 0 deletions app/TUF/EloquentModelStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\TUF;

use App\Models\TufMetadata;
use Tuf\Metadata\StorageBase;

class EloquentModelStorage extends StorageBase
{
public const METADATA_COLUMNS = ['root', 'targets', 'snapshot', 'timestamp', 'mirrors'];

protected TufMetadata $model;

/**
* @var array<string, string>
*/
protected array $container = [];

public function __construct(TufMetadata $model)
{
$this->model = $model;

foreach (self::METADATA_COLUMNS as $column) {
if ($this->model->$column === null) {
continue;
}

$this->write($column, $this->model->$column);
}
}

public function read(string $name): ?string
{
return $this->container[$name] ?? null;
}

public function write(string $name, string $data): void
{
$this->container[$name] = $data;
}

public function delete(string $name): void
{
unset($this->container[$name]);
}

public function persist(): bool
{
foreach (self::METADATA_COLUMNS as $column) {
if (!\array_key_exists($column, $this->container)) {
continue;
}

$this->model->$column = $this->container[$column];
}

return $this->model->save();
}
}
36 changes: 36 additions & 0 deletions app/TUF/HttpLoader.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace App\TUF;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Promise\Create;
use GuzzleHttp\Promise\PromiseInterface;
use Tuf\Exception\RepoFileNotFound;
use Tuf\Loader\LoaderInterface;

class HttpLoader implements LoaderInterface
{
public function __construct(private readonly string $repositoryPath, private readonly Client $http)
{
}

public function load(string $locator, int $maxBytes): PromiseInterface
{
try {
$response = $this->http->get($this->repositoryPath . $locator);
} catch (RequestException $e) {
if ($e->getResponse()?->getStatusCode() !== 200) {
throw new RepoFileNotFound();
}

throw new HttpLoaderException($e->getMessage(), $e->getCode(), $e);
}

// Rewind to start
$response->getBody()->rewind();

// Return response
return Create::promiseFor($response->getBody());
}
}
9 changes: 9 additions & 0 deletions app/TUF/HttpLoaderException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace App\TUF;

use Tuf\Exception\TufException;

class HttpLoaderException extends TufException
{
}
46 changes: 46 additions & 0 deletions app/TUF/TufFetcher.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace App\TUF;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Cache;
use Tuf\Exception\MetadataException;
use Tuf\Metadata\StorageInterface;

class TufFetcher
{
protected StorageInterface $updateStorage;

public function __construct()
{
$this->updateStorage = App::make(StorageInterface::class);
}

public function getReleases(): mixed
{
// Cache response to avoid to make constant calls on the fly
return Cache::remember(
'cms_targets',
(int) config('autoupdates.tuf_repo_cachetime') * 60, // @phpstan-ignore-line
function () {
$targets = $this->updateStorage->getTargets();

// Make sure we have a valid list of targets
if (is_null($targets)) {
throw new MetadataException("Empty targetlist in metadata");
}

// Convert format
return (new Collection($targets->getSigned()['targets']))
->mapWithKeys(function (mixed $target) {
if (!is_array($target) || empty($target['custom']) || !is_array($target['custom'])) {
throw new MetadataException("Empty target custom attribute");
}

return [$target['custom']['version'] => $target['custom']];
});
}
);
}
}
1 change: 1 addition & 0 deletions bootstrap/providers.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
App\Providers\AppServiceProvider::class,
App\Providers\HorizonServiceProvider::class,
App\Providers\HttpclientServiceProvider::class,
App\Providers\TUFServiceProvider::class
];
10 changes: 9 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"laravel/framework": "^11.9",
"laravel/horizon": "^5.29",
"laravel/octane": "^2.5",
"laravel/tinker": "^2.9"
"laravel/tinker": "^2.9",
"php-tuf/php-tuf": "1.0.1"
},
"require-dev": {
"fakerphp/faker": "^1.23",
Expand Down Expand Up @@ -69,6 +70,13 @@
"php-http/discovery": true
}
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/joomla-backports/php-tuf.git",
"no-api": true
}
],
"minimum-stability": "stable",
"prefer-stable": true
}
Loading

0 comments on commit d8e690e

Please sign in to comment.