diff --git a/SOURCES/0001-user-Introduce-user-templates-for-setting-default-se.patch b/SOURCES/0001-user-Introduce-user-templates-for-setting-default-se.patch new file mode 100644 index 0000000..001de67 --- /dev/null +++ b/SOURCES/0001-user-Introduce-user-templates-for-setting-default-se.patch @@ -0,0 +1,925 @@ +From 72427bd4fcae931298c670093f9cbd34ad58f59a Mon Sep 17 00:00:00 2001 +From: Ray Strode +Date: Wed, 4 Aug 2021 19:54:59 -0400 +Subject: [PATCH] user: Introduce user templates for setting default session + etc + +At the moment there's no easy way to set a default session, or +face icon or whatever for all users. If a user has never logged in +before, we just generate their cache file from hardcoded defaults. + +This commit introduces a template system to make it possible for +admins to set up defaults on their own. + +Admins can write either +/etc/accountsservice/user-templates/administrator +or +/etc/accountsservice/user-templates/standard + +files. These files follow the same format as + +/var/lib/AccountsService/users/username + +files, but will support substituting $HOME and $USER to the appropriate +user specific values. + +User templates also support an additional group [Template] that +have an additional key EnvironmentFiles that specify a list +of environment files to load (files with KEY=VALUE pairs in them). +Any keys listed in those environment files will also get substituted. +--- + data/administrator | 6 + + data/meson.build | 10 ++ + data/standard | 6 + + src/daemon.c | 8 +- + src/meson.build | 1 + + src/user.c | 284 ++++++++++++++++++++++++++++++++++++++++++++- + src/user.h | 3 +- + 7 files changed, 305 insertions(+), 13 deletions(-) + create mode 100644 data/administrator + create mode 100644 data/standard + +diff --git a/data/administrator b/data/administrator +new file mode 100644 +index 0000000..ea043c9 +--- /dev/null ++++ b/data/administrator +@@ -0,0 +1,6 @@ ++[Template] ++#EnvironmentFiles=/etc/os-release; ++ ++[User] ++Session= ++Icon=${HOME}/.face +diff --git a/data/meson.build b/data/meson.build +index 2dc57c2..7d9bdcd 100644 +--- a/data/meson.build ++++ b/data/meson.build +@@ -22,30 +22,40 @@ service = act_namespace + '.service' + configure_file( + input: service + '.in', + output: service, + configuration: service_conf, + install: true, + install_dir: dbus_sys_dir, + ) + + policy = act_namespace.to_lower() + '.policy' + + i18n.merge_file( + policy, + input: policy + '.in', + output: policy, + po_dir: po_dir, + install: true, + install_dir: policy_dir, + ) + + if install_systemd_unit_dir + service = 'accounts-daemon.service' + + configure_file( + input: service + '.in', + output: service, + configuration: service_conf, + install: true, + install_dir: systemd_system_unit_dir, + ) + endif ++ ++install_data( ++ 'administrator', ++ install_dir: join_paths(act_datadir, 'accountsservice', 'user-templates'), ++) ++ ++install_data( ++ 'standard', ++ install_dir: join_paths(act_datadir, 'accountsservice', 'user-templates'), ++) +diff --git a/data/standard b/data/standard +new file mode 100644 +index 0000000..ea043c9 +--- /dev/null ++++ b/data/standard +@@ -0,0 +1,6 @@ ++[Template] ++#EnvironmentFiles=/etc/os-release; ++ ++[User] ++Session= ++Icon=${HOME}/.face +diff --git a/src/daemon.c b/src/daemon.c +index 5ce0216..66ac7ba 100644 +--- a/src/daemon.c ++++ b/src/daemon.c +@@ -298,69 +298,63 @@ entry_generator_cachedir (Daemon *daemon, + break; + + /* Only load files in this directory */ + filename = g_build_filename (USERDIR, name, NULL); + regular = g_file_test (filename, G_FILE_TEST_IS_REGULAR); + + if (regular) { + errno = 0; + pwent = getpwnam (name); + if (pwent != NULL) { + *shadow_entry = getspnam (pwent->pw_name); + + return pwent; + } else if (errno == 0) { + g_debug ("user '%s' in cache dir but not present on system, removing", name); + remove_cache_files (name); + } + else { + g_warning ("failed to check if user '%s' in cache dir is present on system: %s", + name, g_strerror (errno)); + } + } + } + + /* Last iteration */ + g_dir_close (dir); + + /* Update all the users from the files in the cache dir */ + g_hash_table_iter_init (&iter, users); + while (g_hash_table_iter_next (&iter, &key, &value)) { +- const gchar *name = key; + User *user = value; +- g_autofree gchar *filename = NULL; +- g_autoptr(GKeyFile) key_file = NULL; + +- filename = g_build_filename (USERDIR, name, NULL); +- key_file = g_key_file_new (); +- if (g_key_file_load_from_file (key_file, filename, 0, NULL)) +- user_update_from_keyfile (user, key_file); ++ user_update_from_cache (user); + } + + *state = NULL; + return NULL; + } + + static struct passwd * + entry_generator_requested_users (Daemon *daemon, + GHashTable *users, + gpointer *state, + struct spwd **shadow_entry) + { + DaemonPrivate *priv = daemon_get_instance_private (daemon); + struct passwd *pwent; + GList *node; + + /* First iteration */ + if (*state == NULL) { + *state = priv->explicitly_requested_users; + } + + /* Every iteration */ + + if (g_hash_table_size (users) < MAX_LOCAL_USERS) { + node = *state; + while (node != NULL) { + const char *name; + + name = node->data; + node = node->next; +diff --git a/src/meson.build b/src/meson.build +index 3970749..d3b0cb9 100644 +--- a/src/meson.build ++++ b/src/meson.build +@@ -1,59 +1,60 @@ + sources = [] + + gdbus_headers = [] + + ifaces = [ + ['accounts-generated', 'org.freedesktop.', 'Accounts'], + ['accounts-user-generated', act_namespace + '.', 'User'], + ['realmd-generated', 'org.freedesktop.', 'realmd'], + ] + + foreach iface: ifaces + gdbus_sources = gnome.gdbus_codegen( + iface[0], + join_paths(data_dir, iface[1] + iface[2] + '.xml'), + interface_prefix: iface[1], + namespace: 'Accounts', + ) + sources += gdbus_sources + gdbus_headers += gdbus_sources[1] + endforeach + + deps = [ + gio_dep, + gio_unix_dep, + ] + + cflags = [ + '-DLOCALSTATEDIR="@0@"'.format(act_localstatedir), + '-DDATADIR="@0@"'.format(act_datadir), ++ '-DSYSCONFDIR="@0@"'.format(act_sysconfdir), + '-DICONDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'icons')), + '-DUSERDIR="@0@"'.format(join_paths(act_localstatedir, 'lib', 'AccountsService', 'users')), + ] + + libaccounts_generated = static_library( + 'accounts-generated', + sources: sources, + include_directories: top_inc, + dependencies: deps, + c_args: cflags, + ) + + libaccounts_generated_dep = declare_dependency( + sources: gdbus_headers, + include_directories: include_directories('.'), + dependencies: gio_dep, + link_with: libaccounts_generated, + ) + + sources = files( + 'daemon.c', + 'extensions.c', + 'main.c', + 'user.c', + 'user-classify.c', + 'util.c', + 'wtmp-helper.c', + ) + + deps = [ +diff --git a/src/user.c b/src/user.c +index 9f57af5..16c7721 100644 +--- a/src/user.c ++++ b/src/user.c +@@ -43,127 +43,384 @@ + #include + + #include "user-classify.h" + #include "daemon.h" + #include "user.h" + #include "accounts-user-generated.h" + #include "util.h" + + struct User { + AccountsUserSkeleton parent; + + GDBusConnection *system_bus_connection; + gchar *object_path; + + Daemon *daemon; + + GKeyFile *keyfile; + + gid_t gid; + gint64 expiration_time; + gint64 last_change_time; + gint64 min_days_between_changes; + gint64 max_days_between_changes; + gint64 days_to_warn; + gint64 days_after_expiration_until_lock; + GVariant *login_history; + gchar *icon_file; + gchar *default_icon_file; + gboolean account_expiration_policy_known; + gboolean cached; ++ gboolean template_loaded; + + guint *extension_ids; + guint n_extension_ids; + + guint changed_timeout_id; + }; + + typedef struct UserClass + { + AccountsUserSkeletonClass parent_class; + } UserClass; + + static void user_accounts_user_iface_init (AccountsUserIface *iface); ++static void user_update_from_keyfile (User *user, GKeyFile *keyfile); + + G_DEFINE_TYPE_WITH_CODE (User, user, ACCOUNTS_TYPE_USER_SKELETON, G_IMPLEMENT_INTERFACE (ACCOUNTS_TYPE_USER, user_accounts_user_iface_init)); + + static gint + account_type_from_pwent (struct passwd *pwent) + { + struct group *grp; + gint i; + + if (pwent->pw_uid == 0) { + g_debug ("user is root so account type is administrator"); + return ACCOUNT_TYPE_ADMINISTRATOR; + } + + grp = getgrnam (ADMIN_GROUP); + if (grp == NULL) { + g_debug (ADMIN_GROUP " group not found"); + return ACCOUNT_TYPE_STANDARD; + } + + for (i = 0; grp->gr_mem[i] != NULL; i++) { + if (g_strcmp0 (grp->gr_mem[i], pwent->pw_name) == 0) { + return ACCOUNT_TYPE_ADMINISTRATOR; + } + } + + return ACCOUNT_TYPE_STANDARD; + } + + static void + user_reset_icon_file (User *user) + { + const char *icon_file; + gboolean icon_is_default; + const char *home_dir; + + icon_file = accounts_user_get_icon_file (ACCOUNTS_USER (user)); + + if (icon_file == NULL || g_strcmp0 (icon_file, user->default_icon_file) == 0) { + icon_is_default = TRUE; + } else { + icon_is_default = FALSE; + } + + g_free (user->default_icon_file); + home_dir = accounts_user_get_home_directory (ACCOUNTS_USER (user)); + + user->default_icon_file = g_build_filename (home_dir, ".face", NULL); + + if (icon_is_default) { + accounts_user_set_icon_file (ACCOUNTS_USER (user), user->default_icon_file); + } + } + ++static gboolean ++user_has_cache_file (User *user) ++{ ++ g_autofree char *filename = NULL; ++ ++ filename = g_build_filename (USERDIR, user_get_user_name (user), NULL); ++ ++ return g_file_test (filename, G_FILE_TEST_EXISTS); ++} ++ ++static gboolean ++is_valid_shell_identifier_character (char c, ++ gboolean first) ++{ ++ return (!first && g_ascii_isdigit (c)) || ++ c == '_' || ++ g_ascii_isalpha (c); ++} ++ ++static char * ++expand_template_variables (User *user, ++ GHashTable *template_variables, ++ const char *str) ++{ ++ GString *s = g_string_new (""); ++ const char *p, *start; ++ char c; ++ ++ p = str; ++ while (*p) { ++ c = *p; ++ if (c == '\\') { ++ p++; ++ c = *p; ++ if (c != '\0') { ++ p++; ++ switch (c) { ++ case '\\': ++ g_string_append_c (s, '\\'); ++ break; ++ case '$': ++ g_string_append_c (s, '$'); ++ break; ++ default: ++ g_string_append_c (s, '\\'); ++ g_string_append_c (s, c); ++ break; ++ } ++ } ++ } else if (c == '$') { ++ gboolean brackets = FALSE; ++ p++; ++ if (*p == '{') { ++ brackets = TRUE; ++ p++; ++ } ++ start = p; ++ while (*p != '\0' && ++ is_valid_shell_identifier_character (*p, p == start)) ++ p++; ++ if (p == start || (brackets && *p != '}')) { ++ g_string_append_c (s, '$'); ++ if (brackets) ++ g_string_append_c (s, '{'); ++ g_string_append_len (s, start, p - start); ++ } else { ++ g_autofree char *variable = NULL; ++ const char *value; ++ ++ if (brackets && *p == '}') ++ p++; ++ ++ variable = g_strndup (start, p - start - 1); ++ ++ value = g_hash_table_lookup (template_variables, variable); ++ if (value) { ++ g_string_append (s, value); ++ } ++ } ++ } else { ++ p++; ++ g_string_append_c (s, c); ++ } ++ } ++ return g_string_free (s, FALSE); ++} ++ ++static void ++load_template_environment_file (User *user, ++ GHashTable *variables, ++ const char *file) ++{ ++ g_autofree char *contents = NULL; ++ g_auto (GStrv) lines = NULL; ++ g_autoptr (GError) error = NULL; ++ gboolean file_loaded; ++ size_t i; ++ ++ file_loaded = g_file_get_contents (file, &contents, NULL, &error); ++ ++ if (!file_loaded) { ++ g_debug ("Couldn't load template environment file %s: %s", ++ file, error->message); ++ return; ++ } ++ ++ lines = g_strsplit (contents, "\n", -1); ++ ++ for (i = 0; lines[i] != NULL; i++) { ++ char *p; ++ char *variable_end; ++ const char *variable; ++ const char *value; ++ ++ p = lines[i]; ++ while (g_ascii_isspace (*p)) ++ p++; ++ if (*p == '#' || *p == '\0') ++ continue; ++ variable = p; ++ while (is_valid_shell_identifier_character (*p, p == variable)) ++ p++; ++ variable_end = p; ++ while (g_ascii_isspace (*p)) ++ p++; ++ if (variable_end == variable || *p != '=') { ++ g_debug ("template environment file %s has invalid line '%s'\n", file, lines[i]); ++ continue; ++ } ++ *variable_end = '\0'; ++ p++; ++ while (g_ascii_isspace (*p)) ++ p++; ++ value = p; ++ ++ if (g_hash_table_lookup (variables, variable) == NULL) { ++ g_hash_table_insert (variables, ++ g_strdup (variable), ++ g_strdup (value)); ++ } ++ ++ } ++} ++ ++static void ++initialize_template_environment (User *user, ++ GHashTable *variables, ++ const char * const *files) ++{ ++ size_t i; ++ ++ g_hash_table_insert (variables, g_strdup ("HOME"), g_strdup (accounts_user_get_home_directory (ACCOUNTS_USER (user)))); ++ g_hash_table_insert (variables, g_strdup ("USER"), g_strdup (user_get_user_name (user))); ++ ++ if (files == NULL) ++ return; ++ ++ for (i = 0; files[i] != NULL; i++) { ++ load_template_environment_file (user, variables, files[i]); ++ } ++} ++ ++static void ++user_update_from_template (User *user) ++{ ++ g_autofree char *filename = NULL; ++ g_autoptr (GKeyFile) key_file = NULL; ++ g_autoptr (GError) error = NULL; ++ g_autoptr (GHashTable) template_variables = NULL; ++ g_auto (GStrv) template_environment_files = NULL; ++ gboolean key_file_loaded = FALSE; ++ const char * const *system_dirs[] = { ++ (const char *[]) { "/run", SYSCONFDIR, NULL }, ++ g_get_system_data_dirs (), ++ NULL ++ }; ++ g_autoptr (GPtrArray) dirs = NULL; ++ AccountType account_type; ++ const char *account_type_string; ++ size_t i, j; ++ g_autofree char *contents = NULL; ++ g_autofree char *expanded = NULL; ++ g_auto (GStrv) lines = NULL; ++ ++ if (user->template_loaded) ++ return; ++ ++ filename = g_build_filename (USERDIR, ++ accounts_user_get_user_name (ACCOUNTS_USER (user)), ++ NULL); ++ ++ account_type = accounts_user_get_account_type (ACCOUNTS_USER (user)); ++ if (account_type == ACCOUNT_TYPE_ADMINISTRATOR) ++ account_type_string = "administrator"; ++ else ++ account_type_string = "standard"; ++ ++ dirs = g_ptr_array_new (); ++ for (i = 0; system_dirs[i] != NULL; i++) { ++ for (j = 0; system_dirs[i][j] != NULL; j++) { ++ char *dir; ++ ++ dir = g_build_filename (system_dirs[i][j], ++ "accountsservice", ++ "user-templates", ++ NULL); ++ g_ptr_array_add (dirs, dir); ++ } ++ } ++ g_ptr_array_add (dirs, NULL); ++ ++ key_file = g_key_file_new (); ++ key_file_loaded = g_key_file_load_from_dirs (key_file, ++ account_type_string, ++ (const char **) dirs->pdata, ++ NULL, ++ G_KEY_FILE_KEEP_COMMENTS, ++ &error); ++ ++ if (!key_file_loaded) { ++ g_debug ("failed to load user template: %s", error->message); ++ return; ++ } ++ ++ template_variables = g_hash_table_new_full (g_str_hash, ++ g_str_equal, ++ g_free, ++ g_free); ++ ++ template_environment_files = g_key_file_get_string_list (key_file, ++ "Template", ++ "EnvironmentFiles", ++ NULL, ++ NULL); ++ ++ initialize_template_environment (user, template_variables, (const char * const *) template_environment_files); ++ ++ g_key_file_remove_group (key_file, "Template", NULL); ++ contents = g_key_file_to_data (key_file, NULL, NULL); ++ lines = g_strsplit (contents, "\n", -1); ++ ++ expanded = expand_template_variables (user, template_variables, contents); ++ ++ key_file_loaded = g_key_file_load_from_data (key_file, ++ expanded, ++ strlen (expanded), ++ G_KEY_FILE_KEEP_COMMENTS, ++ &error); ++ ++ if (key_file_loaded) ++ user_update_from_keyfile (user, key_file); ++ ++ user->template_loaded = key_file_loaded; ++} ++ + void + user_update_from_pwent (User *user, + struct passwd *pwent, + struct spwd *spent) + { + g_autofree gchar *real_name = NULL; + gboolean is_system_account; + const gchar *passwd; + gboolean locked; + PasswordMode mode; + AccountType account_type; + + g_object_freeze_notify (G_OBJECT (user)); + + if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') { + gchar *first_comma = NULL; + gchar *valid_utf8_name = NULL; + + if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) { + valid_utf8_name = pwent->pw_gecos; + first_comma = g_utf8_strchr (valid_utf8_name, -1, ','); + } + else { + g_warning ("User %s has invalid UTF-8 in GECOS field. " + "It would be a good thing to check /etc/passwd.", + pwent->pw_name ? pwent->pw_name : ""); + } + + if (first_comma) { + real_name = g_strndup (valid_utf8_name, +@@ -212,134 +469,150 @@ user_update_from_pwent (User *user, + accounts_user_set_locked (ACCOUNTS_USER (user), locked); + + if (passwd == NULL || passwd[0] != 0) { + mode = PASSWORD_MODE_REGULAR; + } + else { + mode = PASSWORD_MODE_NONE; + } + + if (spent) { + if (spent->sp_lstchg == 0) { + mode = PASSWORD_MODE_SET_AT_LOGIN; + } + + user->expiration_time = spent->sp_expire; + user->last_change_time = spent->sp_lstchg; + user->min_days_between_changes = spent->sp_min; + user->max_days_between_changes = spent->sp_max; + user->days_to_warn = spent->sp_warn; + user->days_after_expiration_until_lock = spent->sp_inact; + user->account_expiration_policy_known = TRUE; + } + + accounts_user_set_password_mode (ACCOUNTS_USER (user), mode); + is_system_account = !user_classify_is_human (accounts_user_get_uid (ACCOUNTS_USER (user)), + accounts_user_get_user_name (ACCOUNTS_USER (user)), + accounts_user_get_shell (ACCOUNTS_USER (user)), + passwd); + accounts_user_set_system_account (ACCOUNTS_USER (user), is_system_account); + ++ if (!user_has_cache_file (user)) ++ user_update_from_template (user); + g_object_thaw_notify (G_OBJECT (user)); + } + +-void ++static void + user_update_from_keyfile (User *user, + GKeyFile *keyfile) + { + gchar *s; + +- g_object_freeze_notify (G_OBJECT (user)); +- + s = g_key_file_get_string (keyfile, "User", "Language", NULL); + if (s != NULL) { + accounts_user_set_language (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "XSession", NULL); + if (s != NULL) { + accounts_user_set_xsession (ACCOUNTS_USER (user), s); + + /* for backward compat */ + accounts_user_set_session (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Session", NULL); + if (s != NULL) { + accounts_user_set_session (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "SessionType", NULL); + if (s != NULL) { + accounts_user_set_session_type (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Email", NULL); + if (s != NULL) { + accounts_user_set_email (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Location", NULL); + if (s != NULL) { + accounts_user_set_location (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "PasswordHint", NULL); + if (s != NULL) { + accounts_user_set_password_hint (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + s = g_key_file_get_string (keyfile, "User", "Icon", NULL); + if (s != NULL) { + accounts_user_set_icon_file (ACCOUNTS_USER (user), s); + g_clear_pointer (&s, g_free); + } + + if (g_key_file_has_key (keyfile, "User", "SystemAccount", NULL)) { + gboolean system_account; + + system_account = g_key_file_get_boolean (keyfile, "User", "SystemAccount", NULL); + accounts_user_set_system_account (ACCOUNTS_USER (user), system_account); + } + + g_clear_pointer (&user->keyfile, g_key_file_unref); + user->keyfile = g_key_file_ref (keyfile); ++} ++ ++void ++user_update_from_cache (User *user) ++{ ++ g_autofree gchar *filename = NULL; ++ g_autoptr(GKeyFile) key_file = NULL; ++ ++ filename = g_build_filename (USERDIR, accounts_user_get_user_name (ACCOUNTS_USER (user)), NULL); ++ ++ key_file = g_key_file_new (); ++ ++ if (!g_key_file_load_from_file (key_file, filename, 0, NULL)) ++ return; ++ ++ g_object_freeze_notify (G_OBJECT (user)); ++ user_update_from_keyfile (user, key_file); + user_set_cached (user, TRUE); + user_set_saved (user, TRUE); +- + g_object_thaw_notify (G_OBJECT (user)); + } + + void + user_update_local_account_property (User *user, + gboolean local) + { + accounts_user_set_local_account (ACCOUNTS_USER (user), local); + } + + void + user_update_system_account_property (User *user, + gboolean system) + { + accounts_user_set_system_account (ACCOUNTS_USER (user), system); + } + + static void + user_save_to_keyfile (User *user, + GKeyFile *keyfile) + { + g_key_file_remove_group (keyfile, "User", NULL); + + if (accounts_user_get_email (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Email", accounts_user_get_email (ACCOUNTS_USER (user))); + + if (accounts_user_get_language (ACCOUNTS_USER (user))) + g_key_file_set_string (keyfile, "User", "Language", accounts_user_get_language (ACCOUNTS_USER (user))); + + if (accounts_user_get_session (ACCOUNTS_USER (user))) +@@ -509,60 +782,63 @@ user_extension_set_property (User *user, + if (!prev || !g_str_equal (printed, prev)) { + g_key_file_set_value (user->keyfile, interface->name, property->name, printed); + + /* Emit a change signal. Use invalidation + * because the data may not be world-readable. + */ + g_dbus_connection_emit_signal (g_dbus_method_invocation_get_connection (invocation), + NULL, /* destination_bus_name */ + g_dbus_method_invocation_get_object_path (invocation), + "org.freedesktop.DBus.Properties", "PropertiesChanged", + g_variant_new_parsed ("( %s, %a{sv}, [ %s ] )", + interface->name, NULL, property->name), + NULL); + + accounts_user_emit_changed (ACCOUNTS_USER (user)); + save_extra_data (user); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("()")); + } + + static void + user_extension_authentication_done (Daemon *daemon, + User *user, + GDBusMethodInvocation *invocation, + gpointer user_data) + { + GDBusInterfaceInfo *interface = user_data; + const gchar *method_name; + ++ if (!user_has_cache_file (user)) ++ user_update_from_template (user); ++ + method_name = g_dbus_method_invocation_get_method_name (invocation); + + if (g_str_equal (method_name, "Get")) + user_extension_get_property (user, daemon, interface, invocation); + else if (g_str_equal (method_name, "GetAll")) + user_extension_get_all_properties (user, daemon, interface, invocation); + else if (g_str_equal (method_name, "Set")) + user_extension_set_property (user, daemon, interface, invocation); + else + g_assert_not_reached (); + } + + static void + user_extension_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) + { + User *user = user_data; + GDBusInterfaceInfo *iface_info; + const gchar *annotation_name; + const gchar *action_id; + gint uid; + gint i; + + /* We don't allow method calls on extension interfaces, so we +diff --git a/src/user.h b/src/user.h +index b3b3380..eb81918 100644 +--- a/src/user.h ++++ b/src/user.h +@@ -30,58 +30,57 @@ + #include "types.h" + + G_BEGIN_DECLS + + #define TYPE_USER (user_get_type ()) + #define USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), TYPE_USER, User)) + #define IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), TYPE_USER)) + + typedef enum { + ACCOUNT_TYPE_STANDARD, + ACCOUNT_TYPE_ADMINISTRATOR, + #define ACCOUNT_TYPE_LAST ACCOUNT_TYPE_ADMINISTRATOR + } AccountType; + + typedef enum { + PASSWORD_MODE_REGULAR, + PASSWORD_MODE_SET_AT_LOGIN, + PASSWORD_MODE_NONE, + #define PASSWORD_MODE_LAST PASSWORD_MODE_NONE + } PasswordMode; + + /* local methods */ + + GType user_get_type (void) G_GNUC_CONST; + User * user_new (Daemon *daemon, + uid_t uid); + + void user_update_from_pwent (User *user, + struct passwd *pwent, + struct spwd *spent); +-void user_update_from_keyfile (User *user, +- GKeyFile *keyfile); ++void user_update_from_cache (User *user); + void user_update_local_account_property (User *user, + gboolean local); + void user_update_system_account_property (User *user, + gboolean system); + gboolean user_get_cached (User *user); + void user_set_cached (User *user, + gboolean cached); + void user_set_saved (User *user, + gboolean saved); + + void user_register (User *user); + void user_unregister (User *user); + void user_changed (User *user); + + void user_save (User *user); + + const gchar * user_get_user_name (User *user); + gboolean user_get_system_account (User *user); + gboolean user_get_local_account (User *user); + const gchar * user_get_object_path (User *user); + uid_t user_get_uid (User *user); + const gchar * user_get_shell (User *user); + + G_END_DECLS + + #endif +-- +2.27.0 + diff --git a/SOURCES/user-template b/SOURCES/user-template new file mode 100644 index 0000000..d36c8e9 --- /dev/null +++ b/SOURCES/user-template @@ -0,0 +1,13 @@ +# This file contains defaults for new users. To edit, first +# copy it to /etc/accountsservice/user-templates and make changes +# there +[Template] +EnvironmentFiles=/etc/os-release; + +[com.redhat.AccountsServiceUser.System] +id='${ID}' +version-id='${VERSION_ID}' + +[User] +Session=gnome +Icon=${HOME}/.face diff --git a/SPECS/accountsservice.spec b/SPECS/accountsservice.spec index 031fbb7..4548d9a 100644 --- a/SPECS/accountsservice.spec +++ b/SPECS/accountsservice.spec @@ -2,13 +2,14 @@ Name: accountsservice Version: 0.6.55 -Release: 1%{?dist} +Release: 2%{?dist} Summary: D-Bus interfaces for querying and manipulating user account information License: GPLv3+ URL: https://www.freedesktop.org/wiki/Software/AccountsService/ #VCS: git:git://git.freedesktop.org/accountsservice Source0: http://www.freedesktop.org/software/accountsservice/accountsservice-%{version}.tar.xz +Source1: user-template BuildRequires: gettext-devel BuildRequires: pkgconfig(dbus-1) @@ -31,6 +32,8 @@ Patch20001: 0001-daemon-if-no-local-users-check-if-machine-is-enrolle.patch Patch30001: 0001-lib-save-os-when-creating-user.patch +Patch40001: 0001-user-Introduce-user-templates-for-setting-default-se.patch + %description The accountsservice project provides a set of D-Bus interfaces for querying and manipulating user account information and an implementation @@ -65,6 +68,11 @@ files needed to build applications that use accountsservice-libs. %install %meson_install +mkdir -p $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates $RPM_BUILD_ROOT%{_sysconfdir}/accountsservice/user-templates +cp $RPM_SOURCE_DIR/user-template $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates/standard +cp $RPM_SOURCE_DIR/user-template $RPM_BUILD_ROOT%{_datadir}/accountsservice/user-templates/administrator + + %find_lang accounts-service %ldconfig_scriptlets libs @@ -81,6 +89,8 @@ files needed to build applications that use accountsservice-libs. %files -f accounts-service.lang %license COPYING %doc README.md AUTHORS +%dir %{_sysconfdir}/accountsservice/user-templates +%dir %{_sysconfdir}/accountsservice %{_sysconfdir}/dbus-1/system.d/org.freedesktop.Accounts.conf %{_libexecdir}/accounts-daemon %{_datadir}/dbus-1/interfaces/org.freedesktop.Accounts.xml @@ -88,6 +98,8 @@ files needed to build applications that use accountsservice-libs. %{_datadir}/dbus-1/system-services/org.freedesktop.Accounts.service %{_datadir}/polkit-1/actions/org.freedesktop.accounts.policy %{_datadir}/accountsservice/interfaces/com.redhat.AccountsServiceUser.System.xml +%{_datadir}/accountsservice/user-templates/administrator +%{_datadir}/accountsservice/user-templates/standard %{_datadir}/dbus-1/interfaces/com.redhat.AccountsServiceUser.System.xml %dir %{_localstatedir}/lib/AccountsService/ %dir %{_localstatedir}/lib/AccountsService/users @@ -107,6 +119,10 @@ files needed to build applications that use accountsservice-libs. %{_datadir}/gtk-doc/html/libaccountsservice/* %changelog +* Wed Aug 04 2021 Ray Strode - 0.6.55-2 +- Add support for user templates so user can specify default session + Resolves: #1812788 + * Fri Jan 15 2021 Ray Strode - 0.6.55-1 - Rebase to 0.6.55 Resolves: #1846376