3 namespace BookStack\Access;
5 use BookStack\Activity\ActivityType;
6 use BookStack\Exceptions\UserRegistrationException;
7 use BookStack\Facades\Activity;
8 use BookStack\Facades\Theme;
9 use BookStack\Theming\ThemeEvents;
10 use BookStack\Users\Models\User;
11 use BookStack\Users\UserRepo;
13 use Illuminate\Support\Str;
15 class RegistrationService
17 public function __construct(
18 protected UserRepo $userRepo,
19 protected EmailConfirmationService $emailConfirmationService,
24 * Check if registrations are allowed in the app settings.
26 * @throws UserRegistrationException
28 public function ensureRegistrationAllowed()
30 if (!$this->registrationAllowed()) {
31 throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
36 * Check if standard BookStack User registrations are currently allowed.
37 * Does not prevent external-auth based registration.
39 protected function registrationAllowed(): bool
41 $authMethod = config('auth.method');
42 $authMethodsWithRegistration = ['standard'];
44 return in_array($authMethod, $authMethodsWithRegistration) && setting('registration-enabled');
48 * Attempt to find a user in the system otherwise register them as a new
49 * user. For use with external auth systems since password is auto-generated.
51 * @throws UserRegistrationException
53 public function findOrRegister(string $name, string $email, string $externalId): User
56 ->where('external_auth_id', '=', $externalId)
63 'password' => Str::random(32),
64 'external_auth_id' => $externalId,
67 $user = $this->registerUser($userData, null, false);
74 * The registrations flow for all users.
76 * @throws UserRegistrationException
78 public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User
80 $userEmail = $userData['email'];
81 $authSystem = $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver();
84 $this->ensureEmailDomainAllowed($userEmail);
86 // Ensure user does not already exist
87 $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
89 throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
92 /** @var ?bool $shouldRegister */
93 $shouldRegister = Theme::dispatch(ThemeEvents::AUTH_PRE_REGISTER, $authSystem, $userData);
94 if ($shouldRegister === false) {
95 throw new UserRegistrationException(trans('errors.auth_pre_register_theme_prevention'), '/login');
99 $newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed);
100 $newUser->attachDefaultRole();
102 // Assign social account if given
103 if ($socialAccount) {
104 $newUser->socialAccounts()->save($socialAccount);
107 Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
108 Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser);
110 // Start email confirmation flow if required
111 if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
115 $this->emailConfirmationService->sendConfirmation($newUser);
116 session()->flash('sent-email-confirmation', true);
117 } catch (Exception $e) {
118 $message = trans('auth.email_confirm_send_error');
120 throw new UserRegistrationException($message, '/register/confirm');
128 * Ensure that the given email meets any active email domain registration restrictions.
129 * Throws if restrictions are active and the email does not match an allowed domain.
131 * @throws UserRegistrationException
133 protected function ensureEmailDomainAllowed(string $userEmail): void
135 $registrationRestrict = setting('registration-restrict');
137 if (!$registrationRestrict) {
141 $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict));
142 $userEmailDomain = mb_substr(mb_strrchr($userEmail, '@'), 1);
143 if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
144 $redirect = $this->registrationAllowed() ? '/register' : '/login';
146 throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect);