Unverified Commit 9c0ef303 authored by John Davis's avatar John Davis Committed by GitHub
Browse files

Merge pull request #20845 from AustralianBioCommons/oidc-pipeline-config

OIDC authentication pipeline - allow adding extra steps to the default pipeline
parents 1265e509 1f4cd329
Loading
Loading
Loading
Loading
+31 −0
Original line number Diff line number Diff line
@@ -4181,6 +4181,37 @@
:Type: str


~~~~~~~~~~~~~~~~~~~~~~
``oidc_auth_pipeline``
~~~~~~~~~~~~~~~~~~~~~~

:Description:
    Sets the full sequence of steps that Python Social Auth goes
    through when authenticating an OIDC login. Use when you want to
    completely customize the pipeline (e.g. for testing).
    By default, Galaxy uses galaxy.authnz.psa_authnz.AUTH_PIPELINE -
    see there for example steps.
    Each element should be an import path to a function, e.g.
    galaxy.authnz.psa_authnz.contains_required_data
:Default: ``None``
:Type: seq


~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``oidc_auth_pipeline_extra``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:Description:
    Sets additional authentication pipeline steps, added after the
    default steps (from galaxy.authnz.psa_authnz.AUTH_PIPELINE).
    Use when you want to keep the default pipeline, but add additional
    custom processing.
    Each element should be an import path to a function, e.g.
    galaxy.authnz.psa_authnz.contains_required_data
:Default: ``None``
:Type: seq


~~~~~~~~~~~~~~~~~~~~~
``oidc_scope_prefix``
~~~~~~~~~~~~~~~~~~~~~
+4 −1
Original line number Diff line number Diff line
@@ -118,7 +118,10 @@ class PSAAuthnz(IdentityProvider):

        self.config[setting_name("USER_MODEL")] = "models.User"
        # Use a custom auth pipeline if configured.
        auth_pipeline = app_config.get("oidc_auth_pipeline", AUTH_PIPELINE)
        auth_pipeline = app_config.oidc_auth_pipeline or AUTH_PIPELINE
        # Add extra steps to the auth pipeline if configured.
        if app_config.oidc_auth_pipeline_extra:
            auth_pipeline = auth_pipeline + tuple(app_config.oidc_auth_pipeline_extra)
        self.config["SOCIAL_AUTH_PIPELINE"] = auth_pipeline
        self.config["DISCONNECT_PIPELINE"] = DISCONNECT_PIPELINE
        self.config[setting_name("AUTHENTICATION_BACKENDS")] = (BACKENDS[provider],)
+16 −7
Original line number Diff line number Diff line
@@ -2282,13 +2282,22 @@ galaxy:
  # <config_dir>.
  #oidc_backends_config_file: oidc_backends_config.xml

  # Sets a custom series of steps in the authentication pipeline
  # for OIDC authentication. The default is
  # galaxy.authnz.psa_authnz.AUTH_PIPELINE
  #oidc_auth_pipeline:
  #  - social_core.pipeline.social_auth.social_details
  #  - social_core.pipeline.social_auth.social_uid

  # Sets the full sequence of steps that Python Social Auth goes through
  # when authenticating an OIDC login. Use when you want to completely
  # customize the pipeline (e.g. for testing).
  # By default, Galaxy uses galaxy.authnz.psa_authnz.AUTH_PIPELINE - see
  # there for example steps.
  # Each element should be an import path to a function, e.g.
  # galaxy.authnz.psa_authnz.contains_required_data
  #oidc_auth_pipeline: null

  # Sets additional authentication pipeline steps, added after the
  # default steps (from galaxy.authnz.psa_authnz.AUTH_PIPELINE).
  # Use when you want to keep the default pipeline, but add additional
  # custom processing.
  # Each element should be an import path to a function, e.g.
  # galaxy.authnz.psa_authnz.contains_required_data
  #oidc_auth_pipeline_extra: null

  # Sets the prefix for OIDC scopes specific to this Galaxy instance. If
  # an API call is made against this Galaxy instance using an OIDC
