Skip to content

Commit

Permalink
🚧[#114] add optional setup config
Browse files Browse the repository at this point in the history
  • Loading branch information
Coperh committed Jul 10, 2024
1 parent d0630df commit df9ec47
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 3 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ jobs:
python: ['3.10', '3.11', '3.12']
django: ['4.2']
mozilla_django_oidc: ['4.0']
setup_config_enabled: ['no', 'yes']

name: Run the test suite (Python ${{ matrix.python }}, Django ${{ matrix.django }}, mozilla-django-oidc ${{ matrix.mozilla_django_oidc }})
name: "Run the test suite (Python ${{ matrix.python }}, Django ${{ matrix.django }},
mozilla-django-oidc ${{ matrix.mozilla_django_oidc }}, Setup Config: ${{ matrix.setup_config_enabled }}))"

services:
postgres:
Expand All @@ -41,13 +43,15 @@ jobs:
run: pip install tox tox-gh-actions

- name: Run tests
run: tox
run: |
tox -- ${{ matrix.setup_config_enabled != 'yes' && '--ignore tests/setupconfig' || '' }}
env:
PYTHON_VERSION: ${{ matrix.python }}
DJANGO: ${{ matrix.django }}
MOZILLA_DJANGO_OIDC: ${{ matrix.mozilla_django_oidc }}
PGUSER: postgres
PGHOST: localhost
SETUP_CONFIG_ENABLED: ${{ matrix.setup_config_enabled }}

- name: Publish coverage report
uses: codecov/codecov-action@v4
Expand Down
Empty file.
89 changes: 89 additions & 0 deletions mozilla_django_oidc_db/setupconfig/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from django.conf import settings
from django.contrib.auth.models import Group

from django_setup_configuration.configuration import BaseConfigurationStep
from django_setup_configuration.exceptions import ConfigurationRunFailed

from ..forms import OpenIDConnectConfigForm
from ..models import OpenIDConnectConfig


class AdminOIDCConfigurationStep(BaseConfigurationStep):
"""
Configure admin login via OpenID Connect
"""

verbose_name = "Configuration for admin login via OpenID Connect"
required_settings = [
"ADMIN_OIDC_OIDC_RP_CLIENT_ID",
"ADMIN_OIDC_OIDC_RP_CLIENT_SECRET",
]
all_settings = required_settings + [
"ADMIN_OIDC_OIDC_RP_SCOPES_LIST",
"ADMIN_OIDC_OIDC_RP_SIGN_ALGO",
"ADMIN_OIDC_OIDC_RP_IDP_SIGN_KEY",
"ADMIN_OIDC_OIDC_OP_DISCOVERY_ENDPOINT",
"ADMIN_OIDC_OIDC_OP_JWKS_ENDPOINT",
"ADMIN_OIDC_OIDC_OP_AUTHORIZATION_ENDPOINT",
"ADMIN_OIDC_OIDC_OP_TOKEN_ENDPOINT",
"ADMIN_OIDC_OIDC_OP_USER_ENDPOINT",
"ADMIN_OIDC_USERNAME_CLAIM",
"ADMIN_OIDC_GROUPS_CLAIM",
"ADMIN_OIDC_CLAIM_MAPPING",
"ADMIN_OIDC_SYNC_GROUPS",
"ADMIN_OIDC_SYNC_GROUPS_GLOB_PATTERN",
"ADMIN_OIDC_DEFAULT_GROUPS",
"ADMIN_OIDC_MAKE_USERS_STAFF",
"ADMIN_OIDC_SUPERUSER_GROUP_NAMES",
"ADMIN_OIDC_OIDC_USE_NONCE",
"ADMIN_OIDC_OIDC_NONCE_SIZE",
"ADMIN_OIDC_OIDC_STATE_SIZE",
"ADMIN_OIDC_OIDC_EXEMPT_URLS",
"ADMIN_OIDC_USERINFO_CLAIMS_SOURCE",
]
enable_setting = "ADMIN_OIDC_CONFIG_ENABLE"

def is_configured(self) -> bool:
return OpenIDConnectConfig.get_solo().enabled

def configure(self):
config = OpenIDConnectConfig.get_solo()

# Use the model defaults
form_data = {
field.name: getattr(config, field.name)
for field in OpenIDConnectConfig._meta.fields
}

# `email` is in the claim_mapping by default, but email is used as the username field
# by OIP, and you cannot map the username field when using OIDC
if "email" in form_data["claim_mapping"]:
del form_data["claim_mapping"]["email"]

# Only override field values with settings if they are defined
for setting in self.all_settings:
value = getattr(settings, setting, None)
if value is not None:
model_field_name = setting.split("ADMIN_OIDC_")[1].lower()
if model_field_name == "default_groups":
for group_name in value:
Group.objects.get_or_create(name=group_name)
value = Group.objects.filter(name__in=value)

