mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-06 05:58:14 -04:00
Queue database imports from upload, S3, or server paths via REST, and manage Cloudflare DNS records from integration tokens and domain UIs.
214 lines
7.6 KiB
PHP
214 lines
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire\Security;
|
|
|
|
use App\Models\IntegrationToken;
|
|
use App\Services\Dns\CloudflareDnsProvider;
|
|
use App\Services\IntegrationTokenValidator;
|
|
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Livewire\Component;
|
|
|
|
class IntegrationTokenEditor extends Component
|
|
{
|
|
use AuthorizesRequests;
|
|
|
|
public IntegrationToken $integrationToken;
|
|
|
|
public string $name = '';
|
|
|
|
public string $newToken = '';
|
|
|
|
public array $capabilities = [];
|
|
|
|
public array $metadata = [];
|
|
|
|
public int $zoneCount = 0;
|
|
|
|
/** @var array<int, array{id: int, name: string, account_name: ?string, managed_records_count: int}> */
|
|
public array $zones = [];
|
|
|
|
public bool $automaticDns = true;
|
|
|
|
public function mount(string $integration_token_uuid): void
|
|
{
|
|
$this->integrationToken = IntegrationToken::ownedByCurrentTeam()
|
|
->whereUuid($integration_token_uuid)
|
|
->firstOrFail();
|
|
|
|
$this->authorize('view', $this->integrationToken);
|
|
|
|
$this->name = $this->integrationToken->name;
|
|
$this->capabilities = $this->integrationToken->capabilities;
|
|
$this->metadata = $this->integrationToken->metadata ?? [];
|
|
$this->loadZones();
|
|
$this->automaticDns = $this->integrationToken->automaticDnsEnabled();
|
|
}
|
|
|
|
protected function rules(): array
|
|
{
|
|
$allowedCapability = $this->integrationToken->provider === 'cloudflare' ? 'dns' : 'secrets';
|
|
|
|
$rules = [
|
|
'name' => ['required', 'string', 'max:255'],
|
|
'newToken' => ['nullable', 'string'],
|
|
'capabilities' => ['required', 'array', 'min:1'],
|
|
'capabilities.*' => ['required', 'in:'.$allowedCapability],
|
|
'automaticDns' => ['boolean'],
|
|
];
|
|
|
|
if ($this->integrationToken->provider === 'infisical') {
|
|
$rules['metadata.base_url'] = ['required', 'url'];
|
|
$rules['metadata.client_id'] = ['required', 'string'];
|
|
}
|
|
|
|
if ($this->integrationToken->provider === 'vault') {
|
|
$rules['metadata.base_url'] = ['required', 'url'];
|
|
$rules['metadata.namespace'] = ['nullable', 'string'];
|
|
}
|
|
|
|
return $rules;
|
|
}
|
|
|
|
protected function messages(): array
|
|
{
|
|
return [
|
|
'capabilities.required' => 'Select at least one capability.',
|
|
'capabilities.min' => 'Select at least one capability.',
|
|
];
|
|
}
|
|
|
|
public function save(IntegrationTokenValidator $validator, CloudflareDnsProvider $cloudflare): void
|
|
{
|
|
$this->authorize('update', $this->integrationToken);
|
|
$validated = $this->validate();
|
|
$provider = $this->integrationToken->provider;
|
|
$token = filled($validated['newToken']) ? $validated['newToken'] : $this->integrationToken->token;
|
|
$metadata = array_filter(data_get($validated, 'metadata', []), fn ($value) => filled($value));
|
|
if ($provider === 'cloudflare') {
|
|
if ($validated['automaticDns']) {
|
|
unset($metadata['automatic_dns']);
|
|
} else {
|
|
$metadata['automatic_dns'] = false;
|
|
}
|
|
}
|
|
$capabilitiesChanged = collect($validated['capabilities'])->sort()->values()->all()
|
|
!== collect($this->integrationToken->capabilities)->sort()->values()->all();
|
|
$metadataChanged = $metadata != ($this->integrationToken->metadata ?? []);
|
|
|
|
try {
|
|
if ((filled($validated['newToken']) || $capabilitiesChanged || $metadataChanged)
|
|
&& ! $validator->validate($provider, $token, $validated['capabilities'], $metadata)) {
|
|
$this->dispatch('error', $validator->errorMessage($provider));
|
|
|
|
return;
|
|
}
|
|
|
|
$updates = [
|
|
'name' => $validated['name'],
|
|
'capabilities' => $validated['capabilities'],
|
|
'metadata' => $metadata ?: null,
|
|
];
|
|
|
|
if (filled($validated['newToken'])) {
|
|
$updates['token'] = $validated['newToken'];
|
|
}
|
|
|
|
DB::transaction(function () use ($updates, $provider, $validated, $capabilitiesChanged, $cloudflare): void {
|
|
$this->integrationToken->update($updates);
|
|
if ($provider === 'cloudflare' && (filled($validated['newToken']) || $capabilitiesChanged)) {
|
|
$cloudflare->syncZones($this->integrationToken);
|
|
}
|
|
});
|
|
$this->newToken = '';
|
|
$this->loadZones();
|
|
|
|
auditLog('ui.integration_token.updated', [
|
|
'team_id' => currentTeam()->id,
|
|
'integration_token_uuid' => $this->integrationToken->uuid,
|
|
'integration_token_name' => $this->integrationToken->name,
|
|
'provider' => $this->integrationToken->provider,
|
|
'rotated' => array_key_exists('token', $updates),
|
|
]);
|
|
|
|
$this->dispatch(
|
|
'integration-token-updated',
|
|
uuid: $this->integrationToken->uuid,
|
|
name: $this->integrationToken->name,
|
|
capabilities: $this->integrationToken->capabilities,
|
|
);
|
|
$this->dispatch('success', 'Integration token updated successfully.');
|
|
} catch (\Throwable $e) {
|
|
handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function delete(string $password = ''): void
|
|
{
|
|
$this->authorize('delete', $this->integrationToken);
|
|
|
|
if ($this->integrationToken->secretManagerLinks()->exists()) {
|
|
$this->dispatch('error', 'This token is used by one or more resources as a secret manager source. Remove those links first.');
|
|
|
|
return;
|
|
}
|
|
|
|
if ($this->integrationToken->managedDnsRecords()->exists()) {
|
|
$this->dispatch('error', 'This token manages DNS records. Remove those domains or records first.');
|
|
|
|
return;
|
|
}
|
|
|
|
$uuid = $this->integrationToken->uuid;
|
|
$name = $this->integrationToken->name;
|
|
$provider = $this->integrationToken->provider;
|
|
$this->integrationToken->delete();
|
|
|
|
auditLog('ui.integration_token.deleted', [
|
|
'team_id' => currentTeam()->id,
|
|
'integration_token_uuid' => $uuid,
|
|
'integration_token_name' => $name,
|
|
'provider' => $provider,
|
|
]);
|
|
|
|
$this->dispatch('integration-token-deleted', uuid: $this->integrationToken->uuid);
|
|
$this->dispatch('close-modal');
|
|
$this->dispatch('success', 'Integration token deleted successfully.');
|
|
}
|
|
|
|
public function refreshZones(CloudflareDnsProvider $cloudflare): void
|
|
{
|
|
$this->authorize('update', $this->integrationToken);
|
|
try {
|
|
$cloudflare->syncZones($this->integrationToken);
|
|
$this->integrationToken->refresh();
|
|
$this->loadZones();
|
|
$this->dispatch('success', "Cloudflare zones refreshed. {$this->zoneCount} accessible zones found.");
|
|
} catch (\Throwable $e) {
|
|
handleError($e, $this);
|
|
}
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.security.integration-token-editor');
|
|
}
|
|
|
|
private function loadZones(): void
|
|
{
|
|
$this->zones = $this->integrationToken->dnsZones()
|
|
->select(['id', 'integration_token_id', 'name', 'account_name'])
|
|
->withCount('managedRecords')
|
|
->orderBy('name')
|
|
->get()
|
|
->map(fn ($zone) => [
|
|
'id' => $zone->id,
|
|
'name' => $zone->name,
|
|
'account_name' => $zone->account_name,
|
|
'managed_records_count' => $zone->managed_records_count,
|
|
])
|
|
->all();
|
|
$this->zoneCount = count($this->zones);
|
|
}
|
|
}
|