Skip to content

Commit

Permalink
Allow to specify the type of remote authentication
Browse files Browse the repository at this point in the history
When configuring remote authentication (by the reverse proxy), one
should be able to augment the openAPI security specification
accordingly.

fixes #5437
  • Loading branch information
mdellweg committed Jun 6, 2024
1 parent 072e43f commit 1f3b4df
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 62 deletions.
2 changes: 1 addition & 1 deletion .github/template_gitref
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2021.08.26-337-g7c7a09a
2021.08.26-338-g2237db8
6 changes: 0 additions & 6 deletions .github/workflows/scripts/before_script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,6 @@ tail -v -n +1 .ci/ansible/Containerfile
cmd_prefix bash -c "echo '%wheel ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/nopasswd"
cmd_prefix bash -c "usermod -a -G wheel pulp"

SCENARIOS=("pulp" "performance" "azure" "gcp" "s3" "generate-bindings" "lowerbounds")
if [[ " ${SCENARIOS[*]} " =~ " ${TEST} " ]]; then
# Many functional tests require these
cmd_prefix dnf install -yq lsof which
fi

if [[ "${REDIS_DISABLED:-false}" == true ]]; then
cmd_prefix bash -c "s6-rc -d change redis"
echo "The Redis service was disabled for $TEST"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ if [ "$TEST" = "azure" ]; then
- ./azurite:/etc/pulp\
command: "azurite-blob --blobHost 0.0.0.0 --cert /etc/pulp/azcert.pem --key /etc/pulp/azkey.pem"' vars/main.yaml
sed -i -e '$a azure_test: true\
pulp_scenario_settings: {"domain_enabled": true}\
pulp_scenario_settings: {"domain_enabled": true, "rest_framework__default_authentication_classes": "@merge pulpcore.app.authentication.PulpRemoteUserAuthentication"}\
pulp_scenario_env: {"otel_bsp_max_export_batch_size": 1, "otel_bsp_max_queue_size": 1, "otel_exporter_otlp_endpoint": "http://localhost:4318", "otel_exporter_otlp_protocol": "http/protobuf", "otel_metric_export_interval": 800, "pulp_otel_enabled": "true"}\
' vars/main.yaml
fi
Expand Down
47 changes: 0 additions & 47 deletions CHANGES/.TEMPLATE.rst

This file was deleted.

2 changes: 2 additions & 0 deletions CHANGES/5437.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add ability to configure the openapi schema for remote user authentication via `REMOTE_USER_OPENAPI_SECURITY_SCHEME`.
It defaults to "mutualTLS" for cert based authentication.
1 change: 1 addition & 0 deletions pulpcore/app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@
TMPFILE_PROTECTION_TIME = 0

REMOTE_USER_ENVIRON_NAME = "REMOTE_USER"
REMOTE_USER_OPENAPI_SECURITY_SCHEME = {"type": "mutualTLS"}

AUTHENTICATION_JSON_HEADER = ""
AUTHENTICATION_JSON_HEADER_JQ_FILTER = ""
Expand Down
13 changes: 13 additions & 0 deletions pulpcore/openapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,9 +518,22 @@ def get_schema(self, request=None, public=False):
server_url = "http://localhost:24817" if not request else request.build_absolute_uri("/")
result["servers"] = [{"url": server_url}]

if "bindings" in request.query_params:
securitySchemes = {
k: v for k, v in result["components"]["securitySchemes"].items() if k == "basicAuth"
}
result["components"]["securitySchemes"] = securitySchemes
return normalize_result_object(result)


class PulpRemoteUserAuthenticationScheme(OpenApiAuthenticationExtension):
target_class = "pulpcore.app.authentication.PulpRemoteUserAuthentication"
name = "RemoteUserAuthentication"

def get_security_definition(self, auto_schema):
return settings.REMOTE_USER_OPENAPI_SECURITY_SCHEME


class JSONHeaderRemoteAuthenticationScheme(OpenApiAuthenticationExtension):
target_class = "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
name = "json_header_remote_authentication"
Expand Down
21 changes: 18 additions & 3 deletions pulpcore/tests/functional/api/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@


@pytest.mark.parallel
@pytest.mark.skipif(
"rest_framework.authentication.BasicAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless BasicAuthentication is enabled",
)
def test_base_auth_success(pulpcore_bindings, pulp_admin_user):
"""Perform HTTP basic authentication with valid credentials.
Expand All @@ -27,6 +32,11 @@ def test_base_auth_success(pulpcore_bindings, pulp_admin_user):


@pytest.mark.parallel
@pytest.mark.skipif(
"rest_framework.authentication.BasicAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless BasicAuthentication is enabled",
)
def test_base_auth_failure(pulpcore_bindings, invalid_user):
"""Perform HTTP basic authentication with invalid credentials.
Expand All @@ -44,6 +54,11 @@ def test_base_auth_failure(pulpcore_bindings, invalid_user):


