URL: https://github.com/SSSD/sssd/pull/485
Author: justin-stephenson
 Title: #485:  Support alternative sources for the files provider 
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/485/head:pr485
git checkout pr485
From 11449444b049ff4c4633b924cba9b78643193983 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <[email protected]>
Date: Fri, 14 Jul 2017 16:08:37 -0400
Subject: [PATCH 1/3] CONFDB: Add passwd_files and group_files options
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add new options to the files provider allowing an administrator to
configure the files provider to read and monitor multiple or
non-standard passwd and group file sources. These options default to
/etc/passwd and /etc/group when unset.

Reviewed-by: Pavel Březina <[email protected]>
Reviewed-by: Jakub Hrozek <[email protected]>
---
 Makefile.am                               |  3 ++-
 src/confdb/confdb.h                       |  4 ++++
 src/config/SSSDConfig/__init__.py.in      |  6 +++++-
 src/config/cfg_rules.ini                  |  4 ++++
 src/config/etc/sssd.api.d/sssd-files.conf |  3 +++
 src/man/sssd-files.5.xml                  | 36 +++++++++++++++++++++++++++++--
 src/providers/files/files_init.c          |  1 +
 7 files changed, 53 insertions(+), 4 deletions(-)
 create mode 100644 src/config/etc/sssd.api.d/sssd-files.conf

diff --git a/Makefile.am b/Makefile.am
index e1bc4aecb..9c65aede4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -4575,7 +4575,8 @@ dist_sssdapiplugin_DATA = \
     src/config/etc/sssd.api.d/sssd-ldap.conf \
     src/config/etc/sssd.api.d/sssd-local.conf \
     src/config/etc/sssd.api.d/sssd-proxy.conf \
-    src/config/etc/sssd.api.d/sssd-simple.conf
+    src/config/etc/sssd.api.d/sssd-simple.conf \
+    src/config/etc/sssd.api.d/sssd-files.conf
 
 edit_cmd = $(SED) \
         -e 's|@sbindir[@]|$(sbindir)|g' \
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index c97a9b804..1d322aaac 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -242,6 +242,10 @@
 #define CONFDB_PROXY_FAST_ALIAS "proxy_fast_alias"
 #define CONFDB_PROXY_MAX_CHILDREN "proxy_max_children"
 
+/* Files Provider */
+#define CONFDB_FILES_PASSWD "passwd_files"
+#define CONFDB_FILES_GROUP "group_files"
+
 /* Secrets Service */
 #define CONFDB_SEC_CONF_ENTRY "config/secrets"
 #define CONFDB_SEC_CONTAINERS_NEST_LEVEL "containers_nest_level"
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index 857d56cb5..32b74e4c7 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -473,7 +473,11 @@ option_strings = {
     'proxy_fast_alias' : _('Whether to look up canonical group name from cache if possible'),
 
     # [provider/proxy/auth]
-    'proxy_pam_target' : _('PAM stack to use')
+    'proxy_pam_target' : _('PAM stack to use'),
+
+    # [provider/files]
+    'passwd_files' : _('Path of passwd file sources.'),
+    'group_files' : _('Path of group file sources.')
 }
 
 def striplist(l):
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
index 4e70bf7b6..551322780 100644
--- a/src/config/cfg_rules.ini
+++ b/src/config/cfg_rules.ini
@@ -404,6 +404,10 @@ option = dyndns_force_tcp
 option = dyndns_auth
 option = dyndns_server
 
+# files provider specific options
+option = passwd_files
+option = group_files
+
 # local provider specific options
 option = create_homedir
 option = remove_homedir
diff --git a/src/config/etc/sssd.api.d/sssd-files.conf b/src/config/etc/sssd.api.d/sssd-files.conf
new file mode 100644
index 000000000..2444d4924
--- /dev/null
+++ b/src/config/etc/sssd.api.d/sssd-files.conf
@@ -0,0 +1,3 @@
+[provider/files]
+passwd_files = str, None, false
+group_files = str, None, false
diff --git a/src/man/sssd-files.5.xml b/src/man/sssd-files.5.xml
index d44fffc03..59e1b6523 100644
--- a/src/man/sssd-files.5.xml
+++ b/src/man/sssd-files.5.xml
@@ -56,14 +56,46 @@
     <refsect1 id='configuration-options'>
         <title>CONFIGURATION OPTIONS</title>
         <para>
