Code

Initially added dyngroup. No qa yet. Not enabled yet.
authorcajus <cajus@594d385d-05f5-0310-b6e9-bd551577e9d8>
Fri, 28 May 2010 16:10:55 +0000 (16:10 +0000)
committercajus <cajus@594d385d-05f5-0310-b6e9-bd551577e9d8>
Fri, 28 May 2010 16:10:55 +0000 (16:10 +0000)
git-svn-id: https://oss.gonicus.de/repositories/gosa/trunk@18796 594d385d-05f5-0310-b6e9-bd551577e9d8

gosa-plugins/dyngroup/README [new file with mode: 0644]
gosa-plugins/dyngroup/plugin.dsc [new file with mode: 0644]
gosa-plugins/dyngroup/plugins/addons/dyngroup/classDynamicLdapGroup.inc [new file with mode: 0644]
gosa-plugins/dyngroup/plugins/addons/dyngroup/dyngroup.tpl [new file with mode: 0644]
gosa-plugins/dyngroup/schema/gosa-dyngroup.schema [new file with mode: 0644]
gosa-plugins/dyngroup/schema/gosa-samba3.schema.patch [new file with mode: 0644]

diff --git a/gosa-plugins/dyngroup/README b/gosa-plugins/dyngroup/README
new file mode 100644 (file)
index 0000000..579db6f
--- /dev/null
@@ -0,0 +1,100 @@
+# ----------------------------------------------------------------------------- #
+#  README                                                                       #
+#  Author(s): Thomas Chemineau - thomas.chemineau<at>gmail.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:
+
+    <tab class="DynamicLdapGroup" name="Dynamic group" />
+
+  Then, add the following line in the deptabs section:
+
+    <tab class="DynamicLdapGroup" name="Dynamic group" />
+
+  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 (file)
index 0000000..9bd952e
--- /dev/null
@@ -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 <gosa-pkg@oss.gonicus.de>"
+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 (file)
index 0000000..4278453
--- /dev/null
@@ -0,0 +1,365 @@
+<?php
+
+/*
+ * This code is part of GOsa (http://www.gosa-project.org)
+ * Copyright (C) 2003-2008 GONICUS GmbH
+ * Copyright (C) 2010 Thomas CHEMINEAU
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/**
+ * This class will allow user to parameter dynamic group.
+ * @author Thomas Chemineau - thomas.chemineau<at>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; $i<sizeof($value); $i++)
+          {
+            $values[$attribute][$i] = str_replace($old_dn, $new_dn, $values[$attribute][$i]);
+          }
+        }
+        $ldap->cd($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 (file)
index 0000000..a3e686b
--- /dev/null
@@ -0,0 +1,17 @@
+<table summary="" style="width:100%; vertical-align:top; text-align:left;" cellpadding="0" border="0">
+ <tr>
+  <td style="width:100%; vertical-align:top;">
+   <h2><img class="center" alt="" align="middle" src="images/rightarrow.png" />&nbsp;{t}Generic{/t}</h2>
+   <table summary="">
+    <tr>
+     <td><label for="{$memberURLAttributeLabel}">{t}{$memberURLAttributeLabel}{/t}</label></td>
+     <td>
+       {foreach item=line from=$memberURLAttributeValue}
+       <input size=70 name="{$memberURLAttributeLabel}" value="{$line}">
+       {/foreach}
+     </td>
+    </tr>
+   </table>
+  </td>
+ </tr>
+</table>
diff --git a/gosa-plugins/dyngroup/schema/gosa-dyngroup.schema b/gosa-plugins/dyngroup/schema/gosa-dyngroup.schema
new file mode 100644 (file)
index 0000000..2615f83
--- /dev/null
@@ -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 (file)
index 0000000..cb91f2a
--- /dev/null
@@ -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