On 03/21/2016 03:39 PM, Petr Cech wrote:
On 03/16/2016 12:37 PM, Pavel Březina wrote:
On 03/15/2016 04:40 PM, Petr Cech wrote:
On 03/10/2016 02:36 PM, Pavel Březina wrote:

[...]

There is whole patch set attached. I worked on tests (003 patch) last
couple days. I know that there are some test cases missing.

My big challenge is test_filter_rules_by_time(). I recognize that we
don't have function for load sudo rule by name. I mean how to obtain
struct sysdb_attrs *rule, for example by name of rule.
If I understand it rigt, we just prepare filter and we say (in attrs)
which fields of rule we want load. Is it right?

Well... write one or just use (name=$name) as filter :-)

Of course, I can do that. :-)

I have hoped there is way how to obtain such filter from
sysdb_get_sudo_filter()

For example:

# ret = sysdb_get_sudo_filter(test_ctx, TEST_RULE_NAME2,
# uid, groupnames, SYSDB_NAME,
# &filter);
# printf(">>> %s\n", filter);

Prints:
# >>>
(&(objectClass=sudoRule)(|(sudoUser=ALL)(name=defaults))(&(dataExpireTimestamp<=1458136945)))


I am little confused from that output. Does it mean that function
sysdb_get_sudo_filter() is only for user, uid and groupnames connected
filters?


Of course, if you see any missing important test case, please write me
:-)

Regards

Hi,

*Patch 1: SYSDB: Add documentation to sysdb_sudo_purge()*

Yea, we don't do this, unless it is a public interface. The best
documentation is the code itself since maintaining documentation
together with active development is a pain in (you know where) and you
can't rely on it since it's often sloppy and quite often it doesn't
mirror recent changes in the code.

But if you insist on this patch, then you should use xor i.e. "either a
or b".

I understand documentation is pain.

Actually I think that the behaviour of function sysdb_sudo_purge() is
little confusing.

I have read the code of this function and I've recognized there are
other private functions:
sysdb_sudo_purge_byfiler()
sysdb_sudo_purge_byname()
sysdb_sudo_purge_byrules()

Public function sysdb_sudo_purge() combines some of them but
exclusively. That's confusing, isn't it?

I do not insist on the use of patch (with documentation).


*Patch 2: SYSDB: Add new funtions into sysdb_sudo*

+errno_t
+sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
+                         const char *name,
+                         struct sysdb_attrs *attrs,
+                         int mod_op)
+{
+    errno_t ret;
+    struct ldb_dn *dn;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    dn = sysdb_sudo_rule_dn(tmp_ctx, domain, name);
+    NULL_CHECK(dn, ret, done);
+
+    ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
Temporary context is not needed here, you can use NULL instead and
talloc_free(dn) in the end.

I am not sure about it. I have tried it but it didn't work for me.


What didn't work for you?

errno_t
sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
                          const char *name,
                          struct sysdb_attrs *attrs,
                          int mod_op)
{
     struct ldb_dn *dn;
     errno_t ret;

     dn = sysdb_sudo_rule_dn(NULL, domain, name);
     if (dn == NULL) {
         return ENOMEM;
     }

     ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
     talloc_free(dn);
     return ret;
}

Comments to this issue is in another mail in this thread.

I have sent patch to samba project.
https://lists.samba.org/archive/samba-technical/2016-March/113068.html

I use my original workaround with tmp_ctx for now.

I would like to wait to answer from samba project and patch
this situation later.


*Patch 3: WIP: TESTS: Test of sysdb_search_sudo_rules*

#define TESTS_PATH "tp_" BASE_FILE_STEM
#define TEST_CONF_DB "test_sysdb_sudorules.ldb"

#define TEST_DOM_NAME "test_domain.test"

#define TEST_CACHE_SUDO_TIMEOUT 20

#define TEST_USER1 "young_user"
#define TEST_UID1 1001
#define TEST_HOMEDIR1 "/home/" TEST_USER1

#define TEST_USER2 "little_user"
#define TEST_UID2 1002
#define TEST_HOMEDIR2 "/home/" TEST_USER2

#define TEST_USER3 "big_user"
#define TEST_UID3 1003
#define TEST_HOMEDIR3 "/home/" TEST_USER3

#define TEST_USER_NON_EXIST "no_user"

Unused.

If you mean TEST_USER_NON_EXIST, it is used in function
test_get_sudo_user_info_no_exist().


#define TEST_GROUP_NAME "test_sudo_group"
#define TEST_GID 10001

#define TEST_SHELL "/bin/bash"

#define TEST_RULE_NAME1  "first_rule"
#define TEST_SUDO_HOST1  "blue_host." TEST_DOM_NAME
#define TEST_SUDO_USER1  TEST_USER1
#define TEST_RUN_AS_USER1  "user_like_root_1"

#define TEST_RULE_NAME2  "second_rule"
#define TEST_SUDO_HOST2  "red_host." TEST_DOM_NAME
#define TEST_SUDO_USER2  TEST_USER2
#define TEST_RUN_AS_USER2  "user_like_root_2"

#define TEST_RULE_NAME3  "third_rule"
#define TEST_SUDO_HOST3  "green_host." TEST_DOM_NAME
#define TEST_SUDO_USER3  TEST_USER3
#define TEST_RUN_AS_USER3  "user_like_root_3"

There is no need to #define homedir and shell, since it doesn't have to
be unique and it's useless in test anyway. It would be nice to avoid
these macros and group it into structure (users/groups/rules) like:

struct test_user {
     const char *name;
     uid_t uid;
     gid_t gid;
} users[] = {{"test-user1", 1001, 1001},
              {"test-user2", 1002, 1002}};

It reads better when there are multiple objects.

Addressed.


static void create_set_of_rules(struct sysdb_test_ctx *test_ctx)
{
    errno_t ret;

    test_ctx->rules = talloc_array(test_ctx, struct sysdb_attrs *, 2);

    test_ctx->rules[0] = sysdb_new_attrs(test_ctx);
    assert_non_null(test_ctx->rules[0]);
    create_rule(test_ctx->rules[0], test_ctx->tctx->dom,
                TEST_RULE_NAME2, TEST_SUDO_HOST2,
                TEST_RUN_AS_USER2, TEST_SUDO_USER2);

    test_ctx->rules[1] = sysdb_new_attrs(test_ctx);
    assert_non_null(test_ctx->rules[1]);
    create_rule(test_ctx->rules[1], test_ctx->tctx->dom,
                TEST_RULE_NAME3, TEST_SUDO_HOST3,
                TEST_RUN_AS_USER3, TEST_SUDO_USER3);

    ret = sysdb_sudo_store(test_ctx->tctx->dom, test_ctx->rules, 2);
    assert_int_equal(ret, EOK);
}

You are breaking talloc hierarchy here.


What you want is
test_ctx->rules = talloc_zero_array(test_ctx, ...)
test_ctx->rules[0] = sysdb_new_attrs(test_ctx->rules) // see the context
change

This is on multiple places, please check and fix it. I suggest you to
also read the Talloc tutorial, especially:
https://talloc.samba.org/talloc/doc/html/libtalloc__context.html

...to see why it is important to keep the hierarchy when possible.

Thanks, I understood now what talloc hierarchy is. I hope I've fixed all
places with broken talloc hierarchy.


To remove_* functions -- if setup/teardown is done right (sysdb/test_ctx
is created in setup and destroyed in teardown which it is) you don't
need them.