-            The files provider has no specific options of its own, however,
-            generic SSSD domain options can be set where applicable.
+            In addition to the options listed below, generic SSSD domain options
+            can be set where applicable.
             Refer to the section <quote>DOMAIN SECTIONS</quote> of the
             <citerefentry>
                 <refentrytitle>sssd.conf</refentrytitle>
                 <manvolnum>5</manvolnum>
             </citerefentry> manual page for details on the configuration
             of an SSSD domain.
+            <variablelist>
+                <varlistentry>
+                    <term>passwd_files (string)</term>
+                    <listitem>
+                        <para>
+                            Comma-separated list of one or multiple password
+                            filenames to be read and enumerated by the files
+                            provider, inotify monitor watches will be set on
+                            each file to detect changes dynamically.
+                        </para>
+                        <para>
+                            Default: /etc/passwd
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
+                    <term>group_files (string)</term>
+                    <listitem>
+                        <para>
+                            Comma-separated list of one or multiple group
+                            filenames to be read and enumerated by the files
+                            provider, inotify monitor watches will be set on
+                            each file to detect changes dynamically.
+                        </para>
+                        <para>
+                            Default: /etc/group
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+            </variablelist>
         </para>
     </refsect1>
 
diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c
index 8e5cd4cf9..b8a051c34 100644
--- a/src/providers/files/files_init.c
+++ b/src/providers/files/files_init.c
@@ -21,6 +21,7 @@
 
 #include "providers/data_provider/dp.h"
 #include "providers/files/files_private.h"
