Commit 90e6e250 authored by Dan Williams's avatar Dan Williams Committed by Dan Williams
Browse files

Ensure PolicyKit autorization before editing system connections, and

2009-04-03  Dan Williams  <dcbw@redhat.com>

	* Ensure PolicyKit autorization before editing system connections, and
		request secrets from the system settings service for system connections.
		This requires splitting each CEPage subclass's *_new() method into
		two parts, the first doing minimal setup, and the second filling in
		the UI with details from the backing NMConnection.  Between the first
		and second parts, if required, secrets are requested from the system
		settings service using PolicyKit.  As a bonus, actually handle errors
		instead of dropping them on the floor.


svn path=/trunk/; revision=1244
parent 16d87d34
2009-04-03 Dan Williams <dcbw@redhat.com>
* Ensure PolicyKit autorization before editing system connections, and
request secrets from the system settings service for system connections.
This requires splitting each CEPage subclass's *_new() method into
two parts, the first doing minimal setup, and the second filling in
the UI with details from the backing NMConnection. Between the first
and second parts, if required, secrets are requested from the system
settings service using PolicyKit. As a bonus, actually handle errors
instead of dropping them on the floor.
2009-03-31 Dan Williams <dcbw@redhat.com>
* src/connection-editor/nm-connection-list.c
......
......@@ -33,6 +33,7 @@ src/connection-editor/page-wired.c
src/connection-editor/page-wired-security.c
src/connection-editor/page-wireless.c
src/connection-editor/page-wireless-security.c
src/connection-editor/polkit-helpers.c
src/connection-editor/nm-connection-editor.c
src/connection-editor/nm-connection-editor.glade
src/connection-editor/nm-connection-list.c
......
......@@ -2,20 +2,21 @@ NULL=
bin_PROGRAMS = nm-connection-editor
nm_connection_editor_CPPFLAGS = \
$(NMA_CFLAGS) \
-DICONDIR=\""$(datadir)/icons"\" \
-DGLADEDIR=\""$(gladedir)"\" \
-DBINDIR=\""$(bindir)"\" \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DNMALOCALEDIR=\"$(datadir)/locale\" \
$(DBUS_CFLAGS) \
nm_connection_editor_CPPFLAGS = \
$(NMA_CFLAGS) \
-DICONDIR=\""$(datadir)/icons"\" \
-DGLADEDIR=\""$(gladedir)"\" \
-DBINDIR=\""$(bindir)"\" \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-DLIBDIR=\""$(libdir)"\" \
-DNMALOCALEDIR=\"$(datadir)/locale\" \
$(DBUS_CFLAGS) \
$(POLKIT_CFLAGS) \
$(DISABLE_DEPRECATED) \
-I${top_srcdir}/src/gconf-helpers \
$(DISABLE_DEPRECATED) \
-I${top_srcdir}/src/gconf-helpers \
-I${top_srcdir}/src/utils \
-I${top_srcdir}/src/wireless-security \
-I${top_builddir}/src/marshallers \
$(NULL)
if NO_POLKIT_GNOME
......@@ -56,7 +57,9 @@ nm_connection_editor_SOURCES = \
ip4-routes-dialog.h \
ip4-routes-dialog.c \
ppp-auth-methods-dialog.c \
ppp-auth-methods-dialog.h
ppp-auth-methods-dialog.h \
polkit-helpers.c \
polkit-helpers.h
nm-connection-editor-service-glue.h: $(top_srcdir)/src/connection-editor/nm-connection-editor-service.xml
dbus-binding-tool --prefix=nm_connection_editor_service --mode=glib-server --output=$@ $<
......@@ -65,6 +68,7 @@ nm_connection_editor_LDADD = \
$(top_builddir)/src/gconf-helpers/libgconf-helpers.la \
${top_builddir}/src/wireless-security/libwireless-security.la \
${top_builddir}/src/utils/libutils.la \
${top_builddir}/src/marshallers/libmarshallers.la \
$(NMA_LIBS) \
$(POLKIT_LIBS)
......
......@@ -17,7 +17,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Red Hat, Inc.
* (C) Copyright 2008 - 2009 Red Hat, Inc.
*/
#include <net/ethernet.h>
......@@ -28,12 +28,30 @@
#include <glib/gi18n.h>
#include "ce-page.h"
#include "nma-marshal.h"
#include "utils.h"
#include "polkit-helpers.h"
#define DBUS_TYPE_G_ARRAY_OF_STRING (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING))
#define DBUS_TYPE_G_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
#define DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT))
static gboolean internal_request_secrets (CEPage *self, GError **error);
G_DEFINE_ABSTRACT_TYPE (CEPage, ce_page, G_TYPE_OBJECT)
enum {
PROP_0,
PROP_CONNECTION,
PROP_INITIALIZED,
PROP_PARENT_WINDOW,
LAST_PROP
};
enum {
CHANGED,
INITIALIZED,
LAST_SIGNAL
};
......@@ -151,10 +169,159 @@ ce_page_entry_to_mac (GtkEntry *entry, gboolean *invalid)
return mac;
}
static void
emit_initialized (CEPage *self, GError *error)
{
self->initialized = TRUE;
g_signal_emit (self, signals[INITIALIZED], 0, NULL, error);
}
static void
try_secrets_again (PolKitAction *action,
gboolean gained_privilege,
GError *error,
gpointer user_data)
{
CEPage *self = user_data;
GError *real_error = NULL;
if (error) {
emit_initialized (self, error);
return;
}
if (gained_privilege) {
/* Yay! Got privilege, try again */
internal_request_secrets (self, &real_error);
} else if (!error) {
/* Sometimes PK screws up and won't return an error even if
* the operation failed.
*/
g_set_error (&real_error, 0, 0, "%s",
_("Insufficient privileges or unknown error retrieving system connection secrets."));
}
if (real_error)
emit_initialized (self, real_error);
g_clear_error (&real_error);
}
static void
get_secrets_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
{
CEPage *self = user_data;
GError *error = NULL;
GHashTable *settings = NULL, *setting_hash;
gboolean do_signal = TRUE;
if (!dbus_g_proxy_end_call (proxy, call, &error,
DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, &settings,
G_TYPE_INVALID)) {
if (pk_helper_is_permission_denied_error (error)) {
/* If permission was denied, try to authenticate */
g_clear_error (&error);
if (pk_helper_obtain_auth (error, self->parent_window, try_secrets_again, self, &error))
do_signal = FALSE; /* 'secrets' signal will happen after auth result */
}
} else {
/* Update the connection with the new secrets */
setting_hash = g_hash_table_lookup (settings, self->setting_name);
if (setting_hash) {
if (!nm_connection_update_secrets (self->connection,
self->setting_name,
setting_hash,
&error)) {
if (!error) {
g_set_error (&error, 0, 0, "%s",
_("Failed to update connection secrets due to an unknown error."));
}
}
}
g_hash_table_destroy (settings);
}
if (do_signal)
emit_initialized (self, error);
}
static gboolean
internal_request_secrets (CEPage *self, GError **error)
{
DBusGProxyCall *call;
GPtrArray *hints = NULL;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (self->proxy != NULL, FALSE);
g_return_val_if_fail (self->setting_name != NULL, FALSE);
hints = g_ptr_array_new ();
call = dbus_g_proxy_begin_call_with_timeout (self->proxy, "GetSecrets",
get_secrets_cb, self, NULL,
10000,
G_TYPE_STRING, self->setting_name,
DBUS_TYPE_G_ARRAY_OF_STRING, hints,
G_TYPE_BOOLEAN, FALSE,
G_TYPE_INVALID);
g_ptr_array_free (hints, TRUE);
if (!call) {
g_set_error (error, 0, 0, "%s", _("Could not request secrets from the system settings service."));
return FALSE;
}
return TRUE;
}
gboolean
ce_page_initialize (CEPage *self,
const char *setting_name,
GError **error)
{
DBusGConnection *g_connection;
gboolean success = FALSE;
NMConnectionScope scope;
g_return_val_if_fail (self != NULL, FALSE);
g_return_val_if_fail (self->connection != NULL, FALSE);
/* Don't need to request secrets from user connections or from
* settings which are known to not require secrets.
*/
scope = nm_connection_get_scope (self->connection);
if (!setting_name || (scope != NM_CONNECTION_SCOPE_SYSTEM)) {
emit_initialized (self, NULL);
return TRUE;
}
g_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, error);
if (!g_connection) {
g_set_error (error, 0, 0, "%s", _("Could not connect to D-Bus to request connection secrets."));
return FALSE;
}
if (self->setting_name)
g_free (self->setting_name);
self->setting_name = g_strdup (setting_name);
self->proxy = dbus_g_proxy_new_for_name (g_connection,
NM_DBUS_SERVICE_SYSTEM_SETTINGS,
nm_connection_get_path (self->connection),
NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS);
if (!self->proxy) {
g_set_error (error, 0, 0, "%s", _("Could not create D-Bus proxy for connection secrets."));
goto out;
}
success = internal_request_secrets (self, error);
out:
dbus_g_connection_unref (g_connection);
return success;
}
static void
ce_page_init (CEPage *self)
{
self->disposed = FALSE;
}
static void
......@@ -173,6 +340,12 @@ dispose (GObject *object)
if (self->xml)
g_object_unref (self->xml);
if (self->proxy)
g_object_unref (self->proxy);
if (self->connection)
g_object_unref (self->connection);
G_OBJECT_CLASS (ce_page_parent_class)->dispose (object);
}
......@@ -181,8 +354,8 @@ finalize (GObject *object)
{
CEPage *self = CE_PAGE (object);
if (self->title)
g_free (self->title);
g_free (self->title);
g_free (self->setting_name);
G_OBJECT_CLASS (ce_page_parent_class)->finalize (object);
}
......@@ -203,6 +376,14 @@ ce_page_get_title (CEPage *self)
return self->title;
}
gboolean
ce_page_get_initialized (CEPage *self)
{
g_return_val_if_fail (CE_IS_PAGE (self), FALSE);
return self->initialized;
}
void
ce_page_changed (CEPage *self)
{
......@@ -211,22 +392,97 @@ ce_page_changed (CEPage *self)
g_signal_emit (self, signals[CHANGED], 0);
}
static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
CEPage *self = CE_PAGE (object);
switch (prop_id) {
case PROP_CONNECTION:
g_value_set_object (value, self->connection);
break;
case PROP_INITIALIZED:
g_value_set_boolean (value, self->initialized);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
CEPage *self = CE_PAGE (object);
switch (prop_id) {
case PROP_CONNECTION:
if (self->connection)
g_object_unref (self->connection);
self->connection = g_value_dup_object (value);
break;
case PROP_PARENT_WINDOW:
self->parent_window = g_value_get_pointer (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ce_page_class_init (CEPageClass *page_class)
{
GObjectClass *object_class = G_OBJECT_CLASS (page_class);
/* virtual methods */
object_class->dispose = dispose;
object_class->finalize = finalize;
object_class->dispose = dispose;
object_class->finalize = finalize;
object_class->get_property = get_property;
object_class->set_property = set_property;
/* Properties */
g_object_class_install_property
(object_class, PROP_CONNECTION,
g_param_spec_object (CE_PAGE_CONNECTION,
"Connection",
"Connection",
NM_TYPE_CONNECTION,
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_INITIALIZED,
g_param_spec_boolean (CE_PAGE_INITIALIZED,
"Initialized",
"Initialized",
FALSE,
G_PARAM_READABLE));
g_object_class_install_property
(object_class, PROP_PARENT_WINDOW,
g_param_spec_pointer (CE_PAGE_PARENT_WINDOW,
"Parent window",
"Parent window",
G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
/* Signals */
signals[CHANGED] =
g_signal_new ("changed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (CEPageClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (CEPageClass, changed),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[INITIALIZED] =
g_signal_new ("initialized",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (CEPageClass, initialized),
NULL, NULL,
nma_marshal_VOID__POINTER_POINTER,
G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
}
......@@ -29,6 +29,7 @@
#include <gtk/gtk.h>
#include <glade/glade.h>
#include <dbus/dbus-glib.h>
#include <nm-connection.h>
#define CE_TYPE_PAGE (ce_page_get_type ())
......@@ -38,13 +39,25 @@
#define CE_IS_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), CE_TYPE_PAGE))
#define CE_PAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CE_TYPE_PAGE, CEPageClass))
#define CE_PAGE_CONNECTION "connection"
#define CE_PAGE_INITIALIZED "initialized"
#define CE_PAGE_PARENT_WINDOW "parent-window"
typedef struct {
GObject parent;
gboolean initialized;
GladeXML *xml;
GtkWidget *page;
char *title;
DBusGProxy *proxy;
gulong secrets_done_validate;
char *setting_name;
NMConnection *connection;
GtkWindow *parent_window;
gboolean disposed;
} CEPage;
......@@ -52,12 +65,17 @@ typedef struct {
GObjectClass parent;
/* Virtual functions */
gboolean (*validate) (CEPage *self, NMConnection *connection, GError **error);
gboolean (*validate) (CEPage *self, NMConnection *connection, GError **error);
/* Signals */
void (*changed) (CEPage *self);
void (*changed) (CEPage *self);
void (*initialized) (CEPage *self, GHashTable *secrets, GError *error);
} CEPageClass;
typedef CEPage* (*CEPageNewFunc)(NMConnection *connection, GtkWindow *parent, GError **error);
GType ce_page_get_type (void);
GtkWidget * ce_page_get_page (CEPage *self);
......@@ -76,5 +94,11 @@ gint ce_spin_output_with_default (GtkSpinButton *spin, gpointer user_data);
int ce_get_property_default (NMSetting *setting, const char *property_name);
gboolean ce_page_initialize (CEPage *self,
const char *setting_name,
GError **error);
gboolean ce_page_get_initialized (CEPage *self);
#endif /* __CE_PAGE_H__ */
......@@ -61,6 +61,7 @@
#include "nm-connection-editor.h"
#include "gconf-helpers.h"
#include "nma-marshal.h"
#include "ce-page.h"
#include "page-wired.h"
......@@ -72,6 +73,7 @@
#include "page-mobile.h"
#include "page-ppp.h"
#include "page-vpn.h"
#include "polkit-helpers.h"
G_DEFINE_TYPE (NMConnectionEditor, nm_connection_editor, G_TYPE_OBJECT)
......@@ -82,8 +84,9 @@ enum {
static guint editor_signals[EDITOR_LAST_SIGNAL] = { 0 };
static void nm_connection_editor_set_connection (NMConnectionEditor *editor,
NMConnection *connection);
static gboolean nm_connection_editor_set_connection (NMConnectionEditor *editor,
NMConnection *connection,
GError **error);
static void
nm_connection_editor_update_title (NMConnectionEditor *editor)
......@@ -396,28 +399,34 @@ nm_connection_editor_class_init (NMConnectionEditorClass *klass)
/* Signals */
editor_signals[EDITOR_DONE] =
g_signal_new ("done",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMConnectionEditorClass, done),
NULL, NULL,
g_cclosure_marshal_VOID__INT,
G_TYPE_NONE, 1, G_TYPE_INT);
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (NMConnectionEditorClass, done),
NULL, NULL,
nma_marshal_VOID__INT_POINTER,
G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER);
}
NMConnectionEditor *
nm_connection_editor_new (NMConnection *connection,
gboolean system_settings_can_modify)
gboolean system_settings_can_modify,
GError **error)
{
NMConnectionEditor *editor;
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
editor = g_object_new (NM_TYPE_CONNECTION_EDITOR, NULL);
if (!editor)
if (!editor) {
g_set_error (error, 0, 0, "%s", _("Error creating connection editor dialog."));
return NULL;
}
editor->system_settings_can_modify = system_settings_can_modify;
nm_connection_editor_set_connection (editor, connection);
if (!nm_connection_editor_set_connection (editor, connection, error)) {
g_object_unref (editor);
return NULL;
}
return editor;
}
......@@ -472,43 +481,92 @@ page_changed (CEPage *page, gpointer user_data)
connection_editor_validate (editor);
}
static gboolean
idle_validate (gpointer user_data)
{
connection_editor_validate (NM_CONNECTION_EDITOR (user_data));
return FALSE;
}
static void
recheck_initialization (NMConnectionEditor *editor)
{
GSList *iter;
/* Check if all pages are initialized; if not, desensitize the editor */
for (iter = editor->pages; iter; iter = g_slist_next (iter)) {
if (!ce_page_get_initialized (CE_PAGE (iter->data))) {
set_editor_sensitivity (editor, FALSE);
return;
}
}
populate_connection_ui (editor);
update_sensitivity (editor, POLKIT_RESULT_UNKNOWN);
/* Validate the connection from an idle handler to ensure that stuff like
* GtkFileChoosers have had a chance to asynchronously find their files.
*/
g_idle_add (idle_validate, editor);
}
static void
add_page (NMConnectionEditor *editor, CEPage *page)
page_initialized (CEPage *page, gpointer unused, GError *error, gpointer user_data)
{
NMConnectionEditor *editor = NM_CONNECTION_EDITOR (user_data);
if (error) {
gtk_widget_hide (editor->window);
g_signal_emit (editor, editor_signals[EDITOR_DONE], 0, GTK_RESPONSE_NONE, error);
return;
}
recheck_initialization (editor);
}
static gboolean
add_page (NMConnectionEditor *editor,
CEPageNewFunc func,
NMConnection *connection,
GError **error)
{
CEPage *page;
GtkWidget *widget;
GtkWidget *notebook;