diff --git a/tuned/exports/dbus_exporter.py b/tuned/exports/dbus_exporter.py
index 0c9e8be3..75a49a4d 100644
--- a/tuned/exports/dbus_exporter.py
+++ b/tuned/exports/dbus_exporter.py
@@ -122,7 +122,28 @@ def _prepare_for_dbus(self, method, wrapper):
func = FunctionType(code.co_consts[0], locals(), method.__name__)
return func
- def export(self, method, in_signature, out_signature):
+ def _polkit_auth(self, action_name, *args):
+ action_id = self._namespace + "." + action_name
+ caller = args[-1]
+ log.debug("checking authorization for action '%s' requested by caller '%s'" % (action_id, caller))
+ ret = self._polkit.check_authorization(caller, action_id)
+ args_copy = args
+ if ret == 1:
+ log.debug("action '%s' requested by caller '%s' was successfully authorized by polkit" % (action_id, caller))
+ elif ret == 2:
+ log.warning("polkit error, but action '%s' requested by caller '%s' was successfully authorized by fallback method" % (action_id, caller))
+ elif ret == 0:
+ log.info("action '%s' requested by caller '%s' wasn't authorized, ignoring the request" % (action_id, caller))
+ args_copy = list(args[:-1]) + [""]
+ elif ret == -1:
+ log.warning("polkit error and action '%s' requested by caller '%s' wasn't authorized by fallback method, ignoring the request" % (action_id, caller))
+ args_copy = list(args[:-1]) + [""]
+ else:
+ log.error("polkit error and unable to use fallback method to authorize action '%s' requested by caller '%s', ignoring the request" % (action_id, caller))
+ args_copy = list(args[:-1]) + [""]
+ return args_copy
+
+ def export(self, method, in_signature, out_signature, action_name=None):
if not ismethod(method):
raise Exception("Only bound methods can be exported.")
@@ -130,28 +151,11 @@ def export(self, method, in_signature, out_signature):
if method_name in self._dbus_methods:
raise Exception("Method with this name is already exported.")
- def wrapper(owner, *args, **kwargs):
- action_id = self._namespace + "." + method.__name__
- caller = args[-1]
- log.debug("checking authorization for action '%s' requested by caller '%s'" % (action_id, caller))
- ret = self._polkit.check_authorization(caller, action_id)
- args_copy = args
- if ret == 1:
- log.debug("action '%s' requested by caller '%s' was successfully authorized by polkit" % (action_id, caller))
- elif ret == 2:
- log.warning("polkit error, but action '%s' requested by caller '%s' was successfully authorized by fallback method" % (action_id, caller))
- elif ret == 0:
- log.info("action '%s' requested by caller '%s' wasn't authorized, ignoring the request" % (action_id, caller))
- args_copy = list(args[:-1]) + [""]
- elif ret == -1:
- log.warning("polkit error and action '%s' requested by caller '%s' wasn't authorized by fallback method, ignoring the request" % (action_id, caller))
- args_copy = list(args[:-1]) + [""]
- else:
- log.error("polkit error and unable to use fallback method to authorize action '%s' requested by caller '%s', ignoring the request" % (action_id, caller))
- args_copy = list(args[:-1]) + [""]
- return method(*args_copy, **kwargs)
+ action_name = action_name or method_name
+ def auth_wrapper(owner, *args, **kwargs):
+ return method(*self._polkit_auth(action_name, *args), **kwargs)
- wrapper = self._prepare_for_dbus(method, wrapper)
+ wrapper = self._prepare_for_dbus(method, auth_wrapper)
wrapper = dbus.service.method(self._interface_name, in_signature, out_signature, sender_keyword = "caller")(wrapper)
self._dbus_methods[method_name] = wrapper
diff --git a/tuned/exports/dbus_exporter_with_properties.py b/tuned/exports/dbus_exporter_with_properties.py
index 64219e24..8b327b98 100644
--- a/tuned/exports/dbus_exporter_with_properties.py
+++ b/tuned/exports/dbus_exporter_with_properties.py
@@ -11,50 +11,62 @@ def __init__(self, bus_name, interface_name, object_name, namespace):
self._property_setters = {}
self._property_getters = {}
- def Get(_, interface_name, property_name):
+ def Get(_, interface_name, property_name, caller):
if interface_name != self._interface_name:
raise DBusException("Unknown interface: %s" % interface_name)
if property_name not in self._property_getters:
raise DBusException("No such property: %s" % property_name)
getter = self._property_getters[property_name]
- return getter()
+ return getter(caller)
- def Set(_, interface_name, property_name, value):
+ def Set(_, interface_name, property_name, value, caller):
if interface_name != self._interface_name:
raise DBusException("Unknown interface: %s" % interface_name)
if property_name not in self._property_setters:
raise DBusException("No such property: %s" % property_name)
setter = self._property_setters[property_name]
- setter(value)
+ setter(value, caller)
- def GetAll(_, interface_name):
+ def GetAll(_, interface_name, caller):
if interface_name != self._interface_name:
raise DBusException("Unknown interface: %s" % interface_name)
- return {name: getter() for name, getter in self._property_getters.items()}
+ return {name: getter(caller) for name, getter in self._property_getters.items()}
def PropertiesChanged(_, interface_name, changed_properties, invalidated_properties):
if interface_name != self._interface_name:
raise DBusException("Unknown interface: %s" % interface_name)
- self._dbus_methods["Get"] = method(PROPERTIES_IFACE, in_signature="ss", out_signature="v")(Get)
- self._dbus_methods["Set"] = method(PROPERTIES_IFACE, in_signature="ssv")(Set)
- self._dbus_methods["GetAll"] = method(PROPERTIES_IFACE, in_signature="s", out_signature="a{sv}")(GetAll)
+ self._dbus_methods["Get"] = method(PROPERTIES_IFACE, in_signature="ss", out_signature="v", sender_keyword="caller")(Get)
+ self._dbus_methods["Set"] = method(PROPERTIES_IFACE, in_signature="ssv", sender_keyword="caller")(Set)
+ self._dbus_methods["GetAll"] = method(PROPERTIES_IFACE, in_signature="s", out_signature="a{sv}", sender_keyword="caller")(GetAll)
self._dbus_methods["PropertiesChanged"] = signal(PROPERTIES_IFACE, signature="sa{sv}as")(PropertiesChanged)
self._signals.add("PropertiesChanged")
+ def _auth_wrapper(self, method, action_name):
+ def wrapper(*args, **kwargs):
+ new_args = self._polkit_auth(action_name, *args)
+ if new_args[-1] == "":
+ raise DBusException("Unauthorized")
+ return method(*new_args, **kwargs)
+ return wrapper
+
def property_changed(self, property_name, value):
self.send_signal("PropertiesChanged", self._interface_name, {property_name: value}, {})
- def property_getter(self, method, property_name):
+ def property_getter(self, method, property_name, action_name=None):
if not ismethod(method):
raise Exception("Only bound methods can be exported.")
if property_name in self._property_getters:
raise Exception("A getter for this property is already registered.")
+ if action_name is not None:
+ method = self._auth_wrapper(method, action_name)
self._property_getters[property_name] = method
- def property_setter(self, method, property_name):
+ def property_setter(self, method, property_name, action_name=None):
if not ismethod(method):
raise Exception("Only bound methods can be exported.")
if property_name in self._property_setters:
raise Exception("A setter for this property is already registered.")
+ if action_name is not None:
+ method = self._auth_wrapper(method, action_name)
self._property_setters[property_name] = method
diff --git a/tuned/ppd/controller.py b/tuned/ppd/controller.py
index 4376c1e9..b7fb00dd 100644
--- a/tuned/ppd/controller.py
+++ b/tuned/ppd/controller.py
@@ -172,7 +172,7 @@ def active_profile(self):
tuned_profile = self._tuned_interface.active_profile()
return self._config.tuned_to_ppd.get(tuned_profile, UNKNOWN_PROFILE)
- @exports.export("sss", "u")
+ @exports.export("sss", "u", "hold-profile")
def HoldProfile(self, profile, reason, app_id, caller):
if profile != PPD_POWER_SAVER and profile != PPD_PERFORMANCE:
raise dbus.exceptions.DBusException(
@@ -180,7 +180,7 @@ def HoldProfile(self, profile, reason, app_id, caller):
)
return self._profile_holds.add(profile, reason, app_id, caller)
- @exports.export("u", "")
+ @exports.export("u", "", "release-profile")
def ReleaseProfile(self, cookie, caller):
if not self._profile_holds.has(cookie):
raise dbus.exceptions.DBusException("No active hold for cookie '%s'" % cookie)
@@ -190,8 +190,8 @@ def ReleaseProfile(self, cookie, caller):
def ProfileReleased(self, cookie):
pass
- @exports.property_setter("ActiveProfile")
- def set_active_profile(self, profile):
+ @exports.property_setter("ActiveProfile", "switch-profile")
+ def set_active_profile(self, profile, caller):
if profile not in self._config.ppd_to_tuned:
raise dbus.exceptions.DBusException("Invalid profile '%s'" % profile)
log.debug("Setting base profile to %s" % profile)
@@ -200,24 +200,24 @@ def set_active_profile(self, profile):
self.switch_profile(profile)
@exports.property_getter("ActiveProfile")
- def get_active_profile(self):
+ def get_active_profile(self, caller):
return self.active_profile()
@exports.property_getter("Profiles")
- def get_profiles(self):
+ def get_profiles(self, caller):
return dbus.Array(
[{"Profile": profile, "Driver": DRIVER} for profile in self._config.ppd_to_tuned.keys()],
signature="a{sv}",
)
@exports.property_getter("Actions")
- def get_actions(self):
+ def get_actions(self, caller):
return dbus.Array([], signature="s")
@exports.property_getter("PerformanceDegraded")
- def get_performance_degraded(self):
+ def get_performance_degraded(self, caller):
return self._performance_degraded
@exports.property_getter("ActiveProfileHolds")
- def get_active_profile_holds(self):
+ def get_active_profile_holds(self, caller):
return self._profile_holds.as_dbus_array()
diff --git a/tuned/ppd/tuned-ppd.policy b/tuned/ppd/tuned-ppd.policy
index 8106808d..f4dea8c3 100644
--- a/tuned/ppd/tuned-ppd.policy
+++ b/tuned/ppd/tuned-ppd.policy
@@ -6,7 +6,17 @@
TuneD
https://tuned-project.org/
-
+
+ Switch power profile
+ Authentication is required to switch power profiles.
+
+ no
+ no
+ yes
+
+
+
+
Hold power profile
Authentication is required to hold power profiles.
@@ -16,7 +26,7 @@
-
+
Release power profile
Authentication is required to release power profiles.