+#include "util/util.h"
 
 int sssm_files_init(TALLOC_CTX *mem_ctx,
                     struct be_ctx *be_ctx,

From 8bf784bb73160d114669fadbdab3d66a5c04cd71 Mon Sep 17 00:00:00 2001
From: Justin Stephenson <[email protected]>
Date: Mon, 17 Jul 2017 15:01:36 -0400
Subject: [PATCH 2/3] FILES: Handle files provider sources
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Setup watches on passwd and group files provided with the files provider
options passwd_files and group_files lists

Resolves:
https://pagure.io/SSSD/sssd/issue/3402

Reviewed-by: Pavel Březina <[email protected]>
Reviewed-by: Jakub Hrozek <[email protected]>
---
 src/providers/files/files_init.c    | 161 +++++++++++++++++---
 src/providers/files/files_ops.c     | 285 ++++++++++++++++++++++--------------
 src/providers/files/files_private.h |   8 +-
 3 files changed, 327 insertions(+), 127 deletions(-)

diff --git a/src/providers/files/files_init.c b/src/providers/files/files_init.c
index b8a051c34..746c04af1 100644
--- a/src/providers/files/files_init.c
+++ b/src/providers/files/files_init.c
@@ -23,6 +23,138 @@
 #include "providers/files/files_private.h"
 #include "util/util.h"
 
+#define DEFAULT_PASSWD_FILE "/etc/passwd"
+#define DEFAULT_GROUP_FILE "/etc/group"
+
+static errno_t files_init_file_sources(TALLOC_CTX *mem_ctx,
+                                       struct be_ctx *be_ctx,
+                                       const char ***_passwd_files,
+                                       const char ***_group_files)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    char *conf_passwd_files;
+    char *conf_group_files;
+    char **passwd_list = NULL;
+    char **group_list = NULL;
+    int num_passwd_files = 0;
+    int num_group_files = 0;
+    const char **passwd_files = NULL;
+    const char **group_files = NULL;
+    const char *dfl_passwd_files = NULL;
+    const char *env_group_files = NULL;
+    int i;
+    errno_t ret;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    dfl_passwd_files = getenv("SSS_FILES_PASSWD");
+    if (dfl_passwd_files) {
+        sss_log(SSS_LOG_ALERT,
+                "Defaulting to %s for the passwd file, "
+                "this should only be used for testing!\n",
+                dfl_passwd_files);
+    } else {
+        dfl_passwd_files = DEFAULT_PASSWD_FILE;
+    }
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Using default passwd file: [%s].\n", dfl_passwd_files);
+
+    env_group_files = getenv("SSS_FILES_GROUP");
+    if (env_group_files) {
+        sss_log(SSS_LOG_ALERT,
+                "Defaulting to %s for the group file, "
+                "this should only be used for testing!\n",
+                env_group_files);
+    } else {
+        env_group_files = DEFAULT_GROUP_FILE;
+    }
+    DEBUG(SSSDBG_TRACE_FUNC,
+          "Using default group file: [%s].\n", DEFAULT_GROUP_FILE);
+
+    ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path,
+                            CONFDB_FILES_PASSWD, dfl_passwd_files,
+                            &conf_passwd_files);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to retrieve confdb passwd files!\n");
+        goto done;
+    }
+
+    ret = confdb_get_string(be_ctx->cdb, tmp_ctx, be_ctx->conf_path,
+                            CONFDB_FILES_GROUP, env_group_files,
+                            &conf_group_files);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Failed to retrieve confdb group files!\n");
+        goto done;
+    }
+
+    ret = split_on_separator(tmp_ctx, conf_passwd_files, ',', true, true,
+                             &passwd_list, &num_passwd_files);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                "Failed to parse passwd list!\n");
+        goto done;
+    }
+
+    passwd_files = talloc_zero_array(tmp_ctx, const char *,
+                                     num_passwd_files + 1);
+    if (passwd_files == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < num_passwd_files; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Using passwd file: [%s].\n", passwd_list[i]);
+
+        passwd_files[i] = talloc_strdup(passwd_files, passwd_list[i]);
+        if (passwd_files[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    /* Retrieve list of group files */
+    ret = split_on_separator(tmp_ctx, conf_group_files, ',', true, true,
+                             &group_list, &num_group_files);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+                "Failed to parse group files!\n");
+        goto done;
+    }
+
+    group_files = talloc_zero_array(tmp_ctx, const char *,
+                                    num_group_files + 1);
+    if (group_files == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero_array() failed\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i = 0; i < num_group_files; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Using group file: [%s].\n", group_list[i]);
+        group_files[i] = talloc_strdup(group_files, group_list[i]);
+        if (group_files[i] == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
+
+    *_passwd_files = talloc_steal(mem_ctx, passwd_files);
+    *_group_files = talloc_steal(mem_ctx, group_files);
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
 int sssm_files_init(TALLOC_CTX *mem_ctx,
                     struct be_ctx *be_ctx,
                     struct data_provider *provider,
@@ -30,32 +162,27 @@ int sssm_files_init(TALLOC_CTX *mem_ctx,
                     void **_module_data)
 {
     struct files_id_ctx *ctx;
-    int ret;
-    const char *passwd_file = NULL;
-    const char *group_file = NULL;
-
-    /* So far this is mostly useful for tests */
-    passwd_file = getenv("SSS_FILES_PASSWD");
-    if (passwd_file == NULL) {
-        passwd_file = "/etc/passwd";
-    }
-
-    group_file = getenv("SSS_FILES_GROUP");
-    if (group_file == NULL) {
-        group_file = "/etc/group";
-    }
+    errno_t ret;
 
     ctx = talloc_zero(mem_ctx, struct files_id_ctx);
     if (ctx == NULL) {
         return ENOMEM;
     }
+
     ctx->be = be_ctx;
     ctx->domain = be_ctx->domain;
-    ctx->passwd_file = passwd_file;
-    ctx->group_file = group_file;
+
+    ret = files_init_file_sources(ctx, be_ctx,
+                                  &ctx->passwd_files,
+                                  &ctx->group_files);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize the passwd/group source files\n");
+        goto done;
+    }
 
     ctx->fctx = sf_init(ctx, be_ctx->ev,
-                        ctx->passwd_file, ctx->group_file,
+                        ctx->passwd_files,
+                        ctx->group_files,
                         ctx);
     if (ctx->fctx == NULL) {
         ret = ENOMEM;
diff --git a/src/providers/files/files_ops.c b/src/providers/files/files_ops.c
index b59a94252..a2a2798d3 100644
--- a/src/providers/files/files_ops.c
+++ b/src/providers/files/files_ops.c
@@ -44,6 +44,7 @@ struct files_ctx {
 
 static errno_t enum_files_users(TALLOC_CTX *mem_ctx,
                                 struct files_id_ctx *id_ctx,
+                                const char *passwd_file,
                                 struct passwd ***_users)
 {
     errno_t ret, close_ret;
@@ -53,12 +54,12 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx,
     FILE *pwd_handle = NULL;
     size_t n_users = 0;
 
-    pwd_handle = fopen(id_ctx->passwd_file, "r");
+    pwd_handle = fopen(passwd_file, "r");
     if (pwd_handle == NULL) {
         ret = errno;
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Cannot open passwd file %s [%d]\n",
-              id_ctx->passwd_file, ret);
+              passwd_file, ret);
         goto done;
     }
 
@@ -133,7 +134,7 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx,
             close_ret = errno;
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Cannot close passwd file %s [%d]\n",
-                  id_ctx->passwd_file, close_ret);
+                  passwd_file, close_ret);
         }
     }
     return ret;
@@ -141,6 +142,7 @@ static errno_t enum_files_users(TALLOC_CTX *mem_ctx,
 
 static errno_t enum_files_groups(TALLOC_CTX *mem_ctx,
                                  struct files_id_ctx *id_ctx,
+                                 const char *group_file,
                                  struct group ***_groups)
 {
     errno_t ret, close_ret;
@@ -150,12 +152,12 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx,
     size_t n_groups = 0;
     FILE *grp_handle = NULL;
 
-    grp_handle = fopen(id_ctx->group_file, "r");
+    grp_handle = fopen(group_file, "r");
     if (grp_handle == NULL) {
         ret = errno;
         DEBUG(SSSDBG_CRIT_FAILURE,
               "Cannot open group file %s [%d]\n",
-              id_ctx->group_file, ret);
+              group_file, ret);
         goto done;
     }
 
@@ -237,7 +239,7 @@ static errno_t enum_files_groups(TALLOC_CTX *mem_ctx,
             close_ret = errno;
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Cannot close group file %s [%d]\n",
-                  id_ctx->group_file, close_ret);
+                  group_file, close_ret);
         }
     }
     return ret;
