URL: https://github.com/SSSD/sssd/pull/136
Author: spbnick
 Title: #136: Tlog integration WIP
Action: opened

PR body:
"""
@lslebodn, @pbrezina, this is the work-in-progress tlog integration patchset 
I'd like to work on with you.
This is not for merging as it is. We can go over it when we meet :)
"""

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/136/head:pr136
git checkout pr136
From 50a5ef8822e8573136f3e1a4841adc6f4a4fa566 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 20 Dec 2016 10:16:47 +0200
Subject: [PATCH 01/11] config: Add session_recording section

Add information on "session_recording" config section, having three
options: "scope", "users", and "groups".

The section is intended for disabling session recording ("scope = none",
default), enabling session recording for all users ("scope = all"), and
enabling it for some specific users and/or groups ("scope = some",
"users = <users>", "groups = <groups>").
---
 src/confdb/confdb.h          | 6 ++++++
 src/config/SSSDConfigTest.py | 6 ++++--
 src/config/etc/sssd.api.conf | 6 ++++++
 3 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index 9055048..0794451 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -159,6 +159,12 @@
 #define CONFDB_IFP_USER_ATTR_LIST "user_attributes"
 #define CONFDB_IFP_WILDCARD_LIMIT "wildcard_limit"
 
+/* Session Recording */
+#define CONFDB_SESSION_RECORDING_CONF_ENTRY "config/session_recording"
+#define CONFDB_SESSION_RECORDING_SCOPE "scope"
+#define CONFDB_SESSION_RECORDING_USERS "users"
+#define CONFDB_SESSION_RECORDING_GROUPS "groups"
+
 /* Domains */
 #define CONFDB_DOMAIN_PATH_TMPL "config/domain/%s"
 #define CONFDB_DOMAIN_BASEDN "cn=domain,cn=config"
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index 0da5d63..c4dc65a 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -1354,7 +1354,8 @@ def testNewConfig(self):
             'ssh',
             'pac',
             'ifp',
-            'secrets']
+            'secrets',
+            'session_recording']
         for section in control_list:
             self.assertTrue(sssdconfig.has_section(section),
                             "Section [%s] missing" %
@@ -1448,7 +1449,8 @@ def testListServices(self):
             'ssh',
             'pac',
             'ifp',
-            'secrets']
+            'secrets',
+            'session_recording']
         service_list = sssdconfig.list_services()
         for service in control_list:
             self.assertTrue(service in service_list,
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
index 5654006..7fea58c 100644
--- a/src/config/etc/sssd.api.conf
+++ b/src/config/etc/sssd.api.conf
@@ -111,6 +111,12 @@ forward_headers = list, None, false
 username = str, None, false
 password = str, None, false
 
+[session_recording]
+# Session recording service
+scope = str, None, false
+users = list, str, false
+groups = list, str, false
+
 [provider]
 #Available provider types
 id_provider = str, None, true

From 08a412dcdd332311cd59e6138563f088de86d7f8 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 11 Aug 2016 14:15:55 +0300
Subject: [PATCH 02/11] BUILD: Support configuring session recording shell

Add support for specifying the shell used for recording user sessions,
at configure time.
---
 configure.ac       |  1 +
 src/conf_macros.m4 | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/configure.ac b/configure.ac
index 2915046..b9ed9e6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -139,6 +139,7 @@ WITH_SEMANAGE
 WITH_AD_GPO_DEFAULT
 WITH_GPO_CACHE_PATH
 WITH_NOLOGIN_SHELL
+WITH_SESSION_RECORDING_SHELL
 WITH_APP_LIBS
 WITH_SUDO
 WITH_SUDO_LIB_PATH
diff --git a/src/conf_macros.m4 b/src/conf_macros.m4
index 427b0e0..5422a57 100644
--- a/src/conf_macros.m4
+++ b/src/conf_macros.m4
@@ -592,6 +592,22 @@ AC_DEFUN([WITH_NOLOGIN_SHELL],
     AC_DEFINE_UNQUOTED(NOLOGIN_SHELL, "$nologin_shell", [The shell used to deny access to users])
   ])
 
+AC_DEFUN([WITH_SESSION_RECORDING_SHELL],
+  [ AC_ARG_WITH([session-recording-shell],
+                [AC_HELP_STRING([--with-session-recording-shell=PATH],
+                                [The shell used to record user sessions [/usr/bin/tlog-rec]]
+                               )
+                ]
+               )
+    session_recording_shell="/usr/bin/tlog-rec"
+    if test x"$with_session_recording_shell" != x; then
+        nologin_shell=$with_session_recording_shell
+    fi
+    AC_SUBST(session_recording_shell)
+    AC_DEFINE_UNQUOTED(SESSION_RECORDING_SHELL, "$session_recording_shell",
+                       [The shell used to record user sessions])
+  ])
+
 AC_ARG_ENABLE([all-experimental-features],
               [AS_HELP_STRING([--enable-all-experimental-features],
                               [build all experimental features])],

From aa1203c7ae6b5b0a030d2cf94df8fcb8f7bb2831 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 18:42:46 +0200
Subject: [PATCH 03/11] Fix func names in sysdb_views.c error messages

---
 src/db/sysdb_views.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
index 9dc48f5..2d2fac8 100644
--- a/src/db/sysdb_views.c
+++ b/src/db/sysdb_views.c
@@ -298,7 +298,7 @@ errno_t sysdb_invalidate_overrides(struct sysdb_ctx *sysdb)
     ret = ldb_search(sysdb->ldb, tmp_ctx, &res, base_dn, LDB_SCOPE_SUBTREE,
                      NULL, "%s", SYSDB_GC);
     if (ret != LDB_SUCCESS) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_search_entry failed.\n");
+        DEBUG(SSSDBG_OP_FAILURE, "ldb_search failed.\n");
         ret = sysdb_error_to_errno(ret);
         goto done;
     }
@@ -353,19 +353,23 @@ add_name_and_aliases_for_name_override(struct sss_domain_info *domain,
         ret = sysdb_attrs_add_string(attrs, SYSDB_DEFAULT_OVERRIDE_NAME,
                                      name_override);
         if (ret != EOK) {
-            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
             return ret;
         }
     }
 
     if (!domain->case_sensitive) {
         ret = sysdb_attrs_add_lc_name_alias(attrs, name_override);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
+            return ret;
+        }
     } else {
         ret = sysdb_attrs_add_string(attrs, SYSDB_NAME_ALIAS, name_override);
-    }
-    if (ret != EOK) {
-        DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_lc_name_alias failed.\n");
-        return ret;
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_string failed.\n");
+            return ret;
+        }
     }
 
     return EOK;
@@ -692,7 +696,7 @@ static errno_t safe_original_attributes(struct sss_domain_info *domain,
             ret = sysdb_attrs_add_val_safe(attrs, SYSDB_NAME_ALIAS,
                                            &el->values[c]);
             if (ret != EOK) {
-                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val failed.\n");
+                DEBUG(SSSDBG_OP_FAILURE, "sysdb_attrs_add_val_safe failed.\n");
                 goto done;
             }
         }
