blob: 696cc40d471363b47259c13f3566ebd4d244584e [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace LdapRecord\Auth;
4
5use Exception;
6use LdapRecord\Auth\Events\Attempting;
7use LdapRecord\Auth\Events\Binding;
8use LdapRecord\Auth\Events\Bound;
9use LdapRecord\Auth\Events\Failed;
10use LdapRecord\Auth\Events\Passed;
11use LdapRecord\Configuration\DomainConfiguration;
12use LdapRecord\Events\DispatcherInterface;
13use LdapRecord\LdapInterface;
14
15class Guard
16{
17 /**
18 * The connection to bind to.
19 *
20 * @var LdapInterface
21 */
22 protected $connection;
23
24 /**
25 * The domain configuration to utilize.
26 *
27 * @var DomainConfiguration
28 */
29 protected $configuration;
30
31 /**
32 * The event dispatcher.
33 *
34 * @var DispatcherInterface
35 */
36 protected $events;
37
38 /**
39 * Constructor.
40 *
41 * @param LdapInterface $connection
42 * @param DomainConfiguration $configuration
43 */
44 public function __construct(LdapInterface $connection, DomainConfiguration $configuration)
45 {
46 $this->connection = $connection;
47 $this->configuration = $configuration;
48 }
49
50 /**
51 * Attempt binding a user to the LDAP server.
52 *
53 * @param string $username
54 * @param string $password
55 * @param bool $stayBound
56 *
57 * @throws UsernameRequiredException
58 * @throws PasswordRequiredException
59 *
60 * @return bool
61 */
62 public function attempt($username, $password, $stayBound = false)
63 {
64 switch (true) {
65 case empty($username):
66 throw new UsernameRequiredException('A username must be specified.');
67 case empty($password):
68 throw new PasswordRequiredException('A password must be specified.');
69 }
70
71 $this->fireAttemptingEvent($username, $password);
72
73 try {
74 $this->bind($username, $password);
75
76 $authenticated = true;
77
78 $this->firePassedEvent($username, $password);
79 } catch (BindException $e) {
80 $authenticated = false;
81 }
82
83 if (! $stayBound) {
84 $this->bindAsConfiguredUser();
85 }
86
87 return $authenticated;
88 }
89
90 /**
91 * Attempt binding a user to the LDAP server. Supports anonymous binding.
92 *
93 * @param string|null $username
94 * @param string|null $password
95 *
96 * @throws BindException
97 * @throws \LdapRecord\ConnectionException
98 */
99 public function bind($username = null, $password = null)
100 {
101 $this->fireBindingEvent($username, $password);
102
103 // Prior to binding, we will upgrade our connectivity to TLS on our current
104 // connection and ensure we are not already bound before upgrading.
105 // This is to prevent subsequent upgrading on several binds.
106 if ($this->connection->isUsingTLS() && ! $this->connection->isBound()) {
107 $this->connection->startTLS();
108 }
109
110 try {
111 if (! $this->connection->bind($username, $password)) {
112 throw new Exception($this->connection->getLastError(), $this->connection->errNo());
113 }
114
115 $this->fireBoundEvent($username, $password);
116 } catch (Exception $e) {
117 $this->fireFailedEvent($username, $password);
118
119 throw BindException::withDetailedError($e, $this->connection->getDetailedError());
120 }
121 }
122
123 /**
124 * Bind to the LDAP server using the configured username and password.
125 *
126 * @throws BindException
127 * @throws \LdapRecord\ConnectionException
128 * @throws \LdapRecord\Configuration\ConfigurationException
129 */
130 public function bindAsConfiguredUser()
131 {
132 $this->bind(
133 $this->configuration->get('username'),
134 $this->configuration->get('password')
135 );
136 }
137
138 /**
139 * Get the event dispatcher instance.
140 *
141 * @return DispatcherInterface
142 */
143 public function getDispatcher()
144 {
145 return $this->events;
146 }
147
148 /**
149 * Set the event dispatcher instance.
150 *
151 * @param DispatcherInterface $dispatcher
152 *
153 * @return void
154 */
155 public function setDispatcher(DispatcherInterface $dispatcher)
156 {
157 $this->events = $dispatcher;
158 }
159
160 /**
161 * Fire the attempting event.
162 *
163 * @param string $username
164 * @param string $password
165 *
166 * @return void
167 */
168 protected function fireAttemptingEvent($username, $password)
169 {
170 if (isset($this->events)) {
171 $this->events->fire(new Attempting($this->connection, $username, $password));
172 }
173 }
174
175 /**
176 * Fire the passed event.
177 *
178 * @param string $username
179 * @param string $password
180 *
181 * @return void
182 */
183 protected function firePassedEvent($username, $password)
184 {
185 if (isset($this->events)) {
186 $this->events->fire(new Passed($this->connection, $username, $password));
187 }
188 }
189
190 /**
191 * Fire the failed event.
192 *
193 * @param string $username
194 * @param string $password
195 *
196 * @return void
197 */
198 protected function fireFailedEvent($username, $password)
199 {
200 if (isset($this->events)) {
201 $this->events->fire(new Failed($this->connection, $username, $password));
202 }
203 }
204
205 /**
206 * Fire the binding event.
207 *
208 * @param string $username
209 * @param string $password
210 *
211 * @return void
212 */
213 protected function fireBindingEvent($username, $password)
214 {
215 if (isset($this->events)) {
216 $this->events->fire(new Binding($this->connection, $username, $password));
217 }
218 }
219
220 /**
221 * Fire the bound event.
222 *
223 * @param string $username
224 * @param string $password
225 *
226 * @return void
227 */
228 protected function fireBoundEvent($username, $password)
229 {
230 if (isset($this->events)) {
231 $this->events->fire(new Bound($this->connection, $username, $password));
232 }
233 }
234}