static int test_sysdb_setup(void **state, bool rules_exist)
{
    int ret;
    struct sysdb_test_ctx *test_ctx;
    struct sss_test_conf_param params[] = {
        { NULL, NULL },             /* Sentinel */
    };
    const char *val[2];
    val[1] = NULL;

    assert_true(leak_check_setup());
    check_leaks_push(global_talloc_context);

    test_ctx = talloc_zero(global_talloc_context, struct
sysdb_test_ctx);
    assert_non_null(test_ctx);

    test_dom_suite_setup(TESTS_PATH);

    test_ctx->tctx = create_dom_test_ctx(test_ctx, TESTS_PATH,
                                         TEST_CONF_DB, TEST_DOM_NAME,
                                         "ipa", params);

Remove params and pass NULL instead.

Addressed.


    assert_non_null(test_ctx->tctx);

    /* config */
    val[0] = "1";
    ret = confdb_add_param(test_ctx->tctx->confdb, true,
                           "config/domain/" TEST_DOM_NAME,
                           "pwd_expiration_warning", val);
    assert_int_equal(ret, EOK);

    /* SUDO RULES Configuration */
    val[0] = "0xFFF0";
    ret = confdb_add_param(test_ctx->tctx->confdb, true,
                           "config/sudo", "debug_level", val);
    assert_int_equal(ret, EOK);

    val[0] = "TEST_CACHE_SUDO_TIMEOUT";

This won't work :-) It would just set entry_cache_sudo_timeout =
TEST_CACHE... but you want an actual value there.

Addressed.


    ret = confdb_add_param(test_ctx->tctx->confdb, true,
                           "config/sudo", "entry_cache_sudo_timeout",
val);
    assert_int_equal(ret, EOK);

    create_groups(test_ctx->tctx->dom);
    create_users(test_ctx->tctx->dom);

    if (rules_exist) {


Additional line... and I believe 'create_rules' is a better name.

Addressed.


        create_set_of_rules(test_ctx);

        ret = sysdb_sudo_set_last_full_refresh(test_ctx->tctx->dom,
time(NULL));
        assert_int_equal(ret, EOK);

        talloc_zfree(test_ctx->rules[1]);
        talloc_zfree(test_ctx->rules[0]);
        talloc_zfree(test_ctx->rules);

If hierarchy is done, talloc_zfree(test_ctx->rules) is enough. Why do
you free it anyway? And it seems pretty much useless but I may have
missed something.

/* TODO */
Test patch is still WIP. I will slightly change logic that I will no
need to destroy test_ctx->rules[0-2]. There is still one commented test
and I would like write one more for filtering by name.


    }

    /* Rules */
    test_ctx->rules = NULL;

    check_leaks_push(test_ctx);
    *state = (void *) test_ctx;
    return 0;
}

I'm getting this compiler error:

/home/pbrezina/workspace/sssd/src/tests/cmocka/test_sysdb_sudo.c:85:1:
error: ‘static’ is not at beginning of declaration
[-Werror=old-style-declaration]
  void static remove_groups(struct sss_domain_info *domain)
  ^
/home/pbrezina/workspace/sssd/src/tests/cmocka/test_sysdb_sudo.c:114:1:
error: ‘static’ is not at beginning of declaration
[-Werror=old-style-declaration]
  void static remove_users(struct sss_domain_info *domain)
  ^

Addressed.


*Patch 4: REFACTOR: sss_cache*

Use memset in init_input_values(). Or even better drop the function and
use initializer 'struct input_values input = {0};'

Addressed.


static bool is_filter_valid(struct cache_tool_ctx *ctx,
                            struct input_values *values, int idb)
{
    bool ret = ((idb & INVALIDATE_USERS) && !ctx->user_filter) ||
        ((idb & INVALIDATE_GROUPS) && !ctx->group_filter) ||
        ((idb & INVALIDATE_NETGROUPS) && !ctx->netgroup_filter) ||
        ((idb & INVALIDATE_SERVICES) && !ctx->service_filter) ||
        ((idb & INVALIDATE_AUTOFSMAPS) && !ctx->autofs_filter) ||
        ((idb & INVALIDATE_SSH_HOSTS) && !ctx->ssh_host_filter) ||
        ((idb & INVALIDATE_SUDO_RULES) && !ctx->sudo_rule_filter) ||
         (values->user && !ctx->user_name) ||
         (values->group && !ctx->group_name) ||
         (values->netgroup && !ctx->netgroup_name) ||
         (values->service && !ctx->service_name) ||
         (values->map && !ctx->autofs_name) ||
         (values->ssh_host && !ctx->ssh_host_name) ||
         (values->sudo_rule && !ctx->sudo_rule_name);

    return ret;
}

Yak, there must be a way how to split this into multiple if's. Can you
try please? What is it supposed to return anyway? From the function name
I suppose you want a negation of ret because ret contains true if the
filter is actually not valid.

What I propose is:

bool is_filter_valid()
{
     if ((idb & INVALIDATE_USERS) && ctx->user_filter == NULL) {
         return false;
     }

     if ((idb & INVALIDATE_GROUPS) && ctx->group_filter == NULL) {
         return false;
     }

     ...

     if (values->sudo_rule != NULL && ctx->sudo_rule_name == NULL) {
         return false;
     }

     return true;
}

Addressed.


if (!is_filter_valid(ctx, &values, idb)) { // NOT is_filter_valid()
     DEBUG(SSSDBG_CRIT_FAILURE, "Construction of filters failed\n");
     ret = ENOMEM;
     goto fini;
}

Addressed.


*Patch 5: TOOL: Invalidation of sudo rules at sss_cache*

This looks good. I did not test it so far though.
_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org

https://lists.fedorahosted.org/admin/lists/sssd-devel@lists.fedorahosted.org





_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/admin/lists/sssd-devel@lists.fedorahosted.org



There is new patch set with fixed tests.

--
Petr^4 Čech
>From 41bd89180607e4037438d044c6c51e29939ebdd5 Mon Sep 17 00:00:00 2001
From: Petr Cech <pc...@redhat.com>
Date: Wed, 24 Feb 2016 09:12:41 -0500
Subject: [PATCH 1/4] SYSDB: Add new funtions into sysdb_sudo

This patch adds two new functions into public
API of sysdb_sudo:
* sysdb_search_sudo_rules
* sysdb_set_sudo_rule_attr

Resolves:
https://fedorahosted.org/sssd/ticket/2081
---
 src/db/sysdb_sudo.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/db/sysdb_sudo.h | 15 +++++++++
 2 files changed, 112 insertions(+)

diff --git a/src/db/sysdb_sudo.c b/src/db/sysdb_sudo.c
index 76116abacb20219f0c1dcdde755e8268e10fd293..50c0981dd1eeb8550bf8dbae63fad83ca2db5921 100644
--- a/src/db/sysdb_sudo.c
+++ b/src/db/sysdb_sudo.c
@@ -889,3 +889,100 @@ done:
 
     return ret;
 }