@@ -1332,7 +1336,7 @@ errno_t sysdb_add_overrides_to_object(struct sss_domain_info *domain,
                 ret = ldb_msg_add_steal_value(obj, attr_map[c].new_attr,
                                               &tmp_el->values[d]);
                 if (ret != LDB_SUCCESS) {
-                    DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_value failed.\n");
+                    DEBUG(SSSDBG_OP_FAILURE, "ldb_msg_add_steal_value failed.\n");
                     ret = sysdb_error_to_errno(ret);
                     goto done;
                 }

From 6d94a80492a98e33cf9b8b425f6361c710a85d4a Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 18:46:36 +0200
Subject: [PATCH 04/11] Rename nss_get_name_from_msg to sysdb_get_name_from_msg

Move nss_get_name_from_msg function from nss_utils.c to sysdb_views.c,
and rename it to sysdb_get_name_from_msg to make it available to all
responder, in anticipation of implementing session recording support.
---
 src/db/sysdb.h                         |  3 +++
 src/db/sysdb_views.c                   | 27 +++++++++++++++++++++++++++
 src/responder/nss/nss_private.h        |  4 ----
 src/responder/nss/nss_protocol_grent.c |  2 +-
 src/responder/nss/nss_protocol_pwent.c |  2 +-
 src/responder/nss/nss_utils.c          | 27 ---------------------------
 6 files changed, 32 insertions(+), 33 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 8a363d0..21f8188 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -617,6 +617,9 @@ uint64_t sss_view_ldb_msg_find_attr_as_uint64(struct sss_domain_info *dom,
                                               const char *attr_name,
                                               uint64_t default_value);
 
+const char *sysdb_get_name_from_msg(const struct sss_domain_info *domain,
+                                    const struct ldb_message *msg);
+
 /* Sysdb initialization.
  * call this function *only* once to initialize the database and get
  * the sysdb ctx */
diff --git a/src/db/sysdb_views.c b/src/db/sysdb_views.c
index 2d2fac8..2198bb1 100644
--- a/src/db/sysdb_views.c
+++ b/src/db/sysdb_views.c
@@ -1644,3 +1644,30 @@ const char *sss_view_ldb_msg_find_attr_as_string(struct sss_domain_info *dom,
     talloc_free(tmp_ctx);
     return val;
 }
+
+const char *
+sysdb_get_name_from_msg(const struct sss_domain_info *domain,
+                        const struct ldb_message *msg)
+{
+    const char *name;
+
+    /* If domain has a view associated we return overridden name
+     * if possible. */
+    if (DOM_HAS_VIEWS(domain)) {
+        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
+                                           NULL);
+        if (name != NULL) {
+            return name;
+        }
+    }
+
+    /* Otherwise we try to return name override from
+     * Default Truest View for trusted users. */
+    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
+    if (name != NULL) {
+        return name;
+    }
+
+    /* If no override is found we return the original name. */
+    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
+}
diff --git a/src/responder/nss/nss_private.h b/src/responder/nss/nss_private.h
index e63fbab..6086230 100644
--- a/src/responder/nss/nss_private.h
+++ b/src/responder/nss/nss_private.h
@@ -136,10 +136,6 @@ nss_setnetgrent_recv(struct tevent_req *req);
 
 /* Utils. */
 
