From b1f24b5475f14a3bbf18b24414be32c78a271333 Mon Sep 17 00:00:00 2001 From: cajus Date: Fri, 28 May 2010 16:10:55 +0000 Subject: [PATCH] Initially added dyngroup. No qa yet. Not enabled yet. git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@18796 594d385d-05f5-0310-b6e9-bd551577e9d8 --- gosa-plugins/dyngroup/README | 100 +++++ gosa-plugins/dyngroup/plugin.dsc | 8 + .../addons/dyngroup/classDynamicLdapGroup.inc | 365 ++++++++++++++++++ .../plugins/addons/dyngroup/dyngroup.tpl | 17 + .../dyngroup/schema/gosa-dyngroup.schema | 14 + .../dyngroup/schema/gosa-samba3.schema.patch | 16 + 6 files changed, 520 insertions(+) create mode 100644 gosa-plugins/dyngroup/README create mode 100644 gosa-plugins/dyngroup/plugin.dsc create mode 100644 gosa-plugins/dyngroup/plugins/addons/dyngroup/classDynamicLdapGroup.inc create mode 100644 gosa-plugins/dyngroup/plugins/addons/dyngroup/dyngroup.tpl create mode 100644 gosa-plugins/dyngroup/schema/gosa-dyngroup.schema create mode 100644 gosa-plugins/dyngroup/schema/gosa-samba3.schema.patch diff --git a/gosa-plugins/dyngroup/README b/gosa-plugins/dyngroup/README new file mode 100644 index 000000000..579db6fc0 --- /dev/null +++ b/gosa-plugins/dyngroup/README @@ -0,0 +1,100 @@ +# ----------------------------------------------------------------------------- # +# README # +# Author(s): Thomas Chemineau - thomas.chemineaugmail.com # +# ----------------------------------------------------------------------------- # + + +1. What this plugin can do ? + + This plugin allow administrator to modify LDAP groups to be populated through + dynamic list feature in OpenLDAP. + + To do that, you have to activate the dynlist overlay in OpenLDAP, and + configure the overlay as decribed bellow. Once the overlay is enabled, member + of a dynamic group will be auto populated. + + This plugin should be configured to appears in groups and departments, under + GOsa. A department could not be a dynamic group, but it can be renamed. This + operation could break LDAP search URLs into dynamic group definition. To + prevent this, this plugin could modify LDAP search URLs when departments and + groups are renamed into the LDAP tree. + + WARNINGS: + Be carefull, GOsa may manage uid into memberUid, and not DN. So, in this + particular case, you can not store DN into memberUid attribute. The main + drawback, in this particular case, is that you can not build LDAP URLs into + dynamic group to search for users directly. The alternative is to look for + memberUid into groups. + + +2. How to activate the dynlist overlay in OpenLDAP ? + + Edit the configuration file (slapd.conf), and put the following lines into + the definition of your database: + + overlay dynlist + dynlist-attrset gosaGroupOfURLs labeledURI + + See http://www.openldap.org/doc/admin24/overlays.html#Dynamic%20Lists to have + more informations on dynamic list overlay. + + If your OpenLDAP server loads modules dnamically, you have to load the + dynlist overlay but putting the following lines in the global section of the + configuration files: + + moduleload dynlist + + Finaly, if you do not want GOsa users to modify memberUid values, you could + add an ACL. This ACL will works only if GOsa is connected on your OpenLDAP + server under an application account (and not under the rootdn defined into + the configuration of your LDAP database in slapd.conf): + + # Disable modify on memberUid for all entries which contains + # gosaGroupOfURLs, because these are dynamic, and we do not want users to + # edit the memberUid attribute. + access to filter="objectClass=gosaGroupOfURLs" attrs=memberUid + by * read + + Verify that LDAP schemas of GOsa contains the definition of the objectclass + named "gosaGroupOfURLs". You have two solutions: the first one is to add it + into the schema named gosa-samba3: + + objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.21 + NAME 'gosaGroupOfURLs' + DESC 'Allow a group to be populated through a labeledURI values' + SUP top + AUXILIARY + MAY ( labeledURI ) ) + + The second one, recommended, is to copy the file gosa-dyngroup.schema into + your OpenLDAP schema directory. Then edit slapd.conf and add the inclusion + to this new schema. + + You can now restart your OpenLDAP server :) + + +3. How to enable this feature in GOsa ? + + It is very easy. Edit /etc/gosa/gosa.conf, and add the following line in + the grouptabs section: + + + + Then, add the following line in the deptabs section: + + + + Then, put the plugin in /usr/share/gosa/plugins/addons, and update GOsa cache + via the update-gosa command. + + +4. Known restrictions in OpenLDAP + + You can't search yet on memberUid in a filter: + http://www.openldap.org/lists/openldap-software/200812/msg00030.html + http://www.openldap.org/lists/openldap-software/200901/msg00079.html + + You have to prefer to use the LDAP compare operation: + http://www.openldap.org/lists/openldap-software/200909/msg00073.html + http://www.openldap.org/lists/openldap-software/200909/msg00125.html + diff --git a/gosa-plugins/dyngroup/plugin.dsc b/gosa-plugins/dyngroup/plugin.dsc new file mode 100644 index 000000000..9bd952eaf --- /dev/null +++ b/gosa-plugins/dyngroup/plugin.dsc @@ -0,0 +1,8 @@ +[gosa-plugin] +name = dyngroup +description = "dynamic group list feature in OpenLDAP" +version = 2.6.7 +author = "Thomas Chemineau - thomas.chemineau@gmail.com" +maintainer = "GOsa packages maintainers group " +homepage = https://oss.gonicus.de/labs/gosa-contrib/ + diff --git a/gosa-plugins/dyngroup/plugins/addons/dyngroup/classDynamicLdapGroup.inc b/gosa-plugins/dyngroup/plugins/addons/dyngroup/classDynamicLdapGroup.inc new file mode 100644 index 000000000..4278453bc --- /dev/null +++ b/gosa-plugins/dyngroup/plugins/addons/dyngroup/classDynamicLdapGroup.inc @@ -0,0 +1,365 @@ +gmail.com + * @version 0.01 + */ +class DynamicLdapGroup extends group +{ + + /** + * The attribute that will use GOsa to store LDAP URI. + * @var array + */ + public $attributes = array('labeledURI'); + + /** + * The objectClass that will use GOsa to identify a group as dynamic. + * @var array + */ + public $objectclasses = array('labeledURIObject'); + + /** + * Default value for the corresponding attribute found in the $this->attributes + * array of this plugin. + * @var string + */ + public $labeledURI = 'ldap:///dc=example,dc=com?memberUid?sub?(objectClass=posixGroup)'; + + /** + * Indicates if the feature is enabled or not. + * @var boolean + */ + private $_isEnabled = false; + + /** + * Indicates if this plugin is manualy activated. + * @var boolean + */ + private $_isManualyActivated = false; + + /** + * Store values of memberUrl. + * @var Array + */ + private $_memberUrls = Array(); + + /** + * Create this object. + * @param Array $config GOsa config. + * @param string $dn Current DN. + */ + public function __construct ($config, $dn) + { + parent::__construct($config, $dn); + $attr_label = $this->attributes[0]; + $this->$attr_label = str_replace('dc=example,dc=com', $this->dn, $this->$attr_label); + } + + /** + * This function is called by GOsa when the current group will be saved into the + * LDAP directory. It will check status of this plugin. + */ + public function check () + { + if (!$this->_isManualyActivated) + { + $this->_isEnabled = $this->isDynamicGroup(); + } + return Array(); + } + + /** + * Execute this plugin. + * @return string HTML to print. + */ + public function execute () + { + // + // Are we trying to modify state of this group ? If so, + // we can edit the current object. + // + if (isset($_POST['modify_state'])) + { + $this->_isEnabled = !$this->_isEnabled; + $this->_isManualyActivated = true; + } + // + // Found if this group is dynamic or not. If it is not dynamic, + // we propose to enable this feature on this group. But by default, + // we mark this feature disabled. + // + $this->check(); + // + // Display a message if this feature is disabled. + // + if (!$this->_isEnabled) + { + return $this->show_disable_header(msgPool::addFeaturesButton(_("Dynamic Group")), msgPool::featuresDisabled(_("Dynamic Group"))); + } + $display = $this->show_disable_header(msgPool::removeFeaturesButton(_("Dynamic Group")), msgPool::featuresEnabled(_("Dynamic Group"))); + // + // Now, we search for current attributes, and display them. + // + $this->save_object(); + $attr_label = $this->attributes[0]; + $attr_value = $this->$attr_label; + // Display values. + // + $smarty = get_smarty(); + $smarty->assign('memberURLAttributeLabel', $attr_label); + $smarty->assign('memberURLAttributeValue', $attr_value); + $display .= $smarty->fetch(get_template_path('dyngroup.tpl', TRUE, dirname(__FILE__))); + return $display; + } + + /** + * Return attributes values of an LDAP entry. + * @param String $dn DN of the LDAP entry. + * @param Array $attributes Attributes to look for. + * @return Array An associative array of requested values. + */ + public function getAttributesValues ($dn, $attributes = Array('dn')) + { + $ldap = $this->config->get_ldap_link(); + $ldap->cat($dn, $attributes); + if ($attrs = $ldap->fetch()) + { + $data = Array(); + foreach ($attributes as $attribute) + { + if (array_key_exists($attribute, $attrs) !== false) + { + $data[$attribute] = $attrs[$attribute]; + unset($data[$attribute]['count']); + } + } + if (sizeof($data) > 0) + { + return $data; + } + } + return false; + } + + /** + * Test if the current group is dynamic or not. + * The more simple way is to test if the objectclass exists into + * the entry. + * @return boolean True if this entry is considered as dynamic. + */ + public function isDynamicGroup () + { + $obj_ocs = $this->getAttributesValues($this->dn, Array('objectClass')); + if ($obj_ocs === false) + { + return false; + } + $obj_ocs = array_map('strtolower', $obj_ocs['objectClass']); + $plu_ocs = $this->objectclasses; + $plu_ocs_size = sizeof($this->objectclasses); + $found = 0; + for ($i=0; $i<$plu_ocs_size && $found<$plu_ocs_size; $i++) + { + $plu_oc = strtolower($plu_ocs[$i]); + if (in_array($plu_oc, $obj_ocs)) + { + $found++; + } + } + if ($found >= $plu_ocs_size) + { + return true; + } + return false; + } + + /** + * Static method to set ACL for this plugin. + */ + public static function plInfo() + { + return Array( + "plShortName" => _("dyngroup"), + "plDescription" => _("Dynamic group setting"), + "plSelfModify" => TRUE, + "plDepends" => Array(), + "plPriority" => 1, + "plSection" => Array("addon"), + "plCategory" => Array( + "groups" => Array( + "description" => _("Dynamic Groups"), + "objectClass" => "labeledURIObject" + ) + ), + "plProvidedAcls" => array( + 'labeledURI' => _('labeledURI'), + ) + ); + } + + /** + * This plugin does nothing when this method is invoked. + */ + public function remove_from_parent () + { + return; + } + + /** + * Modify search base for all URL of all dynamic groups objects into the LDAP + * directory. + */ + public function renameDNsInDynGroupsValues ($old_dn, $new_dn) + { + $ldap = $this->config->get_ldap_link(); + $ldap->cd($this->config->current['BASE']); + // + // Build the LDAP search filter. We take only LDAP entries which have all + // objectClasses and attributes defined by this plugin. + // + $filter = ''; + foreach ($this->objectclasses as $objectclass) + { + $filter .= '(objectClass=' . $objectclass . ')'; + } + foreach ($this->attributes as $attribute) + { + $filter .= '(' . $attribute . '=*)'; + } + $filter = '(&' . $filter . ')'; + // + // The search should return some LDAP entries. If so, performed modifications + // on values (delete the values, and add it again with correct search DN). + // + $ldap->search($filter, Array('dn')); + if ($attrs = $ldap->fetch()) + { + foreach ($attrs as $dn) + { + $values = $this->getAttributesValues($dn, $this->attributes); + if ($values === false || !is_array($values)) + { + continue; + } + foreach ($values as $attribute => $value) + { + for($i=0; $icd($dn); + $ldap->modify($values); + } + } + } + + /** + * This function is called when tab is undisplayed. For example, the current user + * wants to change other settings of this group, but not save it to the LDAP + * directory directly. + */ + public function save_object () + { + $cur_memberURLLabel = $this->attributes[0]; + $cur_memberURLValue = null; + if (isset($_POST[$cur_memberURLLabel])) + { + $cur_memberURLValue = $_POST[$cur_memberURLLabel]; + $this->_isManualyActivated = true; + } + else if (!$this->_isManualyActivated) + { + $obj_memberURLValue = $this->getAttributesValues($this->dn, $this->attributes); + if ($obj_memberURLValue !== false) + { + $cur_memberURLValue = $obj_memberURLValue[$cur_memberURLLabel]; + } + } + if (!is_null($cur_memberURLValue)) + { + $this->$cur_memberURLLabel = $cur_memberURLValue; + } + } + + /** + * That will add additionnal information into the current LDAP entry. + * If this plugin is disable, then it will remove any data that references + * this plugin into the LDAP directory. + * @return boolean + */ + public function save () + { + $ldap = $this->config->get_ldap_link(); + $ldap->cd($this->dn); + // + // We disable dynamic group feature for this group, when: + // - The feature should be disable and this group is not dynamic; + // - The attributes exists into the entry. + // + if (!$this->_isEnabled && $this->isDynamicGroup()) + { + if (array_key_exists($this->attributes[0], $this->attrs) !== false) + { + $data = Array( + $this->attributes[0] => Array() + ); + $ldap->modify($data); + } + $data = Array( + 'objectClass' => $this->objectclasses + ); + $ldap->rm($data); + } + // + // GOsa auto update $this->$attributeLabel with data found into + // forms. So, existing data will be saved, without doing anything. + // The operation is already done, no checks. + // + else if ($this->_isEnabled) + { + $data = Array( + 'objectClass' => $this->objectclasses + ); + $ldap->mod_add($data); + $attributeLabel = $this->attributes[0]; + $data = Array( + $this->attributes[0] => Array($this->$attributeLabel) + ); + $ldap->modify($data); + } + // + // Detect if the current entry is renamed. If so, search for all + // dynamic groups, and modify search basedn if necessary. + // + if (strcasecmp($this->orig_dn, $this->dn) != 0) + { + $this->renameDNsInDynGroupsValues($this->orig_dn, $this->dn); + } + } + +} + +?> diff --git a/gosa-plugins/dyngroup/plugins/addons/dyngroup/dyngroup.tpl b/gosa-plugins/dyngroup/plugins/addons/dyngroup/dyngroup.tpl new file mode 100644 index 000000000..a3e686b77 --- /dev/null +++ b/gosa-plugins/dyngroup/plugins/addons/dyngroup/dyngroup.tpl @@ -0,0 +1,17 @@ + + + + +
+

 {t}Generic{/t}

