]> BookStack Code Mirror - bookstack/blob - app/Access/RegistrationService.php
Lexical: Fixed code in lists, removed extra old alignment code
[bookstack] / app / Access / RegistrationService.php
1 <?php
2
3 namespace BookStack\Access;
4
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;
12 use Exception;
13 use Illuminate\Support\Str;
14
15 class RegistrationService
16 {
17     public function __construct(
18         protected UserRepo $userRepo,
19         protected EmailConfirmationService $emailConfirmationService,
20     ) {
21     }
22
23     /**
24      * Check if registrations are allowed in the app settings.
25      *
26      * @throws UserRegistrationException
27      */
28     public function ensureRegistrationAllowed()
29     {
30         if (!$this->registrationAllowed()) {
31             throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
32         }
33     }
34
35     /**
36      * Check if standard BookStack User registrations are currently allowed.
37      * Does not prevent external-auth based registration.
38      */
39     protected function registrationAllowed(): bool
40     {
41         $authMethod = config('auth.method');
42         $authMethodsWithRegistration = ['standard'];
43
44         return in_array($authMethod, $authMethodsWithRegistration) && setting('registration-enabled');
45     }
46
47     /**
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.
50      *
51      * @throws UserRegistrationException
52      */
53     public function findOrRegister(string $name, string $email, string $externalId): User
54     {
55         $user = User::query()
56             ->where('external_auth_id', '=', $externalId)
57             ->first();
58
59         if (is_null($user)) {
60             $userData = [
61                 'name'             => $name,
62                 'email'            => $email,
63                 'password'         => Str::random(32),
64                 'external_auth_id' => $externalId,
65             ];
66
67             $user = $this->registerUser($userData, null, false);
68         }
69
70         return $user;
71     }
72
73     /**
74      * The registrations flow for all users.
75      *
76      * @throws UserRegistrationException
77      */
78     public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User
79     {
80         $userEmail = $userData['email'];
81         $authSystem = $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver();
82
83         // Email restriction
84         $this->ensureEmailDomainAllowed($userEmail);
85
86         // Ensure user does not already exist
87         $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
88         if ($alreadyUser) {
89             throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
90         }
91
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');
96         }
97
98         // Create the user
99         $newUser = $this->userRepo->createWithoutActivity($userData, $emailConfirmed);
100         $newUser->attachDefaultRole();
101
102         // Assign social account if given
103         if ($socialAccount) {
104             $newUser->socialAccounts()->save($socialAccount);
105         }
106
107         Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
108         Theme::dispatch(ThemeEvents::AUTH_REGISTER, $authSystem, $newUser);
109
110         // Start email confirmation flow if required
111         if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
112             $newUser->save();
113
114             try {
115                 $this->emailConfirmationService->sendConfirmation($newUser);
116                 session()->flash('sent-email-confirmation', true);
117             } catch (Exception $e) {
118                 $message = trans('auth.email_confirm_send_error');
119
120                 throw new UserRegistrationException($message, '/register/confirm');
121             }
122         }
123
124         return $newUser;
125     }
126
127     /**
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.
130      *
131      * @throws UserRegistrationException
132      */
133     protected function ensureEmailDomainAllowed(string $userEmail): void
134     {
135         $registrationRestrict = setting('registration-restrict');
136
137         if (!$registrationRestrict) {
138             return;
139         }
140
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';
145
146             throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect);
147         }
148     }
149 }