mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipatests: add Keycloak Bridge test
Add test code for new bridge server (ipa-tuura) and Keycloak plugin. Add uninstall functions for create_keycloak.py so that the tests can be run repeatedly. Fixes: https://pagure.io/freeipa/issue/9227 Signed-off-by: Scott Poore <spoore@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com> Reviewed-By: Anuja More <amore@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
parent
36591995ac
commit
a4da017272
171
ipatests/pytest_ipa/integration/create_bridge.py
Normal file
171
ipatests/pytest_ipa/integration/create_bridge.py
Normal file
@ -0,0 +1,171 @@
|
||||
import re
|
||||
import textwrap
|
||||
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
|
||||
|
||||
def setup_scim_server(host, version="main"):
|
||||
dir = "/opt/ipa-tuura"
|
||||
password = host.config.admin_password
|
||||
tasks.install_packages(host, ["unzip", "java-11-openjdk-headless",
|
||||
"openssl", "maven", "wget", "git",
|
||||
"firefox", "xorg-x11-server-Xvfb",
|
||||
"python3-pip"])
|
||||
|
||||
# Download ipa-tuura project
|
||||
url = "https://github.com/freeipa/ipa-tuura"
|
||||
host.run_command(["git", "clone", "-b", f"{version}", f"{url}", f"{dir}"])
|
||||
|
||||
# Prepare SSSD config
|
||||
host.run_command(["python", "./prepare_sssd.py"],
|
||||
cwd=f"{dir}/src/install")
|
||||
|
||||
# Install django requirements
|
||||
django_reqs = f"{dir}/src/install/requirements.txt"
|
||||
host.run_command(["pip", "install", "-r", f"{django_reqs}"])
|
||||
|
||||
# Prepare models and database
|
||||
host.run_command(["python", "manage.py", "makemigrations", "ipatuura"],
|
||||
cwd=f"{dir}/src/ipa-tuura")
|
||||
host.run_command(["python", "manage.py", "migrate"],
|
||||
cwd=f"{dir}/src/ipa-tuura")
|
||||
|
||||
# Add necessary admin vars to bashrc
|
||||
env_vars = textwrap.dedent(f"""
|
||||
export DJANGO_SUPERUSER_PASSWORD={password}
|
||||
export DJANGO_SUPERUSER_USERNAME=scim
|
||||
export DJANGO_SUPERUSER_EMAIL=scim@{host.domain.name}
|
||||
""")
|
||||
|
||||
tasks.backup_file(host, '/etc/bashrc')
|
||||
content = host.get_file_contents('/etc/bashrc', encoding='utf-8')
|
||||
new_content = content + f"\n{env_vars}"
|
||||
host.put_file_contents('/etc/bashrc', new_content)
|
||||
host.run_command(['bash'])
|
||||
|
||||
# Create django admin
|
||||
host.run_command(["python", "manage.py", "createsuperuser",
|
||||
"--scim_username", "scim", "--noinput"],
|
||||
cwd=f"{dir}/src/ipa-tuura")
|
||||
|
||||
# Open allowed hosts to any for testing
|
||||
regex = r"^(ALLOWED_HOSTS) .*$"
|
||||
replace = r"\1 = ['*']"
|
||||
settings_file = f"{dir}/src/ipa-tuura/root/settings.py"
|
||||
settings = host.get_file_contents(settings_file, encoding='utf-8')
|
||||
new_settings = re.sub(regex, replace, settings, flags=re.MULTILINE)
|
||||
host.put_file_contents(settings_file, new_settings)
|
||||
|
||||
# Setup keycloak service and config files
|
||||
contents = textwrap.dedent(f"""
|
||||
DJANGO_SUPERUSER_USERNAME=scim
|
||||
DJANGO_SUPERUSER_PASSWORD={password}
|
||||
DJANGO_SUPERUSER_EMAIL=scim@{host.domain.name}
|
||||
""")
|
||||
host.put_file_contents("/etc/sysconfig/scim", contents)
|
||||
|
||||
manage = f"{dir}/src/ipa-tuura/manage.py"
|
||||
contents = textwrap.dedent(f"""
|
||||
[Unit]
|
||||
Description=SCIMv2 Bridge Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=idle
|
||||
WorkingDirectory={dir}/src/ipa-tuura/
|
||||
EnvironmentFile=/etc/sysconfig/scim
|
||||
# Fix this later
|
||||
# User=scim
|
||||
# Group=scim
|
||||
ExecStart=/usr/bin/python {manage} runserver 0.0.0.0:8000
|
||||
TimeoutStartSec=600
|
||||
TimeoutStopSec=600
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
""")
|
||||
host.put_file_contents("/etc/systemd/system/scim.service", contents)
|
||||
host.run_command(["systemctl", "daemon-reload"])
|
||||
host.run_command(["systemctl", "start", "scim"])
|
||||
|
||||
|
||||
def setup_keycloak_scim_plugin(host, bridge_server):
|
||||
dir = "/opt/keycloak"
|
||||
password = host.config.admin_password
|
||||
|
||||
# Install needed packages
|
||||
tasks.install_packages(host, ["unzip", "java-11-openjdk-headless",
|
||||
"openssl", "maven"])
|
||||
|
||||
# Add necessary admin vars to bashrc
|
||||
env_vars = textwrap.dedent(f"""
|
||||
export KEYCLOAK_PATH={dir}
|
||||
""")
|
||||
|
||||
content = host.get_file_contents('/etc/bashrc', encoding='utf-8')
|
||||
new_content = content + f"\n{env_vars}"
|
||||
host.put_file_contents('/etc/bashrc', new_content)
|
||||
host.run_command(['bash'])
|
||||
|
||||
# Download keycloak plugin
|
||||
zipfile = "scim-keycloak-user-storage-spi/archive/refs/tags/0.1.zip"
|
||||
url = f"https://github.com/justin-stephenson/{zipfile}"
|
||||
dest = "/tmp/keycloak-scim-plugin.zip"
|
||||
host.run_command(["wget", "-O", dest, url])
|
||||
|
||||
# Unzip keycloak plugin
|
||||
host.run_command(["unzip", dest, "-d", "/tmp"])
|
||||
|
||||
# Install plugin
|
||||
host.run_command(["./redeploy-plugin.sh"],
|
||||
cwd="/tmp/scim-keycloak-user-storage-spi-0.1")
|
||||
|
||||
# Fix ownership of plugin files
|
||||
host.run_command(["chown", "-R", "keycloak:keycloak", dir])
|
||||
|
||||
# Restore SELinux contexts
|
||||
host.run_command(["restorecon", "-R", f"{dir}"])
|
||||
|
||||
# Rerun Keycloak build step and restart to pickup plugin
|
||||
# This relies on the KC_* vars set in /etc/bashrc from create_keycloak.py
|
||||
host.run_command(['su', '-', 'keycloak', '-c',
|
||||
'/opt/keycloak/bin/kc.sh build'])
|
||||
host.run_command(["systemctl", "restart", "keycloak"])
|
||||
host.run_command(["/opt/keycloak/bin/kc.sh", "show-config"])
|
||||
|
||||
# Login to keycloak as admin
|
||||
kcadmin_sh = "/opt/keycloak/bin/kcadm.sh"
|
||||
kcadmin = [kcadmin_sh, "config", "credentials", "--server",
|
||||
f"https://{host.hostname}:8443/auth/",
|
||||
"--realm", "master", "--user", "admin",
|
||||
"--password", password]
|
||||
tasks.run_repeatedly(host, kcadmin, timeout=60)
|
||||
|
||||
# Configure SCIM User Storage to point to Bridge server
|
||||
provider_type = "org.keycloak.storage.UserStorageProvider"
|
||||
host.run_command([kcadmin_sh, "create", "components",
|
||||
"-r", "master",
|
||||
"-s", "name=scimprov",
|
||||
"-s", "providerId=scim",
|
||||
"-s", f"providerType={provider_type}",
|
||||
"-s", "parentId=master",
|
||||
"-s", f'config.scimurl=["{bridge_server}:8000"]',
|
||||
"-s", 'config.loginusername=["scim"]',
|
||||
"-s", f'config.loginpassword=["{password}"]'])
|
||||
|
||||
|
||||
def uninstall_scim_server(host):
|
||||
host.run_command(["systemctl", "stop", "scim"], raiseonerr=False)
|
||||
host.run_command(["rm", "-rf", "/opt/ipa-tuura",
|
||||
"/etc/sysconfig/scim",
|
||||
"/etc/systemd/system/scim.service",
|
||||
"/tmp/scim-keycloak-user-storage-spi-main",
|
||||
"/tmp/keycloak-scim-plugin.zip"])
|
||||
host.run_command(["systemctl", "daemon-reload"])
|
||||
tasks.restore_files(host)
|
||||
|
||||
|
||||
def uninstall_scim_plugin(host):
|
||||
host.run_command(["rm", "-rf",
|
||||
"/tmp/scim-keycloak-user-storage-spi-main",
|
||||
"/tmp/keycloak-scim-plugin.zip"])
|
@ -95,6 +95,7 @@ def setup_keycloakserver(host, version='17.0.0'):
|
||||
""").format(hostname=host.hostname, STORE_PASS=password,
|
||||
ADMIN_PASS=password)
|
||||
|
||||
tasks.backup_file(host, '/etc/bashrc')
|
||||
content = host.get_file_contents('/etc/bashrc',
|
||||
encoding='utf-8')
|
||||
new_content = content + "\n{}".format(env_vars)
|
||||
@ -157,3 +158,21 @@ def setup_keycloak_client(host):
|
||||
"-s", "secret={0}".format(password)]
|
||||
)
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
def uninstall_keycloak(host):
|
||||
key = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.key")
|
||||
crt = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.crt")
|
||||
keystore = os.path.join(paths.OPENSSL_PRIVATE_DIR, "keycloak.store")
|
||||
|
||||
host.run_command(["systemctl", "stop", "keycloak"], raiseonerr=False)
|
||||
host.run_command(["getcert", "stop-tracking", "-k", key, "-f", crt],
|
||||
raiseonerr=False)
|
||||
host.run_command(["rm", "-rf", "/opt/keycloak",
|
||||
"/etc/sysconfig/keycloak",
|
||||
"/etc/systemd/system/keycloak.service",
|
||||
key, crt, keystore])
|
||||
host.run_command(["systemctl", "daemon-reload"])
|
||||
host.run_command(["userdel", "keycloak"])
|
||||
host.run_command(["groupdel", "keycloak"], raiseonerr=False)
|
||||
tasks.restore_files(host)
|
||||
|
100
ipatests/test_integration/test_sso.py
Normal file
100
ipatests/test_integration/test_sso.py
Normal file
@ -0,0 +1,100 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import textwrap
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
from ipatests.pytest_ipa.integration import tasks, create_keycloak
|
||||
from ipatests.pytest_ipa.integration import create_bridge
|
||||
|
||||
user_code_script = textwrap.dedent("""
|
||||
from selenium import webdriver
|
||||
from datetime import datetime
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
options = Options()
|
||||
options.headless = True
|
||||
driver = webdriver.Firefox(executable_path="/opt/geckodriver", options=options)
|
||||
verification_uri = "https://{hostname}:8443/auth/realms/master/account/#/"
|
||||
driver.get(verification_uri)
|
||||
|
||||
try:
|
||||
element = WebDriverWait(driver, 90).until(
|
||||
EC.element_to_be_clickable((By.ID, "landingSignInButton")))
|
||||
driver.find_element(By.ID, "landingSignInButton").click()
|
||||
element = WebDriverWait(driver, 90).until(
|
||||
EC.presence_of_element_located((By.ID, "kc-login")))
|
||||
driver.find_element(By.ID, "username").send_keys("{username}")
|
||||
driver.find_element(By.ID, "password").send_keys("{password}")
|
||||
driver.find_element(By.ID, "kc-login").click()
|
||||
element = WebDriverWait(driver, 900).until(
|
||||
EC.text_to_be_present_in_element((By.ID, "landingLoggedInUser"),
|
||||
"{username_fl}"))
|
||||
assert driver.find_element(By.ID, "landingLoggedInUser").text \
|
||||
== "{username_fl}"
|
||||
finally:
|
||||
now = datetime.now().strftime("%M-%S")
|
||||
driver.get_screenshot_as_file("/var/log/httpd/screenshot-%s.png" % now)
|
||||
driver.quit()
|
||||
""")
|
||||
|
||||
|
||||
def keycloak_login(host, username, password, username_fl=None):
|
||||
if username_fl is None:
|
||||
username_fl = username
|
||||
contents = user_code_script.format(hostname=host.hostname,
|
||||
username=username,
|
||||
password=password,
|
||||
username_fl=username_fl)
|
||||
try:
|
||||
host.put_file_contents("/tmp/keycloak_login.py", contents)
|
||||
tasks.run_repeatedly(host, ['python3', '/tmp/keycloak_login.py'])
|
||||
finally:
|
||||
host.run_command(["rm", "-f", "/tmp/keycloak_login.py"])
|
||||
|
||||
|
||||
class TestSsoBridge(IntegrationTest):
|
||||
|
||||
# Replicas used instead of clients due to memory requirements
|
||||
# for running Keycloak and Bridge servers
|
||||
num_replicas = 2
|
||||
|
||||
@classmethod
|
||||
def install(cls, mh):
|
||||
cls.keycloak = cls.replicas[0]
|
||||
cls.bridge = cls.replicas[1]
|
||||
tasks.install_master(cls.master, extra_args=['--no-dnssec-validation'])
|
||||
tasks.install_client(cls.master, cls.replicas[0],
|
||||
extra_args=["--mkhomedir"])
|
||||
tasks.install_client(cls.master, cls.replicas[1],
|
||||
extra_args=["--mkhomedir"])
|
||||
tasks.clear_sssd_cache(cls.master)
|
||||
tasks.clear_sssd_cache(cls.keycloak)
|
||||
tasks.clear_sssd_cache(cls.bridge)
|
||||
tasks.kinit_admin(cls.master)
|
||||
username = 'ipauser1'
|
||||
password = cls.keycloak.config.admin_password
|
||||
tasks.create_active_user(cls.master, username, password)
|
||||
create_keycloak.setup_keycloakserver(cls.keycloak)
|
||||
create_keycloak.setup_keycloak_client(cls.keycloak)
|
||||
create_bridge.setup_scim_server(cls.bridge)
|
||||
create_bridge.setup_keycloak_scim_plugin(cls.keycloak,
|
||||
cls.bridge.hostname)
|
||||
|
||||
@classmethod
|
||||
def uninstall(cls, mh):
|
||||
tasks.uninstall_client(cls.keycloak)
|
||||
tasks.uninstall_client(cls.bridge)
|
||||
tasks.uninstall_master(cls.master)
|
||||
create_keycloak.uninstall_keycloak(cls.keycloak)
|
||||
create_bridge.uninstall_scim_server(cls.bridge)
|
||||
create_bridge.uninstall_scim_plugin(cls.keycloak)
|
||||
|
||||
def test_sso_login_with_ipa_user(self):
|
||||
"""
|
||||
Test case to check authenticating to Keycloak as an IPA user
|
||||
"""
|
||||
username = 'ipauser1'
|
||||
username_fl = 'test user'
|
||||
password = self.keycloak.config.admin_password
|
||||
keycloak_login(self.keycloak, username, password, username_fl)
|
Loading…
Reference in New Issue
Block a user