blob: 5a0ab4b5e0111930b2ec70503b2a2b86396b15d2 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
3namespace Adldap\Auth;
4
5use Exception;
6use Throwable;
7use Adldap\Auth\Events\Bound;
8use Adldap\Auth\Events\Failed;
9use Adldap\Auth\Events\Passed;
10use Adldap\Auth\Events\Binding;
11use Adldap\Auth\Events\Attempting;
12use Adldap\Events\DispatcherInterface;
13use Adldap\Connections\ConnectionInterface;
14use Adldap\Configuration\DomainConfiguration;
15
16/**
17 * Class Guard.
18 *
19 * Binds users to the current connection.
20 */
21class Guard implements GuardInterface
22{
23 /**
24 * The connection to bind to.
25 *
26 * @var ConnectionInterface
27 */
28 protected $connection;
29
30 /**
31 * The domain configuration to utilize.
32 *
33 * @var DomainConfiguration
34 */
35 protected $configuration;
36
37 /**
38 * The event dispatcher.
39 *
40 * @var DispatcherInterface
41 */
42 protected $events;
43
44 /**
45 * {@inheritdoc}
46 */
47 public function __construct(ConnectionInterface $connection, DomainConfiguration $configuration)
48 {
49 $this->connection = $connection;
50 $this->configuration = $configuration;
51 }
52
53 /**
54 * {@inheritdoc}
55 */
56 public function attempt($username, $password, $bindAsUser = false)
57 {
58 $this->validateCredentials($username, $password);
59
60 $this->fireAttemptingEvent($username, $password);
61
62 try {
63 $this->bind(
64 $this->applyPrefixAndSuffix($username),
65 $password
66 );
67
68 $result = true;
69
70 $this->firePassedEvent($username, $password);
71 } catch (BindException $e) {
72 // We'll catch the BindException here to allow
73 // developers to use a simple if / else
74 // using the attempt method.
75 $result = false;
76 }
77
78 // If we're not allowed to bind as the user,
79 // we'll rebind as administrator.
80 if ($bindAsUser === false) {
81 // We won't catch any BindException here so we can
82 // catch rebind failures. However this shouldn't
83 // occur if our credentials are correct
84 // in the first place.
85 $this->bindAsAdministrator();
86 }
87
88 return $result;
89 }
90
91 /**
92 * {@inheritdoc}
93 */
94 public function bind($username = null, $password = null)
95 {
96 $this->fireBindingEvent($username, $password);
97
98 try {
99 if (@$this->connection->bind($username, $password) === true) {
100 $this->fireBoundEvent($username, $password);
101 } else {
102 throw new Exception($this->connection->getLastError(), $this->connection->errNo());
103 }
104 } catch (Throwable $e) {
105 $this->fireFailedEvent($username, $password);
106
107 throw (new BindException($e->getMessage(), $e->getCode(), $e))
108 ->setDetailedError($this->connection->getDetailedError());
109 }
110 }
111
112 /**
113 * {@inheritdoc}
114 */
115 public function bindAsAdministrator()
116 {
117 $this->bind(
118 $this->configuration->get('username'),
119 $this->configuration->get('password')
120 );
121 }
122
123 /**
124 * Get the event dispatcher instance.
125 *
126 * @return DispatcherInterface
127 */
128 public function getDispatcher()
129 {
130 return $this->events;
131 }
132
133 /**
134 * Sets the event dispatcher instance.
135 *
136 * @param DispatcherInterface $dispatcher
137 *
138 * @return void
139 */
140 public function setDispatcher(DispatcherInterface $dispatcher)
141 {
142 $this->events = $dispatcher;
143 }
144
145 /**
146 * Applies the prefix and suffix to the given username.
147 *
148 * @param string $username
149 *
150 * @throws \Adldap\Configuration\ConfigurationException If account_suffix or account_prefix do not
151 * exist in the providers domain configuration
152 *
153 * @return string
154 */
155 protected function applyPrefixAndSuffix($username)
156 {
157 $prefix = $this->configuration->get('account_prefix');
158 $suffix = $this->configuration->get('account_suffix');
159
160 return $prefix.$username.$suffix;
161 }
162
163 /**
164 * Validates the specified username and password from being empty.
165 *
166 * @param string $username
167 * @param string $password
168 *
169 * @throws PasswordRequiredException When the given password is empty.
170 * @throws UsernameRequiredException When the given username is empty.
171 */
172 protected function validateCredentials($username, $password)
173 {
174 if (empty($username)) {
175 // Check for an empty username.
176 throw new UsernameRequiredException('A username must be specified.');
177 }
178
179 if (empty($password)) {
180 // Check for an empty password.
181 throw new PasswordRequiredException('A password must be specified.');
182 }
183 }
184
185 /**
186 * Fire the attempting event.
187 *
188 * @param string $username
189 * @param string $password
190 *
191 * @return void
192 */
193 protected function fireAttemptingEvent($username, $password)
194 {
195 if (isset($this->events)) {
196 $this->events->fire(new Attempting($this->connection, $username, $password));
197 }
198 }
199
200 /**
201 * Fire the passed event.
202 *
203 * @param string $username
204 * @param string $password
205 *
206 * @return void
207 */
208 protected function firePassedEvent($username, $password)
209 {
210 if (isset($this->events)) {
211 $this->events->fire(new Passed($this->connection, $username, $password));
212 }
213 }
214
215 /**
216 * Fire the failed event.
217 *
218 * @param string $username
219 * @param string $password
220 *
221 * @return void
222 */
223 protected function fireFailedEvent($username, $password)
224 {
225 if (isset($this->events)) {
226 $this->events->fire(new Failed($this->connection, $username, $password));
227 }
228 }
229
230 /**
231 * Fire the binding event.
232 *
233 * @param string $username
234 * @param string $password
235 *
236 * @return void
237 */
238 protected function fireBindingEvent($username, $password)
239 {
240 if (isset($this->events)) {
241 $this->events->fire(new Binding($this->connection, $username, $password));
242 }
243 }
244
245 /**
246 * Fire the bound event.
247 *
248 * @param string $username
249 * @param string $password
250 *
251 * @return void
252 */
253 protected function fireBoundEvent($username, $password)
254 {
255 if (isset($this->events)) {
256 $this->events->fire(new Bound($this->connection, $username, $password));
257 }
258 }
259}