userRepo = $userRepo; $this->emailConfirmationService = $emailConfirmationService; } /** * Check whether or not registrations are allowed in the app settings. * * @throws UserRegistrationException */ public function ensureRegistrationAllowed() { if (!$this->registrationAllowed()) { throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login'); } } /** * Check if standard BookStack User registrations are currently allowed. * Does not prevent external-auth based registration. */ protected function registrationAllowed(): bool { $authMethod = config('auth.method'); $authMethodsWithRegistration = ['standard']; return in_array($authMethod, $authMethodsWithRegistration) && setting('registration-enabled'); } /** * Attempt to find a user in the system otherwise register them as a new * user. For use with external auth systems since password is auto-generated. * @throws UserRegistrationException */ public function findOrRegister(string $name, string $email, string $externalId): User { $user = User::query() ->where('external_auth_id', '=', $externalId) ->first(); if (is_null($user)) { $userData = [ 'name' => $name, 'email' => $email, 'password' => Str::random(32), 'external_auth_id' => $externalId, ]; $user = $this->registerUser($userData, null, false); } return $user; } /** * The registrations flow for all users. * * @throws UserRegistrationException */ public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User { $userEmail = $userData['email']; // Email restriction $this->ensureEmailDomainAllowed($userEmail); // Ensure user does not already exist $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail)); if ($alreadyUser) { throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login'); } // Create the user $newUser = $this->userRepo->registerNew($userData, $emailConfirmed); // Assign social account if given if ($socialAccount) { $newUser->socialAccounts()->save($socialAccount); } Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser); Theme::dispatch(ThemeEvents::AUTH_REGISTER, $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver(), $newUser); // Start email confirmation flow if required if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) { $newUser->save(); try { $this->emailConfirmationService->sendConfirmation($newUser); session()->flash('sent-email-confirmation', true); } catch (Exception $e) { $message = trans('auth.email_confirm_send_error'); throw new UserRegistrationException($message, '/register/confirm'); } } return $newUser; } /** * Ensure that the given email meets any active email domain registration restrictions. * Throws if restrictions are active and the email does not match an allowed domain. * * @throws UserRegistrationException */ protected function ensureEmailDomainAllowed(string $userEmail): void { $registrationRestrict = setting('registration-restrict'); if (!$registrationRestrict) { return; } $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict)); $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, '@'), 1); if (!in_array($userEmailDomain, $restrictedEmailDomains)) { $redirect = $this->registrationAllowed() ? '/register' : '/login'; throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect); } } }