changeset 354:f24998ec7f8d

Implemented basic SQL authentication module. This module implements authentication against plaintext password stored in SQL database. You wil definitely need to edit the Lua code and put a query working with your database. The example query works against jabberd2 database schema. P.S. This module is just some code glued together from other modules. ;-)
author Tomasz Sterna <tomek@xiaoka.com>
date Tue, 12 Apr 2011 00:30:53 +0200
parents 8ef36af30181
children a5da789b2e7d
files mod_auth_sql/mod_auth_sql.lua
diffstat 1 files changed, 138 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_auth_sql/mod_auth_sql.lua	Tue Apr 12 00:30:53 2011 +0200
@@ -0,0 +1,138 @@
+-- Simple SQL Authentication module for Prosody IM
+-- Copyright (C) 2011 Tomasz Sterna <tomek@xiaoka.com>
+--
+
+local log = require "util.logger".init("auth_sql");
+local new_sasl = require "util.sasl".new;
+local nodeprep = require "util.encodings".stringprep.nodeprep;
+
+local DBI;
+local connection;
+local host,user,store = module.host;
+local params = module:get_option("sql");
+
+local resolve_relative_path = require "core.configmanager".resolve_relative_path;
+
+local function test_connection()
+	if not connection then return nil; end
+	if connection:ping() then
+		return true;
+	else
+		module:log("debug", "Database connection closed");
+		connection = nil;
+	end
+end
+local function connect()
+	if not test_connection() then
+		prosody.unlock_globals();
+		local dbh, err = DBI.Connect(
+			params.driver, params.database,
+			params.username, params.password,
+			params.host, params.port
+		);
+		prosody.lock_globals();
+		if not dbh then
+			module:log("debug", "Database connection failed: %s", tostring(err));
+			return nil, err;
+		end
+		module:log("debug", "Successfully connected to database");
+		dbh:autocommit(false); -- don't commit automatically
+		connection = dbh;
+		return connection;
+	end
+end
+
+do -- process options to get a db connection
+	DBI = require "DBI";
+
+	params = params or { driver = "SQLite3" };
+	
+	if params.driver == "SQLite3" then
+		params.database = resolve_relative_path(prosody.paths.data or ".", params.database or "prosody.sqlite");
+	end
+	
+	assert(params.driver and params.database, "Both the SQL driver and the database need to be specified");
+	
+	assert(connect());
+end
+
+local function getsql(sql, ...)
+	if params.driver == "PostgreSQL" then
+		sql = sql:gsub("`", "\"");
+	end
+	-- do prepared statement stuff
+	local stmt, err = connection:prepare(sql);
+	if not stmt and not test_connection() then error("connection failed"); end
+	if not stmt then module:log("error", "QUERY FAILED: %s %s", err, debug.traceback()); return nil, err; end
+	-- run query
+	local ok, err = stmt:execute(...);
+	if not ok and not test_connection() then error("connection failed"); end
+	if not ok then return nil, err; end
+	
+	return stmt;
+end
+
+function new_default_provider(host)
+	local provider = { name = "sql" };
+	log("debug", "initializing default 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);
+		return nil, "Password based auth not supported.";
+	end
+
+	function provider.get_password(username)
+		log("debug", "get_password for username '%s' at host '%s'", username, module.host);
+
+		local stmt, err = getsql("SELECT `password` FROM `authreg` WHERE `username`=? AND `realm`=?",
+			username, module.host);
+
+		local password = nil;
+		if stmt ~= nil then
+			for row in stmt:rows(true) do
+				password = row.password;
+			end
+		else
+			log("error", "QUERY ERROR: %s %s", err, debug.traceback());
+			return nil;
+		end
+
+		return password;
+	end
+
+	function provider.set_password(username, password)
+		return nil, "Password based auth not supported.";
+	end
+
+	function provider.user_exists(username)
+		return nil, "User exist check not supported.";
+	end
+
+	function provider.create_user(username, password)
+		return nil, "Account creation/modification not supported.";
+	end
+
+	function provider.get_sasl_handler()
+		local realm = module:get_option("sasl_realm") or module.host;
+		local getpass_authentication_profile = {
+			plain = function(sasl, username, realm)
+				local prepped_username = nodeprep(username);
+				if not prepped_username then
+					log("debug", "NODEprep failed on username: %s", username);
+					return "", nil;
+				end
+				local password = usermanager.get_password(prepped_username, realm);
+				if not password then
+					return "", nil;
+				end
+				return password, true;
+			end
+		};
+		return new_sasl(realm, getpass_authentication_profile);
+	end
+
+	return provider;
+end
+
+module:add_item("auth-provider", new_default_provider(module.host));
+