-const char *
-nss_get_name_from_msg(struct sss_domain_info *domain,
-                      struct ldb_message *msg);
-
 int sized_output_name(TALLOC_CTX *mem_ctx,
                       struct resp_ctx *rctx,
                       const char *orig_name,
diff --git a/src/responder/nss/nss_protocol_grent.c b/src/responder/nss/nss_protocol_grent.c
index 7409e04..de7b4bc 100644
--- a/src/responder/nss/nss_protocol_grent.c
+++ b/src/responder/nss/nss_protocol_grent.c
@@ -41,7 +41,7 @@ nss_get_grent(TALLOC_CTX *mem_ctx,
     }
 
     /* Get fields. */
-    name = nss_get_name_from_msg(domain, msg);
+    name = sysdb_get_name_from_msg(domain, msg);
     gid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_GIDNUM, 0);
 
     if (name == NULL || gid == 0) {
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index 783b06a..0d1b1f9 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -225,7 +225,7 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
 
     /* Get fields. */
     upn = ldb_msg_find_attr_as_string(msg, SYSDB_UPN, NULL);
-    name = nss_get_name_from_msg(domain, msg);
+    name = sysdb_get_name_from_msg(domain, msg);
     gid = nss_get_gid(domain, msg);
     uid = sss_view_ldb_msg_find_attr_as_uint64(domain, msg, SYSDB_UIDNUM, 0);
 
diff --git a/src/responder/nss/nss_utils.c b/src/responder/nss/nss_utils.c
index 41081c9..75f0eb6 100644
--- a/src/responder/nss/nss_utils.c
+++ b/src/responder/nss/nss_utils.c
@@ -25,33 +25,6 @@
 #include "confdb/confdb.h"
 #include "responder/common/responder.h"
 
-const char *
-nss_get_name_from_msg(struct sss_domain_info *domain,
-                      struct ldb_message *msg)
-{
-    const char *name;
-
-    /* If domain has a view associated we return overridden name
-     * if possible. */
-    if (DOM_HAS_VIEWS(domain)) {
-        name = ldb_msg_find_attr_as_string(msg, OVERRIDE_PREFIX SYSDB_NAME,
-                                           NULL);
-        if (name != NULL) {
-            return name;
-        }
-    }
-
-    /* Otherwise we try to return name override from
-     * Default Truest View for trusted users. */
-    name = ldb_msg_find_attr_as_string(msg, SYSDB_DEFAULT_OVERRIDE_NAME, NULL);
-    if (name != NULL) {
-        return name;
-    }
-
-    /* If no override is found we return the original name. */
-    return ldb_msg_find_attr_as_string(msg, SYSDB_NAME, NULL);
-}
-
 int sized_output_name(TALLOC_CTX *mem_ctx,
                       struct resp_ctx *rctx,
                       const char *orig_name,

From 0a4f32b271d45860c6c7fe5059470584148e4a3e Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 18:51:13 +0200
Subject: [PATCH 05/11] Move enum cache_req_type to a separate header

Move the definition of enum cache_req_type from cache_req.h to a
separate header cache_req_type.h to avoid include loop, and allow
including it into the session recording interface to be used by the
cache_req module itself.
---
 Makefile.am                                     |  1 +
 src/responder/common/cache_req/cache_req.h      | 31 +-------------
 src/responder/common/cache_req/cache_req_type.h | 54 +++++++++++++++++++++++++
 3 files changed, 56 insertions(+), 30 deletions(-)
 create mode 100644 src/responder/common/cache_req/cache_req_type.h

diff --git a/Makefile.am b/Makefile.am
index 661e944..8f1495a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -639,6 +639,7 @@ dist_noinst_HEADERS = \
     src/responder/common/responder_packet.h \
     src/responder/common/responder_sbus.h \
     src/responder/common/cache_req/cache_req.h \
+    src/responder/common/cache_req/cache_req_type.h \
     src/responder/common/cache_req/cache_req_plugin.h \
     src/responder/common/cache_req/cache_req_private.h \
     src/responder/common/data_provider/rdp.h \
diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
index 7700091..fd3055b 100644
--- a/src/responder/common/cache_req/cache_req.h
+++ b/src/responder/common/cache_req/cache_req.h
@@ -24,36 +24,7 @@
 #include "util/util.h"
 #include "confdb/confdb.h"
 #include "responder/common/negcache.h"
-
-enum cache_req_type {
-    CACHE_REQ_USER_BY_NAME,
-    CACHE_REQ_USER_BY_UPN,
-    CACHE_REQ_USER_BY_ID,
-    CACHE_REQ_USER_BY_CERT,
-    CACHE_REQ_USER_BY_FILTER,
-
-    CACHE_REQ_GROUP_BY_NAME,
-    CACHE_REQ_GROUP_BY_ID,
-    CACHE_REQ_GROUP_BY_FILTER,
-
-    CACHE_REQ_INITGROUPS,
-    CACHE_REQ_INITGROUPS_BY_UPN,
-
-    CACHE_REQ_OBJECT_BY_SID,
-    CACHE_REQ_OBJECT_BY_NAME,
-    CACHE_REQ_OBJECT_BY_ID,
-
-    CACHE_REQ_ENUM_USERS,
-    CACHE_REQ_ENUM_GROUPS,
-    CACHE_REQ_ENUM_SVC,
-
-    CACHE_REQ_SVC_BY_NAME,
-    CACHE_REQ_SVC_BY_PORT,
-
-    CACHE_REQ_NETGROUP_BY_NAME,
-
-    CACHE_REQ_SENTINEL
-};
+#include "responder/common/cache_req/cache_req_type.h"
 
 /* Input data. */
 
diff --git a/src/responder/common/cache_req/cache_req_type.h b/src/responder/common/cache_req/cache_req_type.h
new file mode 100644
index 0000000..43f0744
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_type.h
@@ -0,0 +1,54 @@
+/*
+    Authors:
+        Pavel Březina <pbrez...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_REQ_TYPE_H_
+#define _CACHE_REQ_TYPE_H_
+
+enum cache_req_type {
+    CACHE_REQ_USER_BY_NAME,
+    CACHE_REQ_USER_BY_UPN,
+    CACHE_REQ_USER_BY_ID,
+    CACHE_REQ_USER_BY_CERT,
+    CACHE_REQ_USER_BY_FILTER,
+
+    CACHE_REQ_GROUP_BY_NAME,
+    CACHE_REQ_GROUP_BY_ID,
+    CACHE_REQ_GROUP_BY_FILTER,
+
+    CACHE_REQ_INITGROUPS,
+    CACHE_REQ_INITGROUPS_BY_UPN,
+
+    CACHE_REQ_OBJECT_BY_SID,
+    CACHE_REQ_OBJECT_BY_NAME,
+    CACHE_REQ_OBJECT_BY_ID,
+
+    CACHE_REQ_ENUM_USERS,
+    CACHE_REQ_ENUM_GROUPS,
+    CACHE_REQ_ENUM_SVC,
+
+    CACHE_REQ_SVC_BY_NAME,
+    CACHE_REQ_SVC_BY_PORT,
+
+    CACHE_REQ_NETGROUP_BY_NAME,
+
+    CACHE_REQ_SENTINEL
+};
+
+#endif /* _CACHE_REQ_TYPE_H_ */

From fecd87786ba215279df6fcab0d6d4c67d5325f6c Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 17 Jan 2017 09:18:00 +0200
Subject: [PATCH 06/11] Move struct cache_req_result to a separate header

Move the definition of struct cache_req_result from cache_req.h to a
separate header cache_req_result.h to avoid include loop, and allow
including it into the session recording interface to be used by the
cache_req module itself.
---
 Makefile.am                                       |  1 +
 src/responder/common/cache_req/cache_req.h        | 39 +-------------
 src/responder/common/cache_req/cache_req_result.h | 62 +++++++++++++++++++++++
 3 files changed, 64 insertions(+), 38 deletions(-)
 create mode 100644 src/responder/common/cache_req/cache_req_result.h

diff --git a/Makefile.am b/Makefile.am
index 8f1495a..e5e91e9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -639,6 +639,7 @@ dist_noinst_HEADERS = \
     src/responder/common/responder_packet.h \
     src/responder/common/responder_sbus.h \
     src/responder/common/cache_req/cache_req.h \
+    src/responder/common/cache_req/cache_req_result.h \
     src/responder/common/cache_req/cache_req_type.h \
     src/responder/common/cache_req/cache_req_plugin.h \
     src/responder/common/cache_req/cache_req_private.h \
diff --git a/src/responder/common/cache_req/cache_req.h b/src/responder/common/cache_req/cache_req.h
index fd3055b..7a95cdb 100644
--- a/src/responder/common/cache_req/cache_req.h
+++ b/src/responder/common/cache_req/cache_req.h
@@ -25,6 +25,7 @@
 #include "confdb/confdb.h"
 #include "responder/common/negcache.h"
 #include "responder/common/cache_req/cache_req_type.h"
+#include "responder/common/cache_req/cache_req_result.h"
 
 /* Input data. */
 
@@ -76,44 +77,6 @@ cache_req_data_svc(TALLOC_CTX *mem_ctx,
 
 /* Output data. */
 
-struct cache_req_result {
-    /**
-     * SSSD domain where the result was obtained.
-     */
-    struct sss_domain_info *domain;
-
-    /**
-     * Result from ldb lookup.
-     */
-    struct ldb_result *ldb_result;
-
-    /**
-     * Shortcuts into ldb_result. This shortens the code a little since
-     * callers usually don't don't need to work with ldb_result directly.
-     */
-    unsigned int count;
-    struct ldb_message **msgs;
-
-    /**
-     * If name was used as a lookup parameter, @lookup_name contains name
-     * normalized to @domain rules.
-     */
-    const char *lookup_name;
-
-    /**
-     * If true the result contain attributes of a well known object.
-     * Since this result is manually created it may not contain all
-     * requested attributes, depending on the plug-in.
-     */
-    bool well_known_object;
-
-    /* If this is a well known object, it may not be part of any particular
-     * SSSD domain, but still may be associated with a well known domain
-     * name such as "BUILTIN", or "LOCAL AUTHORITY".
-     */
-    const char *well_known_domain;
-};
-
 /**
  * Shallow copy of cache request result, limiting the result to a maximum
  * numbers of records.
diff --git a/src/responder/common/cache_req/cache_req_result.h b/src/responder/common/cache_req/cache_req_result.h
new file mode 100644
index 0000000..d5908c8
--- /dev/null
+++ b/src/responder/common/cache_req/cache_req_result.h
@@ -0,0 +1,62 @@
+/*
+    Authors:
+        Pavel Březina <pbrez...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef _CACHE_REQ_RESULT_H_
+#define _CACHE_REQ_RESULT_H_
+
+struct cache_req_result {
+    /**
+     * SSSD domain where the result was obtained.
+     */
+    struct sss_domain_info *domain;
+
+    /**
+     * Result from ldb lookup.
+     */
+    struct ldb_result *ldb_result;
+
+    /**
+     * Shortcuts into ldb_result. This shortens the code a little since
+     * callers usually don't don't need to work with ldb_result directly.
+     */
+    unsigned int count;
+    struct ldb_message **msgs;
+
+    /**
+     * If name was used as a lookup parameter, @lookup_name contains name
+     * normalized to @domain rules.
+     */
+    const char *lookup_name;
+
+    /**
+     * If true the result contain attributes of a well known object.
+     * Since this result is manually created it may not contain all
+     * requested attributes, depending on the plug-in.
+     */
+    bool well_known_object;
+
+    /* If this is a well known object, it may not be part of any particular
+     * SSSD domain, but still may be associated with a well known domain
+     * name such as "BUILTIN", or "LOCAL AUTHORITY".
+     */
+    const char *well_known_domain;
+};
+
+#endif /* _CACHE_REQ_RESULT_H_ */

From 24b78bd758c8ba8f3ece7f7735345bdfb31d9201 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Tue, 17 Jan 2017 09:35:37 +0200
Subject: [PATCH 07/11] Move searched domain name to struct cache_req_state

Instead of passing around the name of the domain being searched as
argument in cache_req.c, move it to struct cache_req_state to simplify
adding another request to the chain, which doesn't deal with domain
names.
---
 src/responder/common/cache_req/cache_req.c | 46 ++++++++++++++++++------------
 1 file changed, 28 insertions(+), 18 deletions(-)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index f546e61..20f4263 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -312,6 +312,7 @@ struct cache_req_state {
     struct cache_req *cr;
 
     /* work data */
+    char *domain_name;
     struct sss_domain_info *domain;
     struct sss_domain_info *selected_domain;
     struct cache_req_result **results;
@@ -325,13 +326,11 @@ static errno_t cache_req_add_result(struct cache_req_state *state,
 
 static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
                                        struct tevent_req *req,
-                                       struct cache_req *cr,
-                                       const char *domain);
+                                       struct cache_req *cr);
 
 static void cache_req_input_parsed(struct tevent_req *subreq);
 
-static errno_t cache_req_select_domains(struct tevent_req *req,
-                                        const char *domain);
+static errno_t cache_req_select_domains(struct tevent_req *req);
 
 static errno_t cache_req_next_domain(struct tevent_req *req);
 
@@ -359,6 +358,15 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
 
     state->ev = ev;
     state->dp_success = true;
+    if (domain == NULL) {
+        state->domain_name = NULL;
+    } else {
+        state->domain_name = talloc_strdup(state, domain);
+        if (state->domain_name == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+    }
     state->cr = cr = cache_req_create(state, rctx, data, ncache, midpoint);
     if (state->cr == NULL) {
         ret = ENOMEM;
@@ -375,12 +383,12 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = cache_req_process_input(state, req, cr, domain);
+    ret = cache_req_process_input(state, req, cr);
     if (ret != EOK) {
         goto done;
     }
 
-    ret = cache_req_select_domains(req, domain);
+    ret = cache_req_select_domains(req);
 
 done:
     if (ret == EOK) {
@@ -396,17 +404,19 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
 
 static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
                                        struct tevent_req *req,
-                                       struct cache_req *cr,
-                                       const char *domain)
+                                       struct cache_req *cr)
 {
+    struct cache_req_state *state;
     struct tevent_req *subreq;
 
+    state = tevent_req_data(req, struct cache_req_state);
+
     if (cr->data->name.input == NULL) {
         /* Input was not name, there is no need to process it further. */
         return EOK;
     }
 
-    if (cr->plugin->parse_name == false || domain != NULL) {
+    if (cr->plugin->parse_name == false || state->domain_name != NULL) {
         /* We do not want to parse the name. */
         return cache_req_set_name(cr, cr->data->name.input);
     }
@@ -431,14 +441,13 @@ static void cache_req_input_parsed(struct tevent_req *subreq)
     struct tevent_req *req;
     struct cache_req_state *state;
     char *name;
-    char *domain;
     bool maybe_upn;
     errno_t ret;
 
     req = tevent_req_callback_data(subreq, struct tevent_req);
     state = tevent_req_data(req, struct cache_req_state);
 
-    ret = sss_parse_inp_recv(subreq, state, &name, &domain);
+    ret = sss_parse_inp_recv(subreq, state, &name, &state->domain_name);
     switch (ret) {
     case EOK:
         ret = cache_req_set_name(state->cr, name);
@@ -454,32 +463,32 @@ static void cache_req_input_parsed(struct tevent_req *subreq)
             return;
         }
 
-        domain = NULL;
+        state->domain_name = NULL;
         break;
     default:
         tevent_req_error(req, ret);
         return;
     }
 
-    ret = cache_req_select_domains(req, domain);
+    ret = cache_req_select_domains(req);
     if (ret != EAGAIN) {
         tevent_req_error(req, ret);
         return;
     }
 }
 
-static errno_t cache_req_select_domains(struct tevent_req *req,
-                                        const char *domain)
+static errno_t cache_req_select_domains(struct tevent_req *req)
 {
     struct cache_req_state *state = NULL;
 
     state = tevent_req_data(req, struct cache_req_state);
 
-    if (domain != NULL) {
+    if (state->domain_name != NULL) {
         CACHE_REQ_DEBUG(SSSDBG_TRACE_FUNC, state->cr,
                         "Performing a single domain search\n");
 
-        state->domain = responder_get_domain(state->cr->rctx, domain);
+        state->domain = responder_get_domain(state->cr->rctx,
+                                             state->domain_name);
         if (state->domain == NULL) {
             return ERR_DOMAIN_NOT_FOUND;
         }
@@ -673,7 +682,8 @@ static void cache_req_done(struct tevent_req *subreq)
 
             if (cache_req_assume_upn(state->cr)) {
                 /* Try UPN now. */
-                ret = cache_req_select_domains(req, NULL);
+                state->domain_name = NULL;
+                ret = cache_req_select_domains(req);
                 goto done;
             }
 

From cc0c2f9e81bc0b62ad52049ee9bf1133eb2940ba Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 18:58:08 +0200
Subject: [PATCH 08/11] Simplify next_domain_flag reference in cache_req.c

---
 src/responder/common/cache_req/cache_req.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index 20f4263..7941c72 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -558,8 +558,7 @@ static errno_t cache_req_next_domain(struct tevent_req *req)
 
         /* we will continue with the following domain the next time */
         if (state->check_next) {
-            state->domain = get_next_domain(state->domain,
-                                            cr->plugin->get_next_domain_flags);
+            state->domain = get_next_domain(state->domain, next_domain_flag);
         }
 
         return EAGAIN;

From 8b8ee0ff34246c65a1eaddf867e15b315214ddab Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 19:10:25 +0200
Subject: [PATCH 09/11] Add session recording conf loading to responder

---
 Makefile.am                                   |  4 ++
 src/responder/common/responder.h              |  3 ++
 src/responder/common/responder_common.c       |  9 ++++
 src/responder/common/session_recording_conf.c | 72 +++++++++++++++++++++++++
 src/responder/common/session_recording_conf.h | 76 +++++++++++++++++++++++++++
 src/tests/cwrap/Makefile.am                   |  2 +
 6 files changed, 166 insertions(+)
 create mode 100644 src/responder/common/session_recording_conf.c
 create mode 100644 src/responder/common/session_recording_conf.h

diff --git a/Makefile.am b/Makefile.am
index e5e91e9..cce022b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -524,6 +524,7 @@ SSSD_RESPONDER_OBJ = \
     src/responder/common/responder_packet.c \
     src/responder/common/responder_get_domains.c \
     src/responder/common/responder_utils.c \
+    src/responder/common/session_recording_conf.c \
     src/responder/common/data_provider/rdp_message.c \
     src/responder/common/data_provider/rdp_client.c \
     src/monitor/monitor_iface_generated.c \
@@ -643,6 +644,7 @@ dist_noinst_HEADERS = \
     src/responder/common/cache_req/cache_req_type.h \
     src/responder/common/cache_req/cache_req_plugin.h \
     src/responder/common/cache_req/cache_req_private.h \
+    src/responder/common/session_recording_conf.h \
     src/responder/common/data_provider/rdp.h \
     src/responder/pam/pamsrv.h \
     src/responder/pam/pam_helpers.h \
@@ -2037,6 +2039,7 @@ responder_socket_access_tests_SOURCES = \
     src/responder/common/responder_common.c \
     src/responder/common/responder_packet.c \
     src/responder/common/responder_cmd.c \
+    src/responder/common/session_recording_conf.c \
     src/responder/common/data_provider/rdp_message.c \
     src/responder/common/data_provider/rdp_client.c
 responder_socket_access_tests_CFLAGS = \
@@ -2121,6 +2124,7 @@ TEST_MOCK_RESP_OBJ = \
      src/responder/common/negcache_files.c \
      src/responder/common/negcache.c \
      src/responder/common/responder_common.c \
+     src/responder/common/session_recording_conf.c \
      src/responder/common/data_provider/rdp_message.c \
      src/responder/common/data_provider/rdp_client.c \
      src/responder/common/responder_utils.c \
diff --git a/src/responder/common/responder.h b/src/responder/common/responder.h
index d1fa532..46b78c2 100644
--- a/src/responder/common/responder.h
+++ b/src/responder/common/responder.h
@@ -36,6 +36,7 @@
 #include "data_provider/rdp.h"
 #include "sbus/sssd_dbus.h"
 #include "responder/common/negcache.h"
+#include "responder/common/session_recording_conf.h"
 #include "sss_client/sss_cli.h"
 
 extern hash_table_t *dp_requests;
@@ -126,6 +127,8 @@ struct resp_ctx {
     char *default_domain;
     char override_space;
 
+    struct session_recording_conf sr_conf;
+
     uint32_t cache_req_num;
 
     void *pvt_ctx;
diff --git a/src/responder/common/responder_common.c b/src/responder/common/responder_common.c
index 67922bf..7c54d91 100644
--- a/src/responder/common/responder_common.c
+++ b/src/responder/common/responder_common.c
@@ -1171,6 +1171,15 @@ int sss_process_init(TALLOC_CTX *mem_ctx,
         rctx->override_space = tmp[0];
     }
 
+    /* Read session_recording section */
+    ret = session_recording_conf_load(rctx, rctx->cdb, &rctx->sr_conf);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Failed loading session recording configuration: %s\n",
+              strerror(ret));
+        goto fail;
+    }
+
     ret = sss_monitor_init(rctx, rctx->ev, monitor_intf,
                            svc_name, svc_version, MT_SVC_SERVICE,
                            rctx, &rctx->last_request_time,
diff --git a/src/responder/common/session_recording_conf.c b/src/responder/common/session_recording_conf.c
new file mode 100644
index 0000000..3966833
--- /dev/null
+++ b/src/responder/common/session_recording_conf.c
@@ -0,0 +1,72 @@
+/*
+    SSSD
+
+    Responder session recording configuration management
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "responder/common/session_recording_conf.h"
+#include "util/debug.h"
+#include <string.h>
+#include <errno.h>
+
+errno_t session_recording_conf_load(TALLOC_CTX *mem_ctx,
+                                    struct confdb_ctx *cdb,
+                                    struct session_recording_conf *pconf)
+{
+    int ret;
+    char *str;
+
+    /* Read session_recording/scope option */
+    ret = confdb_get_string(cdb, mem_ctx, CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                            CONFDB_SESSION_RECORDING_SCOPE, "none", &str);
+    if (ret != EOK) goto done;
+    if (strcasecmp(str, "none") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_NONE;
+    } else if (strcasecmp(str, "some") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_SOME;
+    } else if (strcasecmp(str, "all") == 0) {
+        pconf->scope = SESSION_RECORDING_CONF_SCOPE_ALL;
+    } else {
+        DEBUG(SSSDBG_OP_FAILURE,
+              "Unknown value for session recording scope: %s\n",
+              str);
+        ret = EINVAL;
+        goto done;
+    }
+
+    /* Read session_recording/users option */
+    ret = confdb_get_string_as_list(cdb, mem_ctx,
+                                    CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                                    CONFDB_SESSION_RECORDING_USERS,
+                                    &pconf->users);
+    if (ret != EOK && ret != ENOENT) goto done;
+
+    /* Read session_recording/groups option */
+    ret = confdb_get_string_as_list(cdb, mem_ctx,
+                                    CONFDB_SESSION_RECORDING_CONF_ENTRY,
+                                    CONFDB_SESSION_RECORDING_GROUPS,
+                                    &pconf->groups);
+    if (ret != EOK && ret != ENOENT) goto done;
+
+    ret = EOK;
+done:
+    return ret;
+}
diff --git a/src/responder/common/session_recording_conf.h b/src/responder/common/session_recording_conf.h
new file mode 100644
index 0000000..4f658f8
--- /dev/null
+++ b/src/responder/common/session_recording_conf.h
@@ -0,0 +1,76 @@
+/*
+    SSSD
+
+    Responder session recording configuration management
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSS_RESPONDER_SESSION_RECORDING_CONF_H__
+#define __SSS_RESPONDER_SESSION_RECORDING_CONF_H__
+
+#include "confdb/confdb.h"
+#include "util/util_errors.h"
+
+/** Scope of users whose session should be recorded */
+enum session_recording_conf_scope {
+    SESSION_RECORDING_CONF_SCOPE_NONE,  /**< None, no users */
+    SESSION_RECORDING_CONF_SCOPE_SOME,  /**< Some users specified elsewhere */
+    SESSION_RECORDING_CONF_SCOPE_ALL    /**< All users */
+};
+
+/** Session recording configuration (from "session_recording" section) */
+struct session_recording_conf {
+    /**
+     * Session recording scope:
+     * whether to record nobody, everyone, or some users/groups
+     */
+    enum session_recording_conf_scope   scope;
+    /**
+     * NULL-terminated list of users whose session should be recorded.
+     * Can be NULL, meaning empty list. Only applicable if scope is "some".
+     */
+    char                              **users;
+    /**
+     * NULL-terminated list of groups, members of which should have their
+     * sessions recorded. Can be NULL, meaning empty list. Only applicable if
+     * scope is "some"
+     */
+    char                              **groups;
+};
+
+/**
+ * Load session recording configuration from configuration database.
+ *
+ * @param mem_ctx   Memory context to allocate data with.
+ * @param cdb       The configuration database connection object to retrieve
+ *                  data from.
+ * @param pconf     Location for the loaded session configuration.
+ *
+ * @return Status code:
+ *          ENOMEM - memory allocation failed,
+ *          EINVAL - configuration was invalid,
+ *          EIO - an I/O error occurred while communicating with the ConfDB.
+ */
+extern errno_t session_recording_conf_load(
+                                    TALLOC_CTX *mem_ctx,
+                                    struct confdb_ctx *cdb, 
+                                    struct session_recording_conf *pconf);
+
+#endif /* __SSS_RESPONDER_SESSION_RECORDING_CONF_H__ */
diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
index 8ca0026..85ae224 100644
--- a/src/tests/cwrap/Makefile.am
+++ b/src/tests/cwrap/Makefile.am
@@ -71,6 +71,7 @@ SSSD_RESPONDER_OBJ = \
     ../../../src/responder/common/responder_packet.c \
     ../../../src/responder/common/responder_get_domains.c \
     ../../../src/responder/common/responder_utils.c \
+    ../../../src/responder/common/session_recording_conf.c \
     ../../../src/responder/common/data_provider/rdp_message.c \
     ../../../src/responder/common/data_provider/rdp_client.c \
     ../../../src/monitor/monitor_iface_generated.c \
@@ -163,6 +164,7 @@ responder_common_tests_SOURCES =\
     ../../../src/responder/common/responder_common.c \
     ../../../src/responder/common/responder_packet.c \
     ../../../src/responder/common/responder_cmd.c \
+    ../../../src/responder/common/session_recording_conf.c \
     $(NULL)
 responder_common_tests_CFLAGS = \
     $(AM_CFLAGS) \

From 2d3b07262eab57766b95e176a96df77e42d08e9a Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 12 Jan 2017 19:21:38 +0200
Subject: [PATCH 10/11] Add basic session recording shell substitution

---
 Makefile.am                                   |   3 +
 src/db/sysdb.h                                |   2 +
 src/responder/common/cache_req/cache_req.c    | 104 ++++++++-
 src/responder/common/session_recording.c      | 315 ++++++++++++++++++++++++++
 src/responder/common/session_recording.h      |  85 +++++++
 src/responder/common/session_recording_conf.h |   2 +-
 src/responder/nss/nss_protocol_pwent.c        |   8 +-
 src/tests/cwrap/Makefile.am                   |   2 +
 8 files changed, 518 insertions(+), 3 deletions(-)
 create mode 100644 src/responder/common/session_recording.c
 create mode 100644 src/responder/common/session_recording.h

diff --git a/Makefile.am b/Makefile.am
index cce022b..7c37b5f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -524,6 +524,7 @@ SSSD_RESPONDER_OBJ = \
     src/responder/common/responder_packet.c \
     src/responder/common/responder_get_domains.c \
     src/responder/common/responder_utils.c \
+    src/responder/common/session_recording.c \
     src/responder/common/session_recording_conf.c \
     src/responder/common/data_provider/rdp_message.c \
     src/responder/common/data_provider/rdp_client.c \
@@ -644,6 +645,7 @@ dist_noinst_HEADERS = \
     src/responder/common/cache_req/cache_req_type.h \
     src/responder/common/cache_req/cache_req_plugin.h \
     src/responder/common/cache_req/cache_req_private.h \
+    src/responder/common/session_recording.h \
     src/responder/common/session_recording_conf.h \
     src/responder/common/data_provider/rdp.h \
     src/responder/pam/pamsrv.h \
@@ -2124,6 +2126,7 @@ TEST_MOCK_RESP_OBJ = \
      src/responder/common/negcache_files.c \
      src/responder/common/negcache.c \
      src/responder/common/responder_common.c \
+     src/responder/common/session_recording.c \
      src/responder/common/session_recording_conf.c \
      src/responder/common/data_provider/rdp_message.c \
      src/responder/common/data_provider/rdp_client.c \
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 21f8188..8142119 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -174,6 +174,7 @@
 #define SYSDB_OVERRIDE_GROUP_CLASS "groupOverride"
 #define SYSDB_OVERRIDE_DN "overrideDN"
 #define SYSDB_OVERRIDE_OBJECT_DN "overrideObjectDN"
+#define SYSDB_SESSION_RECORDING "sessionRecording"
 
 #define SYSDB_NEXTID_FILTER "("SYSDB_NEXTID"=*)"
 
@@ -226,6 +227,7 @@
                         SYSDB_OVERRIDE_DN, \
                         SYSDB_OVERRIDE_OBJECT_DN, \
                         SYSDB_DEFAULT_OVERRIDE_NAME, \
+                        SYSDB_SESSION_RECORDING, \
                         SYSDB_UUID, \
                         SYSDB_ORIG_DN, \
                         NULL}
diff --git a/src/responder/common/cache_req/cache_req.c b/src/responder/common/cache_req/cache_req.c
index 7941c72..a335b89 100644
--- a/src/responder/common/cache_req/cache_req.c
+++ b/src/responder/common/cache_req/cache_req.c
@@ -24,6 +24,7 @@
 #include <errno.h>
 
 #include "util/util.h"
+#include "responder/common/session_recording.h"
 #include "responder/common/cache_req/cache_req_private.h"
 #include "responder/common/cache_req/cache_req_plugin.h"
 
@@ -319,6 +320,10 @@ struct cache_req_state {
     size_t num_results;
     bool check_next;
     bool dp_success;
+
+    /* NULL-terminated list of results of looking up
+     * groups for which session recording is enabled */
+    struct cache_req_result **sr_groups;
 };
 
 static errno_t cache_req_add_result(struct cache_req_state *state,
@@ -330,6 +335,10 @@ static errno_t cache_req_process_input(TALLOC_CTX *mem_ctx,
 
 static void cache_req_input_parsed(struct tevent_req *subreq);
 
+static errno_t cache_req_sr_get_groups(struct tevent_req *req);
+
+static void cache_req_sr_get_groups_done(struct tevent_req *subreq);
+
 static errno_t cache_req_select_domains(struct tevent_req *req);
 
 static errno_t cache_req_next_domain(struct tevent_req *req);
@@ -388,6 +397,11 @@ struct tevent_req *cache_req_send(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
+    ret = cache_req_sr_get_groups(req);
+    if (ret != EOK) {
+        goto done;
+    }
+
     ret = cache_req_select_domains(req);
 
 done:
@@ -470,6 +484,82 @@ static void cache_req_input_parsed(struct tevent_req *subreq)
         return;
     }
 
+    ret = cache_req_sr_get_groups(req);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    ret = cache_req_select_domains(req);
+    if (ret != EAGAIN) {
+        tevent_req_error(req, ret);
+        return;
+    }
+}
+
+static errno_t cache_req_sr_get_groups(struct tevent_req *req)
+{
+    struct cache_req_state *state;
+    struct cache_req *cr;
+    struct resp_ctx *rctx;
+    struct session_recording_conf *sr_conf;
+    struct tevent_req *subreq;
+
+    state = tevent_req_data(req, struct cache_req_state);
+    cr = state->cr;
+    rctx = cr->rctx;
+    sr_conf = &rctx->sr_conf;
+
+    /* Don't do anything if session recording is not group-selective */
+    if (sr_conf->scope != SESSION_RECORDING_CONF_SCOPE_SOME ||
+        sr_conf->groups == NULL || *sr_conf->groups == NULL) {
+        return EOK;
+    }
+
+    /* Only handle requests for users */
+    switch (cr->data->type) {
+        case CACHE_REQ_USER_BY_NAME:
+        case CACHE_REQ_USER_BY_UPN:
+        case CACHE_REQ_USER_BY_ID:
+        case CACHE_REQ_USER_BY_CERT:
+        case CACHE_REQ_USER_BY_FILTER:
+        case CACHE_REQ_ENUM_USERS:
+            break;
+        default:
+            return EOK;
+    }
+
+    /* Create request for groups with session recording enabled */
+    subreq = session_recording_get_groups_send(state, rctx,
+                                               &state->sr_groups);
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE,
+              "Failed creating request for groups "
+              "with session recording enabled!\n");
+        return ENOMEM;
+    }
+
+    tevent_req_set_callback(subreq, cache_req_sr_get_groups_done, req);
+
+    return EAGAIN;
+}
+
+static void cache_req_sr_get_groups_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+
+    ret = session_recording_get_groups_recv(subreq);
+    talloc_free(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
     ret = cache_req_select_domains(req);
     if (ret != EAGAIN) {
         tevent_req_error(req, ret);
@@ -657,7 +747,19 @@ static void cache_req_done(struct tevent_req *subreq)
 
     switch (ret) {
     case EOK:
-        /* We got some data from this search. Save it. */
+        /* We got some data from this search */
+
+        /* Overlay it with session recording info */
+        ret = session_recording_overlay(state->cr->rctx,
+                                        state->cr->data->type,
+                                        state->selected_domain,
+                                        state->sr_groups,
+                                        result);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        /* Save it. */
         ret = cache_req_create_and_add_result(state, state->selected_domain,
                                               result,
                                               state->cr->data->name.lookup);
diff --git a/src/responder/common/session_recording.c b/src/responder/common/session_recording.c
new file mode 100644
index 0000000..0689f97
--- /dev/null
+++ b/src/responder/common/session_recording.c
@@ -0,0 +1,315 @@
+/*
+    SSSD
+
+    Responder session recording functions
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "responder/common/session_recording.h"
+#include "responder/common/cache_req/cache_req.h"
+#include "responder/nss/nss_private.h"
+
+/**
+ * Internal state of the asynchronous request to retrieve information on
+ * groups for which session recording is enabled.
+ */
+struct session_recording_get_groups_state {
+    /** Memory context to allocate results with */
+    TALLOC_CTX *mem_ctx;
+    /** Responder context to work within */
+    struct resp_ctx *rctx;
+    /** Pointer to name of the next group to retrieve info about */
+    char **pname;
+    /** Next location to store retrieved result pointer in */
+    struct cache_req_result **presult;
+};
+
+/**
+ * Handle a result of a group info request and submit another one, if there's
+ * any.
+ *
+ * @param subreq    The completed group info request.
+ */
+static void session_recording_get_groups_step(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct session_recording_get_groups_state *state =
+            tevent_req_data(req, struct session_recording_get_groups_state);
+    struct resp_ctx *rctx = state->rctx;
+
+    /* Get the lookup result, move onto next array element if we got one */
+    ret = cache_req_group_by_name_recv(state->mem_ctx, subreq,
+                                       state->presult);
+    talloc_zfree(subreq);
+    if (ret == EOK) {
+        state->presult++;
+    } else if (ret != ENOENT) {
+        goto done;
+    }
+
+    /* Move onto the next group name */
+    state->pname++;
+
+    /* If there are no more names */
+    if (*state->pname == NULL) {
+        tevent_req_done(req);
+        ret = EOK;
+        goto done;
+    }
+
+    /* Lookup next name */
+    /* TODO: Figure out proper caching parameters */
+    subreq = cache_req_group_by_name_send(state->mem_ctx, rctx->ev, rctx,
+                                          rctx->ncache, 0, NULL,
+                                          *state->pname);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    tevent_req_set_callback(subreq,
+                            session_recording_get_groups_step, req);
+    return;
+
+done:
+    tevent_req_error(req, ret);
+}
+
+struct tevent_req *session_recording_get_groups_send(
+                                    TALLOC_CTX *mem_ctx,
+                                    struct resp_ctx *rctx,
+                                    struct cache_req_result ***presult_list)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct tevent_req *subreq;
+    struct session_recording_get_groups_state *state;
+    char **pname;
+    size_t num;
+
+    /* Create the tracking "request" */
+    req = tevent_req_create(mem_ctx, &state,
+                            struct session_recording_get_groups_state);
+    if (req == NULL) {
+         return NULL;
+    }
+
+    /* Count groups to request */
+    num = 0;
+    pname = rctx->sr_conf.groups;
+    if (pname != NULL) {
+        for (; *pname != NULL; pname++, num++);
+    }
+
+    /* Allocate array for lookup results, include terminating NULL */
+    *presult_list = talloc_zero_array(mem_ctx,
+                                      struct cache_req_result *, num + 1);
+    if (*presult_list == NULL) {
+        ret = ENOMEM;
+        goto error;
+    }
+
+    /* TODO Handle zero groups */
+
+    /* Fill state */
+    state->mem_ctx = mem_ctx;
+    state->rctx = rctx;
+    state->pname = rctx->sr_conf.groups;
+    state->presult = *presult_list;
+
+    /* Start first group lookup */
+    subreq = cache_req_group_by_name_send(mem_ctx, rctx->ev, rctx,
+                                          rctx->ncache, 0, NULL,
+                                          *state->pname);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto error;
+    }
+    tevent_req_set_callback(subreq,
+                            session_recording_get_groups_step, req);
+
+    return req;
+
+error:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, rctx->ev);
+    return req;
+}
+
+errno_t session_recording_get_groups_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+errno_t session_recording_overlay(struct resp_ctx *rctx,
+                                  enum cache_req_type req_type,
+                                  struct sss_domain_info *domain,
+                                  struct cache_req_result **group_results,
+                                  struct ldb_result *result)
+{
+    errno_t ret;
+    size_t i;
+    struct ldb_message *msg;
+    const char *orig_name;
+    char **pname;
+    bool enabled;
+    TALLOC_CTX *tmp_ctx = NULL;
+    struct sysdb_attrs *attrs;
+
+    if (rctx == NULL || domain == NULL || result == NULL) {
+        return EINVAL;
+    }
+
+    /* Don't do anything if session recording is not selective */
+    if (rctx->sr_conf.scope != SESSION_RECORDING_CONF_SCOPE_SOME) {
+        return EOK;
+    }
+
+    /* Only handle requests for users */
+    switch (req_type) {
+        case CACHE_REQ_USER_BY_NAME:
+        case CACHE_REQ_USER_BY_UPN:
+        case CACHE_REQ_USER_BY_ID:
+        case CACHE_REQ_USER_BY_CERT:
+        case CACHE_REQ_USER_BY_FILTER:
+        case CACHE_REQ_ENUM_USERS:
+            break;
+        default:
+            return EOK;
+    }
+
+    /* For each result message */
+    for (i = 0; i < result->count; i++) {
+        msg = result->msgs[i];
+
+        /* Skip messages with present session recording attribute */
+        if (ldb_msg_find_element(msg, SYSDB_SESSION_RECORDING) != NULL) {
+            continue;
+        }
+
+        /* Get the entry user name */
+        orig_name = sysdb_get_name_from_msg(domain, msg);
+        /* If the entry has no name */
+        if (orig_name == NULL) {
+            continue;
+        }
+
+        /* Create temporary memory context for just this message */
+        tmp_ctx = talloc_new(NULL);
+        if (tmp_ctx == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        /* Start with the assumption recording is disabled */
+        enabled = false;
+
+        /* If we have a list of users to match against */
+        if (rctx->sr_conf.users != NULL) {
+            char *output_name;
+            /* Get the user name in output shape to match against */
+            output_name = sss_output_name(tmp_ctx, orig_name,
+                                          domain->case_preserve,
+                                          rctx->override_space);
+            if (output_name == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+            for (pname = rctx->sr_conf.users;
+                 *pname != NULL;
+                 pname++) {
+                if (strcmp(output_name, *pname) == 0) {
+                    enabled = true;
+                    break;
+                }
+            }
+        }
+
+        /* If we have a list of groups to match against */
+        if (group_results != NULL) {
+            struct cache_req_result **pgroup_result = group_results;
+            struct cache_req_result *group_result;
+            struct ldb_result *user_result;
+            size_t j;
+            size_t k;
+
+            /* Retrieve groups the user is member of */
+            ret = sysdb_initgroups_with_views(tmp_ctx, domain,
+                                              orig_name, &user_result);
+            if (ret != EOK) {
+                goto done;
+            }
+            /*
+             * For each result of a session recording-enabled group lookup
+             */
+            do {
+                group_result = *pgroup_result;
+                /* For each message (match) in the result */
+                for (j = 0; !enabled && j < group_result->count; j++) {
+                    /* For each user's group */
+                    for (k = 1; !enabled && k < user_result->count; k++) {
+                        /* If their DN's match */
+                        if (ldb_dn_compare(
+                                    group_result->msgs[j]->dn,
+                                    user_result->msgs[k]->dn) == 0) {
+                            enabled = true;
+                        }
+                    }
+                }
+            } while (!enabled && *++pgroup_result != NULL);
+        }
+
+        /* Store the attribute in the cache */
+        attrs = sysdb_new_attrs(tmp_ctx);
+        if (attrs == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+        ret = sysdb_attrs_add_bool(attrs, SYSDB_SESSION_RECORDING, enabled);
+        if (ret != EOK) {
+            goto done;
+        }
+        ret = sysdb_set_entry_attr(domain->sysdb, msg->dn, attrs, SYSDB_MOD_ADD);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        /* Store the attribute in the result */
+        ret = ldb_msg_add_string(msg, SYSDB_SESSION_RECORDING,
+                                 enabled ? "TRUE" : "FALSE");
+        if (ret != LDB_SUCCESS) {
+            ret = sysdb_error_to_errno(ret);
+            goto done;
+        }
+
+        /* Free the messages's temporary memory context */
+        talloc_free(tmp_ctx);
+        tmp_ctx = NULL;
+    }
+
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/responder/common/session_recording.h b/src/responder/common/session_recording.h
new file mode 100644
index 0000000..03d590a
--- /dev/null
+++ b/src/responder/common/session_recording.h
@@ -0,0 +1,85 @@
+/*
+    SSSD
+
+    Responder session recording functions
+
+    Authors:
+        Nikolai Kondrashov <nikolai.kondras...@redhat.com>
+
+    Copyright (C) 2016 Red Hat
+
+    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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SSS_RESPONDER_SESSION_RECORDING_H__
+#define __SSS_RESPONDER_SESSION_RECORDING_H__
+
+#include "confdb/confdb.h"
+#include "responder/common/responder.h"
+#include "responder/common/cache_req/cache_req_type.h"
+#include "responder/common/cache_req/cache_req_result.h"
+#include "responder/common/session_recording_conf.h"
+
+/**
+ * Start an asynchronous request to retrieve groups for which session
+ * recording is enabled, for an abstract responder context.
+ *
+ * @param mem_ctx       The memory context to allocate results and request
+ *                      state with.
+ * @param rctx          Abstract responder context to operate within.
+ * @param presult_list  Location for the pointer to a NULL-terminated array of
+ *                      pointers to retrieved group results, allocated with
+ *                      mem_ctx.
+ *
+ * @return The created asynchronous request, or NULL, if failed to allocate
+ *         memory for it.
+ */
+extern struct tevent_req *session_recording_get_groups_send(
+                                    TALLOC_CTX *mem_ctx,
+                                    struct resp_ctx *rctx,
+                                    struct cache_req_result ***presult_list);
+
+/**
+ * Retrieve status code for a finished request to retrieve groups for which
+ * session recording is enabled.
+ *
+ * @return Status code.
+ */
+extern errno_t session_recording_get_groups_recv(struct tevent_req *req);
+
+/**
+ * Overlay an LDB result and corresponding LDB contents for a specific cache
+ * request with session recording information, according to configuration. If
+ * session recording is disabled, or the request is not for a user, no changes
+ * are done.
+ *
+ * @param rctx          Responder context to work within.
+ * @param req_type      The type of request that produced the supplied result.
+ * @param domain        The domain the result was retrieved from.
+ * @param group_results A NULL-terminated list of results for groups with
+ *                      session recording enabled. Only valid if scope is
+ *                      "some". Can be NULL.
+ * @param result        The result to modify, and to use to locate and modify
+ *                      the original entry in the LDB.
+ *
+ * @return Status code.
+ */
+extern errno_t session_recording_overlay(
+                                struct resp_ctx *rctx,
+                                enum cache_req_type req_type,
+                                struct sss_domain_info *domain,
+                                struct cache_req_result **group_results,
+                                struct ldb_result *result);
+
+#endif /* __SSS_RESPONDER_SESSION_RECORDING_H__ */
diff --git a/src/responder/common/session_recording_conf.h b/src/responder/common/session_recording_conf.h
index 4f658f8..55dee8d 100644
--- a/src/responder/common/session_recording_conf.h
+++ b/src/responder/common/session_recording_conf.h
@@ -70,7 +70,7 @@ struct session_recording_conf {
  */
 extern errno_t session_recording_conf_load(
                                     TALLOC_CTX *mem_ctx,
-                                    struct confdb_ctx *cdb, 
+                                    struct confdb_ctx *cdb,
                                     struct session_recording_conf *pconf);
 
 #endif /* __SSS_RESPONDER_SESSION_RECORDING_CONF_H__ */
diff --git a/src/responder/nss/nss_protocol_pwent.c b/src/responder/nss/nss_protocol_pwent.c
index 0d1b1f9..b49e825 100644
--- a/src/responder/nss/nss_protocol_pwent.c
+++ b/src/responder/nss/nss_protocol_pwent.c
@@ -239,7 +239,13 @@ nss_get_pwent(TALLOC_CTX *mem_ctx,
     gecos = sss_view_ldb_msg_find_attr_as_string(domain, msg, SYSDB_GECOS,
                                                  NULL);
     homedir = nss_get_homedir(mem_ctx, nss_ctx, domain, msg, name, upn, uid);
-    shell = nss_get_shell_override(msg, nss_ctx, domain);
+    if (nss_ctx->rctx->sr_conf.scope == SESSION_RECORDING_CONF_SCOPE_ALL ||
+        (nss_ctx->rctx->sr_conf.scope == SESSION_RECORDING_CONF_SCOPE_SOME &&
+         ldb_msg_find_attr_as_bool(msg, SYSDB_SESSION_RECORDING, false))) {
+        shell = SESSION_RECORDING_SHELL;
+    } else {
+        shell = nss_get_shell_override(msg, nss_ctx, domain);
+    }
 
     /* Convert to sized strings. */
     ret = sized_output_name(mem_ctx, nss_ctx->rctx, name, domain, _name);
diff --git a/src/tests/cwrap/Makefile.am b/src/tests/cwrap/Makefile.am
index 85ae224..4d4b0af 100644
--- a/src/tests/cwrap/Makefile.am
+++ b/src/tests/cwrap/Makefile.am
@@ -71,6 +71,7 @@ SSSD_RESPONDER_OBJ = \
     ../../../src/responder/common/responder_packet.c \
     ../../../src/responder/common/responder_get_domains.c \
     ../../../src/responder/common/responder_utils.c \
+    ../../../src/responder/common/session_recording.c \
     ../../../src/responder/common/session_recording_conf.c \
     ../../../src/responder/common/data_provider/rdp_message.c \
     ../../../src/responder/common/data_provider/rdp_client.c \
@@ -164,6 +165,7 @@ responder_common_tests_SOURCES =\
     ../../../src/responder/common/responder_common.c \
     ../../../src/responder/common/responder_packet.c \
     ../../../src/responder/common/responder_cmd.c \
+    ../../../src/responder/common/session_recording.c \
     ../../../src/responder/common/session_recording_conf.c \
     $(NULL)
 responder_common_tests_CFLAGS = \

From 5a58ff84570e2b843a1cb3bf2c0bb18ba367d5f1 Mon Sep 17 00:00:00 2001
From: Nikolai Kondrashov <nikolai.kondras...@redhat.com>
Date: Thu, 11 Aug 2016 14:18:38 +0300
Subject: [PATCH 11/11] intg: Add session recording tests

---
 src/tests/intg/Makefile.am         |   1 +
 src/tests/intg/config.py.m4        |  27 ++--
 src/tests/intg/test_enumeration.py | 251 +++++++++++++++++++++++++++++++++++++
 3 files changed, 266 insertions(+), 13 deletions(-)

diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index e81e5ee..9b68275 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -31,6 +31,7 @@ config.py: config.py.m4
 	   -D "secdbpath=\`$(secdbpath)'" \
 	   -D "libexecpath=\`$(libexecdir)'" \
 	   -D "runstatedir=\`$(runstatedir)'" \
+	   -D "session_recording_shell=\`$(session_recording_shell)'" \
 	   $< > $@
 
 root:
diff --git a/src/tests/intg/config.py.m4 b/src/tests/intg/config.py.m4
index 65e17e5..9aa14dc 100644
--- a/src/tests/intg/config.py.m4
+++ b/src/tests/intg/config.py.m4
@@ -2,16 +2,17 @@
 Build configuration variables.
 """
 
-PREFIX              = "prefix"
-SYSCONFDIR          = "sysconfdir"
-NSS_MODULE_DIR      = PREFIX + "/lib"
-SSSDCONFDIR         = SYSCONFDIR + "/sssd"
-CONF_PATH           = SSSDCONFDIR + "/sssd.conf"
-DB_PATH             = "dbpath"
-PID_PATH            = "pidpath"
-PIDFILE_PATH        = PID_PATH + "/sssd.pid"
-LOG_PATH            = "logpath"
-MCACHE_PATH         = "mcpath"
-SECDB_PATH          = "secdbpath"
-LIBEXEC_PATH        = "libexecpath"
-RUNSTATEDIR         = "runstatedir"
+PREFIX                  = "prefix"
+SYSCONFDIR              = "sysconfdir"
+NSS_MODULE_DIR          = PREFIX + "/lib"
+SSSDCONFDIR             = SYSCONFDIR + "/sssd"
+CONF_PATH               = SSSDCONFDIR + "/sssd.conf"
+DB_PATH                 = "dbpath"
+PID_PATH                = "pidpath"
+PIDFILE_PATH            = PID_PATH + "/sssd.pid"
+LOG_PATH                = "logpath"
+MCACHE_PATH             = "mcpath"
+SECDB_PATH              = "secdbpath"
+LIBEXEC_PATH            = "libexecpath"
+RUNSTATEDIR             = "runstatedir"
+SESSION_RECORDING_SHELL = "session_recording_shell"
diff --git a/src/tests/intg/test_enumeration.py b/src/tests/intg/test_enumeration.py
index 5cb6c3e..b8c8010 100644
--- a/src/tests/intg/test_enumeration.py
+++ b/src/tests/intg/test_enumeration.py
@@ -687,3 +687,254 @@ def test_vetoed_shells(vetoed_shells):
                  shell="/bin/default")
         )
     )
+
+
+@pytest.fixture
+def session_recording_ldap(request, ldap_conn):
+    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+    ent_list.add_user("user1", 1001, 2001, loginShell="/bin/sh1")
+    ent_list.add_user("user2", 1002, 2002, loginShell="/bin/sh2")
+    ent_list.add_user("user3", 1003, 2003, loginShell="/bin/sh3")
+    ent_list.add_group("group1", 2001)
+    ent_list.add_group("group2", 2002)
+    ent_list.add_group("group3", 2003)
+    ent_list.add_group("empty_group", 2010)
+    ent_list.add_group("one_user_group", 2011, ["user1"])
+    ent_list.add_group("two_user_group", 2012, ["user1", "user2"])
+    ent_list.add_group("three_user_group", 2013, ["user1", "user2", "user3"])
+    create_ldap_fixture(request, ldap_conn, ent_list)
+
+
+@pytest.fixture
+def session_recording_none(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = none
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_none(session_recording_none):
+    """Test session recording "none" scope"""
+    ent.assert_passwd(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell="/bin/sh1"),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_all(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = all
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_all_nam(session_recording_all):
+    """Test session recording "all" scope with getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+    # Request a second time to verify positive caching
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+
+
+def test_session_recording_all_uid(session_recording_all):
+    """Test session recording "all" scope with getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+    # Request a second time to verify positive caching
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+
+
+def test_session_recording_all_ent(session_recording_all):
+    """Test session recording "all" scope with getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_empty(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_empty(session_recording_some_empty):
+    """Test session recording "some" scope with no users or groups"""
+    ent.assert_passwd(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell="/bin/sh1"),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_users(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            users = user1, user2
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_users_nam(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell="/bin/sh3"),
+    ))
+
+
+def test_session_recording_some_users_uid(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell="/bin/sh3"),
+    })
+
+
+def test_session_recording_some_users_ent(session_recording_some_users):
+    """Test session recording "some" scope with user list and getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_groups(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            groups = one_user_group, two_user_group
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_groups_nam(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwnam"""
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        user3=dict(name="user3", uid=1003, shell="/bin/sh3"),
+    ))
+
+
+def test_session_recording_some_groups_uid(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwuid"""
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+        1003:dict(name="user3", uid=1003, shell="/bin/sh3"),
+    })
+
+
+def test_session_recording_some_groups_ent(session_recording_some_groups):
+    """Test session recording "some" scope with group list and getpwent"""
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user3", uid=1003, shell="/bin/sh3"),
+        )
+    )
+
+
+@pytest.fixture
+def session_recording_some_users_and_groups(request, ldap_conn, session_recording_ldap):
+    conf = \
+        format_basic_conf(ldap_conn, SCHEMA_RFC2307) + \
+        unindent("""\
+            [session_recording]
+            scope = some
+            users = user3
+            groups = one_user_group
+        """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+
+
+def test_session_recording_some_users_and_groups_nam(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwnam
+    """
+    ent.assert_each_passwd_by_name(dict(
+        user1=dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        user2=dict(name="user2", uid=1002, shell="/bin/sh2"),
+        user3=dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    ))
+
+
+def test_session_recording_some_users_and_groups_uid(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwuid
+    """
+    ent.assert_each_passwd_by_uid({
+        1001:dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+        1002:dict(name="user2", uid=1002, shell="/bin/sh2"),
+        1003:dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+    })
+
+
+def test_session_recording_some_users_and_groups_ent(
+                                    session_recording_some_users_and_groups):
+    """
+    Test session recording "some" scope with user and group lists and getpwent
+    """
+    ent.assert_passwd_list(
+        ent.contains_only(
+            dict(name="user1", uid=1001, shell=config.SESSION_RECORDING_SHELL),
+            dict(name="user2", uid=1002, shell="/bin/sh2"),
+            dict(name="user3", uid=1003, shell=config.SESSION_RECORDING_SHELL),
+        )
+    )
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org

Reply via email to