+
+errno_t sysdb_search_sudo_rules(TALLOC_CTX *mem_ctx,
+                                struct sss_domain_info *domain,
+                                const char *sub_filter,
+                                const char **attrs,
+                                size_t *_msgs_count,
+                                struct ldb_message ***_msgs)
+{
+    TALLOC_CTX *tmp_ctx;
+    size_t msgs_count;
+    struct ldb_message **msgs;
+    struct ldb_dn *dn;
+    char *filter;
+    int ret;
+
+    tmp_ctx = talloc_new(NULL);
+    NULL_CHECK(tmp_ctx, ret, done);
+
+    dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb, SYSDB_TMPL_CUSTOM_SUBTREE,
+                        SUDORULE_SUBDIR, domain->name);
+    if (dn == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to build base dn\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    if (sub_filter == NULL) {
+        filter = talloc_asprintf(tmp_ctx, "(%s)", SUDO_ALL_FILTER);
+    } else {
+        filter = talloc_asprintf(tmp_ctx, "(&%s%s)",
+                                 SUDO_ALL_FILTER, sub_filter);
+    }
+    if (filter == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, "Failed to build filter\n");
+        ret = ENOMEM;
+        goto done;
+    }
+
+    DEBUG(SSSDBG_TRACE_INTERNAL,
+          "Search sudo rules with filter: %s\n", filter);
+
+    ret = sysdb_search_entry(tmp_ctx, domain->sysdb, dn,
+                             LDB_SCOPE_SUBTREE, filter, attrs,
+                             &msgs_count, &msgs);
+
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_INTERNAL, "No such entry\n");
+        *_msgs = NULL;
+        *_msgs_count = 0;
+        goto done;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "Error: %d (%s)\n", ret, sss_strerror(ret));
+        goto done;
+    }
+
+    *_msgs_count = msgs_count;
+    *_msgs = talloc_steal(mem_ctx, msgs);
+
+    ret = EOK;
+
+done:
+    talloc_zfree(tmp_ctx);
+    return ret;
+}
+
+static struct ldb_dn *
+sysdb_sudo_rule_dn(TALLOC_CTX *mem_ctx,
+                  struct sss_domain_info *domain,
+                  const char *name)
+{
+    return sysdb_custom_dn(mem_ctx, domain, name, SUDORULE_SUBDIR);
+}
+
+errno_t
+sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
+                         const char *name,
+                         struct sysdb_attrs *attrs,
+                         int mod_op)
+{
+    errno_t ret;
+    struct ldb_dn *dn;
+    TALLOC_CTX *tmp_ctx;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    dn = sysdb_sudo_rule_dn(tmp_ctx, domain, name);
+    NULL_CHECK(dn, ret, done);
+
+    ret = sysdb_set_entry_attr(domain->sysdb, dn, attrs, mod_op);
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
\ No newline at end of file
diff --git a/src/db/sysdb_sudo.h b/src/db/sysdb_sudo.h
index 515f45ab8b8f51cf7b1d27c1ba28ed8182bce6c0..7ada58b7393e295bdf7277a9aea60010c38aee6b 100644
--- a/src/db/sysdb_sudo.h
+++ b/src/db/sysdb_sudo.h
@@ -122,4 +122,19 @@ sysdb_sudo_store(struct sss_domain_info *domain,
                  struct sysdb_attrs **rules,
                  size_t num_rules);
 
+
+errno_t
+sysdb_search_sudo_rules(TALLOC_CTX *mem_ctx,
+                        struct sss_domain_info *domain,
+                        const char *sub_filter,
+                        const char **attrs,
+                        size_t *_msgs_count,
+                        struct ldb_message ***_msgs);
+
+errno_t
+sysdb_set_sudo_rule_attr(struct sss_domain_info *domain,
+                         const char *name,
+                         struct sysdb_attrs *attrs,
+                         int mod_op);
+
 #endif /* _SYSDB_SUDO_H_ */
-- 
2.5.5

>From 1546c0485d398a3f22b47a5bf10338b883aa17ff Mon Sep 17 00:00:00 2001
From: Petr Cech <pc...@redhat.com>
Date: Thu, 25 Feb 2016 03:28:13 -0500
Subject: [PATCH 2/4] TESTS: Test of sysdb_search_sudo_rules

There are tests functions of sysdb_sudo_rules.

Resolves:
https://fedorahosted.org/sssd/ticket/2081
---
 Makefile.am                        |  18 ++
 src/tests/cmocka/test_sysdb_sudo.c | 587 +++++++++++++++++++++++++++++++++++++
 2 files changed, 605 insertions(+)
 create mode 100644 src/tests/cmocka/test_sysdb_sudo.c

diff --git a/Makefile.am b/Makefile.am
index e2d2c38aea59389d8c0d5887b42e22e30b6b8222..0a714cb56afe35b18424c3fbca0e560676233784 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -231,6 +231,7 @@ if HAVE_CMOCKA
         sdap-tests \
         test_sysdb_views \
         test_sysdb_subdomains \
+        test_sysdb_sudo \
         test_sysdb_utils \
         test_be_ptask \
         test_copy_ccache \
@@ -2375,6 +2376,23 @@ test_sysdb_subdomains_LDADD = \
     libsss_test_common.la \
     $(NULL)
 
+test_sysdb_sudo_SOURCES = \
+    src/tests/cmocka/test_sysdb_sudo.c \
+    src/db/sysdb_sudo.c \
+    src/util/debug.c \
+    $(NULL)
+test_sysdb_sudo_CFLAGS = \
+    $(AM_CFLAGS) \
+    $(NULL)
+test_sysdb_sudo_LDADD = \
+    $(CMOCKA_LIBS) \
+    $(LDB_LIBS) \
+    $(POPT_LIBS) \
+    $(TALLOC_LIBS) \
+    $(SSSD_INTERNAL_LTLIBS) \
+    libsss_test_common.la \
+    $(NULL)
+
 test_sysdb_utils_SOURCES = \
     src/tests/cmocka/test_sysdb_utils.c \
     $(NULL)
diff --git a/src/tests/cmocka/test_sysdb_sudo.c b/src/tests/cmocka/test_sysdb_sudo.c
new file mode 100644
index 0000000000000000000000000000000000000000..ebd06ce520eb2160c9910d6a03860314a96bb005
--- /dev/null
+++ b/src/tests/cmocka/test_sysdb_sudo.c
@@ -0,0 +1,587 @@
+/*
+    Authors:
+        Petr Čech <pc...@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 <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <cmocka.h>
+#include <popt.h>
+
+#include "tests/cmocka/common_mock.h"
+#include "src/db/sysdb_sudo.h"
+
+#define TESTS_PATH "tp_" BASE_FILE_STEM
+#define TEST_CONF_DB "test_sysdb_sudorules.ldb"
+#define TEST_DOM_NAME "test_domain.test"
+
+#define TEST_CACHE_SUDO_TIMEOUT "20"
+
+#define TEST_USER_NON_EXIST "no_user"
+
+#define TEST_GROUP_NAME "test_sudo_group"
+#define TEST_GID 10001
+
+struct test_user {
+    const char *name;
+    uid_t uid;
+    gid_t gid;
+} users[] = {{"test_user1", 1001, 1001},
+             {"test_user2", 1002, 1002},
+             {"test_user3", 1003, 1003}};
+
+struct test_rule {
+    const char *name;
+    const char *host;
+    const char *as_user;
+} rules[] = {{"test_rule1", "test_host1.test_domain.test", "root"},
+             {"test_rule2", "test_host2.test_domain.test", "root"},
+             {"test_rule3", "test_host3.test_domain.test", "root"}};
+
+struct sysdb_test_ctx {
+    struct sss_test_ctx *tctx;
+    struct sysdb_attrs **rules;
+};
+
+static void create_groups(struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    ret = sysdb_add_group(domain, TEST_GROUP_NAME, TEST_GID,
+                          NULL, 30, time(NULL));
+    assert_int_equal(ret, EOK);
+}
+
+static void remove_groups(struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    ret = sysdb_delete_group(domain, TEST_GROUP_NAME, TEST_GID);
+    assert_int_equal(ret, EOK);
+
+}
+
+static void create_users(struct sss_domain_info *domain)
+{
+    errno_t ret;
+    int gid;
+
+    for (int i=0; i < 3; i++) {
+        gid = (i == 0) ? 0 : TEST_GID;
+        ret = sysdb_add_user(domain, users[i].name, users[i].uid, gid,
+                             users[i].name, NULL, "/bin/bash", domain->name,
+                             NULL, 30, time(NULL));
+        assert_int_equal(ret, EOK);
+    }
+}
+
+static void remove_users(struct sss_domain_info *domain)
+{
+    errno_t ret;
+
+    for (int i=0; i < 3; i++) {
+        ret = sysdb_delete_user(domain, users[i].name, users[i].uid);
+        assert_int_equal(ret, EOK);
+    }
+}
+
+static void create_rule(struct sysdb_attrs *rule,
+                        struct sss_domain_info *domain,
+                        const char *name,
+                        const char *host,
+                        const char *run_as_user,
+                        const char *sudo_user)
+{
+    errno_t ret;
+
+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_CN, name);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_HOST, host);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_RUNASUSER,
+                                      run_as_user);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_attrs_add_string_safe(rule, SYSDB_SUDO_CACHE_AT_USER,
+                                      sudo_user);
+    assert_int_equal(ret, EOK);
+}
+
+static void create_rules(struct sysdb_test_ctx *test_ctx)
+{
+    test_ctx->rules = talloc_array(test_ctx, struct sysdb_attrs *, 3);
+
+    for (int i=0; i < 3; i++) {
+        test_ctx->rules[i] = sysdb_new_attrs(test_ctx->rules);
+        assert_non_null(test_ctx->rules[i]);
+
+        create_rule(test_ctx->rules[i], test_ctx->tctx->dom,
+                    rules[i].name, rules[i].host,
+                    rules[i].as_user, users[i].name);
+    }
+}
+
+static void remove_rules(struct sysdb_test_ctx *test_ctx)
+{
+    errno_t ret;
+
+    ret = sysdb_sudo_purge(test_ctx->tctx->dom, NULL, test_ctx->rules, 3);
+    assert_int_equal(ret, EOK);
+}
+
+static int test_sysdb_setup(void **state, bool rules_exist)
+{
+    errno_t ret;
+    struct sysdb_test_ctx *test_ctx;
+    struct sss_test_conf_param params[] = {
+        { NULL, NULL },             /* Sentinel */
+    };
+    const char *val[2];
+    val[1] = NULL;
+
+    assert_true(leak_check_setup());
+    check_leaks_push(global_talloc_context);
+
+    test_ctx = talloc_zero(global_talloc_context, struct sysdb_test_ctx);
+    assert_non_null(test_ctx);
+
+    test_dom_suite_setup(TESTS_PATH);
+
+    test_ctx->tctx = create_dom_test_ctx(NULL, TESTS_PATH, TEST_CONF_DB,
+                                         TEST_DOM_NAME, "ipa", params);
+    assert_non_null(test_ctx->tctx);
+
+    /* config */
+    val[0] = "1";
+    ret = confdb_add_param(test_ctx->tctx->confdb, true,
+                           "config/domain/" TEST_DOM_NAME,
+                           "pwd_expiration_warning", val);
+    assert_int_equal(ret, EOK);
+
+    /* SUDO RULES Configuration */
+    val[0] = "0xFFF0";
+    ret = confdb_add_param(test_ctx->tctx->confdb, true,
+                           "config/sudo", "debug_level", val);
+    assert_int_equal(ret, EOK);
+
+    val[0] = TEST_CACHE_SUDO_TIMEOUT;
+    ret = confdb_add_param(test_ctx->tctx->confdb, true,
+                           "config/sudo", "entry_cache_sudo_timeout", val);
+    assert_int_equal(ret, EOK);
+
+    create_groups(test_ctx->tctx->dom);
+    create_users(test_ctx->tctx->dom);
+
+    check_leaks_push(test_ctx);
+
+    /* We create set of 3 rules. */
+    create_rules(test_ctx);
+
+    if (rules_exist) {
+        /* We store only 0, 1 not 2. */
+        ret = sysdb_sudo_store(test_ctx->tctx->dom, test_ctx->rules, 2);
+        assert_int_equal(ret, EOK);
+    }
+
+    *state = (void *) test_ctx;
+    return 0;
+}
+
+static int setup_sysdb_without_rules(void **state)
+{
+    return test_sysdb_setup(state, false);
+}
+
+static int setup_sysdb_with_rules(void **state)
+{
+    return test_sysdb_setup(state, true);
+}
+
+static int test_sysdb_teardown(void **state)
+{
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    remove_rules(test_ctx);
+    talloc_zfree(test_ctx->rules);
+
+    assert_true(check_leaks_pop(test_ctx));
+
+    remove_users(test_ctx->tctx->dom);
+    remove_groups(test_ctx->tctx->dom);
+
+    talloc_zfree(test_ctx);
+    assert_true(check_leaks_pop(global_talloc_context));
+    assert_true(leak_check_teardown());
+    return 0;
+}
+
+void test_store_sudo(void **state)
+{
+    errno_t ret;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_sudo_store(test_ctx->tctx->dom, &(test_ctx->rules[2]), 1);
+    assert_int_equal(ret, EOK);
+
+}
+
+void test_sudo_purge_by_filter(void **state)
+{
+    errno_t ret;
+    char *delete_filter;
+    int uid = 0;
+    char **groupnames = NULL;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_get_sudo_filter(test_ctx, users[2].name,
+                                uid, groupnames, SYSDB_SUDO_FILTER_USERNAME,
+                                &delete_filter);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_sudo_purge(test_ctx->tctx->dom, delete_filter, NULL, 0);
+    assert_int_equal(ret, EOK);
+
+    talloc_zfree(delete_filter);
+}
+
+void test_sudo_purge_by_rules(void **state)
+{
+    errno_t ret;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_sudo_purge(test_ctx->tctx->dom, NULL, test_ctx->rules, 2);
+    assert_int_equal(ret, EOK);
+}
+
+void test_sudo_set_get_last_full_refresh(void **state)
+{
+    errno_t ret;
+    time_t now;
+    time_t time_before;
+    time_t time_after;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_sudo_get_last_full_refresh(test_ctx->tctx->dom, &time_before);
+    assert_int_equal(ret, EOK);
+
+    sleep(1);
+    now = time(NULL);
+
+    ret = sysdb_sudo_set_last_full_refresh(test_ctx->tctx->dom, now);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_sudo_get_last_full_refresh(test_ctx->tctx->dom, &time_after);
+    assert_int_equal(ret, EOK);
+
+    assert_int_not_equal(time_before, now);
+    assert_int_equal(now, time_after);
+}
+
+void test_sudo_get_filter(void **state)
+{
+    errno_t ret;
+    char *filter;
+    int uid = 0;
+    char **groupnames = NULL;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_get_sudo_filter(test_ctx, users[2].name,
+                                uid, groupnames, SYSDB_SUDO_FILTER_USERNAME,
+                                &filter);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(filter,
+                        "(&(objectClass=sudoRule)(|(sudoUser=test_user3)))");
+
+    talloc_zfree(filter);
+}
+
+void test_get_sudo_user_info(void **state)
+{
+    errno_t ret;
+    char **groupnames = NULL;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    /* User 1 has group. */
+    ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom,
+                                   users[1].name, 0, &groupnames);
+    assert_int_equal(ret, EOK);
+    assert_string_equal(groupnames[0], TEST_GROUP_NAME);
+
+    talloc_zfree(groupnames);
+}
+
+void test_get_sudo_user_info_nonexist_group(void **state)
+{
+    errno_t ret;
+    char **groupnames = NULL;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    /* User 0 hasn't group. */
+    ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom,
+                                   users[0].name, 0, &groupnames);
+    assert_int_equal(ret, EOK);
+
+    talloc_zfree(groupnames);
+}
+
+void test_get_sudo_user_info_no_exist(void **state)
+{
+    errno_t ret;
+    char **groupnames = NULL;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_get_sudo_user_info(test_ctx, test_ctx->tctx->dom,
+                                   TEST_USER_NON_EXIST, 0, &groupnames);
+    assert_int_equal(ret, ENOENT);
+}
+
+void test_set_sudo_rule_attr_exist(void **state)
+{
+    errno_t ret;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_attrs_add_time_t(test_ctx->rules[0], SYSDB_CACHE_EXPIRE, 1);
+    assert_int_equal(ret, EOK);
+
+    /* Rule 0 is saved, so SYSDB_CACHE_EXPIRE exists. */
+    ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name,
+                                   test_ctx->rules[0], SYSDB_MOD_REP);
+    assert_int_equal(ret, EOK);
+}
+
+void test_set_sudo_rule_attr_no_exist(void **state)
+{
+    errno_t ret;
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_attrs_add_time_t(test_ctx->rules[2], SYSDB_CACHE_EXPIRE, 1);
+    assert_int_equal(ret, EOK);
+
+    /* Rule 2 is not saved, so SYSDB_CACHE_EXPIRE not exists. */
+    ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[2].name,
+                                   test_ctx->rules[2], SYSDB_MOD_REP);
+    assert_int_equal(ret, ENOENT);
+}
+
+void test_search_sudo_rules(void **state)
+{
+    errno_t ret;
+    char *filter;
+    const char *attrs[] = {SYSDB_NAME, NULL};
+    struct ldb_message **msgs = NULL;
+    size_t msgs_count;
+    size_t num_rules = 2;
+    const char *rule_names[num_rules];
+    const char *db_results[num_rules];
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    ret = sysdb_get_sudo_filter(test_ctx, NULL, 0, NULL,
+                                SYSDB_SUDO_FILTER_NONE, &filter);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_search_sudo_rules(test_ctx, test_ctx->tctx->dom, filter,
+                                  attrs, &msgs_count, &msgs);
+    assert_int_equal(ret, EOK);
+
+    assert_int_equal(msgs_count, 2);
+
+    rule_names[0] = rules[0].name;
+    rule_names[1] = rules[1].name;
+
+    for (int i = 0; i < num_rules; ++i) {
+        db_results[i] = ldb_msg_find_attr_as_string(msgs[i], SYSDB_NAME, NULL);
+        assert_non_null(db_results[i]);
+    }
+
+    assert_string_not_equal(db_results[0], db_results[1]);
+    assert_true(are_values_in_array(rule_names, num_rules,
+                                    db_results, num_rules));
+
+    talloc_zfree(msgs);
+    talloc_zfree(filter);
+}
+
+void test_filter_rules_by_time(void **state)
+{
+    errno_t ret;
+    time_t now;
+    time_t time_before;
+    time_t time_after;
+    uint32_t _num_rules;
+    struct sysdb_attrs **_rules;
+    char buff[20];
+
+    struct sysdb_test_ctx *test_ctx = talloc_get_type_abort(*state,
+                                                         struct sysdb_test_ctx);
+
+    now = time(NULL);
+    time_before = now - 300;
+    time_after = now - 200;
+
+    strftime(buff, 20, "%Y%m%d%H%M%SZ", localtime(&time_before));
+    ret = sysdb_attrs_add_string(test_ctx->rules[0],
+                                 SYSDB_SUDO_CACHE_AT_NOTBEFORE, buff);
+    assert_int_equal(ret, EOK);
+
+    strftime(buff, 20, "%Y%m%d%H%M%SZ", localtime(&time_after));
+    ret = sysdb_attrs_add_string(test_ctx->rules[0],
+                                 SYSDB_SUDO_CACHE_AT_NOTAFTER, buff);
+    assert_int_equal(ret, EOK);
+
+    ret = sysdb_set_sudo_rule_attr(test_ctx->tctx->dom, rules[0].name,
+                                   test_ctx->rules[0], SYSDB_MOD_REP);
+    assert_int_equal(ret, EOK);
+
+    ret =  sysdb_sudo_filter_rules_by_time(test_ctx, 2, test_ctx->rules, now,
+                                           &_num_rules, &_rules);
+    assert_int_equal(ret, EOK);
+    assert_int_equal(_num_rules, 1);
+
+    talloc_zfree(_rules);
+}
+
+int main(int argc, const char *argv[])
+{
+    int rv;
+    int no_cleanup = 0;
+    poptContext pc;
+    int opt;
+    struct poptOption long_options[] = {
+        POPT_AUTOHELP
+        SSSD_DEBUG_OPTS
+        {"no-cleanup", 'n', POPT_ARG_NONE, &no_cleanup, 0,
+         _("Do not delete the test database after a test run"), NULL },
+        POPT_TABLEEND
+    };
+
+    const struct CMUnitTest tests[] = {
+        /* sysdb_sudo_store() */
+        cmocka_unit_test_setup_teardown(test_store_sudo,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_store_sudo,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_sudo_purge() */
+        cmocka_unit_test_setup_teardown(test_sudo_purge_by_filter,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sudo_purge_by_filter,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        cmocka_unit_test_setup_teardown(test_sudo_purge_by_rules,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sudo_purge_by_rules,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /*
+         * sysdb_sudo_set_last_full_refresh()
+         * sysdb_sudo_get_last_full_refresh()
+         */
+        cmocka_unit_test_setup_teardown(test_sudo_set_get_last_full_refresh,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sudo_set_get_last_full_refresh,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_get_sudo_filter() */
+        cmocka_unit_test_setup_teardown(test_sudo_get_filter,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_sudo_get_filter,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_get_sudo_user_info() */
+        cmocka_unit_test_setup_teardown(test_get_sudo_user_info,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_get_sudo_user_info_nonexist_group,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+
+        cmocka_unit_test_setup_teardown(test_get_sudo_user_info_no_exist,
+                                        setup_sysdb_without_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_set_sudo_rule_attr() */
+        cmocka_unit_test_setup_teardown(test_set_sudo_rule_attr_exist,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+        cmocka_unit_test_setup_teardown(test_set_sudo_rule_attr_no_exist,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_search_sudo_rules() */
+        cmocka_unit_test_setup_teardown(test_search_sudo_rules,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+
+        /* sysdb_sudo_filter_rules_by_time() */
+        cmocka_unit_test_setup_teardown(test_filter_rules_by_time,
+                                        setup_sysdb_with_rules,
+                                        test_sysdb_teardown),
+    };
+
+    /* Set debug level to invalid value so we can deside if -d 0 was used. */
+    debug_level = SSSDBG_TRACE_LIBS;
+
+    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+    while((opt = poptGetNextOpt(pc)) != -1) {
+        switch(opt) {
+        default:
+            fprintf(stderr, "\nInvalid option %s: %s\n\n",
+                    poptBadOption(pc, 0), poptStrerror(opt));
+            poptPrintUsage(pc, stderr, 0);
+            return 1;
+        }
+    }
+    poptFreeContext(pc);
+
+    DEBUG_CLI_INIT(debug_level);
+
+    tests_set_cwd();
+    test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, LOCAL_SYSDB_FILE);
+    test_dom_suite_setup(TESTS_PATH);
+    rv = cmocka_run_group_tests(tests, NULL, NULL);
+
+    if (rv == 0 && no_cleanup == 0) {
+        test_dom_suite_cleanup(TESTS_PATH, TEST_CONF_DB, TEST_DOM_NAME);
+    }
+    return rv;
+}
\ No newline at end of file
-- 
2.5.5

>From 3abf4d2d7608bfe2d533fe9fa6f3769fb28a1d21 Mon Sep 17 00:00:00 2001
From: Petr Cech <pc...@redhat.com>
Date: Wed, 9 Mar 2016 11:30:35 -0500
Subject: [PATCH 3/4] REFACTOR: sss_cache

Refactor of sss_cache tool.

Resolves:
https://fedorahosted.org/sssd/ticket/2081
---
 src/tools/sss_cache.c | 153 ++++++++++++++++++++++++++++++++++----------------
 1 file changed, 106 insertions(+), 47 deletions(-)

diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index 88895e8de374ed3fe16cbc92125ee3b91172e39e..f64b200350721f2317b219681822fe1b33e077a7 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -75,6 +75,16 @@ static errno_t search_autofsmaps(TALLOC_CTX *mem_ctx,
                                  const char *sub_filter, const char **attrs,
                                  size_t *msgs_count, struct ldb_message ***msgs);
 
+struct input_values {
+    char *domain;
+    char *group;
+    char *map;
+    char *netgroup;
+    char *service;
+    char *ssh_host;
+    char *user;
+};
+
 struct cache_tool_ctx {
     struct confdb_ctx *confdb;
     struct sss_domain_info *domains;
@@ -101,6 +111,9 @@ struct cache_tool_ctx {
     bool update_ssh_host_filter;
 };
 
+static void free_input_values(struct input_values *values);
+static bool is_filter_valid(struct cache_tool_ctx *ctx,
+                            struct input_values *values, int idb);
 errno_t init_domains(struct cache_tool_ctx *ctx, const char *domain);
 errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx);
 static errno_t invalidate_entry(TALLOC_CTX *ctx,
@@ -203,6 +216,17 @@ done:
     return ret;
 }
 
+static void free_input_values(struct input_values *values)
+{
+    free(values->domain);
+    free(values->group);
+    free(values->map);
+    free(values->netgroup);
+    free(values->service);
+    free(values->ssh_host);
+    free(values->user);
+}
+
 static errno_t update_filter(struct cache_tool_ctx *tctx,
                              struct sss_domain_info *dinfo,
                              char *name, bool update, const char *fmt,
@@ -571,13 +595,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
 {
     struct cache_tool_ctx *ctx = NULL;
     int idb = INVALIDATE_NONE;
-    char *user = NULL;
-    char *group = NULL;
-    char *netgroup = NULL;
-    char *service = NULL;
-    char *map = NULL;
-    char *ssh_host = NULL;
-    char *domain = NULL;
+    struct input_values values = {0};
     int debug = SSSDBG_DEFAULT;
     errno_t ret = EOK;
 
@@ -588,35 +606,35 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
             0, _("The debug level to run with"), NULL },
         { "everything", 'E', POPT_ARG_NONE, NULL, 'e',
             _("Invalidate all cached entries except for sudo rules"), NULL },
-        { "user", 'u', POPT_ARG_STRING, &user, 0,
+        { "user", 'u', POPT_ARG_STRING, &(values.user), 0,
             _("Invalidate particular user"), NULL },
         { "users", 'U', POPT_ARG_NONE, NULL, 'u',
             _("Invalidate all users"), NULL },
-        { "group", 'g', POPT_ARG_STRING, &group, 0,
+        { "group", 'g', POPT_ARG_STRING, &(values.group), 0,
             _("Invalidate particular group"), NULL },
         { "groups", 'G', POPT_ARG_NONE, NULL, 'g',
             _("Invalidate all groups"), NULL },
-        { "netgroup", 'n', POPT_ARG_STRING, &netgroup, 0,
+        { "netgroup", 'n', POPT_ARG_STRING, &(values.netgroup), 0,
             _("Invalidate particular netgroup"), NULL },
         { "netgroups", 'N', POPT_ARG_NONE, NULL, 'n',
             _("Invalidate all netgroups"), NULL },
-        { "service", 's', POPT_ARG_STRING, &service, 0,
+        { "service", 's', POPT_ARG_STRING, &(values.service), 0,
             _("Invalidate particular service"), NULL },
         { "services", 'S', POPT_ARG_NONE, NULL, 's',
             _("Invalidate all services"), NULL },
 #ifdef BUILD_AUTOFS
-        { "autofs-map", 'a', POPT_ARG_STRING, &map, 0,
+        { "autofs-map", 'a', POPT_ARG_STRING, &(values.map), 0,
             _("Invalidate particular autofs map"), NULL },
         { "autofs-maps", 'A', POPT_ARG_NONE, NULL, 'a',
             _("Invalidate all autofs maps"), NULL },
 #endif /* BUILD_AUTOFS */
 #ifdef BUILD_SSH
-        { "ssh-host", 'h', POPT_ARG_STRING, &ssh_host, 0,
+        { "ssh-host", 'h', POPT_ARG_STRING, &(values.ssh_host), 0,
             _("Invalidate particular SSH host"), NULL },
         { "ssh-hosts", 'H', POPT_ARG_NONE, NULL, 'h',
             _("Invalidate all SSH hosts"), NULL },
 #endif /* BUILD_SSH */
-        { "domain", 'd', POPT_ARG_STRING, &domain, 0,
+        { "domain", 'd', POPT_ARG_STRING, &(values.domain), 0,
             _("Only invalidate entries from a particular domain"), NULL },
         POPT_TABLEEND
     };
@@ -663,8 +681,9 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         BAD_POPT_PARAMS(pc, poptStrerror(ret), ret, fini);
     }
 
-    if (idb == INVALIDATE_NONE && !user && !group &&
-        !netgroup && !service && !map && !ssh_host) {
+    if (idb == INVALIDATE_NONE && !values.user && !values.group &&
+        !values.netgroup && !values.service && !values.map &&
+        !values.ssh_host) {
         BAD_POPT_PARAMS(pc,
                 _("Please select at least one object to invalidate\n"),
                 ret, fini);
@@ -683,32 +702,32 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
     if (idb & INVALIDATE_USERS) {
         ctx->user_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
         ctx->update_user_filter = false;
-    } else if (user) {
-        ctx->user_name = talloc_strdup(ctx, user);
+    } else if (values.user) {
+        ctx->user_name = talloc_strdup(ctx, values.user);
         ctx->update_user_filter = true;
     }
 
     if (idb & INVALIDATE_GROUPS) {
         ctx->group_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
         ctx->update_group_filter = false;
-    } else if (group) {
-        ctx->group_name = talloc_strdup(ctx, group);
+    } else if (values.group) {
+        ctx->group_name = talloc_strdup(ctx, values.group);
         ctx->update_group_filter = true;
     }
 
     if (idb & INVALIDATE_NETGROUPS) {
         ctx->netgroup_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
         ctx->update_netgroup_filter = false;
-    } else if (netgroup) {
-        ctx->netgroup_name = talloc_strdup(ctx, netgroup);
+    } else if (values.netgroup) {
+        ctx->netgroup_name = talloc_strdup(ctx, values.netgroup);
         ctx->update_netgroup_filter = true;
     }
 
     if (idb & INVALIDATE_SERVICES) {
         ctx->service_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
         ctx->update_service_filter = false;
-    } else if (service) {
-        ctx->service_name = talloc_strdup(ctx, service);
+    } else if (values.service) {
+        ctx->service_name = talloc_strdup(ctx, values.service);
         ctx->update_service_filter = true;
     }
 
@@ -716,42 +735,31 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         ctx->autofs_filter = talloc_asprintf(ctx, "(&(objectclass=%s)(%s=*))",
                                              SYSDB_AUTOFS_MAP_OC, SYSDB_NAME);
         ctx->update_autofs_filter = false;
-    } else if (map) {
-        ctx->autofs_name = talloc_strdup(ctx, map);
+    } else if (values.map) {
+        ctx->autofs_name = talloc_strdup(ctx, values.map);
         ctx->update_autofs_filter = true;
     }
 
     if (idb & INVALIDATE_SSH_HOSTS) {
         ctx->ssh_host_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
         ctx->update_ssh_host_filter = false;
-    } else if (ssh_host) {
-        ctx->ssh_host_name = talloc_strdup(ctx, ssh_host);
+    } else if (values.ssh_host) {
+        ctx->ssh_host_name = talloc_strdup(ctx, values.ssh_host);
         ctx->update_ssh_host_filter = true;
     }
 
-    if (((idb & INVALIDATE_USERS) && !ctx->user_filter) ||
-        ((idb & INVALIDATE_GROUPS) && !ctx->group_filter) ||
-        ((idb & INVALIDATE_NETGROUPS) && !ctx->netgroup_filter) ||
-        ((idb & INVALIDATE_SERVICES) && !ctx->service_filter) ||
-        ((idb & INVALIDATE_AUTOFSMAPS) && !ctx->autofs_filter) ||
-        ((idb & INVALIDATE_SSH_HOSTS) && !ctx->ssh_host_filter) ||
-         (user && !ctx->user_name) ||
-         (group && !ctx->group_name) ||
-         (netgroup && !ctx->netgroup_name) ||
-         (service && !ctx->service_name) ||
-         (map && !ctx->autofs_name) ||
-         (ssh_host && !ctx->ssh_host_name)) {
+    if (is_filter_valid(ctx, &values, idb) == false) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Construction of filters failed\n");
         ret = ENOMEM;
         goto fini;
     }
 
-    ret = init_domains(ctx, domain);
+    ret = init_domains(ctx, values.domain);
     if (ret != EOK) {
-        if (domain) {
+        if (values.domain) {
             ERROR("Could not open domain %1$s. If the domain is a subdomain "
                   "(trusted domain), use fully qualified name instead of "
-                  "--domain/-d parameter.\n", domain);
+                  "--domain/-d parameter.\n", values.domain);
         } else {
             ERROR("Could not open available domains\n");
         }
@@ -764,10 +772,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
 
 fini:
     poptFreeContext(pc);
-    free(user);
-    free(group);
-    free(netgroup);
-    free(domain);
+    free_input_values(&values);
     if (ret != EOK && ctx) {
         talloc_zfree(ctx);
     }
@@ -777,6 +782,60 @@ fini:
     return ret;
 }
 
+static bool is_filter_valid(struct cache_tool_ctx *ctx,
+                            struct input_values *values, int idb)
+{
+    if ((idb & INVALIDATE_USERS) && ctx->user_filter == NULL) {
+        return false;
+    }
+
+    if ((idb & INVALIDATE_GROUPS) && ctx->group_filter == NULL) {
+        return false;
+    }
+
+    if ((idb & INVALIDATE_NETGROUPS) && ctx->netgroup_filter == NULL) {
+        return false;
+    }
+
+    if ((idb & INVALIDATE_SERVICES) && ctx->service_filter == NULL) {
+        return false;
+    }
+
+    if ((idb & INVALIDATE_AUTOFSMAPS) && ctx->autofs_filter == NULL) {
+        return false;
+    }
+
+    if ((idb & INVALIDATE_SSH_HOSTS) && ctx->ssh_host_filter == NULL) {
+        return false;
+    }
+
+    if (values->user && ctx->user_name == NULL) {
+        return false;
+    }
+
+    if (values->group && ctx->group_name == NULL) {
+        return false;
+    }
+
+    if (values->netgroup && ctx->netgroup_name == NULL) {
+        return false;
+    }
+
+    if (values->service && ctx->service_name == NULL) {
+        return false;
+    }
+
+    if (values->map && ctx->autofs_name == NULL) {
+        return false;
+    }
+
+    if (values->ssh_host && ctx->ssh_host_name == NULL) {
+        return false;
+    }
+
+    return true;
+}
+
 static errno_t
 search_autofsmaps(TALLOC_CTX *mem_ctx,
                   struct sss_domain_info *domain,
-- 
2.5.5

>From 9e839c320582b384658f73f852da624ddcfe7b03 Mon Sep 17 00:00:00 2001
From: Petr Cech <pc...@redhat.com>
Date: Wed, 9 Mar 2016 11:33:22 -0500
Subject: [PATCH 4/4] TOOL: Invalidation of sudo rules at sss_cache

This patch adds new functionality to sss_cach for invalidation of given
sudo rule or all sudo rules.

Resolves:
https://fedorahosted.org/sssd/ticket/2081
---
 src/man/sss_cache.8.xml | 23 +++++++++++++++++
 src/tools/sss_cache.c   | 66 ++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 86 insertions(+), 3 deletions(-)

diff --git a/src/man/sss_cache.8.xml b/src/man/sss_cache.8.xml
index 1bc3633d9daa69ea59ba7ada0b6e180ece6e508a..81489288ced92ee5d4e7608ac092924369292bca 100644
--- a/src/man/sss_cache.8.xml
+++ b/src/man/sss_cache.8.xml
@@ -180,6 +180,29 @@
                     </para>
                 </listitem>
             </varlistentry>
+           <varlistentry condition="with_sudo">
+                <term>
+                    <option>-r</option>,<option>--sudo-rule</option>
+                    <replaceable>rule</replaceable>
+                </term>
+                <listitem>
+                    <para>
+                        Invalidate particular sudo rule.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry condition="with_sudo">
+                <term>
+                    <option>-R</option>,<option>--sudo-rules</option>
+                </term>
+                <listitem>
+                    <para>
+                        Invalidate all cached sudo rules. This option
+                        overrides invalidation of specific sudo rule
+                        if it was also set.
+                    </para>
+                </listitem>
+            </varlistentry>
             <varlistentry>
                 <term>
                     <option>-d</option>,<option>--domain</option>
diff --git a/src/tools/sss_cache.c b/src/tools/sss_cache.c
index f64b200350721f2317b219681822fe1b33e077a7..569e33159fc52fc547d2e7189aac9bd97ae846bc 100644
--- a/src/tools/sss_cache.c
+++ b/src/tools/sss_cache.c
@@ -31,6 +31,7 @@
 #include "db/sysdb_services.h"
 #include "db/sysdb_autofs.h"
 #include "db/sysdb_ssh.h"
+#include "db/sysdb_sudo.h"
 
 #define INVALIDATE_NONE 0
 #define INVALIDATE_USERS 1
@@ -39,6 +40,7 @@
 #define INVALIDATE_SERVICES 8
 #define INVALIDATE_AUTOFSMAPS 16
 #define INVALIDATE_SSH_HOSTS 32
+#define INVALIDATE_SUDO_RULES 64
 
 #ifdef BUILD_AUTOFS
 #ifdef BUILD_SSH
@@ -67,7 +69,8 @@ enum sss_cache_entry {
     TYPE_NETGROUP,
     TYPE_SERVICE,
     TYPE_AUTOFSMAP,
-    TYPE_SSH_HOST
+    TYPE_SSH_HOST,
+    TYPE_SUDO_RULE
 };
 
 static errno_t search_autofsmaps(TALLOC_CTX *mem_ctx,
@@ -82,6 +85,7 @@ struct input_values {
     char *netgroup;
     char *service;
     char *ssh_host;
+    char *sudo_rule;
     char *user;
 };
 
@@ -95,6 +99,7 @@ struct cache_tool_ctx {
     char *service_filter;
     char *autofs_filter;
     char *ssh_host_filter;
+    char *sudo_rule_filter;
 
     char *user_name;
     char *group_name;
@@ -102,6 +107,7 @@ struct cache_tool_ctx {
     char *service_name;
     char *autofs_name;
     char *ssh_host_name;
+    char *sudo_rule_name;
 
     bool update_user_filter;
     bool update_group_filter;
@@ -109,6 +115,7 @@ struct cache_tool_ctx {
     bool update_service_filter;
     bool update_autofs_filter;
     bool update_ssh_host_filter;
+    bool update_sudo_rule_filter;
 };
 
 static void free_input_values(struct input_values *values);
@@ -185,6 +192,9 @@ int main(int argc, const char *argv[])
         skipped &= !invalidate_entries(tctx, dinfo, TYPE_SSH_HOST,
                                        tctx->ssh_host_filter,
                                        tctx->ssh_host_name);
+        skipped &= !invalidate_entries(tctx, dinfo, TYPE_SUDO_RULE,
+                                       tctx->sudo_rule_filter,
+                                       tctx->sudo_rule_name);
 
         ret = sysdb_transaction_commit(sysdb);
         if (ret != EOK) {
@@ -224,6 +234,7 @@ static void free_input_values(struct input_values *values)
     free(values->netgroup);
     free(values->service);
     free(values->ssh_host);
+    free(values->sudo_rule);
     free(values->user);
 }
 
@@ -380,6 +391,14 @@ static errno_t update_all_filters(struct cache_tool_ctx *tctx,
         return ret;
     }
 
+    /* Update sudo rule filter */
+    ret = update_filter(tctx, dinfo, tctx->sudo_rule_name,
+                        tctx->update_sudo_rule_filter, "(%s=%s)", false,
+                        &tctx->sudo_rule_filter);
+    if (ret != EOK) {
+        return ret;
+    }
+
     return EOK;
 }
 
@@ -432,6 +451,15 @@ static bool invalidate_entries(TALLOC_CTX *ctx,
         ret = ENOSYS;
 #endif /* BUILD_SSH */
         break;
+    case TYPE_SUDO_RULE:
+        type_string = "sudo_rule";
+#ifdef BUILD_SUDO
+        ret = sysdb_search_sudo_rules(ctx, dinfo,
+                                      filter, attrs, &msg_count, &msgs);
+#else  /* BUILD_SUDO */
+        ret = ENOSYS;
+#endif /* BUILD_SUDO */
+        break;
     }
 
     if (ret != EOK) {
@@ -516,6 +544,14 @@ static errno_t invalidate_entry(TALLOC_CTX *ctx,
                     ret = ENOSYS;
 #endif /* BUILD_SSH */
                     break;
+                case TYPE_SUDO_RULE:
+#ifdef BUILD_SUDO
+                    ret = sysdb_set_sudo_rule_attr(domain, name,
+                                                   sys_attrs, SYSDB_MOD_REP);
+#else  /* BUILD_SUDO */
+                    ret = ENOSYS;
+#endif /* BUILD_SUDO */
+                    break;
                 default:
                     return EINVAL;
             }
@@ -605,7 +641,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         { "debug", '\0', POPT_ARG_INT | POPT_ARGFLAG_DOC_HIDDEN, &debug,
             0, _("The debug level to run with"), NULL },
         { "everything", 'E', POPT_ARG_NONE, NULL, 'e',
-            _("Invalidate all cached entries except for sudo rules"), NULL },
+            _("Invalidate all cached entries"), NULL },
         { "user", 'u', POPT_ARG_STRING, &(values.user), 0,
             _("Invalidate particular user"), NULL },
         { "users", 'U', POPT_ARG_NONE, NULL, 'u',
@@ -634,6 +670,12 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         { "ssh-hosts", 'H', POPT_ARG_NONE, NULL, 'h',
             _("Invalidate all SSH hosts"), NULL },
 #endif /* BUILD_SSH */
+#ifdef BUILD_SUDO
+        { "sudo-rule", 'r', POPT_ARG_STRING, &(values.sudo_rule), 0,
+            _("Invalidate particular sudo rule"), NULL },
+        { "sudo-rules", 'R', POPT_ARG_NONE, NULL, 'r',
+            _("Invalidate all cached sudo rules"), NULL },
+#endif /* BUILD_SUDO */
         { "domain", 'd', POPT_ARG_STRING, &(values.domain), 0,
             _("Only invalidate entries from a particular domain"), NULL },
         POPT_TABLEEND
@@ -668,8 +710,14 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
             case 'h':
                 idb |= INVALIDATE_SSH_HOSTS;
                 break;
+            case 'r':
+                idb |= INVALIDATE_SUDO_RULES;
+                break;
             case 'e':
                 idb = INVALIDATE_EVERYTHING;
+#ifdef BUILD_SUDO
+                idb |= INVALIDATE_SUDO_RULES;
+#endif /* BUILD_SUDO */
                 break;
         }
     }
@@ -683,7 +731,7 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
 
     if (idb == INVALIDATE_NONE && !values.user && !values.group &&
         !values.netgroup && !values.service && !values.map &&
-        !values.ssh_host) {
+        !values.ssh_host && !values.sudo_rule) {
         BAD_POPT_PARAMS(pc,
                 _("Please select at least one object to invalidate\n"),
                 ret, fini);
@@ -748,6 +796,14 @@ errno_t init_context(int argc, const char *argv[], struct cache_tool_ctx **tctx)
         ctx->update_ssh_host_filter = true;
     }
 
+    if (idb & INVALIDATE_SUDO_RULES) {
+        ctx->sudo_rule_filter = talloc_asprintf(ctx, "(%s=*)", SYSDB_NAME);
+        ctx->update_sudo_rule_filter = false;
+    } else if (values.sudo_rule) {
+        ctx->sudo_rule_name = talloc_strdup(ctx, values.sudo_rule);
+        ctx->update_sudo_rule_filter = true;
+    }
+
     if (is_filter_valid(ctx, &values, idb) == false) {
         DEBUG(SSSDBG_CRIT_FAILURE, "Construction of filters failed\n");
         ret = ENOMEM;
@@ -833,6 +889,10 @@ static bool is_filter_valid(struct cache_tool_ctx *ctx,
         return false;
     }
 
+    if (values->sudo_rule && ctx->sudo_rule_name == NULL) {
+        return false;
+    }
+
     return true;
 }
 
-- 
2.5.5

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/admin/lists/sssd-devel@lists.fedorahosted.org

Reply via email to