changeset 268:cfcd4efb0fa4

mod_auth_dovecot: Remove asserts (use logger) and refactor socket code
author Javier Torres <javitonino@gmail.com>
date Sat, 30 Oct 2010 23:38:39 +0200
parents 76f3310ec113
children 74846ec9c29f
files mod_auth_dovecot/mod_auth_dovecot.lua
diffstat 1 files changed, 101 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/mod_auth_dovecot/mod_auth_dovecot.lua	Sat Oct 30 23:38:19 2010 +0200
+++ b/mod_auth_dovecot/mod_auth_dovecot.lua	Sat Oct 30 23:38:39 2010 +0200
@@ -16,60 +16,121 @@
 local prosody = _G.prosody;
 
 function new_default_provider(host)
-	local provider = { name = "dovecot" };
+	local provider = { name = "dovecot", c = nil };
 	log("debug", "initializing dovecot authentication provider for host '%s'", host);
-
-	function provider.test_password(username, password)
-		log("debug", "test password '%s' for user %s at host %s", password, username, module.host);
+	
+	-- The following connects to a new socket and send the handshake
+	function provider.connect(self)
+		-- Destroy old socket
+		if (provider.c ~= nil) then
+			provider.c:close();
+		end
 		
-		c = assert(socket.unix());
-		assert(c:connect("/var/run/dovecot/auth-login")); -- FIXME: Hardcoded is bad
+		provider.c = socket.unix();
 		
-		local pid = pposix.getpid();
-
+		-- Create a connection to dovecot socket
+		local socket = "/var/run/dovecot/auth-login";
+		local r, e = provider.c:connect(socket);
+		if (not r) then
+			log("warn", "error connecting to dovecot socket at '%s'. error was '%s'. check permissions", socket, e);
+			return false;
+		end
+		
 		-- Send our handshake
-        -- FIXME: Oh no! There are asserts everywhere
-		assert(c:send("VERSION\t1\t1\n"));
-		assert(c:send("CPID\t" .. pid .. "\n"));
-
-		-- Check their handshake
+		local pid = pposix.getpid();
+		if not provider:send("VERSION\t1\t1\n") then
+			return false
+		end
+		if (not provider:send("CPID\t" .. pid .. "\n")) then
+			return false
+		end
+		
+		-- Parse Dovecot's handshake
 		local done = false;
 		while (not done) do
-			local l = assert(c:receive());
+			local l = provider:receive();
+			if (not l) then
+				return false;
+			end
+			
 			parts = string.gmatch(l, "[^\t]+");
 			first = parts();
 			if (first == "VERSION") then
-				assert(parts() == "1");
-				assert(parts() == "1");
+				-- Version should be 1.1
+				local v1 = parts();
+				local v2 = parts();
+				
+				if (not (v1 == "1" and v2 == "1")) then
+					log("warn", "server version is not 1.1. it is %s.%s", v1, v2);
+					return false;
+				end
 			elseif (first == "MECH") then
+				-- Mechanisms should include PLAIN
 				local ok = false;
 				for p in parts do
 					if p == "PLAIN" then
 						ok = true;
 					end
 				end
-				assert(ok);
+				if (not ok) then
+					log("warn", "server doesn't support PLAIN mechanism. It supports '%s'", l);
+					return false;
+				end
 			elseif (first == "DONE") then
 				done = true;
 			end
 		end
-
+		return true;
+	end
+	
+	function provider.send(self, data)
+		local r, e = provider.c:send(data);
+		if (not r) then
+			log("warn", "error sending '%s' to dovecot. error was '%s'", data, e);
+			return false;
+		end
+		return true;
+	end
+	
+	function provider.receive(self)
+		local r, e = provider.c:receive();
+		if (not r) then
+			log("warn", "error receiving data from dovecot. error was '%s'", socket, e);
+			return false;
+		end
+		return r;
+	end
+	
+	function provider.test_password(username, password)
+		log("debug", "test password '%s' for user %s at host %s", password, username, module.host);
+		
+		if (not provider:connect()) then
+			return nil, "Auth failed. Dovecot communications error";
+		end
+		
 		-- Send auth data
 		username = username .. "@" .. module.host; -- FIXME: this is actually a hack for my server
 		local b64 = base64.encode(username .. "\0" .. username .. "\0" .. password);
 		local id = "54321"; -- FIXME: probably can just be a fixed value if making one request per connection
-		assert(c:send("AUTH\t" .. id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64 .. "\n"));
-		local l = assert(c:receive());
-		assert(c:close());
+		if (not provider:send("AUTH\t" .. id .. "\tPLAIN\tservice=XMPP\tresp=" .. b64 .. "\n")) then
+			return nil, "Auth failed. Dovecot communications error";
+		end
+		
+		-- Get response
+		local l = provider:receive();
+		if (not l) then
+			return nil, "Auth failed. Dovecot communications error";
+		end
 		local parts = string.gmatch(l, "[^\t]+");
-
+		
+		-- Check response
 		if (parts() == "OK") then
 			return true;
 		else
 			return nil, "Auth failed. Invalid username or password.";
 		end
 	end
-
+	
 	function provider.get_password(username)
 		return nil, "Cannot get_password in dovecot backend.";
 	end
@@ -77,32 +138,32 @@
 	function provider.set_password(username, password)
 		return nil, "Cannot set_password in dovecot backend.";
 	end
-
+	
 	function provider.user_exists(username)
-        --TODO: Send an auth request. If it returns FAIL <id> user=<user> then user exists.
-		return nil, "user_exists not yet implemented in dovecot backend.";
+		--TODO: Send an auth request. If it returns FAIL <id> user=<user> then user exists.
+				return nil, "user_exists not yet implemented in dovecot backend.";
 	end
-
+	
 	function provider.create_user(username, password)
 		return nil, "Cannot create_user in dovecot backend.";
 	end
-
+	
 	function provider.get_sasl_handler()
 		local realm = module:get_option("sasl_realm") or module.host;
 		local getpass_authentication_profile = {
 			plain_test = function(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(realm, getpass_authentication_profile);
-	end
-	
-	return provider;
+			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(realm, getpass_authentication_profile);
+end
+
+return provider;
 end
 
 module:add_item("auth-provider", new_default_provider(module.host));