Commit e6dc3b9c authored by Ray Strode's avatar Ray Strode Committed by Ray Strode
Browse files

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.

#63
parent b5697c2a
Pipeline #419613 passed with stages
in 2 minutes and 38 seconds
[Template]
#EnvironmentFiles=/etc/os-release;
[User]
Session=
Icon=${HOME}/.face
......@@ -52,3 +52,13 @@ if install_systemd_unit_dir
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'),
)
[Template]
#EnvironmentFiles=/etc/os-release;
[User]
Session=
Icon=${HOME}/.face
......@@ -326,15 +326,9 @@ entry_generator_cachedir (Daemon *daemon,
/* 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;
......
......@@ -27,6 +27,7 @@ deps = [
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')),
]
......
......@@ -71,6 +71,7 @@ struct User {
gchar *gecos;
gboolean account_expiration_policy_known;
gboolean cached;
gboolean template_loaded;
guint *extension_ids;
guint n_extension_ids;
......@@ -84,6 +85,7 @@ typedef struct UserClass
} 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));
......@@ -138,6 +140,261 @@ user_reset_icon_file (User *user)
}
}
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,
......@@ -242,17 +499,17 @@ user_update_from_pwent (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);
......@@ -313,9 +570,25 @@ user_update_from_keyfile (User *user,
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));
}
......@@ -539,6 +812,9 @@ user_extension_authentication_done (Daemon *daemon,
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"))
......
......@@ -59,8 +59,7 @@ User * user_new (Daemon *daemon,
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,
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment