mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-28 23:24:06 +08:00
Fixed registations bugs and removed email confirmation model
This commit is contained in:
parent
39e81d9a1f
commit
fbfc25fc21
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ Homestead.yaml
|
|||||||
/storage/images
|
/storage/images
|
||||||
_ide_helper.php
|
_ide_helper.php
|
||||||
/storage/debugbar
|
/storage/debugbar
|
||||||
|
.phpstorm.meta.php
|
@ -1,29 +0,0 @@
|
|||||||
<?php namespace BookStack;
|
|
||||||
|
|
||||||
use Illuminate\Notifications\Notifiable;
|
|
||||||
|
|
||||||
class EmailConfirmation extends Model
|
|
||||||
{
|
|
||||||
use Notifiable;
|
|
||||||
|
|
||||||
protected $fillable = ['user_id', 'token'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user that this confirmation is attached to.
|
|
||||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
||||||
*/
|
|
||||||
public function user()
|
|
||||||
{
|
|
||||||
return $this->belongsTo(User::class);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the routing for mail notifications.
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function routeNotificationForMail()
|
|
||||||
{
|
|
||||||
return $this->user->email;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace BookStack\Http\Controllers\Auth;
|
namespace BookStack\Http\Controllers\Auth;
|
||||||
|
|
||||||
|
use BookStack\Exceptions\ConfirmationEmailException;
|
||||||
|
use BookStack\Exceptions\UserRegistrationException;
|
||||||
use BookStack\Repos\UserRepo;
|
use BookStack\Repos\UserRepo;
|
||||||
use BookStack\Services\EmailConfirmationService;
|
use BookStack\Services\EmailConfirmationService;
|
||||||
use BookStack\Services\SocialAuthService;
|
use BookStack\Services\SocialAuthService;
|
||||||
@ -158,7 +160,7 @@ class RegisterController extends Controller
|
|||||||
* @param bool|false|SocialAccount $socialAccount
|
* @param bool|false|SocialAccount $socialAccount
|
||||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
* @throws UserRegistrationException
|
* @throws UserRegistrationException
|
||||||
* @throws \BookStack\Exceptions\ConfirmationEmailException
|
* @throws ConfirmationEmailException
|
||||||
*/
|
*/
|
||||||
protected function registerUser(array $userData, $socialAccount = false)
|
protected function registerUser(array $userData, $socialAccount = false)
|
||||||
{
|
{
|
||||||
@ -195,18 +197,6 @@ class RegisterController extends Controller
|
|||||||
return view('auth/register-confirm');
|
return view('auth/register-confirm');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* View the confirmation email as a standard web page.
|
|
||||||
* @param $token
|
|
||||||
* @return \Illuminate\View\View
|
|
||||||
* @throws UserRegistrationException
|
|
||||||
*/
|
|
||||||
public function viewConfirmEmail($token)
|
|
||||||
{
|
|
||||||
$confirmation = $this->emailConfirmationService->getEmailConfirmationFromToken($token);
|
|
||||||
return view('emails/email-confirmation', ['token' => $confirmation->token]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Confirms an email via a token and logs the user into the system.
|
* Confirms an email via a token and logs the user into the system.
|
||||||
* @param $token
|
* @param $token
|
||||||
@ -219,7 +209,7 @@ class RegisterController extends Controller
|
|||||||
$user = $confirmation->user;
|
$user = $confirmation->user;
|
||||||
$user->email_confirmed = true;
|
$user->email_confirmed = true;
|
||||||
$user->save();
|
$user->save();
|
||||||
auth()->login($confirmation->user);
|
auth()->login($user);
|
||||||
session()->flash('success', 'Your email has been confirmed!');
|
session()->flash('success', 'Your email has been confirmed!');
|
||||||
$this->emailConfirmationService->deleteConfirmationsByUser($user);
|
$this->emailConfirmationService->deleteConfirmationsByUser($user);
|
||||||
return redirect($this->redirectPath);
|
return redirect($this->redirectPath);
|
||||||
|
@ -33,7 +33,7 @@ class Authenticate
|
|||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) {
|
if ($this->auth->check() && setting('registration-confirmation') && !$this->auth->user()->email_confirmed) {
|
||||||
return redirect()->guest(baseUrl('/register/confirm/awaiting'));
|
return redirect(baseUrl('/register/confirm/awaiting'));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->auth->guest() && !setting('app-public')) {
|
if ($this->auth->guest() && !setting('app-public')) {
|
||||||
|
@ -34,7 +34,8 @@ class RedirectIfAuthenticated
|
|||||||
*/
|
*/
|
||||||
public function handle($request, Closure $next)
|
public function handle($request, Closure $next)
|
||||||
{
|
{
|
||||||
if ($this->auth->check()) {
|
$requireConfirmation = setting('registration-confirmation');
|
||||||
|
if ($this->auth->check() && (!$requireConfirmation || ($requireConfirmation && $this->auth->user()->email_confirmed))) {
|
||||||
return redirect('/');
|
return redirect('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,13 +8,15 @@ use Illuminate\Notifications\Messages\MailMessage;
|
|||||||
class ConfirmEmail extends Notification
|
class ConfirmEmail extends Notification
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public $token;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new notification instance.
|
* Create a new notification instance.
|
||||||
*
|
* @param string $token
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct($token)
|
||||||
{
|
{
|
||||||
//
|
$this->token = $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +42,7 @@ class ConfirmEmail extends Notification
|
|||||||
->subject('Confirm your email on ' . session('app-name'))
|
->subject('Confirm your email on ' . session('app-name'))
|
||||||
->greeting('Thanks for joining ' . setting('app-name') . '!')
|
->greeting('Thanks for joining ' . setting('app-name') . '!')
|
||||||
->line('Please confirm your email address by clicking the button below:')
|
->line('Please confirm your email address by clicking the button below:')
|
||||||
->action('Confirm Email', baseUrl('/register/confirm/' . $notifiable->token));
|
->action('Confirm Email', baseUrl('/register/confirm/' . $this->token));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
50
app/Notifications/ResetPassword.php
Normal file
50
app/Notifications/ResetPassword.php
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace BookStack\Notifications;
|
||||||
|
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
|
||||||
|
class ResetPassword extends Notification
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The password reset token.
|
||||||
|
*
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public $token;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a notification instance.
|
||||||
|
*
|
||||||
|
* @param string $token
|
||||||
|
*/
|
||||||
|
public function __construct($token)
|
||||||
|
{
|
||||||
|
$this->token = $token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the notification's channels.
|
||||||
|
*
|
||||||
|
* @param mixed $notifiable
|
||||||
|
* @return array|string
|
||||||
|
*/
|
||||||
|
public function via($notifiable)
|
||||||
|
{
|
||||||
|
return ['mail'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the mail representation of the notification.
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Notifications\Messages\MailMessage
|
||||||
|
*/
|
||||||
|
public function toMail()
|
||||||
|
{
|
||||||
|
return (new MailMessage)
|
||||||
|
->line('You are receiving this email because we received a password reset request for your account.')
|
||||||
|
->action('Reset Password', baseUrl('password/reset/' . $this->token))
|
||||||
|
->line('If you did not request a password reset, no further action is required.');
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,27 @@
|
|||||||
<?php namespace BookStack\Services;
|
<?php namespace BookStack\Services;
|
||||||
|
|
||||||
use BookStack\Notifications\ConfirmEmail;
|
use BookStack\Notifications\ConfirmEmail;
|
||||||
|
use BookStack\Repos\UserRepo;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use BookStack\EmailConfirmation;
|
|
||||||
use BookStack\Exceptions\ConfirmationEmailException;
|
use BookStack\Exceptions\ConfirmationEmailException;
|
||||||
use BookStack\Exceptions\UserRegistrationException;
|
use BookStack\Exceptions\UserRegistrationException;
|
||||||
use BookStack\User;
|
use BookStack\User;
|
||||||
|
use Illuminate\Database\Connection as Database;
|
||||||
|
|
||||||
class EmailConfirmationService
|
class EmailConfirmationService
|
||||||
{
|
{
|
||||||
protected $emailConfirmation;
|
protected $db;
|
||||||
|
protected $users;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* EmailConfirmationService constructor.
|
* EmailConfirmationService constructor.
|
||||||
* @param EmailConfirmation $emailConfirmation
|
* @param Database $db
|
||||||
|
* @param UserRepo $users
|
||||||
*/
|
*/
|
||||||
public function __construct(EmailConfirmation $emailConfirmation)
|
public function __construct(Database $db, UserRepo $users)
|
||||||
{
|
{
|
||||||
$this->emailConfirmation = $emailConfirmation;
|
$this->db = $db;
|
||||||
|
$this->users = $users;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,13 +37,26 @@ class EmailConfirmationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->deleteConfirmationsByUser($user);
|
$this->deleteConfirmationsByUser($user);
|
||||||
|
$token = $this->createEmailConfirmation($user);
|
||||||
|
|
||||||
|
$user->notify(new ConfirmEmail($token));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new email confirmation in the database and returns the token.
|
||||||
|
* @param User $user
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function createEmailConfirmation(User $user)
|
||||||
|
{
|
||||||
$token = $this->getToken();
|
$token = $this->getToken();
|
||||||
$confirmation = $this->emailConfirmation->create([
|
$this->db->table('email_confirmations')->insert([
|
||||||
'user_id' => $user->id,
|
'user_id' => $user->id,
|
||||||
'token' => $token,
|
'token' => $token,
|
||||||
|
'created_at' => Carbon::now(),
|
||||||
|
'updated_at' => Carbon::now()
|
||||||
]);
|
]);
|
||||||
|
return $token;
|
||||||
$confirmation->notify(new ConfirmEmail());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,22 +68,24 @@ class EmailConfirmationService
|
|||||||
*/
|
*/
|
||||||
public function getEmailConfirmationFromToken($token)
|
public function getEmailConfirmationFromToken($token)
|
||||||
{
|
{
|
||||||
$emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first();
|
$emailConfirmation = $this->db->table('email_confirmations')->where('token', '=', $token)->first();
|
||||||
// If not found
|
|
||||||
|
// If not found show error
|
||||||
if ($emailConfirmation === null) {
|
if ($emailConfirmation === null) {
|
||||||
throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register');
|
throw new UserRegistrationException('This confirmation token is not valid or has already been used, Please try registering again.', '/register');
|
||||||
}
|
}
|
||||||
|
|
||||||
// If more than a day old
|
// If more than a day old
|
||||||
if (Carbon::now()->subDay()->gt($emailConfirmation->created_at)) {
|
if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) {
|
||||||
$this->sendConfirmation($emailConfirmation->user);
|
$user = $this->users->getById($emailConfirmation->user_id);
|
||||||
|
$this->sendConfirmation($user);
|
||||||
throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
|
throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$emailConfirmation->user = $this->users->getById($emailConfirmation->user_id);
|
||||||
return $emailConfirmation;
|
return $emailConfirmation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete all email confirmations that belong to a user.
|
* Delete all email confirmations that belong to a user.
|
||||||
* @param User $user
|
* @param User $user
|
||||||
@ -74,7 +93,7 @@ class EmailConfirmationService
|
|||||||
*/
|
*/
|
||||||
public function deleteConfirmationsByUser(User $user)
|
public function deleteConfirmationsByUser(User $user)
|
||||||
{
|
{
|
||||||
return $this->emailConfirmation->where('user_id', '=', $user->id)->delete();
|
return $this->db->table('email_confirmations')->where('user_id', '=', $user->id)->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,7 +103,7 @@ class EmailConfirmationService
|
|||||||
protected function getToken()
|
protected function getToken()
|
||||||
{
|
{
|
||||||
$token = str_random(24);
|
$token = str_random(24);
|
||||||
while ($this->emailConfirmation->where('token', '=', $token)->exists()) {
|
while ($this->db->table('email_confirmations')->where('token', '=', $token)->exists()) {
|
||||||
$token = str_random(25);
|
$token = str_random(25);
|
||||||
}
|
}
|
||||||
return $token;
|
return $token;
|
||||||
|
11
app/User.php
11
app/User.php
@ -1,5 +1,6 @@
|
|||||||
<?php namespace BookStack;
|
<?php namespace BookStack;
|
||||||
|
|
||||||
|
use BookStack\Notifications\ResetPassword;
|
||||||
use Illuminate\Auth\Authenticatable;
|
use Illuminate\Auth\Authenticatable;
|
||||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||||
@ -184,4 +185,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
|||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the password reset notification.
|
||||||
|
* @param string $token
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function sendPasswordResetNotification($token)
|
||||||
|
{
|
||||||
|
$this->notify(new ResetPassword($token));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,6 @@ Route::get('/register/confirm', 'Auth\RegisterController@getRegisterConfirmation
|
|||||||
Route::get('/register/confirm/awaiting', 'Auth\RegisterController@showAwaitingConfirmation');
|
Route::get('/register/confirm/awaiting', 'Auth\RegisterController@showAwaitingConfirmation');
|
||||||
Route::post('/register/confirm/resend', 'Auth\RegisterController@resendConfirmation');
|
Route::post('/register/confirm/resend', 'Auth\RegisterController@resendConfirmation');
|
||||||
Route::get('/register/confirm/{token}', 'Auth\RegisterController@confirmEmail');
|
Route::get('/register/confirm/{token}', 'Auth\RegisterController@confirmEmail');
|
||||||
Route::get('/register/confirm/{token}/email', 'Auth\RegisterController@viewConfirmEmail');
|
|
||||||
Route::get('/register/service/{socialDriver}', 'Auth\RegisterController@socialRegister');
|
Route::get('/register/service/{socialDriver}', 'Auth\RegisterController@socialRegister');
|
||||||
Route::post('/register', 'Auth\RegisterController@postRegister');
|
Route::post('/register', 'Auth\RegisterController@postRegister');
|
||||||
|
|
||||||
@ -162,5 +161,5 @@ Route::get('/password/email', 'Auth\ForgotPasswordController@showLinkRequestForm
|
|||||||
Route::post('/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
|
Route::post('/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
|
||||||
|
|
||||||
// Password reset routes...
|
// Password reset routes...
|
||||||
Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@getReset');
|
Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
|
||||||
Route::post('/password/reset', 'Auth\ResetPasswordController@postReset');
|
Route::post('/password/reset', 'Auth\ResetPasswordController@reset');
|
@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use BookStack\EmailConfirmation;
|
use BookStack\Notifications\ConfirmEmail;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
class AuthTest extends TestCase
|
class AuthTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -57,15 +58,13 @@ class AuthTest extends TestCase
|
|||||||
|
|
||||||
public function test_confirmed_registration()
|
public function test_confirmed_registration()
|
||||||
{
|
{
|
||||||
|
// Fake notifications
|
||||||
|
Notification::fake();
|
||||||
|
|
||||||
// Set settings and get user instance
|
// Set settings and get user instance
|
||||||
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
|
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
|
||||||
$user = factory(\BookStack\User::class)->make();
|
$user = factory(\BookStack\User::class)->make();
|
||||||
|
|
||||||
// Mock Mailer to ensure mail is being sent
|
|
||||||
$mockMailer = Mockery::mock('Illuminate\Contracts\Mail\Mailer');
|
|
||||||
$mockMailer->shouldReceive('send')->with('emails/email-confirmation', Mockery::type('array'), Mockery::type('callable'))->twice();
|
|
||||||
$this->app->instance('mailer', $mockMailer);
|
|
||||||
|
|
||||||
// Go through registration process
|
// Go through registration process
|
||||||
$this->visit('/register')
|
$this->visit('/register')
|
||||||
->see('Sign Up')
|
->see('Sign Up')
|
||||||
@ -76,6 +75,10 @@ class AuthTest extends TestCase
|
|||||||
->seePageIs('/register/confirm')
|
->seePageIs('/register/confirm')
|
||||||
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
|
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
|
||||||
|
|
||||||
|
// Ensure notification sent
|
||||||
|
$dbUser = \BookStack\User::where('email', '=', $user->email)->first();
|
||||||
|
Notification::assertSentTo($dbUser, ConfirmEmail::class);
|
||||||
|
|
||||||
// Test access and resend confirmation email
|
// Test access and resend confirmation email
|
||||||
$this->login($user->email, $user->password)
|
$this->login($user->email, $user->password)
|
||||||
->seePageIs('/register/confirm/awaiting')
|
->seePageIs('/register/confirm/awaiting')
|
||||||
@ -84,19 +87,18 @@ class AuthTest extends TestCase
|
|||||||
->seePageIs('/register/confirm/awaiting')
|
->seePageIs('/register/confirm/awaiting')
|
||||||
->press('Resend Confirmation Email');
|
->press('Resend Confirmation Email');
|
||||||
|
|
||||||
// Get confirmation
|
// Get confirmation and confirm notification matches
|
||||||
$user = $user->where('email', '=', $user->email)->first();
|
$emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
|
||||||
$emailConfirmation = EmailConfirmation::where('user_id', '=', $user->id)->first();
|
Notification::assertSentTo($dbUser, ConfirmEmail::class, function($notification, $channels) use ($emailConfirmation) {
|
||||||
|
return $notification->token === $emailConfirmation->token;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check confirmation email confirmation activation.
|
||||||
// Check confirmation email button and confirmation activation.
|
$this->visit('/register/confirm/' . $emailConfirmation->token)
|
||||||
$this->visit('/register/confirm/' . $emailConfirmation->token . '/email')
|
|
||||||
->see('Email Confirmation')
|
|
||||||
->click('Confirm Email')
|
|
||||||
->seePageIs('/')
|
->seePageIs('/')
|
||||||
->see($user->name)
|
->see($user->name)
|
||||||
->notSeeInDatabase('email_confirmations', ['token' => $emailConfirmation->token])
|
->notSeeInDatabase('email_confirmations', ['token' => $emailConfirmation->token])
|
||||||
->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => true]);
|
->seeInDatabase('users', ['name' => $dbUser->name, 'email' => $dbUser->email, 'email_confirmed' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_restricted_registration()
|
public function test_restricted_registration()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user