3 namespace BookStack\Auth\Access\Guards;
5 use BookStack\Auth\Access\LdapService;
6 use BookStack\Auth\User;
7 use BookStack\Auth\UserRepo;
8 use BookStack\Exceptions\LdapException;
9 use BookStack\Exceptions\LoginAttemptException;
10 use BookStack\Exceptions\LoginAttemptEmailNeededException;
11 use Illuminate\Contracts\Auth\UserProvider;
12 use Illuminate\Contracts\Session\Session;
14 class LdapSessionGuard extends ExternalBaseSessionGuard
17 protected $ldapService;
20 * LdapSessionGuard constructor.
22 public function __construct($name,
23 UserProvider $provider,
25 LdapService $ldapService,
29 $this->ldapService = $ldapService;
30 parent::__construct($name, $provider, $session, $userRepo);
34 * Validate a user's credentials.
36 * @param array $credentials
38 * @throws LdapException
40 public function validate(array $credentials = [])
42 $userDetails = $this->ldapService->getUserDetails($credentials['username']);
43 $this->lastAttempted = $this->provider->retrieveByCredentials([
44 'external_auth_id' => $userDetails['uid']
47 return $this->ldapService->validateUserCredentials($userDetails, $credentials['username'], $credentials['password']);
51 * Attempt to authenticate a user using the given credentials.
53 * @param array $credentials
54 * @param bool $remember
56 * @throws LoginAttemptEmailNeededException
57 * @throws LoginAttemptException
58 * @throws LdapException
60 public function attempt(array $credentials = [], $remember = false)
62 $username = $credentials['username'];
63 $userDetails = $this->ldapService->getUserDetails($username);
64 $this->lastAttempted = $user = $this->provider->retrieveByCredentials([
65 'external_auth_id' => $userDetails['uid']
68 if (!$this->ldapService->validateUserCredentials($userDetails, $username, $credentials['password'])) {
73 $user = $this->freshUserInstanceFromLdapUserDetails($userDetails);
76 $this->checkForUserEmail($user, $credentials['email'] ?? '');
77 $this->saveIfNew($user);
79 // Sync LDAP groups if required
80 if ($this->ldapService->shouldSyncGroups()) {
81 $this->ldapService->syncGroups($user, $username);
84 $this->login($user, $remember);
89 * Save the give user if they don't yet existing in the system.
90 * @throws LoginAttemptException
92 protected function saveIfNew(User $user)
98 // Check for existing users with same email
99 $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
101 throw new LoginAttemptException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
105 $this->userRepo->attachDefaultRole($user);
106 $this->userRepo->downloadAndAssignUserAvatar($user);
110 * Ensure the given user has an email.
111 * Takes the provided email in the request if a value is provided
112 * and the user does not have an existing email.
113 * @throws LoginAttemptEmailNeededException
115 protected function checkForUserEmail(User $user, string $providedEmail)
117 // Request email if missing from user and missing from request
118 if (is_null($user->email) && !$providedEmail) {
119 throw new LoginAttemptEmailNeededException();
122 // Add email to model if non-existing and email provided in request
123 if (!$user->exists && is_null($user->email) && $providedEmail) {
124 $user->email = $providedEmail;
129 * Create a fresh user instance from details provided by a LDAP lookup.
131 protected function freshUserInstanceFromLdapUserDetails(array $ldapUserDetails): User
135 $user->name = $ldapUserDetails['name'];
136 $user->external_auth_id = $ldapUserDetails['uid'];
137 $user->email = $ldapUserDetails['email'];
138 $user->email_confirmed = false;