Skip to content
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
14 changes: 11 additions & 3 deletions .github/workflows/python-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python 3.13
uses: actions/setup-python@v3
with:
Expand Down Expand Up @@ -54,7 +56,9 @@ jobs:
- os: "ubuntu-latest"
python-version: "3.14.0-rc.2"
steps:
- uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
Expand Down Expand Up @@ -90,7 +94,11 @@ jobs:
runs-on: ubuntu-latest
needs: test
steps:
- uses: actions/checkout@v4
- name: Checkout code
uses: actions/checkout@v4
with:
# No shallow clone for a better analysis
fetch-depth: 0

- name: Download coverage report
uses: actions/download-artifact@v4
Expand Down
2 changes: 1 addition & 1 deletion src/django_windowsauthtoken/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def retrieve_auth_user_details(auth_token: str) -> tuple[str, str]:
# Always try to close the token handle, but ignore any issues with it
try:
win32api.CloseHandle(token_handle)
except pywintypes.error as err:
except pywintypes.error as err: # pragma: no cover
# just log and continue
logger.warning(f"Failed to close token handle: {err}")

Expand Down
14 changes: 9 additions & 5 deletions src/django_windowsauthtoken/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ def debug_view(request):

return JsonResponse(
{
"META HTTP_X_IIS_WINDOWSAUTHTOKEN": request.META.get("HTTP_X_IIS_WINDOWSAUTHTOKEN", "N/A"),
"META REMOTE_USER": request.META.get("REMOTE_USER", "N/A"),
"META HTTP_REMOTE_USER": request.META.get("HTTP_REMOTE_USER", "N/A"),
"META WINDOWSAUTHTOKEN_USER": request.META.get("WINDOWSAUTHTOKEN", "N/A"),
"META WINDOWSAUTHTOKEN_DOMAIN": request.META.get("WINDOWSAUTHTOKEN_DOMAIN", "N/A"),
# Include relevant META information for easy debugging
"META___HTTP_X_IIS_WINDOWSAUTHTOKEN": request.META.get("HTTP_X_IIS_WINDOWSAUTHTOKEN", "N/A"),
"META___REMOTE_USER": request.META.get("REMOTE_USER", "N/A"),
"META___HTTP_REMOTE_USER": request.META.get("HTTP_REMOTE_USER", "N/A"),
"META___WINDOWSAUTHTOKEN_USER": request.META.get("WINDOWSAUTHTOKEN_USER", "N/A"),
"META___WINDOWSAUTHTOKEN_DOMAIN": request.META.get("WINDOWSAUTHTOKEN_DOMAIN", "N/A"),
# State of the user object
"user.is_authenticated": request.user.is_authenticated,
"user.is_anonymous": request.user.is_anonymous,
"user.username": request.user.username if request.user.is_authenticated else "N/A",
# Full request representation for deeper debugging if needed, mainly ASGI/WSGI differences
"request": str(request),
# Full META dump for deeper debugging if needed
"META": {k: str(v) for k, v in request.META.items()},
}
)
9 changes: 9 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ def pytest_configure():
"django.contrib.sessions",
"django.contrib.admin",
],
MIDDLEWARE=[
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_windowsauthtoken.middleware.WindowsAuthTokenMiddleware",
"django.contrib.auth.middleware.RemoteUserMiddleware",
],
AUTHENTICATION_BACKENDS=[
"django.contrib.auth.backends.RemoteUserBackend",
],
ROOT_URLCONF="urlconf",
SECRET_KEY="django-insecure-test-key",
)
30 changes: 4 additions & 26 deletions tests/test_middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,30 +235,19 @@ def test_combine_with_remote_user_middleware(mocker, settings, client):
return_value=("testuser", "TESTDOMAIN"),
)

settings.MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_windowsauthtoken.middleware.WindowsAuthTokenMiddleware",
"django.contrib.auth.middleware.RemoteUserMiddleware",
]
settings.AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.RemoteUserBackend",
]
settings.WINDOWSAUTHTOKEN_USERNAME_FORMATTER = "django_windowsauthtoken.formatters.format_email_like"

User = get_user_model()
assert User.objects.count() == 0

response = client.get("/", headers={"X-IIS-WindowsAuthToken": "valid_token"})