@@ -446,35 +448,23 @@ static errno_t refresh_override_attrs(struct files_id_ctx *id_ctx,
     return ret;
 }
 
-static errno_t sf_enum_groups(struct files_id_ctx *id_ctx);
+static errno_t sf_enum_groups(struct files_id_ctx *id_ctx,
+                              const char *group_file);
 
-errno_t sf_enum_users(struct files_id_ctx *id_ctx)
+errno_t sf_enum_users(struct files_id_ctx *id_ctx,
+                      const char *passwd_file)
 {
     errno_t ret;
-    errno_t tret;
     TALLOC_CTX *tmp_ctx = NULL;
     struct passwd **users = NULL;
-    bool in_transaction = false;
 
     tmp_ctx = talloc_new(NULL);
     if (tmp_ctx == NULL) {
         return ENOMEM;
     }
 
-    ret = enum_files_users(tmp_ctx, id_ctx, &users);
-    if (ret != EOK) {
-        goto done;
-    }
-
-    ret = sysdb_transaction_start(id_ctx->domain->sysdb);
-    if (ret != EOK) {
-        goto done;
-    }
-    in_transaction = true;
-
-    /* remove previous cache contents */
-    /* FIXME - this is terribly inefficient */
-    ret = delete_all_users(id_ctx->domain);
+    ret = enum_files_users(tmp_ctx, id_ctx, passwd_file,
+                           &users);
     if (ret != EOK) {
         goto done;
     }
@@ -496,31 +486,8 @@ errno_t sf_enum_users(struct files_id_ctx *id_ctx)
               "override values might not be available.\n");
     }
 
