[gnome-system-tools/users-ui-redesign] Rework user profiles management



commit 60eaf03022e0f501d96766d47254755950603dc1
Author: Milan Bouchet-Valat <nalimilan club fr>
Date:   Tue Nov 17 13:00:28 2009 +0100

    Rework user profiles management
    
    Rework GstUserProfile(s) so that we can detect what profile as given user corresponds to, according to its settings and group memberships, which are considered *as opposed to other profiles*. This allows for manual tweaking of group memberships without losing the profile identification. Most privileged profiles have to go to the end of the configuration file, so that users that match several of them get the most powerful. The profiles member of GstUserProfiles goes from GHashTable to GList because of that new ordering significance.
    
    This leads to create gst_user_profiles_get_for_user(), gst_user_profiles_apply(), and gst_user_profiles_get_from_name(). user_settings_find_new_uid() is no longer static so that we can call from user-profile.c; it should eventually move to liboobs. Obsolete code setting default profile for user_settings_dialog is also removed.

 src/users/callbacks.c      |    1 -
 src/users/group-settings.c |    3 +-
 src/users/table.c          |   33 ------
 src/users/user-profiles.c  |  248 +++++++++++++++++++++++++++++++++++++++++---
 src/users/user-profiles.h  |   23 +++-
 src/users/user-settings.c  |   55 ++--------
 src/users/user-settings.h  |    5 +-
 7 files changed, 265 insertions(+), 103 deletions(-)
---
diff --git a/src/users/callbacks.c b/src/users/callbacks.c
index bd1f251..bd4eb8a 100644
--- a/src/users/callbacks.c
+++ b/src/users/callbacks.c
@@ -570,7 +570,6 @@ on_user_settings_profile_changed (GtkWidget *widget, gpointer data)
 	gtk_tree_model_get (model, &iter, 0, &profile_name, -1);
 	profile = gst_user_profiles_set_current (GST_USERS_TOOL (tool)->profiles, profile_name);
 
-	user_settings_apply_profile (GST_USERS_TOOL (tool), profile);
 	g_free (profile_name);
 }
 
diff --git a/src/users/group-settings.c b/src/users/group-settings.c
index 5d44aad..6fb8c50 100644
--- a/src/users/group-settings.c
+++ b/src/users/group-settings.c
@@ -215,7 +215,8 @@ is_group_root (OobsGroup *group)
 	return (strcmp (name, "root") == 0);
 }
 