assert response.status_code == 200
assert response.wsgi_request.META["REMOTE_USER"] == "testuser@TESTDOMAIN"
assert response.wsgi_request.META["REMOTE_USER"] == r"TESTDOMAIN\testuser"
assert response.wsgi_request.user.is_authenticated is True

assert User.objects.count() == 1, "User should be created by RemoteUserMiddleware"
user = User.objects.first()
assert user == response.wsgi_request.user
assert user.username == "testuser@TESTDOMAIN"
assert user.username == r"TESTDOMAIN\testuser"


@pytest.mark.asyncio
Expand All @@ -269,27 +258,16 @@ async def test_combine_with_remote_user_middleware_async(mocker, settings, async
return_value=("testuser", "TESTDOMAIN"),
)

settings.MIDDLEWARE = [
"django.contrib.sessions.middleware.SessionMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django_windowsauthtoken.middleware.WindowsAuthTokenMiddleware",
"django.contrib.auth.middleware.RemoteUserMiddleware",
]
settings.AUTHENTICATION_BACKENDS = [
"django.contrib.auth.backends.RemoteUserBackend",
]
settings.WINDOWSAUTHTOKEN_USERNAME_FORMATTER = "django_windowsauthtoken.formatters.format_email_like"

User = get_user_model()
assert await User.objects.acount() == 0

response = await async_client.get("/", headers={"X-IIS-WindowsAuthToken": "valid_token"})

assert response.status_code == 200
assert response.asgi_request.META["HTTP_REMOTE_USER"] == "testuser@TESTDOMAIN"
assert response.asgi_request.META["HTTP_REMOTE_USER"] == r"TESTDOMAIN\testuser"
assert response.asgi_request.user.is_authenticated is True

assert await User.objects.acount() == 1, "User should be created by RemoteUserMiddleware"
user = await User.objects.afirst()
assert user == response.asgi_request.user
assert user.username == "testuser@TESTDOMAIN"
assert user.username == r"TESTDOMAIN\testuser"
55 changes: 55 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import pytest


@pytest.mark.django_db
def test_debug_view_authenticated_success(settings, mocker, client):
settings.DEBUG = True

mocker.patch(
"django_windowsauthtoken.middleware.WindowsAuthTokenMiddleware.retrieve_auth_user_details",
return_value=("testuser", "TESTDOMAIN"),
)

response = client.get("/debug/", headers={"X-Iis-WindowsAuthToken": "123"})
assert response.status_code == 200

data = response.json()
assert data["META___HTTP_X_IIS_WINDOWSAUTHTOKEN"] == "123"
assert data["META___REMOTE_USER"] == r"TESTDOMAIN\testuser"
assert data["META___HTTP_REMOTE_USER"] == r"TESTDOMAIN\testuser"
assert data["META___WINDOWSAUTHTOKEN_USER"] == "testuser"
assert data["META___WINDOWSAUTHTOKEN_DOMAIN"] == "TESTDOMAIN"
assert data["user.is_authenticated"] is True
assert data["user.is_anonymous"] is False
assert data["user.username"] == r"TESTDOMAIN\testuser"
assert "request" in data
assert "META" in data


def test_debug_view_unauthenticated_success(settings, client):
settings.DEBUG = True

response = client.get("/debug/")
assert response.status_code == 200

data = response.json()
assert data["META___HTTP_X_IIS_WINDOWSAUTHTOKEN"] == "N/A"
assert data["META___REMOTE_USER"] == "N/A"
assert data["META___HTTP_REMOTE_USER"] == "N/A"
assert data["META___WINDOWSAUTHTOKEN_USER"] == "N/A"
assert data["META___WINDOWSAUTHTOKEN_DOMAIN"] == "N/A"
assert data["user.is_authenticated"] is False
assert data["user.is_anonymous"] is True
assert data["user.username"] == "N/A"
assert "request" in data
assert "META" in data


def test_debug_view_not_debug(client, settings):
settings.DEBUG = False

response = client.get("/debug/")
assert response.status_code == 200

data = response.json()
assert data == {"error": "This view is only available in DEBUG mode."}
3 changes: 3 additions & 0 deletions tests/urlconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
from django.http import HttpResponse
from django.urls import path

from django_windowsauthtoken.views import debug_view


def hello_world(request):
return HttpResponse("Hello, world!")


urlpatterns = [
path("debug/", debug_view, name="debug"),
path("", hello_world, name="home"),
]
Loading