tests/e2e: add forward auth e2e test (#11374)
* add nginx forward_auth e2e tests Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add envoy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove even more duplicate code Signed-off-by: Jens Langhammer <jens@goauthentik.io> * cleanup more Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add traefik static config Signed-off-by: Jens Langhammer <jens@goauthentik.io> * more cleanup, don't generate dex config cause they support env variables Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use default dex entrypoint to use templating Signed-off-by: Jens Langhammer <jens@goauthentik.io> * remove options that are always set as default Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix compose flag Signed-off-by: Jens Langhammer <jens@goauthentik.io> * add caddy Signed-off-by: Jens Langhammer <jens@goauthentik.io> * merge python files Signed-off-by: Jens Langhammer <jens@goauthentik.io> * use whoami api to check better Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix envoy config Signed-off-by: Jens Langhammer <jens@goauthentik.io> * set invalidation flow Signed-off-by: Jens Langhammer <jens@goauthentik.io> * fix logout checks Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/ci-main.yml
									
									
									
									
										vendored
									
									
								
							| @ -180,7 +180,7 @@ jobs: | |||||||
|         uses: ./.github/actions/setup |         uses: ./.github/actions/setup | ||||||
|       - name: Setup e2e env (chrome, etc) |       - name: Setup e2e env (chrome, etc) | ||||||
|         run: | |         run: | | ||||||
|           docker compose -f tests/e2e/docker-compose.yml up -d |           docker compose -f tests/e2e/docker-compose.yml up -d --quiet-pull | ||||||
|       - id: cache-web |       - id: cache-web | ||||||
|         uses: actions/cache@v4 |         uses: actions/cache@v4 | ||||||
|         with: |         with: | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								tests/e2e/proxy_forward_auth/caddy_single/Caddyfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								tests/e2e/proxy_forward_auth/caddy_single/Caddyfile
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,21 @@ | |||||||
|  | http://localhost { | ||||||
|  |     # directive execution order is only as stated if enclosed with route. | ||||||
|  |     route { | ||||||
|  |         # always forward outpost path to actual outpost | ||||||
|  |         reverse_proxy /outpost.goauthentik.io/* http://ak-test-outpost:9000 | ||||||
|  |  | ||||||
|  |         # forward authentication to outpost | ||||||
|  |         forward_auth http://ak-test-outpost:9000 { | ||||||
|  |             uri /outpost.goauthentik.io/auth/caddy | ||||||
|  |  | ||||||
|  |             # capitalization of the headers is important, otherwise they will be empty | ||||||
|  |             copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version | ||||||
|  |  | ||||||
|  |             # optional, in this config trust all private ranges, should probably be set to the outposts IP | ||||||
|  |             trusted_proxies private_ranges | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         # actual site configuration below, for example | ||||||
|  |         reverse_proxy ak-whoami | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										99
									
								
								tests/e2e/proxy_forward_auth/envoy_single/envoy.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								tests/e2e/proxy_forward_auth/envoy_single/envoy.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,99 @@ | |||||||
|  | # yaml-language-server: $schema=https://github.com/jcchavezs/envoy-config-schema/releases/download/v1.21.0/v3_Bootstrap.json | ||||||
|  | static_resources: | ||||||
|  |   listeners: | ||||||
|  |     - name: main_listener | ||||||
|  |       address: | ||||||
|  |         socket_address: | ||||||
|  |           address: 0.0.0.0 | ||||||
|  |           port_value: 10000 | ||||||
|  |       filter_chains: | ||||||
|  |         - filters: | ||||||
|  |             - name: envoy.filters.network.http_connection_manager | ||||||
|  |               typed_config: | ||||||
|  |                 "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager | ||||||
|  |                 stat_prefix: ingress_http | ||||||
|  |                 upgrade_configs: | ||||||
|  |                   - upgrade_type: websocket | ||||||
|  |                 access_log: | ||||||
|  |                   - name: envoy.access_loggers.stdout | ||||||
|  |                     typed_config: | ||||||
|  |                       "@type": type.googleapis.com/envoy.extensions.access_loggers.stream.v3.StdoutAccessLog | ||||||
|  |                 http_filters: | ||||||
|  |                   - name: envoy.filters.http.ext_authz | ||||||
|  |                     typed_config: | ||||||
|  |                       "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz | ||||||
|  |                       transport_api_version: V3 | ||||||
|  |                       http_service: | ||||||
|  |                         path_prefix: /outpost.goauthentik.io/auth/envoy | ||||||
|  |                         server_uri: | ||||||
|  |                           uri: http://ak-test-outpost:9000 | ||||||
|  |                           cluster: authentik_outpost | ||||||
|  |                           timeout: 0.25s | ||||||
|  |                         authorization_request: | ||||||
|  |                           allowed_headers: | ||||||
|  |                             patterns: | ||||||
|  |                               - exact: "cookie" | ||||||
|  |                                 ignore_case: true | ||||||
|  |                         authorization_response: | ||||||
|  |                           allowed_upstream_headers: | ||||||
|  |                             patterns: | ||||||
|  |                               - exact: "set-cookie" | ||||||
|  |                                 ignore_case: true | ||||||
|  |                               - prefix: "x-authentik-" | ||||||
|  |                                 ignore_case: true | ||||||
|  |                           allowed_client_headers_on_success: | ||||||
|  |                             patterns: | ||||||
|  |                               - exact: "cookie" | ||||||
|  |                                 ignore_case: true | ||||||
|  |                   - name: envoy.filters.http.router | ||||||
|  |                     typed_config: | ||||||
|  |                       "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router | ||||||
|  |                 route_config: | ||||||
|  |                   name: local_route | ||||||
|  |                   virtual_hosts: | ||||||
|  |                     - name: local_service | ||||||
|  |                       domains: ["localhost"] | ||||||
|  |                       routes: | ||||||
|  |                         - match: | ||||||
|  |                             prefix: "/outpost.goauthentik.io" | ||||||
|  |                           route: | ||||||
|  |                             cluster: authentik_outpost | ||||||
|  |                         - match: | ||||||
|  |                             prefix: "/" | ||||||
|  |                           route: | ||||||
|  |                             cluster: whoami | ||||||
|  |                     - name: local_service | ||||||
|  |                       domains: ["*"] | ||||||
|  |                       typed_per_filter_config: | ||||||
|  |                         envoy.filters.http.ext_authz: | ||||||
|  |                           "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute | ||||||
|  |                           disabled: true | ||||||
|  |                       routes: | ||||||
|  |                         - match: | ||||||
|  |                             prefix: "/" | ||||||
|  |                           route: | ||||||
|  |                             cluster: authentik_outpost | ||||||
|  |  | ||||||
|  |   clusters: | ||||||
|  |     - name: authentik_outpost | ||||||
|  |       type: LOGICAL_DNS | ||||||
|  |       load_assignment: | ||||||
|  |         cluster_name: authentik_outpost | ||||||
|  |         endpoints: | ||||||
|  |           - lb_endpoints: | ||||||
|  |               - endpoint: | ||||||
|  |                   address: | ||||||
|  |                     socket_address: | ||||||
|  |                       address: ak-test-outpost | ||||||
|  |                       port_value: 9000 | ||||||
|  |     - name: whoami | ||||||
|  |       type: LOGICAL_DNS | ||||||
|  |       load_assignment: | ||||||
|  |         cluster_name: whoami | ||||||
|  |         endpoints: | ||||||
|  |           - lb_endpoints: | ||||||
|  |               - endpoint: | ||||||
|  |                   address: | ||||||
|  |                     socket_address: | ||||||
|  |                       address: ak-whoami | ||||||
|  |                       port_value: 80 | ||||||
							
								
								
									
										59
									
								
								tests/e2e/proxy_forward_auth/nginx_single/nginx.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								tests/e2e/proxy_forward_auth/nginx_single/nginx.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,59 @@ | |||||||
|  | server { | ||||||
|  |     listen 80; | ||||||
|  |     server_name _; | ||||||
|  |  | ||||||
|  |     # Increase buffer size for large headers | ||||||
|  |     # This is needed only if you get 'upstream sent too big header while reading response | ||||||
|  |     # header from upstream' error when trying to access an application protected by goauthentik | ||||||
|  |     proxy_buffers 8 16k; | ||||||
|  |     proxy_buffer_size 32k; | ||||||
|  |  | ||||||
|  |     location / { | ||||||
|  |         proxy_pass http://ak-whoami; | ||||||
|  |         proxy_set_header    Host $host; | ||||||
|  |  | ||||||
|  |         ############################## | ||||||
|  |         # authentik-specific config | ||||||
|  |         ############################## | ||||||
|  |         auth_request /outpost.goauthentik.io/auth/nginx; | ||||||
|  |         error_page 401 = @goauthentik_proxy_signin; | ||||||
|  |         auth_request_set $auth_cookie $upstream_http_set_cookie; | ||||||
|  |         add_header Set-Cookie $auth_cookie; | ||||||
|  |  | ||||||
|  |         # translate headers from the outposts back to the actual upstream | ||||||
|  |         auth_request_set $authentik_username $upstream_http_x_authentik_username; | ||||||
|  |         auth_request_set $authentik_groups $upstream_http_x_authentik_groups; | ||||||
|  |         auth_request_set $authentik_email $upstream_http_x_authentik_email; | ||||||
|  |         auth_request_set $authentik_name $upstream_http_x_authentik_name; | ||||||
|  |         auth_request_set $authentik_uid $upstream_http_x_authentik_uid; | ||||||
|  |  | ||||||
|  |         proxy_set_header X-authentik-username $authentik_username; | ||||||
|  |         proxy_set_header X-authentik-groups $authentik_groups; | ||||||
|  |         proxy_set_header X-authentik-email $authentik_email; | ||||||
|  |         proxy_set_header X-authentik-name $authentik_name; | ||||||
|  |         proxy_set_header X-authentik-uid $authentik_uid; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     # all requests to /outpost.goauthentik.io must be accessible without authentication | ||||||
|  |     location /outpost.goauthentik.io { | ||||||
|  |         proxy_pass          http://ak-test-outpost:9000/outpost.goauthentik.io; | ||||||
|  |         # ensure the host of this vserver matches your external URL you've configured | ||||||
|  |         # in authentik | ||||||
|  |         proxy_set_header    Host $host; | ||||||
|  |         proxy_set_header    X-Original-URL $scheme://$http_host$request_uri; | ||||||
|  |         add_header          Set-Cookie $auth_cookie; | ||||||
|  |         auth_request_set    $auth_cookie $upstream_http_set_cookie; | ||||||
|  |         proxy_pass_request_body off; | ||||||
|  |         proxy_set_header Content-Length ""; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     # Special location for when the /auth endpoint returns a 401, | ||||||
|  |     # redirect to the /start URL which initiates SSO | ||||||
|  |     location @goauthentik_proxy_signin { | ||||||
|  |         internal; | ||||||
|  |         add_header Set-Cookie $auth_cookie; | ||||||
|  |         return 302 /outpost.goauthentik.io/start?rd=$request_uri; | ||||||
|  |         # For domain level, use the below error_page to redirect to your authentik server with the full redirect path | ||||||
|  |         # return 302 https://localhost/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @ -0,0 +1,57 @@ | |||||||
|  | # yaml-language-server: $schema=https://json.schemastore.org/traefik-v2.json | ||||||
|  | api: | ||||||
|  |   insecure: true | ||||||
|  |   debug: true | ||||||
|  |  | ||||||
|  | log: | ||||||
|  |   level: debug | ||||||
|  | accessLog: | ||||||
|  |   filePath: /dev/stdout | ||||||
|  |  | ||||||
|  | entryPoints: | ||||||
|  |   web: | ||||||
|  |     address: ":80" | ||||||
|  |  | ||||||
|  | # Re-use the same config file to define everything | ||||||
|  | providers: | ||||||
|  |   file: | ||||||
|  |     filename: /etc/traefik/traefik.yml | ||||||
|  |  | ||||||
|  | http: | ||||||
|  |   middlewares: | ||||||
|  |     authentik: | ||||||
|  |       forwardAuth: | ||||||
|  |         address: http://ak-test-outpost:9000/outpost.goauthentik.io/auth/traefik | ||||||
|  |         trustForwardHeader: true | ||||||
|  |         authResponseHeaders: | ||||||
|  |           - X-authentik-username | ||||||
|  |           - X-authentik-groups | ||||||
|  |           - X-authentik-email | ||||||
|  |           - X-authentik-name | ||||||
|  |           - X-authentik-uid | ||||||
|  |           - X-authentik-jwt | ||||||
|  |           - X-authentik-meta-jwks | ||||||
|  |           - X-authentik-meta-outpost | ||||||
|  |           - X-authentik-meta-provider | ||||||
|  |           - X-authentik-meta-app | ||||||
|  |           - X-authentik-meta-version | ||||||
|  |   routers: | ||||||
|  |     default-router: | ||||||
|  |       rule: "Host(`localhost`)" | ||||||
|  |       middlewares: | ||||||
|  |         - authentik | ||||||
|  |       priority: 10 | ||||||
|  |       service: app | ||||||
|  |     default-router-auth: | ||||||
|  |       rule: "Host(`localhost`) && PathPrefix(`/outpost.goauthentik.io/`)" | ||||||
|  |       priority: 15 | ||||||
|  |       service: authentik | ||||||
|  |   services: | ||||||
|  |     app: | ||||||
|  |       loadBalancer: | ||||||
|  |         servers: | ||||||
|  |           - url: http://ak-whoami | ||||||
|  |     authentik: | ||||||
|  |       loadBalancer: | ||||||
|  |         servers: | ||||||
|  |           - url: http://ak-test-outpost:9000/outpost.goauthentik.io | ||||||
							
								
								
									
										23
									
								
								tests/e2e/sources_oauth2_dex/dex.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/e2e/sources_oauth2_dex/dex.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | --- | ||||||
|  | enablePasswordDB: true | ||||||
|  | issuer: http://127.0.0.1:5556/dex | ||||||
|  | logger: | ||||||
|  |   level: debug | ||||||
|  | staticClients: | ||||||
|  |   - id: example-app | ||||||
|  |     name: Example App | ||||||
|  |     redirectURIs: | ||||||
|  |       - {{ .Env.AK_REDIRECT_URL }} | ||||||
|  |     secret: {{ .Env.AK_CLIENT_SECRET }} | ||||||
|  | staticPasswords: | ||||||
|  |   - email: admin@example.com | ||||||
|  |     # hash for 'password', for testing | ||||||
|  |     hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W" | ||||||
|  |     userID: "08a8684b-db88-4b73-90a9-3cd1661f5466" | ||||||
|  |     username: admin | ||||||
|  | storage: | ||||||
|  |   config: | ||||||
|  |     file: "/tmp/dex.db" | ||||||
|  |   type: sqlite3 | ||||||
|  | web: | ||||||
|  |   http: 0.0.0.0:5556 | ||||||
| @ -3,8 +3,6 @@ | |||||||
| from dataclasses import asdict | from dataclasses import asdict | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from docker.client import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from guardian.shortcuts import assign_perm | from guardian.shortcuts import assign_perm | ||||||
| from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server | from ldap3 import ALL, ALL_ATTRIBUTES, ALL_OPERATIONAL_ATTRIBUTES, SUBTREE, Connection, Server | ||||||
| from ldap3.core.exceptions import LDAPInvalidCredentialsResult | from ldap3.core.exceptions import LDAPInvalidCredentialsResult | ||||||
| @ -24,29 +22,18 @@ from tests.e2e.utils import SeleniumTestCase, retry | |||||||
| class TestProviderLDAP(SeleniumTestCase): | class TestProviderLDAP(SeleniumTestCase): | ||||||
|     """LDAP and Outpost e2e tests""" |     """LDAP and Outpost e2e tests""" | ||||||
|  |  | ||||||
|     ldap_container: Container |     def start_ldap(self, outpost: Outpost): | ||||||
|  |  | ||||||
|     def tearDown(self) -> None: |  | ||||||
|         super().tearDown() |  | ||||||
|         self.output_container_logs(self.ldap_container) |  | ||||||
|         self.ldap_container.kill() |  | ||||||
|  |  | ||||||
|     def start_ldap(self, outpost: Outpost) -> Container: |  | ||||||
|         """Start ldap container based on outpost created""" |         """Start ldap container based on outpost created""" | ||||||
|         client: DockerClient = from_env() |         self.run_container( | ||||||
|         container = client.containers.run( |  | ||||||
|             image=self.get_container_image("ghcr.io/goauthentik/dev-ldap"), |             image=self.get_container_image("ghcr.io/goauthentik/dev-ldap"), | ||||||
|             detach=True, |  | ||||||
|             ports={ |             ports={ | ||||||
|                 "3389": "3389", |                 "3389": "3389", | ||||||
|                 "6636": "6636", |                 "6636": "6636", | ||||||
|             }, |             }, | ||||||
|             environment={ |             environment={ | ||||||
|                 "AUTHENTIK_HOST": self.live_server_url, |  | ||||||
|                 "AUTHENTIK_TOKEN": outpost.token.key, |                 "AUTHENTIK_TOKEN": outpost.token.key, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     def _prepare(self) -> User: |     def _prepare(self) -> User: | ||||||
|         """prepare user, provider, app and container""" |         """prepare user, provider, app and container""" | ||||||
| @ -68,7 +55,7 @@ class TestProviderLDAP(SeleniumTestCase): | |||||||
|         ) |         ) | ||||||
|         outpost.providers.add(ldap) |         outpost.providers.add(ldap) | ||||||
|  |  | ||||||
|         self.ldap_container = self.start_ldap(outpost) |         self.start_ldap(outpost) | ||||||
|  |  | ||||||
|         # Wait until outpost healthcheck succeeds |         # Wait until outpost healthcheck succeeds | ||||||
|         healthcheck_retries = 0 |         healthcheck_retries = 0 | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| """test OAuth Provider flow""" | """test OAuth Provider flow""" | ||||||
|  |  | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from docker.types import Healthcheck | from docker.types import Healthcheck | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| @ -24,22 +23,17 @@ class TestProviderOAuth2Github(SeleniumTestCase): | |||||||
|         self.client_id = generate_id() |         self.client_id = generate_id() | ||||||
|         self.client_secret = generate_key() |         self.client_secret = generate_key() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image="grafana/grafana:7.1.0", | ||||||
|         """Setup client grafana container which we test OAuth against""" |             ports={ | ||||||
|         return { |  | ||||||
|             "image": "grafana/grafana:7.1.0", |  | ||||||
|             "detach": True, |  | ||||||
|             "ports": { |  | ||||||
|                 "3000": "3000", |                 "3000": "3000", | ||||||
|             }, |             }, | ||||||
|             "auto_remove": True, |             healthcheck=Healthcheck( | ||||||
|             "healthcheck": Healthcheck( |  | ||||||
|                 test=["CMD", "wget", "--spider", "http://localhost:3000"], |                 test=["CMD", "wget", "--spider", "http://localhost:3000"], | ||||||
|                 interval=5 * 1_000 * 1_000_000, |                 interval=5 * 1_000 * 1_000_000, | ||||||
|                 start_period=1 * 1_000 * 1_000_000, |                 start_period=1 * 1_000 * 1_000_000, | ||||||
|             ), |             ), | ||||||
|             "environment": { |             environment={ | ||||||
|                 "GF_AUTH_GITHUB_ENABLED": "true", |                 "GF_AUTH_GITHUB_ENABLED": "true", | ||||||
|                 "GF_AUTH_GITHUB_ALLOW_SIGN_UP": "true", |                 "GF_AUTH_GITHUB_ALLOW_SIGN_UP": "true", | ||||||
|                 "GF_AUTH_GITHUB_CLIENT_ID": self.client_id, |                 "GF_AUTH_GITHUB_CLIENT_ID": self.client_id, | ||||||
| @ -54,7 +48,7 @@ class TestProviderOAuth2Github(SeleniumTestCase): | |||||||
|                 "GF_AUTH_GITHUB_API_URL": self.url("authentik_providers_oauth2_root:github-user"), |                 "GF_AUTH_GITHUB_API_URL": self.url("authentik_providers_oauth2_root:github-user"), | ||||||
|                 "GF_LOG_LEVEL": "debug", |                 "GF_LOG_LEVEL": "debug", | ||||||
|             }, |             }, | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
|  | |||||||
| @ -1,7 +1,6 @@ | |||||||
| """test OAuth2 OpenID Provider flow""" | """test OAuth2 OpenID Provider flow""" | ||||||
|  |  | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from docker.types import Healthcheck | from docker.types import Healthcheck | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| @ -32,21 +31,17 @@ class TestProviderOAuth2OAuth(SeleniumTestCase): | |||||||
|         self.client_secret = generate_key() |         self.client_secret = generate_key() | ||||||
|         self.app_slug = generate_id(20) |         self.app_slug = generate_id(20) | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image="grafana/grafana:7.1.0", | ||||||
|         return { |             ports={ | ||||||
|             "image": "grafana/grafana:7.1.0", |                 "3000": "3000", | ||||||
|             "detach": True, |             }, | ||||||
|             "auto_remove": True, |             healthcheck=Healthcheck( | ||||||
|             "healthcheck": Healthcheck( |  | ||||||
|                 test=["CMD", "wget", "--spider", "http://localhost:3000"], |                 test=["CMD", "wget", "--spider", "http://localhost:3000"], | ||||||
|                 interval=5 * 1_000 * 1_000_000, |                 interval=5 * 1_000 * 1_000_000, | ||||||
|                 start_period=1 * 1_000 * 1_000_000, |                 start_period=1 * 1_000 * 1_000_000, | ||||||
|             ), |             ), | ||||||
|             "ports": { |             environment={ | ||||||
|                 "3000": "3000", |  | ||||||
|             }, |  | ||||||
|             "environment": { |  | ||||||
|                 "GF_AUTH_GENERIC_OAUTH_ENABLED": "true", |                 "GF_AUTH_GENERIC_OAUTH_ENABLED": "true", | ||||||
|                 "GF_AUTH_GENERIC_OAUTH_CLIENT_ID": self.client_id, |                 "GF_AUTH_GENERIC_OAUTH_CLIENT_ID": self.client_id, | ||||||
|                 "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret, |                 "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET": self.client_secret, | ||||||
| @ -60,7 +55,7 @@ class TestProviderOAuth2OAuth(SeleniumTestCase): | |||||||
|                 ), |                 ), | ||||||
|                 "GF_LOG_LEVEL": "debug", |                 "GF_LOG_LEVEL": "debug", | ||||||
|             }, |             }, | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| from json import loads | from json import loads | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from docker import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| from selenium.webdriver.support import expected_conditions as ec | from selenium.webdriver.support import expected_conditions as ec | ||||||
|  |  | ||||||
| @ -34,13 +32,11 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|         self.application_slug = generate_id() |         self.application_slug = generate_id() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |  | ||||||
|     def setup_client(self) -> Container: |     def setup_client(self): | ||||||
|         """Setup client oidc-test-client container which we test OIDC against""" |         """Setup client oidc-test-client container which we test OIDC against""" | ||||||
|         sleep(1) |         sleep(1) | ||||||
|         client: DockerClient = from_env() |         self.run_container( | ||||||
|         container = client.containers.run( |  | ||||||
|             image="ghcr.io/beryju/oidc-test-client:2.1", |             image="ghcr.io/beryju/oidc-test-client:2.1", | ||||||
|             detach=True, |  | ||||||
|             ports={ |             ports={ | ||||||
|                 "9009": "9009", |                 "9009": "9009", | ||||||
|             }, |             }, | ||||||
| @ -50,8 +46,6 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|                 "OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/", |                 "OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/", | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.wait_for_container(container) |  | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
| @ -91,7 +85,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         sleep(2) |         sleep(2) | ||||||
| @ -140,7 +134,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
| @ -210,7 +204,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
| @ -287,7 +281,7 @@ class TestProviderOAuth2OIDC(SeleniumTestCase): | |||||||
|         ) |         ) | ||||||
|         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) |         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) | ||||||
|  |  | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
|         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1"))) |         self.wait.until(ec.presence_of_element_located((By.CSS_SELECTOR, "header > h1"))) | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| from json import loads | from json import loads | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from docker import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| from selenium.webdriver.support import expected_conditions as ec | from selenium.webdriver.support import expected_conditions as ec | ||||||
|  |  | ||||||
| @ -34,13 +32,11 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|         self.application_slug = "test" |         self.application_slug = "test" | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |  | ||||||
|     def setup_client(self) -> Container: |     def setup_client(self): | ||||||
|         """Setup client oidc-test-client container which we test OIDC against""" |         """Setup client oidc-test-client container which we test OIDC against""" | ||||||
|         sleep(1) |         sleep(1) | ||||||
|         client: DockerClient = from_env() |         self.run_container( | ||||||
|         container = client.containers.run( |  | ||||||
|             image="ghcr.io/beryju/oidc-test-client:2.1", |             image="ghcr.io/beryju/oidc-test-client:2.1", | ||||||
|             detach=True, |  | ||||||
|             ports={ |             ports={ | ||||||
|                 "9009": "9009", |                 "9009": "9009", | ||||||
|             }, |             }, | ||||||
| @ -50,8 +46,6 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|                 "OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/", |                 "OIDC_PROVIDER": f"{self.live_server_url}/application/o/{self.application_slug}/", | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.wait_for_container(container) |  | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
| @ -93,7 +87,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009/implicit/") |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|         sleep(2) |         sleep(2) | ||||||
| @ -142,7 +136,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009/implicit/") |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|         self.wait.until(ec.title_contains("authentik")) |         self.wait.until(ec.title_contains("authentik")) | ||||||
| @ -194,7 +188,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|             slug=self.application_slug, |             slug=self.application_slug, | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9009/implicit/") |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|         self.wait.until(ec.title_contains("authentik")) |         self.wait.until(ec.title_contains("authentik")) | ||||||
| @ -268,7 +262,7 @@ class TestProviderOAuth2OIDCImplicit(SeleniumTestCase): | |||||||
|         ) |         ) | ||||||
|         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) |         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) | ||||||
|  |  | ||||||
|         self.container = self.setup_client() |         self.setup_client() | ||||||
|         self.driver.get("http://localhost:9009/implicit/") |         self.driver.get("http://localhost:9009/implicit/") | ||||||
|         self.wait.until(ec.title_contains("authentik")) |         self.wait.until(ec.title_contains("authentik")) | ||||||
|         self.login() |         self.login() | ||||||
|  | |||||||
| @ -2,14 +2,12 @@ | |||||||
|  |  | ||||||
| from base64 import b64encode | from base64 import b64encode | ||||||
| from dataclasses import asdict | from dataclasses import asdict | ||||||
|  | from json import loads | ||||||
| from sys import platform | from sys import platform | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
| from unittest.case import skip, skipUnless | from unittest.case import skip, skipUnless | ||||||
|  |  | ||||||
| from channels.testing import ChannelsLiveServerTestCase | from channels.testing import ChannelsLiveServerTestCase | ||||||
| from docker.client import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
|  |  | ||||||
| from authentik.blueprints.tests import apply_blueprint, reconcile_app | from authentik.blueprints.tests import apply_blueprint, reconcile_app | ||||||
| @ -25,38 +23,26 @@ from tests.e2e.utils import SeleniumTestCase, retry | |||||||
| class TestProviderProxy(SeleniumTestCase): | class TestProviderProxy(SeleniumTestCase): | ||||||
|     """Proxy and Outpost e2e tests""" |     """Proxy and Outpost e2e tests""" | ||||||
|  |  | ||||||
|     proxy_container: Container |     def setUp(self): | ||||||
|  |         super().setUp() | ||||||
|     def tearDown(self) -> None: |         self.run_container( | ||||||
|         super().tearDown() |             image="traefik/whoami:latest", | ||||||
|         self.output_container_logs(self.proxy_container) |             ports={ | ||||||
|         self.proxy_container.kill() |  | ||||||
|  |  | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |  | ||||||
|         return { |  | ||||||
|             "image": "traefik/whoami:latest", |  | ||||||
|             "detach": True, |  | ||||||
|             "ports": { |  | ||||||
|                 "80": "80", |                 "80": "80", | ||||||
|             }, |             }, | ||||||
|             "auto_remove": True, |         ) | ||||||
|         } |  | ||||||
|  |  | ||||||
|     def start_proxy(self, outpost: Outpost) -> Container: |     def start_proxy(self, outpost: Outpost): | ||||||
|         """Start proxy container based on outpost created""" |         """Start proxy container based on outpost created""" | ||||||
|         client: DockerClient = from_env() |         self.run_container( | ||||||
|         container = client.containers.run( |  | ||||||
|             image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"), |             image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"), | ||||||
|             detach=True, |  | ||||||
|             ports={ |             ports={ | ||||||
|                 "9000": "9000", |                 "9000": "9000", | ||||||
|             }, |             }, | ||||||
|             environment={ |             environment={ | ||||||
|                 "AUTHENTIK_HOST": self.live_server_url, |  | ||||||
|                 "AUTHENTIK_TOKEN": outpost.token.key, |                 "AUTHENTIK_TOKEN": outpost.token.key, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
| @ -99,7 +85,7 @@ class TestProviderProxy(SeleniumTestCase): | |||||||
|         outpost.providers.add(proxy) |         outpost.providers.add(proxy) | ||||||
|         outpost.build_user_permissions(outpost.user) |         outpost.build_user_permissions(outpost.user) | ||||||
|  |  | ||||||
|         self.proxy_container = self.start_proxy(outpost) |         self.start_proxy(outpost) | ||||||
|  |  | ||||||
|         # Wait until outpost healthcheck succeeds |         # Wait until outpost healthcheck succeeds | ||||||
|         healthcheck_retries = 0 |         healthcheck_retries = 0 | ||||||
| @ -112,13 +98,15 @@ class TestProviderProxy(SeleniumTestCase): | |||||||
|             sleep(0.5) |             sleep(0.5) | ||||||
|         sleep(5) |         sleep(5) | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9000") |         self.driver.get("http://localhost:9000/api") | ||||||
|         self.login() |         self.login() | ||||||
|         sleep(1) |         sleep(1) | ||||||
|  |  | ||||||
|         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|         self.assertIn(f"X-Authentik-Username: {self.user.username}", full_body_text) |         body = loads(full_body_text) | ||||||
|         self.assertIn("X-Foo: bar", full_body_text) |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|  |         self.assertEqual(body["headers"]["X-Foo"], ["bar"]) | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out") |         self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out") | ||||||
|         sleep(2) |         sleep(2) | ||||||
| @ -173,7 +161,7 @@ class TestProviderProxy(SeleniumTestCase): | |||||||
|         outpost.providers.add(proxy) |         outpost.providers.add(proxy) | ||||||
|         outpost.build_user_permissions(outpost.user) |         outpost.build_user_permissions(outpost.user) | ||||||
|  |  | ||||||
|         self.proxy_container = self.start_proxy(outpost) |         self.start_proxy(outpost) | ||||||
|  |  | ||||||
|         # Wait until outpost healthcheck succeeds |         # Wait until outpost healthcheck succeeds | ||||||
|         healthcheck_retries = 0 |         healthcheck_retries = 0 | ||||||
| @ -186,14 +174,16 @@ class TestProviderProxy(SeleniumTestCase): | |||||||
|             sleep(0.5) |             sleep(0.5) | ||||||
|         sleep(5) |         sleep(5) | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9000") |         self.driver.get("http://localhost:9000/api") | ||||||
|         self.login() |         self.login() | ||||||
|         sleep(1) |         sleep(1) | ||||||
|  |  | ||||||
|         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|         self.assertIn(f"X-Authentik-Username: {self.user.username}", full_body_text) |         body = loads(full_body_text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|         auth_header = b64encode(f"{cred}:{cred}".encode()).decode() |         auth_header = b64encode(f"{cred}:{cred}".encode()).decode() | ||||||
|         self.assertIn(f"Authorization: Basic {auth_header}", full_body_text) |         self.assertEqual(body["headers"]["Authorization"], [f"Basic {auth_header}"]) | ||||||
|  |  | ||||||
|         self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out") |         self.driver.get("http://localhost:9000/outpost.goauthentik.io/sign_out") | ||||||
|         sleep(2) |         sleep(2) | ||||||
|  | |||||||
							
								
								
									
										227
									
								
								tests/e2e/test_provider_proxy_forward.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								tests/e2e/test_provider_proxy_forward.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,227 @@ | |||||||
|  | """Proxy and Outpost e2e tests""" | ||||||
|  |  | ||||||
|  | from json import loads | ||||||
|  | from pathlib import Path | ||||||
|  | from time import sleep | ||||||
|  |  | ||||||
|  | from selenium.webdriver.common.by import By | ||||||
|  |  | ||||||
|  | from authentik.blueprints.tests import apply_blueprint, reconcile_app | ||||||
|  | from authentik.core.models import Application | ||||||
|  | from authentik.flows.models import Flow | ||||||
|  | from authentik.lib.generators import generate_id | ||||||
|  | from authentik.outposts.models import Outpost, OutpostType | ||||||
|  | from authentik.providers.proxy.models import ProxyMode, ProxyProvider | ||||||
|  | from tests.e2e.utils import SeleniumTestCase, retry | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TestProviderProxyForward(SeleniumTestCase): | ||||||
|  |     """Proxy and Outpost e2e tests""" | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|  |             image="traefik/whoami:latest", | ||||||
|  |             name="ak-whoami", | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     def start_outpost(self, outpost: Outpost): | ||||||
|  |         """Start proxy container based on outpost created""" | ||||||
|  |         self.run_container( | ||||||
|  |             image=self.get_container_image("ghcr.io/goauthentik/dev-proxy"), | ||||||
|  |             ports={ | ||||||
|  |                 "9000": "9000", | ||||||
|  |             }, | ||||||
|  |             environment={ | ||||||
|  |                 "AUTHENTIK_TOKEN": outpost.token.key, | ||||||
|  |             }, | ||||||
|  |             name="ak-test-outpost", | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |     @apply_blueprint( | ||||||
|  |         "default/flow-default-authentication-flow.yaml", | ||||||
|  |         "default/flow-default-invalidation-flow.yaml", | ||||||
|  |     ) | ||||||
|  |     @apply_blueprint( | ||||||
|  |         "default/flow-default-provider-authorization-implicit-consent.yaml", | ||||||
|  |         "default/flow-default-provider-invalidation.yaml", | ||||||
|  |     ) | ||||||
|  |     @apply_blueprint( | ||||||
|  |         "system/providers-oauth2.yaml", | ||||||
|  |         "system/providers-proxy.yaml", | ||||||
|  |     ) | ||||||
|  |     @reconcile_app("authentik_crypto") | ||||||
|  |     def prepare(self): | ||||||
|  |         proxy: ProxyProvider = ProxyProvider.objects.create( | ||||||
|  |             name=generate_id(), | ||||||
|  |             mode=ProxyMode.FORWARD_SINGLE, | ||||||
|  |             authorization_flow=Flow.objects.get( | ||||||
|  |                 slug="default-provider-authorization-implicit-consent" | ||||||
|  |             ), | ||||||
|  |             invalidation_flow=Flow.objects.get(slug="default-provider-invalidation-flow"), | ||||||
|  |             internal_host=f"http://{self.host}", | ||||||
|  |             external_host="http://localhost", | ||||||
|  |         ) | ||||||
|  |         # Ensure OAuth2 Params are set | ||||||
|  |         proxy.set_oauth_defaults() | ||||||
|  |         proxy.save() | ||||||
|  |         # we need to create an application to actually access the proxy | ||||||
|  |         Application.objects.create(name=generate_id(), slug=generate_id(), provider=proxy) | ||||||
|  |         outpost: Outpost = Outpost.objects.create( | ||||||
|  |             name=generate_id(), | ||||||
|  |             type=OutpostType.PROXY, | ||||||
|  |         ) | ||||||
|  |         outpost.providers.add(proxy) | ||||||
|  |         outpost.build_user_permissions(outpost.user) | ||||||
|  |  | ||||||
|  |         self.start_outpost(outpost) | ||||||
|  |  | ||||||
|  |         # Wait until outpost healthcheck succeeds | ||||||
|  |         healthcheck_retries = 0 | ||||||
|  |         while healthcheck_retries < 50:  # noqa: PLR2004 | ||||||
|  |             if len(outpost.state) > 0: | ||||||
|  |                 state = outpost.state[0] | ||||||
|  |                 if state.last_seen: | ||||||
|  |                     break | ||||||
|  |             healthcheck_retries += 1 | ||||||
|  |             sleep(0.5) | ||||||
|  |         sleep(5) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     def test_traefik(self): | ||||||
|  |         """Test traefik""" | ||||||
|  |         local_config_path = ( | ||||||
|  |             Path(__file__).parent / "proxy_forward_auth" / "traefik_single" / "config-static.yaml" | ||||||
|  |         ) | ||||||
|  |         self.run_container( | ||||||
|  |             image="docker.io/library/traefik:3.1", | ||||||
|  |             ports={ | ||||||
|  |                 "80": "80", | ||||||
|  |             }, | ||||||
|  |             volumes={ | ||||||
|  |                 local_config_path: { | ||||||
|  |                     "bind": "/etc/traefik/traefik.yml", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.prepare() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/api") | ||||||
|  |         self.login() | ||||||
|  |         sleep(1) | ||||||
|  |  | ||||||
|  |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|  |         body = loads(full_body_text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/outpost.goauthentik.io/sign_out") | ||||||
|  |         sleep(2) | ||||||
|  |         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||||
|  |         session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor) | ||||||
|  |         title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text | ||||||
|  |         self.assertIn("You've logged out of", title) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     def test_nginx(self): | ||||||
|  |         """Test nginx""" | ||||||
|  |         self.prepare() | ||||||
|  |  | ||||||
|  |         # Start nginx last so all hosts are resolvable, otherwise nginx exits | ||||||
|  |         self.run_container( | ||||||
|  |             image="docker.io/library/nginx:1.27", | ||||||
|  |             ports={ | ||||||
|  |                 "80": "80", | ||||||
|  |             }, | ||||||
|  |             volumes={ | ||||||
|  |                 f"{Path(__file__).parent / "proxy_forward_auth" / "nginx_single" / "nginx.conf"}": { | ||||||
|  |                     "bind": "/etc/nginx/conf.d/default.conf", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/api") | ||||||
|  |         self.login() | ||||||
|  |         sleep(1) | ||||||
|  |  | ||||||
|  |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|  |         body = loads(full_body_text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/outpost.goauthentik.io/sign_out") | ||||||
|  |         sleep(2) | ||||||
|  |         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||||
|  |         session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor) | ||||||
|  |         title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text | ||||||
|  |         self.assertIn("You've logged out of", title) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     def test_envoy(self): | ||||||
|  |         """Test envoy""" | ||||||
|  |         self.run_container( | ||||||
|  |             image="docker.io/envoyproxy/envoy:v1.25-latest", | ||||||
|  |             ports={ | ||||||
|  |                 "10000": "80", | ||||||
|  |             }, | ||||||
|  |             volumes={ | ||||||
|  |                 f"{Path(__file__).parent / "proxy_forward_auth" / "envoy_single" / "envoy.yaml"}": { | ||||||
|  |                     "bind": "/etc/envoy/envoy.yaml", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.prepare() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/api") | ||||||
|  |         self.login() | ||||||
|  |         sleep(1) | ||||||
|  |  | ||||||
|  |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|  |         body = loads(full_body_text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/outpost.goauthentik.io/sign_out") | ||||||
|  |         sleep(2) | ||||||
|  |         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||||
|  |         session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor) | ||||||
|  |         title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text | ||||||
|  |         self.assertIn("You've logged out of", title) | ||||||
|  |  | ||||||
|  |     @retry() | ||||||
|  |     def test_caddy(self): | ||||||
|  |         """Test caddy""" | ||||||
|  |         local_config_path = ( | ||||||
|  |             Path(__file__).parent / "proxy_forward_auth" / "caddy_single" / "Caddyfile" | ||||||
|  |         ) | ||||||
|  |         self.run_container( | ||||||
|  |             image="docker.io/library/caddy:2.8", | ||||||
|  |             ports={ | ||||||
|  |                 "80": "80", | ||||||
|  |             }, | ||||||
|  |             volumes={ | ||||||
|  |                 local_config_path: { | ||||||
|  |                     "bind": "/etc/caddy/Caddyfile", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|  |         self.prepare() | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/api") | ||||||
|  |         self.login() | ||||||
|  |         sleep(1) | ||||||
|  |  | ||||||
|  |         full_body_text = self.driver.find_element(By.CSS_SELECTOR, "pre").text | ||||||
|  |         body = loads(full_body_text) | ||||||
|  |  | ||||||
|  |         self.assertEqual(body["headers"]["X-Authentik-Username"], [self.user.username]) | ||||||
|  |  | ||||||
|  |         self.driver.get("http://localhost/outpost.goauthentik.io/sign_out") | ||||||
|  |         sleep(2) | ||||||
|  |         flow_executor = self.get_shadow_root("ak-flow-executor") | ||||||
|  |         session_end_stage = self.get_shadow_root("ak-stage-session-end", flow_executor) | ||||||
|  |         title = session_end_stage.find_element(By.CSS_SELECTOR, ".pf-c-title.pf-m-3xl").text | ||||||
|  |         self.assertIn("You've logged out of", title) | ||||||
| @ -3,8 +3,6 @@ | |||||||
| from dataclasses import asdict | from dataclasses import asdict | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from docker.client import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from pyrad.client import Client | from pyrad.client import Client | ||||||
| from pyrad.dictionary import Dictionary | from pyrad.dictionary import Dictionary | ||||||
| from pyrad.packet import AccessAccept, AccessReject, AccessRequest | from pyrad.packet import AccessAccept, AccessReject, AccessRequest | ||||||
| @ -21,30 +19,19 @@ from tests.e2e.utils import SeleniumTestCase, retry | |||||||
| class TestProviderRadius(SeleniumTestCase): | class TestProviderRadius(SeleniumTestCase): | ||||||
|     """Radius Outpost e2e tests""" |     """Radius Outpost e2e tests""" | ||||||
|  |  | ||||||
|     radius_container: Container |  | ||||||
|  |  | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         super().setUp() |         super().setUp() | ||||||
|         self.shared_secret = generate_key() |         self.shared_secret = generate_key() | ||||||
|  |  | ||||||
|     def tearDown(self) -> None: |     def start_radius(self, outpost: Outpost): | ||||||
|         super().tearDown() |  | ||||||
|         self.output_container_logs(self.radius_container) |  | ||||||
|         self.radius_container.kill() |  | ||||||
|  |  | ||||||
|     def start_radius(self, outpost: Outpost) -> Container: |  | ||||||
|         """Start radius container based on outpost created""" |         """Start radius container based on outpost created""" | ||||||
|         client: DockerClient = from_env() |         self.run_container( | ||||||
|         container = client.containers.run( |  | ||||||
|             image=self.get_container_image("ghcr.io/goauthentik/dev-radius"), |             image=self.get_container_image("ghcr.io/goauthentik/dev-radius"), | ||||||
|             detach=True, |  | ||||||
|             ports={"1812/udp": "1812/udp"}, |             ports={"1812/udp": "1812/udp"}, | ||||||
|             environment={ |             environment={ | ||||||
|                 "AUTHENTIK_HOST": self.live_server_url, |  | ||||||
|                 "AUTHENTIK_TOKEN": outpost.token.key, |                 "AUTHENTIK_TOKEN": outpost.token.key, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     def _prepare(self) -> User: |     def _prepare(self) -> User: | ||||||
|         """prepare user, provider, app and container""" |         """prepare user, provider, app and container""" | ||||||
| @ -62,7 +49,7 @@ class TestProviderRadius(SeleniumTestCase): | |||||||
|         ) |         ) | ||||||
|         outpost.providers.add(radius) |         outpost.providers.add(radius) | ||||||
|  |  | ||||||
|         self.radius_container = self.start_radius(outpost) |         self.start_radius(outpost) | ||||||
|  |  | ||||||
|         # Wait until outpost healthcheck succeeds |         # Wait until outpost healthcheck succeeds | ||||||
|         healthcheck_retries = 0 |         healthcheck_retries = 0 | ||||||
|  | |||||||
| @ -3,8 +3,6 @@ | |||||||
| from json import loads | from json import loads | ||||||
| from time import sleep | from time import sleep | ||||||
|  |  | ||||||
| from docker import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| from selenium.webdriver.support import expected_conditions as ec | from selenium.webdriver.support import expected_conditions as ec | ||||||
|  |  | ||||||
| @ -22,11 +20,8 @@ from tests.e2e.utils import SeleniumTestCase, retry | |||||||
| class TestProviderSAML(SeleniumTestCase): | class TestProviderSAML(SeleniumTestCase): | ||||||
|     """test SAML Provider flow""" |     """test SAML Provider flow""" | ||||||
|  |  | ||||||
|     container: Container |     def setup_client(self, provider: SAMLProvider, force_post: bool = False): | ||||||
|  |  | ||||||
|     def setup_client(self, provider: SAMLProvider, force_post: bool = False) -> Container: |  | ||||||
|         """Setup client saml-sp container which we test SAML against""" |         """Setup client saml-sp container which we test SAML against""" | ||||||
|         client: DockerClient = from_env() |  | ||||||
|         metadata_url = ( |         metadata_url = ( | ||||||
|             self.url( |             self.url( | ||||||
|                 "authentik_api:samlprovider-metadata", |                 "authentik_api:samlprovider-metadata", | ||||||
| @ -36,9 +31,8 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|         ) |         ) | ||||||
|         if force_post: |         if force_post: | ||||||
|             metadata_url += f"&force_binding={SAML_BINDING_POST}" |             metadata_url += f"&force_binding={SAML_BINDING_POST}" | ||||||
|         container = client.containers.run( |         self.run_container( | ||||||
|             image="ghcr.io/beryju/saml-test-sp:1.1", |             image="ghcr.io/beryju/saml-test-sp:1.1", | ||||||
|             detach=True, |  | ||||||
|             ports={ |             ports={ | ||||||
|                 "9009": "9009", |                 "9009": "9009", | ||||||
|             }, |             }, | ||||||
| @ -48,8 +42,6 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|                 "SP_METADATA_URL": metadata_url, |                 "SP_METADATA_URL": metadata_url, | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.wait_for_container(container) |  | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
| @ -85,7 +77,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             slug="authentik-saml", |             slug="authentik-saml", | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client(provider) |         self.setup_client(provider) | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
|         self.wait_for_url("http://localhost:9009/") |         self.wait_for_url("http://localhost:9009/") | ||||||
| @ -153,7 +145,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             slug="authentik-saml", |             slug="authentik-saml", | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client(provider) |         self.setup_client(provider) | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
|  |  | ||||||
| @ -236,7 +228,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             slug="authentik-saml", |             slug="authentik-saml", | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client(provider, True) |         self.setup_client(provider, True) | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
|  |  | ||||||
| @ -319,7 +311,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             slug="authentik-saml", |             slug="authentik-saml", | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client(provider) |         self.setup_client(provider) | ||||||
|         self.driver.get( |         self.driver.get( | ||||||
|             self.url( |             self.url( | ||||||
|                 "authentik_providers_saml:sso-init", |                 "authentik_providers_saml:sso-init", | ||||||
| @ -397,7 +389,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) |         PolicyBinding.objects.create(target=app, policy=negative_policy, order=0) | ||||||
|         self.container = self.setup_client(provider) |         self.setup_client(provider) | ||||||
|         self.driver.get("http://localhost:9009/") |         self.driver.get("http://localhost:9009/") | ||||||
|         self.login() |         self.login() | ||||||
|  |  | ||||||
| @ -444,7 +436,7 @@ class TestProviderSAML(SeleniumTestCase): | |||||||
|             slug="authentik-saml", |             slug="authentik-saml", | ||||||
|             provider=provider, |             provider=provider, | ||||||
|         ) |         ) | ||||||
|         self.container = self.setup_client(provider) |         self.setup_client(provider) | ||||||
|         self.driver.get("http://localhost:9009") |         self.driver.get("http://localhost:9009") | ||||||
|         self.login() |         self.login() | ||||||
|         self.wait_for_url("http://localhost:9009/") |         self.wait_for_url("http://localhost:9009/") | ||||||
|  | |||||||
| @ -1,7 +1,5 @@ | |||||||
| """test LDAP Source""" | """test LDAP Source""" | ||||||
|  |  | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from django.db.models import Q | from django.db.models import Q | ||||||
| from ldap3.core.exceptions import LDAPSessionTerminatedByServerError | from ldap3.core.exceptions import LDAPSessionTerminatedByServerError | ||||||
|  |  | ||||||
| @ -22,22 +20,18 @@ class TestSourceLDAPSamba(SeleniumTestCase): | |||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.admin_password = generate_key() |         self.admin_password = generate_key() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.samba = self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image="ghcr.io/beryju/test-samba-dc:latest", | ||||||
|         return { |             cap_add=["SYS_ADMIN"], | ||||||
|             "image": "ghcr.io/beryju/test-samba-dc:latest", |             ports={ | ||||||
|             "detach": True, |  | ||||||
|             "cap_add": ["SYS_ADMIN"], |  | ||||||
|             "ports": { |  | ||||||
|                 "389": "389/tcp", |                 "389": "389/tcp", | ||||||
|             }, |             }, | ||||||
|             "auto_remove": True, |             environment={ | ||||||
|             "environment": { |  | ||||||
|                 "SMB_DOMAIN": "test.goauthentik.io", |                 "SMB_DOMAIN": "test.goauthentik.io", | ||||||
|                 "SMB_NETBIOS": "goauthentik", |                 "SMB_NETBIOS": "goauthentik", | ||||||
|                 "SMB_ADMIN_PASSWORD": self.admin_password, |                 "SMB_ADMIN_PASSWORD": self.admin_password, | ||||||
|             }, |             }, | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     @retry(exceptions=[LDAPSessionTerminatedByServerError]) |     @retry(exceptions=[LDAPSessionTerminatedByServerError]) | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
| @ -148,7 +142,7 @@ class TestSourceLDAPSamba(SeleniumTestCase): | |||||||
|         UserLDAPSynchronizer(source).sync_full() |         UserLDAPSynchronizer(source).sync_full() | ||||||
|         username = "bob" |         username = "bob" | ||||||
|         password = generate_id() |         password = generate_id() | ||||||
|         result = self.container.exec_run( |         result = self.samba.exec_run( | ||||||
|             ["samba-tool", "user", "setpassword", username, "--newpassword", password] |             ["samba-tool", "user", "setpassword", username, "--newpassword", password] | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(result.exit_code, 0) |         self.assertEqual(result.exit_code, 0) | ||||||
| @ -163,7 +157,7 @@ class TestSourceLDAPSamba(SeleniumTestCase): | |||||||
|         self.assertTrue(user.check_password(password)) |         self.assertTrue(user.check_password(password)) | ||||||
|         # Set new password |         # Set new password | ||||||
|         new_password = generate_id() |         new_password = generate_id() | ||||||
|         result = self.container.exec_run( |         result = self.samba.exec_run( | ||||||
|             ["samba-tool", "user", "setpassword", username, "--newpassword", new_password] |             ["samba-tool", "user", "setpassword", username, "--newpassword", new_password] | ||||||
|         ) |         ) | ||||||
|         self.assertEqual(result.exit_code, 0) |         self.assertEqual(result.exit_code, 0) | ||||||
|  | |||||||
| @ -56,14 +56,10 @@ class TestSourceOAuth1(SeleniumTestCase): | |||||||
|         self.client_secret = generate_key() |         self.client_secret = generate_key() | ||||||
|         self.source_slug = generate_id() |         self.source_slug = generate_id() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image="ghcr.io/beryju/oauth1-test-server:v1.1", | ||||||
|         return { |             ports={"5000": "5001"}, | ||||||
|             "image": "ghcr.io/beryju/oauth1-test-server:v1.1", |             environment={ | ||||||
|             "detach": True, |  | ||||||
|             "ports": {"5000": "5001"}, |  | ||||||
|             "auto_remove": True, |  | ||||||
|             "environment": { |  | ||||||
|                 "OAUTH1_CLIENT_ID": self.client_id, |                 "OAUTH1_CLIENT_ID": self.client_id, | ||||||
|                 "OAUTH1_CLIENT_SECRET": self.client_secret, |                 "OAUTH1_CLIENT_SECRET": self.client_secret, | ||||||
|                 "OAUTH1_REDIRECT_URI": self.url( |                 "OAUTH1_REDIRECT_URI": self.url( | ||||||
| @ -71,7 +67,7 @@ class TestSourceOAuth1(SeleniumTestCase): | |||||||
|                     source_slug=self.source_slug, |                     source_slug=self.source_slug, | ||||||
|                 ), |                 ), | ||||||
|             }, |             }, | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     def create_objects(self): |     def create_objects(self): | ||||||
|         """Create required objects""" |         """Create required objects""" | ||||||
|  | |||||||
| @ -3,15 +3,12 @@ | |||||||
| from json import loads | from json import loads | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from docker.types import Healthcheck | from docker.types import Healthcheck | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| from selenium.webdriver.common.keys import Keys | from selenium.webdriver.common.keys import Keys | ||||||
| from selenium.webdriver.support import expected_conditions as ec | from selenium.webdriver.support import expected_conditions as ec | ||||||
| from selenium.webdriver.support.wait import WebDriverWait | from selenium.webdriver.support.wait import WebDriverWait | ||||||
| from yaml import safe_dump |  | ||||||
|  |  | ||||||
| from authentik.blueprints.tests import apply_blueprint | from authentik.blueprints.tests import apply_blueprint | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| @ -21,69 +18,35 @@ from authentik.sources.oauth.models import OAuthSource | |||||||
| from authentik.stages.identification.models import IdentificationStage | from authentik.stages.identification.models import IdentificationStage | ||||||
| from tests.e2e.utils import SeleniumTestCase, retry | from tests.e2e.utils import SeleniumTestCase, retry | ||||||
|  |  | ||||||
| CONFIG_PATH = "/tmp/dex.yml"  # nosec |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestSourceOAuth2(SeleniumTestCase): | class TestSourceOAuth2(SeleniumTestCase): | ||||||
|     """test OAuth Source flow""" |     """test OAuth Source flow""" | ||||||
|  |  | ||||||
|     container: Container |  | ||||||
|  |  | ||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.client_secret = generate_key() |         self.client_secret = generate_key() | ||||||
|         self.slug = generate_id() |         self.slug = generate_id() | ||||||
|         self.prepare_dex_config() |  | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def prepare_dex_config(self): |             image="ghcr.io/dexidp/dex:v2.28.1", | ||||||
|         """Since Dex does not document which environment |             ports={"5556": "5556"}, | ||||||
|         variables can be used to configure clients""" |             healthcheck=Healthcheck( | ||||||
|         config = { |  | ||||||
|             "enablePasswordDB": True, |  | ||||||
|             "issuer": "http://127.0.0.1:5556/dex", |  | ||||||
|             "logger": {"level": "debug"}, |  | ||||||
|             "staticClients": [ |  | ||||||
|                 { |  | ||||||
|                     "id": "example-app", |  | ||||||
|                     "name": "Example App", |  | ||||||
|                     "redirectURIs": [ |  | ||||||
|                         self.url( |  | ||||||
|                             "authentik_sources_oauth:oauth-client-callback", |  | ||||||
|                             source_slug=self.slug, |  | ||||||
|                         ) |  | ||||||
|                     ], |  | ||||||
|                     "secret": self.client_secret, |  | ||||||
|                 } |  | ||||||
|             ], |  | ||||||
|             "staticPasswords": [ |  | ||||||
|                 { |  | ||||||
|                     "email": "admin@example.com", |  | ||||||
|                     # hash for password |  | ||||||
|                     "hash": "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W", |  | ||||||
|                     "userID": "08a8684b-db88-4b73-90a9-3cd1661f5466", |  | ||||||
|                     "username": "admin", |  | ||||||
|                 } |  | ||||||
|             ], |  | ||||||
|             "storage": {"config": {"file": "/tmp/dex.db"}, "type": "sqlite3"},  # nosec |  | ||||||
|             "web": {"http": "0.0.0.0:5556"}, |  | ||||||
|         } |  | ||||||
|         with open(CONFIG_PATH, "w+", encoding="utf8") as _file: |  | ||||||
|             safe_dump(config, _file) |  | ||||||
|  |  | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |  | ||||||
|         return { |  | ||||||
|             "image": "ghcr.io/dexidp/dex:v2.28.1", |  | ||||||
|             "detach": True, |  | ||||||
|             "ports": {"5556": "5556"}, |  | ||||||
|             "auto_remove": True, |  | ||||||
|             "command": "dex serve /config.yml", |  | ||||||
|             "healthcheck": Healthcheck( |  | ||||||
|                 test=["CMD", "wget", "--spider", "http://localhost:5556/dex/healthz"], |                 test=["CMD", "wget", "--spider", "http://localhost:5556/dex/healthz"], | ||||||
|                 interval=5 * 1_000 * 1_000_000, |                 interval=5 * 1_000 * 1_000_000, | ||||||
|                 start_period=1 * 1_000 * 1_000_000, |                 start_period=1 * 1_000 * 1_000_000, | ||||||
|             ), |             ), | ||||||
|             "volumes": {str(Path(CONFIG_PATH).absolute()): {"bind": "/config.yml", "mode": "ro"}}, |             environment={ | ||||||
|         } |                 "AK_REDIRECT_URL": self.url( | ||||||
|  |                     "authentik_sources_oauth:oauth-client-callback", | ||||||
|  |                     source_slug=self.slug, | ||||||
|  |                 ), | ||||||
|  |                 "AK_CLIENT_SECRET": self.client_secret, | ||||||
|  |             }, | ||||||
|  |             volumes={ | ||||||
|  |                 f"{Path(__file__).parent / "sources_oauth2_dex" / "dex.yaml"}": { | ||||||
|  |                     "bind": "/etc/dex/config.docker.yaml", | ||||||
|  |                 } | ||||||
|  |             }, | ||||||
|  |         ) | ||||||
|  |  | ||||||
|     def create_objects(self): |     def create_objects(self): | ||||||
|         """Create required objects""" |         """Create required objects""" | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
|  |  | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from docker.types import Healthcheck | from docker.types import Healthcheck | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| @ -77,19 +76,15 @@ class TestSourceSAML(SeleniumTestCase): | |||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.slug = generate_id() |         self.slug = generate_id() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image="kristophjunge/test-saml-idp:1.15", | ||||||
|         return { |             ports={"8080": "8080"}, | ||||||
|             "image": "kristophjunge/test-saml-idp:1.15", |             healthcheck=Healthcheck( | ||||||
|             "detach": True, |  | ||||||
|             "ports": {"8080": "8080"}, |  | ||||||
|             "auto_remove": True, |  | ||||||
|             "healthcheck": Healthcheck( |  | ||||||
|                 test=["CMD", "curl", "http://localhost:8080"], |                 test=["CMD", "curl", "http://localhost:8080"], | ||||||
|                 interval=5 * 1_000 * 1_000_000, |                 interval=5 * 1_000 * 1_000_000, | ||||||
|                 start_period=1 * 1_000 * 1_000_000, |                 start_period=1 * 1_000 * 1_000_000, | ||||||
|             ), |             ), | ||||||
|             "volumes": { |             volumes={ | ||||||
|                 str( |                 str( | ||||||
|                     (Path(__file__).parent / Path("test-saml-idp/saml20-sp-remote.php")).absolute() |                     (Path(__file__).parent / Path("test-saml-idp/saml20-sp-remote.php")).absolute() | ||||||
|                 ): { |                 ): { | ||||||
| @ -97,7 +92,7 @@ class TestSourceSAML(SeleniumTestCase): | |||||||
|                     "mode": "ro", |                     "mode": "ro", | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|             "environment": { |             environment={ | ||||||
|                 "SIMPLESAMLPHP_SP_ENTITY_ID": "entity-id", |                 "SIMPLESAMLPHP_SP_ENTITY_ID": "entity-id", | ||||||
|                 "SIMPLESAMLPHP_SP_NAME_ID_FORMAT": ( |                 "SIMPLESAMLPHP_SP_NAME_ID_FORMAT": ( | ||||||
|                     "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" |                     "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" | ||||||
| @ -107,7 +102,7 @@ class TestSourceSAML(SeleniumTestCase): | |||||||
|                     self.url("authentik_sources_saml:acs", source_slug=self.slug) |                     self.url("authentik_sources_saml:acs", source_slug=self.slug) | ||||||
|                 ), |                 ), | ||||||
|             }, |             }, | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     @apply_blueprint( |     @apply_blueprint( | ||||||
|  | |||||||
| @ -2,7 +2,6 @@ | |||||||
|  |  | ||||||
| from pprint import pformat | from pprint import pformat | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any |  | ||||||
|  |  | ||||||
| from docker.types import Healthcheck | from docker.types import Healthcheck | ||||||
|  |  | ||||||
| @ -20,22 +19,18 @@ class TestSourceSCIM(SeleniumTestCase): | |||||||
|     def setUp(self): |     def setUp(self): | ||||||
|         self.slug = generate_id() |         self.slug = generate_id() | ||||||
|         super().setUp() |         super().setUp() | ||||||
|  |         self.run_container( | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |             image=( | ||||||
|         return { |  | ||||||
|             "image": ( |  | ||||||
|                 "ghcr.io/suvera/scim2-compliance-test-utility@sha256:eca913bb73" |                 "ghcr.io/suvera/scim2-compliance-test-utility@sha256:eca913bb73" | ||||||
|                 "c46892cd1fb2dfd2fef1c5881e6abc5cb0eec7e92fb78c1b933ece" |                 "c46892cd1fb2dfd2fef1c5881e6abc5cb0eec7e92fb78c1b933ece" | ||||||
|             ), |             ), | ||||||
|             "detach": True, |             ports={"8080": "8080"}, | ||||||
|             "ports": {"8080": "8080"}, |             healthcheck=Healthcheck( | ||||||
|             "auto_remove": True, |  | ||||||
|             "healthcheck": Healthcheck( |  | ||||||
|                 test=["CMD", "curl", "http://localhost:8080"], |                 test=["CMD", "curl", "http://localhost:8080"], | ||||||
|                 interval=5 * 1_000 * 1_000_000, |                 interval=5 * 1_000 * 1_000_000, | ||||||
|                 start_period=1 * 1_000 * 1_000_000, |                 start_period=1 * 1_000 * 1_000_000, | ||||||
|             ), |             ), | ||||||
|         } |         ) | ||||||
|  |  | ||||||
|     @retry() |     @retry() | ||||||
|     def test_scim_conformance(self): |     def test_scim_conformance(self): | ||||||
|  | |||||||
| @ -9,6 +9,7 @@ from os import environ | |||||||
| from sys import stderr | from sys import stderr | ||||||
| from time import sleep | from time import sleep | ||||||
| from typing import Any | from typing import Any | ||||||
|  | from unittest.case import TestCase | ||||||
|  |  | ||||||
| from django.apps import apps | from django.apps import apps | ||||||
| from django.contrib.staticfiles.testing import StaticLiveServerTestCase | from django.contrib.staticfiles.testing import StaticLiveServerTestCase | ||||||
| @ -19,6 +20,7 @@ from django.urls import reverse | |||||||
| from docker import DockerClient, from_env | from docker import DockerClient, from_env | ||||||
| from docker.errors import DockerException | from docker.errors import DockerException | ||||||
| from docker.models.containers import Container | from docker.models.containers import Container | ||||||
|  | from docker.models.networks import Network | ||||||
| from selenium import webdriver | from selenium import webdriver | ||||||
| from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException | from selenium.common.exceptions import NoSuchElementException, TimeoutException, WebDriverException | ||||||
| from selenium.webdriver.common.by import By | from selenium.webdriver.common.by import By | ||||||
| @ -31,6 +33,7 @@ from structlog.stdlib import get_logger | |||||||
| from authentik.core.api.users import UserSerializer | from authentik.core.api.users import UserSerializer | ||||||
| from authentik.core.models import User | from authentik.core.models import User | ||||||
| from authentik.core.tests.utils import create_test_admin_user | from authentik.core.tests.utils import create_test_admin_user | ||||||
|  | from authentik.lib.generators import generate_id | ||||||
|  |  | ||||||
| RETRIES = int(environ.get("RETRIES", "3")) | RETRIES = int(environ.get("RETRIES", "3")) | ||||||
| IS_CI = "CI" in environ | IS_CI = "CI" in environ | ||||||
| @ -54,9 +57,32 @@ def get_local_ip() -> str: | |||||||
|     return ip_addr |     return ip_addr | ||||||
|  |  | ||||||
|  |  | ||||||
| class DockerTestCase: | class DockerTestCase(TestCase): | ||||||
|     """Mixin for dealing with containers""" |     """Mixin for dealing with containers""" | ||||||
|  |  | ||||||
|  |     max_healthcheck_attempts = 30 | ||||||
|  |  | ||||||
|  |     __client: DockerClient | ||||||
|  |     __network: Network | ||||||
|  |  | ||||||
|  |     __label_id = generate_id() | ||||||
|  |  | ||||||
|  |     def setUp(self) -> None: | ||||||
|  |         self.__client = from_env() | ||||||
|  |         self.__network = self.docker_client.networks.create(name=f"authentik-test-{generate_id()}") | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def docker_client(self) -> DockerClient: | ||||||
|  |         return self.__client | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def docker_network(self) -> Network: | ||||||
|  |         return self.__network | ||||||
|  |  | ||||||
|  |     @property | ||||||
|  |     def docker_labels(self) -> dict: | ||||||
|  |         return {"io.goauthentik.test": self.__label_id} | ||||||
|  |  | ||||||
|     def wait_for_container(self, container: Container): |     def wait_for_container(self, container: Container): | ||||||
|         """Check that container is health""" |         """Check that container is health""" | ||||||
|         attempt = 0 |         attempt = 0 | ||||||
| @ -67,47 +93,30 @@ class DockerTestCase: | |||||||
|                 return container |                 return container | ||||||
|             sleep(1) |             sleep(1) | ||||||
|             attempt += 1 |             attempt += 1 | ||||||
|             if attempt >= 30:  # noqa: PLR2004 |             if attempt >= self.max_healthcheck_attempts: | ||||||
|                 self.failureException("Container failed to start") |                 self.failureException("Container failed to start") | ||||||
|  |  | ||||||
|  |  | ||||||
| class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): |  | ||||||
|     """StaticLiveServerTestCase which automatically creates a Webdriver instance""" |  | ||||||
|  |  | ||||||
|     host = get_local_ip() |  | ||||||
|     container: Container | None = None |  | ||||||
|     wait_timeout: int |  | ||||||
|     user: User |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         if IS_CI: |  | ||||||
|             print("::group::authentik Logs", file=stderr) |  | ||||||
|         super().setUp() |  | ||||||
|         apps.get_app_config("authentik_tenants").ready() |  | ||||||
|         self.wait_timeout = 60 |  | ||||||
|         self.driver = self._get_driver() |  | ||||||
|         self.driver.implicitly_wait(30) |  | ||||||
|         self.wait = WebDriverWait(self.driver, self.wait_timeout) |  | ||||||
|         self.logger = get_logger() |  | ||||||
|         self.user = create_test_admin_user() |  | ||||||
|         if specs := self.get_container_specs(): |  | ||||||
|             self.container = self._start_container(specs) |  | ||||||
|  |  | ||||||
|     def get_container_image(self, base: str) -> str: |     def get_container_image(self, base: str) -> str: | ||||||
|         """Try to pull docker image based on git branch, fallback to main if not found.""" |         """Try to pull docker image based on git branch, fallback to main if not found.""" | ||||||
|         client: DockerClient = from_env() |  | ||||||
|         image = f"{base}:gh-main" |         image = f"{base}:gh-main" | ||||||
|         try: |         try: | ||||||
|             branch_image = f"{base}:{get_docker_tag()}" |             branch_image = f"{base}:{get_docker_tag()}" | ||||||
|             client.images.pull(branch_image) |             self.docker_client.images.pull(branch_image) | ||||||
|             return branch_image |             return branch_image | ||||||
|         except DockerException: |         except DockerException: | ||||||
|             client.images.pull(image) |             self.docker_client.images.pull(image) | ||||||
|         return image |         return image | ||||||
|  |  | ||||||
|     def _start_container(self, specs: dict[str, Any]) -> Container: |     def run_container(self, **specs: dict[str, Any]) -> Container: | ||||||
|         client: DockerClient = from_env() |         if "network_mode" not in specs: | ||||||
|         container = client.containers.run(**specs) |             specs["network"] = self.__network.name | ||||||
|  |         specs["labels"] = self.docker_labels | ||||||
|  |         specs["detach"] = True | ||||||
|  |         specs["auto_remove"] = True | ||||||
|  |         if hasattr(self, "live_server_url"): | ||||||
|  |             specs.setdefault("environment", {}) | ||||||
|  |             specs["environment"]["AUTHENTIK_HOST"] = self.live_server_url | ||||||
|  |         container = self.docker_client.containers.run(**specs) | ||||||
|         container.reload() |         container.reload() | ||||||
|         state = container.attrs.get("State", {}) |         state = container.attrs.get("State", {}) | ||||||
|         if "Health" not in state: |         if "Health" not in state: | ||||||
| @ -117,25 +126,53 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | |||||||
|  |  | ||||||
|     def output_container_logs(self, container: Container | None = None): |     def output_container_logs(self, container: Container | None = None): | ||||||
|         """Output the container logs to our STDOUT""" |         """Output the container logs to our STDOUT""" | ||||||
|         _container = container or self.container |  | ||||||
|         if IS_CI: |         if IS_CI: | ||||||
|             image = _container.image |             image = container.image | ||||||
|             tags = image.tags[0] if len(image.tags) > 0 else str(image) |             tags = image.tags[0] if len(image.tags) > 0 else str(image) | ||||||
|             print(f"::group::Container logs - {tags}") |             print(f"::group::Container logs - {tags}") | ||||||
|         for log in _container.logs().decode().split("\n"): |         for log in container.logs().decode().split("\n"): | ||||||
|             print(log) |             print(log) | ||||||
|         if IS_CI: |         if IS_CI: | ||||||
|             print("::endgroup::") |             print("::endgroup::") | ||||||
|  |  | ||||||
|     def get_container_specs(self) -> dict[str, Any] | None: |     def tearDown(self): | ||||||
|         """Optionally get container specs which will launched on setup, wait for the container to |         containers = self.docker_client.containers.list( | ||||||
|         be healthy, and deleted again on tearDown""" |             filters={"label": ",".join(f"{x}={y}" for x, y in self.docker_labels.items())} | ||||||
|         return None |         ) | ||||||
|  |         for container in containers: | ||||||
|  |             self.output_container_logs(container) | ||||||
|  |             try: | ||||||
|  |                 container.kill() | ||||||
|  |             except DockerException: | ||||||
|  |                 pass | ||||||
|  |         self.__network.remove() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | ||||||
|  |     """StaticLiveServerTestCase which automatically creates a Webdriver instance""" | ||||||
|  |  | ||||||
|  |     host = get_local_ip() | ||||||
|  |     wait_timeout: int | ||||||
|  |     user: User | ||||||
|  |  | ||||||
|  |     def setUp(self): | ||||||
|  |         if IS_CI: | ||||||
|  |             print("::group::authentik Logs", file=stderr) | ||||||
|  |         apps.get_app_config("authentik_tenants").ready() | ||||||
|  |         self.wait_timeout = 60 | ||||||
|  |         self.driver = self._get_driver() | ||||||
|  |         self.driver.implicitly_wait(30) | ||||||
|  |         self.wait = WebDriverWait(self.driver, self.wait_timeout) | ||||||
|  |         self.logger = get_logger() | ||||||
|  |         self.user = create_test_admin_user() | ||||||
|  |         super().setUp() | ||||||
|  |  | ||||||
|     def _get_driver(self) -> WebDriver: |     def _get_driver(self) -> WebDriver: | ||||||
|         count = 0 |         count = 0 | ||||||
|         try: |         try: | ||||||
|             return webdriver.Chrome() |             opts = webdriver.ChromeOptions() | ||||||
|  |             opts.add_argument("--disable-search-engine-choice-screen") | ||||||
|  |             return webdriver.Chrome(options=opts) | ||||||
|         except WebDriverException: |         except WebDriverException: | ||||||
|             pass |             pass | ||||||
|         while count < RETRIES: |         while count < RETRIES: | ||||||
| @ -151,18 +188,15 @@ class SeleniumTestCase(DockerTestCase, StaticLiveServerTestCase): | |||||||
|         raise ValueError(f"Webdriver failed after {RETRIES}.") |         raise ValueError(f"Webdriver failed after {RETRIES}.") | ||||||
|  |  | ||||||
|     def tearDown(self): |     def tearDown(self): | ||||||
|         super().tearDown() |  | ||||||
|         if IS_CI: |         if IS_CI: | ||||||
|             print("::endgroup::", file=stderr) |             print("::endgroup::", file=stderr) | ||||||
|  |         super().tearDown() | ||||||
|         if IS_CI: |         if IS_CI: | ||||||
|             print("::group::Browser logs") |             print("::group::Browser logs") | ||||||
|         for line in self.driver.get_log("browser"): |         for line in self.driver.get_log("browser"): | ||||||
|             print(line["message"]) |             print(line["message"]) | ||||||
|         if IS_CI: |         if IS_CI: | ||||||
|             print("::endgroup::") |             print("::endgroup::") | ||||||
|         if self.container: |  | ||||||
|             self.output_container_logs() |  | ||||||
|             self.container.kill() |  | ||||||
|         self.driver.quit() |         self.driver.quit() | ||||||
|  |  | ||||||
|     def wait_for_url(self, desired_url): |     def wait_for_url(self, desired_url): | ||||||
|  | |||||||
| @ -6,8 +6,6 @@ from tempfile import mkdtemp | |||||||
| import pytest | import pytest | ||||||
| import yaml | import yaml | ||||||
| from channels.testing import ChannelsLiveServerTestCase | from channels.testing import ChannelsLiveServerTestCase | ||||||
| from docker import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from docker.types.healthcheck import Healthcheck | from docker.types.healthcheck import Healthcheck | ||||||
|  |  | ||||||
| from authentik.core.tests.utils import create_test_flow | from authentik.core.tests.utils import create_test_flow | ||||||
| @ -27,11 +25,11 @@ from tests.e2e.utils import DockerTestCase, get_docker_tag | |||||||
| class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase): | class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase): | ||||||
|     """Test Docker Controllers""" |     """Test Docker Controllers""" | ||||||
|  |  | ||||||
|     def _start_container(self, ssl_folder: str) -> Container: |     def setUp(self): | ||||||
|         client: DockerClient = from_env() |         super().setUp() | ||||||
|         container = client.containers.run( |         self.ssl_folder = mkdtemp() | ||||||
|  |         self.run_container( | ||||||
|             image="library/docker:dind", |             image="library/docker:dind", | ||||||
|             detach=True, |  | ||||||
|             network_mode="host", |             network_mode="host", | ||||||
|             privileged=True, |             privileged=True, | ||||||
|             healthcheck=Healthcheck( |             healthcheck=Healthcheck( | ||||||
| @ -41,18 +39,11 @@ class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase): | |||||||
|             ), |             ), | ||||||
|             environment={"DOCKER_TLS_CERTDIR": "/ssl"}, |             environment={"DOCKER_TLS_CERTDIR": "/ssl"}, | ||||||
|             volumes={ |             volumes={ | ||||||
|                 f"{ssl_folder}/": { |                 f"{self.ssl_folder}/": { | ||||||
|                     "bind": "/ssl", |                     "bind": "/ssl", | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.wait_for_container(container) |  | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super().setUp() |  | ||||||
|         self.ssl_folder = mkdtemp() |  | ||||||
|         self.container = self._start_container(self.ssl_folder) |  | ||||||
|         # Ensure that local connection have been created |         # Ensure that local connection have been created | ||||||
|         outpost_connection_discovery() |         outpost_connection_discovery() | ||||||
|         self.provider: ProxyProvider = ProxyProvider.objects.create( |         self.provider: ProxyProvider = ProxyProvider.objects.create( | ||||||
| @ -91,7 +82,6 @@ class OutpostDockerTests(DockerTestCase, ChannelsLiveServerTestCase): | |||||||
|  |  | ||||||
|     def tearDown(self) -> None: |     def tearDown(self) -> None: | ||||||
|         super().tearDown() |         super().tearDown() | ||||||
|         self.container.kill() |  | ||||||
|         try: |         try: | ||||||
|             rmtree(self.ssl_folder) |             rmtree(self.ssl_folder) | ||||||
|         except PermissionError: |         except PermissionError: | ||||||
|  | |||||||
| @ -6,8 +6,6 @@ from tempfile import mkdtemp | |||||||
| import pytest | import pytest | ||||||
| import yaml | import yaml | ||||||
| from channels.testing.live import ChannelsLiveServerTestCase | from channels.testing.live import ChannelsLiveServerTestCase | ||||||
| from docker import DockerClient, from_env |  | ||||||
| from docker.models.containers import Container |  | ||||||
| from docker.types.healthcheck import Healthcheck | from docker.types.healthcheck import Healthcheck | ||||||
|  |  | ||||||
| from authentik.core.tests.utils import create_test_flow | from authentik.core.tests.utils import create_test_flow | ||||||
| @ -27,11 +25,11 @@ from tests.e2e.utils import DockerTestCase, get_docker_tag | |||||||
| class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase): | class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase): | ||||||
|     """Test Docker Controllers""" |     """Test Docker Controllers""" | ||||||
|  |  | ||||||
|     def _start_container(self, ssl_folder: str) -> Container: |     def setUp(self): | ||||||
|         client: DockerClient = from_env() |         super().setUp() | ||||||
|         container = client.containers.run( |         self.ssl_folder = mkdtemp() | ||||||
|  |         self.run_container( | ||||||
|             image="library/docker:dind", |             image="library/docker:dind", | ||||||
|             detach=True, |  | ||||||
|             network_mode="host", |             network_mode="host", | ||||||
|             privileged=True, |             privileged=True, | ||||||
|             healthcheck=Healthcheck( |             healthcheck=Healthcheck( | ||||||
| @ -41,18 +39,11 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase): | |||||||
|             ), |             ), | ||||||
|             environment={"DOCKER_TLS_CERTDIR": "/ssl"}, |             environment={"DOCKER_TLS_CERTDIR": "/ssl"}, | ||||||
|             volumes={ |             volumes={ | ||||||
|                 f"{ssl_folder}/": { |                 f"{self.ssl_folder}/": { | ||||||
|                     "bind": "/ssl", |                     "bind": "/ssl", | ||||||
|                 } |                 } | ||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         self.wait_for_container(container) |  | ||||||
|         return container |  | ||||||
|  |  | ||||||
|     def setUp(self): |  | ||||||
|         super().setUp() |  | ||||||
|         self.ssl_folder = mkdtemp() |  | ||||||
|         self.container = self._start_container(self.ssl_folder) |  | ||||||
|         # Ensure that local connection have been created |         # Ensure that local connection have been created | ||||||
|         outpost_connection_discovery() |         outpost_connection_discovery() | ||||||
|         self.provider: ProxyProvider = ProxyProvider.objects.create( |         self.provider: ProxyProvider = ProxyProvider.objects.create( | ||||||
| @ -91,7 +82,6 @@ class TestProxyDocker(DockerTestCase, ChannelsLiveServerTestCase): | |||||||
|  |  | ||||||
|     def tearDown(self) -> None: |     def tearDown(self) -> None: | ||||||
|         super().tearDown() |         super().tearDown() | ||||||
|         self.container.kill() |  | ||||||
|         try: |         try: | ||||||
|             rmtree(self.ssl_folder) |             rmtree(self.ssl_folder) | ||||||
|         except PermissionError: |         except PermissionError: | ||||||
|  | |||||||
| @ -32,7 +32,7 @@ http: | |||||||
|         app: |         app: | ||||||
|             loadBalancer: |             loadBalancer: | ||||||
|                 servers: |                 servers: | ||||||
|                     - url: http://ipp.internal |                     - url: http://ip.internal | ||||||
|         authentik: |         authentik: | ||||||
|             loadBalancer: |             loadBalancer: | ||||||
|                 servers: |                 servers: | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Jens L.
					Jens L.