-    ret = sysdb_transaction_commit(id_ctx->domain->sysdb);
-    if (ret != EOK) {
-        goto done;
-    }
-    in_transaction = false;
-
-    /* Covers the case when someone edits /etc/group, adds a group member and
-     * only then edits passwd and adds the user. The reverse is not needed,
-     * because member/memberof links are established when groups are saved.
-     */
-    ret = sf_enum_groups(id_ctx);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "Cannot refresh groups\n");
-        goto done;
-    }
-
     ret = EOK;
 done:
-    if (in_transaction) {
-        tret = sysdb_transaction_cancel(id_ctx->domain->sysdb);
-        if (tret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Cannot cancel transaction: %d\n", ret);
-        }
-    }
     talloc_free(tmp_ctx);
     return ret;
 }
@@ -698,13 +665,12 @@ static errno_t save_file_group(struct files_id_ctx *id_ctx,
     return ret;
 }
 
-static errno_t sf_enum_groups(struct files_id_ctx *id_ctx)
+static errno_t sf_enum_groups(struct files_id_ctx *id_ctx,
+                              const char *group_file)
 {
     errno_t ret;
-    errno_t tret;
     TALLOC_CTX *tmp_ctx = NULL;
     struct group **groups = NULL;
-    bool in_transaction = false;
     const char **cached_users = NULL;
 
     tmp_ctx = talloc_new(NULL);
@@ -712,7 +678,8 @@ static errno_t sf_enum_groups(struct files_id_ctx *id_ctx)
         return ENOMEM;
     }
 
-    ret = enum_files_groups(tmp_ctx, id_ctx, &groups);
+    ret = enum_files_groups(tmp_ctx, id_ctx, group_file,
+                            &groups);
     if (ret != EOK) {
         goto done;
     }
@@ -722,18 +689,6 @@ static errno_t sf_enum_groups(struct files_id_ctx *id_ctx)
         goto done;
     }
 
-    ret = sysdb_transaction_start(id_ctx->domain->sysdb);
-    if (ret != EOK) {
-        goto done;
-    }
-    in_transaction = true;
-
-    /* remove previous cache contents */
-    ret = delete_all_groups(id_ctx->domain);
-    if (ret != EOK) {
-        goto done;
-    }
-
     for (size_t i = 0; groups[i]; i++) {
         ret = save_file_group(id_ctx, groups[i], cached_users);
         if (ret != EOK) {
@@ -750,21 +705,8 @@ static errno_t sf_enum_groups(struct files_id_ctx *id_ctx)
               "override values might not be available.\n");
     }
 
-    ret = sysdb_transaction_commit(id_ctx->domain->sysdb);
-    if (ret != EOK) {
-        goto done;
-    }
-    in_transaction = false;
-
     ret = EOK;
 done:
-    if (in_transaction) {
-        tret = sysdb_transaction_cancel(id_ctx->domain->sysdb);
-        if (tret != EOK) {
-            DEBUG(SSSDBG_CRIT_FAILURE,
-                  "Cannot cancel transaction: %d\n", ret);
-        }
-    }
     talloc_free(tmp_ctx);
     return ret;
 }
@@ -783,21 +725,17 @@ static int sf_passwd_cb(const char *filename, uint32_t flags, void *pvt)
 {
     struct files_id_ctx *id_ctx;
     errno_t ret;
+    errno_t tret;
+    bool in_transaction = false;
 
     id_ctx = talloc_get_type(pvt, struct files_id_ctx);
     if (id_ctx == NULL) {
-        return EINVAL;
+        ret = EINVAL;
+        goto done;
     }
 
     DEBUG(SSSDBG_TRACE_FUNC, "passwd notification\n");
 
-    if (strcmp(filename, id_ctx->passwd_file) != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Wrong file, expected %s, got %s\n",
-              id_ctx->passwd_file, filename);
-        return EINVAL;
-    }
-
     id_ctx->updating_passwd = true;
     dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain);
 
@@ -805,11 +743,64 @@ static int sf_passwd_cb(const char *filename, uint32_t flags, void *pvt)
     dp_sbus_reset_users_memcache(id_ctx->be->provider);
     dp_sbus_reset_initgr_memcache(id_ctx->be->provider);
 
