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