]> BookStack Code Mirror - bookstack/blob - app/Auth/Access/Guards/LdapSessionGuard.php
223088d05c32f361a2058567aeedf9b7fdd74a24
[bookstack] / app / Auth / Access / Guards / LdapSessionGuard.php
1 <?php
2
3 namespace BookStack\Auth\Access\Guards;
4
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;
13
14 class LdapSessionGuard extends ExternalBaseSessionGuard
15 {
16
17     protected $ldapService;
18     protected $userRepo;
19
20     /**
21      * LdapSessionGuard constructor.
22      */
23     public function __construct($name,
24         UserProvider $provider,
25         Session $session,
26         LdapService $ldapService,
27         UserRepo $userRepo
28     )
29     {
30         $this->ldapService = $ldapService;
31         $this->userRepo = $userRepo;
32         parent::__construct($name, $provider, $session);
33     }
34
35     /**
36      * Validate a user's credentials.
37      *
38      * @param array $credentials
39      * @return bool
40      * @throws LdapException
41      */
42     public function validate(array $credentials = [])
43     {
44         $userDetails = $this->ldapService->getUserDetails($credentials['username']);
45         $this->lastAttempted = $this->provider->retrieveByCredentials([
46             'external_auth_id' => $userDetails['uid']
47         ]);
48
49         return $this->ldapService->validateUserCredentials($userDetails, $credentials['username'], $credentials['password']);
50     }
51
52     /**
53      * Attempt to authenticate a user using the given credentials.
54      *
55      * @param array $credentials
56      * @param bool $remember
57      * @return bool
58      * @throws LoginAttemptEmailNeededException
59      * @throws LoginAttemptException
60      * @throws LdapException
61      */
62     public function attempt(array $credentials = [], $remember = false)
63     {
64         $username = $credentials['username'];
65         $userDetails = $this->ldapService->getUserDetails($username);
66         $this->lastAttempted = $user = $this->provider->retrieveByCredentials([
67             'external_auth_id' => $userDetails['uid']
68         ]);
69
70         if (!$this->ldapService->validateUserCredentials($userDetails, $username, $credentials['password'])) {
71             return false;
72         }
73
74         if (is_null($user)) {
75             $user = $this->freshUserInstanceFromLdapUserDetails($userDetails);
76         }
77
78         $this->checkForUserEmail($user, $credentials['email'] ?? '');
79         $this->saveIfNew($user);
80
81         // Sync LDAP groups if required
82         if ($this->ldapService->shouldSyncGroups()) {
83             $this->ldapService->syncGroups($user, $username);
84         }
85
86         $this->login($user, $remember);
87         return true;
88     }
89
90     /**
91      * Save the give user if they don't yet existing in the system.
92      * @throws LoginAttemptException
93      */
94     protected function saveIfNew(User $user)
95     {
96         if ($user->exists) {
97             return;
98         }
99
100         // Check for existing users with same email
101         $alreadyUser = $user->newQuery()->where('email', '=', $user->email)->count() > 0;
102         if ($alreadyUser) {
103             throw new LoginAttemptException(trans('errors.error_user_exists_different_creds', ['email' => $user->email]));
104         }
105
106         $user->save();
107         $this->userRepo->attachDefaultRole($user);
108         $this->userRepo->downloadAndAssignUserAvatar($user);
109     }
110
111     /**
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
116      */
117     protected function checkForUserEmail(User $user, string $providedEmail)
118     {
119         // Request email if missing from user and missing from request
120         if (is_null($user->email) && !$providedEmail) {
121             throw new LoginAttemptEmailNeededException();
122         }
123
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;
127         }
128     }
129
130     /**
131      * Create a fresh user instance from details provided by a LDAP lookup.
132      */
133     protected function freshUserInstanceFromLdapUserDetails(array $ldapUserDetails): User
134     {
135         $user = new User();
136
137         $user->name = $ldapUserDetails['name'];
138         $user->external_auth_id = $ldapUserDetails['uid'];
139         $user->email = $ldapUserDetails['email'];
140         $user->email_confirmed = false;
141
142         return $user;
143     }
144
145 }