d4adbd
From 857713c5a9c8e0b62c06dd92e69c09eeb34b2e99 Mon Sep 17 00:00:00 2001
d4adbd
From: Anuja More <amore@redhat.com>
d4adbd
Date: Mon, 23 May 2022 12:26:34 +0530
d4adbd
Subject: [PATCH] Add end to end integration tests for external IdP
d4adbd
d4adbd
Added tests for HBAC and SUDO rule and other
d4adbd
test scenarios.
d4adbd
d4adbd
Related : https://pagure.io/freeipa/issue/8805
d4adbd
Related: https://pagure.io/freeipa/issue/8803
d4adbd
Related: https://pagure.io/freeipa/issue/8804
d4adbd
d4adbd
Signed-off-by: Anuja More <amore@redhat.com>
d4adbd
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
d4adbd
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
d4adbd
---
d4adbd
 ipatests/test_integration/test_idp.py | 260 ++++++++++++++++++++++----
d4adbd
 1 file changed, 226 insertions(+), 34 deletions(-)
d4adbd
d4adbd
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
d4adbd
index 8f9e92e6a..2ffe6a208 100644
d4adbd
--- a/ipatests/test_integration/test_idp.py
d4adbd
+++ b/ipatests/test_integration/test_idp.py
d4adbd
@@ -1,6 +1,8 @@
d4adbd
 from __future__ import absolute_import
d4adbd
 
d4adbd
 import time
d4adbd
+import pytest
d4adbd
+import re
d4adbd
 
d4adbd
 import textwrap
d4adbd
 from ipaplatform.paths import paths
d4adbd
@@ -22,12 +24,12 @@ driver.get(verification_uri)
d4adbd
 try:
d4adbd
     element = WebDriverWait(driver, 90).until(
d4adbd
         EC.presence_of_element_located((By.ID, "username")))
d4adbd
-    driver.find_element_by_id("username").send_keys("testuser1")
d4adbd
-    driver.find_element_by_id("password").send_keys("{passwd}")
d4adbd
-    driver.find_element_by_id("kc-login").click()
d4adbd
+    driver.find_element(By.ID, "username").send_keys("testuser1")
d4adbd
+    driver.find_element(By.ID, "password").send_keys("{passwd}")
d4adbd
+    driver.find_element(By.ID, "kc-login").click()
d4adbd
     element = WebDriverWait(driver, 90).until(
d4adbd
         EC.presence_of_element_located((By.ID, "kc-login")))
d4adbd
-    driver.find_element_by_id("kc-login").click()
d4adbd
+    driver.find_element(By.ID, "kc-login").click()
d4adbd
     assert "Device Login Successful" in driver.page_source
d4adbd
 finally:
d4adbd
     now = datetime.now().strftime("%M-%S")
d4adbd
@@ -39,18 +41,12 @@ finally:
d4adbd
 def add_user_code(host, verification_uri):
d4adbd
     contents = user_code_script.format(uri=verification_uri,
d4adbd
                                        passwd=host.config.admin_password)
d4adbd
-    host.put_file_contents("/tmp/add_user_code.py", contents)
d4adbd
-    tasks.run_repeatedly(
d4adbd
-        host, ['python3', '/tmp/add_user_code.py'])
d4adbd
-
d4adbd
-
d4adbd
-def get_verification_uri(host, since, keycloak_server_name):
d4adbd
-    command = textwrap.dedent("""
d4adbd
-    journalctl -u ipa-otpd\\* --since="%s" | grep "user_code:" | awk '{ print substr($7,2,9) }'""" % since)  # noqa: E501
d4adbd
-    user_code = host.run_command(command).stdout_text.rstrip("\r\n")
d4adbd
-    uri = ("https://{0}:8443/auth/realms/master/device?user_code={1}".format(
d4adbd
-        keycloak_server_name, user_code))
d4adbd
-    return uri
d4adbd
+    try:
d4adbd
+        host.put_file_contents("/tmp/add_user_code.py", contents)
d4adbd
+        tasks.run_repeatedly(
d4adbd
+            host, ['python3', '/tmp/add_user_code.py'])
d4adbd
+    finally:
d4adbd
+        host.run_command(["rm", "-f", "/tmp/add_user_code.py"])
d4adbd
 
