coolify/app/Console/Commands/Mapledeploy/UserSetPassword.php
rosslh 65d85fb890
All checks were successful
Build MapleDeploy Coolify Image / build (push) Successful in 41s
fix(coolify-access): prefer managed root team
2026-06-14 14:39:05 -04:00

107 lines
3.8 KiB
PHP

<?php
namespace App\Console\Commands\Mapledeploy;
use App\Enums\Role;
use App\Models\Team;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class UserSetPassword extends Command
{
protected $signature = 'mapledeploy:user:set-password
{user_id : Coolify user id}
{--email= : New user email address}
{--name= : New user display name}';
protected $description = 'Set a Coolify user password for MapleDeploy dashboard access management';
public function handle(): int
{
$password = rtrim((string) stream_get_contents(STDIN), "\n");
$updatesOwner = $this->option('email') !== null || $this->option('name') !== null;
$input = [
'password' => $password,
'email' => $this->option('email'),
'name' => $this->option('name'),
];
$rules = ['password' => ['required', 'string', 'min:8']];
if ($updatesOwner) {
$rules['email'] = ['required', 'string', 'email', 'max:255'];
$rules['name'] = ['required', 'string', 'max:255'];
}
$validator = Validator::make($input, $rules);
if ($validator->fails()) {
return $this->failWith('INVALID_INPUT');
}
$user = User::find($this->argument('user_id'));
if (! $user) {
return $this->failWith('USER_NOT_FOUND');
}
$rootTeam = null;
if ((int) $user->id !== 0) {
$rootTeam = Team::find(0);
if (! $rootTeam) {
return $this->failWith('ROOT_TEAM_MISSING');
}
}
$changes = [
'password' => Hash::make($password),
// MapleDeploy branding: clear the revocation marker when the
// dashboard intentionally restores this Coolify login.
'remember_token' => null,
];
if ($updatesOwner) {
$email = Str::lower((string) $input['email']);
if (User::whereEmail($email)->whereKeyNot($user->id)->exists()) {
return $this->failWith('EMAIL_EXISTS');
}
// MapleDeploy branding: claiming root admin transfers the Coolify
// account identity so the previous email holder cannot recover it.
$changes['email'] = $email;
$changes['name'] = $input['name'];
}
DB::transaction(function () use ($user, $changes, $updatesOwner, $rootTeam) {
$user->forceFill($changes)->save();
if ($updatesOwner && ! $user->hasVerifiedEmail()) {
$user->markEmailAsVerified();
}
if ($rootTeam) {
// MapleDeploy branding: matching an existing Coolify user by
// email must grant the same root-team admin access as a newly
// dashboard-created user.
$user->teams()->syncWithoutDetaching([
$rootTeam->id => ['role' => Role::ADMIN->value],
]);
}
// MapleDeploy branding: password resets from the dashboard should
// end browser sessions authenticated with the previous password.
DB::table('sessions')->where('user_id', $user->id)->delete();
});
$this->line(json_encode([
'user' => [
'id' => $user->id,
'email' => $user->email,
'name' => $user->name,
],
], 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;
}
}