-/* Get the OobsGroup corresponding to a name, or NULL if it does not exist */
+/* Get the OobsGroup corresponding to a name, or NULL if it does not exist.
+ * Don't forget to unref the group when valid. */
 OobsGroup *
 group_settings_get_group_from_name (const gchar *name)
 {
diff --git a/src/users/table.c b/src/users/table.c
index d9650b9..4780611 100644
--- a/src/users/table.c
+++ b/src/users/table.c
@@ -154,39 +154,6 @@ table_populate_profiles (GstUsersTool *tool,
 }
 
 void
-table_set_default_profile (GstUsersTool *tool)
-{
-	GtkWidget *combo;
-	GtkTreeModel *model;
-	GtkTreeIter iter;
-	GstUserProfile *default_profile;
-	gchar *profile;
-	gboolean valid;
-
-	default_profile = gst_user_profiles_get_default_profile (tool->profiles);
-	combo = gst_dialog_get_widget (GST_TOOL (tool)->main_dialog, "user_settings_profile_menu");
-	model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
-	valid = gtk_tree_model_get_iter_first (model, &iter);
-
-	while (valid) {
-		gtk_tree_model_get (model, &iter, 0, &profile, -1);
-
-		if (default_profile &&
-		    strcmp (profile, default_profile->name) == 0) {
-			g_signal_handlers_block_by_func (G_OBJECT (combo), on_user_settings_profile_changed, GST_TOOL (tool)->main_dialog);
-			gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combo), &iter);
-			g_signal_handlers_unblock_by_func (G_OBJECT (combo), on_user_settings_profile_changed, GST_TOOL (tool)->main_dialog);
-			valid = FALSE;
-		} else
-			valid = gtk_tree_model_iter_next (model, &iter);
-
-		g_free (profile);
-	}
-
-	user_settings_apply_profile (tool, default_profile);
-}
-
-void
 create_tables (GstUsersTool *tool)
 {
 	create_users_table (tool);
diff --git a/src/users/user-profiles.c b/src/users/user-profiles.c
index 138ed60..1ddfbaa 100644
--- a/src/users/user-profiles.c
+++ b/src/users/user-profiles.c
@@ -15,11 +15,16 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
  *
- * Authors: Carlos Garnacho Parro <carlosg gnome org>.
+ * Authors: Carlos Garnacho Parro <carlosg gnome org>,
+ *          Milan Bouchet-Valat <nalimilan club fr>.
  */
 
+#include <string.h>
 #include <glib.h>
+#include <gio/gio.h>
 #include "user-profiles.h"
+#include "user-settings.h"
+#include "group-settings.h"
 
 #define PROFILES_FILE "/etc/gnome-system-tools/users/profiles"
 #define GST_USER_PROFILES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GST_TYPE_USER_PROFILES, GstUserProfilesPrivate))
@@ -30,7 +35,8 @@ struct _GstUserProfilesPrivate
 {
 	GstUserProfile *default_profile;
 	GstUserProfile *current_profile;
-	GHashTable     *profiles;
+	GList          *profiles;
+	GList          *all_groups;
 };
 
 static void   gst_user_profiles_class_init (GstUserProfilesClass *class);
@@ -74,8 +80,9 @@ load_profiles (GstUserProfiles *profiles)
 {
 	GstUserProfilesPrivate *priv;
 	GKeyFile *key_file;
-	gchar **groups, **group;
+	gchar **groups, **group, **group_name;
 	GstUserProfile *profile;
+	GList *l, *item;
 
 	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
 	key_file = g_key_file_new ();
@@ -93,10 +100,22 @@ load_profiles (GstUserProfiles *profiles)
 
 	while (*group) {
 		profile = create_profile (key_file, *group);
-		g_hash_table_replace (priv->profiles, profile->name, profile);
+		priv->profiles = g_list_prepend (priv->profiles, profile);
 
 		if (profile->is_default)
-			priv->default_profile = priv->current_profile = profile;
+			priv->default_profile = profile;
+
+		/* Add the groups to global list */
+		group_name = profile->groups;
+		while (group_name && *group_name) {
+			if (!g_list_find_custom (priv->all_groups,
+			                         *group_name,
+			                         (GCompareFunc) strcmp))
+				priv->all_groups = g_list_append (priv->all_groups,
+				                                  *group_name);
+
+			group_name++;
+		}
 
 		group++;
 	}
@@ -122,7 +141,7 @@ gst_user_profiles_init (GstUserProfiles *profiles)
 
 	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
 
-	priv->profiles = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify) free_profile);
+	priv->profiles = NULL;
 	load_profiles (profiles);
 }
 
@@ -130,11 +149,19 @@ static void
 gst_user_profiles_finalize (GObject *object)
 {
 	GstUserProfilesPrivate *priv;
+	GList *l;
 
 	priv = GST_USER_PROFILES_GET_PRIVATE (object);
 
-	if (priv->profiles)
-		g_hash_table_unref (priv->profiles);
+	if (priv->profiles) {
+		for (l = priv->profiles; l; l = l->next)
+			free_profile ((GstUserProfile *) l->data);
+
+		g_list_free (priv->profiles);
+	}
+
+	/* Free list structure, actual strings are owned by the profiles */
+	g_list_free (priv->all_groups);
 }
 
 GstUserProfiles*
@@ -143,24 +170,47 @@ gst_user_profiles_get ()
 	return g_object_new (GST_TYPE_USER_PROFILES, NULL);
 }
 
-void
-retrieve_profile_names (gchar           *name,
-			GstUserProfile  *profile,
-			GList          **list)
+/*
+ * Get the profile correspoding to a given name,
+ * or NULL if no such profile exists.
+ */
+GstUserProfile*
+gst_user_profiles_get_from_name (GstUserProfiles *profiles,
+                                 const gchar      *name)
 {
-	*list = g_list_prepend (*list, name);
+	GstUserProfilesPrivate *priv;
+	GstUserProfile *profile;
+	GList *l;
+
+	g_return_val_if_fail (GST_IS_USER_PROFILES (profiles), NULL);
+
+	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
+
+	for (l = priv->profiles; l; l = l->next) {
+		profile = l->data;
+		if (strcmp (name, profile->name) == 0)
+			return profile;
+	    }
+
+	return NULL;
 }
 
 GList*
 gst_user_profiles_get_names (GstUserProfiles *profiles)
 {
 	GstUserProfilesPrivate *priv;
+	GstUserProfile *profile;
 	GList *names = NULL;
+	GList *l;
 
 	g_return_val_if_fail (GST_IS_USER_PROFILES (profiles), NULL);
 	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
 
-	g_hash_table_foreach (priv->profiles, (GHFunc) retrieve_profile_names, &names);
+	for (l = priv->profiles; l; l = l->next) {
+		profile = l->data;
+		names = g_list_prepend (names, profile->name);
+	}
+
 	return names;
 }
 