d4adbd
 
d4adbd
 def kinit_idp(host, user, keycloak_server):
d4adbd
@@ -58,11 +54,14 @@ def kinit_idp(host, user, keycloak_server):
d4adbd
     tasks.kdestroy_all(host)
d4adbd
     # create armor for FAST
d4adbd
     host.run_command(["kinit", "-n", "-c", ARMOR])
d4adbd
-    since = time.strftime('%Y-%m-%d %H:%M:%S')
d4adbd
     cmd = ["kinit", "-T", ARMOR, user]
d4adbd
+
d4adbd
     with host.spawn_expect(cmd, default_timeout=100) as e:
d4adbd
-        e.expect('Authenticate at .+: ')
d4adbd
-        uri = get_verification_uri(host, since, keycloak_server.hostname)
d4adbd
+        e.expect('Authenticate at (.+) and press ENTER.:')
d4adbd
+        prompt = e.get_last_output()
d4adbd
+        uri = re.search(r'Authenticate at (.*?) and press ENTER.:', prompt
d4adbd
+                        ).group(1)
d4adbd
+        time.sleep(15)
d4adbd
         if uri:
d4adbd
             add_user_code(keycloak_server, uri)
d4adbd
         e.sendline('\n')
d4adbd
@@ -74,21 +73,27 @@ def kinit_idp(host, user, keycloak_server):
d4adbd
 
d4adbd
 class TestIDPKeycloak(IntegrationTest):
d4adbd
 
d4adbd
-    num_replicas = 1
d4adbd
+    num_replicas = 2
d4adbd
     topology = 'line'
d4adbd
 
d4adbd
     @classmethod
d4adbd
     def install(cls, mh):
d4adbd
-        tasks.install_master(cls.master, setup_dns=True)
d4adbd
-        tasks.install_client(cls.master, cls.replicas[0])
d4adbd
-        content = cls.master.get_file_contents(paths.IPA_DEFAULT_CONF,
d4adbd
-                                               encoding='utf-8')
d4adbd
-        new_content = content + "\noidc_child_debug_level = 10"
d4adbd
-        cls.master.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
d4adbd
+        cls.client = cls.replicas[0]
d4adbd
+        cls.replica = cls.replicas[1]
d4adbd
+        tasks.install_master(cls.master)
d4adbd
+        tasks.install_client(cls.master, cls.replicas[0],
d4adbd
+                             extra_args=["--mkhomedir"])
d4adbd
+        tasks.install_replica(cls.master, cls.replicas[1])
d4adbd
+        for host in [cls.master, cls.replicas[0], cls.replicas[1]]:
d4adbd
+            content = host.get_file_contents(paths.IPA_DEFAULT_CONF,
d4adbd
+                                             encoding='utf-8')
d4adbd
+            new_content = content + "\noidc_child_debug_level = 10"
d4adbd
+            host.put_file_contents(paths.IPA_DEFAULT_CONF, new_content)
d4adbd
         with tasks.remote_sssd_config(cls.master) as sssd_config:
d4adbd
             sssd_config.edit_domain(
d4adbd
                 cls.master.domain, 'krb5_auth_timeout', 1100)
d4adbd
         tasks.clear_sssd_cache(cls.master)
d4adbd
+        tasks.clear_sssd_cache(cls.replicas[0])
d4adbd
         tasks.kinit_admin(cls.master)
d4adbd
         cls.master.run_command(["ipa", "config-mod", "--user-auth-type=idp",
d4adbd
                                 "--user-auth-type=password"])
d4adbd
@@ -97,20 +102,207 @@ class TestIDPKeycloak(IntegrationTest):
d4adbd
         cls.replicas[0].run_command(xvfb)
d4adbd
 
d4adbd
     def test_auth_keycloak_idp(self):
d4adbd
-        keycloak_srv = self.replicas[0]
d4adbd
-        create_quarkus.setup_keycloakserver(keycloak_srv)
d4adbd
+        """
d4adbd
+        Test case to check that OAuth 2.0 Device
d4adbd
+        Authorization Grant is working as
d4adbd
+        expected for user configured with external idp.
d4adbd
+        """
d4adbd
+        create_quarkus.setup_keycloakserver(self.client)
d4adbd
         time.sleep(60)
