Attaching updated example (works well for me). Please feedback if it is not doing the right calls. I haven't updated it to use the new polkit-gtk button but that will be the next step.
2009/7/27 David Zeuthen <da...@fubar.dk>: > On Fri, 2009-07-24 at 17:40 +1000, Robert Ancell wrote: >> Hi, >> >> I'm trying to learn how to use PolicyKit for configuration of a >> server. Find attached a demo program (which I plan to post as a a >> tutorial) which does the following: >> - Has a server providing a method "Reflect" which reverses a string >> - Has a client with a text entry that uses that uses the server to >> reflect entered text >> - The client has an "unlock" button that should authorize with the PolKit >> once >> >> Note I've tried to make the simplest example not the most efficient. > > The code doesn't really compile without mirror.h... > >> I've been stumbling along for a while and am in need of some help: >> - I've used POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION in >> polkit_authority_check_authorization_sync to prompt the user for >> authentication but sometimes it just blocks for ages instead of >> popping up the dialog. When should I use this flag and when let the >> client do the authorization? >> - I haven't been able to get the client to manually authorize or have >> persistent authorization, how do I do this? >> - Any pointers? >> >> The documentation I have been working from: >> http://dbus.freedesktop.org/doc/dbus-specification.html >> http://hal.freedesktop.org/docs/PolicyKit/ >> http://people.freedesktop.org/~david/polkit-0.92-newdocs/ >> http://cgit.freedesktop.org/PolicyKit/tree/docs/PORTING-GUIDE > > First you appear to be mixing the new PolicyKit version with the old one > (the server links against polkit-gobject-1, the client against > polkit-dbus). In the new PolicyKit, with version > 0.90) the client is > not supposed to know anything about PolicyKit at all - it simply invokes > methods on the server and the server simply does authorization checks > using polkit. If the server passes ALLOW_USER_INTERACTION to polkit it > means that Authentication Dialogs may appear - but the client would > never need to know anything about this. In fact, the client itself > _cannot_ even speak to PolicyKit - it is not allowed. > > It is not clear to me what exactly you are trying to achieve - are you > trying to do stuff that works like the OS X lock? For example > > http://lh4.ggpht.com/dr.k.anil/SE5J8JltXOI/AAAAAAAACe4/b9a-eq03RV8/accounts.jpg > > If so, you need to add support for this in the Server itself, e.g. > something like > > interface com.example.Mirror { > /// Returns %TRUE if the configuration interface is locked for the > /// caller - e.g. Conf*() methods will require authentication > GetIsLocked (OUT boolean is_locked); > > /// Returns %TRUE iff the configuration can be unlocked > GetCanUnlock (OUT boolean can_be_unlocked); > > /// Returns %TRUE iff the configuration can be locked > GetCanLock (OUT boolean can_be_locked); > > /// Unlock the interface for the caller so the caller can call > /// Conf*() methods without authenticating > Unlock(); > > /// Lock the configuration interface - caller can no longer call > /// Conf*() methods without authenticating. > Lock(); > > /// Emitted when the lock status changes - all callers should > /// check with GetIsLocked() > signal LockChanged(); > > /// --- Actual methods > > ConfMethod1(...); > ConfMethod2(...); > ... > }; > > Note that GetIsLocked() depends on the actual caller (the configuration > interface may be locked for one caller and unlocked for another one) - > so it cannot be a property. For the same reasons the LockChanged() > signal doesn't include the state. > > So, anyway, with a design like this, then on the server side > > - You would use CheckAuthorization() without ALLOW_USER_INTERACTION for > when implementing GetIsLocked() and GetCanUnlock() > > - For Unlock() you would use ALLOW_USER_INTERACTION > > - For Lock() you will need some API not yet added (CheckAuthorization() > needs to return the tmp authz identifier - and we need a way to > revoke a tmp authz by id). > > On the client side > > - Use GetCanUnlock(), GetCanLock() and GetIsLocked() to render the lock > icon and decide if it is sensitive or not. You'd use Unlock() and > Lock() when the user clicks the lock. > > Hmm, it might be nice to have library API for doing all this since it is > rather complicated. Like, we'd have some code in polkit-gobject-1 to do > the server side bits and maybe a polkit-gtk-1 library for the client > side bits. Unfortunately we don't have a nice D-Bus library for GLib > just yet so it is not yet feasible to do the server side bits just > yet... > > Hope this helps. > > David > > >
Makefile
Description: Binary data
mirror.conf
Description: Binary data
mirror.policy
Description: Binary data
<?xml version="1.0" encoding="UTF-8" ?> <node name="/com/example/Mirror"> <interface name="com.example.Mirror"> <method name="Unlock"> <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> <arg type="b" name="out" direction="out" /> </method> <method name="Reflect"> <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> <arg type="s" name="in" direction="in" /> <arg type="s" name="out" direction="out" /> </method> </interface> </node>
#include <sys/types.h> #include <unistd.h> #include <gtk/gtk.h> #include <dbus/dbus.h> #include "mirror-client-glue.h" GtkWidget *mirror_entry, *error_label; DBusGProxy *proxy = NULL; static void mirror_cb(GtkWidget *entry) { char *result; GError *error = NULL; com_example_Mirror_reflect(proxy, gtk_entry_get_text(GTK_ENTRY(entry)), &result, &error); if (error != NULL) { gtk_label_set(GTK_LABEL(error_label), error->message); return; } gtk_label_set(GTK_LABEL(error_label), ""); gtk_entry_set_text(GTK_ENTRY(entry), result); } static void authorize_cb(GtkWidget *button) { gboolean is_unlocked; GError *error = NULL; com_example_Mirror_unlock(proxy, &is_unlocked, &error); if (error != NULL) { gtk_label_set(GTK_LABEL(error_label), error->message); return; } /* Disable button once authorized */ if(is_unlocked) { gtk_widget_set_sensitive(mirror_entry, TRUE); gtk_widget_set_sensitive(button, FALSE); } } int main(int argc, char **argv) { DBusGConnection *connection; GtkWidget *window, *vbox, *label, *button; GError *error = NULL; gtk_init(&argc, &argv); connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (error) { printf("Failed to get bus: %s\n", error->message); return 1; } proxy = dbus_g_proxy_new_for_name(connection, "com.example.Mirror", "/com/example/Mirror", "com.example.Mirror"); window = gtk_window_new(GTK_WINDOW_TOPLEVEL); g_signal_connect(G_OBJECT(window), "delete-event", G_CALLBACK(gtk_main_quit), NULL); vbox = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), vbox); label = gtk_label_new("Text to send:"); gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, TRUE, 3); mirror_entry = gtk_entry_new(); g_signal_connect(G_OBJECT(mirror_entry), "activate", G_CALLBACK(mirror_cb), NULL); gtk_widget_set_sensitive(GTK_WIDGET(mirror_entry), FALSE); gtk_box_pack_start(GTK_BOX(vbox), mirror_entry, FALSE, TRUE, 3); error_label = gtk_label_new(""); gtk_label_set_line_wrap(GTK_LABEL(error_label), TRUE); gtk_box_pack_start(GTK_BOX(vbox), error_label, FALSE, TRUE, 3); button = gtk_button_new_with_label("Unlock"); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(authorize_cb), NULL); gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, TRUE, 3); gtk_widget_show_all(window); gtk_main(); return 0; }
#include <stdio.h> #include <dbus/dbus-glib-lowlevel.h> #include <polkit/polkit.h> #include "mirror-server.h" #include "mirror-server-glue.h" /* Boilerplate for Mirror object */ G_DEFINE_TYPE(Mirror, mirror, G_TYPE_OBJECT) Mirror *mirror_new() { return g_object_new(TYPE_MIRROR, NULL); } static void mirror_init(Mirror *mirror) {} static void mirror_class_init(MirrorClass *klass) { /* Add D-Bus metadata */ dbus_g_object_type_install_info(TYPE_MIRROR, &dbus_glib_mirror_object_info); } /* Table of D-Bus sessions that are authorized */ static GHashTable *authorization_table; static gboolean unlock(const gchar *sender, GError **error) { PolkitAuthorizationResult *result; gboolean is_authorized = FALSE; /* No need if already authorized */ if (g_hash_table_lookup(authorization_table, sender)) return TRUE; /* Authorize with PolicyKit */ result = polkit_authority_check_authorization_sync(polkit_authority_get(), polkit_system_bus_name_new(sender), "com.example.mirror.use", NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, error); if (result && polkit_authorization_result_get_is_authorized(result)) { /* Record this sender as authorized */ g_hash_table_insert(authorization_table, g_strdup(sender), GINT_TO_POINTER(TRUE)); is_authorized = TRUE; } if (result) g_object_unref(result); return is_authorized; } gboolean mirror_unlock(Mirror *mirror, DBusGMethodInvocation *context) { GError *error = NULL; gboolean is_unlocked; is_unlocked = unlock(dbus_g_method_get_sender(context), &error); if (error) { printf("Failed to check authorization: %s\n", error->message); dbus_g_method_return_error(context, error); return FALSE; } dbus_g_method_return(context, is_unlocked); return TRUE; } /* Method expored over D-Bus and uses PolKit for authorization */ gboolean mirror_reflect(Mirror *mirror, const gchar *name, DBusGMethodInvocation *context) { GError *error = NULL; gboolean is_unlocked; gchar *value; printf("reflect '%s' by %s\n", name, dbus_g_method_get_sender(context)); is_unlocked = unlock(dbus_g_method_get_sender(context), &error); if (!is_unlocked) { if (!error) { printf("Not authorized\n"); error = g_error_new(DBUS_GERROR_REMOTE_EXCEPTION, 0, "Not authorized"); } else printf("Failed to check authorization: %s\n", error->message); dbus_g_method_return_error(context, error); g_error_free(error); return FALSE; } value = g_strdup(name); g_strreverse(value); dbus_g_method_return(context, value); return TRUE; } int main(int argc, char **argv) { GError *error = NULL; DBusGConnection *connection; DBusError dbus_error; Mirror *mirror; GMainLoop *loop; int result; /* Initialise GLib */ g_type_init(); authorization_table = g_hash_table_new(g_str_hash, g_str_equal); /* Connect to the system D-Bus */ connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error); if (error) { printf("Failed to get bus: %s\n", error->message); return 1; } /* Register this services name on the bus */ dbus_error_init(&dbus_error); result = dbus_bus_request_name(dbus_g_connection_get_connection(connection), "com.example.Mirror", DBUS_NAME_FLAG_DO_NOT_QUEUE, &dbus_error); if (dbus_error_is_set(&dbus_error)) { printf("Failed to register name: %s\n", dbus_error.message); return 1; } if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { printf("Unable to get bus name\n"); return 1; } /* Make a new object and connect it to the bus */ mirror = mirror_new(); dbus_g_connection_register_g_object(connection, "/com/example/Mirror", G_OBJECT(mirror)); /* Loop, processing requests */ loop = g_main_loop_new(g_main_context_default(), TRUE); g_main_loop_run(loop); return 0; }
#ifndef __MIRROR_H #define __MIRROR_H #include <glib-object.h> #include <dbus/dbus-glib.h> G_BEGIN_DECLS #define TYPE_MIRROR (mirror_get_type ()) #define MIRROR(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_MIRROR, Mirror)) #define MIRROR_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TYPE_MIRROR, MirrorClass)) #define IS_MIRROR(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_MIRROR)) #define IS_MIRROR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TYPE_MIRROR)) #define MIRROR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_MIRROR, MirrorClass)) typedef struct { GObject parent; } Mirror; typedef struct { GObjectClass parent_class; } MirrorClass; GType mirror_get_type(); Mirror *mirror_new(); gboolean mirror_unlock(Mirror *mirror, DBusGMethodInvocation *context); gboolean mirror_is_unlocked(Mirror *mirror, DBusGMethodInvocation *context); gboolean mirror_reflect(Mirror *mirror, const gchar *name, DBusGMethodInvocation *context); #endif /* __MIRROR_H */
_______________________________________________ polkit-devel mailing list polkit-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/polkit-devel