-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* confirmable * updates to confirmable * tweak wording * Apply fixes from StyleCI (#156) * work on inertia confirmations * use configured timeouts * Apply fixes from StyleCI (#158) * change within * remove unused variable * localize
- Loading branch information
1 parent
f3a45bd
commit 9ebb099
Showing
7 changed files
with
346 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
@props(['title' => __('Confirm Password'), 'content' => __('For your security, please confirm your password to continue.'), 'button' => __('Confirm')]) | ||
|
||
@php | ||
$confirmableId = md5($attributes->wire('then')); | ||
@endphp | ||
|
||
<span | ||
{{ $attributes->wire('then') }} | ||
x-data | ||
x-ref="span" | ||
x-on:click="$wire.startConfirmingPassword('{{ $confirmableId }}')" | ||
x-on:password-confirmed.window="setTimeout(() => $event.detail.id === '{{ $confirmableId }}' && $refs.span.dispatchEvent(new CustomEvent('then', { bubbles: false })), 250);" | ||
> | ||
{{ $slot }} | ||
</span> | ||
|
||
@once | ||
<x-jet-dialog-modal wire:model="confirmingPassword"> | ||
<x-slot name="title"> | ||
{{ $title }} | ||
</x-slot> | ||
|
||
<x-slot name="content"> | ||
{{ $content }} | ||
|
||
<div class="mt-4" x-data="{}" x-on:confirming-password.window="setTimeout(() => $refs.confirmable_password.focus(), 250)"> | ||
<x-jet-input type="password" class="mt-1 block w-3/4" placeholder="Password" | ||
x-ref="confirmable_password" | ||
wire:model.defer="confirmablePassword" | ||
wire:keydown.enter="confirmPassword" /> | ||
|
||
<x-jet-input-error for="confirmable_password" class="mt-2" /> | ||
</div> | ||
</x-slot> | ||
|
||
<x-slot name="footer"> | ||
<x-jet-secondary-button wire:click="stopConfirmingPassword" wire:loading.attr="disabled"> | ||
Nevermind | ||
</x-jet-secondary-button> | ||
|
||
<x-jet-button class="ml-2" wire:click="confirmPassword" wire:loading.attr="disabled"> | ||
{{ $button }} | ||
</x-jet-button> | ||
</x-slot> | ||
</x-jet-dialog-modal> | ||
@endonce |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
<?php | ||
|
||
namespace Laravel\Jetstream; | ||
|
||
use Illuminate\Contracts\Auth\StatefulGuard; | ||
use Illuminate\Support\Facades\Auth; | ||
use Illuminate\Validation\ValidationException; | ||
use Laravel\Fortify\Actions\ConfirmPassword; | ||
|
||
trait ConfirmsPasswords | ||
{ | ||
/** | ||
* Indicates if the user's password is being confirmed. | ||
* | ||
* @var bool | ||
*/ | ||
public $confirmingPassword = false; | ||
|
||
/** | ||
* The ID of the operation being confirmed. | ||
* | ||
* @var string|null | ||
*/ | ||
public $confirmableId = null; | ||
|
||
/** | ||
* The user's password. | ||
* | ||
* @var string | ||
*/ | ||
public $confirmablePassword = ''; | ||
|
||
/** | ||
* Start confirming the user's password. | ||
* | ||
* @param string $confirmableId | ||
* @return void | ||
*/ | ||
public function startConfirmingPassword(string $confirmableId) | ||
{ | ||
$this->resetErrorBag(); | ||
|
||
if ($this->passwordIsConfirmed()) { | ||
return $this->dispatchBrowserEvent('password-confirmed', [ | ||
'id' => $confirmableId, | ||
]); | ||
} | ||
|
||
$this->confirmingPassword = true; | ||
$this->confirmableId = $confirmableId; | ||
$this->confirmablePassword = ''; | ||
|
||
$this->dispatchBrowserEvent('confirming-password'); | ||
} | ||
|
||
/** | ||
* Stop confirming the user's password. | ||
* | ||
* @return void | ||
*/ | ||
public function stopConfirmingPassword() | ||
{ | ||
$this->confirmingPassword = false; | ||
$this->confirmableId = null; | ||
$this->confirmablePassword = ''; | ||
} | ||
|
||
/** | ||
* Confirm the user's password. | ||
* | ||
* @return void | ||
*/ | ||
public function confirmPassword() | ||
{ | ||
if (! app(ConfirmPassword::class)(app(StatefulGuard::class), Auth::user(), $this->confirmablePassword)) { | ||
throw ValidationException::withMessages([ | ||
'confirmable_password' => [__('This password does not match our records.')], | ||
]); | ||
} | ||
|
||
session(['auth.password_confirmed_at' => time()]); | ||
|
||
$this->dispatchBrowserEvent('password-confirmed', [ | ||
'id' => $this->confirmableId, | ||
]); | ||
|
||
$this->stopConfirmingPassword(); | ||
} | ||
|
||
/** | ||
* Ensure that the user's password has been recently confirmed. | ||
* | ||
* @param int|null $maximumSecondsSinceConfirmation | ||
* @return void | ||
*/ | ||
protected function ensurePasswordIsConfirmed($maximumSecondsSinceConfirmation = null) | ||
{ | ||
$maximumSecondsSinceConfirmation = $maximumSecondsSinceConfirmation ?: config('auth.password_timeout', 900); | ||
|
||
return $this->passwordIsConfirmed($maximumSecondsSinceConfirmation) ? null : abort(403); | ||
} | ||
|
||
/** | ||
* Determine if the user's password has been recently confirmed. | ||
* | ||
* @param int|null $maximumSecondsSinceConfirmation | ||
* @return bool | ||
*/ | ||
protected function passwordIsConfirmed($maximumSecondsSinceConfirmation = null) | ||
{ | ||
$maximumSecondsSinceConfirmation = $maximumSecondsSinceConfirmation ?: config('auth.password_timeout', 900); | ||
|
||
return (time() - session('auth.password_confirmed_at', 0)) < $maximumSecondsSinceConfirmation; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
stubs/inertia/resources/js/Jetstream/ConfirmsPassword.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
<template> | ||
<span> | ||
<span @click="startConfirmingPassword"> | ||
<slot /> | ||
</span> | ||
|
||
<jet-dialog-modal :show="confirmingPassword" @close="confirmingPassword = false"> | ||
<template #title> | ||
{{ title }} | ||
</template> | ||
|
||
<template #content> | ||
{{ content }} | ||
|
||
<div class="mt-4"> | ||
<jet-input type="password" class="mt-1 block w-3/4" placeholder="Password" | ||
ref="password" | ||
v-model="form.password" | ||
@keyup.enter.native="confirmPassword" /> | ||
|
||
<jet-input-error :message="form.error" class="mt-2" /> | ||
</div> | ||
</template> | ||
|
||
<template #footer> | ||
<jet-secondary-button @click.native="confirmingPassword = false"> | ||
Nevermind | ||
</jet-secondary-button> | ||
|
||
<jet-button class="ml-2" @click.native="confirmPassword" :class="{ 'opacity-25': form.processing }" :disabled="form.processing"> | ||
{{ button }} | ||
</jet-button> | ||
</template> | ||
</jet-dialog-modal> | ||
</span> | ||
</template> | ||
|
||
<script> | ||
import JetButton from './Button' | ||
import JetDialogModal from './DialogModal' | ||
import JetInput from './Input' | ||
import JetInputError from './InputError' | ||
import JetSecondaryButton from './SecondaryButton' | ||
export default { | ||
props: { | ||
title: { | ||
default: 'Confirm Password', | ||
}, | ||
content: { | ||
default: 'For your security, please confirm your password to continue.', | ||
}, | ||
button: { | ||
default: 'Confirm', | ||
} | ||
}, | ||
components: { | ||
JetButton, | ||
JetDialogModal, | ||
JetInput, | ||
JetInputError, | ||
JetSecondaryButton, | ||
}, | ||
data() { | ||
return { | ||
confirmingPassword: false, | ||
form: this.$inertia.form({ | ||
password: '', | ||
error: '', | ||
}, { | ||
bag: 'confirmPassword', | ||
}) | ||
} | ||
}, | ||
methods: { | ||
startConfirmingPassword() { | ||
this.form.error = ''; | ||
axios.get('/user/confirmed-password-status').then(response => { | ||
if (response.data.confirmed) { | ||
this.$emit('confirmed'); | ||
} else { | ||
this.confirmingPassword = true; | ||
this.form.password = ''; | ||
setTimeout(() => { | ||
this.$refs.password.focus() | ||
}, 250) | ||
} | ||
}) | ||
}, | ||
confirmPassword() { | ||
this.form.processing = true; | ||
axios.post('/user/confirm-password', { | ||
password: this.form.password, | ||
}).then(response => { | ||
this.confirmingPassword = false; | ||
this.form.password = ''; | ||
this.form.error = ''; | ||
this.form.processing = false; | ||
this.$nextTick(() => this.$emit('confirmed')); | ||
}).catch(error => { | ||
this.form.processing = false; | ||
this.form.error = error.response.data.errors.password[0]; | ||
}); | ||
} | ||
} | ||
} | ||
</script> |
Oops, something went wrong.