URL: https://github.com/SSSD/sssd/pull/102
Author: mzidek-rh
 Title: #102: Sssctl no case sensitive searches
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/102/head:pr102
git checkout pr102
From 20596900fca35c3039a22679f8eb4c7d1ba2be2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzi...@redhat.com>
Date: Thu, 1 Dec 2016 15:56:39 +0100
Subject: [PATCH 1/4] common: Fix domain case sensitivity init

Fix initialization of domain case sensitivity.

Resolves:
https://fedorahosted.org/sssd/ticket/3235
---
 src/confdb/confdb.c | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/confdb/confdb.c b/src/confdb/confdb.c
index f0e3942..cfe765c 100644
--- a/src/confdb/confdb.c
+++ b/src/confdb/confdb.c
@@ -1292,7 +1292,7 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
     }
 
     tmp = ldb_msg_find_attr_as_string(res->msgs[0],
-                                      CONFDB_DOMAIN_CASE_SENSITIVE, "true");
+                                      CONFDB_DOMAIN_CASE_SENSITIVE, NULL);
     if (tmp != NULL) {
         if (strcasecmp(tmp, "true") == 0) {
             domain->case_sensitive = true;
@@ -1310,9 +1310,15 @@ static int confdb_get_domain_internal(struct confdb_ctx *cdb,
         }
     } else {
         /* default */
-        domain->case_sensitive = true;
-        domain->case_preserve = true;
+        if (strcasecmp(domain->provider, "ad") == 0) {
+            domain->case_sensitive = false;
+            domain->case_sensitive = false;
+        } else {
+            domain->case_sensitive = true;
+            domain->case_preserve = true;
+        }
     }
+
     if (domain->case_sensitive == false &&
         strcasecmp(domain->provider, "local") == 0) {
         DEBUG(SSSDBG_FATAL_FAILURE,

From 89d31727c8292aefb333e11899635dbeaa223993 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzi...@redhat.com>
Date: Thu, 1 Dec 2016 14:51:37 +0100
Subject: [PATCH 2/4] sssctl: Users and groups search by alias

Also search by alias when using sssctl user/group-show command.

Resolves:
https://fedorahosted.org/sssd/ticket/3235
---
 src/tools/sssctl/sssctl_cache.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
index b1a7cc9..93601b9 100644
--- a/src/tools/sssctl/sssctl_cache.c
+++ b/src/tools/sssctl/sssctl_cache.c
@@ -295,10 +295,12 @@ static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
     const char *filter;
     char *filter_value;
     bool qualify_attr = false;
+    bool use_alias = false;
 
     if (strcmp(attr_name, SYSDB_NAME) == 0) {
         if (obj_type == CACHED_USER || obj_type == CACHED_GROUP) {
             qualify_attr = true;
+            use_alias = true;
         }
     }
 
@@ -327,8 +329,15 @@ static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
-    filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))",
-                             class, attr_name, filter_value);
+    if (use_alias) {
+         filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
+                                  class, attr_name, filter_value,
+                                  SYSDB_NAME_ALIAS, filter_value);
+    } else {
+        filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(%s=%s))",
+                                 class, attr_name, filter_value);
+    }
+
     talloc_free(filter_value);
 
     return filter;

From 04e8d5a5d2d85734cb3edd7b8097a7dc2c08f3d7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzi...@redhat.com>
Date: Thu, 1 Dec 2016 15:22:42 +0100
Subject: [PATCH 3/4] sssctl: Case insensitive filters

Lowercase the filter in case insensitive domains.

Resolves:
https://fedorahosted.org/sssd/ticket/3235
---
 src/tools/sssctl/sssctl_cache.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/tools/sssctl/sssctl_cache.c b/src/tools/sssctl/sssctl_cache.c