d4adbd
-        create_quarkus.setup_keycloak_client(keycloak_srv)
d4adbd
+        create_quarkus.setup_keycloak_client(self.client)
d4adbd
         tasks.kinit_admin(self.master)
d4adbd
-        cmd = ["ipa", "idp-add", "keycloak", "--provider=keycloak",
d4adbd
+        cmd = ["ipa", "idp-add", "keycloakidp", "--provider=keycloak",
d4adbd
                "--client-id=ipa_oidc_client", "--org=master",
d4adbd
-               "--base-url={0}:8443/auth".format(keycloak_srv.hostname)]
d4adbd
+               "--base-url={0}:8443/auth".format(self.client.hostname)]
d4adbd
         self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
d4adbd
-            keycloak_srv.config.admin_password))
d4adbd
+            self.client.config.admin_password))
d4adbd
         tasks.user_add(self.master, 'keycloakuser',
d4adbd
                        extra_args=["--user-auth-type=idp",
d4adbd
                                    "--idp-user-id=testuser1@ipa.test",
d4adbd
-                                   "--idp=keycloak"]
d4adbd
+                                   "--idp=keycloakidp"]
d4adbd
                        )
d4adbd
+        list_user = self.master.run_command(
d4adbd
+            ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
d4adbd
+        )
d4adbd
+        assert "keycloakuser" in list_user.stdout_text
d4adbd
+        list_by_idp = self.master.run_command(["ipa", "user-find",
d4adbd
+                                               "--idp=keycloakidp"]
d4adbd
+                                              )
d4adbd
+        assert "keycloakuser" in list_by_idp.stdout_text
d4adbd
+        list_by_user = self.master.run_command(
d4adbd
+            ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
d4adbd
+        )
d4adbd
+        assert "keycloakidp" in list_by_user.stdout_text
d4adbd
+        tasks.clear_sssd_cache(self.master)
d4adbd
+        kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
d4adbd
+
d4adbd
+    @pytest.fixture
d4adbd
+    def hbac_setup_teardown(self):
d4adbd
+        # allow sshd only on given host
d4adbd
+        tasks.kinit_admin(self.master)
d4adbd
+        self.master.run_command(["ipa", "hbacrule-disable", "allow_all"])
d4adbd
+        self.master.run_command(["ipa", "hbacrule-add", "rule1"])
d4adbd
+        self.master.run_command(["ipa", "hbacrule-add-user", "rule1",
d4adbd
+                                 "--users=keycloakuser"]
d4adbd
+                                )
d4adbd
+        self.master.run_command(["ipa", "hbacrule-add-host", "rule1",
d4adbd
+                                 "--hosts", self.replica.hostname])
d4adbd
+        self.master.run_command(["ipa", "hbacrule-add-service", "rule1",
d4adbd
+                                 "--hbacsvcs=sshd"]
d4adbd
+                                )
d4adbd
+        tasks.clear_sssd_cache(self.master)
d4adbd
+        tasks.clear_sssd_cache(self.replica)
d4adbd
+        yield
d4adbd
+
d4adbd
+        # cleanup
d4adbd
+        tasks.kinit_admin(self.master)
d4adbd
+        self.master.run_command(["ipa", "hbacrule-enable", "allow_all"])
d4adbd
+        self.master.run_command(["ipa", "hbacrule-del", "rule1"])
d4adbd
+
d4adbd
+    def test_auth_hbac(self, hbac_setup_teardown):
d4adbd
+        """
d4adbd
+        Test case to check that hbacrule is working as
d4adbd
+        expected for user configured with external idp.
d4adbd
+        """
d4adbd
+        kinit_idp(self.master, 'keycloakuser', keycloak_server=self.client)
d4adbd
+        ssh_cmd = "ssh -q -K -l keycloakuser {0} whoami"
d4adbd
+        valid_ssh = self.master.run_command(
d4adbd
+            ssh_cmd.format(self.replica.hostname))
d4adbd
+        assert "keycloakuser" in valid_ssh.stdout_text
d4adbd
+        negative_ssh = self.master.run_command(
d4adbd
+            ssh_cmd.format(self.master.hostname), raiseonerr=False
d4adbd
+        )
d4adbd
+        assert negative_ssh.returncode == 255
d4adbd
+
d4adbd
+    def test_auth_sudo_idp(self):
d4adbd
+        """
d4adbd
+        Test case to check that sudorule is working as
d4adbd
+        expected for user configured with external idp.
d4adbd
+        """
d4adbd
+        tasks.kdestroy_all(self.master)
d4adbd
+        tasks.kinit_admin(self.master)
d4adbd
+        #  rule: keycloakuser are allowed to execute yum on
d4adbd
+        #  the client machine as root.
d4adbd
+        cmdlist = [
d4adbd
+            ["ipa", "sudocmd-add", "/usr/bin/yum"],
d4adbd
+            ["ipa", "sudorule-add", "sudorule"],
d4adbd
+            ['ipa', 'sudorule-add-user', '--users=keycloakuser',
d4adbd
+             'sudorule'],
d4adbd
+            ['ipa', 'sudorule-add-host', '--hosts',
d4adbd
+             self.client.hostname, 'sudorule'],
d4adbd
+            ['ipa', 'sudorule-add-runasuser',
d4adbd
+             '--users=root', 'sudorule'],
d4adbd
+            ['ipa', 'sudorule-add-allow-command',
d4adbd
+             '--sudocmds=/usr/bin/yum', 'sudorule'],
d4adbd
+            ['ipa', 'sudorule-show', 'sudorule', '--all'],
d4adbd
+            ['ipa', 'sudorule-add-option',
d4adbd
+             'sudorule', '--sudooption', "!authenticate"]
d4adbd
+        ]
d4adbd
+        for cmd in cmdlist:
d4adbd
+            self.master.run_command(cmd)
d4adbd
+        tasks.clear_sssd_cache(self.master)
d4adbd
+        tasks.clear_sssd_cache(self.client)
d4adbd
+        try:
d4adbd
+            cmd = 'sudo -ll -U keycloakuser'
d4adbd
+            test = self.client.run_command(cmd).stdout_text
d4adbd
+            assert "User keycloakuser may run the following commands" in test
d4adbd
+            assert "/usr/bin/yum" in test
d4adbd
+            kinit_idp(self.client, 'keycloakuser', self.client)
d4adbd
+            test_sudo = 'su -c "sudo yum list wget" keycloakuser'
d4adbd
+            self.client.run_command(test_sudo)
d4adbd
+            list_fail = self.master.run_command(cmd).stdout_text
d4adbd
+            assert "User keycloakuser is not allowed to run sudo" in list_fail
d4adbd
+        finally:
d4adbd
+            tasks.kinit_admin(self.master)
d4adbd
+            self.master.run_command(['ipa', 'sudorule-del', 'sudorule'])
d4adbd
+            self.master.run_command(["ipa", "sudocmd-del", "/usr/bin/yum"])
d4adbd
+
d4adbd
+    def test_auth_replica(self):
d4adbd
+        """
d4adbd
+        Test case to check that OAuth 2.0 Device
d4adbd
+        Authorization is working as expected on replica.
d4adbd
+        """
d4adbd
+        tasks.clear_sssd_cache(self.master)
d4adbd
+        tasks.clear_sssd_cache(self.replica)
d4adbd
+        tasks.kinit_admin(self.replica)
d4adbd
+        list_user = self.master.run_command(
d4adbd
+            ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test"]
d4adbd
+        )
d4adbd
+        assert "keycloakuser" in list_user.stdout_text
d4adbd
+        list_by_idp = self.replica.run_command(["ipa", "user-find",
d4adbd
+                                                "--idp=keycloakidp"]
d4adbd
+                                               )
d4adbd
+        assert "keycloakuser" in list_by_idp.stdout_text
d4adbd
+        list_by_user = self.replica.run_command(
d4adbd
+            ["ipa", "user-find", "--idp-user-id=testuser1@ipa.test", "--all"]
d4adbd
+        )
d4adbd
+        assert "keycloakidp" in list_by_user.stdout_text
d4adbd
+        kinit_idp(self.replica, 'keycloakuser', keycloak_server=self.client)
d4adbd
+
d4adbd
+    def test_idp_with_services(self):
d4adbd
+        """
d4adbd
+        Test case to check that services can be configured
d4adbd
+        auth indicator as idp.
d4adbd
+        """
d4adbd
         tasks.clear_sssd_cache(self.master)
