Skip to content

Commit 7eb938b

Browse files
authored
Merge pull request #3631 from codeeu/fix/password-max-length
edit to online course page
2 parents 17bb68f + b310317 commit 7eb938b

6 files changed

Lines changed: 116 additions & 1 deletion

File tree

app/Http/Controllers/UserEmailChangeController.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,24 @@ public function resend(Request $request): RedirectResponse
7777
'We sent another confirmation link to '.$user->fresh()->pending_email.'. Check that inbox (and spam folder).',
7878
);
7979
}
80+
81+
public function confirmHere(Request $request): RedirectResponse
82+
{
83+
$user = $request->user();
84+
85+
$validated = $request->validate([
86+
'confirm_password' => $this->emailChangeService->requiresPassword($user)
87+
? 'required|string|max:72'
88+
: 'nullable|string|max:72',
89+
]);
90+
91+
$updatedUser = $this->emailChangeService->confirmPendingForAuthenticatedUser(
92+
$user,
93+
$validated['confirm_password'] ?? null,
94+
);
95+
96+
return redirect()
97+
->route('profile')
98+
->with('flash', 'Your login email has been updated to '.$updatedUser->email.'.');
99+
}
80100
}

app/Services/UserEmailChangeService.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,44 @@ public function resendConfirmation(User $user): array
8989
return ['status' => 'confirmation_resent'];
9090
}
9191

92+
/**
93+
* Confirm a pending change while logged in (fallback when the new inbox does not receive mail).
94+
*
95+
* @throws ValidationException
96+
*/
97+
public function confirmPendingForAuthenticatedUser(User $user, ?string $password): User
98+
{
99+
$pendingEmail = SupportEmailAddress::normalize((string) ($user->pending_email ?? ''));
100+
if ($pendingEmail === null || $user->pending_email_token === null) {
101+
throw ValidationException::withMessages([
102+
'pending_email' => 'There is no pending email change to confirm.',
103+
]);
104+
}
105+
106+
if ($this->requiresPassword($user)) {
107+
if ($password === null || $password === '' || ! Hash::check($password, (string) $user->password)) {
108+
throw ValidationException::withMessages([
109+
'confirm_password' => 'Your current password is incorrect.',
110+
]);
111+
}
112+
}
113+
114+
if ($this->emailInUseByAnotherUser($pendingEmail, (int) $user->id)) {
115+
$this->cancelPending($user);
116+
117+
throw ValidationException::withMessages([
118+
'new_email' => 'That email address is already in use on another CodeWeek account.',
119+
]);
120+
}
121+
122+
return DB::transaction(function () use ($user, $pendingEmail) {
123+
$oldEmail = SupportEmailAddress::normalize((string) ($user->email ?? ''));
124+
$this->applyEmailChange($user, $oldEmail, $pendingEmail);
125+
126+
return $user->fresh();
127+
});
128+
}
129+
92130
private function issuePendingToken(User $user): string
93131
{
94132
$token = Str::random(64);

resources/views/emails/en/pending-email-change-confirmation.blade.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
Confirm new login email
1010
@endcomponent
1111

12+
If the button does not work, copy and paste this link into your browser:
13+
14+
{{ $confirmUrl }}
15+
1216
If you did not request this change, you can ignore this email.
1317

1418
Thanks,<br>

resources/views/profile.blade.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class="border-2 border-solid border-dark-blue-200 w-full rounded-full h-12 px-4
7474
<p class="mb-4 text-base text-slate-500">
7575
A separate notice was sent to {{ $profileUser->email }} — that email does not contain the confirmation link.
7676
</p>
77-
<div class="flex flex-col tablet:flex-row gap-4">
77+
<div class="flex flex-col tablet:flex-row gap-4 mb-6">
7878
<form method="POST" action="{{ route('user.email-change.resend') }}">
7979
{{ csrf_field() }}
8080
<button type="submit" class="bg-dark-blue text-white rounded-full py-2.5 px-6 font-semibold text-base">
@@ -88,6 +88,29 @@ class="border-2 border-solid border-dark-blue-200 w-full rounded-full h-12 px-4
8888
</button>
8989
</form>
9090
</div>
91+
<div class="border-t border-dark-blue-200 pt-4">
92+
<p class="mb-3 font-medium text-dark-blue">Can't find the email at {{ $profileUser->pending_email }}?</p>
93+
<p class="mb-4 text-base text-slate-500">
94+
If you can access that inbox elsewhere (for example a shared team mailbox), check there first.
95+
Otherwise, while you are signed in here, you can confirm the change on this page.
96+
</p>
97+
<form method="POST" action="{{ route('user.email-change.confirm-here') }}">
98+
{{ csrf_field() }}
99+
@if (empty($profileUser->provider))
100+
<div class="mb-3">
101+
<label class="block text-lg text-slate-500 mb-2" for="confirm_password">Current password *</label>
102+
<input id="confirm_password" type="password" name="confirm_password"
103+
class="border-2 border-solid border-dark-blue-200 w-full rounded-full h-12 px-4 appearance-none text-slate-600"
104+
required autocomplete="current-password">
105+
@component('components.validation-errors', ['field'=>'confirm_password'])@endcomponent
106+
</div>
107+
@endif
108+
<button type="submit"
109+
class="bg-primary hover:bg-hover-orange duration-300 text-[#20262C] rounded-full py-2.5 px-6 font-semibold text-base">
110+
Confirm change to {{ $profileUser->pending_email }}
111+
</button>
112+
</form>
113+
</div>
91114
</div>
92115
@else
93116
<div class="border-2 border-solid border-dark-blue-200 rounded-xl px-5 py-4">

routes/web.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,9 @@
500500
Route::post('user/email-change/resend', [UserEmailChangeController::class, 'resend'])
501501
->name('user.email-change.resend')
502502
->middleware(['auth', 'throttle:6,1']);
503+
Route::post('user/email-change/confirm-here', [UserEmailChangeController::class, 'confirmHere'])
504+
->name('user.email-change.confirm-here')
505+
->middleware(['auth', 'throttle:6,1']);
503506
Route::get('email/change/confirm/{user}/{token}', [UserEmailChangeController::class, 'confirm'])
504507
->name('user.email-change.confirm')
505508
->middleware('signed');

tests/Feature/UserEmailChangeTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,33 @@ public function test_user_can_resend_pending_confirmation_email(): void
138138
Mail::assertNotSent(PendingEmailChangeNotification::class);
139139
}
140140

141+
public function test_user_can_confirm_pending_change_from_profile(): void
142+
{
143+
$user = User::factory()->create([
144+
'email' => 'old@example.com',
145+
'email_display' => 'old@example.com',
146+
'provider' => null,
147+
'password' => bcrypt('correct-password'),
148+
'pending_email' => 'new@example.com',
149+
'pending_email_token' => hash('sha256', 'pending-token'),
150+
'pending_email_requested_at' => now(),
151+
'email_verified_at' => now(),
152+
]);
153+
154+
$this->signIn($user);
155+
156+
$response = $this->post(route('user.email-change.confirm-here'), [
157+
'confirm_password' => 'correct-password',
158+
]);
159+
160+
$response->assertRedirect(route('profile'));
161+
$response->assertSessionHas('flash');
162+
163+
$user->refresh();
164+
$this->assertSame('new@example.com', $user->email);
165+
$this->assertNull($user->pending_email);
166+
}
167+
141168
public function test_profile_update_still_cannot_change_login_email_directly(): void
142169
{
143170
$user = User::factory()->create([

0 commit comments

Comments
 (0)