Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check idp availability #103

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 27 additions & 3 deletions mozilla_django_oidc_db/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
from django.utils.http import url_has_allowed_host_and_scheme
from django.views.generic import TemplateView

import requests
from mozilla_django_oidc.views import (
OIDCAuthenticationCallbackView,
OIDCAuthenticationRequestView as _OIDCAuthenticationRequestView,
)

from .config import get_setting_from_config, store_config
from .exceptions import OIDCProviderOutage
from .models import OpenIDConnectConfig, OpenIDConnectConfigBase

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -181,7 +183,8 @@ def get(
if not self.allow_next_from_query:
self._validate_return_url(request, return_url=return_url)

self.check_idp_availability()
if self.get_settings("OIDCDB_CHECK_IDP_AVAILABILITY", False):
self.check_idp_availability()

response = super().get(request, *args, **kwargs)

Expand Down Expand Up @@ -241,9 +244,30 @@ def check_idp_availability(self) -> None:
"""
Hook for subclasses.

Raise :class:`OIDCProviderOutage` if the Identity Provider is not available.
Raise :class:`OIDCProviderOutage` if the Identity Provider is not available,
which your application code needs to handle.

The default implementation checks if the endpoint has a status code < 401.
"""
pass
endpoint = self.OIDC_OP_AUTH_ENDPOINT
try:
# Verify that the identity provider endpoint can be reached. This is where
# the user ultimately gets redirected to.
#
# 5 seconds wait time is probably already too long for a good user
# experience, but we don't want to be *too* aggressive.
response = requests.get(endpoint, timeout=5.0)
# some IDPs have been observed to return HTTP 400 because of the missing
# query params
if response.status_code > 400:
response.raise_for_status()
except requests.RequestException as exc:
logger.info(
"OIDC provider endpoint '%s' could not be retrieved",
endpoint,
exc_info=exc,
)
raise OIDCProviderOutage("Identity provider appears to be down.") from exc

def get_extra_params(self, request: HttpRequest) -> dict[str, str]:
"""
Expand Down
25 changes: 25 additions & 0 deletions tests/test_init_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

import pytest

from mozilla_django_oidc_db.exceptions import OIDCProviderOutage


@pytest.mark.oidcconfig(
oidc_op_authorization_endpoint="http://localhost:8080/openid-connect/auth"
Expand Down Expand Up @@ -46,3 +48,26 @@ def test_keycloak_idp_hint_via_settings(dummy_config, settings, client):

query = parse_qs(parsed_url.query)
assert query["kc_idp_hint"] == ["keycloak-idp1"]


def test_check_idp_availability_not_available(
dummy_config, settings, client, requests_mock
):
settings.OIDCDB_CHECK_IDP_AVAILABILITY = True
requests_mock.get("https://mock-oidc-provider:9999/oidc/auth", status_code=503)
start_url = reverse("oidc_authentication_init")

with pytest.raises(OIDCProviderOutage):
client.get(start_url)


def test_check_idp_availability_available(
dummy_config, settings, client, requests_mock
):
settings.OIDCDB_CHECK_IDP_AVAILABILITY = True
requests_mock.get("https://mock-oidc-provider:9999/oidc/auth", status_code=400)
start_url = reverse("oidc_authentication_init")

response = client.get(start_url)

assert response.status_code == 302
4 changes: 3 additions & 1 deletion tests/test_init_flow_custom_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def check_idp_availability(self) -> None:
)


def test_idp_check_mechanism(auth_request):
def test_idp_check_mechanism(auth_request, settings):
settings.OIDCDB_CHECK_IDP_AVAILABILITY = True

with pytest.raises(OIDCProviderOutage):
oidc_init_with_idp_check(auth_request)