+19 −3
Original line number Diff line number Diff line
@@ -3085,10 +3085,26 @@ mapping:
        type: seq
        required: false
        desc: |
          Sets the sequence of steps that Python Social Auth goes
          through when authenticating an OIDC login. By default,
          Galaxy uses galaxy.authnz.psa_authnz.AUTH_PIPELINE - see
          Sets the full sequence of steps that Python Social Auth goes
          through when authenticating an OIDC login. Use when
          you want to completely customize the pipeline (e.g. for testing).

          By default, Galaxy uses galaxy.authnz.psa_authnz.AUTH_PIPELINE - see
          there for example steps.

          Each element should be an import path to a function,
          e.g. galaxy.authnz.psa_authnz.contains_required_data

      oidc_auth_pipeline_extra:
        type: seq
        required: false
        desc: |
          Sets additional authentication pipeline steps, added after the
          default steps (from galaxy.authnz.psa_authnz.AUTH_PIPELINE).

          Use when you want to keep the default pipeline, but add additional
          custom processing.

          Each element should be an import path to a function,
          e.g. galaxy.authnz.psa_authnz.contains_required_data

+54 −3
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ from datetime import (
    datetime,
    timedelta,
)
from types import SimpleNamespace
from typing import Optional
from unittest.mock import MagicMock

@@ -31,6 +32,7 @@ from galaxy import model
from galaxy.authnz.managers import AuthnzManager
from galaxy.authnz.psa_authnz import (
    _decode_access_token_helper,
    AUTH_PIPELINE,
    decode_access_token,
    PSAAuthnz,
)
@@ -286,10 +288,11 @@ def test_decode_access_token_opaque_token():
def test_oidc_config_custom_auth_pipeline(mock_oidc_config_file, mock_oidc_backend_config_file):
    custom_auth_pipeline = ("custom", "auth", "steps")
    mock_app = MagicMock()
    mock_app.config.get.side_effect = lambda k, default=None: {"oidc_auth_pipeline": custom_auth_pipeline}.get(
        k, default
    mock_app.config = SimpleNamespace(
        oidc_auth_pipeline=custom_auth_pipeline,
        oidc_auth_pipeline_extra=None,
        oidc=defaultdict(dict),
    )
    mock_app.config.oidc = defaultdict(dict)
    manager = AuthnzManager(
        app=mock_app, oidc_config_file=mock_oidc_config_file, oidc_backends_config_file=mock_oidc_backend_config_file
    )
@@ -300,3 +303,51 @@ def test_oidc_config_custom_auth_pipeline(mock_oidc_config_file, mock_oidc_backe
        app_config=mock_app.config,
    )
    assert psa_authnz.config["SOCIAL_AUTH_PIPELINE"] == custom_auth_pipeline


def test_oidc_config_auth_pipeline_extra(mock_oidc_config_file, mock_oidc_backend_config_file):
    """
    Test that the oidc_auth_pipeline_extra config option is used to extend the auth pipeline.
    """
    custom_auth_pipeline_extra = ["extra", "auth", "steps"]
    mock_app = MagicMock()
    mock_app.config = SimpleNamespace(
        oidc_auth_pipeline=None,
        oidc_auth_pipeline_extra=custom_auth_pipeline_extra,
        oidc=defaultdict(dict),
    )
    manager = AuthnzManager(
        app=mock_app, oidc_config_file=mock_oidc_config_file, oidc_backends_config_file=mock_oidc_backend_config_file
    )
    psa_authnz = PSAAuthnz(
        provider="oidc",
        oidc_config=manager.oidc_config,
        oidc_backend_config=manager.oidc_backends_config,
        app_config=mock_app.config,
    )
    assert psa_authnz.config["SOCIAL_AUTH_PIPELINE"] == AUTH_PIPELINE + tuple(custom_auth_pipeline_extra)


def test_oidc_config_custom_auth_pipeline_and_extra(mock_oidc_config_file, mock_oidc_backend_config_file):
    """
    Test that the oidc_auth_pipeline_extra config option is used to extend the auth pipeline,
    when a custom auth pipeline is also specified in the config file.
    """
    custom_auth_pipeline = ("custom", "auth", "steps")
    custom_auth_pipeline_extra = ["extra", "auth", "steps"]
    mock_app = MagicMock()
    mock_app.config = SimpleNamespace(
        oidc_auth_pipeline=custom_auth_pipeline,
        oidc_auth_pipeline_extra=custom_auth_pipeline_extra,
        oidc=defaultdict(dict),
    )
    manager = AuthnzManager(
        app=mock_app, oidc_config_file=mock_oidc_config_file, oidc_backends_config_file=mock_oidc_backend_config_file
    )
    psa_authnz = PSAAuthnz(
        provider="oidc",
        oidc_config=manager.oidc_config,
        oidc_backend_config=manager.oidc_backends_config,
        app_config=mock_app.config,
    )
    assert psa_authnz.config["SOCIAL_AUTH_PIPELINE"] == custom_auth_pipeline + tuple(custom_auth_pipeline_extra)