Thursday, February 24, 2011

Writing PolicyKit applications in Python without D-Bus

I'm always happy when more libraries get Python bindings, and today I've been especially excited by the fact that GObject introspection support for PolicyKit has finally appeared in Natty repositories. Basically it means, that PolicyKit is now accessible from Python through a native API without direct D-Bus communication. I've tried to port the querying example from PolicyKit manual and it did work!

The Python example below is an almost line-by-line port of the original, but it should give you a basic idea on how to use the API for your own scripts.

#! /usr/bin/env python

import sys, os
from gi.repository import GObject, Gio, Polkit

def on_tensec_timeout(loop):
  print("Ten seconds have passed. Now exiting.")
  loop.quit()
  return False

def check_authorization_cb(authority, res, loop):
    try:
        result = authority.check_authorization_finish(res)
        if result.get_is_authorized():
            print("Authorized")
        elif result.get_is_challenge():
            print("Challenge")
        else:
            print("Not authorized")
    except GObject.GError as error:
         print("Error checking authorization: %s" % error.message)
        
    print("Authorization check has been cancelled "
          "and the dialog should now be hidden.\n"
          "This process will exit in ten seconds.")
    GObject.timeout_add(10000, on_tensec_timeout, loop)

def do_cancel(cancellable):
    print("Timer has expired; cancelling authorization check")
    cancellable.cancel()
    return False

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("usage: %s <action_id>" % sys.argv[0])
        sys.exit(1)
    action_id = sys.argv[1]

    mainloop = GObject.MainLoop()
    authority = Polkit.Authority.get()
    subject = Polkit.UnixProcess.new(os.getppid())

    cancellable = Gio.Cancellable()
    GObject.timeout_add(10 * 1000, do_cancel, cancellable)

    authority.check_authorization(subject,
        action_id, #"org.freedesktop.policykit.exec",
        None,
        Polkit.CheckAuthorizationFlags.ALLOW_USER_INTERACTION,
        cancellable,
        check_authorization_cb,
        mainloop)

    mainloop.run()

In order to run this example, make sure you have the gir1.2-polkit-1.0 package installed and provide an action in the command line, for example:

./polkit-test org.freedesktop.policykit.exec

I suppose, the API is still a little rough on the edges, but it's already usable and I'm going to try it for an upcoming D-Bus service in indicator-cpufreq. Looks like it's perfect time to start moving things to GObject introspection already.

7 comments:

  1. Many thanks! But I've not understan one thing: one time we get the authorization where to put the code?
    (example: append a string to a system file owned by root)
    we must still create a dbus method?
    thanks again

    ReplyDelete
  2. No, just insert your code or a method call into the check_authorization_cb (if result.get_is_authorized() clause).

    ReplyDelete
  3. I get this:
    [fabio@hp6735b Materiale]$ python polkit_test.py
    Authorized
    Traceback (most recent call last):
    File "polkit_test.py", line 16, in check_authorization_cb
    my_func()
    File "polkit_test.py", line 35, in my_func
    f = open('/etc/test','a')
    IOError: [Errno 13] Permission denied: '/etc/test'
    Timer has expired; cancelling authorization check

    ReplyDelete
  4. Ah, I see. You're running the process as a regular user here. Not sure if it's possible to temporary give root privileges to a user process with polkit, as I only tried this method with a dbus service (which is running as root).

    This way, the service will be able to write into /etc/test anytime you call the dbus method, but it will also check if the caller is allowed to perform this action (or ask to enter the sudo password). I think this behaviour may be configured in the busconfig file for the service either, but not sure about this.

    ReplyDelete
  5. thanks, the problem is that I make much confusion between dbus and policykit and I can't figure out how to solve my problem...
    My hope whose that with this method I don't have to use dbus.

    ReplyDelete
  6. can you provide an example? I don't know where to break my head :)

    ReplyDelete
    Replies
    1. yep, look at jockey or indicator-cpufreq (both have a dbus service to change things).

      Delete