From 4bb0739670e1b44eb53ad1b1568c17e78cf6c3ce Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Thu, 7 Jun 2012 00:58:27 +0200 Subject: [PATCH] user-accounts: Implement enterprise logins in add dialog * Use realmd for domain joining and lookup, runtime dependency * Validate join domain correctly * Add UmRealmManager for handling some stuff above the autogenerated realmd dbus code * Show a dialog if the user's credentials cannot be used to join the domain. Prompt for admin creds. * Register the user's login with the AccountsService * This depends on the CacheUser() method of AccountsService https://bugzilla.gnome.org/show_bug.cgi?id=677548 --- configure.ac | 17 + panels/user-accounts/Makefile.am | 19 +- panels/user-accounts/data/Makefile.am | 1 + panels/user-accounts/data/account-dialog.ui | 359 +++++++- .../data/org.freedesktop.realmd.xml | 145 ++++ panels/user-accounts/um-account-dialog.c | 593 +++++++++++++- panels/user-accounts/um-realm-manager.c | 765 ++++++++++++++++++ panels/user-accounts/um-realm-manager.h | 91 +++ 8 files changed, 1969 insertions(+), 21 deletions(-) create mode 100644 panels/user-accounts/data/org.freedesktop.realmd.xml create mode 100644 panels/user-accounts/um-realm-manager.c create mode 100644 panels/user-accounts/um-realm-manager.h diff --git a/configure.ac b/configure.ac index 2e474f463..c81922115 100644 --- a/configure.ac +++ b/configure.ac @@ -225,6 +225,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, no) +if test "$KRB5_CONFIG" = "no"; then + AC_MSG_ERROR([krb5-config executable not found in your path - should be installed with the kerberos libraries]) +fi + +AC_MSG_CHECKING(for krb5 libraries and flags) +KRB5_CFLAGS="`$KRB5_CONFIG --cflags`" +KRB5_LIBS="`$KRB5_CONFIG --libs`" +AC_MSG_RESULT($KRB5_CFLAGS $KRB5_LIBS) + +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 83d1a07d6..8a7da5d42 100644 --- a/panels/user-accounts/Makefile.am +++ b/panels/user-accounts/Makefile.am @@ -23,6 +23,10 @@ if BUILD_CHEESE AM_CPPFLAGS += $(CHEESE_CFLAGS) endif +BUILT_SOURCES = \ + um-realm-generated.c \ + um-realm-generated.h + libuser_accounts_la_SOURCES = \ um-account-type.h \ um-account-type.c \ @@ -53,7 +57,10 @@ libuser_accounts_la_SOURCES = \ um-editable-combo.c \ um-user-panel.h \ um-user-panel.c \ - um-user-module.c + um-user-module.c \ + um-realm-manager.c \ + um-realm-manager.h \ + $(BUILT_SOURCES) libuser_accounts_la_LIBADD = \ $(PANEL_LIBS) \ @@ -69,18 +76,26 @@ endif libuser_accounts_la_LDFLAGS = $(PANEL_LDFLAGS) +um-realm-generated.c: $(srcdir)/data/org.freedesktop.realmd.xml + $(AM_V_GEN) gdbus-codegen --interface-prefix org.freedesktop.realmd. \ + --generate-c-code um-realm-generated --c-namespace UmRealm $< +um-realm-generated.h: um-realm-generated.c + noinst_PROGRAMS = frob-account-dialog 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.c \ um-user-manager.h \ um-utils.h \ - um-utils.c + um-utils.c \ + $(BUILT_SOURCES) frob_account_dialog_LDADD = \ $(libuser_accounts_la_LIBADD) diff --git a/panels/user-accounts/data/Makefile.am b/panels/user-accounts/data/Makefile.am index dbc17c651..d03d14b9f 100644 --- a/panels/user-accounts/data/Makefile.am +++ b/panels/user-accounts/data/Makefile.am @@ -16,6 +16,7 @@ desktop_DATA = $(Desktop_in_files:.desktop.in=.desktop) EXTRA_DIST = \ gnome-user-accounts-panel.desktop.in.in \ + org.freedesktop.realmd.xml \ $(ui_DATA) CLEANFILES = \ diff --git a/panels/user-accounts/data/account-dialog.ui b/panels/user-accounts/data/account-dialog.ui index 7751ef574..b99f21296 100644 --- a/panels/user-accounts/data/account-dialog.ui +++ b/panels/user-accounts/data/account-dialog.ui @@ -205,15 +205,20 @@ True + False True - 10 10 + 10 - + True + False + 5 1 - Enterprise Widgets + 0 + _Domain True + enterprise-domain @@ -221,13 +226,359 @@ 0 0 + 1 + 1 + + + + + True + False + 1 + _Login Name + True + enterprise-login + + + + 0 + 1 + 1 + 1 + + + + + True + False + 1 + _Password + True + enterprise-password + + + + 0 + 2 + 1 + 1 + + + + + True + False + vertical + 3 + + + True + False + True + True + 0 + + + True + + + + + False + True + + + + + True + False + 0 + Tip: Enterprise domain or realm name + + + + + + False + True + + + + + 1 + 0 + 1 + 1 + + + + + True + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + True + True + False + + True + True + + + 1 + 2 + 1 + 1 + + + + + + + False + 10 + False + True + True + dialog + + + False + vertical + 2 + + + False + end + + + gtk-cancel + False + True + True + True + True + False + True + + + False + True + 0 + + + + + C_ontinue + False + True + True + True + True + True + False + True + + + False + True + 1 + + + + + False + True + end + 0 + + + + + True + False + 5 + vertical + 10 + + + True + False + Domain Administrator Login + + + + + + + False + True + 0 + + + + + True + False + 0 + 0 + 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. + + + False + True + 1 + + + + + True + False + 12 + True + 6 + 12 + + + True + False + 1 + _Domain + True + join-domain + + + + 0 + 0 + 1 + 1 + + + + + True + False + 5 + 5 + 0 + + + 1 + 0 + 1 + 1 + + + + + True + False + 1 + Administrator _Name + True + join-name + + + + 0 + 1 + 1 + 1 + + + + + True + True + True + + True + + + 1 + 1 + 1 + 1 + + + + + True + False + 1 + Administrator Password + True + join-password + + + + 0 + 2 + 1 + 1 + + + + + True + True + True + False + + True + True + + + 1 + 2 + 1 + 1 False - False + True + 2 + + False + True + 1 + + + + + + button1 + button2 + + diff --git a/panels/user-accounts/data/org.freedesktop.realmd.xml b/panels/user-accounts/data/org.freedesktop.realmd.xml new file mode 100644 index 000000000..e508489e2 --- /dev/null +++ b/panels/user-accounts/data/org.freedesktop.realmd.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/panels/user-accounts/um-account-dialog.c b/panels/user-accounts/um-account-dialog.c index 40aac6c5c..a6c16377d 100644 --- a/panels/user-accounts/um-account-dialog.c +++ b/panels/user-accounts/um-account-dialog.c @@ -26,23 +26,35 @@ #include #include "um-account-dialog.h" +#include "um-realm-manager.h" #include "um-user-manager.h" #include "um-utils.h" -static void dialog_validate (UmAccountDialog *self); - -#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)) -#define UM_ACCOUNT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UM_TYPE_ACCOUNT_DIALOG, \ - UmAccountDialogClass)) - typedef enum { UM_LOCAL, UM_ENTERPRISE, NUM_MODES } UmAccountMode; +static void mode_change (UmAccountDialog *self, + UmAccountMode mode); + +static void dialog_validate (UmAccountDialog *self); + +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)) +#define UM_ACCOUNT_DIALOG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UM_TYPE_ACCOUNT_DIALOG, \ + UmAccountDialogClass)) + struct _UmAccountDialog { GtkDialog parent; GtkWidget *container_widget; @@ -63,6 +75,22 @@ struct _UmAccountDialog { GtkWidget *local_account_type; /* Enterprise widgets */ + guint realmd_watch; + GtkWidget *enterprise_button; + GtkListStore *enterprise_realms; + GtkComboBox *enterprise_domain; + 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; }; struct _UmAccountDialogClass { @@ -230,8 +258,8 @@ on_name_changed (GtkEditable *editable, } static void -local_area_init (UmAccountDialog *self, - GtkBuilder *builder) +local_init (UmAccountDialog *self, + GtkBuilder *builder) { GtkWidget *widget; @@ -260,6 +288,527 @@ local_prepare (UmAccountDialog *self) gtk_combo_box_set_active (GTK_COMBO_BOX (self->local_account_type), 0); } +static gboolean +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); + + 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); + return valid_name && valid_domain; +} + +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 (user_data); + enterprise_add_realm (self, realm); +} + + +static void +on_register_user (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + GError *error = NULL; + UmUser *user = NULL; + + um_user_manager_cache_user_finish (UM_USER_MANAGER (source), + result, &user, &error); + + /* This is where we're finally done */ + if (error == NULL) { + finish_action (self); + complete_dialog (self, user); + + } else { + show_error_dialog (self, _("Failed to register account"), error); + g_message ("Couldn't cache user account: %s", error->message); + finish_action (self); + g_error_free (error); + } +} + +static gchar * +enterprise_calculate_login (UmAccountDialog *self) +{ + const gchar *format = um_realm_kerberos_get_login_format (self->selected_realm); + return g_strdup_printf (format, gtk_entry_get_text (self->enterprise_login)); +} + +static void +on_permit_user_login (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + UmUserManager *manager; + GError *error = NULL; + gchar *login; + + um_realm_kerberos_call_change_permitted_logins_finish (UM_REALM_KERBEROS (source), + result, &error); + if (error == NULL) { + + /* + * Now tell the account service about this user. The account service + * should also lookup information about this via the realm and make + * sure all that is functional. + */ + manager = um_user_manager_ref_default (); + login = enterprise_calculate_login (self); + um_user_manager_cache_user (manager, login, self->cancellable, + on_register_user, g_object_ref (self), + g_object_unref); + + g_free (login); + g_object_unref (manager); + + } else { + show_error_dialog (self, _("Failed to register account"), error); + g_message ("Couldn't permit logins on account: %s", error->message); + finish_action (self); + } + + g_object_unref (self); +} + +static void +enterprise_permit_user_login (UmAccountDialog *self) +{ + gchar *login; + const gchar *add[2]; + const gchar *remove[1]; + + login = enterprise_calculate_login (self); + + add[0] = login; + add[1] = NULL; + remove[0] = NULL; + + um_realm_kerberos_call_change_permitted_logins (self->selected_realm, + add, remove, + self->cancellable, + on_permit_user_login, + g_object_ref (self)); + + g_free (login); +} + +static void +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), + 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 { + g_dbus_error_strip_remote_error (error); + 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); + GError *error = NULL; + GBytes *creds; + + um_realm_login_finish (result, &creds, &error); + + /* 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_message ("Couldn't log in as admin to join domain: %s", error->message); + g_error_free (error); + } + + g_object_unref (self); +} + +static void +join_init (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_signal_connect (self->join_dialog, "response", + G_CALLBACK (on_join_response), self); +} + +static void +on_realm_joined (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + GError *error = NULL; + + um_realm_join_finish (self->selected_realm, + result, &error); + + /* Yay, joined the domain, register the user locally */ + if (error == NULL) { + enterprise_permit_user_login (self); + + /* 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, error); + + /* Other failure */ + } else { + show_error_dialog (self, _("Failed to join domain"), error); + g_message ("Failed to join the domain: %s", error->message); + finish_action (self); + g_error_free (error); + } + + 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_permit_user_login (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); + finish_action (self); + gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_login)); + + } else if (g_error_matches (error, UM_REALM_ERROR, UM_REALM_ERROR_BAD_PASSWORD)) { + set_entry_validation_error (self->enterprise_password, error->message); + finish_action (self); + gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_password)); + + /* Other login failure */ + } else { + g_dbus_error_strip_remote_error (error); + show_error_dialog (self, _("Failed to log into domain"), error); + g_message ("Couldn't log in as user: %s", error->message); + 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), + self->cancellable, + on_realm_login, + g_object_ref (self)); +} + +static void +on_realm_discover_input (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + GError *error = NULL; + GList *realms; + + realms = um_realm_manager_discover_finish (self->realm_manager, + result, &error); + + /* Found a realm, log user into domain */ + if (error == NULL) { + 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 { + finish_action (self); + g_message ("Couldn't discover domain: %s", error->message); + gtk_widget_grab_focus (GTK_WIDGET (self->enterprise_domain_entry)); + g_dbus_error_strip_remote_error (error); + set_entry_validation_error (self->enterprise_domain_entry, + error->message); + g_error_free (error); + } + + g_object_unref (self); +} + +static void +enterprise_add_user (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_realm_manager_created (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + GError *error = NULL; + GList *realms, *l; + + g_clear_object (&self->realm_manager); + + self->realm_manager = um_realm_manager_new_finish (result, &error); + if (error != NULL) { + g_warning ("Couldn't contact realmd service: %s", error->message); + g_error_free (error); + return; + } + + /* Lookup all the realm objects */ + 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, triggers realm-added signal */ + um_realm_manager_discover (self->realm_manager, "", self->cancellable, + NULL, NULL); + + /* Show the 'Enterprise Login' stuff, and update mode */ + gtk_widget_show (self->enterprise_button); + mode_change (self, self->mode); +} + +static void +on_realmd_appeared (GDBusConnection *connection, + const gchar *name, + const gchar *name_owner, + gpointer user_data) +{ + UmAccountDialog *self = UM_ACCOUNT_DIALOG (user_data); + um_realm_manager_new (self->cancellable, on_realm_manager_created, self); +} + +static void +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); + gtk_widget_hide (self->enterprise_button); + mode_change (self, UM_LOCAL); +} + +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_init (UmAccountDialog *self, + GtkBuilder *builder) +{ + GtkWidget *widget; + + 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, "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); + self->enterprise_domain_entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))); + + widget = (GtkWidget *) gtk_builder_get_object (builder, "enterprise-login"); + 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"); + 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"); + self->enterprise_button = widget; + gtk_widget_hide (widget); + + self->realmd_watch = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.realmd", + G_BUS_NAME_WATCHER_FLAGS_AUTO_START, + on_realmd_appeared, on_realmd_disappeared, + self, NULL); +} + +static void +enterprise_prepare (UmAccountDialog *self) +{ + gtk_entry_set_text (GTK_ENTRY (self->enterprise_login), ""); + gtk_entry_set_text (GTK_ENTRY (self->enterprise_password), ""); +} + static void dialog_validate (UmAccountDialog *self) { @@ -270,8 +819,7 @@ dialog_validate (UmAccountDialog *self) valid = local_validate (self); break; case UM_ENTERPRISE: - /* TODO: Implement */ - valid = FALSE; + valid = enterprise_validate (self); break; default: valid = FALSE; @@ -442,7 +990,9 @@ um_account_dialog_init (UmAccountDialog *self) gtk_container_add (GTK_CONTAINER (content), widget); self->container_widget = widget; - local_area_init (self, builder); + local_init (self, builder); + enterprise_init (self, builder); + join_init (self, builder); mode_init (self, builder); g_object_unref (builder); @@ -461,8 +1011,7 @@ um_account_dialog_response (GtkDialog *dialog, local_create_user (self); break; case UM_ENTERPRISE: - /* TODO: */ - g_assert_not_reached (); + enterprise_add_user (self); break; default: g_assert_not_reached (); @@ -484,6 +1033,18 @@ um_account_dialog_dispose (GObject *obj) if (self->cancellable) g_cancellable_cancel (self->cancellable); + if (self->realmd_watch) + g_bus_unwatch_name (self->realmd_watch); + self->realmd_watch = 0; + + 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; + } + G_OBJECT_CLASS (um_account_dialog_parent_class)->dispose (obj); } @@ -494,6 +1055,7 @@ um_account_dialog_finalize (GObject *obj) if (self->cancellable) g_object_unref (self->cancellable); + g_object_unref (self->enterprise_realms); G_OBJECT_CLASS (um_account_dialog_parent_class)->finalize (obj); } @@ -535,6 +1097,7 @@ um_account_dialog_show (UmAccountDialog *self, self->cancellable = g_cancellable_new (); local_prepare (self); + enterprise_prepare (self); mode_change (self, UM_LOCAL); dialog_validate (self); diff --git a/panels/user-accounts/um-realm-manager.c b/panels/user-accounts/um-realm-manager.c new file mode 100644 index 000000000..6e73c8633 --- /dev/null +++ b/panels/user-accounts/um-realm-manager.c @@ -0,0 +1,765 @@ +/* -*- 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 + * Stef Walter + */ + +#include "config.h" + +#include "um-realm-manager.h" + +#include + +#include +#include +#include + +#include +#include +#include +#include + + +struct _UmRealmManager { + UmRealmProviderProxy parent; + guint diagnostics_sig; + 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; + + g_return_if_fail (realms != NULL); + + async = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + um_realm_manager_load); + load = g_slice_new0 (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); + while (1) { + 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 guint +hash_realm_info (gconstpointer value) +{ + const gchar *name, *path, *iface; + g_variant_get ((GVariant *)value, "(&s&o&s)", &name, &path, &iface); + return g_str_hash (name) ^ g_str_hash (path) ^ g_str_hash (iface); +} + +static void +um_realm_manager_init (UmRealmManager *self) +{ + self->realms = g_hash_table_new_full (hash_realm_info, 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; + + if (G_OBJECT_CLASS (um_realm_manager_parent_class)->notify) + 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)); + if (realms != NULL) + um_realm_manager_load (self, realms, NULL, NULL, NULL); + } +} + +static void +um_realm_manager_dispose (GObject *obj) +{ + UmRealmManager *self = UM_REALM_MANAGER (obj); + GDBusConnection *connection; + + if (self->diagnostics_sig) { + connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (self)); + if (connection != NULL) + g_dbus_connection_signal_unsubscribe (connection, self->diagnostics_sig); + self->diagnostics_sig = 0; + } + + G_OBJECT_CLASS (um_realm_manager_parent_class)->dispose (obj); +} + +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->dispose = um_realm_manager_dispose; + 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); +} + +static void +on_realm_diagnostics (GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + const gchar *message; + + if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(s)"))) { + /* Data is already formatted appropriately for stderr */ + g_variant_get (parameters, "(&s)", &message); + g_printerr ("%s", message); + } +} + +static void +on_realm_manager_async_init (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *async = G_SIMPLE_ASYNC_RESULT (user_data); + UmRealmManager *self; + GError *error = NULL; + GDBusProxy *proxy; + GObject *object; + guint sig; + + object = g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, &error); + + if (error != NULL) + g_simple_async_result_take_error (async, error); + if (object != NULL) { + proxy = G_DBUS_PROXY (object); + sig = g_dbus_connection_signal_subscribe (g_dbus_proxy_get_connection (proxy), + g_dbus_proxy_get_name_owner (proxy), + "org.freedesktop.realmd.Diagnostics", + "Diagnostics", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_realm_diagnostics, + NULL, + NULL); + + self = UM_REALM_MANAGER (object); + self->diagnostics_sig = sig; + g_simple_async_result_set_op_res_gpointer (async, self, g_object_unref); + } + + g_simple_async_result_complete (async); + g_object_unref (async); +} + +void +um_realm_manager_new (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *async; + + async = g_simple_async_result_new (NULL, callback, user_data, + um_realm_manager_new); + + g_async_initable_new_async (UM_TYPE_REALM_MANAGER, + G_PRIORITY_DEFAULT, cancellable, + on_realm_manager_async_init, g_object_ref (async), + "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); + + g_object_unref (async); +} + +UmRealmManager * +um_realm_manager_new_finish (GAsyncResult *result, + GError **error) +{ + UmRealmManager *self; + GSimpleAsyncResult *async; + + g_return_val_if_fail (g_simple_async_result_is_valid (result, NULL, + um_realm_manager_new), 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; + + self = g_simple_async_result_get_op_res_gpointer (async); + if (self != NULL) + g_object_ref (self); + return 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_GENERIC, + _("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; + 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; + + 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_GENERIC, + _("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); +} + +void +um_realm_login (const gchar *realm, + const gchar *domain, + const gchar *user, + const gchar *password, + 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); + 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) { + if (login->credentials) + *credentials = g_bytes_ref (login->credentials); + else + *credentials = NULL; + } + + 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 000000000..02e6e2ddf --- /dev/null +++ b/panels/user-accounts/um-realm-manager.h @@ -0,0 +1,91 @@ +/* -*- 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 + */ + +#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_GENERIC, +} 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); + +void um_realm_login (const gchar *realm_name, + const gchar *domain, + const gchar *login, + const gchar *password, + 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__ */