view mod_auth_external/mod_auth_external.lua @ 737:e4ea03b060ed

mod_archive: switch from/to The XEP-0136 is not very explicit about the meening of <from> and <to> elements, but the examples are clear: <from> means it comes from the user in the 'with' attribute of the collection. That is the opposite of what is currently implemented in that module. So for better compatibility with complient clients, this switch the 'from' and 'to' fields
author Olivier Goffart <ogoffart@woboq.com>
date Wed, 04 Jul 2012 14:08:43 +0200
parents 8e9e5c7d97ff
children 881ec9919144
line wrap: on
line source

--
-- NOTE: currently this uses lpc; when waqas fixes process, it can go back to that
--
-- Prosody IM
-- Copyright (C) 2010 Waqas Hussain
-- Copyright (C) 2010 Jeff Mitchell
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--


local nodeprep = require "util.encodings".stringprep.nodeprep;
--local process = require "process";
local lpc = require "lpc";

local config = require "core.configmanager";
local log = module._log;
local host = module.host;
local script_type = config.get(host, "core", "external_auth_protocol") or "generic";
assert(script_type == "ejabberd" or script_type == "generic");
local command = config.get(host, "core", "external_auth_command") or "";
assert(type(command) == "string");
assert(not host:find(":"));
local usermanager = require "core.usermanager";
local jid_bare = require "util.jid".bare;
local new_sasl = require "util.sasl".new;

--local proc;
local pid;
local readfile;
local writefile;

local function send_query(text)
	if pid and lpc.wait(pid,1) ~= nil then
    	    log("debug","error, process died, force reopen");
	    pid=nil;
	end
	if not pid then
		log("debug", "Opening process " .. command);
		-- proc = process.popen(command);
		pid, writefile, readfile = lpc.run(command);
	end
	-- if not proc then
	if not pid then
		log("debug", "Process failed to open");
		return nil;
	end
	-- proc:write(text);
	-- proc:flush();

	writefile:write(text);
	writefile:flush();
	if script_type == "ejabberd" then
		-- return proc:read(4); -- FIXME do properly
		return readfile:read(4); -- FIXME do properly
	elseif script_type == "generic" then
		-- return proc:read(1);
		return readfile:read();
	end
end

function do_query(kind, username, password)
	if not username then return nil, "not-acceptable"; end
	username = nodeprep(username);
	if not username then return nil, "jid-malformed"; end
	
	local query = (password and "%s:%s:%s:%s" or "%s:%s:%s"):format(kind, username, host, password);
	local len = #query
	if len > 1000 then return nil, "policy-violation"; end
	
	if script_type == "ejabberd" then
		local lo = len % 256;
		local hi = (len - lo) / 256;
		query = string.char(hi, lo)..query;
	end
	if script_type == "generic" then
		query = query..'\n';
	end
	
	local response = send_query(query);
	if (script_type == "ejabberd" and response == "\0\2\0\0") or
		(script_type == "generic" and response == "0") then
			return nil, "not-authorized";
	elseif (script_type == "ejabberd" and response == "\0\2\0\1") or
		(script_type == "generic" and response == "1") then
			return true;
	else
		log("debug", "Nonsense back");
		--proc:close();
		--proc = nil;
		return nil, "internal-server-error";
	end
end

function new_external_provider(host)
	local provider = { name = "external" };

	function provider.test_password(username, password)
		return do_query("auth", username, password);
	end

	function provider.set_password(username, password)
		return do_query("setpass", username, password);
	end

	function provider.user_exists(username)
		return do_query("isuser", username);
	end

	function provider.create_user(username, password) return nil, "Account creation/modification not available."; end
	
	function provider.get_sasl_handler()
		local testpass_authentication_profile = {
			plain_test = function(sasl, username, password, realm)
				local prepped_username = nodeprep(username);
				if not prepped_username then
					log("debug", "NODEprep failed on username: %s", username);
					return "", nil;
				end
				return usermanager.test_password(prepped_username, realm, password), true;
			end,
		};
		return new_sasl(module.host, testpass_authentication_profile);
	end

	function provider.is_admin(jid)
		local admins = config.get(host, "core", "admins");
		if admins ~= config.get("*", "core", "admins") then
			if type(admins) == "table" then
				jid = jid_bare(jid);
				for _,admin in ipairs(admins) do
					if admin == jid then return true; end
				end
			elseif admins then
				log("error", "Option 'admins' for host '%s' is not a table", host);
			end
		end
		return usermanager.is_admin(jid); -- Test whether it's a global admin instead
	end

	return provider;
end

module:add_item("auth-provider", new_external_provider(module.host));