feat(coolify): delete mapledeploy users
All checks were successful
Build MapleDeploy Coolify Image / build (push) Successful in 1m26s

This commit is contained in:
rosslh 2026-06-21 16:26:12 -04:00
parent bc67ddc40c
commit 5cd9357ebd
2 changed files with 130 additions and 0 deletions

View file

@ -0,0 +1,61 @@
<?php
namespace App\Console\Commands\Mapledeploy;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class UserDelete extends Command
{
protected $signature = 'mapledeploy:user:delete {user_id : Coolify user id}';
protected $description = 'Delete a Coolify user for MapleDeploy dashboard access management';
public function handle(): int
{
$rawUserId = $this->argument('user_id');
$userId = filter_var($rawUserId, FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
if ($userId === false) {
return $this->failWith('INVALID_USER_ID');
}
if ($userId === 0) {
return $this->failWith('CANNOT_DELETE_ROOT_USER');
}
$user = User::find($userId);
if (! $user) {
$this->line(json_encode([
'deleted' => null,
'alreadyDeleted' => true,
'id' => $userId,
], JSON_THROW_ON_ERROR));
return self::SUCCESS;
}
$deleted = [
'id' => $user->id,
'email' => $user->email,
];
DB::transaction(function () use ($user) {
$user->tokens()->delete();
// MapleDeploy branding: deletion must end any active browser sessions.
DB::table('sessions')->where('user_id', $user->id)->delete();
$user->delete();
});
$this->line(json_encode(['deleted' => $deleted], JSON_THROW_ON_ERROR));
return self::SUCCESS;
}
private function failWith(string $code): int
{
$this->line(json_encode(['error' => $code], JSON_THROW_ON_ERROR));
return self::FAILURE;
}
}

View file

@ -238,6 +238,75 @@ function runMapledeployUserCommand(array $arguments, string $stdin = ''): array
->and($memberUser->fresh()->remember_token)->toBeNull();
});
test('MapleDeploy user delete command removes non-root users', function () {
runMapledeployUserCommand([
'mapledeploy:user:create',
'--admin',
'--email=owner@example.com',
'--name=Owner',
], "owner-password\n");
$member = runMapledeployUserCommand([
'mapledeploy:user:create',
'--email=delete-me@example.com',
'--name=Delete Me',
'--team-role=admin',
], "member-password\n");
$memberUser = User::findOrFail($member['json']['user']['id']);
DB::table('personal_access_tokens')->insert([
'tokenable_type' => User::class,
'tokenable_id' => $memberUser->id,
'name' => 'delete-token',
'token' => hash('sha256', 'delete-token'),
'team_id' => '0',
'abilities' => json_encode(['*'], JSON_THROW_ON_ERROR),
'created_at' => now(),
'updated_at' => now(),
]);
DB::table('sessions')->insert([
'id' => 'delete-session',
'user_id' => $memberUser->id,
'ip_address' => '127.0.0.1',
'user_agent' => 'Test Browser',
'payload' => base64_encode('delete-payload'),
'last_activity' => now()->timestamp,
]);
$deleteRoot = runMapledeployUserCommand(['mapledeploy:user:delete', '0']);
expect($deleteRoot['exitCode'])->toBe(1)
->and($deleteRoot['json'])->toBe(['error' => 'CANNOT_DELETE_ROOT_USER']);
$invalid = runMapledeployUserCommand(['mapledeploy:user:delete', 'not-a-user-id']);
expect($invalid['exitCode'])->toBe(1)
->and($invalid['json'])->toBe(['error' => 'INVALID_USER_ID']);
$delete = runMapledeployUserCommand([
'mapledeploy:user:delete',
(string) $memberUser->id,
]);
expect($delete['exitCode'])->toBe(0)
->and($delete['json']['deleted'])->toBe([
'id' => $memberUser->id,
'email' => 'delete-me@example.com',
])
->and(User::find($memberUser->id))->toBeNull()
->and(DB::table('personal_access_tokens')->where('tokenable_id', $memberUser->id)->count())->toBe(0)
->and(DB::table('sessions')->where('user_id', $memberUser->id)->count())->toBe(0);
$missing = runMapledeployUserCommand([
'mapledeploy:user:delete',
(string) $memberUser->id,
]);
expect($missing['exitCode'])->toBe(0)
->and($missing['json'])->toBe([
'deleted' => null,
'alreadyDeleted' => true,
'id' => $memberUser->id,
]);
});
test('MapleDeploy password command can transfer root ownership identity', function () {
runMapledeployUserCommand([
'mapledeploy:user:create',