]> BookStack Code Mirror - bookstack/blob - app/Http/Controllers/Auth/RegisterController.php
Start user invite system
[bookstack] / app / Http / Controllers / Auth / RegisterController.php
1 <?php
2
3 namespace BookStack\Http\Controllers\Auth;
4
5 use BookStack\Auth\Access\EmailConfirmationService;
6 use BookStack\Auth\Access\SocialAuthService;
7 use BookStack\Auth\SocialAccount;
8 use BookStack\Auth\User;
9 use BookStack\Auth\UserRepo;
10 use BookStack\Exceptions\ConfirmationEmailException;
11 use BookStack\Exceptions\SocialDriverNotConfigured;
12 use BookStack\Exceptions\SocialSignInAccountNotUsed;
13 use BookStack\Exceptions\SocialSignInException;
14 use BookStack\Exceptions\UserRegistrationException;
15 use BookStack\Exceptions\UserTokenExpiredException;
16 use BookStack\Exceptions\UserTokenNotFoundException;
17 use BookStack\Http\Controllers\Controller;
18 use Exception;
19 use Illuminate\Foundation\Auth\RegistersUsers;
20 use Illuminate\Http\RedirectResponse;
21 use Illuminate\Http\Request;
22 use Illuminate\Http\Response;
23 use Illuminate\Routing\Redirector;
24 use Illuminate\View\View;
25 use Laravel\Socialite\Contracts\User as SocialUser;
26 use Validator;
27
28 class RegisterController extends Controller
29 {
30     /*
31     |--------------------------------------------------------------------------
32     | Register Controller
33     |--------------------------------------------------------------------------
34     |
35     | This controller handles the registration of new users as well as their
36     | validation and creation. By default this controller uses a trait to
37     | provide this functionality without requiring any additional code.
38     |
39     */
40
41     use RegistersUsers;
42
43     protected $socialAuthService;
44     protected $emailConfirmationService;
45     protected $userRepo;
46
47     /**
48      * Where to redirect users after login / registration.
49      *
50      * @var string
51      */
52     protected $redirectTo = '/';
53     protected $redirectPath = '/';
54
55     /**
56      * Create a new controller instance.
57      *
58      * @param SocialAuthService $socialAuthService
59      * @param \BookStack\Auth\EmailConfirmationService $emailConfirmationService
60      * @param UserRepo $userRepo
61      */
62     public function __construct(SocialAuthService $socialAuthService, EmailConfirmationService $emailConfirmationService, UserRepo $userRepo)
63     {
64         $this->middleware('guest')->only(['getRegister', 'postRegister', 'socialRegister']);
65         $this->socialAuthService = $socialAuthService;
66         $this->emailConfirmationService = $emailConfirmationService;
67         $this->userRepo = $userRepo;
68         $this->redirectTo = url('/');
69         $this->redirectPath = url('/');
70         parent::__construct();
71     }
72
73     /**
74      * Get a validator for an incoming registration request.
75      *
76      * @param  array $data
77      * @return \Illuminate\Contracts\Validation\Validator
78      */
79     protected function validator(array $data)
80     {
81         return Validator::make($data, [
82             'name' => 'required|min:2|max:255',
83             'email' => 'required|email|max:255|unique:users',
84             'password' => 'required|min:6',
85         ]);
86     }
87
88     /**
89      * Check whether or not registrations are allowed in the app settings.
90      * @throws UserRegistrationException
91      */
92     protected function checkRegistrationAllowed()
93     {
94         if (!setting('registration-enabled')) {
95             throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
96         }
97     }
98
99     /**
100      * Show the application registration form.
101      * @return Response
102      * @throws UserRegistrationException
103      */
104     public function getRegister()
105     {
106         $this->checkRegistrationAllowed();
107         $socialDrivers = $this->socialAuthService->getActiveDrivers();
108         return view('auth.register', ['socialDrivers' => $socialDrivers]);
109     }
110
111     /**
112      * Handle a registration request for the application.
113      * @param Request|Request $request
114      * @return RedirectResponse|Redirector
115      * @throws UserRegistrationException
116      */
117     public function postRegister(Request $request)
118     {
119         $this->checkRegistrationAllowed();
120         $this->validator($request->all())->validate();
121
122         $userData = $request->all();
123         return $this->registerUser($userData);
124     }
125
126     /**
127      * Create a new user instance after a valid registration.
128      * @param  array  $data
129      * @return User
130      */
131     protected function create(array $data)
132     {
133         return User::create([
134             'name' => $data['name'],
135             'email' => $data['email'],
136             'password' => bcrypt($data['password']),
137         ]);
138     }
139
140     /**
141      * The registrations flow for all users.
142      * @param array $userData
143      * @param bool|false|SocialAccount $socialAccount
144      * @param bool $emailVerified
145      * @return RedirectResponse|Redirector
146      * @throws UserRegistrationException
147      */
148     protected function registerUser(array $userData, $socialAccount = false, $emailVerified = false)
149     {
150         $registrationRestrict = setting('registration-restrict');
151
152         if ($registrationRestrict) {
153             $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict));
154             $userEmailDomain = $domain = mb_substr(mb_strrchr($userData['email'], "@"), 1);
155             if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
156                 throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), '/register');
157             }
158         }
159
160         $newUser = $this->userRepo->registerNew($userData, $emailVerified);
161         if ($socialAccount) {
162             $newUser->socialAccounts()->save($socialAccount);
163         }
164
165         if ((setting('registration-confirmation') || $registrationRestrict) && !$emailVerified) {
166             $newUser->save();
167
168             try {
169                 $this->emailConfirmationService->sendConfirmation($newUser);
170             } catch (Exception $e) {
171                 session()->flash('error', trans('auth.email_confirm_send_error'));
172             }
173
174             return redirect('/register/confirm');
175         }
176
177         auth()->login($newUser);
178         session()->flash('success', trans('auth.register_success'));
179         return redirect($this->redirectPath());
180     }
181
182     /**
183      * Show the page to tell the user to check their email
184      * and confirm their address.
185      */
186     public function getRegisterConfirmation()
187     {
188         return view('auth.register-confirm');
189     }
190
191     /**
192      * Confirms an email via a token and logs the user into the system.
193      * @param $token
194      * @return RedirectResponse|Redirector
195      * @throws ConfirmationEmailException
196      * @throws Exception
197      */
198     public function confirmEmail($token)
199     {
200         try {
201             $userId = $this->emailConfirmationService->checkTokenAndGetUserId($token);
202         } catch (Exception $exception) {
203
204             if ($exception instanceof UserTokenNotFoundException) {
205                 session()->flash('error', trans('errors.email_confirmation_invalid'));
206                 return redirect('/register');
207             }
208
209             if ($exception instanceof UserTokenExpiredException) {
210                 $user = $this->userRepo->getById($exception->userId);
211                 $this->emailConfirmationService->sendConfirmation($user);
212                 session()->flash('error', trans('errors.email_confirmation_expired'));
213                 return redirect('/register/confirm');
214             }
215
216             throw $exception;
217         }
218
219         $user = $this->userRepo->getById($userId);
220         $user->email_confirmed = true;
221         $user->save();
222
223         auth()->login($user);
224         session()->flash('success', trans('auth.email_confirm_success'));
225         $this->emailConfirmationService->deleteByUser($user);
226
227         return redirect($this->redirectPath);
228     }
229
230     /**
231      * Shows a notice that a user's email address has not been confirmed,
232      * Also has the option to re-send the confirmation email.
233      * @return View
234      */
235     public function showAwaitingConfirmation()
236     {
237         return view('auth.user-unconfirmed');
238     }
239
240     /**
241      * Resend the confirmation email
242      * @param Request $request
243      * @return View
244      */
245     public function resendConfirmation(Request $request)
246     {
247         $this->validate($request, [
248             'email' => 'required|email|exists:users,email'
249         ]);
250         $user = $this->userRepo->getByEmail($request->get('email'));
251
252         try {
253             $this->emailConfirmationService->sendConfirmation($user);
254         } catch (Exception $e) {
255             session()->flash('error', trans('auth.email_confirm_send_error'));
256             return redirect('/register/confirm');
257         }
258
259         session()->flash('success', trans('auth.email_confirm_resent'));
260         return redirect('/register/confirm');
261     }
262
263     /**
264      * Redirect to the social site for authentication intended to register.
265      * @param $socialDriver
266      * @return mixed
267      * @throws UserRegistrationException
268      * @throws SocialDriverNotConfigured
269      */
270     public function socialRegister($socialDriver)
271     {
272         $this->checkRegistrationAllowed();
273         session()->put('social-callback', 'register');
274         return $this->socialAuthService->startRegister($socialDriver);
275     }
276
277     /**
278      * The callback for social login services.
279      * @param $socialDriver
280      * @param Request $request
281      * @return RedirectResponse|Redirector
282      * @throws SocialSignInException
283      * @throws UserRegistrationException
284      * @throws SocialDriverNotConfigured
285      */
286     public function socialCallback($socialDriver, Request $request)
287     {
288         if (!session()->has('social-callback')) {
289             throw new SocialSignInException(trans('errors.social_no_action_defined'), '/login');
290         }
291
292         // Check request for error information
293         if ($request->has('error') && $request->has('error_description')) {
294             throw new SocialSignInException(trans('errors.social_login_bad_response', [
295                 'socialAccount' => $socialDriver,
296                 'error' => $request->get('error_description'),
297             ]), '/login');
298         }
299
300         $action = session()->pull('social-callback');
301
302         // Attempt login or fall-back to register if allowed.
303         $socialUser = $this->socialAuthService->getSocialUser($socialDriver);
304         if ($action == 'login') {
305             try {
306                 return $this->socialAuthService->handleLoginCallback($socialDriver, $socialUser);
307             } catch (SocialSignInAccountNotUsed $exception) {
308                 if ($this->socialAuthService->driverAutoRegisterEnabled($socialDriver)) {
309                     return $this->socialRegisterCallback($socialDriver, $socialUser);
310                 }
311                 throw $exception;
312             }
313         }
314
315         if ($action == 'register') {
316             return $this->socialRegisterCallback($socialDriver, $socialUser);
317         }
318
319         return redirect()->back();
320     }
321
322     /**
323      * Detach a social account from a user.
324      * @param $socialDriver
325      * @return RedirectResponse|Redirector
326      */
327     public function detachSocialAccount($socialDriver)
328     {
329         return $this->socialAuthService->detachSocialAccount($socialDriver);
330     }
331
332     /**
333      * Register a new user after a registration callback.
334      * @param string $socialDriver
335      * @param SocialUser $socialUser
336      * @return RedirectResponse|Redirector
337      * @throws UserRegistrationException
338      */
339     protected function socialRegisterCallback(string $socialDriver, SocialUser $socialUser)
340     {
341         $socialUser = $this->socialAuthService->handleRegistrationCallback($socialDriver, $socialUser);
342         $socialAccount = $this->socialAuthService->fillSocialAccount($socialDriver, $socialUser);
343         $emailVerified = $this->socialAuthService->driverAutoConfirmEmailEnabled($socialDriver);
344
345         // Create an array of the user data to create a new user instance
346         $userData = [
347             'name' => $socialUser->getName(),
348             'email' => $socialUser->getEmail(),
349             'password' => str_random(30)
350         ];
351         return $this->registerUser($userData, $socialAccount, $emailVerified);
352     }
353 }