mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-24 21:24:06 +08:00
Fixed registations bugs and removed email confirmation model
This commit is contained in:
parent
39e81d9a1f
commit
fbfc25fc21
3
.gitignore
vendored
3
.gitignore
vendored
@ -10,4 +10,5 @@ Homestead.yaml
|
||||
/public/bower
|
||||
/storage/images
|
||||
_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;
|
||||
|
||||
use BookStack\Exceptions\ConfirmationEmailException;
|
||||
use BookStack\Exceptions\UserRegistrationException;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use BookStack\Services\EmailConfirmationService;
|
||||
use BookStack\Services\SocialAuthService;
|
||||
@ -158,7 +160,7 @@ class RegisterController extends Controller
|
||||
* @param bool|false|SocialAccount $socialAccount
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws UserRegistrationException
|
||||
* @throws \BookStack\Exceptions\ConfirmationEmailException
|
||||
* @throws ConfirmationEmailException
|
||||
*/
|
||||
protected function registerUser(array $userData, $socialAccount = false)
|
||||
{
|
||||
@ -195,18 +197,6 @@ class RegisterController extends Controller
|
||||
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.
|
||||
* @param $token
|
||||
@ -219,7 +209,7 @@ class RegisterController extends Controller
|
||||
$user = $confirmation->user;
|
||||
$user->email_confirmed = true;
|
||||
$user->save();
|
||||
auth()->login($confirmation->user);
|
||||
auth()->login($user);
|
||||
session()->flash('success', 'Your email has been confirmed!');
|
||||
$this->emailConfirmationService->deleteConfirmationsByUser($user);
|
||||
return redirect($this->redirectPath);
|
||||
|
@ -33,7 +33,7 @@ class Authenticate
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
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')) {
|
||||
|
@ -34,7 +34,8 @@ class RedirectIfAuthenticated
|
||||
*/
|
||||
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('/');
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,15 @@ use Illuminate\Notifications\Messages\MailMessage;
|
||||
class ConfirmEmail extends Notification
|
||||
{
|
||||
|
||||
public $token;
|
||||
|
||||
/**
|
||||
* 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'))
|
||||
->greeting('Thanks for joining ' . setting('app-name') . '!')
|
||||
->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;
|
||||
|
||||
use BookStack\Notifications\ConfirmEmail;
|
||||
use BookStack\Repos\UserRepo;
|
||||
use Carbon\Carbon;
|
||||
use BookStack\EmailConfirmation;
|
||||
use BookStack\Exceptions\ConfirmationEmailException;
|
||||
use BookStack\Exceptions\UserRegistrationException;
|
||||
use BookStack\User;
|
||||
use Illuminate\Database\Connection as Database;
|
||||
|
||||
class EmailConfirmationService
|
||||
{
|
||||
protected $emailConfirmation;
|
||||
protected $db;
|
||||
protected $users;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
$token = $this->getToken();
|
||||
$confirmation = $this->emailConfirmation->create([
|
||||
'user_id' => $user->id,
|
||||
'token' => $token,
|
||||
]);
|
||||
$token = $this->createEmailConfirmation($user);
|
||||
|
||||
$confirmation->notify(new ConfirmEmail());
|
||||
$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();
|
||||
$this->db->table('email_confirmations')->insert([
|
||||
'user_id' => $user->id,
|
||||
'token' => $token,
|
||||
'created_at' => Carbon::now(),
|
||||
'updated_at' => Carbon::now()
|
||||
]);
|
||||
return $token;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,22 +68,24 @@ class EmailConfirmationService
|
||||
*/
|
||||
public function getEmailConfirmationFromToken($token)
|
||||
{
|
||||
$emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first();
|
||||
// If not found
|
||||
$emailConfirmation = $this->db->table('email_confirmations')->where('token', '=', $token)->first();
|
||||
|
||||
// If not found show error
|
||||
if ($emailConfirmation === null) {
|
||||
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 (Carbon::now()->subDay()->gt($emailConfirmation->created_at)) {
|
||||
$this->sendConfirmation($emailConfirmation->user);
|
||||
if (Carbon::now()->subDay()->gt(new Carbon($emailConfirmation->created_at))) {
|
||||
$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');
|
||||
}
|
||||
|
||||
$emailConfirmation->user = $this->users->getById($emailConfirmation->user_id);
|
||||
return $emailConfirmation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete all email confirmations that belong to a user.
|
||||
* @param User $user
|
||||
@ -74,7 +93,7 @@ class EmailConfirmationService
|
||||
*/
|
||||
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()
|
||||
{
|
||||
$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);
|
||||
}
|
||||
return $token;
|
||||
|
11
app/User.php
11
app/User.php
@ -1,5 +1,6 @@
|
||||
<?php namespace BookStack;
|
||||
|
||||
use BookStack\Notifications\ResetPassword;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
@ -184,4 +185,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||
|
||||
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::post('/register/confirm/resend', 'Auth\RegisterController@resendConfirmation');
|
||||
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::post('/register', 'Auth\RegisterController@postRegister');
|
||||
|
||||
@ -162,5 +161,5 @@ Route::get('/password/email', 'Auth\ForgotPasswordController@showLinkRequestForm
|
||||
Route::post('/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');
|
||||
|
||||
// Password reset routes...
|
||||
Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@getReset');
|
||||
Route::post('/password/reset', 'Auth\ResetPasswordController@postReset');
|
||||
Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
|
||||
Route::post('/password/reset', 'Auth\ResetPasswordController@reset');
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use BookStack\EmailConfirmation;
|
||||
use BookStack\Notifications\ConfirmEmail;
|
||||
use Illuminate\Support\Facades\Notification;
|
||||
|
||||
class AuthTest extends TestCase
|
||||
{
|
||||
@ -57,15 +58,13 @@ class AuthTest extends TestCase
|
||||
|
||||
public function test_confirmed_registration()
|
||||
{
|
||||
// Fake notifications
|
||||
Notification::fake();
|
||||
|
||||
// Set settings and get user instance
|
||||
$this->setSettings(['registration-enabled' => 'true', 'registration-confirmation' => 'true']);
|
||||
$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
|
||||
$this->visit('/register')
|
||||
->see('Sign Up')
|
||||
@ -76,6 +75,10 @@ class AuthTest extends TestCase
|
||||
->seePageIs('/register/confirm')
|
||||
->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
|
||||
$this->login($user->email, $user->password)
|
||||
->seePageIs('/register/confirm/awaiting')
|
||||
@ -84,19 +87,18 @@ class AuthTest extends TestCase
|
||||
->seePageIs('/register/confirm/awaiting')
|
||||
->press('Resend Confirmation Email');
|
||||
|
||||
// Get confirmation
|
||||
$user = $user->where('email', '=', $user->email)->first();
|
||||
$emailConfirmation = EmailConfirmation::where('user_id', '=', $user->id)->first();
|
||||
|
||||
|
||||
// Check confirmation email button and confirmation activation.
|
||||
$this->visit('/register/confirm/' . $emailConfirmation->token . '/email')
|
||||
->see('Email Confirmation')
|
||||
->click('Confirm Email')
|
||||
// Get confirmation and confirm notification matches
|
||||
$emailConfirmation = DB::table('email_confirmations')->where('user_id', '=', $dbUser->id)->first();
|
||||
Notification::assertSentTo($dbUser, ConfirmEmail::class, function($notification, $channels) use ($emailConfirmation) {
|
||||
return $notification->token === $emailConfirmation->token;
|
||||
});
|
||||
|
||||
// Check confirmation email confirmation activation.
|
||||
$this->visit('/register/confirm/' . $emailConfirmation->token)
|
||||
->seePageIs('/')
|
||||
->see($user->name)
|
||||
->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()
|
||||
|
Loading…
x
Reference in New Issue
Block a user