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;
21 * LdapSessionGuard constructor.
23 public function __construct($name,
24 UserProvider $provider,
26 LdapService $ldapService,
30 $this->ldapService = $ldapService;
31 $this->userRepo = $userRepo;
32 parent::__construct($name, $provider, $session);
36 * Validate a user's credentials.
38 * @param array $credentials
40 * @throws LdapException
42 public function validate(array $credentials = [])
44 $userDetails = $this->ldapService->getUserDetails($credentials['username']);
45 $this->lastAttempted = $this->provider->retrieveByCredentials([
46 'external_auth_id' => $userDetails['uid']
49 return $this->ldapService->validateUserCredentials($userDetails, $credentials['username'], $credentials['password']);
53 * Attempt to authenticate a user using the given credentials.
55 * @param array $credentials
56 * @param bool $remember
58 * @throws LoginAttemptEmailNeededException
59 * @throws LoginAttemptException
60 * @throws LdapException
62 public function attempt(array $credentials = [], $remember = false)
64 $username = $credentials['username'];
65 $userDetails = $this->ldapService->getUserDetails($username);
66 $this->lastAttempted = $user = $this->provider->retrieveByCredentials([
67 'external_auth_id' => $userDetails['uid']
70 if (!$this->ldapService->validateUserCredentials($userDetails, $username, $credentials['password'])) {
75 $user = $this->freshUserInstanceFromLdapUserDetails($userDetails);
78 $this->checkForUserEmail($user, $credentials['email'] ?? '');
79 $this->saveIfNew($user);
81 // Sync LDAP groups if required
82 if ($this->ldapService->shouldSyncGroups()) {
83 $this->ldapService->syncGroups($user, $username);
86 $this->login($user, $remember);
91 * Save the give user if they don't yet existing in the system.
92 * @throws LoginAttemptException
94 protected function saveIfNew(User $user)
100 // Check for existing users with same email
101 $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
103 throw new LoginAttemptException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
107 $this->userRepo->attachDefaultRole($user);
108 $this->userRepo->downloadAndAssignUserAvatar($user);
112 * Ensure the given user has an email.
113 * Takes the provided email in the request if a value is provided
114 * and the user does not have an existing email.
115 * @throws LoginAttemptEmailNeededException
117 protected function checkForUserEmail(User $user, string $providedEmail)
119 // Request email if missing from user and missing from request
120 if (is_null($user->email) && !$providedEmail) {
121 throw new LoginAttemptEmailNeededException();
124 // Add email to model if non-existing and email provided in request
125 if (!$user->exists && is_null($user->email) && $providedEmail) {
126 $user->email = $providedEmail;
131 * Create a fresh user instance from details provided by a LDAP lookup.
133 protected function freshUserInstanceFromLdapUserDetails(array $ldapUserDetails): User
137 $user->name = $ldapUserDetails['name'];
138 $user->external_auth_id = $ldapUserDetails['uid'];
139 $user->email = $ldapUserDetails['email'];
140 $user->email_confirmed = false;