mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-11-23 11:22:33 +01:00
Got standard form-based registration working
This commit is contained in:
parent
2c3fb557d6
commit
dec0cbb1b2
15
app/EmailConfirmation.php
Normal file
15
app/EmailConfirmation.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Oxbow;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class EmailConfirmation extends Model
|
||||
{
|
||||
protected $fillable = ['user_id', 'token'];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo('Oxbow\User');
|
||||
}
|
||||
}
|
7
app/Exceptions/ConfirmationEmailException.php
Normal file
7
app/Exceptions/ConfirmationEmailException.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php namespace Oxbow\Exceptions;
|
||||
|
||||
|
||||
class ConfirmationEmailException extends NotifyException
|
||||
{
|
||||
|
||||
}
|
7
app/Exceptions/UserRegistrationException.php
Normal file
7
app/Exceptions/UserRegistrationException.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php namespace Oxbow\Exceptions;
|
||||
|
||||
|
||||
class UserRegistrationException extends NotifyException
|
||||
{
|
||||
|
||||
}
|
@ -2,7 +2,12 @@
|
||||
|
||||
namespace Oxbow\Http\Controllers\Auth;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Oxbow\Exceptions\SocialSignInException;
|
||||
use Oxbow\Exceptions\UserRegistrationException;
|
||||
use Oxbow\Repos\UserRepo;
|
||||
use Oxbow\Services\EmailConfirmationService;
|
||||
use Oxbow\Services\Facades\Setting;
|
||||
use Oxbow\Services\SocialAuthService;
|
||||
use Oxbow\User;
|
||||
use Validator;
|
||||
@ -30,15 +35,22 @@ class AuthController extends Controller
|
||||
protected $redirectAfterLogout = '/login';
|
||||
|
||||
protected $socialAuthService;
|
||||
protected $emailConfirmationService;
|
||||
protected $userRepo;
|
||||
|
||||
/**
|
||||
* Create a new authentication controller instance.
|
||||
* @param SocialAuthService $socialAuthService
|
||||
* @param SocialAuthService $socialAuthService
|
||||
* @param EmailConfirmationService $emailConfirmationService
|
||||
* @param UserRepo $userRepo
|
||||
*/
|
||||
public function __construct(SocialAuthService $socialAuthService)
|
||||
public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
|
||||
{
|
||||
$this->middleware('guest', ['only' => ['getLogin', 'postLogin', 'getRegister']]);
|
||||
$this->socialAuthService = $socialAuthService;
|
||||
$this->emailConfirmationService = $emailConfirmationService;
|
||||
$this->userRepo = $userRepo;
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,7 +64,7 @@ class AuthController extends Controller
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|max:255',
|
||||
'email' => 'required|email|max:255|unique:users',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
'password' => 'required|min:6',
|
||||
]);
|
||||
}
|
||||
|
||||
@ -71,6 +83,13 @@ class AuthController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
protected function checkRegistrationAllowed()
|
||||
{
|
||||
if(!\Setting::get('registration-enabled')) {
|
||||
throw new UserRegistrationException('Registrations are currently disabled.', '/login');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application registration form.
|
||||
*
|
||||
@ -78,10 +97,104 @@ class AuthController extends Controller
|
||||
*/
|
||||
public function getRegister()
|
||||
{
|
||||
$this->checkRegistrationAllowed();
|
||||
$socialDrivers = $this->socialAuthService->getActiveDrivers();
|
||||
return view('auth.register', ['socialDrivers' => $socialDrivers]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a registration request for the application.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* @throws UserRegistrationException
|
||||
*/
|
||||
public function postRegister(Request $request)
|
||||
{
|
||||
$this->checkRegistrationAllowed();
|
||||
$validator = $this->validator($request->all());
|
||||
|
||||
if ($validator->fails()) {
|
||||
$this->throwValidationException(
|
||||
$request, $validator
|
||||
);
|
||||
}
|
||||
|
||||
if(\Setting::get('registration-restrict')) {
|
||||
$restrictedEmailDomains = explode(',', str_replace(' ', '', \Setting::get('registration-restrict')));
|
||||
$userEmailDomain = $domain = substr(strrchr($request->get('email'), "@"), 1);
|
||||
if(!in_array($userEmailDomain, $restrictedEmailDomains)) {
|
||||
throw new UserRegistrationException('That email domain does not have access to this application', '/register');
|
||||
}
|
||||
}
|
||||
|
||||
$newUser = $this->create($request->all());
|
||||
$newUser->attachRoleId(\Setting::get('registration-role'), 1);
|
||||
|
||||
if(\Setting::get('registration-confirmation') || \Setting::get('registration-restrict')) {
|
||||
$newUser->email_confirmed = false;
|
||||
$newUser->save();
|
||||
$this->emailConfirmationService->sendConfirmation($newUser);
|
||||
return redirect('/register/confirm');
|
||||
}
|
||||
|
||||
auth()->login($newUser);
|
||||
return redirect($this->redirectPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the page to tell the user to check thier email
|
||||
* and confirm their address.
|
||||
*/
|
||||
public function getRegisterConfirmation()
|
||||
{
|
||||
return view('auth/register-confirm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms an email via a token and logs the user into the system.
|
||||
* @param $token
|
||||
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||
* @throws UserRegistrationException
|
||||
*/
|
||||
public function confirmEmail($token)
|
||||
{
|
||||
$confirmation = $this->emailConfirmationService->getEmailConfirmationFromToken($token);
|
||||
$user = $confirmation->user;
|
||||
$user->email_confirmed = true;
|
||||
$user->save();
|
||||
auth()->login($confirmation->user);
|
||||
session()->flash('success', 'Your email has been confirmed!');
|
||||
$this->emailConfirmationService->deleteConfirmationsByUser($user);
|
||||
return redirect($this->redirectPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a notice that a user's email address has not been confirmed,
|
||||
* Also has the option to re-send the confirmation email.
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function showAwaitingConfirmation()
|
||||
{
|
||||
return view('auth/user-unconfirmed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend the confirmation email
|
||||
* @param Request $request
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function resendConfirmation(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'email' => 'required|email|exists:users,email'
|
||||
]);
|
||||
$user = $this->userRepo->getByEmail($request->get('email'));
|
||||
$this->emailConfirmationService->sendConfirmation($user);
|
||||
\Session::flash('success', 'Confirmation email resent, Please check your inbox.');
|
||||
return redirect('/register/confirm');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application login form.
|
||||
*
|
||||
|
@ -38,6 +38,7 @@ class SettingController extends Controller
|
||||
$key = str_replace('setting-', '', trim($name));
|
||||
Setting::put($key, $value);
|
||||
}
|
||||
session()->flash('success', 'Settings Saved');
|
||||
return redirect('/settings');
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,6 @@ class UserController extends Controller
|
||||
}
|
||||
|
||||
if ($request->has('password') && $request->get('password') != '') {
|
||||
//dd('cat');
|
||||
$password = $request->get('password');
|
||||
$user->password = bcrypt($password);
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ namespace Oxbow\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Oxbow\Exceptions\UserRegistrationException;
|
||||
use Setting;
|
||||
|
||||
class Authenticate
|
||||
@ -34,6 +35,9 @@ class Authenticate
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
{
|
||||
if(auth()->check() && auth()->user()->email_confirmed == false) {
|
||||
return redirect()->guest('/register/confirm/awaiting');
|
||||
}
|
||||
if ($this->auth->guest() && !Setting::get('app-public')) {
|
||||
if ($request->ajax()) {
|
||||
return response('Unauthorized.', 401);
|
||||
|
@ -88,6 +88,11 @@ Route::get('/login', 'Auth\AuthController@getLogin');
|
||||
Route::post('/login', 'Auth\AuthController@postLogin');
|
||||
Route::get('/logout', 'Auth\AuthController@getLogout');
|
||||
Route::get('/register', 'Auth\AuthController@getRegister');
|
||||
Route::get('/register/confirm', 'Auth\AuthController@getRegisterConfirmation');
|
||||
Route::get('/register/confirm/awaiting', 'Auth\AuthController@showAwaitingConfirmation');
|
||||
Route::post('/register/confirm/resend', 'Auth\AuthController@resendConfirmation');
|
||||
Route::get('/register/confirm/{token}', 'Auth\AuthController@confirmEmail');
|
||||
Route::post('/register', 'Auth\AuthController@postRegister');
|
||||
|
||||
// Password reset link request routes...
|
||||
Route::get('/password/email', 'Auth\PasswordController@getEmail');
|
||||
|
@ -21,4 +21,9 @@ class UserRepo
|
||||
public function getByEmail($email) {
|
||||
return $this->user->where('email', '=', $email)->first();
|
||||
}
|
||||
|
||||
public function getById($id)
|
||||
{
|
||||
return $this->user->findOrFail($id);
|
||||
}
|
||||
}
|
102
app/Services/EmailConfirmationService.php
Normal file
102
app/Services/EmailConfirmationService.php
Normal file
@ -0,0 +1,102 @@
|
||||
<?php namespace Oxbow\Services;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\Mail\Mailer;
|
||||
use Illuminate\Mail\Message;
|
||||
use Oxbow\EmailConfirmation;
|
||||
use Oxbow\Exceptions\ConfirmationEmailException;
|
||||
use Oxbow\Exceptions\UserRegistrationException;
|
||||
use Oxbow\Repos\UserRepo;
|
||||
use Oxbow\Setting;
|
||||
use Oxbow\User;
|
||||
|
||||
class EmailConfirmationService
|
||||
{
|
||||
protected $mailer;
|
||||
protected $emailConfirmation;
|
||||
|
||||
/**
|
||||
* EmailConfirmationService constructor.
|
||||
* @param Mailer $mailer
|
||||
* @param EmailConfirmation $emailConfirmation
|
||||
*/
|
||||
public function __construct(Mailer $mailer, EmailConfirmation $emailConfirmation)
|
||||
{
|
||||
$this->mailer = $mailer;
|
||||
$this->emailConfirmation = $emailConfirmation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create new confirmation for a user,
|
||||
* Also removes any existing old ones.
|
||||
* @param User $user
|
||||
* @throws ConfirmationEmailException
|
||||
*/
|
||||
public function sendConfirmation(User $user)
|
||||
{
|
||||
if($user->email_confirmed) {
|
||||
throw new ConfirmationEmailException('Email has already been confirmed, Try logging in.', '/login');
|
||||
}
|
||||
$this->deleteConfirmationsByUser($user);
|
||||
$token = $this->getToken();
|
||||
$this->emailConfirmation->create([
|
||||
'user_id' => $user->id,
|
||||
'token' => $token,
|
||||
]);
|
||||
$this->mailer->send('emails/email-confirmation', ['token' => $token], function (Message $message) use ($user) {
|
||||
$appName = \Setting::get('app-name', 'BookStack');
|
||||
$message->to($user->email, $user->name)->subject('Confirm your email on ' . $appName . '.');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an email confirmation by looking up the token,
|
||||
* Ensures the token has not expired.
|
||||
* @param string $token
|
||||
* @return EmailConfirmation
|
||||
* @throws UserRegistrationException
|
||||
*/
|
||||
public function getEmailConfirmationFromToken($token)
|
||||
{
|
||||
$emailConfirmation = $this->emailConfirmation->where('token', '=', $token)->first();
|
||||
// If not found
|
||||
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);
|
||||
throw new UserRegistrationException('The confirmation token has expired, A new confirmation email has been sent.', '/register/confirm');
|
||||
}
|
||||
|
||||
return $emailConfirmation;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete all email confirmations that belong to a user.
|
||||
* @param User $user
|
||||
* @return mixed
|
||||
*/
|
||||
public function deleteConfirmationsByUser(User $user)
|
||||
{
|
||||
return $this->emailConfirmation->where('user_id', '=', $user->id)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a unique token within the email confirmation database.
|
||||
* @return string
|
||||
*/
|
||||
protected function getToken()
|
||||
{
|
||||
$token = str_random(24);
|
||||
while ($this->emailConfirmation->where('token', '=', $token)->exists()) {
|
||||
$token = str_random(25);
|
||||
}
|
||||
return $token;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -14,8 +14,8 @@ class CreateSocialAccountsTable extends Migration
|
||||
{
|
||||
Schema::create('social_accounts', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->indexed();
|
||||
$table->string('driver')->indexed();
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('driver')->index();
|
||||
$table->string('driver_id');
|
||||
$table->string('avatar');
|
||||
$table->timestamps();
|
||||
|
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddEmailConfirmationTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->boolean('email_confirmed')->default(true);
|
||||
});
|
||||
|
||||
Schema::create('email_confirmations', function (Blueprint $table) {
|
||||
$table->increments('id');
|
||||
$table->integer('user_id')->index();
|
||||
$table->string('token')->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('email_confirmed');
|
||||
});
|
||||
Schema::drop('email_confirmations');
|
||||
}
|
||||
}
|
19
resources/views/auth/register-confirm.blade.php
Normal file
19
resources/views/auth/register-confirm.blade.php
Normal file
@ -0,0 +1,19 @@
|
||||
@extends('public')
|
||||
|
||||
@section('header-buttons')
|
||||
@if(!$signedIn)
|
||||
<a href="/login"><i class="zmdi zmdi-sign-in"></i>Sign in</a>
|
||||
@endif
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="text-center">
|
||||
<div class="center-box">
|
||||
<h2>Thanks for registering!</h2>
|
||||
<p>Please check your email and click the confirmation button to access {{ \Setting::get('app-name') }}.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@stop
|
@ -8,9 +8,9 @@
|
||||
|
||||
<div class="text-center">
|
||||
<div class="center-box">
|
||||
<h1>Register</h1>
|
||||
<h1>Sign Up</h1>
|
||||
|
||||
<form action="/login" method="POST">
|
||||
<form action="/register" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
|
||||
<div class="form-group">
|
||||
@ -29,13 +29,14 @@
|
||||
</div>
|
||||
|
||||
<div class="from-group">
|
||||
<button class="button block pos">Sign In</button>
|
||||
<button class="button block pos">Create Account</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@if(count($socialDrivers) > 0)
|
||||
<hr class="margin-top">
|
||||
<h3 class="text-muted">Social Registration</h3>
|
||||
<p class="text-small">Register and sign in using another service.</p>
|
||||
@if(isset($socialDrivers['google']))
|
||||
<a href="/register/service/google" style="color: #DC4E41;"><i class="zmdi zmdi-google-plus-box zmdi-hc-4x"></i></a>
|
||||
@endif
|
||||
|
30
resources/views/auth/user-unconfirmed.blade.php
Normal file
30
resources/views/auth/user-unconfirmed.blade.php
Normal file
@ -0,0 +1,30 @@
|
||||
@extends('public')
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<h2>Email Address not confirmed</h2>
|
||||
<p class="text-muted">Your email address has not yet been confirmed. <br>
|
||||
Please click the link in the email that was sent shortly after you registered. <br>
|
||||
If you cannot find the email you can re-send the confirmation email by submitting the form below.
|
||||
</p>
|
||||
<hr>
|
||||
<form action="/register/confirm/resend" method="POST">
|
||||
{!! csrf_field() !!}
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
@if(auth()->check())
|
||||
@include('form/text', ['name' => 'email', 'model' => auth()->user()])
|
||||
@else
|
||||
@include('form/text', ['name' => 'email'])
|
||||
@endif
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<button type="submit" class="button pos">Resend Confirmation Email</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@stop
|
@ -157,7 +157,7 @@
|
||||
<table style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;width:100%;">
|
||||
<tr style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;">
|
||||
<td class="padding" style="margin-top:0;margin-bottom:0;margin-right:0;margin-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;line-height:1.6;padding-top:10px;padding-bottom:10px;padding-right:0;padding-left:0;">
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;"><a class="btn-primary" href="{{ url('user/confirm/'.$token) }}" style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm Email</a></p>
|
||||
<p style="margin-top:0;margin-right:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;line-height:1.6;margin-bottom:10px;font-weight:normal;font-size:14px;color:#888888;"><a class="btn-primary" href="{{ url('/register/confirm/'.$token) }}" style="margin-top:0;margin-bottom:0;margin-left:0;padding-top:0;padding-bottom:0;padding-right:0;padding-left:0;font-family:'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif;font-size:100%;text-decoration:none;color:#FFF;background-color:#348eda;border-style:solid;border-color:#348eda;border-width:10px 20px;line-height:2;font-weight:bold;margin-right:10px;text-align:center;cursor:pointer;display:inline-block;border-radius:4px;">Confirm Email</a></p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
@ -37,6 +37,19 @@
|
||||
<div class="links text-center">
|
||||
@yield('header-buttons')
|
||||
</div>
|
||||
@if($signedIn)
|
||||
<img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
|
||||
<div class="dropdown-container" data-dropdown>
|
||||
<span class="user-name" data-dropdown-toggle>
|
||||
{{ $currentUser->name }} <i class="zmdi zmdi-caret-down"></i>
|
||||
</span>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/logout" class="text-neg"><i class="zmdi zmdi-run zmdi-hc-lg"></i>Logout</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user