File

util/roles.lua @ 13854:0b01f40df0f9 13.0

mod_http_file_share: Add media-src 'self' to Content-Security-Policy header This allows certain media files to be loaded when navigated to directly in a web browser. Note that in some browsers (Chrome), the media gets transformed internally into a HTML page with some basic styles, but these are blocked due to our default-src policy of 'none' Although this could be unblocked with style-src unsafe-inline, it is not our plan to fix this, because this would have negative security implications. The reason for our CSP is to prevent the file share service from being used to host malicious HTML/CSS/JS. Yes, CSS can be malicious. Our file share service is for uploading and downloading files, it is not a substitute for website/content hosting.
author Matthew Wild <mwild1@gmail.com>
date Fri, 18 Apr 2025 12:25:06 +0100
parent 12987:2cf8d98d8a28
line wrap: on
line source

local array = require "prosody.util.array";
local it = require "prosody.util.iterators";
local new_short_id = require "prosody.util.id".short;

local role_methods = {};
local role_mt = {
	__index = role_methods;
	__name = "role";
	__add = nil;
};

local function is_role(o)
	local mt = getmetatable(o);
	return mt == role_mt;
end

local function _new_may(permissions, inherited_mays)
	local n_inherited = inherited_mays and #inherited_mays;
	return function (role, action, context)
		-- Note: 'role' may be a descendent role, not only the one we're attached to
		local policy = permissions[action];
		if policy ~= nil then
			return policy;
		end
		if n_inherited then
			for i = 1, n_inherited do
				policy = inherited_mays[i](role, action, context);
				if policy ~= nil then
					return policy;
				end
			end
		end
		return nil;
	end
end

local permissions_key = {};

-- {
-- Required:
--   name = "My fancy role";
--
-- Optional:
--   inherits = { role_obj... }
--   default = true
--   priority = 100
--   permissions = {
--     ["foo"] = true; -- allow
--     ["bar"] = false; -- deny
--   }
-- }
local function new(base_config, overrides)
	local config = setmetatable(overrides or {}, { __index = base_config });
	local permissions = {};
	local inherited_mays;
	if config.inherits then
		inherited_mays = array.pluck(config.inherits, "may");
	end
	local new_role = {
		id = new_short_id();
		name = config.name;
		description = config.description;
		default = config.default;
		priority = config.priority;
		may = _new_may(permissions, inherited_mays);
		inherits = config.inherits;
		[permissions_key] = permissions;
	};
	local desired_permissions = config.permissions or config[permissions_key];
	for k, v in pairs(desired_permissions or {}) do
		permissions[k] = v;
	end
	return setmetatable(new_role, role_mt);
end

function role_mt:__freeze()
	local t = {
		id = self.id;
		name = self.name;
		description = self.description;
		default = self.default;
		priority = self.priority;
		inherits = self.inherits;
		permissions = self[permissions_key];
	};
	return t;
end

function role_methods:clone(overrides)
	return new(self, overrides);
end

function role_methods:set_permission(permission_name, policy, overwrite)
	local permissions = self[permissions_key];
	if overwrite ~= true and permissions[permission_name] ~= nil and permissions[permission_name] ~= policy then
		return false, "policy-already-exists";
	end
	permissions[permission_name] = policy;
	return true;
end

function role_methods:policies()
	local policy_iterator, s, v = it.join(pairs(self[permissions_key]));
	if self.inherits then
		for _, inherited_role in ipairs(self.inherits) do
			policy_iterator:append(inherited_role:policies());
		end
	end
	return policy_iterator, s, v;
end

function role_mt.__tostring(self)
	return ("role<[%s] %s>"):format(self.id or "nil", self.name or "[no name]");
end

function role_mt.__pairs(self)
	return it.filter(permissions_key, next, self);
end

return {
	is_role = is_role;
	new = new;
};