@@ -174,7 +224,7 @@ gst_user_profiles_set_current (GstUserProfiles *profiles,
 
 	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
 
-	priv->current_profile = g_hash_table_lookup (priv->profiles, profile);
+	priv->current_profile = gst_user_profiles_get_from_name (profiles, profile);
 
 	return priv->current_profile;
 }
@@ -202,3 +252,171 @@ gst_user_profiles_get_default_profile (GstUserProfiles *profiles)
 
 	return priv->default_profile;
 }
+
+/*
+ * Find the profile that matches the settings of a given user.
+ * Returns NULL if no profile exactly fits.
+ *
+ * Note that our groups handling checks that user is member of all
+ * groups of the returned profile, as well as not member of any
+ * group of any other profile. This ensures we are tolerant to
+ * custom groups while not completely breaking the profiles concept.
+ *
+ * If @strict is FALSE, only groups and shell will be checked,
+ * for consistency with gst_user_profiles_apply().
+ */
+GstUserProfile*
+gst_user_profiles_get_for_user (GstUserProfiles *profiles,
+                                OobsUser        *user,
+                                gboolean         strict)
+{
+	GstUserProfilesPrivate *priv;
+	GstUserProfile *profile;
+	GstUserProfile *matched;
+	GHashTableIter iter;
+	gpointer value;
+	GFile *file_home, *file_prefix;
+	const gchar *shell, *home;
+	gint uid;
+	gchar **groups, **group_name;
+	OobsGroup *group;
+	GList *l, *m;
+	gboolean matched_groups, in_profile, in_group;
+
+	g_return_val_if_fail (GST_IS_USER_PROFILES (profiles), NULL);
+	g_return_val_if_fail (OOBS_IS_USER (user), NULL);
+
+	shell = oobs_user_get_shell (user);
+	home = oobs_user_get_home_directory (user);
+	uid = oobs_user_get_uid (user);
+
+	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
+
+	matched = NULL;
+	for (l = priv->profiles; l; l = l->next) {
+		profile = (GstUserProfile *) l->data;
+
+		/* also check for settings that should not be changed for existing users */
+		if (strict)
+		  {
+			  /* check that UID is in the range */
+			  if (!(profile->uid_min <= uid && uid <= profile->uid_max))
+				  continue;
+
+			  /* check that home is under the prefix */
+			  file_home = g_file_new_for_path (home);
+			  file_prefix = g_file_new_for_path (profile->home_prefix);
+			  if (!g_file_has_prefix (file_home, file_prefix)) {
+				  g_object_unref (file_home);
+				  g_object_unref (file_prefix);
+				  continue;
+			  }
+			  else {
+				  g_object_unref (file_home);
+				  g_object_unref (file_prefix);
+			  }
+		  }
+
+		/* check that shell is the right one */
+		if (strcmp (shell, profile->shell) != 0)
+			continue;
+
+		/* check user's membership to all groups of the profile,
+		/* also check that user is not member of any group
+		 * that defines another profile vs the current one */
+		matched_groups = TRUE;
+		for (m = priv->all_groups; m && matched_groups; m = m->next) {
+			in_profile = FALSE;
+			group_name = profile->groups;
+			while (group_name && *group_name) {
+				if (strcmp ((char *) m->data, *group_name) == 0)
+					in_profile = TRUE;
+
+				group_name++;
+			}
+
+			group = group_settings_get_group_from_name ((char *) m->data);
+			in_group = user_settings_is_user_in_group (user, group);
+			if ((in_profile && !in_group) || (!in_profile && in_group)) {
+				if (group)
+					g_object_unref (group);
+
+				matched_groups = FALSE;
+				break;
+			}
+		}
+
+		/* stop at first match, since the list has been reverted on loading,
+		 * most privileged profiles must be at the end of the config file */
+		if (matched_groups) {
+			matched = profile;
+			break;
+		}
+	}
+
+	return matched;
+}
+
+/*
+ * Change user settings to fit a given profile. User will be added to groups of
+ * the passed profile, and removed from groups defining other profiles. If user_new
+ * is TRUE, only shell and groups will be changed: forcing other settings would
+ * break the account.
+ */
+void
+gst_user_profiles_apply (GstUserProfiles *profiles,
+                         GstUserProfile  *profile,
+                         OobsUser        *user,
+                         gboolean         new_user)
+{
+	GstUserProfilesPrivate *priv;
+	gint uid;
+	char *home;
+	char **group_name;
+	OobsGroup *group;
+	GList *l;
+	gboolean in_profile;
+
+	g_return_if_fail (GST_IS_USER_PROFILES (profiles));
+	g_return_if_fail (profile != NULL);
+	g_return_if_fail (OOBS_IS_USER (user));
+	/* used to build home dir, we normally ensure this before calling */
+	g_return_if_fail (oobs_user_get_login_name (user) != NULL);
+
+	priv = GST_USER_PROFILES_GET_PRIVATE (profiles);
+
+	/* default shell */
+	oobs_user_set_shell (user, profile->shell);
+
+	/* add user to groups from the profile, remove it from groups of other profiles */
+	for (l = priv->all_groups; l; l = l->next) {
+		in_profile = FALSE;
+		group_name = profile->groups;
+		while (group_name && *group_name) {
+			if (strcmp ((char *) l->data, *group_name) == 0)
+				in_profile = TRUE;
+
+			group_name++;
+		}
+
+		group = group_settings_get_group_from_name ((char *) l->data);
+		if (in_profile)
+			oobs_group_add_user (group, user);
+		else
+			oobs_group_remove_user (group, user);
+	}
+
+	if (!new_user) /* don't apply settings below to existing users */
+		return;
+
+	/* default UID */
+	uid = user_settings_find_new_uid (profile->uid_min, profile->uid_max);
+
+	/* default home prefix */
+	home = g_build_path (G_DIR_SEPARATOR_S,
+			     profile->home_prefix,
+			     oobs_user_get_login_name (user),
+			     NULL);
+	oobs_user_set_home_directory (user, home);
+	g_free (home);
+}
diff --git a/src/users/user-profiles.h b/src/users/user-profiles.h
index 8957d03..6ef4abd 100644
--- a/src/users/user-profiles.h
+++ b/src/users/user-profiles.h
@@ -25,6 +25,8 @@ G_BEGIN_DECLS
 
 #include <glib.h>
 #include <glib-object.h>
