[gnome-control-center/wip/add-account: 6/7] user-accounts: Initial code implementing joining domain
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center/wip/add-account: 6/7] user-accounts: Initial code implementing joining domain
- Date: Wed, 6 Jun 2012 12:10:01 +0000 (UTC)
commit f4c57932147ba66052a551352acd4b18e9426dc2
Author: Stef Walter <stefw gnome org>
Date: Fri Jun 1 16:36:43 2012 +0200
user-accounts: Initial code implementing joining domain
* Validate join domain correctly
* Add UmRealmManager for handling some stuff above the autogenerated
realmd dbus code
configure.ac | 17 +
panels/user-accounts/Makefile.am | 4 +
panels/user-accounts/data/account-dialog.ui | 220 +++++++
.../user-accounts/data/org.freedesktop.realmd.xml | 9 +-
panels/user-accounts/um-account-dialog.c | 525 ++++++++++++----
panels/user-accounts/um-realm-manager.c | 668 ++++++++++++++++++++
panels/user-accounts/um-realm-manager.h | 96 +++
7 files changed, 1405 insertions(+), 134 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 2bf29b9..4e0acf3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -224,6 +224,23 @@ PKG_CHECK_MODULES(ISOCODES, iso-codes)
AC_DEFINE_UNQUOTED([ISO_CODES_PREFIX],["`$PKG_CONFIG --variable=prefix iso-codes`"],[ISO codes prefix])
ISO_CODES=iso-codes
+# Kerberos kerberos support
+AC_PATH_PROG(KRB5_CONFIG, krb5-config)
+AC_MSG_CHECKING(for working krb5-config)
+if test -x "$KRB5_CONFIG"; then
+ KRB5_CFLAGS="`$KRB5_CONFIG --cflags`"
+ KRB5_LIBS="`$KRB5_CONFIG --libs`"
+ AC_MSG_RESULT(yes)
+elif test x$KRB5_PASSED_LIBS = x; then
+ AC_MSG_ERROR(no. Please install MIT kerberos devel package)
+fi
+
+AC_SUBST(KRB5_CFLAGS)
+AC_SUBST(KRB5_LIBS)
+
+USER_ACCOUNTS_PANEL_CFLAGS="$USER_ACCOUNTS_PANEL_CFLAGS $KRB5_CFLAGS"
+USER_ACCOUNTS_PANEL_LIBS="$USER_ACCOUNTS_PANEL_LIBS $KRB5_LIBS"
+
dnl ==============================================
dnl End: Check that we meet the dependencies
dnl ==============================================
diff --git a/panels/user-accounts/Makefile.am b/panels/user-accounts/Makefile.am
index d98b5fb..2d94d69 100644
--- a/panels/user-accounts/Makefile.am
+++ b/panels/user-accounts/Makefile.am
@@ -60,6 +60,8 @@ libuser_accounts_la_SOURCES = \
um-user-panel.h \
um-user-panel.c \
um-user-module.c \
+ um-realm-manager.c \
+ um-realm-manager.h \
$(BUILT_SOURCES)
libuser_accounts_la_LIBADD = \
@@ -85,6 +87,8 @@ frob_account_dialog_SOURCES = \
frob-account-dialog.c \
um-account-dialog.h \
um-account-dialog.c \
+ um-realm-manager.c \
+ um-realm-manager.h \
um-user.h \
um-user.c \
um-user-manager.h \
diff --git a/panels/user-accounts/data/account-dialog.ui b/panels/user-accounts/data/account-dialog.ui
index 78ab9eb..14557fa 100644
--- a/panels/user-accounts/data/account-dialog.ui
+++ b/panels/user-accounts/data/account-dialog.ui
@@ -392,4 +392,224 @@
</row>
</data>
</object>
+ <object class="GtkDialog" id="join-dialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">10</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">2</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="button1">
+ <property name="label">gtk-cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button2">
+ <property name="label" translatable="yes">C_ontinue</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_underline">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Domain Administrator Login</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.2"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label12">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ <property name="label" translatable="yes">In order to use enterpise logins, this computer needs to be
+enrolled in the domain. Please have your network administrator
+type their domain password here.</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="hexpand">True</property>
+ <property name="row_spacing">6</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">_Domain</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-domain</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="join-domain">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="margin_bottom">5</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label14">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Administrator _Name</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-name</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="join-name">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">â</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">1</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label15">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">1</property>
+ <property name="label" translatable="yes">Administrator Password</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">join-password</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="join-password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hexpand">True</property>
+ <property name="invisible_char">â</property>
+ <property name="invisible_char_set">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">2</property>
+ <property name="width">1</property>
+ <property name="height">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">button1</action-widget>
+ <action-widget response="-5">button2</action-widget>
+ </action-widgets>
+ </object>
</interface>
diff --git a/panels/user-accounts/data/org.freedesktop.realmd.xml b/panels/user-accounts/data/org.freedesktop.realmd.xml
index d929d2c..364609a 100644
--- a/panels/user-accounts/data/org.freedesktop.realmd.xml
+++ b/panels/user-accounts/data/org.freedesktop.realmd.xml
@@ -17,7 +17,7 @@
* Each realm is a DBus object and is represeted by a:
* s: DBus bus name of the realm
* o: DBus object path of the realm
- * s: DBus interface name, like 'ofr.KerberosRealm' (below)
+ * s: DBus interface name, like 'ofr.Kerberos' (below)
-->
<property name="Realms" type="a(sos)" access="read"/>
@@ -53,7 +53,7 @@
<!--
* This interface is implemented by Kerberos realms.
-->
- <interface name="org.freedesktop.realmd.KerberosRealm">
+ <interface name="org.freedesktop.realmd.Kerberos">
<!--
* The kerberos realm name. Usually capitalized.
@@ -66,6 +66,11 @@
<property name="Domain" type="s" access="read"/>
<!--
+ * The suggested Administrator login name for this realm
+ -->
+ <property name="SuggestedAdministrator" type="s" access="read"/>
+
+ <!--
* The format for user logins when this realm is enrolled.
* This property may not be valid unless machine is enrolled
* in this realm. The format contains a %s where the user name
diff --git a/panels/user-accounts/um-account-dialog.c b/panels/user-accounts/um-account-dialog.c
index 4907426..a6174e9 100644
--- a/panels/user-accounts/um-account-dialog.c
+++ b/panels/user-accounts/um-account-dialog.c
@@ -27,10 +27,18 @@
#include <gtk/gtk.h>
#include "um-account-dialog.h"
-#include "um-realm-generated.h"
+#include "um-realm-manager.h"
#include "um-user-manager.h"
#include "um-utils.h"
+static void on_join_login (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data);
+
+static void on_realm_joined (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data);
+
#define UM_ACCOUNT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_ACCOUNT_DIALOG, \
UmAccountDialogClass))
#define UM_IS_ACCOUNT_DIALOG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_ACCOUNT_DIALOG))
@@ -63,9 +71,18 @@ struct _UmAccountDialog {
GtkWidget *enterprise_button;
GtkListStore *enterprise_realms;
GtkComboBox *enterprise_domain;
- GtkWidget *enterprise_login;
- GtkWidget *enterprise_password;
- UmRealmProvider *realm_provider;
+ GtkEntry *enterprise_domain_entry;
+ gboolean enterprise_domain_chosen;
+ GtkEntry *enterprise_login;
+ GtkEntry *enterprise_password;
+ UmRealmManager *realm_manager;
+ UmRealmKerberos *selected_realm;
+
+ /* Join credential dialog */
+ GtkDialog *join_dialog;
+ GtkLabel *join_domain;
+ GtkEntry *join_name;
+ GtkEntry *join_password;
};
typedef struct {
@@ -81,18 +98,60 @@ get_account_mode (UmAccountDialog *self)
}
static void
-action_in_progress (UmAccountDialog *self,
- gboolean processing)
+show_error_dialog (UmAccountDialog *self,
+ GError *error)
{
- gtk_widget_set_sensitive (GTK_WIDGET (self->notebook), !processing);
- gtk_widget_set_sensitive (self->bar, !processing);
- gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, !processing);
+ GtkWidget *dialog;
- gtk_widget_set_visible (GTK_WIDGET (self->spinner), processing);
- if (processing)
- gtk_spinner_start (self->spinner);
- else
- gtk_spinner_stop (self->spinner);
+ dialog = gtk_message_dialog_new (GTK_WINDOW (self),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ _("Failed to join domain"));
+
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
+ "%s", error->message);
+
+ g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), NULL);
+ gtk_window_present (GTK_WINDOW (dialog));
+}
+
+static void
+begin_action (UmAccountDialog *self)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (self->notebook), FALSE);
+ gtk_widget_set_sensitive (self->bar, FALSE);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, FALSE);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->spinner), TRUE);
+ gtk_spinner_start (self->spinner);
+}
+
+static void
+finish_action (UmAccountDialog *self)
+{
+ gtk_widget_set_sensitive (GTK_WIDGET (self->notebook), TRUE);
+ gtk_widget_set_sensitive (self->bar, TRUE);
+ gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, TRUE);
+
+ gtk_widget_set_visible (GTK_WIDGET (self->spinner), FALSE);
+ gtk_spinner_stop (self->spinner);
+}
+
+static void
+complete_dialog (UmAccountDialog *self,
+ UmUser *user)
+{
+ g_cancellable_cancel (self->cancellable);
+
+ if (user != NULL) {
+ g_simple_async_result_set_op_res_gpointer (self->async,
+ g_object_ref (user),
+ g_object_unref);
+ }
+
+ g_simple_async_result_complete_in_idle (self->async);
+ gtk_widget_hide (GTK_WIDGET (self));
}
static gboolean
@@ -127,11 +186,18 @@ enterprise_validate (UmAccountDialog *self)
const gchar *name;
gboolean valid_name;
gboolean valid_domain;
+ GtkTreeIter iter;
name = gtk_entry_get_text (GTK_ENTRY (self->enterprise_login));
valid_name = is_valid_name (name);
- name = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (self->enterprise_domain));
+ if (gtk_combo_box_get_active_iter (self->enterprise_domain, &iter)) {
+ gtk_tree_model_get (gtk_combo_box_get_model (self->enterprise_domain),
+ &iter, 0, &name, -1);
+ } else {
+ name = gtk_entry_get_text (self->enterprise_domain_entry);
+ }
+
valid_domain = is_valid_name (name);
/* TODO: More validation here, and also checking of the domain */
@@ -307,14 +373,6 @@ on_name_changed (GtkEditable *editable,
}
static void
-on_text_changed (GObject *object,
- GParamSpec *spec,
- gpointer user_data)
-{
- dialog_validate (UM_ACCOUNT_DIALOG (user_data));
-}
-
-static void
local_construct (UmAccountDialog *self,
GtkBuilder *builder)
{
@@ -353,34 +411,16 @@ on_create_local_user (GObject *object,
GError *error = NULL;
UmUser *user;
- action_in_progress (self, FALSE);
+ finish_action (self);
if (!um_user_manager_create_user_finish (UM_USER_MANAGER (object), res, &user, &error)) {
- if (!g_error_matches (error, UM_USER_MANAGER_ERROR, UM_USER_MANAGER_ERROR_PERMISSION_DENIED)) {
- GtkWidget *dialog;
-
- dialog = gtk_message_dialog_new (GTK_WINDOW (self),
- GTK_DIALOG_DESTROY_WITH_PARENT,
- GTK_MESSAGE_ERROR,
- GTK_BUTTONS_CLOSE,
- _("Failed to create user"));
-
- gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
- "%s", error->message);
-
- g_signal_connect (G_OBJECT (dialog), "response",
- G_CALLBACK (gtk_widget_destroy), NULL);
- gtk_window_present (GTK_WINDOW (dialog));
- }
+ if (!g_error_matches (error, UM_USER_MANAGER_ERROR, UM_USER_MANAGER_ERROR_PERMISSION_DENIED))
+ show_error_dialog (self, error);
g_error_free (error);
gtk_widget_grab_focus (self->local_name);
} else {
- g_simple_async_result_set_op_res_gpointer (self->async,
- g_object_ref (user),
- g_object_unref);
- g_simple_async_result_complete_in_idle (self->async);
- gtk_widget_hide (GTK_WIDGET (self));
+ complete_dialog (self, user);
}
}
@@ -394,13 +434,14 @@ local_add (UmAccountDialog *self)
GtkTreeModel *model;
GtkTreeIter iter;
+ begin_action (self);
+
name = gtk_entry_get_text (GTK_ENTRY (self->local_name));
username = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (self->local_login));
model = gtk_combo_box_get_model (GTK_COMBO_BOX (self->local_account_type));
gtk_combo_box_get_active_iter (GTK_COMBO_BOX (self->local_account_type), &iter);
gtk_tree_model_get (model, &iter, 1, &account_type, -1);
- action_in_progress (self, TRUE);
manager = um_user_manager_ref_default ();
um_user_manager_create_user (manager,
username,
@@ -412,133 +453,315 @@ local_add (UmAccountDialog *self)
g_object_unref (manager);
}
-static gboolean
-on_create_enterprise (gpointer data)
+static void
+enterprise_add_realm (UmAccountDialog *self,
+ UmRealmKerberos *realm)
+{
+ GtkTreeIter iter;
+
+ gtk_list_store_append (self->enterprise_realms, &iter);
+ gtk_list_store_set (self->enterprise_realms, &iter,
+ 0, um_realm_kerberos_get_domain (realm),
+ 1, realm,
+ -1);
+
+ /* Select the domain if appropriate */
+ if (!self->enterprise_domain_chosen && um_realm_kerberos_get_enrolled (realm))
+ gtk_combo_box_set_active_iter (self->enterprise_domain, &iter);
+
+}
+static void
+on_manager_realm_added (UmRealmManager *manager,
+ UmRealmKerberos *realm,
+ gpointer user_data)
{
- UmAccountDialog *self = UM_ACCOUNT_DIALOG (data);
- action_in_progress (self, FALSE);
- return FALSE;
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ enterprise_add_realm (self, realm);
}
static void
-enterprise_add (UmAccountDialog *self)
+enterprise_register_user (UmAccountDialog *self)
{
- /* TODO: Implement adding of users */
- action_in_progress (self, TRUE);
+ UmUser *user = NULL;
- g_timeout_add_seconds_full (G_PRIORITY_DEFAULT, 3, on_create_enterprise,
- g_object_ref (self), g_object_unref);
+ /* TODO: And here is where we register/create the user */
+
+ finish_action (self);
+ complete_dialog (self, user);
}
static void
-on_realm_proxy_created (GObject *source,
- GAsyncResult *result,
- gpointer user_data)
+on_join_response (GtkDialog *dialog,
+ gint response,
+ gpointer user_data)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+ gtk_widget_hide (GTK_WIDGET (dialog));
+ if (response != GTK_RESPONSE_OK) {
+ finish_action (self);
+ return;
+ }
+
+ /* Prompted for some admin credentials, try to use them to log in */
+ um_realm_login (um_realm_kerberos_get_name (self->selected_realm),
+ um_realm_kerberos_get_domain (self->selected_realm),
+ gtk_entry_get_text (self->join_name),
+ gtk_entry_get_text (self->join_password),
+ UM_REALM_LOGIN_TO_CREDENTIALS,
+ self->cancellable,
+ on_join_login,
+ g_object_ref (self));
+}
+
+static void
+join_show_prompt (UmAccountDialog *self,
+ GError *error)
+{
+ const gchar *name;
+
+ gtk_entry_set_text (self->join_password, "");
+ gtk_widget_grab_focus (GTK_WIDGET (self->join_password));
+
+ gtk_label_set_text (self->join_domain,
+ um_realm_kerberos_get_domain (self->selected_realm));
+
+ clear_entry_validation_error (self->join_name);
+ clear_entry_validation_error (self->join_password);
+
+ if (error == NULL) {
+ name = um_realm_kerberos_get_suggested_administrator (self->selected_realm);
+ if (name && !g_str_equal (name, "")) {
+ gtk_entry_set_text (self->join_name, name);
+ } else {
+ gtk_widget_grab_focus (GTK_WIDGET (self->join_name));
+ }
+
+ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
+ set_entry_validation_error (self->join_password, error->message);
+
+ } else {
+ set_entry_validation_error (self->join_name, error->message);
+ }
+
+ gtk_window_set_transient_for (GTK_WINDOW (self->join_dialog), GTK_WINDOW (self));
+ gtk_window_set_modal (GTK_WINDOW (self->join_dialog), TRUE);
+ gtk_window_present (GTK_WINDOW (self->join_dialog));
+
+ /* And now we wait for on_join_response() */
+}
+
+static void
+on_join_login (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
- UmRealmKerberosRealm *realm;
GError *error = NULL;
- GtkTreeIter iter;
+ GBytes *creds;
- realm = um_realm_kerberos_realm_proxy_new_finish (result, &error);
+ um_realm_login_finish (result, &creds, &error);
- if (error != NULL) {
- g_warning ("Couldn't create realm proxy: %s", error->message);
+ /* Logged in as admin successfully, use creds to join domain */
+ if (error == NULL) {
+ um_realm_join (self->selected_realm,
+ creds, self->cancellable, on_realm_joined,
+ g_object_ref (self));
+ g_bytes_unref (creds);
+
+ /* Couldn't login as admin, show prompt again */
+ } else {
+ join_show_prompt (self, error);
g_error_free (error);
- return;
}
- gtk_list_store_append (self->enterprise_realms, &iter);
- gtk_list_store_set (self->enterprise_realms, &iter,
- 0, um_realm_kerberos_realm_get_domain (realm),
- 1, realm,
- -1);
+ g_object_unref (self);
+}
- if (um_realm_kerberos_realm_get_enrolled (realm))
- gtk_combo_box_set_active_iter (self->enterprise_domain, &iter);
+static void
+join_construct (UmAccountDialog *self,
+ GtkBuilder *builder)
+{
+ self->join_dialog = GTK_DIALOG (gtk_builder_get_object (builder, "join-dialog"));
+ self->join_domain = GTK_LABEL (gtk_builder_get_object (builder, "join-domain"));
+ self->join_name = GTK_ENTRY (gtk_builder_get_object (builder, "join-name"));
+ self->join_password = GTK_ENTRY (gtk_builder_get_object (builder, "join-password"));
- g_object_unref (realm);
+ g_signal_connect (self->join_dialog, "response",
+ G_CALLBACK (on_join_response), self);
}
static void
-enterprise_populate_realms (UmAccountDialog *self,
- GDBusConnection *connection,
- GVariant *realms)
+on_realm_joined (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
- GVariantIter iter;
- const gchar *path;
- const gchar *name;
- const gchar *iface;
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ GError *error = NULL;
- if (realms == NULL)
- return;
+ um_realm_join_finish (self->selected_realm,
+ result, &error);
+
+ /* Yay, joined the domain, register the user locally */
+ if (error == NULL) {
+ enterprise_register_user (self);
- /* We only support kerberos realms */
+ /* Credential failure while joining domain, prompt for admin creds */
+ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN) ||
+ g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
+ join_show_prompt (self, NULL);
- g_variant_iter_init (&iter, realms);
- while (g_variant_iter_loop (&iter, "(&s&o&s)", &name, &path, &iface)) {
- if (g_str_equal (iface, "org.freedesktop.realmd.KerberosRealm")) {
- um_realm_kerberos_realm_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE,
- name, path, self->cancellable,
- on_realm_proxy_created, self);
+ /* Other failure */
+ } else {
+ show_error_dialog (self, error);
+ finish_action (self);
+ }
+
+ g_object_unref (self);
+}
+
+static void
+on_realm_login (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ GError *error = NULL;
+ GBytes *creds;
+
+ um_realm_login_finish (result, &creds, &error);
+ if (error == NULL) {
+
+ /* Already joined to the domain, just register this user */
+ if (um_realm_kerberos_get_enrolled (self->selected_realm)) {
+ enterprise_register_user (self);
+
+ /* Join the domain, try using the user's creds */
+ } else {
+ um_realm_join (self->selected_realm,
+ creds,
+ self->cancellable,
+ on_realm_joined,
+ g_object_ref (self));
}
+
+ g_bytes_unref (creds);
+
+ /* A problem with the user's login name or password */
+ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN)) {
+ set_entry_validation_error (self->enterprise_login, error->message);
+ } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) {
+ set_entry_validation_error (self->enterprise_password, error->message);
+
+ /* Other login failure */
+ } else {
+ show_error_dialog (self, error);
+ finish_action (self);
}
+
+ g_clear_error (&error);
+ g_object_unref (self);
+}
+
+static void
+enterprise_check_login (UmAccountDialog *self)
+{
+ g_assert (self->selected_realm);
+
+ um_realm_login (um_realm_kerberos_get_name (self->selected_realm),
+ um_realm_kerberos_get_domain (self->selected_realm),
+ gtk_entry_get_text (self->enterprise_login),
+ gtk_entry_get_text (self->enterprise_password),
+ UM_REALM_LOGIN_TO_CREDENTIALS,
+ self->cancellable,
+ on_realm_login,
+ g_object_ref (self));
}
static void
-on_provider_discover_default (GObject *source,
- GAsyncResult *result,
- gpointer user_data)
+on_realm_discover_input (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
{
UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
- GDBusConnection *connection;
GError *error = NULL;
- GVariant *realms;
- gint relevance;
+ GList *realms;
- um_realm_provider_call_discover_finish (self->realm_provider, &relevance,
- &realms, result, &error);
+ realms = um_realm_manager_discover_finish (self->realm_manager,
+ result, &error);
+
+ /* Found a realm, log user into domain */
if (error == NULL) {
- connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->realm_provider));
- enterprise_populate_realms (self, connection, realms);
- g_variant_unref (realms);
+ g_assert (realms != NULL);
+ self->selected_realm = g_object_ref (realms->data);
+ enterprise_check_login (self);
+ g_list_free_full (realms, g_object_unref);
+
+ /* The domain is likely invalid*/
} else {
- g_warning ("Couldn't discover default realm: %s", error->message);
+ finish_action (self);
+ gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_domain_entry));
+ set_entry_validation_error (self->enterprise_domain_entry,
+ error->message);
g_error_free (error);
}
+
+ g_object_unref (self);
+}
+
+static void
+enterprise_add (UmAccountDialog *self)
+{
+ GtkTreeIter iter;
+
+ begin_action (self);
+ g_clear_object (&self->selected_realm);
+
+ /* Already know about this realm, try to login as user */
+ if (gtk_combo_box_get_active_iter (self->enterprise_domain, &iter)) {
+ gtk_tree_model_get (gtk_combo_box_get_model (self->enterprise_domain),
+ &iter, 1, &self->selected_realm, -1);
+ enterprise_check_login (self);
+
+ /* Something the user typed, we need to discover realm */
+ } else {
+ um_realm_manager_discover (self->realm_manager,
+ gtk_entry_get_text (self->enterprise_domain_entry),
+ self->cancellable,
+ on_realm_discover_input,
+ g_object_ref (self));
+ }
}
static void
-on_provider_proxy_created (GObject *source,
+on_realm_manager_created (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
- GDBusConnection *connection;
GError *error = NULL;
- GVariant *realms;
+ GList *realms, *l;
- g_clear_object (&self->realm_provider);
+ g_clear_object (&self->realm_manager);
- self->realm_provider = um_realm_provider_proxy_new_for_bus_finish (result, &error);
+ self->realm_manager = um_realm_manager_new_finish (result, &error);
if (error != NULL) {
- if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN))
- g_warning ("Couldn't contact realmd service: %s", error->message);
+ g_warning ("Couldn't contact realmd service: %s", error->message);
g_error_free (error);
- }
-
- if (!self->realm_provider)
return;
-
- connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (self->realm_provider));
+ }
/* Lookup all the realm objects */
- realms = um_realm_provider_get_realms (self->realm_provider);
- enterprise_populate_realms (self, connection, realms);
+ realms = um_realm_manager_get_realms (self->realm_manager);
+ for (l = realms; l != NULL; l = g_list_next (l))
+ enterprise_add_realm (self, l->data);
+ g_list_free (realms);
+ g_signal_connect (self->realm_manager, "realm-added",
+ G_CALLBACK (on_manager_realm_added), self);
- /* When no realms try to discover a sensible default */
- um_realm_provider_call_discover (self->realm_provider, "", self->cancellable,
- on_provider_discover_default, self);
+ /* When no realms try to discover a sensible default, triggers realm-added signal */
+ um_realm_manager_discover (self->realm_manager, "", self->cancellable,
+ NULL, NULL);
/* Show the 'Enterprise Login' stuff, and update bar */
gtk_widget_show (self->enterprise_button);
@@ -554,22 +777,50 @@ on_realmd_appeared (GDBusConnection *connection,
UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
um_realm_provider_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE,
name, "/org/freedesktop/realmd",
- self->cancellable, on_provider_proxy_created, self);
+ self->cancellable, on_realm_manager_created, self);
}
static void
-on_realmd_disappeared (GDBusConnection *connection,
- const gchar *name,
+on_realmd_disappeared (GDBusConnection *unused1,
+ const gchar *unused2,
gpointer user_data)
{
UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+ if (self->realm_manager) {
+ g_signal_handlers_disconnect_by_func (self->realm_manager,
+ on_manager_realm_added,
+ self);
+ g_object_unref (self->realm_manager);
+ self->realm_manager = NULL;
+ }
+
gtk_list_store_clear (self->enterprise_realms);
- g_clear_object (&self->realm_provider);
gtk_widget_hide (self->enterprise_button);
bar_update (self, NULL);
}
static void
+on_domain_changed (GtkComboBox *widget,
+ gpointer user_data)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+
+ dialog_validate (self);
+ self->enterprise_domain_chosen = TRUE;
+ clear_entry_validation_error (self->enterprise_domain_entry);
+}
+
+static void
+on_entry_changed (GtkEditable *editable,
+ gpointer user_data)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data);
+ dialog_validate (self);
+ clear_entry_validation_error (GTK_ENTRY (editable));
+}
+
+static void
enterprise_construct (UmAccountDialog *self,
GtkBuilder *builder)
{
@@ -578,18 +829,19 @@ enterprise_construct (UmAccountDialog *self,
self->enterprise_realms = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_OBJECT);
widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-domain");
- g_signal_connect (widget, "notify::text", G_CALLBACK (on_text_changed), self);
+ g_signal_connect (widget, "changed", G_CALLBACK (on_domain_changed), self);
self->enterprise_domain = GTK_COMBO_BOX (widget);
gtk_combo_box_set_model (self->enterprise_domain,
GTK_TREE_MODEL (self->enterprise_realms));
gtk_combo_box_set_entry_text_column (self->enterprise_domain, 0);
widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-login");
- g_signal_connect (widget, "notify::text", G_CALLBACK (on_text_changed), self);
- self->enterprise_login = widget;
+ g_signal_connect (widget, "changed", G_CALLBACK (on_entry_changed), self);
+ self->enterprise_login = GTK_ENTRY (widget);
widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-password");
- self->enterprise_password = widget;
+ g_signal_connect (widget, "changed", G_CALLBACK (on_entry_changed), self);
+ self->enterprise_password = GTK_ENTRY (widget);
/* Initially we hide the 'Enterprise Login' stuff */
widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-button");
@@ -676,6 +928,8 @@ um_account_dialog_constructed (GObject *obj)
enterprise_construct (self, builder);
bar_construct (self, builder);
+ join_construct (self, builder);
+
g_object_unref (builder);
}
@@ -701,10 +955,7 @@ um_account_dialog_response (GtkDialog *dialog,
break;
case GTK_RESPONSE_CANCEL:
case GTK_RESPONSE_DELETE_EVENT:
- g_cancellable_cancel (self->cancellable);
- g_simple_async_result_set_op_res_gpointer (self->async, NULL, NULL);
- g_simple_async_result_complete_in_idle (self->async);
- gtk_widget_hide (GTK_WIDGET (self));
+ complete_dialog (self, NULL);
break;
default:
g_warn_if_reached ();
@@ -713,14 +964,23 @@ um_account_dialog_response (GtkDialog *dialog,
}
static void
-um_account_dialog_finalize (GObject *obj)
+um_account_dialog_dispose (GObject *obj)
{
UmAccountDialog *self = UM_ACCOUNT_DIALOG (obj);
g_cancellable_cancel (self->cancellable);
+ on_realmd_disappeared (NULL, NULL, self);
+
+ G_OBJECT_CLASS (um_account_dialog_parent_class)->dispose (obj);
+}
+
+static void
+um_account_dialog_finalize (GObject *obj)
+{
+ UmAccountDialog *self = UM_ACCOUNT_DIALOG (obj);
+
g_object_unref (self->cancellable);
g_object_unref (self->enterprise_realms);
- g_clear_object (&self->realm_provider);
if (self->realmd_watch)
g_bus_unwatch_name (self->realmd_watch);
@@ -735,6 +995,7 @@ um_account_dialog_class_init (UmAccountDialogClass *klass)
GtkDialogClass *dialog_class = GTK_DIALOG_CLASS (klass);
object_class->constructed = um_account_dialog_constructed;
+ object_class->dispose = um_account_dialog_dispose;
object_class->finalize = um_account_dialog_finalize;
dialog_class->response = um_account_dialog_response;
diff --git a/panels/user-accounts/um-realm-manager.c b/panels/user-accounts/um-realm-manager.c
new file mode 100644
index 0000000..9f6f949
--- /dev/null
+++ b/panels/user-accounts/um-realm-manager.c
@@ -0,0 +1,668 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009-2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ * Stef Walter <stefw gnome org>
+ */
+
+#include "config.h"
+
+#include "um-realm-manager.h"
+
+#include <krb5/krb5.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+
+
+struct _UmRealmManager {
+ UmRealmProviderProxy parent;
+ GHashTable *realms;
+};
+
+typedef struct {
+ UmRealmProviderProxyClass parent_class;
+} UmRealmManagerClass;
+
+enum {
+ REALM_ADDED,
+ NUM_SIGNALS,
+};
+
+static gint signals[NUM_SIGNALS] = { 0, };
+
+G_DEFINE_TYPE (UmRealmManager, um_realm_manager, UM_REALM_TYPE_PROVIDER_PROXY);
+
+GQuark
+um_realm_error_get_quark (void)
+{
+ static GQuark quark = 0;
+ if (quark == 0)
+ quark = g_quark_from_static_string ("um-realm-error");
+ return quark;
+}
+
+typedef struct {
+ UmRealmManager *manager;
+ GList *realms;
+ gint outstanding;
+} LoadClosure;
+
+static void
+load_closure_free (gpointer data)
+{
+ LoadClosure *load = data;
+ g_list_free_full (load->realms, g_object_unref);
+ g_object_unref (load->manager);
+ g_slice_free (LoadClosure, load);
+}
+
+static void
+on_realm_proxy_created (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+ LoadClosure *load = g_simple_async_result_get_op_res_gpointer (async);
+ UmRealmManager *self = load->manager;
+ UmRealmKerberos *realm;
+ UmRealmKerberos *have;
+ GError *error = NULL;
+ GDBusProxy *proxy;
+ GVariant *info;
+
+ realm = um_realm_kerberos_proxy_new_finish (result, &error);
+ if (error == NULL) {
+ proxy = G_DBUS_PROXY (realm);
+ info = g_variant_new ("(sos)",
+ g_dbus_proxy_get_name (proxy),
+ g_dbus_proxy_get_object_path (proxy),
+ g_dbus_proxy_get_interface_name (proxy));
+
+ /* Add it to the manager, unless race */
+ have = g_hash_table_lookup (self->realms, info);
+ if (have == NULL) {
+ g_hash_table_insert (self->realms,
+ g_variant_ref_sink (info), realm);
+ g_signal_emit (self, signals[REALM_ADDED], 0, realm);
+
+ } else {
+ g_object_unref (realm);
+ g_variant_unref (info);
+ realm = have;
+ }
+
+ load->realms = g_list_prepend (load->realms, g_object_ref (realm));
+
+ } else {
+ g_simple_async_result_take_error (async, error);
+ }
+
+ if (load->outstanding-- == 1)
+ g_simple_async_result_complete (async);
+
+ g_object_unref (async);
+}
+
+static void
+um_realm_manager_load (UmRealmManager *self,
+ GVariant *realms,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async;
+ GDBusConnection *connection;
+ LoadClosure *load;
+ GVariantIter iter;
+ GVariant *info;
+ UmRealmKerberos *realm;
+ const gchar *path;
+ const gchar *iface;
+ const gchar *name;
+
+ async = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+ um_realm_manager_load);
+ load = g_slice_new (LoadClosure);
+ load->manager = g_object_ref (self);
+ g_simple_async_result_set_op_res_gpointer (async, load, load_closure_free);
+
+ connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (self));
+
+ g_variant_iter_init (&iter, realms);
+ for (;;) {
+ info = g_variant_iter_next_value (&iter);
+ if (info == NULL)
+ break;
+ realm = g_hash_table_lookup (self->realms, info);
+ if (realm == NULL) {
+ g_variant_get (info, "(&s&o&s)", &name, &path, &iface);
+ if (g_str_equal (iface, "org.freedesktop.realmd.Kerberos")) {
+ um_realm_kerberos_proxy_new (connection,
+ G_DBUS_PROXY_FLAGS_NONE,
+ name, path, cancellable,
+ on_realm_proxy_created,
+ g_object_ref (async));
+ load->outstanding++;
+ }
+ } else {
+ load->realms = g_list_prepend (load->realms, g_object_ref (realm));
+ }
+ g_variant_unref (info);
+ }
+
+ if (load->outstanding == 0)
+ g_simple_async_result_complete_in_idle (async);
+}
+
+static GList *
+um_realm_manager_load_finish (UmRealmManager *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (result);
+ LoadClosure *load;
+ GList *realms;
+
+ if (g_simple_async_result_propagate_error (async, error))
+ return NULL;
+
+ load = g_simple_async_result_get_op_res_gpointer (async);
+ realms = g_list_reverse (load->realms);
+ load->realms = NULL;
+ return realms;
+}
+
+static void
+um_realm_manager_init (UmRealmManager *self)
+{
+ self->realms = g_hash_table_new_full (g_variant_hash, g_variant_equal,
+ (GDestroyNotify)g_variant_unref,
+ g_object_unref);
+}
+
+static void
+um_realm_manager_notify (GObject *obj,
+ GParamSpec *spec)
+{
+ UmRealmManager *self = UM_REALM_MANAGER (obj);
+ GVariant *realms;
+
+ G_OBJECT_CLASS (um_realm_manager_parent_class)->notify (obj, spec);
+
+ if (g_str_equal (spec->name, "realms")) {
+ realms = um_realm_provider_get_realms (UM_REALM_PROVIDER (self));
+ um_realm_manager_load (self, realms, NULL, NULL, NULL);
+ }
+}
+
+static void
+um_realm_manager_finalize (GObject *obj)
+{
+ UmRealmManager *self = UM_REALM_MANAGER (obj);
+
+ g_hash_table_destroy (self->realms);
+
+ G_OBJECT_CLASS (um_realm_manager_parent_class)->finalize (obj);
+}
+
+static void
+um_realm_manager_class_init (UmRealmManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->notify = um_realm_manager_notify;
+ object_class->finalize = um_realm_manager_finalize;
+
+ signals[REALM_ADDED] = g_signal_new ("realm-added", UM_TYPE_REALM_MANAGER,
+ G_SIGNAL_RUN_FIRST, 0, NULL, NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_NONE, 1, UM_REALM_TYPE_KERBEROS);
+}
+
+void
+um_realm_manager_new (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_async_initable_new_async (UM_TYPE_REALM_MANAGER,
+ G_PRIORITY_DEFAULT,
+ cancellable, callback, user_data,
+ "g-name", "org.freedesktop.realmd",
+ "g-bus-type", G_BUS_TYPE_SYSTEM,
+ "g-object-path", "/org/freedesktop/realmd",
+ "g-interface-name", "org.freedesktop.realmd.Provider",
+ NULL);
+}
+
+UmRealmManager *
+um_realm_manager_new_finish (GAsyncResult *result,
+ GError **error)
+{
+ GObject *self;
+ GObject *source_object;
+
+ source_object = g_async_result_get_source_object (result);
+ self = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, error);
+ g_object_unref (source_object);
+
+ return UM_REALM_MANAGER (self);
+}
+
+typedef struct {
+ GCancellable *cancellable;
+ GList *realms;
+} DiscoverClosure;
+
+static void
+discover_closure_free (gpointer data)
+{
+ DiscoverClosure *discover = data;
+ g_clear_object (&discover->cancellable);
+ g_list_free_full (discover->realms, g_object_unref);
+ g_slice_free (DiscoverClosure, discover);
+}
+
+static void
+on_manager_load (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+ DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
+ GError *error = NULL;
+
+ discover->realms = um_realm_manager_load_finish (UM_REALM_MANAGER (source),
+ result, &error);
+ if (error != NULL)
+ g_simple_async_result_take_error (async, error);
+ g_simple_async_result_complete (async);
+
+ g_object_unref (async);
+}
+
+static void
+on_provider_discover (GObject *source,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data);
+ DiscoverClosure *discover = g_simple_async_result_get_op_res_gpointer (async);
+ UmRealmManager *self = UM_REALM_MANAGER (source);
+ GError *error = NULL;
+ GVariant *realms;
+ gint relevance;
+
+ um_realm_provider_call_discover_finish (UM_REALM_PROVIDER (self),
+ &relevance, &realms, result, &error);
+ if (error == NULL) {
+ um_realm_manager_load (self, realms, discover->cancellable,
+ on_manager_load, g_object_ref (async));
+
+ } else {
+ g_simple_async_result_take_error (async, error);
+ g_simple_async_result_complete (async);
+ }
+
+ g_object_unref (async);
+}
+
+void
+um_realm_manager_discover (UmRealmManager *self,
+ const gchar *input,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *res;
+ DiscoverClosure *discover;
+
+ g_return_if_fail (UM_IS_REALM_MANAGER (self));
+ g_return_if_fail (input != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ res = g_simple_async_result_new (G_OBJECT (self), callback, user_data,
+ um_realm_manager_discover);
+ discover = g_slice_new0 (DiscoverClosure);
+ discover->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+ g_simple_async_result_set_op_res_gpointer (res, discover, discover_closure_free);
+
+ um_realm_provider_call_discover (UM_REALM_PROVIDER (self), input, cancellable,
+ on_provider_discover, g_object_ref (res));
+
+ g_object_unref (res);
+}
+
+GList *
+um_realm_manager_discover_finish (UmRealmManager *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GSimpleAsyncResult *async;
+ DiscoverClosure *discover;
+ GList *realms;
+
+ g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self),
+ um_realm_manager_discover), NULL);
+ g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+ async = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (async, error))
+ return NULL;
+
+ discover = g_simple_async_result_get_op_res_gpointer (async);
+ if (!discover->realms) {
+ g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_FAIL,
+ _("No such domain or realm found"));
+ return NULL;
+ }
+
+ realms = discover->realms;
+ discover->realms = NULL;
+ return realms;
+}
+
+GList *
+um_realm_manager_get_realms (UmRealmManager *self)
+{
+ g_return_val_if_fail (UM_IS_REALM_MANAGER (self), NULL);
+ return g_hash_table_get_values (self->realms);
+}
+
+void
+um_realm_join (UmRealmKerberos *realm,
+ GBytes *credentials,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GVariant *options;
+ GVariant *creds;
+
+ g_return_if_fail (UM_REALM_IS_KERBEROS (realm));
+ g_return_if_fail (credentials != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ creds = g_variant_new_from_data (G_VARIANT_TYPE ("ay"),
+ g_bytes_get_data (credentials, NULL),
+ g_bytes_get_size (credentials),
+ TRUE, (GDestroyNotify)g_bytes_unref, credentials);
+ options = g_variant_new_array (G_VARIANT_TYPE ("{sv}"), NULL, 0);
+
+ um_realm_kerberos_call_enroll_with_credential_cache (realm,
+ g_variant_ref_sink (creds),
+ g_variant_ref_sink (options),
+ cancellable,
+ callback,
+ user_data);
+
+ g_variant_unref (options);
+ g_variant_unref (creds);
+}
+
+gboolean
+um_realm_join_finish (UmRealmKerberos *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GError *call_error = NULL;
+ gchar *dbus_error;
+
+ g_return_val_if_fail (UM_REALM_IS_KERBEROS (self), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ um_realm_kerberos_call_enroll_with_credential_cache_finish (self,
+ result,
+ &call_error);
+ if (call_error == NULL)
+ return TRUE;
+
+ dbus_error = g_dbus_error_get_remote_error (call_error);
+ if (dbus_error == NULL) {
+ g_propagate_error (error, call_error);
+ return FALSE;
+ }
+
+ g_dbus_error_strip_remote_error (call_error);
+
+ if (g_str_equal (dbus_error, "org.freedesktop.realmd.Error.AuthFailed")) {
+ g_set_error (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
+ call_error->message);
+ g_error_free (call_error);
+ } else {
+ g_propagate_error (error, call_error);
+ }
+
+ g_free (dbus_error);
+ return FALSE;
+}
+
+typedef struct {
+ gchar *domain;
+ gchar *realm;
+ gchar *user;
+ gchar *password;
+ gboolean unique;
+ GBytes *credentials;
+} LoginClosure;
+
+static void
+login_closure_free (gpointer data)
+{
+ LoginClosure *login = data;
+ g_free (login->domain);
+ g_free (login->realm);
+ g_free (login->user);
+ g_free (login->password);
+ g_bytes_unref (login->credentials);
+ g_slice_free (LoginClosure, login);
+}
+
+static krb5_error_code
+login_perform_kinit (krb5_context k5,
+ const gchar *realm,
+ const gchar *login,
+ const gchar *password,
+ const gchar *filename)
+{
+ krb5_get_init_creds_opt *opts;
+ krb5_error_code code;
+ krb5_principal principal;
+ krb5_ccache ccache;
+ krb5_creds creds;
+ gchar *name;
+
+ name = g_strdup_printf ("%s %s", login, realm);
+ code = krb5_parse_name (k5, name, &principal);
+ g_free (name);
+
+ if (code != 0)
+ return code;
+
+ if (filename == NULL)
+ code = krb5_cc_default (k5, &ccache);
+ else
+ code = krb5_cc_resolve (k5, filename, &ccache);
+
+ if (code != 0) {
+ krb5_free_principal (k5, principal);
+ return code;
+ }
+
+ code = krb5_get_init_creds_opt_alloc (k5, &opts);
+ g_return_val_if_fail (code == 0, code);
+
+ code = krb5_get_init_creds_opt_set_out_ccache (k5, opts, ccache);
+ g_return_val_if_fail (code == 0, code);
+
+ code = krb5_get_init_creds_password (k5, &creds, principal,
+ (char *)password,
+ NULL, 0, 0, NULL, opts);
+
+ krb5_get_init_creds_opt_free (k5, opts);
+ krb5_cc_close (k5, ccache);
+ krb5_free_principal (k5, principal);
+
+ if (code == 0)
+ krb5_free_cred_contents (k5, &creds);
+
+ return code;
+}
+
+static void
+kinit_thread_func (GSimpleAsyncResult *async,
+ GObject *object,
+ GCancellable *cancellable)
+{
+ LoginClosure *login = g_simple_async_result_get_op_res_gpointer (async);
+ krb5_context k5 = NULL;
+ krb5_error_code code;
+ GError *error = NULL;
+ gchar *filename = NULL;
+ gchar *contents;
+ gsize length;
+ gint temp_fd;
+
+ if (login->unique) {
+ filename = g_build_filename (g_get_user_runtime_dir (),
+ "um-krb5-creds.XXXXXX", NULL);
+ temp_fd = g_mkstemp_full (filename, O_RDWR, S_IRUSR | S_IWUSR);
+ if (temp_fd == -1) {
+ g_warning ("Couldn't create credential cache file: %s: %s",
+ filename, g_strerror (errno));
+ g_free (filename);
+ filename = NULL;
+ } else {
+ close (temp_fd);
+ }
+ }
+
+ code = krb5_init_context (&k5);
+ if (code == 0) {
+ code = login_perform_kinit (k5, login->realm, login->user,
+ login->password, filename);
+ }
+
+ switch (code) {
+ case 0:
+ if (filename != NULL) {
+ g_file_get_contents (filename, &contents, &length, &error);
+ if (error == NULL) {
+ login->credentials = g_bytes_new_take (contents, length);
+ } else {
+ g_warning ("Couldn't read credential cache: %s", error->message);
+ g_error_free (error);
+ }
+ }
+ break;
+
+ case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+ case KRB5KDC_ERR_CLIENT_REVOKED:
+ case KRB5KDC_ERR_KEY_EXP:
+ case KRB5KDC_ERR_POLICY:
+ case KRB5KDC_ERR_ETYPE_NOSUPP:
+ g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_LOGIN,
+ _("Cannot log in as %s at the %s domain"),
+ login->user, login->domain);
+ break;
+ case KRB5KDC_ERR_PREAUTH_FAILED:
+ g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD,
+ _("Invalid password, please try again"));
+ break;
+ default:
+ g_simple_async_result_set_error (async, UM_REALM_ERROR, UM_REALM_ERROR_FAIL,
+ _("Couldn't connect to the %s domain: %s"),
+ login->domain, krb5_get_error_message (k5, code));
+ break;
+ }
+
+ if (filename) {
+ g_unlink (filename);
+ g_free (filename);
+ }
+
+ if (k5)
+ krb5_free_context (k5);
+
+ g_simple_async_result_complete_in_idle (async);
+}
+
+void
+um_realm_login (const gchar *realm,
+ const gchar *domain,
+ const gchar *user,
+ const gchar *password,
+ UmRealmLoginFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *async;
+ LoginClosure *login;
+
+ g_return_if_fail (realm != NULL);
+ g_return_if_fail (user != NULL);
+ g_return_if_fail (password != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+
+ async = g_simple_async_result_new (NULL, callback, user_data,
+ um_realm_login);
+ login = g_slice_new0 (LoginClosure);
+ login->domain = g_strdup (domain ? domain : realm);
+ login->realm = g_strdup (realm);
+ login->user = g_strdup (user);
+ login->password = g_strdup (password);
+ login->unique = (flags & UM_REALM_LOGIN_TO_CREDENTIALS);
+ g_simple_async_result_set_op_res_gpointer (async, login, login_closure_free);
+
+ g_simple_async_result_set_handle_cancellation (async, TRUE);
+ g_simple_async_result_run_in_thread (async, kinit_thread_func,
+ G_PRIORITY_DEFAULT, cancellable);
+
+ g_object_unref (async);
+}
+
+gboolean
+um_realm_login_finish (GAsyncResult *result,
+ GBytes **credentials,
+ GError **error)
+{
+ GSimpleAsyncResult *async;
+ LoginClosure *login;
+
+ g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL,
+ um_realm_login), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ async = G_SIMPLE_ASYNC_RESULT (result);
+ if (g_simple_async_result_propagate_error (async, error))
+ return FALSE;
+
+ login = g_simple_async_result_get_op_res_gpointer (async);
+ if (credentials && login->credentials)
+ *credentials = g_bytes_ref (login->credentials);
+
+ return TRUE;
+}
diff --git a/panels/user-accounts/um-realm-manager.h b/panels/user-accounts/um-realm-manager.h
new file mode 100644
index 0000000..8fe2b0c
--- /dev/null
+++ b/panels/user-accounts/um-realm-manager.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2012 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Stef Walter <stefw gnome org>
+ */
+
+#ifndef __UM_REALM_MANAGER_H__
+#define __UM_REALM_MANAGER_H__
+
+#include "um-realm-generated.h"
+
+G_BEGIN_DECLS
+
+typedef enum {
+ UM_REALM_ERROR_BAD_LOGIN,
+ UM_REALM_ERROR_BAD_PASSWORD,
+ UM_REALM_ERROR_FAIL,
+} UmRealmErrors;
+
+#define UM_REALM_ERROR (um_realm_error_get_quark ())
+
+GQuark um_realm_error_get_quark (void) G_GNUC_CONST;
+
+#define UM_TYPE_REALM_MANAGER (um_realm_manager_get_type ())
+#define UM_REALM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_REALM_MANAGER, UmRealmManager))
+#define UM_IS_REALM_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_REALM_MANAGER))
+
+typedef struct _UmRealmManager UmRealmManager;
+
+GType um_realm_manager_get_type (void) G_GNUC_CONST;
+
+void um_realm_manager_new (GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+UmRealmManager * um_realm_manager_new_finish (GAsyncResult *result,
+ GError **error);
+
+void um_realm_manager_discover (UmRealmManager *self,
+ const gchar *input,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+GList * um_realm_manager_discover_finish (UmRealmManager *self,
+ GAsyncResult *result,
+ GError **error);
+
+GList * um_realm_manager_get_realms (UmRealmManager *self);
+
+typedef enum {
+ UM_REALM_LOGIN_TO_CREDENTIALS = 1 << 0,
+} UmRealmLoginFlags;
+
+void um_realm_login (const gchar *realm_name,
+ const gchar *domain,
+ const gchar *login,
+ const gchar *password,
+ UmRealmLoginFlags flags,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean um_realm_login_finish (GAsyncResult *result,
+ GBytes **credentials,
+ GError **error);
+
+void um_realm_join (UmRealmKerberos *realm,
+ GBytes *credentials,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean um_realm_join_finish (UmRealmKerberos *self,
+ GAsyncResult *result,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* __UM_REALM_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]