Skip to content

Commit

Permalink
Tests: Use keycloak container during tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ericof committed Nov 9, 2023
1 parent 484c323 commit 53d5631
Show file tree
Hide file tree
Showing 9 changed files with 3,770 additions and 10 deletions.
1 change: 0 additions & 1 deletion src/pas/plugins/oidc/browser/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ def __call__(self):
"nonce": session.get("nonce"),
"redirect_uri": self.context.get_redirect_uris(),
}

if self.context.getProperty("use_pkce"):
# Build a random string of 43 to 128 characters
# and send it in the request as a base64-encoded urlsafe string of the sha256 hash of that string
Expand Down
19 changes: 19 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
from pas.plugins.oidc.testing import FUNCTIONAL_TESTING
from pas.plugins.oidc.testing import INTEGRATION_TESTING
from pathlib import Path
from pytest_plone import fixtures_factory

import pytest


pytest_plugins = ["pytest_plone"]

Expand All @@ -14,3 +17,19 @@
)
)
)


@pytest.fixture(scope="session")
def docker_compose_file(pytestconfig):
"""Fixture pointing to the docker-compose file to be used."""
return Path(str(pytestconfig.rootdir)).resolve() / "tests" / "docker-compose.yml"


@pytest.fixture
def wait_for():
def func(thread):
if not thread:
return
thread.join()

return func
41 changes: 41 additions & 0 deletions tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
version: "3.9"

services:
keycloak:
build:
context: keycloak
args:
KEYCLOAK_VERSION: 22.0.0
command: ['start-dev', '--import-realm']
depends_on:
- db
environment:
JAVA_OPTS_APPEND: -Dkeycloak.profile.feature.upload_scripts=enabled
KC_DB: postgres
KC_DB_PASSWORD: postgres
KC_DB_URL: jdbc:postgresql://db/keycloak
KC_DB_USERNAME: postgres
KC_HEALTH_ENABLED: false
KC_HTTP_ENABLED: true
KC_METRICS_ENABLED: false
KC_HOSTNAME_URL: http://127.0.0.1:8180/
KC_PROXY: reencrypt
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
volumes:
- ./keycloak/import:/opt/keycloak/data/import
ports:
- 8180:8080

db:
image: postgres:14.9
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "postgres", "-U", "root" ]
timeout: 45s
interval: 5s
retries: 10
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: keycloak
POSTGRES_HOST: postgres
52 changes: 50 additions & 2 deletions tests/functional/conftest.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,70 @@
from plone import api
from plone.app.testing import SITE_OWNER_NAME
from plone.app.testing import SITE_OWNER_PASSWORD
from plone.app.testing import TEST_USER_NAME
from plone.app.testing import TEST_USER_PASSWORD
from plone.restapi.testing import RelativeSession
from plone.testing.zope import Browser
from requests.exceptions import ConnectionError
from zope.component.hooks import setSite

import pytest
import requests
import transaction


def is_responsive(url: str) -> bool:
try:
response = requests.get(url)
if response.status_code == 200:
return True
except ConnectionError:
return False


@pytest.fixture(scope="session")
def keycloak_service(docker_ip, docker_services):
"""Ensure that keycloak service is up and responsive."""
# `port_for` takes a container port and returns the corresponding host port
port = docker_services.port_for("keycloak", 8080)
url = f"http://{docker_ip}:{port}"
docker_services.wait_until_responsive(
timeout=30.0, pause=0.1, check=lambda: is_responsive(url)
)
return url


@pytest.fixture()
def app(functional):
return functional["app"]


@pytest.fixture(scope="session")
def keycloak(keycloak_service):
return {
"issuer": f"{keycloak_service}/realms/plone-test",
"client_id": "plone",
"client_secret": "12345678", # nosec B105
"scope": ("openid", "profile", "email"),
}


@pytest.fixture()
def portal(functional):
def portal(functional, keycloak):
portal = functional["portal"]
return portal
setSite(portal)
plugin = portal.acl_users.oidc
with api.env.adopt_roles(["Manager", "Member"]):
for key, value in keycloak.items():
setattr(plugin, key, value)
transaction.commit()
yield portal
with api.env.adopt_roles(["Manager", "Member"]):
for key, value in keycloak.items():
if key != "scope":
value = ""
setattr(plugin, key, value)
transaction.commit()


@pytest.fixture()
Expand Down
15 changes: 8 additions & 7 deletions tests/functional/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,14 @@ def test_challenge_anonymous(self, browser_anonymous):
location = browser.headers["location"]
expected = f"{self.plugin_url}/login?came_from={quoted_url}"
assert location == expected

# If we would follow the redirect, we would get a 500 Internal Server Error
# because the login view calls get_oauth2_client on the plugin,
# and we have not setup such a client in the tests. We may need to mock
# some code.
# But the challenge plugin works: it makes sure we end up on the login view
# of our plugin, and not on the standard Plone login form.
# Follow one redirect, view the login page of the plugin
browser.handleErrors = False
browser.open(location)
assert browser.url == location
# The next redirect will send us to keycloak
location = browser.headers["location"]
expected = "http://127.0.0.1:8180/realms/plone-test/protocol/openid-connect/auth?client_id=plone"
assert location.startswith(expected)

def test_challenge_authenticated_user(self, browser_user):
browser = browser_user
Expand Down
30 changes: 30 additions & 0 deletions tests/keycloak/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1
ARG KEYCLOAK_VERSION
FROM quay.io/keycloak/keycloak:$KEYCLOAK_VERSION as builder

# Configure postgres database vendor
ENV KC_DB=postgres

# Disable health and metrics support
ENV KC_HEALTH_ENABLED=false
ENV KC_METRICS_ENABLED=false

# Enable features
ENV KC_FEATURES="token-exchange,scripts,preview,admin_fine_grained_authz"

WORKDIR /opt/keycloak

# Build
RUN /opt/keycloak/bin/kc.sh build --cache=ispn

FROM quay.io/keycloak/keycloak:$KEYCLOAK_VERSION
LABEL image.version=$KEYCLOAK_VERSION
COPY --from=builder /opt/keycloak/ /opt/keycloak/

USER root
RUN sed -i '/disabledAlgorithms/ s/ SHA1,//' /etc/crypto-policies/back-ends/java.config
USER keycloak

RUN /opt/keycloak/bin/kc.sh show-config

ENTRYPOINT ["/opt/keycloak/bin/kc.sh"]
Empty file added tests/keycloak/import/.gitkeep
Empty file.
Loading

0 comments on commit 53d5631

Please sign in to comment.