+#include <oobs/oobs-user.h>
+#include <oobs/oobs-group.h>
 
 #define GST_TYPE_USER_PROFILES           (gst_user_profiles_get_type ())
 #define GST_USER_PROFILES(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_USER_PROFILES, GstUserProfiles))
@@ -60,14 +62,23 @@ struct _GstUserProfile
 	gchar **groups;
 };
 
-GType            gst_user_profiles_get_type    (void);
-GstUserProfiles* gst_user_profiles_get         (void);
+GType            gst_user_profiles_get_type            (void);
+GstUserProfiles* gst_user_profiles_get                 (void);
 
-GList*           gst_user_profiles_get_names   (GstUserProfiles *profiles);
-GstUserProfile*  gst_user_profiles_set_current (GstUserProfiles *profiles,
-						const gchar     *profile);
-GstUserProfile*  gst_user_profiles_get_current (GstUserProfiles *profiles);
+GstUserProfile*  gst_user_profiles_get_from_name       (GstUserProfiles *profiles,
+                                                        const gchar *name);
+GList*           gst_user_profiles_get_names           (GstUserProfiles *profiles);
+GstUserProfile*  gst_user_profiles_set_current         (GstUserProfiles *profiles,
+						        const gchar     *profile);
+GstUserProfile*  gst_user_profiles_get_current         (GstUserProfiles *profiles);
 GstUserProfile*  gst_user_profiles_get_default_profile (GstUserProfiles *profiles);
+GstUserProfile*  gst_user_profiles_get_for_user        (GstUserProfiles *profiles,
+                                                        OobsUser        *user,
+                                                        gboolean         strict);
+void             gst_user_profiles_apply               (GstUserProfiles *profiles,
+                                                        GstUserProfile  *profile,
+                                                        OobsUser        *user,
+                                                        gboolean         new_user);
 
 
 G_END_DECLS
diff --git a/src/users/user-settings.c b/src/users/user-settings.c
index 29dea79..6998fd9 100644
--- a/src/users/user-settings.c
+++ b/src/users/user-settings.c
@@ -286,9 +286,9 @@ get_no_passwd_login_group ()
 	return NULL;
 }
 
