]> BookStack Code Mirror - bookstack/blob - app/Auth/Access/RegistrationService.php
Started work on details/summary blocks
[bookstack] / app / Auth / Access / RegistrationService.php
1 <?php
2
3 namespace BookStack\Auth\Access;
4
5 use BookStack\Actions\ActivityType;
6 use BookStack\Auth\SocialAccount;
7 use BookStack\Auth\User;
8 use BookStack\Auth\UserRepo;
9 use BookStack\Exceptions\UserRegistrationException;
10 use BookStack\Facades\Activity;
11 use BookStack\Facades\Theme;
12 use BookStack\Theming\ThemeEvents;
13 use Exception;
14 use Illuminate\Support\Str;
15
16 class RegistrationService
17 {
18     protected $userRepo;
19     protected $emailConfirmationService;
20
21     /**
22      * RegistrationService constructor.
23      */
24     public function __construct(UserRepo $userRepo, EmailConfirmationService $emailConfirmationService)
25     {
26         $this->userRepo = $userRepo;
27         $this->emailConfirmationService = $emailConfirmationService;
28     }
29
30     /**
31      * Check whether or not registrations are allowed in the app settings.
32      *
33      * @throws UserRegistrationException
34      */
35     public function ensureRegistrationAllowed()
36     {
37         if (!$this->registrationAllowed()) {
38             throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
39         }
40     }
41
42     /**
43      * Check if standard BookStack User registrations are currently allowed.
44      * Does not prevent external-auth based registration.
45      */
46     protected function registrationAllowed(): bool
47     {
48         $authMethod = config('auth.method');
49         $authMethodsWithRegistration = ['standard'];
50
51         return in_array($authMethod, $authMethodsWithRegistration) && setting('registration-enabled');
52     }
53
54     /**
55      * Attempt to find a user in the system otherwise register them as a new
56      * user. For use with external auth systems since password is auto-generated.
57      *
58      * @throws UserRegistrationException
59      */
60     public function findOrRegister(string $name, string $email, string $externalId): User
61     {
62         $user = User::query()
63             ->where('external_auth_id', '=', $externalId)
64             ->first();
65
66         if (is_null($user)) {
67             $userData = [
68                 'name'             => $name,
69                 'email'            => $email,
70                 'password'         => Str::random(32),
71                 'external_auth_id' => $externalId,
72             ];
73
74             $user = $this->registerUser($userData, null, false);
75         }
76
77         return $user;
78     }
79
80     /**
81      * The registrations flow for all users.
82      *
83      * @throws UserRegistrationException
84      */
85     public function registerUser(array $userData, ?SocialAccount $socialAccount = null, bool $emailConfirmed = false): User
86     {
87         $userEmail = $userData['email'];
88
89         // Email restriction
90         $this->ensureEmailDomainAllowed($userEmail);
91
92         // Ensure user does not already exist
93         $alreadyUser = !is_null($this->userRepo->getByEmail($userEmail));
94         if ($alreadyUser) {
95             throw new UserRegistrationException(trans('errors.error_user_exists_different_creds', ['email' => $userEmail]), '/login');
96         }
97
98         // Create the user
99         $newUser = $this->userRepo->registerNew($userData, $emailConfirmed);
100
101         // Assign social account if given
102         if ($socialAccount) {
103             $newUser->socialAccounts()->save($socialAccount);
104         }
105
106         Activity::add(ActivityType::AUTH_REGISTER, $socialAccount ?? $newUser);
107         Theme::dispatch(ThemeEvents::AUTH_REGISTER, $socialAccount ? $socialAccount->driver : auth()->getDefaultDriver(), $newUser);
108
109         // Start email confirmation flow if required
110         if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
111             $newUser->save();
112
113             try {
114                 $this->emailConfirmationService->sendConfirmation($newUser);
115                 session()->flash('sent-email-confirmation', true);
116             } catch (Exception $e) {
117                 $message = trans('auth.email_confirm_send_error');
118
119                 throw new UserRegistrationException($message, '/register/confirm');
120             }
121         }
122
123         return $newUser;
124     }
125
126     /**
127      * Ensure that the given email meets any active email domain registration restrictions.
128      * Throws if restrictions are active and the email does not match an allowed domain.
129      *
130      * @throws UserRegistrationException
131      */
132     protected function ensureEmailDomainAllowed(string $userEmail): void
133     {
134         $registrationRestrict = setting('registration-restrict');
135
136         if (!$registrationRestrict) {
137             return;
138         }
139
140         $restrictedEmailDomains = explode(',', str_replace(' ', '', $registrationRestrict));
141         $userEmailDomain = $domain = mb_substr(mb_strrchr($userEmail, '@'), 1);
142         if (!in_array($userEmailDomain, $restrictedEmailDomains)) {
143             $redirect = $this->registrationAllowed() ? '/register' : '/login';
144
145             throw new UserRegistrationException(trans('auth.registration_email_domain_invalid'), $redirect);
146         }
147     }
148 }