enterprise: allow deletion/modification of users when in read-only mode (#12289)

* enterprise: allow deletion/modification of users when in read-only mode

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* actually 10.5+

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* Apply suggestions from code review

Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
Signed-off-by: Jens L. <jens@beryju.org>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Tana M Berry <tanamarieberry@yahoo.com>
This commit is contained in:
Jens L.
2024-12-10 13:07:50 +01:00
committed by GitHub
parent 28a23110c2
commit 81ae02e623
3 changed files with 59 additions and 1 deletions

View File

@ -6,6 +6,7 @@ from django.http import HttpRequest, HttpResponse, JsonResponse
from django.urls import resolve from django.urls import resolve
from structlog.stdlib import BoundLogger, get_logger from structlog.stdlib import BoundLogger, get_logger
from authentik.core.api.users import UserViewSet
from authentik.enterprise.api import LicenseViewSet from authentik.enterprise.api import LicenseViewSet
from authentik.enterprise.license import LicenseKey from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import LicenseUsageStatus from authentik.enterprise.models import LicenseUsageStatus
@ -59,6 +60,9 @@ class EnterpriseMiddleware:
# Flow executor is mounted as an API path but explicitly allowed # Flow executor is mounted as an API path but explicitly allowed
if request.resolver_match._func_path == class_to_path(FlowExecutorView): if request.resolver_match._func_path == class_to_path(FlowExecutorView):
return True return True
# Always allow making changes to users, even in case the license has ben exceeded
if request.resolver_match._func_path == class_to_path(UserViewSet):
return True
# Only apply these restrictions to the API # Only apply these restrictions to the API
if "authentik_api" not in request.resolver_match.app_names: if "authentik_api" not in request.resolver_match.app_names:
return True return True

View File

@ -215,3 +215,49 @@ class TestReadOnly(FlowTestCase):
{"detail": "Request denied due to expired/invalid license.", "code": "denied_license"}, {"detail": "Request denied due to expired/invalid license.", "code": "denied_license"},
) )
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
@patch(
"authentik.enterprise.license.LicenseKey.validate",
MagicMock(
return_value=LicenseKey(
aud="",
exp=expiry_valid,
name=generate_id(),
internal_users=100,
external_users=100,
)
),
)
@patch(
"authentik.enterprise.license.LicenseKey.get_internal_user_count",
MagicMock(return_value=1000),
)
@patch(
"authentik.enterprise.license.LicenseKey.get_external_user_count",
MagicMock(return_value=1000),
)
@patch(
"authentik.enterprise.license.LicenseKey.record_usage",
MagicMock(),
)
def test_manage_users(self):
"""Test that managing users is still possible"""
License.objects.create(key=generate_id())
usage = LicenseUsage.objects.create(
internal_user_count=100,
external_user_count=100,
status=LicenseUsageStatus.VALID,
)
usage.record_date = now() - timedelta(weeks=THRESHOLD_READ_ONLY_WEEKS + 1)
usage.save(update_fields=["record_date"])
admin = create_test_admin_user()
self.client.force_login(admin)
# Reading is always allowed
response = self.client.get(reverse("authentik_api:user-list"))
self.assertEqual(response.status_code, 200)
# Writing should also be allowed
response = self.client.patch(reverse("authentik_api:user-detail", kwargs={"pk": admin.pk}))
self.assertEqual(response.status_code, 200)

View File

@ -101,7 +101,15 @@ The following events occur when a license expires or the internal/external user
- After another 2 weeks, users get a warning banner - After another 2 weeks, users get a warning banner
- After another 2 weeks, the authentik Enterprise instance becomes read-only - After another 2 weeks, the authentik Enterprise instance becomes "read-only"
When an authentik instance is in read-only mode, the following actions are still possible:
- Users can authenticate and authorize applications
- Licenses can be modified
- Users can be modified/deleted <span class="badge badge--version">authentik 2024.10.5+</span>
After the violation is corrected (either the user count returns to be within the limits of the license or the license is renewed), authentik will return to the standard read-write mode and the notification will disappear.
### About users and licenses ### About users and licenses