sam + ldap patch

Neil Sequeira <[email protected]> Wed, 17 Sep 2003 22:54:37 -0400
Newsgroups gmane.comp.horde.sam
Organization NCS Consulting Inc.
Message-ID <[email protected]>
Here's a start at an LDAP backend for sam.  It needs the spamassassin 
ldap patch at http://bugzilla.spamassassin.org/show_bug.cgi?id=2205 
to be of any use.

Right now the user needs to be able to modify the spamassassin 
attribute of their ldap record (there's no 'rootdn' setting) and 
defaults are configured inside the backend parameters.

It's more-or-less a hack of spamd_sql but so far it seems get the job 
done.


	-neil


-- 
sam mailing list
Frequently Asked Questions: http://horde.org/faq/
To unsubscribe, mail: [email protected]
sam.ldap.patch (text/x-diff, 8.6 KB)
diff -ru -N sam/config/backends.php.dist sam.new/config/backends.php.dist
--- sam/config/backends.php.dist	2003-09-06 13:53:24.000000000 -0400
+++ sam.new/config/backends.php.dist	2003-09-17 22:37:48.000000000 -0400
@@ -121,3 +121,49 @@
         ),
     ),
 );
+
+/* SpamAssassin LDAP storage example. */
+$backends['spamd_ldap'] = array(
+    'preferred' => '',
+    'hordeauth' => true,
+    'driver' => 'spamd_ldap',
+    'params' => array(
+        'ldapserver' => 'localhost',
+        'basedn' => 'ou=users,dc=example,dc=com',
+	'attribute' => 'spamassassinConfig',
+	'uid' => 'uid',
+        'option_map' => array(
+            'hit_level' => 'required_hits',
+            'subject_tag' => 'subject_tag',
+            'rewrite_sub' => 'rewrite_subject',
+            'level_stars' => 'spam_level_stars',
+            'report_header' => 'report_header',
+            'terse_report' => 'use_terse_report',
+            'defang_mime' => 'defang_mime',
+            'skip_rbl' => 'skip_rbl_checks',
+        ),
+	'defaults' => array(
+	    'hit_level' => '5',
+	    'subject_tag' => '***SPAM***',
+	    'rewrite_sub' => 1,
+	    'level_stars' => 1,
+	    'report_header' => 0,
+	    'terse_report' => 1,
+	    'defang_mime' => 1,
+	    'skip_rbl' => 1,
+	),
+	'capabilities' => array(
+		'hit_level',
+		'report_header',
+		'rewrite_sub',
+		'subject_tag',
+		'level_stars',
+		'defang_mime',
+		'whitelist_to',
+		'whitelist_from',
+		'blacklist_to',
+		'blacklist_from',
+		'skip_rbl'
+	),
+    ),
+);
diff -ru -N sam/lib/Driver/spamd_ldap.php sam.new/lib/Driver/spamd_ldap.php
--- sam/lib/Driver/spamd_ldap.php	1969-12-31 19:00:00.000000000 -0500
+++ sam.new/lib/Driver/spamd_ldap.php	2003-09-17 22:35:56.000000000 -0400
@@ -0,0 +1,221 @@
+<?php
+/**
+ * SAM storage implementation for LDAP backend - based on spamd_sql.php.
+ * Requires SpamAssassin patch found at: http://bugzilla.spamassassin.org/show_bug.cgi?id=2205
+ *
+ * Required parameters:
+ * ====================
+ *   'ldapserver'   --  The hostname of the ldap server.
+ *   'basedn'       --  The password associated with 'username'.
+ *   'attribute'    --  The database type (ie. 'pgsql', 'mysql, etc.).
+ *   'uid'          --  The communication protocol ('tcp', 'unix', etc.).
+ *
+ */
+ 
+class SAM_Driver_spamd_ldap extends SAM_Driver {
+
+    /**
+     * Handle for the current database connection.
+     *
+     * @var object DB $_db
+     */
+    var $_linkid;
+
+    /**
+     * Boolean indicating whether or not we're connected to the SQL server.
+     *
+     * @var boolean $_connected
+     */
+    var $_connected = false;
+
+    /**
+     * List of the capabilities supported by this driver.
+     *
+     * @var array $_capabilities
+     */
+/*
+    var $_capabilities = array('hit_level',
+                               'report_header',
+                               'rewrite_sub',
+                               'subject_tag',
+                               'level_stars',
+                               'defang_mime',
+                               'whitelist_to',
+                               'whitelist_from',
+                               'blacklist_to',
+                               'blacklist_from',
+                               'skip_rbl');
+*/
+    /**
+     * Constructs a new SQL storage object.
+     *
+     * @access public
+     *
+     * @param string $user               The user who owns these SPAM options.
+     * @param optional array $params     A hash containing connection
+     *                                   parameters.
+     */
+    function SAM_Driver_spamd_ldap($user, $params = array())
+    {
+        $this->_user = $user;
+        $this->_params = $params;
+        $this->_capabilities = $params['capabilities'];
+    }
+
+    /**
+     * Converts a SAM attribute to an option that SpamAssassin will use.
+     *
+     * @access private
+     *
+     * @param string $attribute          The SAM attribute to convert.
+     *
+     * @return string   The converted SpamAssassin option or the original
+     *                  attribute if no match is found.
+     */
+    function _mapAttributeToOption($attribute)
+    {
+        return isset($this->_params['option_map'][$attribute])
+               ? $this->_params['option_map'][$attribute] : $attribute;
+    }
+
+    /**
+     * Converts a SpamAssassin option to a SAM attribute.
+     *
+     * @access private
+     *
+     * @param string $option             The SpamAssassin option to convert.
+     *
+     * @return string   The converted SAM attribute or the original option if
+     *                  no match is found.
+     */
+    function _mapOptionToAttribute($option)
+    {
+        $attribute_map = array();
+        if (isset($this->_params['option_map'])) {
+            $attribute_map = array_flip($this->_params['option_map']);
+        }
+
+        return isset($attribute_map[$option])
+               ? $attribute_map[$option] : $option;
+    }
+
+    /**
+     * Retrieve an option set from the storage backend.
+     *
+     * @access private
+     *
+     * @return mixed    Array of option-value pairs or a PEAR_Error object
+     *                  on failure.
+     */
+    function retrieve()
+    {
+        /* Make sure we have a valid database connection. */
+        $this->_connect();
+        /* set default values */
+        $this->_setDefaults();
+        $eres=false;
+        $user=$this->_user;
+        $attrib=strtolower($this->_params['attribute']);
+        $filter="(".$this->_params['uid']."=$user)";
+        $res=ldap_search($this->_linkid, $this->_params['basedn'], $filter, array($attrib));
+        if ($res) $eres=ldap_get_entries($this->_linkid, $res);
+        if ($eres && isset($eres[0][$attrib])) {
+            for ($i=0;$i<$eres[0][$attrib]['count'];$i++) {
+                list($a,$v)=explode(" ", $eres[0][$attrib][$i]);
+                $ra=$this->_mapOptionToAttribute($a);
+                if (is_numeric($v))
+                    $this->_options[$ra]=(int) $v;
+                else
+                    $this->_options[$ra]=$v;
+            }
+        }
+    }
+
+    /**
+     * Set default values
+     *
+     * @access private
+     *
+     */
+    function _setDefaults()
+    {
+        $defaults=$this->_params['defaults'];
+        foreach($defaults as $a => $v) {
+            $this->_options[$a]=$v;
+        }
+    }
+    
+
+    /**
+     * Store an option set in the storage backend.
+     *
+     * @access private
+     *
+     * @return boolean    True on success or false on failure.
+     */
+    function store()
+    {
+        /* Make sure we have a valid database connection. */
+        if (!$this->_connect()) return false;
+        $user=$this->_user;
+        $attrib=$this->_params['attribute'];
+        $userdn=$this->_params['uid']."=$user,".$this->_params['basedn'];       
+        $store=$this->_options;
+        foreach($store as $a => $v) {
+            $sa=$this->_mapAttributeToOption($a);
+            if (is_array($v)) {
+                foreach($v as $av)
+                    $entry[$attrib][]="$sa $av";
+            } else
+                $entry[$attrib][]="$sa $v";
+        }
+        return (ldap_modify($this->_linkid, $userdn, $entry));
+    }
+    
+    /**
+     * Attempts to open a connection to the LDAP server.
+     *
+     * @access private
+     *
+     * @return boolean    True on success or false on failure.
+     */
+    function _connect()
+    {
+        if ($this->_connected) return true;
+        $bindpass=Auth::getCredential('password');
+        $user=$this->_user;
+        $binddn=$this->_params['uid']."=$user,".$this->_params['basedn'];
+        Horde::assertDriverConfig($this->_params, 'spamd_ldap',
+            array('ldapserver', 'basedn', 'attribute', 'uid'),
+            'SAM backend', 'backends.php', '$backends');
+        for ($tries=3;$tries > 0;$tries--) {                        /* try three times */
+            $lc = ldap_connect($this->_params['ldapserver']);
+            if ($lc) {
+                $lb = ldap_bind($lc, $binddn, $bindpass);
+                if ($lb) {
+                    $this->_linkid=$lc;
+                    $this->connected=true;
+                    return true;
+                } else  {
+                    ldap_unbind($lc);
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Disconnect from the LDAP server and clean up the connection.
+     *
+     * @return boolean  True on success, false on failure.
+     */
+    function _disconnect()
+    {
+        if ($this->_connected) {
+            $this->_connected = false;
+            return ldap_unbind($this->_linkid);
+        }
+        return true;
+    }
+
+}