d4adbd
-        kinit_idp(self.master, 'keycloakuser', keycloak_srv)
d4adbd
+        tasks.kinit_admin(self.master)
d4adbd
+        domain = self.master.domain.name.upper()
d4adbd
+        services = [
d4adbd
+            "DNS/{0}@{1}".format(self.master.hostname, domain),
d4adbd
+            "HTTP/{0}@{1}".format(self.client.hostname, domain),
d4adbd
+            "dogtag/{0}@{1}".format(self.master.hostname, domain),
d4adbd
+            "ipa-dnskeysyncd/{0}@{1}".format(self.master.hostname, domain)
d4adbd
+        ]
d4adbd
+        try:
d4adbd
+            for service in services:
d4adbd
+                test = self.master.run_command(["ipa", "service-mod", service,
d4adbd
+                                                "--auth-ind=idp"]
d4adbd
+                                               )
d4adbd
+                assert "Authentication Indicators: idp" in test.stdout_text
d4adbd
+        finally:
d4adbd
+            for service in services:
d4adbd
+                self.master.run_command(["ipa", "service-mod", service,
d4adbd
+                                         "--auth-ind="])
d4adbd
+
d4adbd
+    def test_idp_backup_restore(self):
d4adbd
+        """
d4adbd
+        Test case to check that after restore data is retrieved
d4adbd
+        with related idp configuration.
d4adbd
+        """
d4adbd
+        tasks.kinit_admin(self.master)
d4adbd
+        user = "backupuser"
d4adbd
+        cmd = ["ipa", "idp-add", "testidp", "--provider=keycloak",
d4adbd
+               "--client-id=ipa_oidc_client", "--org=master",
d4adbd
+               "--base-url={0}:8443/auth".format(self.client.hostname)]
d4adbd
+        self.master.run_command(cmd, stdin_text="{0}\n{0}".format(
d4adbd
+            self.client.config.admin_password))
d4adbd
+
d4adbd
+        tasks.user_add(self.master, user,
d4adbd
+                       extra_args=["--user-auth-type=idp",
d4adbd
+                                   "--idp-user-id=testuser1@ipa.test",
d4adbd
+                                   "--idp=testidp"]
d4adbd
+                       )
d4adbd
+
d4adbd
+        backup_path = tasks.get_backup_dir(self.master)
d4adbd
+        # change data after backup
d4adbd
+        self.master.run_command(['ipa', 'user-del', user])
d4adbd
+        self.master.run_command(['ipa', 'idp-del', 'testidp'])
d4adbd
+        dirman_password = self.master.config.dirman_password
d4adbd
+        self.master.run_command(['ipa-restore', backup_path],
d4adbd
+                                stdin_text=dirman_password + '\nyes')
d4adbd
+        try:
d4adbd
+            list_user = self.master.run_command(
d4adbd
+                ['ipa', 'user-show', 'backupuser', '--all']
d4adbd
+            ).stdout_text
d4adbd
+            assert "External IdP configuration: testidp" in list_user
d4adbd
+            assert "User authentication types: idp" in list_user
d4adbd
+            assert ("External IdP user identifier: "
d4adbd
+                    "testuser1@ipa.test") in list_user
d4adbd
+            list_idp = self.master.run_command(['ipa', 'idp-find', 'testidp'])
d4adbd
+            assert "testidp" in list_idp.stdout_text
d4adbd
+            kinit_idp(self.master, user, self.client)
d4adbd
+        finally:
d4adbd
+            tasks.kdestroy_all(self.master)
d4adbd
+            tasks.kinit_admin(self.master)
d4adbd
+            self.master.run_command(["rm", "-rf", backup_path])
d4adbd
+            self.master.run_command(["ipa", "idp-del", "testidp"])
d4adbd
-- 
d4adbd
2.36.1
d4adbd