FusionDirectory
class_ldapFilter.inc
Go to the documentation of this file.
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4 
5  Copyright (C) 2013-2020 FusionDirectory
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21 
37 {
38  static $operators = ['!', '&', '|'];
39 
40  protected $operator;
41  protected $subparts;
42 
43  function __construct ($operator, $subparts)
44  {
45  $this->operator = $operator;
46  $this->subparts = $subparts;
47  }
48 
49  function __toString ()
50  {
51  return '('.$this->operator.join($this->subparts).')';
52  }
53 
54  function __invoke ($array)
55  {
56  $stopValue = FALSE;
57  switch ($this->operator) {
58  case '!':
59  return !$this->subparts[0]($array);
60  case '|':
61  $stopValue = TRUE;
62  case '&':
63  foreach ($this->subparts as $subpart) {
64  if ($subpart($array) == $stopValue) {
65  return $stopValue;
66  }
67  }
68  return !$stopValue;
69  default:
70  die('Unknown operator');
71  }
72  }
73 
74  function getOperator ()
75  {
76  return $this->operator;
77  }
78 
79  function getSubparts ()
80  {
81  return $this->subparts;
82  }
83 
84  function listUsedAttributes (&$result = [])
85  {
86  foreach ($this->subparts as $subpart) {
87  $subpart->listUsedAttributes($result);
88  }
89  return $result;
90  }
91 
92  static function parse ($filter)
93  {
94  // Remove starting and ending parenthesis
95  $filter = preg_replace(['/^\\s*\\(/', '/\\)\\s*$/'], '', $filter);
96 
97  if (in_array($filter[0], ldapFilter::$operators)) {
98  $subfilters = [];
99  /* We need an ending parenthesis in order to catch last subpart correctly */
100  $filter .= ')';
101  $offset = 0;
102  $level = 0;
103  $open = 0;
104  while (preg_match('/[^\\\\](\\(|\\))/', $filter, $m, PREG_OFFSET_CAPTURE, $offset)) {
105  $offset = $m[0][1] + 1;
106  if ($m[1][0] == '(') {
107  if ($level == 0) {
108  $open = $m[1][1];
109  }
110  $level++;
111  } elseif ($m[1][0] == ')') {
112  $level--;
113  if ($level == 0) {
114  $subfilters[] = ldapFilter::parse(substr($filter, $open + 1, $m[0][1] - $open));
115  }
116  }
117  }
118  if (in_array($filter[0], ['&', '|']) && (count($subfilters) == 1)) {
119  /* Avoid empty levels */
120  return $subfilters[0];
121  } else {
122  return new ldapFilter($filter[0], $subfilters);
123  }
124  } elseif (preg_match('/^([^\\(\\)\\=\\>\\<]+)('.join('|', ldapFilterLeaf::$operators).')([^\\(\\)]*)$/', $filter, $m)) {
125  return new ldapFilterLeaf($m[1], $m[2], $m[3]);
126  } else {
127  throw new FusionDirectoryException('Failed to parse '.$filter);
128  }
129  }
130 }
131 
136 {
137  static $operators = ['=','~=','>=','<='];
138 
139  protected $pattern;
140  protected $dnFilter = FALSE;
141 
142  function __construct ($left, $operator, $right)
143  {
144  if (@strrpos($left, ':dn:', -4) !== FALSE) {
145  $this->dnFilter = TRUE;
146  $left = substr($left, 0, -4);
147  }
148  parent::__construct($operator, [$left, $right]);
149  if (($this->operator == '=') || ($this->operator == '~=')) {
150  $prefix = '';
151  $suffix = '';
152  if (preg_match('/^\\*/', $this->subparts[1])) {
153  $prefix = '.*';
154  }
155  if (preg_match('/\\*$/', $this->subparts[1])) {
156  $suffix = '.*';
157  }
158  $search = preg_replace(['/^\\*/','/\\*$/'], '', $this->subparts[1]);
159  if ($this->dnFilter) {
160  $this->pattern = '/'.$left.'='.$prefix.preg_quote($search, '/').$suffix.',/';
161  } elseif ($this->subparts[1] == '*') {
162  $this->pattern = '/^.*$/';
163  } else {
164  $this->pattern = '/^'.$prefix.preg_quote($search, '/').$suffix.'$/';
165  }
166  }
167  }
168 
169  function isDnFilter ()
170  {
171  return $this->dnFilter;
172  }
173 
174  function __toString ()
175  {
176  return '('.$this->subparts[0].($this->dnFilter ? ':dn:' : '').$this->operator.$this->subparts[1].')';
177  }
178 
179  function __invoke ($array)
180  {
181  if ($this->dnFilter) {
182  switch ($this->operator) {
183  case '~=':
184  trigger_error('Filter apply might not work as expected');
185  case '=':
186  return (isset($array['dn']) && preg_match($this->pattern, $array['dn']));
187  default:
188  die('Unsupported dn operator: '.$this->operator);
189  }
190  }
191  if (isset($array[$this->subparts[0]])) {
192  $values = $array[$this->subparts[0]];
193  if (!is_array($values)) {
194  $values = [$values];
195  }
196  foreach ($values as $value) {
197  switch ($this->operator) {
198  case '~=':
199  trigger_error('Filter apply might not work as expected');
200  case '=':
201  if (preg_match($this->pattern, $value)) {
202  return TRUE;
203  }
204  break;
205  case '<=':
206  if ($value <= $this->subparts[1]) {
207  return TRUE;
208  }
209  break;
210  case '>=':
211  if ($value >= $this->subparts[1]) {
212  return TRUE;
213  }
214  break;
215  default:
216  die('Unknown operator: '.$this->operator);
217  }
218  }
219  }
220  return FALSE;
221  }
222 
223  function listUsedAttributes (&$result = [])
224  {
225  if ($this->dnFilter) {
226  $result['dn'] = 'dn';
227  } else {
228  $result[$this->subparts[0]] = $this->subparts[0];
229  }
230  return $result;
231  }
232 }
233 
234 /* Converts a filter object to search for templates */
235 function fdTemplateFilter (ldapFilter $filter)
236 {
237  if ($filter instanceof ldapFilterLeaf) {
238  if ($filter->isDnFilter()) {
239  return $filter;
240  } elseif ($filter->getOperator() == '=') {
241  $subparts = $filter->getSubparts();
242  if ($subparts[0] == '_template_cn') {
243  return new ldapFilterLeaf('cn', '=', $subparts[1]);
244  } else {
245  return new ldapFilterLeaf('fdTemplateField', '=', $subparts[0].':'.$subparts[1]);
246  }
247  } else {
248  trigger_error('Not able to adapt this filter for templates');
249  }
250  } else {
251  $subparts = $filter->getSubparts();
252  foreach ($subparts as &$subpart) {
253  $subpart = fdTemplateFilter($subpart);
254  }
255  unset($subpart);
256  return new ldapFilter($filter->getOperator(), $subparts);
257  }
258  return $filter;
259 }
260 
261 /* Removes parts specific to templates from a search filter */
262 function fdNoTemplateFilter (ldapFilter $filter)
263 {
264  if ($filter instanceof ldapFilterLeaf) {
265  if (!$filter->isDnFilter()) {
266  $subparts = $filter->getSubparts();
267  if ($subparts[0] == '_template_cn') {
268  return FALSE;
269  }
270  }
271  } else {
272  $subparts = $filter->getSubparts();
273  foreach ($subparts as &$subpart) {
274  $subpart = fdNoTemplateFilter($subpart);
275  }
276  unset($subpart);
277  return new ldapFilter($filter->getOperator(), array_filter($subparts));
278  }
279  return $filter;
280 }
Leaf of an LDAP filter, for instance (objectClass=*)
This class allows to parse and execute on a array an LDAP filter Example: $filter = ldapFilter::parse...
Parent class for all exceptions thrown in FusionDirectory.