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
>
>
>

Attachment: Makefile
Description: Binary data

Attachment: mirror.conf
Description: Binary data

Attachment: 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

Reply via email to