-static gboolean
-is_user_in_group (OobsUser  *user,
-		  OobsGroup *group)
+gboolean
+user_settings_is_user_in_group (OobsUser  *user,
+		                OobsGroup *group)
 {
 	OobsUser *tmp_user;
 	GList *users = NULL;
@@ -352,9 +352,9 @@ uid_exists (uid_t uid)
 	return FALSE;
 }
 
-static uid_t
-find_new_uid (gint uid_min,
-	      gint uid_max)
+uid_t
+user_settings_find_new_uid (gint uid_min,
+                            gint uid_max)
 {
 	OobsUsersConfig *config;
 	OobsList *list;
@@ -442,8 +442,8 @@ user_settings_set (OobsUser *user)
 				oobs_users_config_get_default_shell (config));
 
 		widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_uid");
-		uid = find_new_uid (GST_USERS_TOOL (tool)->minimum_uid,
-				    GST_USERS_TOOL (tool)->maximum_uid);
+		uid = user_settings_find_new_uid (GST_USERS_TOOL (tool)->minimum_uid,
+		                                  GST_USERS_TOOL (tool)->maximum_uid);
 		gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), uid);
 		gst_dialog_try_set_sensitive (tool->main_dialog, widget, TRUE);
 		gtk_widget_hide (notice);
@@ -503,9 +503,6 @@ user_settings_set (OobsUser *user)
 	widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_notebook");
 	gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 0);
 
-	if (!login)
-		table_set_default_profile (GST_USERS_TOOL (tool));
-
 	if (user) {
 		widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_home");
 		set_entry_text (widget, oobs_user_get_home_directory (user));
@@ -808,40 +805,6 @@ user_settings_dialog_get_data (GtkWidget *dialog)
 	return user;
 }
 
-void
-user_settings_apply_profile (GstUsersTool   *users_tool,
-			     GstUserProfile *profile)
-{
-	GstTool *tool;
-	GtkWidget *widget;
-	gint uid;
-
-	if (!profile)
-		return;
-
-	tool = GST_TOOL (users_tool);
-
-	/* default UID */
-	widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_uid");
-	uid = find_new_uid (profile->uid_min, profile->uid_max);
-	gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget), uid);
-
-	/* default shell */
-	widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_shell");
-	set_entry_text (GTK_BIN (widget)->child, profile->shell);
-
-	/* default home prefix */
-	widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_home");
-	g_object_set_data (G_OBJECT (widget), "default-home", profile->home_prefix);
-
-	widget = gst_dialog_get_widget (tool->main_dialog, "user_settings_name");
-	on_user_settings_login_changed (GTK_EDITABLE (widget), NULL);
-
-	/* default groups */
-	privileges_table_set_from_profile (profile);
-}
-
-
 /*
  * Common to all modular edit dialogs: run the dialog after filling
  * the user's name and face and handling window settings.
@@ -957,7 +920,7 @@ on_edit_user_passwd (GtkButton *button, gpointer user_data)
 	}
 	else {
 		gst_dialog_try_set_sensitive (tool->main_dialog, nocheck_toggle, TRUE);
-		if (is_user_in_group (user, no_passwd_login_group))
+		if (user_settings_is_user_in_group (user, no_passwd_login_group))
 			gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (nocheck_toggle),
 			                              TRUE);
 		else
diff --git a/src/users/user-settings.h b/src/users/user-settings.h
index c32c775..7586d09 100644
--- a/src/users/user-settings.h
+++ b/src/users/user-settings.h
@@ -24,6 +24,8 @@
 #ifndef __USER_SETTINGS_H
 #define __USER_SETTINGS_H
 
+#include <gtk/gtk.h>
+
 #define NO_PASSWD_LOGIN_GROUP "nopasswdlogin"
 
 gboolean        user_delete                      (GtkTreeModel *model,
@@ -32,8 +34,9 @@ void	        user_settings_set	         (OobsUser *user);
 gint            user_settings_dialog_run         (GtkWidget *dialog);
 
 OobsUser *      user_settings_dialog_get_data    (GtkWidget *dialog);
-void            user_settings_apply_profile      (GstUsersTool *tool, GstUserProfile *profile);
 GdkPixbuf *     user_settings_get_user_face      (OobsUser *user, int size);
+uid_t           user_settings_find_new_uid       (gint uid_min,
+                                                  gint uid_max);
 
 
 #endif /* USER_SETTINGS_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]