From 1972464a202684e16846c020ad0ae06ad29d3c86 Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Sat, 24 Jul 2021 19:21:35 +0200 Subject: [PATCH] tenants: make event retention configurable on tenant level Signed-off-by: Jens Langhammer --- authentik/events/models.py | 12 ++++++-- authentik/lib/utils/time.py | 4 +-- authentik/tenants/api.py | 1 + .../migrations/0004_tenant_event_retention.py | 24 ++++++++++++++++ authentik/tenants/models.py | 13 +++++++++ authentik/tenants/tests.py | 28 +++++++++++++++++++ schema.yml | 9 ++++++ web/src/locales/en.po | 20 +++++++++++++ web/src/locales/pseudo-LOCALE.po | 20 +++++++++++++ web/src/pages/tenants/TenantForm.ts | 20 +++++++++++++ 10 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 authentik/tenants/migrations/0004_tenant_event_retention.py diff --git a/authentik/events/models.py b/authentik/events/models.py index e52330d46..3c2aede09 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -24,8 +24,10 @@ from authentik.events.geo import GEOIP_READER from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict from authentik.lib.sentry import SentryIgnoredException from authentik.lib.utils.http import get_client_ip +from authentik.lib.utils.time import timedelta_from_string from authentik.policies.models import PolicyBindingModel from authentik.stages.email.utils import TemplateEmailMessage +from authentik.tenants.models import Tenant from authentik.tenants.utils import DEFAULT_TENANT LOGGER = get_logger("authentik.events") @@ -37,7 +39,8 @@ GAUGE_EVENTS = Gauge( def default_event_duration(): - """Default duration an Event is saved""" + """Default duration an Event is saved. + This is used as a fallback when no tenant is available""" return now() + timedelta(days=365) @@ -147,7 +150,12 @@ class Event(ExpiringModel): "method": request.method, } if hasattr(request, "tenant"): - self.tenant = sanitize_dict(model_to_dict(request.tenant)) + tenant: Tenant = request.tenant + # Because self.created only gets set on save, we can't use it's value here + # hence we set self.created to now and then use it + self.created = now() + self.expires = self.created + timedelta_from_string(tenant.event_retention) + self.tenant = sanitize_dict(model_to_dict(tenant)) if hasattr(request, "user"): original_user = None if hasattr(request, "session"): diff --git a/authentik/lib/utils/time.py b/authentik/lib/utils/time.py index a4109f5af..b13852cd4 100644 --- a/authentik/lib/utils/time.py +++ b/authentik/lib/utils/time.py @@ -5,12 +5,12 @@ from django.core.exceptions import ValidationError from django.utils.translation import gettext_lazy as _ ALLOWED_KEYS = ( - "days", - "seconds", "microseconds", "milliseconds", + "seconds", "minutes", "hours", + "days", "weeks", ) diff --git a/authentik/tenants/api.py b/authentik/tenants/api.py index 673373f8d..4e7f810e9 100644 --- a/authentik/tenants/api.py +++ b/authentik/tenants/api.py @@ -38,6 +38,7 @@ class TenantSerializer(ModelSerializer): "flow_invalidation", "flow_recovery", "flow_unenrollment", + "event_retention", ] diff --git a/authentik/tenants/migrations/0004_tenant_event_retention.py b/authentik/tenants/migrations/0004_tenant_event_retention.py new file mode 100644 index 000000000..751528e7f --- /dev/null +++ b/authentik/tenants/migrations/0004_tenant_event_retention.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.5 on 2021-07-24 17:06 + +from django.db import migrations, models + +import authentik.lib.utils.time + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_tenants", "0003_tenant_branding_favicon"), + ] + + operations = [ + migrations.AddField( + model_name="tenant", + name="event_retention", + field=models.TextField( + default="days=365", + help_text="Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).", + validators=[authentik.lib.utils.time.timedelta_string_validator], + ), + ), + ] diff --git a/authentik/tenants/models.py b/authentik/tenants/models.py index 1c1da4e5a..7be1c2f84 100644 --- a/authentik/tenants/models.py +++ b/authentik/tenants/models.py @@ -5,6 +5,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from authentik.flows.models import Flow +from authentik.lib.utils.time import timedelta_string_validator class Tenant(models.Model): @@ -22,6 +23,7 @@ class Tenant(models.Model): ) branding_title = models.TextField(default="authentik") + branding_logo = models.TextField( default="/static/dist/assets/icons/icon_left_brand.svg" ) @@ -40,6 +42,17 @@ class Tenant(models.Model): Flow, null=True, on_delete=models.SET_NULL, related_name="tenant_unenrollment" ) + event_retention = models.TextField( + default="days=365", + validators=[timedelta_string_validator], + help_text=_( + ( + "Events will be deleted after this duration." + "(Format: weeks=3;days=2;hours=3,seconds=2)." + ) + ), + ) + def __str__(self) -> str: if self.default: return "Default tenant" diff --git a/authentik/tenants/tests.py b/authentik/tenants/tests.py index 4eab392c4..383006e37 100644 --- a/authentik/tenants/tests.py +++ b/authentik/tenants/tests.py @@ -1,9 +1,12 @@ """Test tenants""" from django.test import TestCase +from django.test.client import RequestFactory from django.urls import reverse from django.utils.encoding import force_str +from authentik.events.models import Event, EventAction from authentik.lib.config import CONFIG +from authentik.lib.utils.time import timedelta_from_string from authentik.tenants.models import Tenant @@ -57,3 +60,28 @@ class TestTenants(TestCase): "ui_footer_links": CONFIG.y("footer_links"), }, ) + + def test_event_retention(self): + """Test tenant's event retention""" + tenant = Tenant.objects.create( + domain="foo", + default=True, + branding_title="custom", + event_retention="weeks=3", + ) + factory = RequestFactory() + request = factory.get("/") + request.tenant = tenant + event = Event.new( + action=EventAction.SYSTEM_EXCEPTION, message="test" + ).from_http(request) + self.assertEqual( + event.expires.day, (event.created + timedelta_from_string("weeks=3")).day + ) + self.assertEqual( + event.expires.month, + (event.created + timedelta_from_string("weeks=3")).month, + ) + self.assertEqual( + event.expires.year, (event.created + timedelta_from_string("weeks=3")).year + ) diff --git a/schema.yml b/schema.yml index 188405b40..76c538679 100644 --- a/schema.yml +++ b/schema.yml @@ -25902,6 +25902,9 @@ components: type: string format: uuid nullable: true + event_retention: + type: string + description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' PatchedTokenRequest: type: object description: Token Serializer @@ -27977,6 +27980,9 @@ components: type: string format: uuid nullable: true + event_retention: + type: string + description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' required: - domain - tenant_uuid @@ -28012,6 +28018,9 @@ components: type: string format: uuid nullable: true + event_retention: + type: string + description: 'Events will be deleted after this duration.(Format: weeks=3;days=2;hours=3,seconds=2).' required: - domain Token: diff --git a/web/src/locales/en.po b/web/src/locales/en.po index b1120c1df..b36d768d4 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -1230,6 +1230,10 @@ msgstr "Duo activation" msgid "Duo push-notifications" msgstr "Duo push-notifications" +#: src/pages/tenants/TenantForm.ts +msgid "Duration after which events will be deleted from the database." +msgstr "Duration after which events will be deleted from the database." + #: src/pages/providers/oauth2/OAuth2ProviderForm.ts msgid "Each provider has a different issuer, based on the application slug." msgstr "Each provider has a different issuer, based on the application slug." @@ -1408,6 +1412,10 @@ msgstr "Event Log" msgid "Event info" msgstr "Event info" +#: src/pages/tenants/TenantForm.ts +msgid "Event retention" +msgstr "Event retention" + #: src/pages/events/EventInfoPage.ts msgid "Event {0}" msgstr "Event {0}" @@ -1666,6 +1674,10 @@ msgstr "Forgot username or password?" msgid "Form didn't return a promise for submitting" msgstr "Form didn't return a promise for submitting" +#: src/pages/tenants/TenantForm.ts +msgid "Format: \"weeks=3;days=2;hours=3,seconds=2\"." +msgstr "Format: \"weeks=3;days=2;hours=3,seconds=2\"." + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Forward auth (domain level)" msgstr "Forward auth (domain level)" @@ -2643,6 +2655,10 @@ msgstr "Optionally set this to your parent domain, if you want authentication an msgid "Order" msgstr "Order" +#: src/pages/tenants/TenantForm.ts +msgid "Other global settings" +msgstr "Other global settings" + #: src/pages/admin-overview/charts/OutpostStatusChart.ts msgid "Outdated outposts" msgstr "Outdated outposts" @@ -4659,6 +4675,10 @@ msgstr "When selected, incoming assertion's Signatures will be validated against msgid "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged." msgstr "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged." +#: src/pages/tenants/TenantForm.ts +msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." +msgstr "When using an external logging solution for archiving, this can be set to \"minutes=5\"." + #: src/flows/FlowExecutor.ts msgid "Whoops!" msgstr "Whoops!" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 84b3a12f8..91533286f 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -1222,6 +1222,10 @@ msgstr "" msgid "Duo push-notifications" msgstr "" +#: src/pages/tenants/TenantForm.ts +msgid "Duration after which events will be deleted from the database." +msgstr "" + #: src/pages/providers/oauth2/OAuth2ProviderForm.ts msgid "Each provider has a different issuer, based on the application slug." msgstr "" @@ -1400,6 +1404,10 @@ msgstr "" msgid "Event info" msgstr "" +#: src/pages/tenants/TenantForm.ts +msgid "Event retention" +msgstr "" + #: src/pages/events/EventInfoPage.ts msgid "Event {0}" msgstr "" @@ -1658,6 +1666,10 @@ msgstr "" msgid "Form didn't return a promise for submitting" msgstr "" +#: src/pages/tenants/TenantForm.ts +msgid "Format: \"weeks=3;days=2;hours=3,seconds=2\"." +msgstr "" + #: src/pages/providers/proxy/ProxyProviderForm.ts msgid "Forward auth (domain level)" msgstr "" @@ -2635,6 +2647,10 @@ msgstr "" msgid "Order" msgstr "" +#: src/pages/tenants/TenantForm.ts +msgid "Other global settings" +msgstr "" + #: src/pages/admin-overview/charts/OutpostStatusChart.ts msgid "Outdated outposts" msgstr "" @@ -4644,6 +4660,10 @@ msgstr "" msgid "When this option is enabled, all executions of this policy will be logged. By default, only execution errors are logged." msgstr "" +#: src/pages/tenants/TenantForm.ts +msgid "When using an external logging solution for archiving, this can be set to \"minutes=5\"." +msgstr "" + #: src/flows/FlowExecutor.ts msgid "Whoops!" msgstr "" diff --git a/web/src/pages/tenants/TenantForm.ts b/web/src/pages/tenants/TenantForm.ts index 200c3619e..074119db0 100644 --- a/web/src/pages/tenants/TenantForm.ts +++ b/web/src/pages/tenants/TenantForm.ts @@ -162,6 +162,26 @@ export class TenantForm extends ModelForm { + + + ${t`Other global settings`} + +
+ + +

+ ${t`Duration after which events will be deleted from the database.`} +

+

+ ${t`When using an external logging solution for archiving, this can be set to "minutes=5".`} +

+

${t`Format: "weeks=3;days=2;hours=3,seconds=2".`}

+
+
+
`; }