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
4 changes: 4 additions & 0 deletions authentik/enterprise/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.urls import resolve
from structlog.stdlib import BoundLogger, get_logger

from authentik.core.api.users import UserViewSet
from authentik.enterprise.api import LicenseViewSet
from authentik.enterprise.license import LicenseKey
from authentik.enterprise.models import LicenseUsageStatus
Expand Down Expand Up @@ -59,6 +60,9 @@ def is_request_always_allowed(self, request: HttpRequest):
# Flow executor is mounted as an API path but explicitly allowed
if request.resolver_match._func_path == class_to_path(FlowExecutorView):
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
if "authentik_api" not in request.resolver_match.app_names:
return True
Expand Down
46 changes: 46 additions & 0 deletions authentik/enterprise/tests/test_read_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,49 @@ def test_manage_flows(self):
{"detail": "Request denied due to expired/invalid license.", "code": "denied_license"},
)
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)
10 changes: 9 additions & 1 deletion website/docs/enterprise/manage-enterprise.md
Original file line number Diff line number Diff line change
Expand Up @@ -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, the authentik Enterprise instance becomes “read-only”
- After another 2 weeks, the authentik Enterprise instance becomes "read-only"
Copy link
Copy Markdown
Contributor

@tanberry tanberry Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- After another 2 weeks, the authentik Enterprise instance becomes "read-only"
- After another 2 weeks (six weeks after the initial violation), 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

Expand Down