diff --git a/app/Livewire/NavbarDeleteTeam.php b/app/Livewire/NavbarDeleteTeam.php index e97cceb0d..9508c2adc 100644 --- a/app/Livewire/NavbarDeleteTeam.php +++ b/app/Livewire/NavbarDeleteTeam.php @@ -2,10 +2,8 @@ namespace App\Livewire; -use App\Models\InstanceSettings; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class NavbarDeleteTeam extends Component @@ -19,12 +17,8 @@ public function mount() public function delete($password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } $currentTeam = currentTeam(); diff --git a/app/Livewire/Project/Database/BackupEdit.php b/app/Livewire/Project/Database/BackupEdit.php index 18ad93016..d70c52411 100644 --- a/app/Livewire/Project/Database/BackupEdit.php +++ b/app/Livewire/Project/Database/BackupEdit.php @@ -2,12 +2,9 @@ namespace App\Livewire\Project\Database; -use App\Models\InstanceSettings; use App\Models\ScheduledDatabaseBackup; use Exception; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Locked; use Livewire\Attributes\Validate; use Livewire\Component; @@ -154,12 +151,8 @@ public function delete($password) { $this->authorize('manageBackups', $this->backup->database); - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } try { diff --git a/app/Livewire/Project/Database/BackupExecutions.php b/app/Livewire/Project/Database/BackupExecutions.php index 0b6d8338b..44f903fcc 100644 --- a/app/Livewire/Project/Database/BackupExecutions.php +++ b/app/Livewire/Project/Database/BackupExecutions.php @@ -2,11 +2,9 @@ namespace App\Livewire\Project\Database; -use App\Models\InstanceSettings; use App\Models\ScheduledDatabaseBackup; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class BackupExecutions extends Component @@ -69,12 +67,8 @@ public function cleanupDeleted() public function deleteBackup($executionId, $password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } $execution = $this->backup->executions()->where('id', $executionId)->first(); diff --git a/app/Livewire/Project/Service/Database.php b/app/Livewire/Project/Service/Database.php index 4bcf866d3..1e183c6bc 100644 --- a/app/Livewire/Project/Service/Database.php +++ b/app/Livewire/Project/Service/Database.php @@ -4,12 +4,9 @@ use App\Actions\Database\StartDatabaseProxy; use App\Actions\Database\StopDatabaseProxy; -use App\Models\InstanceSettings; use App\Models\ServiceDatabase; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class Database extends Component @@ -96,12 +93,8 @@ public function delete($password) try { $this->authorize('delete', $this->database); - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } $this->database->delete(); diff --git a/app/Livewire/Project/Service/FileStorage.php b/app/Livewire/Project/Service/FileStorage.php index 54ef82872..079115bb6 100644 --- a/app/Livewire/Project/Service/FileStorage.php +++ b/app/Livewire/Project/Service/FileStorage.php @@ -3,7 +3,6 @@ namespace App\Livewire\Project\Service; use App\Models\Application; -use App\Models\InstanceSettings; use App\Models\LocalFileVolume; use App\Models\ServiceApplication; use App\Models\ServiceDatabase; @@ -16,8 +15,6 @@ use App\Models\StandalonePostgresql; use App\Models\StandaloneRedis; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Validate; use Livewire\Component; @@ -141,12 +138,8 @@ public function delete($password) { $this->authorize('update', $this->resource); - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } try { diff --git a/app/Livewire/Project/Service/ServiceApplicationView.php b/app/Livewire/Project/Service/ServiceApplicationView.php index 68544f1ab..4302c05fb 100644 --- a/app/Livewire/Project/Service/ServiceApplicationView.php +++ b/app/Livewire/Project/Service/ServiceApplicationView.php @@ -2,12 +2,9 @@ namespace App\Livewire\Project\Service; -use App\Models\InstanceSettings; use App\Models\ServiceApplication; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Validate; use Livewire\Component; use Spatie\Url\Url; @@ -128,12 +125,8 @@ public function delete($password) try { $this->authorize('delete', $this->application); - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } $this->application->delete(); diff --git a/app/Livewire/Project/Shared/Danger.php b/app/Livewire/Project/Shared/Danger.php index 0ed1347f8..8bf3c7438 100644 --- a/app/Livewire/Project/Shared/Danger.php +++ b/app/Livewire/Project/Shared/Danger.php @@ -3,13 +3,10 @@ namespace App\Livewire\Project\Shared; use App\Jobs\DeleteResourceJob; -use App\Models\InstanceSettings; use App\Models\Service; use App\Models\ServiceApplication; use App\Models\ServiceDatabase; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; use Visus\Cuid2\Cuid2; @@ -93,12 +90,8 @@ public function mount() public function delete($password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } if (! $this->resource) { diff --git a/app/Livewire/Project/Shared/Destination.php b/app/Livewire/Project/Shared/Destination.php index 28e3f23e7..ffd18b35c 100644 --- a/app/Livewire/Project/Shared/Destination.php +++ b/app/Livewire/Project/Shared/Destination.php @@ -5,12 +5,9 @@ use App\Actions\Application\StopApplicationOneServer; use App\Actions\Docker\GetContainersStatus; use App\Events\ApplicationStatusChanged; -use App\Models\InstanceSettings; use App\Models\Server; use App\Models\StandaloneDocker; use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; use Visus\Cuid2\Cuid2; @@ -140,12 +137,8 @@ public function addServer(int $network_id, int $server_id) public function removeServer(int $network_id, int $server_id, $password) { try { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } if ($this->resource->destination->server->id == $server_id && $this->resource->destination->id == $network_id) { diff --git a/app/Livewire/Project/Shared/Storages/Show.php b/app/Livewire/Project/Shared/Storages/Show.php index c8dc68d66..2091eca14 100644 --- a/app/Livewire/Project/Shared/Storages/Show.php +++ b/app/Livewire/Project/Shared/Storages/Show.php @@ -2,11 +2,8 @@ namespace App\Livewire\Project\Shared\Storages; -use App\Models\InstanceSettings; use App\Models\LocalPersistentVolume; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class Show extends Component @@ -84,12 +81,8 @@ public function delete($password) { $this->authorize('update', $this->resource); - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } $this->storage->delete(); diff --git a/app/Livewire/Server/Delete.php b/app/Livewire/Server/Delete.php index 8c2c54c99..27a6e7aca 100644 --- a/app/Livewire/Server/Delete.php +++ b/app/Livewire/Server/Delete.php @@ -3,11 +3,8 @@ namespace App\Livewire\Server; use App\Actions\Server\DeleteServer; -use App\Models\InstanceSettings; use App\Models\Server; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class Delete extends Component @@ -29,12 +26,8 @@ public function mount(string $server_uuid) public function delete($password) { - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } try { $this->authorize('delete', $this->server); diff --git a/app/Livewire/Server/Security/TerminalAccess.php b/app/Livewire/Server/Security/TerminalAccess.php index 284eea7dd..310edcfe4 100644 --- a/app/Livewire/Server/Security/TerminalAccess.php +++ b/app/Livewire/Server/Security/TerminalAccess.php @@ -2,11 +2,8 @@ namespace App\Livewire\Server\Security; -use App\Models\InstanceSettings; use App\Models\Server; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Attributes\Validate; use Livewire\Component; @@ -44,13 +41,9 @@ public function toggleTerminal($password) throw new \Exception('Only team administrators and owners can modify terminal access.'); } - // Verify password unless two-step confirmation is disabled - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + // Verify password + if (! verifyPasswordConfirmation($password, $this)) { + return; } // Toggle the terminal setting diff --git a/app/Livewire/Settings/Advanced.php b/app/Livewire/Settings/Advanced.php index be38ae1d8..b011d2dc1 100644 --- a/app/Livewire/Settings/Advanced.php +++ b/app/Livewire/Settings/Advanced.php @@ -5,8 +5,6 @@ use App\Models\InstanceSettings; use App\Models\Server; use App\Rules\ValidIpOrCidr; -use Auth; -use Hash; use Livewire\Attributes\Validate; use Livewire\Component; @@ -157,9 +155,7 @@ public function instantSave() public function toggleTwoStepConfirmation($password): bool { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - + if (! verifyPasswordConfirmation($password, $this)) { return false; } diff --git a/app/Livewire/Team/AdminView.php b/app/Livewire/Team/AdminView.php index 6d6915ae2..c8d44d42b 100644 --- a/app/Livewire/Team/AdminView.php +++ b/app/Livewire/Team/AdminView.php @@ -2,10 +2,7 @@ namespace App\Livewire\Team; -use App\Models\InstanceSettings; use App\Models\User; -use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Hash; use Livewire\Component; class AdminView extends Component @@ -58,12 +55,8 @@ public function delete($id, $password) return redirect()->route('dashboard'); } - if (! data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { - if (! Hash::check($password, Auth::user()->password)) { - $this->addError('password', 'The provided password is incorrect.'); - - return; - } + if (! verifyPasswordConfirmation($password, $this)) { + return; } if (! auth()->user()->isInstanceAdmin()) { diff --git a/app/Models/User.php b/app/Models/User.php index f04b6fa77..b790efcf1 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -443,4 +443,13 @@ public function hasEmailChangeRequest(): bool && $this->email_change_code_expires_at && Carbon::now()->lessThan($this->email_change_code_expires_at); } + + /** + * Check if the user has a password set. + * OAuth users are created without passwords. + */ + public function hasPassword(): bool + { + return ! empty($this->password); + } } diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 1066f1a63..3d9e9e729 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -33,6 +33,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\File; +use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Http; use Illuminate\Support\Facades\Process; use Illuminate\Support\Facades\RateLimiter; @@ -3308,3 +3309,57 @@ function formatContainerStatus(string $status): string return str($status)->headline()->value(); } } + +/** + * Check if password confirmation should be skipped. + * Returns true if: + * - Two-step confirmation is globally disabled + * - User has no password (OAuth users) + * + * Used by modal-confirmation.blade.php to determine if password step should be shown. + * + * @return bool True if password confirmation should be skipped + */ +function shouldSkipPasswordConfirmation(): bool +{ + // Skip if two-step confirmation is globally disabled + if (data_get(InstanceSettings::get(), 'disable_two_step_confirmation')) { + return true; + } + + // Skip if user has no password (OAuth users) + if (! Auth::user()?->hasPassword()) { + return true; + } + + return false; +} + +/** + * Verify password for two-step confirmation. + * Skips verification if: + * - Two-step confirmation is globally disabled + * - User has no password (OAuth users) + * + * @param mixed $password The password to verify (may be array if skipped by frontend) + * @param \Livewire\Component|null $component Optional Livewire component to add errors to + * @return bool True if verification passed (or skipped), false if password is incorrect + */ +function verifyPasswordConfirmation(mixed $password, ?Livewire\Component $component = null): bool +{ + // Skip if password confirmation should be skipped + if (shouldSkipPasswordConfirmation()) { + return true; + } + + // Verify the password + if (! Hash::check($password, Auth::user()->password)) { + if ($component) { + $component->addError('password', 'The provided password is incorrect.'); + } + + return false; + } + + return true; +} diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php index edff3b6bf..73939092e 100644 --- a/resources/views/components/modal-confirmation.blade.php +++ b/resources/views/components/modal-confirmation.blade.php @@ -29,17 +29,23 @@ @php use App\Models\InstanceSettings; + // Global setting to disable ALL two-step confirmation (text + password) $disableTwoStepConfirmation = data_get(InstanceSettings::get(), 'disable_two_step_confirmation'); + // Skip ONLY password confirmation for OAuth users (they have no password) + $skipPasswordConfirmation = shouldSkipPasswordConfirmation(); if ($temporaryDisableTwoStepConfirmation) { $disableTwoStepConfirmation = false; + $skipPasswordConfirmation = false; } + // When password step is skipped, Step 2 becomes final - change button text from "Continue" to "Confirm" + $effectiveStep2ButtonText = ($skipPasswordConfirmation && $step2ButtonText === 'Continue') ? 'Confirm' : $step2ButtonText; @endphp