Merge remote-tracking branch 'origin/next' into audit-policies

This commit is contained in:
Andras Bacsai committed 2026-06-04 10:18:54 +02:00
commit 9dca7ca351
148 files changed
+5987 -1101

No files matched your search

@@ -21,8 +21,6 @@ class ConfigurationChecker extends Component
public array $configurationDiff = [];
public array $groupedConfigurationChanges = [];
public Application|Service|StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $resource;
public function getListeners(): array
@@ -50,21 +48,56 @@ class ConfigurationChecker extends Component
$this->configurationChanged();
}
/**
* Members must never see environment variable values, so redact every
* environment-section change before it is serialized to the browser.
*
* @param array<int, array<string, mixed>> $changes
* @return array<int, array<string, mixed>>
*/
private function redactEnvironmentChanges(array $changes, bool $redact): array
{
if (! $redact) {
return $changes;
}
return collect($changes)
->map(function (array $change): array {
if (data_get($change, 'section') !== 'environment') {
return $change;
}
$change['old_display_value'] = data_get($change, 'old_display_value') === '-' ? '-' : '••••••••';
$change['new_display_value'] = data_get($change, 'new_display_value') === '-' ? '-' : '••••••••';
$change['old_full_value'] = null;
$change['new_full_value'] = null;
$change['expandable'] = false;
$change['display_summary'] = data_get($change, 'type') === 'changed' ? 'Changed' : null;
return $change;
})
->all();
}
public function configurationChanged(): void
{
$this->resource->refresh();
if ($this->resource instanceof Application) {
$diff = $this->resource->pendingDeploymentConfigurationDiff();
// Fail closed: only owners/admins may see unlocked env values.
$redactEnvironment = ! (bool) auth()->user()?->isAdmin();
$array = $diff->toArray();
$array['changes'] = $this->redactEnvironmentChanges($array['changes'] ?? [], $redactEnvironment);
$this->isConfigurationChanged = $diff->isChanged();
$this->configurationDiff = $diff->toArray();
$this->groupedConfigurationChanges = $diff->groupedChanges();
$this->configurationDiff = $array;
return;
}
$this->isConfigurationChanged = $this->resource->isConfigurationChanged();
$this->configurationDiff = [];
$this->groupedConfigurationChanges = [];
}
}
@@ -7,6 +7,8 @@ use App\Models\EnvironmentVariable;
use App\Support\ValidationPatterns;
use App\Traits\EnvironmentVariableProtection;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use Livewire\Component;
class All extends Component
@@ -25,6 +27,8 @@ class All extends Component
public string $view = 'normal';
public string $search = '';
public bool $is_env_sorting_enabled = false;
public bool $use_build_secrets = false;
@@ -35,6 +39,20 @@ class All extends Component
'environmentVariableDeleted' => 'refreshEnvs',
];
public function updatedSearch(): void
{
$this->clearEnvironmentVariableCaches();
}
private function clearEnvironmentVariableCaches(): void
{
unset($this->environmentVariables);
unset($this->environmentVariablesPreview);
unset($this->hardcodedEnvironmentVariables);
unset($this->hardcodedEnvironmentVariablesPreview);
unset($this->hasEnvironmentVariables);
}
public function mount()
{
$this->is_env_sorting_enabled = data_get($this->resource, 'settings.is_env_sorting_enabled', false);
@@ -65,8 +83,27 @@ class All extends Component
public function getEnvironmentVariablesProperty()
{
$query = $this->resource->environment_variables()
->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END");
return $this->getEnvironmentVariables(false);
}
public function getEnvironmentVariablesPreviewProperty()
{
return $this->getEnvironmentVariables(true);
}
private function getEnvironmentVariables(bool $isPreview, bool $withSearch = true): Collection
{
$query = $isPreview
? $this->resource->environment_variables_preview()
: $this->resource->environment_variables();
$query->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END");
if ($withSearch && $this->searchTerm() !== '') {
$escapedSearch = addcslashes(Str::lower($this->searchTerm()), '%_\\');
$query->whereRaw("LOWER(key) LIKE ? ESCAPE '\\'", ['%'.$escapedSearch.'%']);
}
if ($this->is_env_sorting_enabled) {
$query->orderBy('key');
@@ -77,18 +114,17 @@ class All extends Component
return $this->nullLockedValues($query->get());
}
public function getEnvironmentVariablesPreviewProperty()
private function searchTerm(): string
{
$query = $this->resource->environment_variables_preview()
->orderByRaw("CASE WHEN is_required = true AND (value IS NULL OR value = '') THEN 0 ELSE 1 END");
return trim($this->search);
}
if ($this->is_env_sorting_enabled) {
$query->orderBy('key');
} else {
$query->orderBy('order');
}
return $this->nullLockedValues($query->get());
public function getHasEnvironmentVariablesProperty(): bool
{
return $this->environmentVariables->isNotEmpty() ||
$this->environmentVariablesPreview->isNotEmpty() ||
$this->hardcodedEnvironmentVariables->isNotEmpty() ||
$this->hardcodedEnvironmentVariablesPreview->isNotEmpty();
}
private function nullLockedValues($envs)
@@ -105,6 +141,11 @@ class All extends Component
return $envs;
}
public function getIsSearchActiveProperty(): bool
{
return $this->searchTerm() !== '';
}
public function getHardcodedEnvironmentVariablesProperty()
{
return $this->getHardcodedVariables(false);
@@ -152,6 +193,12 @@ class All extends Component
return ! in_array($var['key'], $managedKeys);
});
if ($this->searchTerm() !== '') {
$hardcodedVars = $hardcodedVars->filter(function ($var) {
return str($var['key'])->contains($this->searchTerm(), true);
});
}
// Apply sorting based on is_env_sorting_enabled
if ($this->is_env_sorting_enabled) {
$hardcodedVars = $hardcodedVars->sortBy('key')->values();
@@ -163,9 +210,9 @@ class All extends Component
public function getDevView()
{
$this->variables = $this->formatEnvironmentVariables($this->environmentVariables);
$this->variables = $this->formatEnvironmentVariables($this->getEnvironmentVariables(false, false));
if ($this->showPreview) {
$this->variablesPreview = $this->formatEnvironmentVariables($this->environmentVariablesPreview);
$this->variablesPreview = $this->formatEnvironmentVariables($this->getEnvironmentVariables(true, false));
}
}
@@ -301,9 +348,7 @@ class All extends Component
$environment->order = $maxOrder + 1;
$environment->save();
// Clear computed property cache to force refresh
unset($this->environmentVariables);
unset($this->environmentVariablesPreview);
$this->clearEnvironmentVariableCaches();
$this->dispatch('success', 'Environment variable added.');
}
@@ -432,9 +477,7 @@ class All extends Component
public function refreshEnvs()
{
$this->resource->refresh();
// Clear computed property cache to force refresh
unset($this->environmentVariables);
unset($this->environmentVariablesPreview);
$this->clearEnvironmentVariableCaches();
$this->getDevView();
}
}
@@ -0,0 +1,91 @@
<?php
namespace App\Livewire\Project\Shared;
use App\Models\Service;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Livewire\Component;
class ResourceDetails extends Component
{
use AuthorizesRequests;
public $resource;
public ?string $project_uuid = null;
public ?string $project_name = null;
public ?string $environment_uuid = null;
public ?string $environment_name = null;
public ?string $server_uuid = null;
public ?string $server_name = null;
public array $stack_applications = [];
public array $stack_databases = [];
public function mount()
{
$this->authorize('view', $this->resource);
$environment = $this->resource->environment ?? null;
if ($environment) {
$this->environment_uuid = $environment->uuid;
$this->environment_name = $environment->name;
$project = $environment->project ?? null;
if ($project) {
$this->project_uuid = $project->uuid;
$this->project_name = $project->name;
}
}
$server = $this->resolveServer();
if ($server) {
$this->server_uuid = $server->uuid;
$this->server_name = $server->name;
}
if ($this->resource instanceof Service) {
$this->stack_applications = $this->resource->applications
->map(fn ($app) => [
'name' => $app->human_name ?: $app->name,
'uuid' => $app->uuid,
])
->values()
->all();
$this->stack_databases = $this->resource->databases
->map(fn ($db) => [
'name' => $db->human_name ?: $db->name,
'uuid' => $db->uuid,
])
->values()
->all();
}
}
private function resolveServer()
{
try {
if (isset($this->resource->destination) && $this->resource->destination && isset($this->resource->destination->server)) {
return $this->resource->destination->server;
}
if (method_exists($this->resource, 'server') && $this->resource->server) {
return $this->resource->server;
}
} catch (\Throwable $e) {
return null;
}
return null;
}
public function render()
{
return view('livewire.project.shared.resource-details');
}
}