LdapQueries::sanitizeDn()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace kalanis\kw_mapper\Storage\Database\Dialects;
4
5
6
use kalanis\kw_mapper\Interfaces\IQueryBuilder;
7
use kalanis\kw_mapper\MapperException;
8
use kalanis\kw_mapper\Records\TFill;
9
use kalanis\kw_mapper\Storage\Shared\QueryBuilder;
10
11
12
/**
13
 * Class LdapQueries
14
 * @package kalanis\kw_mapper\Storage\Database\Dialects
15
 * LDAP queries
16
 * @link https://ldap.com/ldap-dns-and-rdns/
17
 * @link https://docs.microsoft.com/cs-cz/windows/win32/adsi/search-filter-syntax?redirectedfrom=MSDN
18
 * @link https://www.php.net/manual/en/function.ldap-search.php#28593
19
 * @link https://docs.ldap.com/specs/rfc4514.txt
20
 */
21
class LdapQueries
22
{
23
    use TFill;
24
25
    /** @var array<string, string> */
26
    protected array $sanitizer = [
27
        ' ' => '\20',
28
        '#' => '\23',
29
        '"' => '\22',
30
        '+' => '\2b',
31
        ',' => '\2c',
32
        ';' => '\3b',
33
        '<' => '\3c',
34
        '=' => '\3d',
35
        '>' => '\3e',
36
        '\\' => '\5c',
37
    ];
38
39
    /**
40
     * @param string $domain
41
     * @return string
42
     *
43
     * The domain is simple http link ->
44
     * http://username:[email protected]:9090/path/to/somewhere?
45
     * where hostname.tld is parsed as domain component and /path/to/somewhere as organization units
46
     */
47 1
    public function domainDn(string $domain): string
48
    {
49 1
        $parsed = parse_url($domain);
50
51 1
        if (false === $parsed || empty($parsed['host']) || empty($parsed['path'])) {
52 1
            return '';
53
        }
54
55 1
        $parts = explode('.', $parsed['host']);
56 1
        $tld = array_slice($parts, -1, 1);
57 1
        $domain = array_slice($parts, -2, 1);
58 1
        $trailed = array_filter(explode('/', $parsed['path']));
59
60 1
        $locators = [];
61 1
        $subs = count($trailed);
62 1
        if (2 < $subs) {
63 1
            $locators[] = 'uid=' . $this->sanitizeDn(strval(end($trailed)));
64 1
            $locators[] = 'ou=' . $this->sanitizeDn(strval(prev($trailed)));
65 1
            $locators[] = 'cn=' . $this->sanitizeDn(strval(prev($trailed)));
66 1
        } elseif (1 < $subs) {
67 1
            $locators[] = 'ou=' . $this->sanitizeDn(strval(end($trailed)));
68 1
            $locators[] = 'cn=' . $this->sanitizeDn(strval(prev($trailed)));
69 1
        } elseif ($subs) {
70 1
            $locators[] = 'cn=' . $this->sanitizeDn(strval(end($trailed)));
71
        }
72 1
        $locators[] = 'dc=' . $this->sanitizeDn(strval(reset($domain)));
73 1
        $locators[] = 'dc=' . $this->sanitizeDn(strval(reset($tld)));
74 1
        return implode(',', $locators);
75
    }
76
77 1
    public function userDn(string $domain, string $username): string
78
    {
79 1
        $parsed = parse_url($domain);
80
81 1
        if (false === $parsed || empty($parsed['host']) || empty($parsed['path'])) {
82 1
            return '';
83
        }
84
85 1
        $parts = explode('.', $parsed['host']);
86 1
        $tld = array_slice($parts, -1, 1);
87 1
        $domain = array_slice($parts, -2, 1);
88 1
        $trailed = array_filter(explode('/', $parsed['path']));
89
90 1
        $locators = [];
91 1
        $locators[] = 'uid=' . $this->sanitizeDn($username);
92 1
        $subs = count($trailed);
93 1
        if (1 < $subs) {
94 1
            $locators[] = 'ou=' . $this->sanitizeDn(strval(end($trailed)));
95 1
            $locators[] = 'cn=' . $this->sanitizeDn(strval(prev($trailed)));
96 1
        } elseif ($subs) {
97 1
            $locators[] = 'cn=' . $this->sanitizeDn(strval(end($trailed)));
98
        }
99 1
        $locators[] = 'dc=' . $this->sanitizeDn(strval(reset($domain)));
100 1
        $locators[] = 'dc=' . $this->sanitizeDn(strval(reset($tld)));
101 1
        return implode(',', $locators);
102
    }
103
104 2
    protected function sanitizeDn(string $dn): string
105
    {
106 2
        return strtr($dn, $this->sanitizer);
107
    }
108
109
    /**
110
     * @param QueryBuilder $builder
111
     * @return array<string|int, int|string|float|null>
112
     */
113 1
    public function changed(QueryBuilder $builder): array
114
    {
115 1
        $props = [];
116 1
        $params = $builder->getParams();
117 1
        foreach ($builder->getProperties() as $property) {
118 1
            $props[$property->getColumnName()] = $params[$property->getColumnKey()];
119
        }
120 1
        return $props;
121
    }
122
123
    /**
124
     * @param QueryBuilder $builder
125
     * @throws MapperException
126
     * @return string
127
     */
128 2
    public function filter(QueryBuilder $builder): string
129
    {
130 2
        $cond = [];
131 2
        foreach ($builder->getConditions() as $condition) {
132 2
            $cond[] = $this->addCompare($condition, $builder->getParams());
133
        }
134 2
        return sprintf('(%s%s)', $this->howMergeRules($builder), implode('', $cond));
135
    }
136
137 2
    protected function howMergeRules(QueryBuilder $builder): string
138
    {
139 2
        return (IQueryBuilder::RELATION_AND == $builder->getRelation()) ? '&' : '|';
140
    }
141
142
    /**
143
     * @param QueryBuilder\Condition $condition
144
     * @param array<string, int|string|float> $params
145
     * @throws MapperException
146
     * @return string
147
     */
148 2
    protected function addCompare(QueryBuilder\Condition $condition, array $params): string
149
    {
150 2
        if ($condition->isRaw()) {
151 1
            return strval($condition->getRaw());
152
        }
153 2
        $columnName = strval($condition->getColumnName());
154 2
        switch ($condition->getOperation()) {
155
            case IQueryBuilder::OPERATION_NULL:
156 1
                return sprintf('(%s=*)', $columnName);
157
            case IQueryBuilder::OPERATION_NNULL:
158 1
                return sprintf('(!(%s=*))', $columnName);
159
            case IQueryBuilder::OPERATION_EQ:
160 1
                return sprintf('(%s=%s)', $columnName, $params[strval($condition->getColumnKey())]);
161
            case IQueryBuilder::OPERATION_NEQ:
162 1
                return sprintf('(!(%s=%s))', $columnName, $params[strval($condition->getColumnKey())]);
163
            case IQueryBuilder::OPERATION_GT:
164 2
                return sprintf('(%s>%s)', $columnName, $params[strval($condition->getColumnKey())]);
165
            case IQueryBuilder::OPERATION_GTE:
166 1
                return sprintf('(%s>=%s)', $columnName, $params[strval($condition->getColumnKey())]);
167
            case IQueryBuilder::OPERATION_LT:
168 1
                return sprintf('(%s<%s)', $columnName, $params[strval($condition->getColumnKey())]);
169
            case IQueryBuilder::OPERATION_LTE:
170 1
                return sprintf('(%s<=%s)', $columnName, $params[strval($condition->getColumnKey())]);
171
            case IQueryBuilder::OPERATION_LIKE:
172 1
                return sprintf('(%s=%s)', $columnName, $this->changePercents(strval($params[strval($condition->getColumnKey())])));
173
            case IQueryBuilder::OPERATION_NLIKE:
174 1
                return sprintf('(!(%s=%s))', $columnName, $this->changePercents(strval($params[strval($condition->getColumnKey())])));
175
            case IQueryBuilder::OPERATION_IN:
176 1
                return sprintf('(|%s)', $this->changeIn($columnName, $condition->getColumnKey(), $params));
177
            case IQueryBuilder::OPERATION_NIN:
178 1
                return sprintf('(!(|%s))', $this->changeIn($columnName, $condition->getColumnKey(), $params));
179
            case IQueryBuilder::OPERATION_REXP:
180
            default:
181 1
                throw new MapperException(sprintf('Unknown operation *%s*!', $condition->getOperation()));
182
        }
183
    }
184
185 1
    protected function changePercents(string $in): string
186
    {
187 1
        return strtr($in, ['%' => '*']);
188
    }
189
190
    /**
191
     * @param string $columnName
192
     * @param string|string[] $keys
193
     * @param array<string, int|string|float> $params
194
     * @return string
195
     */
196 1
    protected function changeIn(string $columnName, $keys, array $params): string
197
    {
198 1
        if (!is_array($keys)) {
199 1
            $keys = (array) $keys;
200
        }
201 1
        if (empty($keys)) {
202 1
            return sprintf('(%s=0)', $columnName);
203
        }
204 1
        $vars = [];
205 1
        foreach ($keys as $key) {
206 1
            $val = isset($params[$key]) ? $params[$key] : '0';
207 1
            $vars[] = sprintf('(%s=%s)', $columnName, $val);
208
        }
209 1
        return implode('', $vars);
210
    }
211
}
212