-    ret = sf_enum_users(id_ctx);
+    ret = sysdb_transaction_start(id_ctx->domain->sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+    in_transaction = true;
+
+    ret = delete_all_users(id_ctx->domain);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* All users were deleted, therefore we need to enumerate each file again */
+    for (size_t i = 0; id_ctx->passwd_files[i] != NULL; i++) {
+        ret = sf_enum_users(id_ctx, id_ctx->passwd_files[i]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Cannot enumerate users\n");
+            goto done;
+        }
+    }
+
+    /* Covers the case when someone edits /etc/group, adds a group member and
+     * only then edits passwd and adds the user. The reverse is not needed,
+     * because member/memberof links are established when groups are saved.
+     */
+    ret = delete_all_groups(id_ctx->domain);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* All groups were deleted, therefore we need to enumerate each file again */
+    for (size_t i = 0; id_ctx->group_files[i] != NULL; i++) {
+        ret = sf_enum_groups(id_ctx, id_ctx->group_files[i]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Cannot enumerate groups\n");
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_commit(id_ctx->domain->sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+    in_transaction = false;
 
     id_ctx->updating_passwd = false;
     sf_cb_done(id_ctx);
     files_account_info_finished(id_ctx, BE_REQ_USER, ret);
+
+    ret = EOK;
+done:
+    if (in_transaction) {
+        tret = sysdb_transaction_cancel(id_ctx->domain->sysdb);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Cannot cancel transaction: %d\n", ret);
+        }
+    }
+
     return ret;
 }
 
@@ -817,21 +808,17 @@ static int sf_group_cb(const char *filename, uint32_t flags, void *pvt)
 {
     struct files_id_ctx *id_ctx;
     errno_t ret;
+    errno_t tret;
+    bool in_transaction = false;
 
     id_ctx = talloc_get_type(pvt, struct files_id_ctx);
     if (id_ctx == NULL) {
-        return EINVAL;
+        ret = EINVAL;
+        goto done;
     }
 
     DEBUG(SSSDBG_TRACE_FUNC, "group notification\n");
 
-    if (strcmp(filename, id_ctx->group_file) != 0) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Wrong file, expected %s, got %s\n",
-              id_ctx->group_file, filename);
-        return EINVAL;
-    }
-
     id_ctx->updating_groups = true;
     dp_sbus_domain_inconsistent(id_ctx->be->provider, id_ctx->domain);
 
@@ -839,11 +826,47 @@ static int sf_group_cb(const char *filename, uint32_t flags, void *pvt)
     dp_sbus_reset_groups_memcache(id_ctx->be->provider);
     dp_sbus_reset_initgr_memcache(id_ctx->be->provider);
 
-    ret = sf_enum_groups(id_ctx);
+    ret = sysdb_transaction_start(id_ctx->domain->sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+    in_transaction = true;
+
+    ret = delete_all_groups(id_ctx->domain);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* All groups were deleted, therefore we need to enumerate each file again */
+    for (size_t i = 0; id_ctx->group_files[i] != NULL; i++) {
+        ret = sf_enum_groups(id_ctx, id_ctx->group_files[i]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "Cannot enumerate groups\n");
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_commit(id_ctx->domain->sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+    in_transaction = false;
 
     id_ctx->updating_groups = false;
     sf_cb_done(id_ctx);
     files_account_info_finished(id_ctx, BE_REQ_GROUP, ret);
+
+    ret = EOK;
+
+done:
+    if (in_transaction) {
+        tret = sysdb_transaction_cancel(id_ctx->domain->sysdb);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Cannot cancel transaction: %d\n", ret);
+        }
+    }
+
     return ret;
 }
 
@@ -853,19 +876,62 @@ static void startup_enum_files(struct tevent_context *ev,
 {
     struct files_id_ctx *id_ctx = talloc_get_type(pvt, struct files_id_ctx);
     errno_t ret;
+    errno_t tret;
+    bool in_transaction = false;
 
     talloc_zfree(imm);
 
-    ret = sf_enum_users(id_ctx);
+    ret = sysdb_transaction_start(id_ctx->domain->sysdb);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Enumerating users failed, data might be inconsistent!\n");
+        goto done;
     }
+    in_transaction = true;
 
-    ret = sf_enum_groups(id_ctx);
+    ret = delete_all_users(id_ctx->domain);
     if (ret != EOK) {
-        DEBUG(SSSDBG_CRIT_FAILURE,
-              "Enumerating groups failed, data might be inconsistent!\n");
+        goto done;
+    }
+
+    ret = delete_all_groups(id_ctx->domain);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    for (size_t i = 0; id_ctx->passwd_files[i] != NULL; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Startup user enumeration of [%s]\n", id_ctx->passwd_files[i]);
+        ret = sf_enum_users(id_ctx, id_ctx->passwd_files[i]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Enumerating users failed, data might be inconsistent!\n");
+            goto done;
+        }
+    }
+
+    for (size_t i = 0; id_ctx->group_files[i] != NULL; i++) {
+        DEBUG(SSSDBG_TRACE_FUNC,
+              "Startup group enumeration of [%s]\n", id_ctx->group_files[i]);
+        ret = sf_enum_groups(id_ctx, id_ctx->group_files[i]);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Enumerating groups failed, data might be inconsistent!\n");
+            goto done;
+        }
+    }
+
+    ret = sysdb_transaction_commit(id_ctx->domain->sysdb);
+    if (ret != EOK) {
+        goto done;
+    }
+    in_transaction = false;
+
+done:
+    if (in_transaction) {
+        tret = sysdb_transaction_cancel(id_ctx->domain->sysdb);
+        if (tret != EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE,
+                  "Cannot cancel transaction: %d\n", ret);
+        }
     }
 }
 
