new git integraton code

This commit is contained in:
Mark Gregory
2026-06-24 16:11:42 +01:00
parent 44244ec59e
commit f05305617d
4 changed files with 141 additions and 16 deletions
@@ -7,6 +7,8 @@ namespace Modules\Helpdesk\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use JsonException;
use Modules\Helpdesk\Models\GitRepository;
use Modules\Helpdesk\Services\GitWebhookIngestionService;
@@ -162,7 +164,12 @@ final class GitWebhookController
{
$giteaSignature = (string) $request->header('X-Gitea-Signature', '');
return hash_equals(hash_hmac('sha256', $rawPayload, $secret), $giteaSignature)
$sha256 = hash_hmac('sha256', $rawPayload, $secret);
$sha1 = hash_hmac('sha1', $rawPayload, $secret);
return hash_equals($sha256, $giteaSignature)
|| hash_equals('sha256=' . $sha256, $giteaSignature)
|| hash_equals('sha1=' . $sha1, $giteaSignature)
|| $this->hasValidGitHubSignature($request, $secret, $rawPayload)
|| $this->hasValidGenericSignature($request, $secret, $rawPayload);
}
@@ -183,7 +190,7 @@ final class GitWebhookController
*/
private function hasValidGenericSignature(Request $request, string $secret, string $rawPayload): bool
{
$token = (string) ($request->header('X-Helpdesk-Token') ?: $request->header('X-Gitlab-Token'));
$token = (string) ($request->header('X-Helpdesk-Token') ?: $request->header('X-Gitlab-Token') ?: $request->header('X-Gitea-Token'));
$signature = (string) ($request->header('X-Hub-Signature-256') ?: $request->header('X-Hub-Signature'));
return hash_equals($secret, $token)
@@ -205,7 +212,8 @@ final class GitWebhookController
$actual = trim((string) $request->header('Authorization', ''));
return hash_equals($expected, $actual)
|| hash_equals('Basic ' . $expected, $actual);
|| hash_equals('Basic ' . $expected, $actual)
|| (Str::startsWith($expected, 'Basic ') && hash_equals($expected, $actual));
}
/**
@@ -217,38 +225,101 @@ final class GitWebhookController
{
$repositories = GitRepository::query()
->where('is_active', true)
->whereIn('provider', ['gitea', 'git'])
->whereRaw('LOWER(provider) IN (?, ?)', ['gitea', 'git'])
->get();
if ($repositories->isEmpty()) {
Log::warning('Helpdesk Gitea webhook received but no active Gitea/git repository is configured.');
return null;
}
$payloadValues = array_filter([
$payloadValues = array_values(array_filter([
Arr::get($payload, 'repository.full_name'),
Arr::get($payload, 'repository.name'),
Arr::get($payload, 'repository.html_url'),
Arr::get($payload, 'repository.clone_url'),
Arr::get($payload, 'repository.ssh_url'),
Arr::get($payload, 'repository.url'),
], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== '');
Arr::get($payload, 'repository.owner.username') && Arr::get($payload, 'repository.name')
? Arr::get($payload, 'repository.owner.username') . '/' . Arr::get($payload, 'repository.name')
: null,
], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== ''));
$normalisedPayloadValues = array_values(array_filter(array_map(
fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value),
$payloadValues,
)));
foreach ($repositories as $repository) {
foreach ($payloadValues as $value) {
$payloadValue = trim((string) $value);
$remoteUrl = trim((string) $repository->remote_url);
$repositoryValues = array_filter([
$repository->remote_url,
$repository->name,
Str::of((string) $repository->remote_url)->afterLast(':')->beforeLast('.git')->value(),
], static fn (mixed $value): bool => is_scalar($value) && trim((string) $value) !== '');
if ($remoteUrl !== '' && ($payloadValue === $remoteUrl || str_contains($remoteUrl, $payloadValue) || str_contains($payloadValue, $remoteUrl))) {
return $repository;
}
$normalisedRepositoryValues = array_values(array_filter(array_map(
fn (mixed $value): ?string => $this->normaliseRepositoryIdentity((string) $value),
$repositoryValues,
)));
if (strcasecmp($payloadValue, (string) $repository->name) === 0) {
return $repository;
foreach ($normalisedPayloadValues as $payloadValue) {
foreach ($normalisedRepositoryValues as $repositoryValue) {
if ($payloadValue === $repositoryValue
|| str_ends_with($repositoryValue, '/' . $payloadValue)
|| str_ends_with($payloadValue, '/' . $repositoryValue)) {
return $repository;
}
}
}
}
return $repositories->count() === 1 ? $repositories->first() : null;
if ($repositories->count() === 1) {
return $repositories->first();
}
Log::warning('Helpdesk Gitea webhook did not match any configured repository.', [
'payload_repository' => Arr::get($payload, 'repository.full_name') ?: Arr::get($payload, 'repository.clone_url') ?: Arr::get($payload, 'repository.html_url'),
'configured_repository_count' => $repositories->count(),
]);
return null;
}
/**
* Normalise a repository name or URL so Gitea payloads match local settings reliably.
*/
private function normaliseRepositoryIdentity(string $value): ?string
{
$value = trim($value);
if ($value === '') {
return null;
}
$value = Str::of($value)
->replace('\\', '/')
->replace('ssh://git@', 'ssh://')
->replace('git@', '')
->replace('.git', '')
->lower()
->value();
$path = parse_url($value, PHP_URL_PATH);
if (is_string($path) && trim($path, '/') !== '') {
$value = trim($path, '/');
} elseif (str_contains($value, ':')) {
$value = Str::of($value)->afterLast(':')->trim('/')->value();
}
$segments = array_values(array_filter(explode('/', trim($value, '/'))));
if ($segments === []) {
return null;
}
return implode('/', array_slice($segments, -2));
}
/**