dnfdaemon: Make it possible to set 'interactive' option for Repo::confirm_key(), Repo::enable(), Repo::disable()

The operation can require root privileges, for which it can ask the user
through polkit. It's okay when the operation is interactive, aka issued
by a user, but when the operation is ran by a background service, then
the password prompt coming out of blue is wrong. This change allows to
tell the daemon server whether it can ask for the root password or not.
This commit is contained in:
Milan Crha 2025-07-02 18:22:34 +02:00
parent d63616e8ce
commit 0f1b5f2dc8
3 changed files with 177 additions and 12 deletions

View File

@ -48,33 +48,95 @@ along with libdnf. If not, see <https://www.gnu.org/licenses/>.
<arg name="repositories" type="aa{sv}" direction="out"/>
</method>
<!--
confirm_key_with_options:
@key_id: id of the key in question
@confirmed: whether the key import is confirmed by user
@options: an array of key/value pairs to modify the call behavior
Confirm repository OpenPGP key import.
Following @options are supported:
- interactive: boolean, default true
Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
Unknown options are ignored.
-->
<method name="confirm_key_with_options">
<arg name="key_id" type="s" direction="in"/>
<arg name="confirmed" type="b" direction="in"/>
<arg name="options" type="a{sv}" direction="in" />
</method>
<!--
confirm_key:
@key_id: id of the key in question
@confirmed: whether the key import is confirmed by user
Confirm repository OpenPGP key import.
This is equivalent to call "confirm_key_with_options()" with empty options.
-->
<method name="confirm_key">
<arg name="key_id" type="s" direction="in"/>
<arg name="confirmed" type="b" direction="in"/>
</method>
<!--
enable_with_options:
@repo_ids: array of strings containing all repo ids to be enabled
@options: an array of key/value pairs to modify the call behavior
Enable repositories based on the list of given ids.
Following @options are supported:
- interactive: boolean, default true
Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
Unknown options are ignored.
-->
<method name="enable_with_options">
<arg name="repo_ids" type="as" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<!--
enable:
@repo_ids: array of strings containing all repo ids to be enabled
Enable repositories based on the list of given ids.
This is equivalent to call "enable_with_options()" with empty options.
-->
<method name="enable">
<arg name="repo_ids" type="as" direction="in"/>
</method>
<!--
disable_with_options:
@repo_ids: array of strings containing all repo ids to be disabled
@options: an array of key/value pairs to modify the call behavior
Disable repositories based on the list of given ids.
Following @options are supported:
- interactive: boolean, default true
Set to "true", when the operation is done by a user, thus user interaction like password prompts can be done.
Unknown options are ignored.
-->
<method name="disable_with_options">
<arg name="repo_ids" type="as" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<!--
disable:
@repo_ids: array of strings containing all repo ids to be disabled
Disable repositories based on the list of given ids.
This is equivalent to call "disable_with_options()" with empty options.
-->
<method name="disable">
<arg name="repo_ids" type="as" direction="in"/>

View File

