1
|
|
|
<?php |
2
|
|
|
declare(strict_types = 1); |
3
|
|
|
|
4
|
|
|
/** |
5
|
|
|
* Micro |
6
|
|
|
* |
7
|
|
|
* @author Raffael Sahli <[email protected]> |
8
|
|
|
* @copyright Copryright (c) 2017 gyselroth GmbH (https://gyselroth.com) |
9
|
|
|
* @license MIT https://opensource.org/licenses/MIT |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Micro\Auth\Adapter\Basic; |
13
|
|
|
|
14
|
|
|
use \Micro\Auth\Exception; |
15
|
|
|
use \Psr\Log\LoggerInterface; |
16
|
|
|
use \Micro\Ldap as LdapServer; |
17
|
|
|
use \Micro\Auth\Adapter\AdapterInterface; |
18
|
|
|
use \Micro\Auth\Adapter\AbstractAdapter; |
19
|
|
|
|
20
|
|
|
class Ldap extends AbstractBasic |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* Ldap |
24
|
|
|
* |
25
|
|
|
* @var LdapServer |
26
|
|
|
*/ |
27
|
|
|
protected $ldap; |
28
|
|
|
|
29
|
|
|
|
30
|
|
|
/** |
31
|
|
|
* LDAP DN |
32
|
|
|
* |
33
|
|
|
* @var string |
34
|
|
|
*/ |
35
|
|
|
protected $ldap_dn; |
36
|
|
|
|
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* my account filter |
40
|
|
|
* |
41
|
|
|
* @var string |
42
|
|
|
*/ |
43
|
|
|
protected $account_filter = '(uid=%s)'; |
44
|
|
|
|
45
|
|
|
|
46
|
|
|
public function __construct(LdapServer $ldap, LoggerInterface $logger, ?Iterable $config=null) |
47
|
|
|
{ |
48
|
|
|
$this->logger = $logger; |
49
|
|
|
$this->ldap = $ldap; |
50
|
|
|
$this->setOptions($config); |
51
|
|
|
} |
52
|
|
|
|
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Set options |
56
|
|
|
* |
57
|
|
|
* @param Iterable $config |
58
|
|
|
* @return AdapterInterface |
59
|
|
|
*/ |
60
|
|
|
public function setOptions(? Iterable $config = null) : AdapterInterface |
61
|
|
|
{ |
62
|
|
|
if ($config === null) { |
63
|
|
|
return $this; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
foreach ($config as $option => $value) { |
67
|
|
|
switch ($option) { |
68
|
|
|
case 'account_filter': |
|
|
|
|
69
|
|
|
$this->account_filter = $value; |
70
|
|
|
unset($config[$option]); |
71
|
|
|
break; |
72
|
|
|
} |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
parent::setOptions($config); |
76
|
|
|
|
77
|
|
|
return $this; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* LDAP Auth |
83
|
|
|
* |
84
|
|
|
* @param string $username |
85
|
|
|
* @param string $password |
86
|
|
|
* @return bool |
87
|
|
|
*/ |
88
|
|
|
protected function plainAuth(string $username, string $password): bool |
89
|
|
|
{ |
90
|
|
|
$this->ldap->connect(); |
91
|
|
|
$resource = $this->ldap->getResource(); |
92
|
|
|
|
93
|
|
|
$esc_username = ldap_escape($username); |
94
|
|
|
$filter = htmlspecialchars_decode(sprintf($this->account_filter, $esc_username)); |
95
|
|
|
$result = ldap_search($resource, $this->ldap->getBase(), $filter, ['dn']); |
96
|
|
|
$entries = ldap_get_entries($resource, $result); |
97
|
|
|
|
98
|
|
|
if ($entries['count'] === 0) { |
99
|
|
|
$this->logger->warning("user not found with ldap filter [{$filter}]", [ |
100
|
|
|
'category' => get_class($this) |
101
|
|
|
]); |
102
|
|
|
|
103
|
|
|
return false; |
104
|
|
|
} elseif ($entries['count'] > 1) { |
105
|
|
|
$this->logger->warning("more than one user found with ldap filter [{$filter}]", [ |
106
|
|
|
'category' => get_class($this) |
107
|
|
|
]); |
108
|
|
|
|
109
|
|
|
return false; |
110
|
|
|
} |
111
|
|
|
|
112
|
|
|
$dn = $entries[0]['dn']; |
113
|
|
|
$this->logger->info("found ldap user [{$dn}] with filter [{$filter}]", [ |
114
|
|
|
'category' => get_class($this) |
115
|
|
|
]); |
116
|
|
|
|
117
|
|
|
$result = ldap_bind($resource, $dn, $password); |
118
|
|
|
$this->logger->info("bind ldap user [{$dn}]", [ |
119
|
|
|
'category' => get_class($this), |
120
|
|
|
'result' => $result |
121
|
|
|
]); |
122
|
|
|
|
123
|
|
|
if ($result === false) { |
124
|
|
|
return false; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$this->identifier = $username; |
128
|
|
|
$this->ldap_dn = $dn; |
129
|
|
|
|
130
|
|
|
return true; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
|
134
|
|
|
/** |
135
|
|
|
* Get attributes |
136
|
|
|
* |
137
|
|
|
* @return array |
138
|
|
|
*/ |
139
|
|
|
public function getAttributes(): array |
140
|
|
|
{ |
141
|
|
|
$search = []; |
142
|
|
|
foreach ($this->map as $attr => $value) { |
143
|
|
|
$search[] = $value['attr']; |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
$result = ldap_read($this->ldap->getResource(), $this->ldap_dn, '(objectClass=*)', $search); |
147
|
|
|
$entries = ldap_get_entries($this->ldap->getResource(), $result); |
148
|
|
|
$attributes = $entries[0]; |
149
|
|
|
|
150
|
|
|
$this->logger->info("get ldap user [{$this->ldap_dn}] attributes", [ |
151
|
|
|
'category' => get_class($this), |
152
|
|
|
'params' => $attributes, |
153
|
|
|
]); |
154
|
|
|
|
155
|
|
|
return $attributes; |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.