@@ -884,22 +950,29 @@ static struct snotify_ctx *sf_setup_watch(TALLOC_CTX *mem_ctx,
 
 struct files_ctx *sf_init(TALLOC_CTX *mem_ctx,
                           struct tevent_context *ev,
-                          const char *passwd_file,
-                          const char *group_file,
+                          const char **passwd_files,
+                          const char **group_files,
                           struct files_id_ctx *id_ctx)
 {
     struct files_ctx *fctx;
     struct tevent_immediate *imm;
+    int i;
 
     fctx = talloc(mem_ctx, struct files_ctx);
     if (fctx == NULL) {
         return NULL;
     }
 
-    fctx->pwd_watch = sf_setup_watch(fctx, ev, passwd_file,
-                                     sf_passwd_cb, id_ctx);
-    fctx->grp_watch = sf_setup_watch(fctx, ev, group_file,
-                                     sf_group_cb, id_ctx);
+    for (i = 0; passwd_files[i]; i++) {
+        fctx->pwd_watch = sf_setup_watch(fctx, ev, passwd_files[i],
+                                         sf_passwd_cb, id_ctx);
+        }
+
+    for (i = 0; group_files[i]; i++) {
+        fctx->grp_watch = sf_setup_watch(fctx, ev, group_files[i],
+                                         sf_group_cb, id_ctx);
+    }
+
     if (fctx->pwd_watch == NULL || fctx->grp_watch == NULL) {
         talloc_free(fctx);
         return NULL;
diff --git a/src/providers/files/files_private.h b/src/providers/files/files_private.h
index a7d195c90..f44e6d458 100644
--- a/src/providers/files/files_private.h
+++ b/src/providers/files/files_private.h
@@ -39,8 +39,8 @@ struct files_id_ctx {
     struct sss_domain_info *domain;
     struct files_ctx *fctx;
 
-    const char *passwd_file;
-    const char *group_file;
+    const char **passwd_files;
+    const char **group_files;
 
     bool updating_passwd;
     bool updating_groups;
@@ -53,8 +53,8 @@ struct files_id_ctx {
 /* files_ops.c */
 struct files_ctx *sf_init(TALLOC_CTX *mem_ctx,
                           struct tevent_context *ev,
-                          const char *passwd_file,
-                          const char *group_file,
+                          const char **passwd_files,
+                          const char **group_files,
                           struct files_id_ctx *id_ctx);
 
 /* files_id.c */

From 10f072959df28180349471408067e2fece8303be Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <[email protected]>
Date: Mon, 26 Mar 2018 17:30:14 +0200
Subject: [PATCH 3/3] Tests: Add a test for the multiple files feature
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Adds an integration test for the new feature.

Reviewed-by: Pavel Březina <[email protected]>
---
 src/tests/intg/test_files_provider.py | 61 ++++++++++++++++++++++++++++++++++-
 1 file changed, 60 insertions(+), 1 deletion(-)

diff --git a/src/tests/intg/test_files_provider.py b/src/tests/intg/test_files_provider.py
index 41bfd8844..ce5c7b774 100644
--- a/src/tests/intg/test_files_provider.py
+++ b/src/tests/intg/test_files_provider.py
@@ -25,6 +25,7 @@
 import pwd
 import grp
 import pytest
+import tempfile
 
 import ent
 import sssd_id
@@ -33,7 +34,7 @@
                          call_sssd_enumeration,
                          call_sssd_getpwuid)
 from sssd_group import call_sssd_getgrnam, call_sssd_getgrgid
-from files_ops import passwd_ops_setup, group_ops_setup
+from files_ops import passwd_ops_setup, group_ops_setup, PasswdOps, GroupOps
 from util import unindent
 
 # Sync this with files_ops.c
@@ -59,6 +60,11 @@
                 dir='/home/ov/user1',
                 shell='/bin/ov_user1_shell')
 
+ALT_USER1 = dict(name='altuser1', passwd='x', uid=60001, gid=70001,
+                 gecos='User for tests from alt files',
+                 dir='/home/altuser1',
+                 shell='/bin/bash')
+
 CANARY_GR = dict(name='canary',
                  gid=300001,
                  mem=[])
@@ -79,6 +85,10 @@
                    gid=40000,
                    mem=[])
 
+ALT_GROUP1 = dict(name='alt_group1',
+                  gid=80001,
+                  mem=['alt_user1'])
+
 
 def start_sssd():
     """Start sssd and add teardown for stopping it and removing state"""
@@ -145,6 +155,38 @@ def files_domain_only(request):
     return None
 
 
[email protected]
+def files_multiple_sources(request):
+    _, alt_passwd_path = tempfile.mkstemp(prefix='altpasswd')
+    request.addfinalizer(lambda: os.unlink(alt_passwd_path))
+    alt_pwops = PasswdOps(alt_passwd_path)
+
+    _, alt_group_path = tempfile.mkstemp(prefix='altgroup')
+    request.addfinalizer(lambda: os.unlink(alt_group_path))
+    alt_grops = GroupOps(alt_group_path)
+
+    passwd_list = ",".join([os.environ["NSS_WRAPPER_PASSWD"], alt_passwd_path])
+    group_list = ",".join([os.environ["NSS_WRAPPER_GROUP"], alt_group_path])
+
+    conf = unindent("""\
+        [sssd]
+        domains             = files
+        services            = nss
+
+        [nss]
+        debug_level = 10
+
+        [domain/files]
+        id_provider = files
+        passwd_files = {passwd_list}
+        group_files = {group_list}
+        debug_level = 10
+    """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return alt_pwops, alt_grops
+
+
 @pytest.fixture
 def proxy_to_files_domain_only(request):
     conf = unindent("""\
@@ -1054,3 +1096,20 @@ def test_no_sssd_conf(add_user_with_canary, no_sssd_conf):
     res, user = sssd_getpwnam_sync(USER1["name"])
     assert res == NssReturnCode.SUCCESS
     assert user == USER1
+
+
+def test_multiple_passwd_group_files(add_user_with_canary,
+                                     add_group_with_canary,
+                                     files_multiple_sources):
+    """
+    Test that users and groups can be mirrored from multiple files
+    """
+    alt_pwops, alt_grops = files_multiple_sources
+    alt_pwops.useradd(**ALT_USER1)
+    alt_grops.groupadd(**ALT_GROUP1)
+
+    check_user(USER1)
+    check_user(ALT_USER1)
+
+    check_group(GROUP1)
+    check_group(ALT_GROUP1)
_______________________________________________
sssd-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to