diff --git a/lib/api/kolab_api_service_domain.php b/lib/api/kolab_api_service_domain.php index 9781eeb..8803109 100644 --- a/lib/api/kolab_api_service_domain.php +++ b/lib/api/kolab_api_service_domain.php @@ -1,422 +1,445 @@ <?php /* +--------------------------------------------------------------------------+ | This file is part of the Kolab Web Admin Panel | | | | Copyright (C) 2011-2012, Kolab Systems AG | | | | This program is free software: you can redistribute it and/or modify | | it under the terms of the GNU Affero General Public License as published | | by the Free Software Foundation, either version 3 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 Affero General Public License for more details. | | | | You should have received a copy of the GNU Affero General Public License | | along with this program. If not, see <http://www.gnu.org/licenses/> | +--------------------------------------------------------------------------+ | Author: Aleksander Machniak <machniak@kolabsys.com> | | Author: Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> | +--------------------------------------------------------------------------+ */ /** * Service providing domain mutations */ class kolab_api_service_domain extends kolab_api_service { /** * Returns service capabilities. * * @param string $domain Domain name * * @return array Capabilities list */ public function capabilities($domain) { $auth = Auth::get_instance(); $conf = Conf::get_instance(); $domain_base_dn = $conf->get('domain_base_dn'); if (empty($domain_base_dn)) { return array(); } $effective_rights = $auth->list_rights($domain_base_dn); $rights = array(); if (in_array('add', $effective_rights['entrylevelrights'])) { $rights['add'] = "w"; } if (in_array('delete', $effective_rights['entrylevelrights'])) { $rights['delete'] = "w"; } if (in_array('modrdn', $effective_rights['entrylevelrights'])) { $rights['edit'] = "w"; } if (in_array('read', $effective_rights['entrylevelrights'])) { $rights['find'] = "r"; $rights['info'] = "r"; } $rights['effective_rights'] = "r"; return $rights; } public function domain_add($getdata, $postdata) { Log::trace("domain.add(\$getdata = " . var_export($getdata, TRUE) . ", \$postdata = " . var_export($postdata, TRUE) . ")"); $conf = Conf::get_instance(); $dna = $conf->get('domain_name_attribute'); if (empty($dna)) { $dna = 'associateddomain'; } if (empty($postdata[$dna])) { Log::error("domain.add called without '" . $dna . "' specified"); return false; } $auth = Auth::get_instance($conf->get('kolab', 'primary_domain')); // parse input attributes $attributes = $this->parse_input_attributes('domain', $postdata); $attributes[$dna] = (array) $attributes[$dna]; $domain = array_shift($attributes[$dna]); $this->_mod_domain_attrs($domain, $attributes); $result = $auth->domain_add($domain, $attributes); if ($result) { if ($id = $this->unique_attribute_value($result)) { $attributes['id'] = $id; } $this->_after_domain_created($attributes, $domain); return $attributes; } return false; } /** * Domain delete. * * @param array $get GET parameters * @param array $post POST parameters * * @return bool True on success, False on failure * @throws kolab_api_exception */ public function domain_delete($getdata, $postdata) { Log::trace("domain.delete(\$getdata = '" . var_export($getdata, TRUE) . "', \$postdata = '" . var_export($postdata, TRUE) . "')"); if (empty($postdata['id'])) { Log::error("domain.delete called without a Domain ID"); return false; } $auth = Auth::get_instance(); // check if domain is empty if (empty($postdata['force']) || strtolower($postdata['force']) == 'false') { if (!$auth->domain_is_empty($postdata['id'])) { throw new kolab_api_exception(kolab_api_exception::DOMAIN_NOT_EMPTY); } } $result = $auth->domain_delete($postdata['id']); if ($result) { return $result; } return false; } public function domain_edit($getdata, $postdata) { Log::trace("domain.edit(\$getdata = '" . var_export($getdata, TRUE) . "', \$postdata = '" . var_export($postdata, TRUE) . "')"); if (empty($postdata['id'])) { Log::error("domain.edit called without a Domain ID"); return false; } $auth = Auth::get_instance(); // check if domain is empty when changing status to deleted, as in domain.delete if ($postdata['inetdomainstatus'] == 'deleted' && (empty($postdata['force']) || strtolower($postdata['force']) == 'false') ) { $domain = $auth->domain_info($postdata['id']); if (!empty($domain) && $domain[key($domain)]['inetdomainstatus'] != 'deleted' && !$auth->domain_is_empty($domain) ) { throw new kolab_api_exception(kolab_api_exception::DOMAIN_NOT_EMPTY); } } $attributes = $this->parse_input_attributes('domain', $postdata, $postdata['type_id']); - $this->_mod_domain_attrs(null, $attributes); + $this->_mod_domain_attrs(null, $attributes, $postdata['id']); $result = $auth->domain_edit($postdata['id'], $attributes, $postdata['type_id']); if ($result) { return $result; } return false; } public function domain_effective_rights($getdata, $postdata) { $auth = Auth::get_instance(); $conf = Conf::get_instance(); $dna = $conf->get('domain_name_attribute'); if (empty($dna)) { $dna = 'associateddomain'; } // TODO: Input validation if (!empty($getdata[$dna])) { $entry_dn = $getdata[$dna]; $unique_attr = self::unique_attribute(); $domain = $auth->domain_find_by_attribute(array($unique_attr => $entry_dn)); if (!empty($domain)) { $entry_dn = key($domain); } } else { $entry_dn = $conf->get('ldap', 'domain_base_dn'); } // TODO: Fix searching the correct base_dn... Perhaps find the entry // first. $effective_rights = $auth->list_rights($entry_dn); return $effective_rights; } public function domain_find($getdata, $postdata) { $conf = Conf::get_instance(); $dna = $conf->get('domain_name_attribute'); if (empty($dna)) { $dna = 'associateddomain'; } if (empty($getdata[$dna])) { Log::error("domain.find called without a '" . $dna . "' parameter"); return false; } $auth = Auth::get_instance(); $domain = $auth->domain_find_by_attribute(array($dna => $getdata[$dna])); if (!empty($domain)) { return $domain; } return false; } /** * Domain information. * * @param array $get GET parameters * @param array $post POST parameters * * @return array|bool Domain attributes, False on error */ public function domain_info($getdata, $postdata) { Log::trace("domain.info(\$getdata = '" . var_export($getdata, TRUE) . "', \$postdata = '" . var_export($postdata, TRUE) . "')"); if (empty($getdata['id'])) { Log::error("domain.info called without a Domain ID"); return false; } $auth = Auth::get_instance(); $attrs = $this->object_attributes('domain'); $result = $auth->domain_info($getdata['id'], $attrs); // normalize result $result = $this->parse_result_attributes('domain', $result); if (empty($result['id'])) { $result['id'] = $getdata['id']; } if ($result) { return $result; } return false; } /** * Modify hosted domain attributes */ - protected function _mod_domain_attrs($domain, &$attributes) + protected function _mod_domain_attrs($domain, &$attributes, $olddn = null) { // Generate attributes (aci, inetdomainbasedn) for hosted domains $conf = Conf::get_instance(); if ($conf->get('kolab_wap', 'hosted_root_dn')) { $domain_name_attribute = $conf->get('ldap', 'domain_name_attribute'); $hosted_root_dn = $conf->get('kolab_wap', 'hosted_root_dn'); $mgmt_root_dn = $conf->get('kolab_wap', 'mgmt_root_dn'); if (empty($mgmt_root_dn)) { $mgmt_root_dn = $conf->get('root_dn'); } if (empty($domain_name_attribute)) { $domain_name_attribute = 'associateddomain'; } if (!is_array($attributes[$domain_name_attribute])) { $attributes[$domain_name_attribute] = (array) $attributes[$domain_name_attribute]; } if (empty($domain)) { $domain = $attributes[$domain_name_attribute][0]; } if (!in_array($domain, $attributes[$domain_name_attribute])) { array_unshift($attributes[$domain_name_attribute], $domain); } $domain_root_dn = 'ou=' . $domain . ',' . $hosted_root_dn; $aci = array( '(targetattr = "*")' . '(version 3.0; acl "Deny Unauthorized"; deny (all)' . '(userdn != "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . ' || ' . 'ldap:///ou=People,' . $domain_root_dn . '??sub?(objectclass=inetorgperson)") AND NOT ' . 'roledn = "ldap:///cn=kolab-admin,' . $mgmt_root_dn . '";)', '(targetattr != "userPassword")' . '(version 3.0;acl "Search Access";allow (read,compare,search)' . '(userdn = "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . ' || ' . 'ldap:///ou=People,' . $domain_root_dn . '??sub?(objectclass=inetorgperson)");)', '(targetattr = "*")' . '(version 3.0;acl "Kolab Administrators";allow (all)' . '(roledn = "ldap:///cn=kolab-admin,' . $domain_root_dn . ' || ' . 'ldap:///cn=kolab-admin,' . $mgmt_root_dn . '");)' ); - $attributes['aci'] = $aci; - $attributes['inetdomainbasedn'] = $domain_root_dn; + // Preserve important old aci and inetdomainbasedn attributes of the + // management and hosted domain. + if (!empty($olddn)) { + $auth = Auth::get_instance(); + $info = $auth->domain_info($olddn); + if (!empty($info)) { + $oldattributes = reset($info); + if (!empty($oldattributes['inetdomainbasedn']) + && $oldattributes['inetdomainbasedn'] == $hosted_root_dn + || "dc=".implode(",dc=",explode(".",$domain)) == $mgmt_root_dn + ) { + $aci = $oldattributes['aci']; + if (!empty($oldattributes['inetdomainbasedn'])) { + $domain_root_dn = $oldattributes['inetdomainbasedn']; + } else { + $domain_root_dn = null; + } + } + } + } + + $attributes['aci'] = $aci; + if (!empty($domain_root_dn)) { + $attributes['inetdomainbasedn'] = $domain_root_dn; + } $this->is_hosted = true; } } /** * Create LDAP object related to the new hosted domain */ protected function _after_domain_created($attributes, $domain) { if (!$this->is_hosted) { return; } $conf = Conf::get_instance(); $ou_service = $this->controller->get_service('ou'); $role_service = $this->controller->get_service('role'); $hosted_root_dn = $conf->get('kolab_wap', 'hosted_root_dn'); $mgmt_root_dn = $conf->get('kolab_wap', 'mgmt_root_dn'); $domain_root_dn = 'ou=' . $domain . ',' . $hosted_root_dn; if (empty($mgmt_root_dn)) { $mgmt_root_dn = $conf->get('root_dn'); } $ou_domain = array( 'ou' => $domain, 'base_dn' => $hosted_root_dn, 'description' => $domain, 'type_id' => 1, ); $ou_domain['aci'] = array( '(targetattr = "*")' . '(version 3.0;acl "Deny Unauthorized"; deny (all)' . '(userdn != "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . ' || ' . 'ldap:///ou=People,' . $domain_root_dn . '??sub?(objectclass=inetorgperson)") AND NOT ' . 'roledn = "ldap:///cn=kolab-admin,' . $mgmt_root_dn . '";)', '(targetattr != "userPassword")' . '(version 3.0;acl "Search Access";allow (read,compare,search,write)' . '(userdn = "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . ' || ' . 'ldap:///ou=People,' . $domain_root_dn . '??sub?(objectclass=inetorgperson)");)', '(targetattr = "*")' . '(version 3.0;acl "Kolab Administrators";allow (all)' . '(roledn = "ldap:///cn=kolab-admin,' . $domain_root_dn . ' || ' . 'ldap:///cn=kolab-admin,' . $mgmt_root_dn . '");)', '(target = "ldap:///ou=*,' . $domain_root_dn . '")(targetattr="objectclass || aci || ou")' . '(version 3.0;acl "Allow Domain sub-OU Registration"; allow (add)' . '(userdn = "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . '");)', '(target = "ldap:///uid=*,ou=People,' . $domain_root_dn . '")(targetattr="*")' . '(version 3.0;acl "Allow Domain First User Registration"; allow (add)' . '(userdn = "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . '");)', '(target = "ldap:///cn=*,' . $domain_root_dn . '")(targetattr="objectclass || cn")' . '(version 3.0;acl "Allow Domain Role Registration"; allow (add)' . '(userdn = "ldap:///uid=kolab-service,ou=Special Users,' . $mgmt_root_dn . '");)', ); $ou_service->ou_add(null, $ou_domain); // Add OU trees foreach (array('Groups', 'People', 'Resources', 'Shared Folders') as $item) { $ou = array( 'ou' => $item, 'base_dn' => $domain_root_dn, 'type_id' => 1, 'description' => $item, ); $ou_service->ou_add(null, $ou); } // Add an admin role $role = array( 'cn' => 'kolab-admin', 'description' => 'Domain Administrator', 'type_id' => 1, 'base_dn' => $domain_root_dn, ); $role_service->role_add(null, $role); } }