add persist_user_denylist and persist_user_allowlist

Closes: #3511
This commit is contained in:
Luca Matei Pintilie
2025-11-28 21:54:38 +01:00
committed by mathieui
parent fdae7b03e0
commit fbb93dbd08
6 changed files with 170 additions and 3 deletions
+2
View File
@@ -19,6 +19,8 @@ For admins
----------
- Command line option --test-config (or -t) has been added. When used,
biboumi will just exit without any error if the configuration is correct
- Options persist_user_denylist and persist_user_allowlist have been added to
granualy control who can set the persist option
For packagers
-------------
+13
View File
@@ -246,6 +246,19 @@ configuration file is located: for example if biboumi reads its
configuration from /etc/biboumi/biboumi.cfg, the policy_directory value
will be /etc/biboumi.
persist_user_denylist
~~~~~~~~~~~~~~~~~~~~~
A list of XMPP users or domains who are not allowed to change the `persist`
ad-hoc option and who will not be impacted by the `persistent_by_default`
config option. Specific JIDs in this list will take priority over allowed JIDs
or domains.
persist_user_allowlist
~~~~~~~~~~~~~~~~~~~~~~
A list of XMPP XMPP users or domains who are allowed to change the `persist`
ad-hoc option.
TLS configuration
-----------------
+4 -1
View File
@@ -7,6 +7,7 @@
#include <utils/tolower.hpp>
#include <utils/uuid.hpp>
#include <logger/logger.hpp>
#include <utils/is_requester_allowed_to_persist.hpp>
#include <utils/revstr.hpp>
#include <utils/split.hpp>
#include <xmpp/jid.hpp>
@@ -499,7 +500,9 @@ void Bridge::leave_irc_channel(Iid&& iid, const std::string& status_message, con
bool persistent = false;
#ifdef USE_DATABASE
const auto goptions = Database::get_global_options(this->user_jid);
if (goptions.col<Database::GlobalPersistent>())
if (!utils::is_requester_allowed_to_persist(Jid(this->user_jid)))
persistent = false;
else if (goptions.col<Database::GlobalPersistent>())
persistent = true;
else
{
@@ -0,0 +1,55 @@
#pragma once
#include "xmpp/jid.hpp"
#include <config/config.hpp>
namespace utils
{
/**
* Check config if a given requester is allowed to persist target
*
* The following values are checked
*
* - A user's jid (foo@example.com)
* - A user's domain (example.com)
*
* The following config options are checked in order
*
* 1. `persist_user_denylist` contains the requester's jid
* 2. `persist_user_allowlist` contains the requester's jid
* 3. `persist_user_denylist` contains the requester's domain
* 4. `persist_user_allowlist` contains the contains the requester's domain
* 5. allow
*/
inline bool is_requester_allowed_to_persist(const Jid &requester) {
// requester.bare checks
// deny
// 1.
bool jid_in_user_denylist = Config::is_in_list("persist_user_denylist", requester.bare());
if (jid_in_user_denylist)
return false;
// allow
// 2.
bool jid_in_user_allowlist = Config::is_in_list("persist_user_allowlist", requester.bare());
if (jid_in_user_allowlist)
return true;
// requester.domain checks
// deny
// 3.
bool domain_in_user_denylist = Config::is_in_list("persist_user_denylist", requester.domain);
if (domain_in_user_denylist)
return false;
// allow
// 4.
bool domain_in_user_allowlist = Config::is_in_list("persist_user_allowlist", requester.domain);
if (domain_in_user_allowlist)
return true;
// default allow
// 5.
return true;
}
}
+7 -2
View File
@@ -3,6 +3,7 @@
#include <utils/scopeguard.hpp>
#include <bridge/bridge.hpp>
#include <config/config.hpp>
#include <utils/is_requester_allowed_to_persist.hpp>
#include <utils/string.hpp>
#include <utils/split.hpp>
#include <xmpp/jid.hpp>
@@ -159,6 +160,7 @@ void ConfigureGlobalStep1(XmppComponent&, AdhocSession& session, XmlNode& comman
}
}
if (utils::is_requester_allowed_to_persist(Jid(session.get_owner_jid())))
{
XmlSubNode persistent(x, "field");
persistent["var"] = "persistent";
@@ -208,7 +210,8 @@ void ConfigureGlobalStep2(XmppComponent& xmpp_component, AdhocSession& session,
bridge->set_record_history(options.col<Database::RecordHistory>());
}
else if (field->get_tag("var") == "persistent" &&
value)
value
&& utils::is_requester_allowed_to_persist(Jid(session.get_owner_jid())))
options.col<Database::GlobalPersistent>() = to_bool(value->get_inner());
}
@@ -640,6 +643,7 @@ void insert_irc_channel_configuration_form(XmlNode& node, const Jid& requester,
}
}
if (utils::is_requester_allowed_to_persist(requester))
{
XmlSubNode persistent(x, "field");
persistent["var"] = "persistent";
@@ -699,7 +703,7 @@ bool handle_irc_channel_configuration_form(XmppComponent& xmpp_component, const
else if (field->get_tag("var") == "encoding_in" && value)
options.col<Database::EncodingIn>() = value->get_inner();
else if (field->get_tag("var") == "persistent" && value)
else if (field->get_tag("var") == "persistent" && value && utils::is_requester_allowed_to_persist(requester))
options.col<Database::Persistent>() = to_bool(value->get_inner());
else if (field->get_tag("var") == "record_history" &&
value && !value->get_inner().empty())
@@ -953,3 +957,4 @@ void GetIrcConnectionInfoStep1(XmppComponent& component, AdhocSession& session,
message = ss.str();
}
+89
View File
@@ -0,0 +1,89 @@
#include "catch2/catch_test_macros.hpp"
#include "utils/is_requester_allowed_to_persist.hpp"
#include "xmpp/jid.hpp"
TEST_CASE("default")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "");
Config::set("persist_user_allowlist", "");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == true);
Config::clear();
}
TEST_CASE("denylist - jid")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "foo@example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == false);
Config::clear();
}
TEST_CASE("denylist - domain")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == false);
Config::clear();
}
TEST_CASE("allowlist - jid")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_allowlist", "foo@example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == true);
Config::clear();
}
TEST_CASE("allowlist - domain")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_allowlist", "example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == true);
Config::clear();
}
TEST_CASE("denylist + allowlist - deny domain, allow jid")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "example.com");
Config::set("persist_user_allowlist", "foo@example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == true);
Config::clear();
}
TEST_CASE("denylist + allowlist - deny jid, allow domain")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "foo@example.com");
Config::set("persist_user_allowlist", "example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == false);
Config::clear();
}
TEST_CASE("denylist + allowlist - deny jid, allow jid")
{
Jid jid("foo@example.com");
Config::clear();
Config::set("persist_user_denylist", "foo@example.com");
Config::set("persist_user_allowlist", "foo@example.com");
bool result = utils::is_requester_allowed_to_persist(jid);
CHECK(result == false);
Config::clear();
}