FusionDirectory
class_objects.inc
1 <?php
2 
3 /*
4  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
5  Copyright (C) 2013-2016 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 
27 class objects
28 {
46  static function ls ($types, $attrs = NULL, string $ou = NULL, string $filter = '', bool $checkAcl = FALSE, string $scope = 'subtree', bool $templateSearch = FALSE, bool $sizeLimit = FALSE): array
47  {
48  global $ui, $config;
49 
50  if ($ou === NULL) {
51  $ou = $config->current['BASE'];
52  }
53 
54  if (!is_array($types)) {
55  $types = [$types];
56  }
57 
58  if ($checkAcl) {
59  if (count($types) > 1) {
60  throw new FusionDirectoryException('Cannot evaluate ACL for several types');
61  }
62  $infos = static::infos(reset($types));
63  $acl = $infos['aclCategory'].'/'.$infos['mainTab'];
64  $tplAcl = $infos['aclCategory'].'/template';
65  }
66 
67  $attrsAcls = [];
68  if ($attrs === NULL) {
69  if ($templateSearch) {
70  $attrs = 'cn';
71  } else {
72  $attrs = [];
73  foreach ($types as $type) {
74  $infos = static::infos($type);
75  if ($infos['mainAttr']) {
76  $attrs[] = $infos['mainAttr'];
77  }
78  }
79  $attrs = array_unique($attrs);
80  if (count($attrs) == 1) {
81  $attrs = $attrs[0];
82  } elseif (count($attrs) == 0) {
83  $attrs = ['dn' => 'raw'];
84  }
85  }
86  } elseif ($checkAcl) {
87  if (is_array($attrs)) {
88  $search_attrs = array_keys($attrs);
89  } else {
90  $search_attrs = [$attrs];
91  }
92  foreach ($search_attrs as $search_attr) {
93  //Below str_replace allows us to remove the options, resulting in proper ACL inspection. (ACLs do not take options).
94  $search_attr = preg_replace('/;x-.*/', '', $search_attr);
95  $category = $ui->getAttributeCategory($types[0], $search_attr);
96  if ($category === FALSE) {
97  throw new FusionDirectoryException('Could not find ACL for attribute "'.$search_attr.'" for type "'.$types[0].'"');
98  }
99  if ($category === TRUE) {
100  continue;
101  }
102  if (strpos($ui->get_permissions($ou, $category, $search_attr), 'r') === FALSE) {
103  $attrsAcls[$search_attr] = [$category, $search_attr];
104  }
105  }
106  }
107 
108  if (is_array($attrs)) {
109  $search_attrs = array_keys($attrs);
110  } else {
111  $search_attrs = [$attrs];
112  }
113  if ($templateSearch) {
114  $search_attrs[] = 'fdTemplateField';
115  $search_attrs[] = 'cn';
116  }
117 
118  try {
119  $ldap = static::search($types, $search_attrs, $ou, $filter, $checkAcl, $scope, $templateSearch, $partialFilterAcls, $sizeLimit);
120  } catch (NonExistingBranchException $e) {
121  return [];
122  }
123  $result = [];
124  while ($fetched_attrs = $ldap->fetch()) {
125  $key = $fetched_attrs['dn'];
126  if ($checkAcl) {
127  if (strpos($ui->get_permissions($key, $acl), 'r') === FALSE) {
128  continue;
129  }
130  foreach ($partialFilterAcls as $partialFilterAcl) {
131  if (strpos($ui->get_permissions($key, $partialFilterAcl[0], $partialFilterAcl[1]), 'r') === FALSE) {
132  continue 2;
133  }
134  }
135  }
136  if (is_array($attrs)) {
137  $result[$key] = [];
138  foreach ($attrs as $attr => $mode) {
139  if (isset($fetched_attrs[$attr])) {
140  if (isset($attrsAcls[$attr]) &&
141  (strpos($ui->get_permissions($key, $attrsAcls[$attr][0], $attrsAcls[$attr][1]), 'r') === FALSE)) {
142  continue;
143  }
144  switch ($mode) {
145  case '*':
146  unset($fetched_attrs[$attr]['count']);
147  case 'raw':
148  $result[$key][$attr] = $fetched_attrs[$attr];
149  break;
150  case 'b64':
151  unset($fetched_attrs[$attr]['count']);
152  $result[$key][$attr] = array_map('base64_encode', $fetched_attrs[$attr]);
153  break;
154  case 1:
155  default:
156  $result[$key][$attr] = $fetched_attrs[$attr][0];
157  }
158  }
159  }
160  if ($templateSearch) {
161  if (
162  isset($fetched_attrs['cn']) &&
163  (!$checkAcl || (strpos($ui->get_permissions($key, $tplAcl, 'template_cn'), 'r') !== FALSE))
164  ) {
165  $result[$key]['cn'] = $fetched_attrs['cn'][0];
166  }
167  $result[$key]['fdTemplateField'] = [];
168  foreach ($fetched_attrs['fdTemplateField'] as $templateField) {
169  $attr = explode(':', $templateField, 2)[0];
170  if (isset($attrs[$attr])) {
171  if (isset($attrsAcls[$attr]) &&
172  (strpos($ui->get_permissions($key, $attrsAcls[$attr][0], $attrsAcls[$attr][1]), 'r') === FALSE)) {
173  continue;
174  }
175  $result[$key]['fdTemplateField'][] = $templateField;
176  }
177  }
178  if (empty($result[$key]['fdTemplateField'])) {
179  unset($result[$key]['fdTemplateField']);
180  }
181  }
182  if (count($result[$key]) === 0) {
183  unset($result[$key]);
184  }
185  } elseif ($templateSearch) {
186  if ($attrs == 'cn') {
187  if (
188  isset($fetched_attrs['cn']) &&
189  (!$checkAcl || (strpos($ui->get_permissions($key, $tplAcl, 'template_cn'), 'r') !== FALSE))
190  ) {
191  $result[$key] = $fetched_attrs['cn'][0];
192  }
193  } else {
194  if (isset($attrsAcls[$attrs]) &&
195  (strpos($ui->get_permissions($key, $attrsAcls[$attrs][0], $attrsAcls[$attrs][1]), 'r') === FALSE)) {
196  continue;
197  }
198  foreach ($fetched_attrs['fdTemplateField'] as $templateField) {
199  list($attr, $value) = explode(':', $templateField, 2);
200  if ($attrs == $attr) {
201  $result[$key] = $value;
202  break;
203  }
204  }
205  }
206  } elseif (isset($fetched_attrs[$attrs])) {
207  if (isset($attrsAcls[$attrs]) &&
208  (strpos($ui->get_permissions($key, $attrsAcls[$attrs][0], $attrsAcls[$attrs][1]), 'r') === FALSE)) {
209  continue;
210  }
211  $result[$key] = $fetched_attrs[$attrs][0];
212  }
213  }
214  return $result;
215  }
216 
227  static function count ($types, string $ou = NULL, string $filter = '', bool $checkAcl = FALSE, bool $templateSearch = FALSE): int
228  {
229  try {
230  $ldap = static::search($types, ['dn'], $ou, $filter, $checkAcl, 'subtree', $templateSearch, $partialFilterAcls);
231  if (!empty($partialFilterAcls)) {
232  throw new FusionDirectoryException('Not enough rights to use "'.$partialFilterAcls[0][1].'" in filter');
233  }
234  } catch (EmptyFilterException $e) {
235  return 0;
236  } catch (NonExistingBranchException $e) {
237  return 0;
238  }
239  return $ldap->count();
240  }
241 
242  private static function search ($types, $search_attrs, string $ou = NULL, string $filter = '', bool $checkAcl = FALSE, string $scope = 'subtree', bool $templateSearch = FALSE, &$partialFilterAcls = [], bool $sizeLimit = FALSE): ldapMultiplexer
243  {
244  global $config, $ui;
245 
246  $partialFilterAcls = [];
247 
248  if (!is_array($types)) {
249  $types = [$types];
250  }
251 
252  if ($ou === NULL) {
253  $ou = $config->current['BASE'];
254  }
255 
256  $typeFilters = [];
257  foreach ($types as $type) {
258  $infos = static::infos($type);
259 
260  if ($infos['filter'] == '') {
261  if ($infos['filterRDN'] == '') {
262  continue;
263  } else {
264  $typeFilters[] = $infos['filterRDN'];
265  }
266  } elseif ($infos['filterRDN'] == '') {
267  $typeFilters[] = $infos['filter'];
268  } else {
269  $typeFilters[] = '(&'.$infos['filter'].$infos['filterRDN'].')';
270  }
271  }
272  if (empty($typeFilters)) {
273  throw new EmptyFilterException();
274  }
275 
276  $ldap = $config->get_ldap_link($sizeLimit);
277  if (!$ldap->dn_exists($ou)) {
278  throw new NonExistingBranchException($ou);
279  }
280  if (empty($filter)) {
281  $filter = '(|'.implode($typeFilters).')';
282  } else {
283  if ($checkAcl) {
284  if (count($types) > 1) {
285  throw new FusionDirectoryException('Cannot evaluate ACL for several types');
286  }
287  $filterObject = ldapFilter::parse($filter);
288  $filterAttributes = $filterObject->listUsedAttributes();
289  unset($filterAttributes['_template_cn']);
290  foreach ($filterAttributes as $acl) {
291  $category = $ui->getAttributeCategory($types[0], $acl);
292  if ($category === FALSE) {
293  throw new FusionDirectoryException('Could not find ACL for attribute "'.$acl.'" for type "'.$types[0].'"');
294  }
295  if ($category === TRUE) {
296  continue;
297  }
298  if (strpos($ui->get_permissions($ou, $category, $acl), 'r') === FALSE) {
299  $partialFilterAcls[] = [$category, $acl];
300  }
301  }
302  }
303  if (!preg_match('/^\(.*\)$/', $filter)) {
304  $filter = '('.$filter.')';
305  }
306  $filter = '(&'.$filter.'(|'.implode($typeFilters).'))';
307  }
308  if ($templateSearch) {
309  $templateFilterObject = new ldapFilter(
310  '&',
311  [
312  new ldapFilterLeaf('objectClass', '=', 'fdTemplate'),
313  fdTemplateFilter(ldapFilter::parse($filter)),
314  ]
315  );
316  $filter = "$templateFilterObject";
317  } else {
318  $filterObject = fdNoTemplateFilter(ldapFilter::parse($filter));
319  $filter = "$filterObject";
320  }
321  $ldap->cd($ou);
322  $ldap->search($filter, $search_attrs, $scope);
323  if (!$ldap->success()) {
324  if ($sizeLimit && $ldap->hitSizeLimit()) {
325  // Check for size limit exceeded messages for GUI feedback
326  $ui->getSizeLimitHandler()->setLimitExceeded();
327  } else {
328  throw new LDAPFailureException($ldap->get_error());
329  }
330  }
331  return $ldap;
332  }
333 
342  static function open (string $dn, string $type): simpleTabs
343  {
344  $infos = static::infos($type);
345  $tabClass = $infos['tabClass'];
346 
347  $tabObject = new $tabClass($type, $dn);
348  logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, "Openned as $type object");
349 
350  return $tabObject;
351  }
352 
356  static function link (string $dn, string $type, string $subaction = '', $text = NULL, bool $icon = TRUE, bool $link = TRUE): string
357  {
358  global $config;
359 
360  $infos = static::infos($type);
361  if ($link) {
362  if (!isset($infos['management'])) {
363  throw new NoManagementClassException('Asked for link for type "'.$type.'" but it does not have a management class');
364  }
365  $pInfos = pluglist::pluginInfos($infos['management']);
366  $index = $pInfos['INDEX'];
367  $action = 'edit';
368  if ($subaction != '') {
369  $action .= '_'.$subaction;
370  }
371  $href = "main.php?plug=$index&amp;reset=1&amp;act=listing_$action&amp;dn=".urlencode($dn);
372  }
373 
374  if ($text === NULL) {
375  $ldap = $config->get_ldap_link();
376  $ldap->cat($dn, [$infos['nameAttr']]);
377  if ($attrs = $ldap->fetch()) {
378  if (isset($attrs[$infos['nameAttr']][0])) {
379  $text = $attrs[$infos['nameAttr']][0];
380  } else {
381  $text = $dn;
382  }
383  } else {
384  throw new NonExistingLdapNodeException($dn);
385  }
386  } elseif (is_array($text)) {
387  $text = $text[$infos['nameAttr']][0];
388  }
389 
390  $text = htmlescape($text);
391 
392  if ($icon && isset($infos['icon'])) {
393  $text = '<img alt="'.htmlescape($infos['name']).'" title="'.htmlescape($dn).'" src="'.htmlescape($infos['icon']).'" class="center"/>&nbsp;'.$text;
394  }
395 
396  if ($link) {
397  $text = '<a href="'.$href.'">'.$text.'</a>';
398  }
399 
400  return $text;
401  }
402 
403  static function create (string $type): simpleTabs
404  {
405  return static::open('new', $type);
406  }
407 
408  static function delete (string $dn, string $type, bool $checkAcl = TRUE): array
409  {
410  $tabObject = static::open($dn, $type);
411  return $tabObject->delete($checkAcl);
412  }
413 
414  static function createTemplate (string $type): simpleTabs
415  {
416  $infos = static::infos($type);
417  $tabClass = $infos['tabClass'];
418 
419  /* Pass fake attrs object to force template mode */
420  $attrsObject = new stdClass();
421  $attrsObject->attrs = [];
422  $attrsObject->is_template = TRUE;
423 
424  $tabObject = new $tabClass($type, 'new', $attrsObject);
425  logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $type, 'Create template of type');
426 
427  return $tabObject;
428  }
429 
430  static function &infos (string $type): array
431  {
432  global $config;
433 
434  if (!isset($config->data['OBJECTS'][strtoupper($type)])) {
435  throw new NonExistingObjectTypeException($type);
436  }
437 
438  $infos =& $config->data['OBJECTS'][strtoupper($type)];
439 
440  if (!isset($infos['filterRDN'])) {
441  if (empty($infos['ou'])) {
442  $infos['filterRDN'] = '';
443  } else {
444  $parts = ldap_explode_dn(preg_replace('/,$/', '', $infos['ou']), 0);
445  if ($parts !== FALSE) {
446  unset($parts['count']);
447  $dnFilter = [];
448  foreach ($parts as $part) {
449  preg_match('/([^=]+)=(.*)$/', $part, $m);
450  $dnFilter[] = '('.$m[1].':dn:='.$m[2].')';
451  }
452  if (count($dnFilter) > 1) {
453  $infos['filterRDN'] = '(&'.implode('', $dnFilter).')';
454  } else {
455  $infos['filterRDN'] = $dnFilter[0];
456  }
457  }
458  }
459  }
460 
461  return $infos;
462  }
463 
464  static function isOfType ($attrs, string $type): bool
465  {
466  $filter = static::getFilterObject($type);
467  return $filter($attrs);
468  }
469 
470  /* This method allows to cache parsed filter in filterObject key in objectTypes */
471  static function getFilterObject (string $type): ldapFilter
472  {
473  global $config;
474 
475  $infos =& static::infos($type);
476 
477  if (!isset($infos['filterObject'])) {
478  $infos['filterObject'] = ldapFilter::parse($infos['filter']);
479  }
480  return $infos['filterObject'];
481  }
482 
483  /* This method allows to cache searched attributes list in objectTypes */
484  static function getSearchedAttributes (string $type): array
485  {
486  global $config;
487 
488  $infos =& static::infos($type);
489 
490  if (!isset($infos['searchAttributes'])) {
491  $searchAttrs = [];
492  if (!empty($infos['mainAttr'])) {
493  $searchAttrs[$infos['mainAttr']] = $infos['aclCategory'].'/'.$infos['mainTab'];
494  }
495  if (!empty($infos['nameAttr'])) {
496  $searchAttrs[$infos['nameAttr']] = $infos['aclCategory'].'/'.$infos['mainTab'];
497  }
498  foreach ($config->data['TABS'][$infos['tabGroup']] as $tab) {
499  if (!plugin_available($tab['CLASS'])) {
500  continue;
501  }
502  $plInfos = pluglist::pluginInfos($tab['CLASS']);
503  if (isset($plInfos['plSearchAttrs'])) {
504  foreach ($plInfos['plSearchAttrs'] as $attr) {
505  $searchAttrs[$attr] = $infos['aclCategory'].'/'.$tab['CLASS'];
506  }
507  }
508  }
509  $infos['searchAttributes'] = $searchAttrs;
510  }
511 
512  return $infos['searchAttributes'];
513  }
514 
515  static function types (): array
516  {
517  global $config;
518  return array_keys($config->data['OBJECTS']);
519  }
520 
521  /* !\brief This method returns a list of all available templates for the given type
522  */
523  static function getTemplates (string $type, string $requiredPermissions = 'r', string $filter = ''): array
524  {
525  global $config, $ui;
526 
527  $infos = static::infos($type);
528 
529  $templates = [];
530  $departments = $config->getDepartmentList();
531  foreach ($departments as $key => $value) {
532  // Search all templates from the current dn.
533  try {
534  $ldap = static::search($type, ['cn'], $infos['ou'].$value, $filter, FALSE, 'subtree', TRUE);
535  } catch (NonExistingBranchException $e) {
536  continue;
537  }
538  if ($ldap->count() != 0) {
539  while ($attrs = $ldap->fetch()) {
540  $dn = $attrs['dn'];
541  if (($requiredPermissions != '')
542  && !preg_match('/'.$requiredPermissions.'/', $ui->get_permissions($dn, $infos['aclCategory'].'/'.'template'))) {
543  continue;
544  }
545  $templates[$dn] = $attrs['cn'][0].' - '.$key;
546  }
547  }
548  }
549  natcasesort($templates);
550  reset($templates);
551  return $templates;
552  }
553 }
htmlescape(string $str)
Escape string for HTML output.
Definition: php_setup.inc:32
This class contains all function to manage tabs classes.
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...
static ls($types, $attrs=NULL, string $ou=NULL, string $filter='', bool $checkAcl=FALSE, string $scope='subtree', bool $templateSearch=FALSE, bool $sizeLimit=FALSE)
Get list of object of objectTypes from $types in $ou.
static count($types, string $ou=NULL, string $filter='', bool $checkAcl=FALSE, bool $templateSearch=FALSE)
Get count of objects of objectTypes from $types in $ou.
static open(string $dn, string $type)
Create the tab object for the given dn.
static debug(int $level, int $line, string $function, string $file, $data, string $info='')
Debug output method.
Parent class for all exceptions thrown in FusionDirectory.
Class for handling objects and their types.
plugin_available($plugin)
Check if plugin is available.
Definition: functions.inc:108
static link(string $dn, string $type, string $subaction='', $text=NULL, bool $icon=TRUE, bool $link=TRUE)