form_data[model_field_name] = value
form_data["enabled"] = True

# Use the admin form to apply validation and fetch URLs from the discovery endpoint
form = OpenIDConnectConfigForm(data=form_data)
if not form.is_valid():
raise ConfigurationRunFailed(
f"Something went wrong while saving configuration: {form.errors}"
)

form.save()

def test_configuration(self):
"""
TODO not sure if it is feasible (because there are different possible IdPs),
but it would be nice if we could test the login automatically
"""
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ Documentation = "https://mozilla-django-oidc-db.readthedocs.io/en/latest/"
Changelog = "https://github.com/maykinmedia/mozilla-django-oidc-db/blob/master/CHANGELOG.rst"

[project.optional-dependencies]
setupconfig = [
"django-setup-configuration>=0.1.0",
]
tests = [
"psycopg2",
"pytest",
Expand Down
Empty file added tests/setupconfig/__init__.py
Empty file.
65 changes: 65 additions & 0 deletions tests/setupconfig/test_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import pytest

from mozilla_django_oidc_db.models import (
OpenIDConnectConfig,
UserInformationClaimsSources,
)
from mozilla_django_oidc_db.setupconfig.auth import AdminOIDCConfigurationStep

IDENTITY_PROVIDER = "sdfsdfsdfsdfsd"


def test_configure():
AdminOIDCConfigurationStep().configure()

config = OpenIDConnectConfig.get_solo()

assert config.enabled
assert config.oidc_rp_client_id == "client-id"
assert config.oidc_rp_client_secret == "secret"
assert config.oidc_rp_scopes_list == ["open_id", "email", "profile", "extra_scope"]
assert config.oidc_rp_sign_algo == "RS256"
assert config.oidc_rp_idp_sign_key == "key"
assert config.oidc_op_discovery_endpoint == ""
assert (
config.oidc_op_jwks_endpoint
== f"{IDENTITY_PROVIDER}protocol/openid-connect/certs"
)
assert (
config.oidc_op_authorization_endpoint
== f"{IDENTITY_PROVIDER}protocol/openid-connect/auth"
)
assert (
config.oidc_op_token_endpoint
== f"{IDENTITY_PROVIDER}protocol/openid-connect/token"
)
assert (
config.oidc_op_user_endpoint
== f"{IDENTITY_PROVIDER}protocol/openid-connect/userinfo"
)
assert config.username_claim == "claim_name"
assert config.groups_claim == "groups_claim_name"
assert config.claim_mapping == {"first_name": "given_name"}
assert not config.sync_groups
assert config.sync_groups_glob_pattern == "local.groups.*"
assert list(group.name for group in config.default_groups.all()) == [
"Admins",
"Read-only",
]
assert config.make_users_staff
assert config.superuser_group_names == ["superuser"]
assert not config.oidc_use_nonce
assert config.oidc_nonce_size == 48
assert config.oidc_state_size == 48
assert config.oidc_exempt_urls == ["http://testserver/some-endpoint"]
assert config.userinfo_claims_source == UserInformationClaimsSources.id_token


@pytest.mark.skip(reason="Testing config for DigiD OIDC is not implemented yet")
def test_configuration_check_ok():
raise NotImplementedError


@pytest.mark.skip(reason="Testing config for DigiD OIDC is not implemented yet")
def test_configuration_check_failures():
raise NotImplementedError
9 changes: 8 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tox]
envlist =
py{310,311,312}-django{42}-mozilla_django_oidc{40}
py{310,311,312}-django{42}-mozilla_django_oidc{40}-setup_config_{enabled,disabled}
isort
black
docs
Expand All @@ -11,6 +11,9 @@ python =
3.10: py310
3.11: py311
3.12: py312
SETUP_CONFIG_ENABLED =
yes: setup_config_enabled
no: setup_config_disabled

[gh-actions:env]
DJANGO =
Expand All @@ -21,6 +24,7 @@ MOZILLA_DJANGO_OIDC =
extras =
tests
coverage
setup_config_enabled: setupconfig
deps =
django42: Django~=4.2.0
mozilla_django_oidc40: mozilla-django-oidc~=4.0.0
Expand All @@ -30,8 +34,10 @@ passenv =
PGPASSWORD
PGPORT
PGHOST
SETUP_CONFIG_ENABLED
setenv =
PYTHONPATH = {toxinidir}

commands =
pytest tests \
--cov --cov-report xml:reports/coverage-{envname}.xml \
Expand All @@ -52,6 +58,7 @@ basepython=python
changedir=docs
skipsdist=true
extras =
setupconfig
db
docs
tests
Expand Down

0 comments on commit df9ec47

Please sign in to comment.