class_objects.inc 18.29 KiB
<?php
/*
  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
  Copyright (C) 2013-2016  FusionDirectory
  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.
  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
/*!
 * \brief Class for handling objects and their types
 * Allows to list, open, create and delete objects
class objects
  /*!
   * \brief Get list of object of objectTypes from $types in $ou
   * \param array   $types the objectTypes to list
   * \param mixed   $attrs The attributes to fetch.
   * If this is a single value, the resulting associative array will have for each dn the value of this attribute.
   * If this is an array, the keys must be the wanted attributes, and the values can be either 1, '*', 'b64' or 'raw'
   *  depending if you want a single value or an array of values. 'raw' means untouched LDAP value and is only useful for dns.
   *  Other values are considered to be 1. 'b64' means an array of base64 encoded values and is mainly useful through webservice for binary attributes.
   * \param string  $ou the LDAP branch to search in, base will be used if it is NULL
   * \param string  $filter an additional filter to use in the LDAP search. (Might use special _template_cn field to search in template cn).
   * \param boolean $checkAcl should ACL be ignored or checked? Defaults to FALSE.
   * \param string  $scope scope, defaults to subtree. When using one, be careful what you put in $ou.
   * \param boolean $templateSearch Whether to search for templates or normal objects.
   * \return The list of objects as an associative array (keys are dns)
  static function ls ($types, $attrs = NULL, string $ou = NULL, string $filter = '', bool $checkAcl = FALSE, string $scope = 'subtree', bool $templateSearch = FALSE, bool $sizeLimit = FALSE): array
    global $ui, $config;
    if ($ou === NULL) {
      $ou = $config->current['BASE'];
    if (!is_array($types)) {
      $types = [$types];
    if ($checkAcl) {
      if (count($types) > 1) {
        throw new FusionDirectoryException('Cannot evaluate ACL for several types');
      $infos  = static::infos(reset($types));
      $acl    = $infos['aclCategory'].'/'.$infos['mainTab'];
      $tplAcl = $infos['aclCategory'].'/template';
    $attrsAcls = [];
    if ($attrs === NULL) {
      if ($templateSearch) {
        $attrs = 'cn';
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
} else { $attrs = []; foreach ($types as $type) { $infos = static::infos($type); if ($infos['mainAttr']) { $attrs[] = $infos['mainAttr']; } } $attrs = array_unique($attrs); if (count($attrs) == 1) { $attrs = $attrs[0]; } elseif (count($attrs) == 0) { $attrs = ['dn' => 'raw']; } } } elseif ($checkAcl) { if (is_array($attrs)) { $search_attrs = array_keys($attrs); } else { $search_attrs = [$attrs]; } foreach ($search_attrs as $search_attr) { //Below str_replace allows us to remove the options, resulting in proper ACL inspection. (ACLs do not take options). $search_attr = preg_replace('/;x-.*/', '', $search_attr); $category = $ui->getAttributeCategory($types[0], $search_attr); if ($category === FALSE) { throw new FusionDirectoryException('Could not find ACL for attribute "'.$search_attr.'" for type "'.$types[0].'"'); } if ($category === TRUE) { continue; } if (strpos($ui->get_permissions($ou, $category, $search_attr), 'r') === FALSE) { $attrsAcls[$search_attr] = [$category, $search_attr]; } } } if (is_array($attrs)) { $search_attrs = array_keys($attrs); } else { $search_attrs = [$attrs]; } if ($templateSearch) { $search_attrs[] = 'fdTemplateField'; $search_attrs[] = 'cn'; } try { $ldap = static::search($types, $search_attrs, $ou, $filter, $checkAcl, $scope, $templateSearch, $partialFilterAcls, $sizeLimit); } catch (NonExistingBranchException $e) { return []; } $result = []; while ($fetched_attrs = $ldap->fetch()) { $key = $fetched_attrs['dn']; if ($checkAcl) { if (strpos($ui->get_permissions($key, $acl), 'r') === FALSE) { continue; } foreach ($partialFilterAcls as $partialFilterAcl) { if (strpos($ui->get_permissions($key, $partialFilterAcl[0], $partialFilterAcl[1]), 'r') === FALSE) { continue 2; } } } if (is_array($attrs)) { $result[$key] = []; foreach ($attrs as $attr => $mode) { if (isset($fetched_attrs[$attr])) { if (isset($attrsAcls[$attr]) &&
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
(strpos($ui->get_permissions($key, $attrsAcls[$attr][0], $attrsAcls[$attr][1]), 'r') === FALSE)) { continue; } switch ($mode) { case '*': unset($fetched_attrs[$attr]['count']); case 'raw': $result[$key][$attr] = $fetched_attrs[$attr]; break; case 'b64': unset($fetched_attrs[$attr]['count']); $result[$key][$attr] = array_map('base64_encode', $fetched_attrs[$attr]); break; case 1: default: $result[$key][$attr] = $fetched_attrs[$attr][0]; } } } if ($templateSearch) { if ( isset($fetched_attrs['cn']) && (!$checkAcl || (strpos($ui->get_permissions($key, $tplAcl, 'template_cn'), 'r') !== FALSE)) ) { $result[$key]['cn'] = $fetched_attrs['cn'][0]; } $result[$key]['fdTemplateField'] = []; foreach ($fetched_attrs['fdTemplateField'] as $templateField) { $attr = explode(':', $templateField, 2)[0]; if (isset($attrs[$attr])) { if (isset($attrsAcls[$attr]) && (strpos($ui->get_permissions($key, $attrsAcls[$attr][0], $attrsAcls[$attr][1]), 'r') === FALSE)) { continue; } $result[$key]['fdTemplateField'][] = $templateField; } } if (empty($result[$key]['fdTemplateField'])) { unset($result[$key]['fdTemplateField']); } } if (count($result[$key]) === 0) { unset($result[$key]); } } elseif ($templateSearch) { if ($attrs == 'cn') { if ( isset($fetched_attrs['cn']) && (!$checkAcl || (strpos($ui->get_permissions($key, $tplAcl, 'template_cn'), 'r') !== FALSE)) ) { $result[$key] = $fetched_attrs['cn'][0]; } } else { if (isset($attrsAcls[$attrs]) && (strpos($ui->get_permissions($key, $attrsAcls[$attrs][0], $attrsAcls[$attrs][1]), 'r') === FALSE)) { continue; } foreach ($fetched_attrs['fdTemplateField'] as $templateField) { list($attr, $value) = explode(':', $templateField, 2); if ($attrs == $attr) { $result[$key] = $value; break; } } } } elseif (isset($fetched_attrs[$attrs])) { if (isset($attrsAcls[$attrs]) && (strpos($ui->get_permissions($key, $attrsAcls[$attrs][0], $attrsAcls[$attrs][1]), 'r') === FALSE)) { continue; }
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
$result[$key] = $fetched_attrs[$attrs][0]; } } return $result; } /*! * \brief Get count of objects of objectTypes from $types in $ou * * \param array $types the objectTypes to list * \param string $ou the LDAP branch to search in, base will be used if it is NULL * \param string $filter an additional filter to use in the LDAP search. * \param boolean $checkAcl Should ACL be checked on the filtered attributes. * * \return The number of objects of type $type in $ou */ static function count ($types, string $ou = NULL, string $filter = '', bool $checkAcl = FALSE, bool $templateSearch = FALSE): int { try { $ldap = static::search($types, ['dn'], $ou, $filter, $checkAcl, 'subtree', $templateSearch, $partialFilterAcls); if (!empty($partialFilterAcls)) { throw new FusionDirectoryException('Not enough rights to use "'.$partialFilterAcls[0][1].'" in filter'); } } catch (EmptyFilterException $e) { return 0; } catch (NonExistingBranchException $e) { return 0; } return $ldap->count(); } 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 { global $config, $ui; $partialFilterAcls = []; if (!is_array($types)) { $types = [$types]; } if ($ou === NULL) { $ou = $config->current['BASE']; } $typeFilters = []; foreach ($types as $type) { $infos = static::infos($type); if ($infos['filter'] == '') { if ($infos['filterRDN'] == '') { continue; } else { $typeFilters[] = $infos['filterRDN']; } } elseif ($infos['filterRDN'] == '') { $typeFilters[] = $infos['filter']; } else { $typeFilters[] = '(&'.$infos['filter'].$infos['filterRDN'].')'; } } if (empty($typeFilters)) { throw new EmptyFilterException(); } $ldap = $config->get_ldap_link($sizeLimit); if (!$ldap->dn_exists($ou)) { throw new NonExistingBranchException($ou); } if (empty($filter)) {
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
$filter = '(|'.implode($typeFilters).')'; } else { if ($checkAcl) { if (count($types) > 1) { throw new FusionDirectoryException('Cannot evaluate ACL for several types'); } $filterObject = ldapFilter::parse($filter); $filterAttributes = $filterObject->listUsedAttributes(); unset($filterAttributes['_template_cn']); foreach ($filterAttributes as $acl) { $category = $ui->getAttributeCategory($types[0], $acl); if ($category === FALSE) { throw new FusionDirectoryException('Could not find ACL for attribute "'.$acl.'" for type "'.$types[0].'"'); } if ($category === TRUE) { continue; } if (strpos($ui->get_permissions($ou, $category, $acl), 'r') === FALSE) { $partialFilterAcls[] = [$category, $acl]; } } } if (!preg_match('/^\(.*\)$/', $filter)) { $filter = '('.$filter.')'; } $filter = '(&'.$filter.'(|'.implode($typeFilters).'))'; } if ($templateSearch) { $templateFilterObject = new ldapFilter( '&', [ new ldapFilterLeaf('objectClass', '=', 'fdTemplate'), fdTemplateFilter(ldapFilter::parse($filter)), ] ); $filter = "$templateFilterObject"; } else { $filterObject = fdNoTemplateFilter(ldapFilter::parse($filter)); $filter = "$filterObject"; } $ldap->cd($ou); $ldap->search($filter, $search_attrs, $scope); if (!$ldap->success()) { if ($sizeLimit && $ldap->hitSizeLimit()) { // Check for size limit exceeded messages for GUI feedback $ui->getSizeLimitHandler()->setLimitExceeded(); } else { throw new LDAPFailureException($ldap->get_error()); } } return $ldap; } /*! * \brief Create the tab object for the given dn * * \param string $type the objectType to open * \param string $dn the dn to open * * \return The created tab object */ static function open (string $dn, string $type): simpleTabs { $infos = static::infos($type); $tabClass = $infos['tabClass']; $tabObject = new $tabClass($type, $dn); logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $dn, "Openned as $type object"); return $tabObject;
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
} /** * @param string|array|null $text */ static function link (string $dn, string $type, string $subaction = '', $text = NULL, bool $icon = TRUE, bool $link = TRUE): string { global $config; $infos = static::infos($type); if ($link) { if (!isset($infos['management'])) { throw new NoManagementClassException('Asked for link for type "'.$type.'" but it does not have a management class'); } $pInfos = pluglist::pluginInfos($infos['management']); $index = $pInfos['INDEX']; $action = 'edit'; if ($subaction != '') { $action .= '_'.$subaction; } $href = "main.php?plug=$index&amp;reset=1&amp;act=listing_$action&amp;dn=".urlencode($dn); } if ($text === NULL) { $ldap = $config->get_ldap_link(); $ldap->cat($dn, [$infos['nameAttr']]); if ($attrs = $ldap->fetch()) { if (isset($attrs[$infos['nameAttr']][0])) { $text = $attrs[$infos['nameAttr']][0]; } else { $text = $dn; } } else { throw new NonExistingLdapNodeException($dn); } } elseif (is_array($text)) { $text = $text[$infos['nameAttr']][0]; } $text = htmlescape($text); if ($icon && isset($infos['icon'])) { $text = '<img alt="'.htmlescape($infos['name']).'" title="'.htmlescape($dn).'" src="'.htmlescape($infos['icon']).'" class="center"/>&nbsp;'.$text; } if ($link) { $text = '<a href="'.$href.'">'.$text.'</a>'; } return $text; } static function create (string $type): simpleTabs { return static::open('new', $type); } static function delete (string $dn, string $type, bool $checkAcl = TRUE): array { $tabObject = static::open($dn, $type); return $tabObject->delete($checkAcl); } static function createTemplate (string $type): simpleTabs { $infos = static::infos($type); $tabClass = $infos['tabClass']; /* Pass fake attrs object to force template mode */ $attrsObject = new stdClass();
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
$attrsObject->attrs = []; $attrsObject->is_template = TRUE; $tabObject = new $tabClass($type, 'new', $attrsObject); logging::debug(DEBUG_TRACE, __LINE__, __FUNCTION__, __FILE__, $type, 'Create template of type'); return $tabObject; } static function &infos (string $type): array { global $config; if (!isset($config->data['OBJECTS'][strtoupper($type)])) { throw new NonExistingObjectTypeException($type); } $infos =& $config->data['OBJECTS'][strtoupper($type)]; if (!isset($infos['filterRDN'])) { if (empty($infos['ou'])) { $infos['filterRDN'] = ''; } else { $parts = ldap_explode_dn(preg_replace('/,$/', '', $infos['ou']), 0); if ($parts !== FALSE) { unset($parts['count']); $dnFilter = []; foreach ($parts as $part) { preg_match('/([^=]+)=(.*)$/', $part, $m); $dnFilter[] = '('.$m[1].':dn:='.$m[2].')'; } if (count($dnFilter) > 1) { $infos['filterRDN'] = '(&'.implode('', $dnFilter).')'; } else { $infos['filterRDN'] = $dnFilter[0]; } } } } return $infos; } static function isOfType ($attrs, string $type): bool { $filter = static::getFilterObject($type); return $filter($attrs); } /* This method allows to cache parsed filter in filterObject key in objectTypes */ static function getFilterObject (string $type): ldapFilter { global $config; $infos =& static::infos($type); if (!isset($infos['filterObject'])) { $infos['filterObject'] = ldapFilter::parse($infos['filter']); } return $infos['filterObject']; } /* This method allows to cache searched attributes list in objectTypes */ static function getSearchedAttributes (string $type): array { global $config; $infos =& static::infos($type); if (!isset($infos['searchAttributes'])) {
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
$searchAttrs = []; if (!empty($infos['mainAttr'])) { $searchAttrs[$infos['mainAttr']] = $infos['aclCategory'].'/'.$infos['mainTab']; } if (!empty($infos['nameAttr'])) { $searchAttrs[$infos['nameAttr']] = $infos['aclCategory'].'/'.$infos['mainTab']; } foreach ($config->data['TABS'][$infos['tabGroup']] as $tab) { if (!plugin_available($tab['CLASS'])) { continue; } $plInfos = pluglist::pluginInfos($tab['CLASS']); if (isset($plInfos['plSearchAttrs'])) { foreach ($plInfos['plSearchAttrs'] as $attr) { $searchAttrs[$attr] = $infos['aclCategory'].'/'.$tab['CLASS']; } } } $infos['searchAttributes'] = $searchAttrs; } return $infos['searchAttributes']; } static function types (): array { global $config; return array_keys($config->data['OBJECTS']); } /* !\brief This method returns a list of all available templates for the given type */ static function getTemplates (string $type, string $requiredPermissions = 'r', string $filter = ''): array { global $config, $ui; $infos = static::infos($type); $templates = []; $departments = $config->getDepartmentList(); foreach ($departments as $key => $value) { // Search all templates from the current dn. try { $ldap = static::search($type, ['cn'], $infos['ou'].$value, $filter, FALSE, 'subtree', TRUE); } catch (NonExistingBranchException $e) { continue; } if ($ldap->count() != 0) { while ($attrs = $ldap->fetch()) { $dn = $attrs['dn']; if (($requiredPermissions != '') && !preg_match('/'.$requiredPermissions.'/', $ui->get_permissions($dn, $infos['aclCategory'].'/'.'template'))) { continue; } $templates[$dn] = $attrs['cn'][0].' - '.$key; } } } natcasesort($templates); reset($templates); return $templates; } }