Skip to content

Commit

Permalink
Add test for password recovery.
Browse files Browse the repository at this point in the history
  • Loading branch information
edwh committed Nov 18, 2021
1 parent 1a9ee81 commit 5ee4b0a
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 61 deletions.
4 changes: 3 additions & 1 deletion app/Events/PasswordChanged.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ class PasswordChanged
use Dispatchable, InteractsWithSockets, SerializesModels;

public $user;
public $oldPassword;

/**
* Create a new event instance.
*
* @return void
*/
public function __construct(User $user)
public function __construct(User $user, String $oldPassword)
{
$this->user = $user;
$this->oldPassword = $oldPassword;
}
}
100 changes: 44 additions & 56 deletions app/Http/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ public function postProfilePasswordEdit(Request $request)
}

if ($request->input('new-password') == $request->input('new-password-repeat') && Hash::check($request->input('current-password'), $user->password)) {
$oldPassword = $user->password;
$user->setPassword(Hash::make($request->input('new-password')));
$user->save();

Expand All @@ -212,7 +213,7 @@ public function postProfilePasswordEdit(Request $request)
'recovery_expires' => strftime('%Y-%m-%d %X', time() + (24 * 60 * 60)),
]);

event(new PasswordChanged($user));
event(new PasswordChanged($user, $oldPassword));

return redirect()->back()->with('message', 'User Password Updated!');
}
Expand Down Expand Up @@ -421,17 +422,15 @@ public function postEdit(Request $request)
return redirect()->back()->with('success', 'Profile updated');
}

/**
* @ToDo : test and delete commented
*/
public function recover()
public function recover(Request $request)
{
$User = new User;

if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST' && isset($_POST['email']) && ! empty($_POST['email'])) {
$email = $_POST['email'];
$email = $request->get('email');

if ($request->getMethod() == 'POST' && $email) {
if (empty($email) || ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
$response['danger'] = 'Please input a <strong>valid</strong> email.';
$response['danger'] = __('passwords.invalid');
} else {
$user = $User->where('email', $email)->first();

Expand All @@ -448,39 +447,41 @@ public function recover()

// update record
$user->update([
'recovery' => $data['recovery'],
'recovery_expires' => $data['recovery_expires'],
'recovery' => $data['recovery'],
'recovery_expires' => $data['recovery_expires'],
]);

User::find($id)->notify(new ResetPassword([
'url' => env('APP_URL').'/user/reset?recovery='.$data['recovery'],
]));

$response['success'] = 'Email Sent! Please check your inbox and follow instructions';
$response['success'] = __('passwords.sent');
} else {
$response['danger'] = 'This email is not in our database.';
$response['danger'] = __('passwords.user');
}
}

return view('auth.forgot-password', [//user.recover
'title' => 'Account recovery',
'response' => $response,
'title' => __('passwords.recover_title'),
'response' => $response,
]);
}

return view('auth.forgot-password', [//user.recover
'title' => 'Account recovery',
'title' => __('passwords.recover_title'),
]);
}

