mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(audit): stop duplicate and empty audit rows
- Set env var version and compose parsing version while creating, so a create writes one row - Record no rows for the auto-created preview copy of an env var - Record env and application API updates only with the fields that really changed - Store no change data for new env vars, and null for empty change data - Store metadata team_id as an integer - Drop the unused level and resource indexes of audit_events Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
13298591d8
commit
b7d0e6be2a
@@ -3631,6 +3631,7 @@ class ApplicationsController extends Controller
|
||||
}
|
||||
|
||||
$this->authorize('update', $application);
|
||||
$settingsBefore = $application->settings->getAttributes();
|
||||
|
||||
if ($request->has('redact_all_env_values_in_logs') || $request->has('include_coolify_default_variables')) {
|
||||
$this->authorize('manageEnvironment', $application);
|
||||
@@ -4092,12 +4093,18 @@ class ApplicationsController extends Controller
|
||||
$dnsCleanup->queueReleaseOfRemovedHostnames($application, $previousDnsHostnames, (int) $teamId);
|
||||
app(ResourceDnsRecords::class)->queueAutomaticRecords($application, $previousDnsHostnames);
|
||||
|
||||
auditLog('api.application.updated', [
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'changed_fields' => array_values(array_intersect($allowedFields, array_keys($request->all()))),
|
||||
]);
|
||||
$changedSettings = collect($application->settings->getAttributes())
|
||||
->reject(fn (mixed $value, string $field): bool => $field === 'updated_at' || ($settingsBefore[$field] ?? null) == $value)
|
||||
->keys();
|
||||
$changedFields = [...auditSavedFields($application), ...$changedSettings];
|
||||
if ($changedFields !== []) {
|
||||
auditLog('api.application.updated', [
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'application_name' => $application->name,
|
||||
'changed_fields' => $changedFields,
|
||||
]);
|
||||
}
|
||||
|
||||
if ($instantDeploy) {
|
||||
$deployment_uuid = new_public_id();
|
||||
@@ -4339,15 +4346,20 @@ class ApplicationsController extends Controller
|
||||
$env->value = $request->value;
|
||||
}
|
||||
$env->fill($request->only(['is_literal', 'is_multiline', 'is_shown_once', 'is_runtime', 'is_buildtime', 'comment']));
|
||||
$env->save();
|
||||
// One audit row with the application context, instead of a second row from the model.
|
||||
$env->withoutAuditLogging(fn () => $env->save());
|
||||
$changedFields = auditSavedFields($env);
|
||||
|
||||
auditLog('api.application.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
'is_preview' => (bool) $is_preview,
|
||||
]);
|
||||
if ($changedFields !== []) {
|
||||
auditLog('api.application.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'application_uuid' => $application->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
'is_preview' => (bool) $is_preview,
|
||||
'changed_fields' => $changedFields,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
|
||||
}
|
||||
|
||||
@@ -4016,14 +4016,19 @@ class DatabasesController extends Controller
|
||||
if ($request->has('comment')) {
|
||||
$env->comment = $request->comment;
|
||||
}
|
||||
$env->save();
|
||||
// One audit row with the database context, instead of a second row from the model.
|
||||
$env->withoutAuditLogging(fn () => $env->save());
|
||||
$changedFields = auditSavedFields($env);
|
||||
|
||||
auditLog('api.database.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'database_uuid' => $database->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
]);
|
||||
if ($changedFields !== []) {
|
||||
auditLog('api.database.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'database_uuid' => $database->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
'changed_fields' => $changedFields,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json($this->removeSensitiveEnvData($env))->setStatusCode(201);
|
||||
}
|
||||
|
||||
@@ -1584,14 +1584,19 @@ class ServicesController extends Controller
|
||||
if ($request->has('comment')) {
|
||||
$env->comment = $request->comment;
|
||||
}
|
||||
$env->save();
|
||||
// One audit row with the service context, instead of a second row from the model.
|
||||
$env->withoutAuditLogging(fn () => $env->save());
|
||||
$changedFields = auditSavedFields($env);
|
||||
|
||||
auditLog('api.service.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'service_uuid' => $service->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
]);
|
||||
if ($changedFields !== []) {
|
||||
auditLog('api.service.env_updated', [
|
||||
'team_id' => $teamId,
|
||||
'service_uuid' => $service->uuid,
|
||||
'env_uuid' => $env->uuid,
|
||||
'env_key' => $env->key,
|
||||
'changed_fields' => $changedFields,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json($this->removeSensitiveData($env))->setStatusCode(201);
|
||||
}
|
||||
|
||||
@@ -329,6 +329,7 @@ class Application extends BaseModel
|
||||
$application->manual_webhook_secret_gitlab ??= Str::random(40);
|
||||
$application->manual_webhook_secret_bitbucket ??= Str::random(40);
|
||||
$application->manual_webhook_secret_gitea ??= Str::random(40);
|
||||
$application->compose_parsing_version = self::$parserVersion;
|
||||
});
|
||||
static::addGlobalScope('withRelations', function ($builder) {
|
||||
$builder->withCount([
|
||||
@@ -426,8 +427,6 @@ class Application extends BaseModel
|
||||
ApplicationSetting::create([
|
||||
'application_id' => $application->id,
|
||||
]);
|
||||
$application->compose_parsing_version = self::$parserVersion;
|
||||
$application->save();
|
||||
|
||||
// Add default NIXPACKS_NODE_VERSION environment variable for Nixpacks applications
|
||||
if ($application->build_pack === 'nixpacks') {
|
||||
|
||||
@@ -123,7 +123,7 @@ class AuditEvent extends Model
|
||||
*/
|
||||
public static function recordModelMutation(string $event, array $context, array $changes): void
|
||||
{
|
||||
self::recordWithChanges($event, $context, $changes);
|
||||
self::recordWithChanges($event, $context, $changes ?: null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,6 +161,11 @@ class AuditEvent extends Model
|
||||
*/
|
||||
private static function attributesFor(string $event, array $context, ?array $changes, string $level): array
|
||||
{
|
||||
// API tokens store team_id as a string. Keep it an integer, so JSON filters on metadata work.
|
||||
if (is_string($context['team_id'] ?? null) && ctype_digit($context['team_id'])) {
|
||||
$context['team_id'] = (int) $context['team_id'];
|
||||
}
|
||||
|
||||
$teamId = data_get(auth()->user()?->currentAccessToken(), 'team_id')
|
||||
?? (array_key_exists('team_id', $context)
|
||||
? $context['team_id']
|
||||
|
||||
@@ -48,6 +48,9 @@ class EnvironmentVariable extends BaseModel
|
||||
{
|
||||
use Auditable;
|
||||
|
||||
/** The key is in the metadata of the row, and the flags of a new variable have little audit value. */
|
||||
protected bool $auditCreatedChanges = false;
|
||||
|
||||
/**
|
||||
* Flags that a "# coolify:" line of the developer view can set, with their defaults.
|
||||
*
|
||||
@@ -140,6 +143,9 @@ class EnvironmentVariable extends BaseModel
|
||||
|
||||
protected static function booted()
|
||||
{
|
||||
static::creating(function (ModelsEnvironmentVariable $environment_variable) {
|
||||
$environment_variable->version = config('constants.coolify.version');
|
||||
});
|
||||
static::created(function (ModelsEnvironmentVariable $environment_variable) {
|
||||
if ($environment_variable->resourceable_type === Application::class && ! $environment_variable->is_preview) {
|
||||
$found = ModelsEnvironmentVariable::where('key', $environment_variable->key)
|
||||
@@ -152,7 +158,8 @@ class EnvironmentVariable extends BaseModel
|
||||
$application = Application::find($environment_variable->resourceable_id);
|
||||
// A compose preview generates its own magic secrets on the first pull request parse.
|
||||
if ($application && ! ($application->build_pack === 'dockercompose' && $environment_variable->isGeneratedMagicVariable())) {
|
||||
ModelsEnvironmentVariable::create([
|
||||
// The preview copy is part of the same change, so it gets no audit rows of its own.
|
||||
$preview = new ModelsEnvironmentVariable([
|
||||
'key' => $environment_variable->key,
|
||||
'value' => $environment_variable->value,
|
||||
'is_multiline' => $environment_variable->is_multiline ?? false,
|
||||
@@ -166,12 +173,10 @@ class EnvironmentVariable extends BaseModel
|
||||
'resourceable_id' => $environment_variable->resourceable_id,
|
||||
'is_preview' => true,
|
||||
]);
|
||||
$preview->withoutAuditLogging(fn () => $preview->save());
|
||||
}
|
||||
}
|
||||
}
|
||||
$environment_variable->update([
|
||||
'version' => config('constants.coolify.version'),
|
||||
]);
|
||||
});
|
||||
|
||||
static::saving(function (ModelsEnvironmentVariable $environmentVariable) {
|
||||
|
||||
@@ -100,10 +100,7 @@ class Service extends BaseModel
|
||||
if (blank($service->name)) {
|
||||
$service->name = 'service-'.new_public_id();
|
||||
}
|
||||
});
|
||||
static::created(function ($service) {
|
||||
$service->compose_parsing_version = self::$parserVersion;
|
||||
$service->save();
|
||||
});
|
||||
static::saving(function ($service) {
|
||||
if ($service->isDirty('docker_compose_raw')) {
|
||||
|
||||
@@ -90,7 +90,7 @@ trait Auditable
|
||||
"{$resourceType}_uuid" => $this->getAttribute('uuid'),
|
||||
"{$resourceType}_name" => $this->getAttribute('name') ?? $this->getAttribute('key'),
|
||||
'changed_fields' => $changedFields,
|
||||
], $this->auditChanges(
|
||||
], $action === 'created' && ($this->auditCreatedChanges ?? true) === false ? [] : $this->auditChanges(
|
||||
$action,
|
||||
$action === 'updated' ? $changedFields : array_keys($this->getAttributes()),
|
||||
));
|
||||
|
||||
Reference in New Issue
Block a user