tests/e2e: add endpoint tests (#19072)

* tests/e2e: add endpoint tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* dont rely on hostname

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
Jens L.
2025-12-28 12:53:00 +01:00
committed by GitHub
parent a98d70ea7b
commit 4f4e37f2b0
4 changed files with 100 additions and 2 deletions

View File

@@ -1,11 +1,13 @@
services:
chromium:
image: docker.io/selenium/standalone-chromium:143.0
image: ghcr.io/goauthentik/selenium:143.0-ak-0.35.3
shm_size: 2g
network_mode: host
restart: always
extra_hosts:
- "host.docker.internal:host-gateway"
labels:
- io.goauthentik.tests=selenium
mailpit:
image: docker.io/axllent/mailpit:v1.28.0
ports:

View File

@@ -0,0 +1,64 @@
"""test default login flow"""
from authentik.blueprints.tests import apply_blueprint, reconcile_app
from authentik.crypto.apps import MANAGED_KEY
from authentik.crypto.models import CertificateKeyPair
from authentik.endpoints.connectors.agent.models import AgentConnector, EnrollmentToken
from authentik.endpoints.models import Device, EndpointStage, StageMode
from authentik.events.models import Event, EventAction
from authentik.flows.models import Flow, FlowStageBinding
from authentik.lib.generators import generate_id
from tests.e2e.utils import SeleniumTestCase, retry
class TestEndpointsFlow(SeleniumTestCase):
"""test default login flow"""
@reconcile_app("authentik_crypto")
def setUp(self):
super().setUp()
self.connector = AgentConnector.objects.create(
name=generate_id(),
challenge_key=CertificateKeyPair.objects.filter(managed=MANAGED_KEY).first(),
)
self.enrollment_token = EnrollmentToken.objects.create(
name=generate_id(), connector=self.connector
)
@retry()
@apply_blueprint(
"default/flow-default-authentication-flow.yaml",
"default/flow-default-invalidation-flow.yaml",
)
def test_login(self):
"""test default login flow"""
rc, output = self.driver_container.exec_run(
["ak-sysd", "domains", "join", "ak", "-a", self.live_server_url],
user="root",
environment={"AK_SYS_INSECURE_ENV_TOKEN": self.enrollment_token.key},
)
self.assertEqual(rc, 0, str(output))
dev = Device.objects.first()
self.assertIsNotNone(dev)
stage = EndpointStage.objects.create(
name=generate_id(), connector=self.connector, mode=StageMode.REQUIRED
)
FlowStageBinding.objects.create(
target=Flow.objects.get(slug="default-authentication-flow"), stage=stage, order=0
)
self.driver.get(
self.url(
"authentik_core:if-flow",
flow_slug="default-authentication-flow",
)
)
self.login()
self.wait_for_url(self.if_user_url("/library"))
self.assert_user(self.user)
login_evt = Event.objects.filter(action=EventAction.LOGIN).first()
self.assertIsNotNone(login_evt)
self.assertEqual(login_evt.context["device"]["pk"], dev.pk.hex)

View File

@@ -2,10 +2,12 @@
import socket
from collections.abc import Callable
from functools import lru_cache, wraps
from functools import cached_property, lru_cache, wraps
from json import JSONDecodeError, dumps, loads
from os import environ, getenv
from pathlib import Path
from sys import stderr
from tempfile import gettempdir
from time import sleep
from typing import Any
from unittest.case import TestCase
@@ -21,6 +23,7 @@ from docker import DockerClient, from_env
from docker.errors import DockerException
from docker.models.containers import Container
from docker.models.networks import Network
from requests import RequestException
from selenium import webdriver
from selenium.common.exceptions import (
DetachedShadowRootException,
@@ -43,6 +46,7 @@ from authentik.core.api.users import UserSerializer
from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.generators import generate_id
from authentik.lib.utils.http import get_http_session
from authentik.root.test_runner import get_docker_tag
IS_CI = "CI" in environ
@@ -179,6 +183,7 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
opts = webdriver.ChromeOptions()
opts.accept_insecure_certs = True
opts.add_argument("--disable-search-engine-choice-screen")
opts.add_extension(self._get_chrome_extension())
# This breaks selenium when running remotely...?
# opts.set_capability("goog:loggingPrefs", {"browser": "ALL"})
opts.add_experimental_option(
@@ -200,6 +205,31 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase):
count += 1
raise ValueError(f"Webdriver failed after {RETRIES}.")
def _get_chrome_extension(self):
path = Path(gettempdir()) / "ak-chrome.crx"
try:
self.logger.info("Downloading chrome extension...", path=path)
res = get_http_session().get(
"https://pkg.goauthentik.io/packages/authentik_browser-ext/browser-ext/authentik_chrome.zip",
stream=True,
)
with open(path, "w+b") as _ext:
for chunk in res.iter_content(chunk_size=1024):
if chunk:
_ext.write(chunk)
except RequestException as exc:
if path.exists() and not IS_CI:
self.logger.info(
"Failed to download chrome extension, using cached copy", path=path
)
return path
raise exc
return path
@cached_property
def driver_container(self) -> Container:
return self.docker_client.containers.list(filters={"label": "io.goauthentik.tests"})[0]
def tearDown(self):
if IS_CI:
print("::endgroup::", file=stderr)