jira/apps/api/plane/tests/unit/middleware/test_api_authentication.py
sriram veeraghanta fd16d033fc
fix(api): reject API key auth for deactivated user accounts (#9225)
The custom API key authentication only verified that the APIToken row was
active and unexpired; it never checked the owning user's is_active flag.
DRF's IsAuthenticated only checks user.is_authenticated (always True for a
real User), so a user whose account was deactivated could keep using a
previously issued API key indefinitely.

Add user__is_active=True to the validate_api_token() lookup so a token tied
to a disabled account is treated as invalid (a generic AuthenticationFailed,
avoiding account-state disclosure). Applied to both the external API
middleware (plane/api) and the identical, currently unused copy in
plane/app to prevent the gap from being reintroduced.

Adds unit coverage on validate_api_token and an end-to-end contract test
proving GET /api/v1/users/me/ is denied once the account is deactivated.
2026-06-10 11:36:45 +05:30

46 lines
1.6 KiB
Python

# Copyright (c) 2023-present Plane Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.
"""
Unit tests for APIKeyAuthentication.
Covers the access-control guarantees of the external API key authentication:
- a valid token belonging to an active user authenticates successfully
- a valid token is rejected once the underlying user account is deactivated
(prevents an authentication bypass via a disabled account that still holds
a previously generated API key)
"""
import pytest
from rest_framework.exceptions import AuthenticationFailed
from plane.api.middleware.api_authentication import APIKeyAuthentication
from plane.db.models import APIToken
@pytest.mark.unit
class TestAPIKeyAuthentication:
@pytest.mark.django_db
def test_validate_api_token_authenticates_active_user(self, create_user):
token = APIToken.objects.create(
user=create_user, label="Active Token", token="active-user-token"
)
user, returned_token = APIKeyAuthentication().validate_api_token(token.token)
assert user == create_user
assert returned_token == token.token
@pytest.mark.django_db
def test_validate_api_token_rejects_deactivated_user(self, create_user):
token = APIToken.objects.create(
user=create_user, label="Stale Token", token="deactivated-user-token"
)
# Account is deactivated by an administrator after the token was issued.
create_user.is_active = False
create_user.save()
with pytest.raises(AuthenticationFailed):
APIKeyAuthentication().validate_api_token(token.token)