3 commits - lib/api lib/Auth lib/Auth.php lib/client lib/ext lib/kolab_api_exception.php lib/kolab_client_task.php lib/locale public_html/js public_html/skins
Aleksander Machniak
machniak at kolabsys.com
Fri Jan 17 11:57:48 CET 2014
lib/Auth.php | 5
lib/Auth/LDAP.php | 428 +++++++++++++----------------
lib/api/kolab_api_service_domain.php | 30 +-
lib/api/kolab_api_service_domain_types.php | 2
lib/api/kolab_api_service_domains.php | 1
lib/client/kolab_client_task_domain.php | 19 +
lib/client/kolab_client_task_user.php | 7
lib/ext/Net/LDAP3.php | 28 +
lib/kolab_api_exception.php | 71 ++++
lib/kolab_client_task.php | 32 +-
lib/locale/en_US.php | 11
public_html/js/kolab_admin.js | 29 +
public_html/skins/default/style.css | 4
13 files changed, 420 insertions(+), 247 deletions(-)
New commits:
commit 11efdc8b637e3ebebf9bcb5fdc1fafe1c05f1a11
Author: Aleksander Machniak <alec at alec.pl>
Date: Fri Jan 17 11:57:22 2014 +0100
Refactored domain_add()
diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 5958166..a2e951f 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -159,9 +159,12 @@ class LDAP extends Net_LDAP3 {
$domain_base_dn = $this->conf->get('ldap', 'domain_base_dn');
$domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute');
+ $service_bind_dn = $this->conf->get('ldap', 'service_bind_dn');
$primary_domain = $this->conf->get('kolab', 'primary_domain');
- $_primary_domain = str_replace('.', '_', $primary_domain);
- $_domain = str_replace('.', '_', $domain);
+
+ if (empty($service_bind_dn)) {
+ $service_bind_dn = $this->conf->get('ldap', 'bind_dn');
+ }
if (empty($domain_name_attribute)) {
$domain_name_attribute = 'associateddomain';
@@ -190,124 +193,6 @@ class LDAP extends Net_LDAP3 {
$inetdomainbasedn = $this->_standard_root_dn($domain);
}
- $cn = str_replace(array(',', '='), array('\2C', '\3D'), $inetdomainbasedn);
-
- $dn = "cn=" . $cn . ",cn=mapping tree,cn=config";
- $attrs = array(
- 'objectclass' => array(
- 'top',
- 'extensibleObject',
- 'nsMappingTree',
- ),
- 'nsslapd-state' => 'backend',
- 'cn' => $inetdomainbasedn,
- 'nsslapd-backend' => $_domain,
- );
-
- $replica_hosts = $this->list_replicas();
-
- if (!empty($replica_hosts)) {
- foreach ($replica_hosts as $replica_host) {
- Log::trace("Iterating over replication partners (now: $replica_host)");
- $ldap = new Net_LDAP3($this->config);
- $ldap->config_set("log_hook", array($this, "_log"));
- $ldap->config_set('host', $replica_host);
- $ldap->config_set('hosts', array($replica_host));
- $ldap->connect();
- $ldap->bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
- $result = $ldap->add_entry($dn, $attrs);
-
- if (!$result) {
- Log::error("Error adding $dn to $replica_host");
- }
-
- $ldap->close();
- }
- } else {
- $this->add_entry($dn, $attrs);
- }
-
- $result = $this->_read("cn=" . $_primary_domain . ",cn=ldbm database,cn=plugins,cn=config", array('nsslapd-directory'));
- if (!$result) {
- $result = $this->_read("cn=" . $primary_domain . ",cn=ldbm database,cn=plugins,cn=config", array('nsslapd-directory'));
- }
-
- if (!$result) {
- $result = $this->_read("cn=userRoot,cn=ldbm database,cn=plugins,cn=config", array('nsslapd-directory'));
- }
-
- $this->_log(LOG_DEBUG, "Primary domain ldbm database configuration entry: " . var_export($result, true));
-
- $result = $result[key($result)];
- $orig_directory = $result['nsslapd-directory'];
- $directory = str_replace($_primary_domain, $_domain, $result['nsslapd-directory']);
-
- if ($directory == $orig_directory) {
- $directory = str_replace($primary_domain, $_domain, $result['nsslapd-directory']);
- }
-
- if ($directory == $orig_directory) {
- $directory = str_replace("userRoot", $_domain, $result['nsslapd-directory']);
- }
-
- $dn = "cn=" . $_domain . ",cn=ldbm database,cn=plugins,cn=config";
- $attrs = array(
- 'objectclass' => array(
- 'top',
- 'extensibleobject',
- 'nsbackendinstance',
- ),
- 'cn' => $_domain,
- 'nsslapd-suffix' => $inetdomainbasedn,
- 'nsslapd-cachesize' => '-1',
- 'nsslapd-cachememsize' => '10485760',
- 'nsslapd-readonly' => 'off',
- 'nsslapd-require-index' => 'off',
- 'nsslapd-dncachememsize' => '10485760'
- );
-
- if (!empty($replica_hosts)) {
- foreach ($replica_hosts as $replica_host) {
- $ldap = new Net_LDAP3($this->config);
- $ldap->config_set("log_hook", array($this, "_log"));
- $ldap->config_set('host', $replica_host);
- $ldap->config_set('hosts', array($replica_host));
- $ldap->connect();
- $ldap->bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
-
- $ldap->config_set('return_attributes', array('nsslapd-directory'));
- $result = $ldap->get_entry("cn=" . $_primary_domain . ",cn=ldbm database,cn=plugins,cn=config");
- if (!$result) {
- $result = $ldap->get_entry("cn=" . $primary_domain . ",cn=ldbm database,cn=plugins,cn=config");
- }
-
- if (!$result) {
- $result = $ldap->get_entry("cn=userRoot,cn=ldbm database,cn=plugins,cn=config");
- }
-
- $this->_log(LOG_DEBUG, "Primary domain ldbm database configuration entry: " . var_export($result, true));
-
- $result = $result[key($result)];
- $orig_directory = $result['nsslapd-directory'];
- $directory = str_replace($_primary_domain, $_domain, $result['nsslapd-directory']);
-
- if ($directory == $orig_directory) {
- $directory = str_replace($primary_domain, $_domain, $result['nsslapd-directory']);
- }
-
- if ($directory == $orig_directory) {
- $directory = str_replace("userRoot", $_domain, $result['nsslapd-directory']);
- }
-
- $attrs['nsslapd-directory'] = $directory;
-
- $ldap->add_entry($dn, $attrs);
- $ldap->close();
- }
- } else {
- $this->add_entry($dn, $attrs);
- }
-
// Query the ACI for the primary domain
if ($domain_entry = $this->_find_domain($primary_domain)) {
$domain_entry = array_shift($domain_entry);
@@ -332,13 +217,6 @@ class LDAP extends Net_LDAP3 {
$_aci = $aci;
}
- $service_bind_dn = $this->conf->get('ldap', 'service_bind_dn');
- if (empty($service_bind_dn)) {
- $service_bind_dn = $this->conf->get('ldap', 'bind_dn');
- }
-
- $dn = $inetdomainbasedn;
-
// @TODO: this list should be configurable or auto-created somehow
$self_attrs = array(
'carLicense', 'description', 'displayName', 'facsimileTelephoneNumber', 'homePhone',
@@ -352,31 +230,65 @@ class LDAP extends Net_LDAP3 {
$self_attrs = array_merge($self_attrs, array('kolabDelegate', 'kolabInvitationPolicy', 'kolabAllowSMTPSender'));
}
- $attrs = array(
- // @TODO: Probably just use ldap_explode_dn()
- 'dc' => substr($dn, (strpos($dn, '=')+1), ((strpos($dn, ',')-strpos($dn, '='))-1)),
- 'objectclass' => array(
- 'top',
- 'domain',
+ $_domain = str_replace('.', '_', $domain);
+ $dn = $inetdomainbasedn;
+ $cn = str_replace(array(',', '='), array('\2C', '\3D'), $dn);
+
+ // Additional domain entries in various trees
+ $entries = array(
+ "cn={$cn},cn=mapping tree,cn=config" => array(
+ 'objectclass' => array(
+ 'top',
+ 'extensibleObject',
+ 'nsMappingTree',
+ ),
+ 'nsslapd-state' => 'backend',
+ 'cn' => $inetdomainbasedn,
+ 'nsslapd-backend' => $_domain,
),
- 'aci' => array(
- // Self-modification
- "(targetattr = \"" . implode(" || ", $self_attrs) . "\")(version 3.0; acl \"Enable self write for common attributes\"; allow (read,compare,search,write) userdn=\"ldap:///self\";)",
- // Directory Administrators
- "(targetattr = \"*\")(version 3.0; acl \"Directory Administrators Group\"; allow (all) (groupdn=\"ldap:///cn=Directory Administrators," . $inetdomainbasedn . "\" or roledn=\"ldap:///cn=kolab-admin," . $inetdomainbasedn . "\");)",
- // Configuration Administrators
- "(targetattr = \"*\")(version 3.0; acl \"Configuration Administrators Group\"; allow (all) groupdn=\"ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot\";)",
- // Administrator users
- "(targetattr = \"*\")(version 3.0; acl \"Configuration Administrator\"; allow (all) userdn=\"ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot\";)",
- // SIE Group
- $_aci,
- // Search Access,
- "(targetattr != \"userPassword\") (version 3.0; acl \"Search Access\"; allow (read,compare,search) (userdn = \"ldap:///" . $inetdomainbasedn . "??sub?(objectclass=*)\");)",
- // Service Search Access
- "(targetattr = \"*\") (version 3.0; acl \"Service Search Access\"; allow (read,compare,search) (userdn = \"ldap:///" . $service_bind_dn . "\");)",
+ "cn={$_domain},cn=ldbm database,cn=plugins,cn=config" => array(
+ 'objectclass' => array(
+ 'top',
+ 'extensibleobject',
+ 'nsbackendinstance',
+ ),
+ 'cn' => $_domain,
+ 'nsslapd-suffix' => $inetdomainbasedn,
+ 'nsslapd-cachesize' => '-1',
+ 'nsslapd-cachememsize' => '10485760',
+ 'nsslapd-readonly' => 'off',
+ 'nsslapd-require-index' => 'off',
+ 'nsslapd-dncachememsize' => '10485760',
+ 'nsslapd-directory' => true, // will be replaced below
+ ),
+ $inetdomainbasedn => array(
+ // @TODO: Probably just use ldap_explode_dn()
+ 'dc' => substr($dn, (strpos($dn, '=')+1), ((strpos($dn, ',')-strpos($dn, '='))-1)),
+ 'objectclass' => array(
+ 'top',
+ 'domain',
+ ),
+ 'aci' => array(
+ // Self-modification
+ "(targetattr = \"" . implode(" || ", $self_attrs) . "\")(version 3.0; acl \"Enable self write for common attributes\"; allow (read,compare,search,write) userdn=\"ldap:///self\";)",
+ // Directory Administrators
+ "(targetattr = \"*\")(version 3.0; acl \"Directory Administrators Group\"; allow (all) (groupdn=\"ldap:///cn=Directory Administrators,{$inetdomainbasedn}\" or roledn=\"ldap:///cn=kolab-admin,{$inetdomainbasedn}\");)",
+ // Configuration Administrators
+ "(targetattr = \"*\")(version 3.0; acl \"Configuration Administrators Group\"; allow (all) groupdn=\"ldap:///cn=Configuration Administrators,ou=Groups,ou=TopologyManagement,o=NetscapeRoot\";)",
+ // Administrator users
+ "(targetattr = \"*\")(version 3.0; acl \"Configuration Administrator\"; allow (all) userdn=\"ldap:///uid=admin,ou=Administrators,ou=TopologyManagement,o=NetscapeRoot\";)",
+ // SIE Group
+ $_aci,
+ // Search Access,
+ "(targetattr != \"userPassword\") (version 3.0; acl \"Search Access\"; allow (read,compare,search) (userdn = \"ldap:///{$inetdomainbasedn}??sub?(objectclass=*)\");)",
+ // Service Search Access
+ "(targetattr = \"*\") (version 3.0; acl \"Service Search Access\"; allow (read,compare,search) (userdn = \"ldap:///{$service_bind_dn}\");)",
+ ),
),
);
+ $replica_hosts = $this->list_replicas();
+
if (!empty($replica_hosts)) {
foreach ($replica_hosts as $replica_host) {
$ldap = new Net_LDAP3($this->config);
@@ -385,84 +297,77 @@ class LDAP extends Net_LDAP3 {
$ldap->config_set('hosts', array($replica_host));
$ldap->connect();
$ldap->bind($_SESSION['user']->user_bind_dn, $_SESSION['user']->user_bind_pw);
- $ldap->add_entry($dn, $attrs);
+
+ foreach ($entries as $dn => $attrs) {
+ if (isset($attrs['nsslapd-directory'])) {
+ $attrs['nsslapd-directory'] = $this->nsslapd_directory($ldap, $domain);
+ }
+ if (!$ldap->add_entry($dn, $attrs)) {
+ Log::error("Error adding $dn to $replica_host");
+ }
+ }
$ldap->close();
}
- } else {
- $this->add_entry($dn, $attrs);
+ }
+ else {
+ foreach ($entries as $dn => $attrs) {
+ if (isset($attrs['nsslapd-directory'])) {
+ $attrs['nsslapd-directory'] = $this->nsslapd_directory($this, $domain);
+ }
+ if (!$this->add_entry($dn, $attrs)) {
+ Log::error("Error adding $dn");
+ }
+ }
}
if (!empty($replica_hosts)) {
$this->add_replication_agreements($inetdomainbasedn);
}
- $dn = "cn=Directory Administrators," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array(
- 'top',
- 'groupofuniquenames',
+ // add OUs, do this after adding replication agreements
+ $entries = array(
+ "cn=Directory Administrators,$inetdomainbasedn" => array(
+ 'cn' => 'Directory Administrators',
+ 'objectclass' => array('top', 'groupofuniquenames'),
+ 'uniquemember' => array('cn=Directory Manager'),
),
- 'cn' => 'Directory Administrators',
- 'uniquemember' => array(
- 'cn=Directory Manager'
+ "cn=kolab-admin,$inetdomainbasedn" => array(
+ 'cn' => 'kolab-admin',
+ 'objectclass' => array(
+ 'top',
+ 'ldapsubentry',
+ 'nsroledefinition',
+ 'nssimpleroledefinition',
+ 'nsmanagedroledefinition',
+ ),
),
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = "ou=Groups," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array('top', 'organizationalunit'),
- 'ou' => 'Groups',
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = "ou=People," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array('top', 'organizationalunit'),
- 'ou' => 'People',
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = "ou=Special Users," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array('top', 'organizationalunit'),
- 'ou' => 'Special Users',
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = "ou=Resources," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array('top', 'organizationalunit'),
- 'ou' => 'Resources',
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = "ou=Shared Folders," . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array('top', 'organizationalunit'),
- 'ou' => 'Shared Folders',
- );
-
- $this->add_entry($dn, $attrs);
-
- $dn = 'cn=kolab-admin,' . $inetdomainbasedn;
- $attrs = array(
- 'objectclass' => array(
- 'top',
- 'ldapsubentry',
- 'nsroledefinition',
- 'nssimpleroledefinition',
- 'nsmanagedroledefinition',
+ // @TODO: these OUs DN should be read from config
+ "ou=Groups,$inetdomainbasedn" => array(
+ 'ou' => 'Groups',
+ 'objectclass' => array('top', 'organizationalunit'),
+ ),
+ "ou=People,$inetdomainbasedn" => array(
+ 'ou' => 'People',
+ 'objectclass' => array('top', 'organizationalunit'),
+ ),
+ "ou=Special Users,$inetdomainbasedn" => array(
+ 'ou' => 'Special Users',
+ 'objectclass' => array('top', 'organizationalunit'),
+ ),
+ "ou=Resources,$inetdomainbasedn" => array(
+ 'ou' => 'Resources',
+ 'objectclass' => array('top', 'organizationalunit'),
+ ),
+ "ou=Shared Folders,$inetdomainbasedn" => array(
+ 'ou' => 'Shared Folders',
+ 'objectclass' => array('top', 'organizationalunit'),
),
- 'cn' => 'kolab-admin'
);
- $this->add_entry($dn, $attrs);
+ // create set of OUs and other domain entries
+ foreach ($entries as $dn => $attrs) {
+ $this->add_entry($dn, $attrs);
+ }
return true;
}
@@ -1595,6 +1500,40 @@ class LDAP extends Net_LDAP3 {
}
/**
+ * Finds nsslapd-directory for specified domain
+ */
+ protected function nsslapd_directory($ldap, $domain)
+ {
+ $primary_domain = $this->conf->get('kolab', 'primary_domain');
+ $_primary_domain = str_replace('.', '_', $primary_domain);
+ $_domain = str_replace('.', '_', $domain);
+ $roots = array($_primary_domain, $primary_domain, 'userRoot');
+
+ foreach ($roots as $root) {
+ if ($result = $ldap->get_entry("cn=$root,cn=ldbm database,cn=plugins,cn=config")) {
+ break;
+ }
+ }
+
+ $this->_log(LOG_DEBUG, "Primary domain ldbm database configuration entry: " . var_export($result, true));
+
+ $result = $result[key($result)];
+ $orig_directory = $result['nsslapd-directory'];
+ $directory = $orig_directory;
+
+ reset($roots);
+ foreach ($roots as $root) {
+ if ($directory == $orig_directory) {
+ $directory = str_replace($root, $_domain, $result['nsslapd-directory']);
+ }
+ }
+
+ $this->_log(LOG_DEBUG, "nsslapd-directory for domain $domain is $directory");
+
+ return $directory;
+ }
+
+ /**
* Get global handle for memcache access
*
* @return object Memcache
commit f434be6ad70510471bf1ccada311c869a5bc6f62
Author: Aleksander Machniak <alec at alec.pl>
Date: Fri Jan 17 11:02:18 2014 +0100
Delete domains by status change and related improvements e.g.
check if domain is empty and warn the user before setting status to deleted
TODO: shell script for real domains deletion
diff --git a/lib/Auth.php b/lib/Auth.php
index 40f3049..4aee7ad 100644
--- a/lib/Auth.php
+++ b/lib/Auth.php
@@ -234,6 +234,11 @@ class Auth {
return $this->auth_instance()->domain_info($domaindata);
}
+ public function domain_is_empty($domain)
+ {
+ return $this->auth_instance()->domain_is_empty($domain);
+ }
+
public function find_recipient($address)
{
return $this->auth_instance()->find_recipient($address);
diff --git a/lib/Auth/LDAP.php b/lib/Auth/LDAP.php
index 0075166..5958166 100644
--- a/lib/Auth/LDAP.php
+++ b/lib/Auth/LDAP.php
@@ -483,9 +483,18 @@ class LDAP extends Net_LDAP3 {
public function domain_delete($domain)
{
- $base_dn = $this->conf->get('ldap', 'domain_base_dn');
+ $domain = $this->domain_info($domain);
+
+ if (empty($domain)) {
+ return false;
+ }
+
+ $domain_dn = key($domain);
+ $attributes = array_merge($domain[$domain_dn], array('inetdomainstatus' => 'deleted'));
- return $this->entry_delete($domain, array(), $base_dn);
+ // for performance reasons we set only domain status,
+ // cronjob script should delete such domain later
+ return $this->modify_entry($domain_dn, $domain[$domain_dn], $attributes);
}
public function domain_find_by_attribute($attribute)
@@ -521,6 +530,38 @@ class LDAP extends Net_LDAP3 {
}
/**
+ * Checkes if specified domain is empty (no users assigned)
+ *
+ * @param string $domain Domain name
+ *
+ * @return bool True if domain is empty, False otherwise
+ */
+ public function domain_is_empty($domain)
+ {
+ $this->_log(LOG_DEBUG, "Auth::LDAP::domain_is_empty($domain)");
+
+ $domain_name_attribute = $this->conf->get('ldap', 'domain_name_attribute');
+
+ if (empty($domain_name_attribute)) {
+ $domain_name_attribute = 'associateddomain';
+ }
+
+ $domain = $this->domain_info($domain);
+
+ if (!empty($domain)) {
+ $domain_dn = key($domain);
+ $domain_name = $domain[$domain_dn][$domain_name_attribute];
+ }
+ else {
+ return false;
+ }
+
+ $result = $this->list_users(array('entrydn'), null, array('page_size' => 1), $domain_name);
+
+ return is_array($result) && $result['count'] == 0;
+ }
+
+ /**
* Proxy to parent function in order to enable us to insert our
* configuration.
*/
@@ -746,11 +787,11 @@ class LDAP extends Net_LDAP3 {
return $this->_list($base_dn, $filter, 'sub', $attributes, $search, $params);
}
- public function list_users($attributes = array(), $search = array(), $params = array())
+ public function list_users($attributes = array(), $search = array(), $params = array(), $domain = null)
{
- $this->_log(LOG_DEBUG, "Auth::LDAP::list_users(" . var_export($attributes, true) . ", " . var_export($search, true) . ", " . var_export($params, true));
+ $this->_log(LOG_DEBUG, "Auth::LDAP::list_users(" . var_export($attributes, true) . ", " . var_export($search, true) . ", " . var_export($params, true) . ", " . $domain . ")");
- $base_dn = $this->_subject_base_dn('user');
+ $base_dn = $this->_subject_base_dn('user', false, $domain);
$filter = $this->conf->get('user_filter');
if (empty($filter)) {
@@ -1189,9 +1230,13 @@ class LDAP extends Net_LDAP3 {
}
}
- private function _subject_base_dn($subject, $strict = false)
+ private function _subject_base_dn($subject, $strict = false, $domain = null)
{
- $subject_base_dn = $this->conf->get_raw($this->domain, $subject . "_base_dn");
+ if (empty($domain)) {
+ $domain = $this->domain;
+ }
+
+ $subject_base_dn = $this->conf->get_raw($domain, $subject . "_base_dn");
if (empty($subject_base_dn)) {
$subject_base_dn = $this->conf->get_raw("ldap", $subject . "_base_dn");
@@ -1203,10 +1248,10 @@ class LDAP extends Net_LDAP3 {
}
// Attempt to get a configured base_dn
- $base_dn = $this->conf->get($this->domain, "base_dn");
+ $base_dn = $this->conf->get($domain, "base_dn");
if (empty($base_dn)) {
- $base_dn = $this->domain_root_dn($this->domain);
+ $base_dn = $this->domain_root_dn($domain);
}
if (!empty($subject_base_dn)) {
diff --git a/lib/api/kolab_api_service_domain.php b/lib/api/kolab_api_service_domain.php
index 4d5fcaa..711b37b 100644
--- a/lib/api/kolab_api_service_domain.php
+++ b/lib/api/kolab_api_service_domain.php
@@ -105,6 +105,7 @@ class kolab_api_service_domain extends kolab_api_service
* @param array $post POST parameters
*
* @return bool True on success, False on failure
+ * @throws kolab_api_exception
*/
public function domain_delete($getdata, $postdata)
{
@@ -115,8 +116,15 @@ class kolab_api_service_domain extends kolab_api_service
return false;
}
- // TODO: Input validation
- $auth = Auth::get_instance();
+ $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) {
@@ -135,13 +143,25 @@ class kolab_api_service_domain extends kolab_api_service
return false;
}
- $auth = Auth::get_instance();
+ $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 ($domain['inetdomainstatus'] != 'deleted' && !$auth->domain_is_empty($postdata['id'])) {
+ throw new kolab_api_exception(kolab_api_exception::DOMAIN_NOT_EMPTY);
+ }
+ }
+
+
$attributes = $this->parse_input_attributes('domain', $postdata);
$result = $auth->domain_edit($postdata['id'], $attributes, $postdata['type_id']);
- // @TODO: return unique attribute or all attributes as domain_add()
if ($result) {
- return true;
+ return $result;
}
return false;
diff --git a/lib/api/kolab_api_service_domain_types.php b/lib/api/kolab_api_service_domain_types.php
index 6829047..189b39f 100644
--- a/lib/api/kolab_api_service_domain_types.php
+++ b/lib/api/kolab_api_service_domain_types.php
@@ -41,7 +41,7 @@ class kolab_api_service_domain_types extends kolab_api_service
'optional' => true,
'type' => 'select',
'values' => array(
- '', 'active', 'suspended',
+ '', 'active', 'suspended', 'deleted',
),
),
),
diff --git a/lib/api/kolab_api_service_domains.php b/lib/api/kolab_api_service_domains.php
index ca9ea01..2373877 100644
--- a/lib/api/kolab_api_service_domains.php
+++ b/lib/api/kolab_api_service_domains.php
@@ -31,6 +31,7 @@ class kolab_api_service_domains extends kolab_api_service
public $list_attribs = array(
'associateddomain',
+ 'inetdomainstatus',
'objectclass',
'entrydn',
);
diff --git a/lib/client/kolab_client_task_domain.php b/lib/client/kolab_client_task_domain.php
index 4a1190a..5c23e8a 100644
--- a/lib/client/kolab_client_task_domain.php
+++ b/lib/client/kolab_client_task_domain.php
@@ -34,7 +34,7 @@ class kolab_client_task_domain extends kolab_client_task
'name' => array('associateddomain'),
);
- protected $list_attribs = array('associateddomain');
+ protected $list_attribs = array('associateddomain', 'inetdomainstatus');
/**
* Default action.
@@ -122,6 +122,7 @@ class kolab_client_task_domain extends kolab_client_task
'type_id' => 'system',
'type_id_name' => 'system',
'associateddomain' => 'system',
+ 'inetdomainstatus' => 'system',
);
//console("domain_form() \$data", $data);
@@ -190,12 +191,26 @@ class kolab_client_task_domain extends kolab_client_task
/**
* List item handler
*/
- protected function list_item_handler($item)
+ protected function list_item_handler($item, &$class)
{
+ if ($item['inetdomainstatus'] == 'deleted') {
+ $class[] = 'deleted';
+ }
+
if (is_array($item['associateddomain'])) {
return $item['associateddomain'][0];
}
return $item['associateddomain'];
}
+
+ /**
+ * Returns false when object matches current domain
+ */
+ protected function is_deletable($data)
+ {
+ // domain delete is done by setting inetdomainstatus to 'deleted'
+ return false;
+ }
+
}
diff --git a/lib/client/kolab_client_task_user.php b/lib/client/kolab_client_task_user.php
index 40c2fcc..6d51754 100644
--- a/lib/client/kolab_client_task_user.php
+++ b/lib/client/kolab_client_task_user.php
@@ -255,4 +255,11 @@ class kolab_client_task_user extends kolab_client_task
return empty($item['displayname']) ? $item['cn'] : $item['displayname'];
}
+ /**
+ * Returns true when current object matches logged user
+ */
+ protected function is_deletable($data)
+ {
+ return parent::is_deletable($data) && $data['id'] != $_SESSION['user']['id'];
+ }
}
diff --git a/lib/ext/Net/LDAP3.php b/lib/ext/Net/LDAP3.php
index 9a4ecb2..3f69613 100644
--- a/lib/ext/Net/LDAP3.php
+++ b/lib/ext/Net/LDAP3.php
@@ -677,7 +677,6 @@ class Net_LDAP3
if (ldap_delete($this->conn, $entry_dn) === false) {
$this->_debug("LDAP: S: " . ldap_error($this->conn));
- $this->_debug("LDAP: Delete failed. " . ldap_error($this->conn));
return false;
}
@@ -686,6 +685,32 @@ class Net_LDAP3
return true;
}
+ /**
+ * Deletes specified entry and all entries in the tree
+ */
+ public function delete_entry_recursive($entry_dn)
+ {
+ // searching for sub entries, but not scope sub, just one level
+ $this->config_set('return_attributes', array('entrydn'));
+ $result = $this->search($entry_dn, '(objectclass=*)', 'one');
+
+ if ($result) {
+ $entries = $result->entries(true);
+
+ foreach (array_keys($entries) as $sub_dn) {
+ if (!$this->delete_entry_recursive($sub_dn)) {
+ return false;
+ }
+ }
+ }
+
+ if (!$this->delete_entry($entry_dn)) {
+ return false;
+ }
+
+ return true;
+ }
+
public function effective_rights($subject)
{
$effective_rights_control_oid = "1.3.6.1.4.1.42.2.27.9.5.2";
@@ -1411,7 +1436,6 @@ class Net_LDAP3
if ($result) {
return $mod_array;
}
-
}
/**
diff --git a/lib/kolab_client_task.php b/lib/kolab_client_task.php
index 20c4bc0..8cac7e0 100644
--- a/lib/kolab_client_task.php
+++ b/lib/kolab_client_task.php
@@ -1358,16 +1358,13 @@ class kolab_client_task
));
}
- if (!empty($data['id']) && in_array('delete', (array) $data['effective_rights']['entry'])) {
+ // add delete button
+ if ($this->is_deletable($data)) {
$id = $data['id'];
-
- // disable delete for self
- if ($id != $_SESSION['user']['id']) {
- $form->add_button(array(
- 'value' => kolab_html::escape($this->translate('button.delete')),
- 'onclick' => "kadm.{$name}_delete('{$id}')",
- ));
- }
+ $form->add_button(array(
+ 'value' => kolab_html::escape($this->translate('button.delete')),
+ 'onclick' => "kadm.{$name}_delete('{$id}')",
+ ));
}
$ac_min_len = $this->config_get('autocomplete_min_length', 1, Conf::INT);
@@ -1378,12 +1375,21 @@ class kolab_client_task
$this->output->set_env('autocomplete_min_length', $ac_min_len);
$this->output->add_translation('form.required.empty', 'form.maxcount.exceeded',
$name . '.add.success', $name . '.edit.success', $name . '.delete.success',
- $name . '.delete.confirm', 'add', 'edit', 'delete');
+ $name . '.delete.confirm', $name . '.delete.force',
+ 'add', 'edit', 'delete');
return $form;
}
/**
+ * Check wether specified object can be deleted
+ */
+ protected function is_deletable($data)
+ {
+ return !empty($data['id']) && in_array('delete', (array) $data['effective_rights']['entry']);
+ }
+
+ /**
* Search form.
*
* @return string HTML output of the form
@@ -1564,8 +1570,10 @@ class kolab_client_task
continue;
}
+ $class = array('selectable');
+
if (method_exists($this, 'list_item_handler')) {
- $item = $this->list_item_handler($item);
+ $item = $this->list_item_handler($item, $class);
}
else {
$item = array_shift($item);
@@ -1579,7 +1587,7 @@ class kolab_client_task
$cells = array();
$cells[] = array('class' => 'name', 'body' => kolab_html::escape($item),
'onclick' => "kadm.command('$task.info', '$idx')");
- $rows[] = array('id' => $i, 'class' => 'selectable', 'cells' => $cells);
+ $rows[] = array('id' => $i, 'class' => implode(' ', $class), 'cells' => $cells);
}
}
else {
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index 5e288e9..7f59947 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -49,10 +49,11 @@ $LANG['domain.add'] = 'Add Domain';
$LANG['domain.add.success'] = 'Domain created successfully.';
$LANG['domain.associateddomain'] = 'Domain name(s)';
$LANG['domain.delete.confirm'] = 'Are you sure, you want to delete this domain?';
+$LANG['domain.delete.force'] = "There are users assigned to this domain.\nAre you sure, you want to delete this domain and all assigned objects?";
$LANG['domain.delete.success'] = 'Domain deleted successfully.';
$LANG['domain.edit'] = 'Edit domain';
$LANG['domain.edit.success'] = 'Domain updated successfully.';
-$LANG['domain.inetdomainbasedn'] = 'Custom Root DN(s)';
+$LANG['domain.inetdomainbasedn'] = 'Custom Root DN';
$LANG['domain.inetdomainstatus'] = 'Status';
$LANG['domain.list'] = 'Domains List';
$LANG['domain.norecords'] = 'No domain records found!';
diff --git a/public_html/js/kolab_admin.js b/public_html/js/kolab_admin.js
index e487688..87f355b 100644
--- a/public_html/js/kolab_admin.js
+++ b/public_html/js/kolab_admin.js
@@ -1406,6 +1406,7 @@ function kolab_admin()
this.domain_delete = function(id)
{
+ this.env.delete_domain_id = id;
this.delete_handler(id, 'domain');
};
@@ -1433,7 +1434,17 @@ function kolab_admin()
this.domain_delete_response = function(response)
{
- this.response_handler(response, 'domain.delete', 'domain.list', {refresh: 1});
+ // domain is not empty
+ if (response && response.code == 450) {
+ this.set_busy(false);
+
+ if (confirm(this.t('domain.delete.force'))) {
+ this.set_busy(true, 'deleting');
+ this.api_post('domain.delete', {'id': this.env.delete_domain_id, force: 1}, 'domain_delete_response');
+ }
+ }
+ else
+ this.response_handler(response, 'domain.delete', 'domain.list', {refresh: 1});
};
this.domain_add_response = function(response)
@@ -1443,7 +1454,21 @@ function kolab_admin()
this.domain_edit_response = function(response)
{
- this.response_handler(response, 'domain.edit', 'domain.list', {refresh: 1});
+ // domain is not empty and delete was requested
+ if (response && response.code == 450) {
+ this.set_busy(false);
+
+ // warn the user and re-send the request
+ if (confirm(this.t('domain.delete.force'))) {
+ var data = this.serialize_form('#'+this.env.form_id);
+ data.force = 1;
+
+ this.set_busy(true, 'saving');
+ this.api_post('domain.edit', data, 'domain_edit_response');
+ }
+ }
+ else
+ this.response_handler(response, 'domain.edit', 'domain.list', {refresh: 1});
};
this.user_info = function(id)
diff --git a/public_html/skins/default/style.css b/public_html/skins/default/style.css
index 6647caa..c6af986 100644
--- a/public_html/skins/default/style.css
+++ b/public_html/skins/default/style.css
@@ -95,6 +95,10 @@ table.list tbody tr td.empty-body {
text-align: center;
}
+table.list tbody tr.deleted td {
+ color: #999;
+}
+
fieldset {
border: 1px solid #d0d0d0;
border-radius: 3px;
commit 955f4186c5e3681226d01e5f4e77ff0af99f09d6
Author: Aleksander Machniak <alec at alec.pl>
Date: Thu Jan 16 12:19:38 2014 +0100
Add kolab_api_exception object for API responses
diff --git a/lib/kolab_api_exception.php b/lib/kolab_api_exception.php
new file mode 100644
index 0000000..c8d763d
--- /dev/null
+++ b/lib/kolab_api_exception.php
@@ -0,0 +1,71 @@
+<?php
+/*
+ +--------------------------------------------------------------------------+
+ | This file is part of the Kolab Web Admin Panel |
+ | |
+ | Copyright (C) 2011-2014, 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 at kolabsys.com> |
+ +--------------------------------------------------------------------------+
+*/
+
+/**
+ * Main exception class for Kolab Admin API responses
+ */
+class kolab_api_exception extends Exception
+{
+ const UNAUTHORIZED = 401;
+ const FORBIDDEN = 403;
+ const NOT_FOUND = 404;
+ const TIMEOUT = 408;
+ const DOMAIN_NOT_EMPTY = 450;
+ const SERVER_ERROR = 500;
+ const TEMP_ERROR = 503;
+
+ /**
+ * Constructor
+ */
+ function __construct()
+ {
+ $args = func_get_args();
+
+ if (isset($args[1])) {
+ $code = $args[1];
+ $message = $args[0];
+ }
+ else if (is_int($args[0])) {
+ $code = $args[0];
+ $message = null;
+ }
+ else {
+ $message = $args[0];
+ }
+
+ if (!$code) {
+ $code = self::SERVER_ERROR;
+ }
+
+ if (!$message) {
+ $message = kolab_api_controller::translate("error.$code");
+
+ if (!$message) {
+ $message = "Server error.";
+ }
+ }
+
+ parent::__construct($message, $code);
+ }
+}
diff --git a/lib/locale/en_US.php b/lib/locale/en_US.php
index bc42754..5e288e9 100644
--- a/lib/locale/en_US.php
+++ b/lib/locale/en_US.php
@@ -64,6 +64,14 @@ $LANG['domain.type_id'] = 'Standard Domain';
$LANG['edit'] = 'Edit';
$LANG['error'] = 'Error';
+$LANG['error.401'] = 'Unauthorized.';
+$LANG['error.403'] = 'Access forbidden.';
+$LANG['error.404'] = 'Object not found.';
+$LANG['error.408'] = 'Request timeout.';
+$LANG['error.450'] = 'Domain is not empty.';
+$LANG['error.500'] = 'Internal server error.';
+$LANG['error.503'] = 'Service unavailable. Try again later.';
+
$LANG['form.required.empty'] = 'Some of the required fields are empty!';
$LANG['form.maxcount.exceeded'] = 'Maximum count of items exceeded!';
More information about the commits
mailing list