public function reset()
public function reset(Request $request)
{
$User = new User;

if (! isset($_GET['recovery']) || empty($_GET['recovery'])) {
$recovery = $request->post('recovery');

if (!$recovery) {
$valid_code = false;
} else {
$recovery = filter_var($_GET['recovery'], FILTER_SANITIZE_STRING);
$recovery = filter_var($recovery, FILTER_SANITIZE_STRING);
$user = $User->where('recovery', '=', $recovery)->first();

if (is_object($user) && strtotime($user->recovery_expires) > time()) {
Expand All @@ -490,52 +491,39 @@ public function reset()
}
}

if (strtoupper($_SERVER['REQUEST_METHOD']) == 'POST' && isset($_POST['password']) && ! empty($_POST['password']) && isset($_POST['confirm_password']) && ! empty($_POST['confirm_password'])) {
$recovery = $_POST['recovery'];
$pwd = $_POST['password'];
$cpwd = $_POST['confirm_password'];
if (empty($recovery) || ! filter_var($recovery, FILTER_SANITIZE_STRING)) {
$response['danger'] = 'Recovery code invalid.';
$pwd = $request->post('password');
$cpwd = $request->post('confirm_password');
$response = null;
$email = null;

if ($request->getMethod() == 'POST' && $pwd && $cpwd) {
if (!$valid_code) {
$response['danger'] = __('passwords.token');
} elseif ($pwd !== $cpwd) {
$response['danger'] = 'The passwords do not match';
$response['danger'] = __('passwords.match');
} else {
$user = $User->where('recovery', '=', $recovery)->first();
if (! empty($user)) {
$data = [
$email = $user->email;
$oldPassword = $user->password;

$update = $user->update([
'password' => crypt($pwd, '$1$'.strrev(md5(env('APP_KEY')))),
];
$update = $user->update($data);
if ($update) {
return redirect('login')->with('success', 'Password updated, please login to continue');
} else {
$response['danger'] = 'Could not update the password.';
}
]);

if ($update) {
event(new PasswordChanged($user, $oldPassword));
return redirect('login')->with('success', __('passwords.updated'));
} else {
$response['danger'] = 'No account matches the recovery code';
$response['danger'] = __('passwords.failed');
}
}
}

if (! isset($recovery)) {
$recovery = null;
}

if (! isset($response)) {
$response = null;
}

if (isset($user)) {
$email = $user->email;
} else {
$email = null;
}

return view('auth.reset-password', [//user.reset
'title' => 'Account recovery',
'recovery' => $recovery,
'valid_code' => $valid_code,
'response' => $response,
'email' => $email,
return view('auth.reset-password', [
'title' => 'Account recovery',
'recovery' => $recovery,
'valid_code' => $valid_code,
'response' => $response,
'email' => $email,
]);
}

Expand Down
9 changes: 6 additions & 3 deletions app/Listeners/ChangeWikiPassword.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public function __construct(Request $request)
public function handle(PasswordChanged $event)
{
$user = $event->user;
$oldpw = $event->oldPassword;

if ($user->wiki_sync_status !== WikiSyncStatus::Created) {
Log::info("No wiki account for '".$user->username."' - not attempting to change password");
Expand All @@ -42,14 +43,16 @@ public function handle(PasswordChanged $event)

try {
$api = MediawikiApi::newFromApiEndpoint(env('WIKI_URL').'/api.php');
$api->login(new ApiUser($user->mediawiki, $this->request->input('current-password')));

$api->login(new ApiUser($user->mediawiki, $oldpw));
$token = $api->getToken('csrf');

// The Mediawiki new password is the Laravel hashed password.
$changePasswordRequest = FluentRequest::factory()
->setAction('changeauthenticationdata')
->setParam('changeauthrequest', 'MediaWiki\Auth\PasswordAuthenticationRequest')
->setParam('password', $this->request->input('new-password'))
->setParam('retype', $this->request->input('new-password'))
->setParam('password', $user->password)
->setParam('retype', $user->password)
->setParam('changeauthtoken', $token);
$api->postRequest($changePasswordRequest);
} catch (\Exception $ex) {
Expand Down
6 changes: 5 additions & 1 deletion resources/lang/en/passwords.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,9 @@
'throttled' => 'Please wait before retrying.',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that e-mail address.",

'invalid' => 'Please input a valid email.',
'recover_title' => 'Account Recovery',
'match' => 'The passwords do not match',
'updated' => 'Password updated, please login to continue.',
'failed' => 'Could not update the password.'
];
85 changes: 85 additions & 0 deletions tests/Feature/Users/PasswordResetTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php

namespace Tests\Feature;

use App\Notifications\ResetPassword;
use App\User;
use DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Notification;
use Tests\TestCase;

class PasswordResetTest extends TestCase
{
public function testInvalidEmail() {
$response = $this->post('/user/recover', [
'email' => 'bademail!'
]);

$response->assertSeeText(__('passwords.invalid'));
}

public function testUnknownEmail() {
$response = $this->post('/user/recover', [
'email' => 'nobody@nowhere.com'
]);

$response->assertSeeText(__('passwords.user'));
}

public function testResetSuccess()
{
Notification::fake();
Event::fake();

$restarter = factory(User::class)->states('Restarter')->create();

$response = $this->post('/user/recover', [
'email' => $restarter->email
]);

$response->assertSeeText(__('passwords.sent'));

Notification::assertSentTo(
[$restarter], ResetPassword::class
);

$restarter->refresh();

// Invalid code
$response = $this->post('/user/reset', [
'recovery' => '',
'password' => 'newpass',
'confirm_password' => 'newpass'
]);

// Invalid code
$response = $this->post('/user/reset', [
'recovery' => $restarter->recovery . '1',
'password' => 'newpass',
'confirm_password' => 'newpass'
]);

$response->assertSeeText('using is invalid');

// Valid but mismatch passwords.
$response = $this->post('/user/reset', [
'recovery' => $restarter->recovery,
'password' => 'newpass',
'confirm_password' => 'mismatch'
]);

$response->assertSeeText(__('passwords.match'));

// Valid - should redirect to login patch and dispatch password changed event to update the wiki.
$this->followingRedirects();
$response = $this->post('/user/reset', [
'recovery' => $restarter->recovery,
'password' => 'newpass',
'confirm_password' => 'newpass'
]);

$response->assertSeeText(__('passwords.updated'));
Event::assertDispatched(\App\Events\PasswordChanged::class);
}
}

0 comments on commit 5ee4b0a

Please sign in to comment.