@ -276,6 +276,16 @@ void Repo::dbus_register() {
session.get_threads_manager().handle_method(*this, &Repo::list, call, session.session_locale);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"confirm_key_with_options"},
sdbus::Signature{"sb"},
{"key_id", "confirmed"},
sdbus::Signature{""},
{},
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::confirm_key_with_options, call);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"confirm_key"},
sdbus::Signature{"sb"},
@ -286,6 +296,16 @@ void Repo::dbus_register() {
session.get_threads_manager().handle_method(*this, &Repo::confirm_key, call);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"enable_with_options"},
sdbus::Signature{"asa{sv}"},
{"repo_ids", "options"},
sdbus::Signature{""},
{},
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::enable_with_options, call, session.session_locale);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"enable"},
sdbus::Signature{"as"},
@ -296,6 +316,16 @@ void Repo::dbus_register() {
session.get_threads_manager().handle_method(*this, &Repo::enable, call, session.session_locale);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"disable_with_options"},
sdbus::Signature{"asa{sv}"},
{"repo_ids", "options"},
sdbus::Signature{""},
{},
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::disable_with_options, call, session.session_locale);
},
{}},
sdbus::MethodVTableItem{
sdbus::MethodName{"disable"},
sdbus::Signature{"as"},
@ -318,6 +348,16 @@ void Repo::dbus_register() {
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::list, call, session.session_locale);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO,
"confirm_key_with_options",
"sba{sv}",
{"key_id", "confirmed", "options"},
"",
{},
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::confirm_key_with_options, call);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO,
"confirm_key",
@ -328,10 +368,18 @@ void Repo::dbus_register() {
[this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::confirm_key, call);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO, "enable_with_options", "asa{sv}", {"repo_ids", "options"}, "", {}, [this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::enable_with_options, call, session.session_locale);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO, "enable", "as", {"repo_ids"}, "", {}, [this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::enable, call, session.session_locale);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO, "disable_with_options", "asa{sv}", {"repo_ids", "options"}, "", {}, [this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::disable_with_options, call, session.session_locale);
});
dbus_object->registerMethod(
dnfdaemon::INTERFACE_REPO, "disable", "as", {"repo_ids"}, "", {}, [this](sdbus::MethodCall call) -> void {
session.get_threads_manager().handle_method(*this, &Repo::disable, call, session.session_locale);
@ -339,12 +387,11 @@ void Repo::dbus_register() {
#endif
}
sdbus::MethodReply Repo::confirm_key(sdbus::MethodCall & call) {
std::string key_id;
bool confirmed;
call >> key_id >> confirmed;
sdbus::MethodReply Repo::impl_confirm_key(sdbus::MethodCall & call, const std::string &key_id, bool confirmed,
const dnfdaemon::KeyValueMap & options) {
bool interactive = dnfdaemon::key_value_map_get<bool>(options, "interactive", true);
if (confirmed) {
if (!session.check_authorization(dnfdaemon::POLKIT_CONFIRM_KEY_IMPORT, call.getSender())) {
if (!session.check_authorization(dnfdaemon::POLKIT_CONFIRM_KEY_IMPORT, call.getSender(), interactive)) {
session.confirm_key(key_id, false);
throw std::runtime_error("Not authorized");
}
@ -353,6 +400,24 @@ sdbus::MethodReply Repo::confirm_key(sdbus::MethodCall & call) {
return call.createReply();
}
sdbus::MethodReply Repo::confirm_key_with_options(sdbus::MethodCall & call) {
std::string key_id;
bool confirmed;
dnfdaemon::KeyValueMap options{};
call >> key_id >> confirmed >> options;
return impl_confirm_key(call, key_id, confirmed, options);
}
sdbus::MethodReply Repo::confirm_key(sdbus::MethodCall & call) {
std::string key_id;
bool confirmed;
dnfdaemon::KeyValueMap options{};
call >> key_id >> confirmed;
return impl_confirm_key(call, key_id, confirmed, options);
}
sdbus::MethodReply Repo::list(sdbus::MethodCall & call) {
dnfdaemon::KeyValueMap options;
call >> options;
@ -449,11 +514,11 @@ void Repo::enable_disable_repos(const std::vector<std::string> & ids, const bool
}
}
sdbus::MethodReply Repo::enable_disable(sdbus::MethodCall && call, const bool & enable) {
sdbus::MethodReply Repo::impl_enable_disable(sdbus::MethodCall & call, bool enable, const std::vector<std::string> & ids,
const dnfdaemon::KeyValueMap & options) {
auto sender = call.getSender();
std::vector<std::string> ids;
call >> ids;
auto is_authorized = session.check_authorization(dnfdaemon::POLKIT_REPOCONF_WRITE, sender);
bool interactive = dnfdaemon::key_value_map_get<bool>(options, "interactive", true);
auto is_authorized = session.check_authorization(dnfdaemon::POLKIT_REPOCONF_WRITE, sender, interactive);
if (!is_authorized) {
throw sdbus::Error(dnfdaemon::ERROR_REPOCONF, "Not authorized.");
}
@ -463,3 +528,35 @@ sdbus::MethodReply Repo::enable_disable(sdbus::MethodCall && call, const bool &
auto reply = call.createReply();
return reply;
}
sdbus::MethodReply Repo::enable_with_options(sdbus::MethodCall & call) {
std::vector<std::string> ids;
dnfdaemon::KeyValueMap options;
call >> ids >> options;
return impl_enable_disable(call, true, ids, options);
}
sdbus::MethodReply Repo::enable(sdbus::MethodCall & call) {
std::vector<std::string> ids;
dnfdaemon::KeyValueMap options{};
call >> ids;
return impl_enable_disable(call, true, ids, options);
}
sdbus::MethodReply Repo::disable_with_options(sdbus::MethodCall & call) {
std::vector<std::string> ids;
dnfdaemon::KeyValueMap options;
call >> ids >> options;
return impl_enable_disable(call, false, ids, options);
}
sdbus::MethodReply Repo::disable(sdbus::MethodCall & call) {
std::vector<std::string> ids;
dnfdaemon::KeyValueMap options{};
call >> ids;
return impl_enable_disable(call, false, ids, options);
}

View File

@ -36,10 +36,16 @@ public:
private:
sdbus::MethodReply list(sdbus::MethodCall & call);
sdbus::MethodReply impl_confirm_key(sdbus::MethodCall & call, const std::string &key_id, bool confirmed,
const dnfdaemon::KeyValueMap & options);
sdbus::MethodReply confirm_key_with_options(sdbus::MethodCall & call);
sdbus::MethodReply confirm_key(sdbus::MethodCall & call);
sdbus::MethodReply enable_disable(sdbus::MethodCall && call, const bool & enable);
sdbus::MethodReply enable(sdbus::MethodCall & call) { return enable_disable(std::move(call), true); };
sdbus::MethodReply disable(sdbus::MethodCall & call) { return enable_disable(std::move(call), false); };
sdbus::MethodReply impl_enable_disable(sdbus::MethodCall & call, bool enable, const std::vector<std::string> & ids,
const dnfdaemon::KeyValueMap & options);
sdbus::MethodReply enable_with_options(sdbus::MethodCall & call);
sdbus::MethodReply enable(sdbus::MethodCall & call);
sdbus::MethodReply disable_with_options(sdbus::MethodCall & call);
sdbus::MethodReply disable(sdbus::MethodCall & call);
void enable_disable_repos(const std::vector<std::string> & ids, const bool enable);
};