🔧(backend) add setting to toggle application token exchange mechanism

Introduce a configuration flag to enable or disable the
application token exchange (service account) mechanism.

This allows activating alternative authentication backends
without requiring full application token configuration.

Required to support the upcoming add-ons authentication backend.
This commit is contained in:
lebaudantoine
2026-04-29 16:57:51 +02:00
parent e5a804f748
commit 012857f8c6
7 changed files with 133 additions and 1 deletions
@@ -123,6 +123,25 @@ def test_api_rooms_list_with_expired_token(settings):
assert "expired" in str(response.data).lower()
@mock.patch.object(ResourceServerAuthentication, "authenticate", return_value=None)
def test_api_rooms_list_with_application_disabled(mock_rs_authenticate, settings):
"""Listing rooms should return 401 when application is disabled."""
settings.APPLICATION_ENABLED = False
user = UserFactory()
# Generate expired token
token = generate_test_token(user, [ApplicationScope.ROOMS_LIST])
client = APIClient()
client.credentials(HTTP_AUTHORIZATION=f"Bearer {token}")
response = client.get("/external-api/v1.0/rooms/")
assert response.status_code == 401
mock_rs_authenticate.assert_called_once()
@responses.activate
def test_api_rooms_list_with_invalid_rs_token(settings):
"""Listing rooms with invalid resource server token should return 400."""
@@ -1106,6 +1125,64 @@ def test_resource_server_authentication_successful(settings):
assert expected_ids == results_id
@responses.activate
def test_resource_server_authentication_successful_when_application_disabled(settings):
"""Resource server should keep working when the application auth backend is disabled."""
settings.APPLICATION_ENABLED = False
user = UserFactory(sub="very-specific-sub")
other_user = UserFactory()
RoomFactory(access_level=RoomAccessLevel.PUBLIC)
RoomFactory(access_level=RoomAccessLevel.TRUSTED)
RoomFactory(access_level=RoomAccessLevel.RESTRICTED)
room_user_accesses = RoomFactory(
access_level=RoomAccessLevel.RESTRICTED, users=[user]
)
RoomFactory(access_level=RoomAccessLevel.RESTRICTED, users=[other_user])
assert (
settings.OIDC_RS_BACKEND_CLASS
== "core.external_api.authentication.ResourceServerBackend"
)
settings.OIDC_RS_CLIENT_ID = "some_client_id"
settings.OIDC_RS_CLIENT_SECRET = "some_client_secret"
settings.OIDC_RS_SCOPES_PREFIX = "lasuite_meet"
settings.OIDC_OP_URL = "https://oidc.example.com"
settings.OIDC_VERIFY_SSL = False
settings.OIDC_TIMEOUT = 5
settings.OIDC_PROXY = None
settings.OIDC_OP_JWKS_ENDPOINT = "https://oidc.example.com/jwks"
settings.OIDC_OP_INTROSPECTION_ENDPOINT = "https://oidc.example.com/introspect"
responses.add(
responses.POST,
"https://oidc.example.com/introspect",
json={
"iss": "https://oidc.example.com",
"aud": "some_client_id", # settings.OIDC_RS_CLIENT_ID
"sub": "very-specific-sub",
"client_id": "some_service_provider",
"scope": "openid lasuite_meet lasuite_meet:rooms:list lasuite_meet:rooms:retrieve",
"active": True,
},
)
client = APIClient()
client.credentials(HTTP_AUTHORIZATION="Bearer some_token")
response = client.get("/external-api/v1.0/rooms/")
assert response.status_code == 200
results = response.json()["results"]
assert len(results) == 1
expected_ids = {str(room_user_accesses.id)}
results_id = {result["id"] for result in results}
assert expected_ids == results_id
@responses.activate
def test_resource_server_denies_access_with_insufficient_scopes(settings):
"""Requests should be denied when the token lacks required scopes.
@@ -19,6 +19,35 @@ from core.models import ApplicationScope, User
pytestmark = pytest.mark.django_db
def test_api_applications_generate_token_application_disabled(settings):
"""When APPLICATION_ENABLED is False, the endpoint should return 404."""
settings.APPLICATION_ENABLED = False
user = UserFactory(email="user@example.com")
application = ApplicationFactory(
is_active=True,
scopes=[ApplicationScope.ROOMS_LIST],
)
plain_secret = "test-secret-123"
application.client_secret = plain_secret
application.save()
client = APIClient()
response = client.post(
"/external-api/v1.0/application/token/",
{
"client_id": application.client_id,
"client_secret": plain_secret,
"grant_type": "client_credentials",
"scope": user.email,
},
format="json",
)
assert response.status_code == 404
def test_api_applications_generate_token_success(settings):
"""Valid credentials should return a JWT token."""
UserFactory(email="User.Family@example.com")