diff mod_storage_ldap/mod_storage_ldap.lua @ 809:1d51c5e38faa

Add LDAP plugin suite
author rob@hoelz.ro
date Sun, 02 Sep 2012 15:35:50 +0200
parents
children 2469f779b3f7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_storage_ldap/mod_storage_ldap.lua	Sun Sep 02 15:35:50 2012 +0200
@@ -0,0 +1,180 @@
+-- vim:sts=4 sw=4
+
+-- Prosody IM
+-- Copyright (C) 2008-2010 Matthew Wild
+-- Copyright (C) 2008-2010 Waqas Hussain
+-- Copyright (C) 2012 Rob Hoelz
+--
+-- This project is MIT/X11 licensed. Please see the
+-- COPYING file in the source package for more information.
+--
+
+----------------------------------------
+-- Constants and such --
+----------------------------------------
+
+local setmetatable = setmetatable;
+local ldap         = module:require 'ldap';
+local vcardlib     = module:require 'ldap/vcard';
+local st           = require 'util.stanza';
+local gettime      = require 'socket'.gettime;
+
+if not ldap then
+    return;
+end
+
+local CACHE_EXPIRY = 300;
+local params       = module:get_option('ldap');
+
+----------------------------------------
+-- Utility Functions --
+----------------------------------------
+
+local function ldap_record_to_vcard(record)
+    return vcardlib.create {
+        record = record,
+        format = params.vcard_format,
+    }
+end
+
+local get_alias_for_user;
+
+do
+  local user_cache;
+  local last_fetch_time;
+
+  local function populate_user_cache()
+      local ld = ldap.getconnection();
+
+      local usernamefield = params.user.usernamefield;
+      local namefield     = params.user.namefield;
+
+      user_cache = {};
+
+      for _, attrs in ld:search { base = params.user.basedn, scope = 'onelevel', filter = params.user.filter } do
+          user_cache[attrs[usernamefield]] = attrs[namefield];
+      end
+      last_fetch_time = gettime();
+  end
+
+  function get_alias_for_user(user)
+      if last_fetch_time and last_fetch_time + CACHE_EXPIRY < gettime() then
+          user_cache = nil;
+      end
+      if not user_cache then
+          populate_user_cache();
+      end
+      return user_cache[user];
+  end
+end
+
+----------------------------------------
+-- General Setup --
+----------------------------------------
+
+local ldap_store   = {};
+ldap_store.__index = ldap_store;
+
+local adapters = {
+    roster = {},
+    vcard  = {},
+}
+
+for k, v in pairs(adapters) do
+    setmetatable(v, ldap_store);
+    v.__index = v;
+    v.name    = k;
+end
+
+function ldap_store:get(username)
+    return nil, "get method unimplemented on store '" .. tostring(self.name) .. "'"
+end
+
+function ldap_store:set(username, data)
+    return nil, "LDAP storage is currently read-only";
+end
+
+----------------------------------------
+-- Roster Storage Implementation --
+----------------------------------------
+
+function adapters.roster:get(username)
+    local ld = ldap.getconnection();
+    local contacts = {};
+
+    local memberfield = params.groups.memberfield;
+    local namefield   = params.groups.namefield;
+    local filter      = memberfield .. '=' .. tostring(username);
+
+    local groups = {};
+    for _, config in ipairs(params.groups) do
+        groups[ config[namefield] ] = config.name;
+    end
+
+    -- XXX this kind of relies on the way we do groups at INOC
+    for _, attrs in ld:search { base = params.groups.basedn, scope = 'onelevel', filter = filter } do
+        if groups[ attrs[namefield] ] then
+            local members = attrs[memberfield];
+
+            for _, user in ipairs(members) do
+                if user ~= username then
+                    local jid    = user .. '@' .. module.host;
+                    local record = contacts[jid];
+
+                    if not record then
+                        record = {
+                            subscription = 'both',
+                            groups       = {},
+                            name         = get_alias_for_user(user),
+                        };
+                        contacts[jid] = record;
+                    end
+
+                    record.groups[ groups[ attrs[namefield] ] ] = true;
+                end
+            end
+        end
+    end
+
+    return contacts;
+end
+
+----------------------------------------
+-- vCard Storage Implementation --
+----------------------------------------
+
+function adapters.vcard:get(username)
+    if not params.vcard_format then
+        return nil, '';
+    end
+
+    local ld     = ldap.getconnection();
+    local filter = params.user.usernamefield .. '=' .. tostring(username);
+
+    local match = ldap.singlematch {
+        base   = params.user.basedn,
+        filter = filter,
+    };
+    if match then
+        match.jid = username .. '@' .. module.host
+        return st.preserialize(ldap_record_to_vcard(match));
+    else
+        return nil, 'not found';
+    end
+end
+
+----------------------------------------
+-- Driver Definition --
+----------------------------------------
+
+local driver = { name = "ldap" };
+
+function driver:open(store, typ)
+    local adapter = adapters[store];
+
+    if adapter and not typ then
+        return adapter;
+    end
+    return nil, "unsupported-store";
+end
+module:add_item("data-driver", driver);