index 93601b9..a8d703d 100644
--- a/src/tools/sssctl/sssctl_cache.c
+++ b/src/tools/sssctl/sssctl_cache.c
@@ -329,6 +329,14 @@ static const char *sssctl_create_filter(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    if (dom->case_sensitive == false) {
+        char *filter_value_old;
+
+        filter_value_old = filter_value;
+        filter_value = sss_tc_utf8_str_tolower(mem_ctx, filter_value_old);
+        talloc_free(filter_value_old);
+    }
+
     if (use_alias) {
          filter = talloc_asprintf(mem_ctx, "(&(objectClass=%s)(|(%s=%s)(%s=%s)))",
                                   class, attr_name, filter_value,

From 0aa18539c8bdc93e1fcac9d1d559682c07c60848 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzi...@redhat.com>
Date: Fri, 2 Dec 2016 13:21:47 +0100
Subject: [PATCH 4/4] tests: sssctl user/group-show basic tests

Add basic tests for sssctl user/group-show commands. This includes
regression test for ticket #3235.

Resolves:
https://fedorahosted.org/sssd/ticket/3235
---
 src/tests/intg/Makefile.am    |   1 +
 src/tests/intg/test_sssctl.py | 364 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 365 insertions(+)
 create mode 100644 src/tests/intg/test_sssctl.py

diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
index 1e08ead..e81e5ee 100644
--- a/src/tests/intg/Makefile.am
+++ b/src/tests/intg/Makefile.am
@@ -18,6 +18,7 @@ dist_noinst_DATA = \
     test_netgroup.py \
     secrets.py \
     test_secrets.py \
+    test_sssctl.py \
     $(NULL)
 
 config.py: config.py.m4
diff --git a/src/tests/intg/test_sssctl.py b/src/tests/intg/test_sssctl.py
new file mode 100644
index 0000000..f75a95e
--- /dev/null
+++ b/src/tests/intg/test_sssctl.py
@@ -0,0 +1,364 @@
+#
+# sssctl tool integration test
+#
+# Copyright (c) 2016 Red Hat, Inc.
+# Author: Michal Zidek <mzi...@redhat.com>
+#
+# This 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; version 2 only
+#
+# 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/>.
+#
+import os
+import ent
+import grp
+import pwd
+import subprocess
+import pytest
+import stat
+import time
+import signal
+import ds_openldap
+import ldap_ent
+import config
+from util import unindent
+
+LDAP_BASE_DN = "dc=example,dc=com"
+
+
+@pytest.fixture(scope="module")
+def ds_inst(request):
+    """LDAP server instance fixture"""
+    ds_inst = ds_openldap.DSOpenLDAP(
+        config.PREFIX, 10389, LDAP_BASE_DN,
+        "cn=admin", "Secret123")
+    try:
+        ds_inst.setup()
+    except:
+        ds_inst.teardown()
+        raise
+    request.addfinalizer(lambda: ds_inst.teardown())
+    return ds_inst
+
+
+@pytest.fixture(scope="module")
+def ldap_conn(request, ds_inst):
+    """LDAP server connection fixture"""
+    ldap_conn = ds_inst.bind()
+    ldap_conn.ds_inst = ds_inst
+    request.addfinalizer(lambda: ldap_conn.unbind_s())
+    return ldap_conn
+
+
+def create_ldap_fixture(request, ldap_conn, ent_list):
+    """Add LDAP entries and add teardown for removing them"""
+    for entry in ent_list:
+        ldap_conn.add_s(entry[0], entry[1])
+
+    def teardown():
+        for entry in ent_list:
+            ldap_conn.delete_s(entry[0])
+    request.addfinalizer(teardown)
+
+
+def create_conf_fixture(request, contents):
+    """Generate sssd.conf and add teardown for removing it"""
+    conf = open(config.CONF_PATH, "w")
+    conf.write(contents)
+    conf.close()
+    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
+    request.addfinalizer(lambda: os.unlink(config.CONF_PATH))
+
+
+def stop_sssd():
+    pid_file = open(config.PIDFILE_PATH, "r")
+    pid = int(pid_file.read())
+    os.kill(pid, signal.SIGTERM)
+    while True:
+        try:
+            os.kill(pid, signal.SIGCONT)
+        except:
+            break
+        time.sleep(1)
+
+
+def create_sssd_fixture(request):
+    """Start sssd and add teardown for stopping it and removing state"""
+    if subprocess.call(["sssd", "-D", "-f"]) != 0:
+        raise Exception("sssd start failed")
+
+    def teardown():
+        try:
+            stop_sssd()
+        except:
+            pass
+        for path in os.listdir(config.DB_PATH):
+            os.unlink(config.DB_PATH + "/" + path)
+        for path in os.listdir(config.MCACHE_PATH):
+            os.unlink(config.MCACHE_PATH + "/" + path)
+    request.addfinalizer(teardown)
+
+
+@pytest.fixture
+def portable_LC_ALL(request):
+    os.environ["LC_ALL"] = "C"
+    return None
+
+
+def load_data_to_ldap(request, ldap_conn):
+    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
+    ent_list.add_user("user1", 1001, 2001)
+    ent_list.add_user("CamelCaseUser1", 1002, 2002)
+
+    ent_list.add_group("group1", 2001, ["user1"])
+    ent_list.add_group("CamelCaseGroup1", 2002, ["CamelCaseUser1"])
+
+    create_ldap_fixture(request, ldap_conn, ent_list)
+
+
+@pytest.fixture
+def sanity_rfc2307(request, ldap_conn):
+    load_data_to_ldap(request, ldap_conn)
+
+    conf = unindent("""\
+        [sssd]
+        domains             = LDAP
+        services            = nss
+
+        [nss]
+
+        [domain/LDAP]
+        ldap_auth_disable_tls_never_use_in_production = true
+        ldap_schema         = rfc2307
+        id_provider         = ldap
+        auth_provider       = ldap
+        sudo_provider       = ldap
+        ldap_uri            = {ldap_conn.ds_inst.ldap_url}
+        ldap_search_base    = {ldap_conn.ds_inst.base_dn}
+    """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
+@pytest.fixture
+def fqname_rfc2307(request, ldap_conn):
+    load_data_to_ldap(request, ldap_conn)
+
+    conf = unindent("""\
+        [sssd]
+        domains             = LDAP
+        services            = nss
+
+        [nss]
+
+        [domain/LDAP]
+        ldap_auth_disable_tls_never_use_in_production = true
+        ldap_schema         = rfc2307
+        id_provider         = ldap
+        auth_provider       = ldap
+        sudo_provider       = ldap
+        ldap_uri            = {ldap_conn.ds_inst.ldap_url}
+        ldap_search_base    = {ldap_conn.ds_inst.base_dn}
+        use_fully_qualified_names = true
+    """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
+@pytest.fixture
+def fqname_case_insensitive_rfc2307(request, ldap_conn):
+    load_data_to_ldap(request, ldap_conn)
+
+    conf = unindent("""\
+        [sssd]
+        domains             = LDAP
+        services            = nss
+
+        [nss]
+
+        [domain/LDAP]
+        ldap_auth_disable_tls_never_use_in_production = true
+        ldap_schema         = rfc2307
+        id_provider         = ldap
+        auth_provider       = ldap
+        sudo_provider       = ldap
+        ldap_uri            = {ldap_conn.ds_inst.ldap_url}
+        ldap_search_base    = {ldap_conn.ds_inst.base_dn}
+        use_fully_qualified_names = true
+        case_sensitive = false
+    """).format(**locals())
+    create_conf_fixture(request, conf)
+    create_sssd_fixture(request)
+    return None
+
+
+def test_user_show_basic_sanity(ldap_conn, sanity_rfc2307, portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_passwd_by_name(
+        'user1',
+        dict(name='user1', passwd='*', uid=1001, gid=2001,
+             gecos='1001', shell='/bin/bash'))
+    ent.assert_passwd_by_name(
+        'CamelCaseUser1',
+        dict(name='CamelCaseUser1', passwd='*', uid=1002, gid=2002,
+             gecos='1002', shell='/bin/bash'))
+
+    output = subprocess.check_output(["sssctl", "user-show", "user1"])
+    assert output.find("Name: user1") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show",
+                                      "CamelCaseUser1"])
+    assert output.find("Name: CamelCaseUser1") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show", "camelcaseuser1"])
+    assert output.find("User camelcaseuser1 is not present in cache.") != -1
+
+
+def test_user_show_basic_fqname(ldap_conn, fqname_rfc2307, portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_passwd_by_name(
+        'user1@LDAP',
+        dict(name='user1@LDAP', passwd='*', uid=1001, gid=2001,
+             gecos='1001', shell='/bin/bash'))
+    ent.assert_passwd_by_name(
+        'CamelCaseUser1@LDAP',
+        dict(name='CamelCaseUser1@LDAP', passwd='*', uid=1002, gid=2002,
+             gecos='1002', shell='/bin/bash'))
+
+    output = subprocess.check_output(["sssctl", "user-show", "user1@LDAP"])
+    assert output.find("Name: user1@LDAP") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show",
+                                      "CamelCaseUser1@LDAP"])
+    assert output.find("Name: CamelCaseUser1@LDAP") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show",
+                                      "camelcaseuser1@LDAP"])
+    assert output.find("User camelcaseuser1 is not present in cache.") != -1
+
+
+def test_user_show_basic_fqname_insensitive(ldap_conn,
+                                            fqname_case_insensitive_rfc2307,
+                                            portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_passwd_by_name(
+        'user1@LDAP',
+        dict(name='user1@LDAP', passwd='*', uid=1001, gid=2001,
+             gecos='1001', shell='/bin/bash'))
+    ent.assert_passwd_by_name(
+        'CamelCaseUser1@LDAP',
+        dict(name='camelcaseuser1@LDAP', passwd='*', uid=1002, gid=2002,
+             gecos='1002', shell='/bin/bash'))
+
+    output = subprocess.check_output(["sssctl", "user-show", "user1@LDAP"])
+    assert output.find("Name: user1@LDAP") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show",
+                                      "CamelCaseUser1@LDAP"])
+    assert output.find("Name: camelcaseuser1@LDAP") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "user-show",
+                                      "camelcaseuser1@LDAP"])
+    assert output.find("Name: camelcaseuser1@LDAP") != -1
+    assert output.find("Initgroups expiration time: Initgroups were not yet "
+                       "performed") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+
+def test_group_show_basic_sanity(ldap_conn, sanity_rfc2307, portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_group_by_name(
+        "group1",
+        dict(mem=ent.contains_only("user1")))
+    ent.assert_group_by_name(
+        "CamelCaseGroup1",
+        dict(mem=ent.contains_only("CamelCaseUser1")))
+
+    output = subprocess.check_output(["sssctl", "group-show", "group1"])
+    assert output.find("Name: group1") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "CamelCaseGroup1"])
+    assert output.find("Name: CamelCaseGroup1") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "camelcasegroup1"])
+    assert output.find("Group camelcasegroup1 is not present in cache.") != -1
+
+
+def test_group_show_basic_fqname(ldap_conn, fqname_rfc2307, portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_group_by_name(
+        "group1@LDAP",
+        dict(mem=ent.contains_only("user1@LDAP")))
+    ent.assert_group_by_name(
+        "CamelCaseGroup1@LDAP",
+        dict(mem=ent.contains_only("CamelCaseUser1@LDAP")))
+
+    output = subprocess.check_output(["sssctl", "group-show", "group1@LDAP"])
+    assert output.find("Name: group1@LDAP") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "CamelCaseGroup1@LDAP"])
+    assert output.find("Name: CamelCaseGroup1@LDAP") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "camelcasegroup1@LDAP"])
+    assert output.find("Group camelcasegroup1 is not present in cache.") != -1
+
+
+def test_group_show_basic_fqname_insensitive(ldap_conn,
+                                             fqname_case_insensitive_rfc2307,
+                                             portable_LC_ALL):
+    # Fill the cache first
+    ent.assert_group_by_name(
+        "group1@LDAP",
+        dict(mem=ent.contains_only("user1@LDAP")))
+    ent.assert_group_by_name(
+        "camelcasegroup1@LDAP",
+        dict(mem=ent.contains_only("camelcaseuser1@LDAP")))
+
+    output = subprocess.check_output(["sssctl", "group-show", "group1@LDAP"])
+    assert output.find("Name: group1@LDAP") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "CamelCaseGroup1@LDAP"])
+    assert output.find("Name: camelcasegroup1@LDAP") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
+
+    output = subprocess.check_output(["sssctl", "group-show",
+                                      "camelcasegroup1@LDAP"])
+    assert output.find("Name: camelcasegroup1@LDAP") != -1
+    assert output.find("Cached in InfoPipe: No") != -1
_______________________________________________
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