+ + + + + +
+ {foreach item=line from=$memberURLAttributeValue} + + {/foreach} +
+
diff --git a/gosa-plugins/dyngroup/schema/gosa-dyngroup.schema b/gosa-plugins/dyngroup/schema/gosa-dyngroup.schema new file mode 100644 index 000000000..2615f83ec --- /dev/null +++ b/gosa-plugins/dyngroup/schema/gosa-dyngroup.schema @@ -0,0 +1,14 @@ + +# +# OpenLDAP schema to add gosaGroupOfURLs objectClass. +# This objectClass is the same as defined by the OpenLDAP project, but it is +# defined as AUXILIARY. +# + +objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.21 + NAME 'gosaGroupOfURLs' + DESC 'Allow a group to be populated through a labeledURI values' + SUP top + AUXILIARY + MAY ( labeledURI ) ) + diff --git a/gosa-plugins/dyngroup/schema/gosa-samba3.schema.patch b/gosa-plugins/dyngroup/schema/gosa-samba3.schema.patch new file mode 100644 index 000000000..cb91f2aa6 --- /dev/null +++ b/gosa-plugins/dyngroup/schema/gosa-samba3.schema.patch @@ -0,0 +1,16 @@ +--- gosa-samba3.schema.old 2010-01-30 18:54:45.000000000 +0100 ++++ gosa-samba3.schema 2010-01-30 16:29:10.000000000 +0100 +@@ -395,6 +395,13 @@ + DESC 'Settings for gosa. Replaces parts of the gosa.conf. (v2.6)' SUP top STRUCTURAL + MAY ( gosaSetting ) ) + ++objectclass ( 1.3.6.1.4.1.10098.1.2.1.19.21 ++ NAME 'gosaGroupOfURLs' ++ DESC 'Allow a group to be populated through a labeledURI values' ++ SUP top ++ AUXILIARY ++ MAY ( labeledURI ) ) ++ + # GOto submenu entries + objectclass (1.3.6.1.4.1.10098.1.2.1.43 NAME 'gotoSubmenuEntry' + DESC 'GOto - contains environment settings (v2.6)' SUP top STRUCTURAL -- 2.30.2