@pytest.mark.parallel
@pytest.mark.skipif(
"rest_framework.authentication.BasicAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless BasicAuthentication is enabled",
)
def test_base_auth_required(pulpcore_bindings, anonymous_user):
"""Perform HTTP basic authentication with no credentials.
Expand All @@ -63,7 +78,7 @@ def test_base_auth_required(pulpcore_bindings, anonymous_user):
@pytest.mark.parallel
@pytest.mark.skipif(
"django.contrib.auth.backends.RemoteUserBackend" not in settings.AUTHENTICATION_BACKENDS
and "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
or "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless RemoteUserBackend and JSONHeaderRemoteAuthentication are enabled",
)
Expand All @@ -86,7 +101,7 @@ def test_jq_header_remote_auth(pulpcore_bindings, anonymous_user):
@pytest.mark.parallel
@pytest.mark.skipif(
"django.contrib.auth.backends.RemoteUserBackend" not in settings.AUTHENTICATION_BACKENDS
and "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
or "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless RemoteUserBackend and JSONHeaderRemoteAuthentication are enabled",
)
Expand Down Expand Up @@ -115,7 +130,7 @@ def test_jq_header_remote_auth_denied_by_wrong_header(pulpcore_bindings, anonymo
@pytest.mark.parallel
@pytest.mark.skipif(
"django.contrib.auth.backends.RemoteUserBackend" not in settings.AUTHENTICATION_BACKENDS
and "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
or "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
not in settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"],
reason="Test can't run unless RemoteUserBackend and JSONHeaderRemoteAuthentication are enabled",
)
Expand Down
16 changes: 15 additions & 1 deletion pulpcore/tests/functional/api/test_openapi_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,26 @@ def test_no_dup_operation_ids(pulp_openapi_schema):
assert len(dup_ids) == 0, f"Duplicate operationIds found: {dup_ids}"


@pytest.mark.parallel
def test_remote_user_auth_security_scheme(pulp_settings, pulp_openapi_schema):
if (
"pulpcore.app.authentication.PulpRemoteUserAuthentication"
not in pulp_settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"]
):
pytest.skip("Test can't run unless PulpRemoteUserAuthentication is enabled.")

expected_security_scheme = pulp_settings.REMOTE_USER_OPENAPI_SECURITY_SCHEME
security_schemes = pulp_openapi_schema["components"]["securitySchemes"]

assert security_schemes["remote_user_authentication"] == expected_security_scheme


@pytest.mark.parallel
def test_external_auth_on_security_scheme(pulp_settings, pulp_openapi_schema):
if (
"django.contrib.auth.backends.RemoteUserBackend"
not in pulp_settings.AUTHENTICATION_BACKENDS
and "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
or "pulpcore.app.authentication.JSONHeaderRemoteAuthentication"
not in pulp_settings.REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"]
):
pytest.skip(
Expand Down
12 changes: 10 additions & 2 deletions staging_docs/admin/learn/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ Defaults to `30` seconds.

### REMOTE_USER_ENVIRON_NAME

The name of the WSGI environment variable to read for `webserver authentication
<webserver-authentication>`.
The name of the WSGI environment variable to read for `webserver authentication <webserver-authentication>`.
It is only used with the `PulpRemoteUserAuthentication` authentication class.

!!! warning
Configuring this has serious security implications. See the [Django warning at the end of this
Expand All @@ -283,6 +283,14 @@ Defaults to `'REMOTE_USER'`.



### REMOTE_USER_OPENAPI_SECURITY_SCHEME

A JSON object representing the security scheme advertised for the `PulpRemoteUserAuthentication` authentication class.

Defaults to `{"type": "mutualTLS"}`, which represents x509 certificate based authentication.



### ALLOWED_IMPORT_PATHS

One or more real filesystem paths that Remotes with filesystem paths can import from. For example
Expand Down
1 change: 1 addition & 0 deletions template_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pulp_settings:
upload_protection_time: 10
pulp_settings_azure:
domain_enabled: true
rest_framework__default_authentication_classes: '@merge pulpcore.app.authentication.PulpRemoteUserAuthentication'
pulp_settings_gcp: null
pulp_settings_s3:
authentication_backends: '@merge django.contrib.auth.backends.RemoteUserBackend'
Expand Down
2 changes: 1 addition & 1 deletion unittest_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ aiotools
pytest<8
pytest-django
pytest-asyncio
pytest-redis
pytest-redis<3.1.0

0 comments on commit 1f3b4df

Please sign in to comment.