view mod_auth_ha1/mod_auth_ha1.lua @ 4249:64aa1d9d70ac

mod_rest: Catch and log errors in callback promise chain From the code it looks like it should be possible to reply to an error stanza, but it did not. Turns out I was saved by my local developer mode module which throws errors if an attempt is made to create an errror reply to an error stanza. However nothing collects this error from the promise, so all I got was confusion.
author Kim Alvefur <zash@zash.se>
date Sun, 15 Nov 2020 16:25:49 +0100
parents 31c4d92a81e5
children
line wrap: on
line source

-- Prosody IM
-- Copyright (C) 2014 Matthew Wild
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--

local usermanager = require "core.usermanager";
local new_sasl = require "util.sasl".new;

local nodeprep = require "util.encodings".stringprep.nodeprep;
local nameprep = require "util.encodings".stringprep.nameprep;
local md5 = require "util.hashes".md5;

local host = module.host;

local auth_filename = module:get_option_string("auth_ha1_file", "auth.txt");
local auth_data = {};

function reload_auth_data()
	local f, err = io.open(auth_filename);
	if not f then
		module:log("error", "Failed to read from auth file: %s", err);
		return;
	end
	auth_data = {};
	local line_number, imported_count, not_authorized_count = 0, 0, 0;
	for line in f:lines() do
		line_number = line_number + 1;
		local username, hash, realm, state = line:match("^([^:]+):(%x+):([^:]+):(.+)$");
		if not username then
			if line:sub(1,1) ~= "#" then
				module:log("error", "Unable to parse line %d of auth file, skipping", line_number);
			end
		else
			username, realm = nodeprep(username), nameprep(realm);
			if not username then
				module:log("error", "Invalid username on line %d of auth file, skipping", line_number);
			elseif not realm then
				module:log("error", "Invalid hostname/realm on line %d of auth file, skipping", line_number);
			elseif state ~= "authorized" then
				not_authorized_count = not_authorized_count + 1;
			elseif realm == host then
				auth_data[username] = hash;
				imported_count = imported_count + 1;
			end
		end
	end
	f:close();
	module:log("debug", "Loaded %d accounts from auth file (%d authorized)", imported_count, imported_count-not_authorized_count);
end

function module.load()
	reload_auth_data();
end

module:hook_global("config-reloaded", reload_auth_data);

-- define auth provider
local provider = {};

function provider.test_password(username, password)
	module:log("debug", "test password for user %s at host %s, %s", username, host, password);

	local test_hash = md5(username..":"..host..":"..password, true);

	if test_hash == auth_data[username] then
		return true;
	else
		return nil, "Auth failed. Invalid username or password.";
	end
end

function provider.set_password(username, password)
	return nil, "Changing passwords not supported";
end

function provider.user_exists(username)
	if not auth_data[username] then
		module:log("debug", "account not found for username '%s' at host '%s'", username, host);
		return nil, "Auth failed. Invalid username";
	end
	return true;
end

function provider.create_user(username, password)
	return nil, "User creation not supported";
end

function provider.delete_user(username)
	return nil , "User deletion not supported";
end

function provider.get_sasl_handler()
	return new_sasl(host, {
		plain_test = function(sasl, username, password, realm)
			return usermanager.test_password(username, realm, password), true;
		end
	});
end

module:provides("auth", provider);