Use random generated token for password resetting
This commit is contained in:
parent
e2575f8a9a
commit
0e70a88e13
|
|
@ -198,8 +198,7 @@ class AuthController extends Controller
|
|||
|
||||
$uid = $user->uid;
|
||||
// Generate token for password resetting
|
||||
$token = base64_encode($user->getToken().substr(time(), 4, 6).str_random(16));
|
||||
|
||||
$token = generate_random_token();
|
||||
$url = Option::get('site_url')."/auth/reset?uid=$uid&token=$token";
|
||||
|
||||
try {
|
||||
|
|
@ -216,6 +215,7 @@ class AuthController extends Controller
|
|||
return json(trans('auth.forgot.failed', ['msg' => $e->getMessage()]), 2);
|
||||
}
|
||||
|
||||
Cache::put("pwd_reset_token_$uid", $token, 60);
|
||||
Cache::put($lastMailCacheKey, time(), 60);
|
||||
|
||||
return json(trans('auth.forgot.success'), 0);
|
||||
|
|
@ -223,31 +223,23 @@ class AuthController extends Controller
|
|||
|
||||
public function reset(UserRepository $users, Request $request)
|
||||
{
|
||||
if ($request->has('uid') && $request->has('token')) {
|
||||
// Get user instance from repository
|
||||
$user = $users->get($request->input('uid'));
|
||||
// Retrieve token from cache
|
||||
$uid = $request->get('uid');
|
||||
$token = Cache::get("pwd_reset_token_$uid");
|
||||
|
||||
if (! $user)
|
||||
return redirect('auth/forgot')->with('msg', trans('auth.reset.invalid'));
|
||||
// Get user instance from repository
|
||||
$user = $users->get($uid);
|
||||
|
||||
// Unpack to get user token & timestamp
|
||||
$decoded = base64_decode($request->input('token'));
|
||||
$token = substr($decoded, 0, -22);
|
||||
$timestamp = substr($decoded, strlen($token), 6);
|
||||
|
||||
if ($user->getToken() != $token) {
|
||||
return redirect('auth/forgot')->with('msg', trans('auth.reset.invalid'));
|
||||
}
|
||||
|
||||
// More than 1 hour
|
||||
if ((substr(time(), 4, 6) - $timestamp) > 3600) {
|
||||
return redirect('auth/forgot')->with('msg', trans('auth.reset.expired'));
|
||||
}
|
||||
|
||||
return view('auth.reset')->with('user', $user);
|
||||
} else {
|
||||
return redirect('auth/login')->with('msg', trans('auth.check.anonymous'));
|
||||
if (! $user) {
|
||||
return redirect('auth/forgot')->with('msg', trans('auth.reset.invalid'));
|
||||
}
|
||||
|
||||
// No token exist or token mismatch (maybe expired)
|
||||
if (is_null($token) || $token != $request->get('token')) {
|
||||
return redirect('auth/forgot')->with('msg', trans('auth.reset.expired'));
|
||||
}
|
||||
|
||||
return view('auth.reset')->with('user', $user);
|
||||
}
|
||||
|
||||
public function handleReset(Request $request, UserRepository $users)
|
||||
|
|
@ -258,26 +250,23 @@ class AuthController extends Controller
|
|||
'token' => 'required',
|
||||
]);
|
||||
|
||||
$decoded = base64_decode($request->input('token'));
|
||||
$token = substr($decoded, 0, -22);
|
||||
$timestamp = intval(substr($decoded, strlen($token), 6));
|
||||
// Retrieve token from cache
|
||||
$uid = $request->get('uid');
|
||||
$token = Cache::get("pwd_reset_token_$uid");
|
||||
|
||||
$user = $users->get($request->input('uid'));
|
||||
if (! $user)
|
||||
return json(trans('auth.reset.invalid'), 1);
|
||||
// Get user instance from repository
|
||||
$user = $users->get($uid);
|
||||
|
||||
if ($user->getToken() != $token) {
|
||||
if (! $user) {
|
||||
return json(trans('auth.reset.invalid'), 1);
|
||||
}
|
||||
|
||||
// More than 1 hour
|
||||
if ((intval(substr(time(), 4, 6)) - $timestamp) > 3600) {
|
||||
// No token exist or token mismatch (maybe expired)
|
||||
if (is_null($token) || $token != $request->get('token')) {
|
||||
return json(trans('auth.reset.expired'), 1);
|
||||
}
|
||||
|
||||
$users->get($request->input('uid'))->changePasswd($request->input('password'));
|
||||
|
||||
Log::info("[Password Reset] Password of user [{$request->input('uid')}] has been changed");
|
||||
$user->changePasswd($request->get('password'));
|
||||
|
||||
return json(trans('auth.reset.success'), 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ reset:
|
|||
title: Reset Password
|
||||
button: Reset
|
||||
invalid: Invalid link.
|
||||
expired: This link is expired.
|
||||
message: :username, reset your email address here.
|
||||
success: Password resetted successfully.
|
||||
expired: This link is expired, please resend a verification email.
|
||||
message: :username, reset your password here.
|
||||
success: Your password was reset successfully.
|
||||
|
||||
bind:
|
||||
title: Bind Email
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ reset:
|
|||
title: 重置密码
|
||||
button: 重置
|
||||
invalid: 无效的链接
|
||||
expired: 链接已过期
|
||||
expired: 链接已失效,请重新发送验证邮件
|
||||
message: :username,在这里重置你的密码
|
||||
success: 密码重置成功
|
||||
|
||||
|
|
|
|||
|
|
@ -449,11 +449,8 @@ class AuthControllerTest extends TestCase
|
|||
'msg' => trans('auth.forgot.unregistered')
|
||||
]);
|
||||
|
||||
$uid = $user->uid;
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time(), 4, 6).str_random(16)
|
||||
);
|
||||
$url = Option::get('site_url')."/auth/reset?uid=$uid&token=$token";
|
||||
$token = generate_random_token();
|
||||
$url = Option::get('site_url')."/auth/reset?uid={$user->uid}&token=$token";
|
||||
// An email should be send
|
||||
// Laravel supports `Mail::fake()` since v5.4, but now we cannot
|
||||
// Thanks: https://stackoverflow.com/questions/31120567/unittesting-laravel-5-mail-using-mock
|
||||
|
|
@ -490,7 +487,10 @@ class AuthControllerTest extends TestCase
|
|||
])->seeJson([
|
||||
'errno' => 0,
|
||||
'msg' => trans('auth.forgot.success')
|
||||
])->assertCacheHas($lastMailCacheKey);
|
||||
])->assertCacheHas([
|
||||
$lastMailCacheKey,
|
||||
"pwd_reset_token_{$user->uid}"
|
||||
]);
|
||||
$this->flushCache();
|
||||
|
||||
// Should handle exception when sending email
|
||||
|
|
@ -514,32 +514,23 @@ class AuthControllerTest extends TestCase
|
|||
|
||||
// Should be redirected if `uid` or `token` is empty
|
||||
$this->visit('/auth/reset')
|
||||
->seePageIs('/auth/login')
|
||||
->see(trans('auth.check.anonymous'));
|
||||
->seePageIs('/auth/forgot')
|
||||
->see(trans('auth.reset.invalid'));
|
||||
|
||||
// Should be redirected if `uid` is invalid
|
||||
$this->visit('/auth/reset?uid=-1&token=nothing')
|
||||
->seePageIs('/auth/forgot')
|
||||
->see(trans('auth.reset.invalid'));
|
||||
|
||||
// Should be redirected if `token` is invalid
|
||||
$this->visit('/auth/reset?uid=' . $user->uid . '&token=nothing')
|
||||
->seePageIs('/auth/forgot')
|
||||
->see(trans('auth.reset.invalid'));
|
||||
|
||||
// Should be redirected if expired
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time() - 60 * 60 * 2, 4, 6).str_random(16)
|
||||
);
|
||||
$this->visit('/auth/reset?uid=' . $user->uid . '&token=' . $token)
|
||||
// Should be redirected if `token` is invalid or expired
|
||||
$this->visit("/auth/reset?uid={$user->uid}&token=nothing")
|
||||
->seePageIs('/auth/forgot')
|
||||
->see(trans('auth.reset.expired'));
|
||||
|
||||
// Success
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time(), 4, 6).str_random(16)
|
||||
);
|
||||
$uri = $this->visit('/auth/reset?uid=' . $user->uid . '&token=' . $token)
|
||||
$token = generate_random_token();
|
||||
$uri = $this->withCache(["pwd_reset_token_{$user->uid}" => $token])
|
||||
->visit("/auth/reset?uid={$user->uid}&token=$token")
|
||||
->currentUri;
|
||||
$this->assertContains('/auth/reset', $uri);
|
||||
}
|
||||
|
|
@ -611,24 +602,9 @@ class AuthControllerTest extends TestCase
|
|||
'msg' => trans('validation.required', ['attribute' => 'token'])
|
||||
]);
|
||||
|
||||
// Should be forbidden if expired
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time() - 60 * 60 * 2, 4, 6).str_random(16)
|
||||
);
|
||||
$this->post(
|
||||
'/auth/reset', [
|
||||
'uid' => $user->uid,
|
||||
'password' => '12345678',
|
||||
'token' => $token
|
||||
])->seeJson([
|
||||
'errno' => 1,
|
||||
'msg' => trans('auth.reset.expired')
|
||||
]);
|
||||
$token = generate_random_token();
|
||||
|
||||
// Should return a warning if the user is not existed
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time(), 4, 6).str_random(16)
|
||||
);
|
||||
$this->post(
|
||||
'/auth/reset', [
|
||||
'uid' => -1,
|
||||
|
|
@ -639,22 +615,23 @@ class AuthControllerTest extends TestCase
|
|||
'msg' => trans('auth.reset.invalid')
|
||||
]);
|
||||
|
||||
// Should be forbidden if `token` is invalid
|
||||
$this->post(
|
||||
// Should be forbidden if `token` is invalid or expired
|
||||
$this->withCache(
|
||||
["pwd_reset_token_{$user->uid}" => $token]
|
||||
)->post(
|
||||
'/auth/reset', [
|
||||
'uid' => $user->uid,
|
||||
'password' => '12345678',
|
||||
'token' => 'invalid'
|
||||
'token' => 'something-else'
|
||||
])->seeJson([
|
||||
'errno' => 1,
|
||||
'msg' => trans('auth.reset.invalid')
|
||||
'msg' => trans('auth.reset.expired')
|
||||
]);
|
||||
|
||||
// Success
|
||||
$token = base64_encode(
|
||||
$user->getToken().substr(time(), 4, 6).str_random(16)
|
||||
);
|
||||
$this->post(
|
||||
$this->withCache(
|
||||
["pwd_reset_token_{$user->uid}" => $token]
|
||||
)->post(
|
||||
'/auth/reset', [
|
||||
'uid' => $user->uid,
|
||||
'password' => '12345678',
|
||||
|
|
@ -663,11 +640,9 @@ class AuthControllerTest extends TestCase
|
|||
'errno' => 0,
|
||||
'msg' => trans('auth.reset.success')
|
||||
]);
|
||||
// We must re-query the user model,
|
||||
// because the old instance hasn't been changed
|
||||
// after resetting password.
|
||||
$user = User::find($user->uid);
|
||||
$this->assertTrue($user->verifyPassword('12345678'));
|
||||
|
||||
// Re-fetch user data and verify it
|
||||
$this->assertTrue($user->fresh()->verifyPassword('12345678'));
|
||||
}
|
||||
|
||||
public function testVerify()
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user