diff --git a/authentik/events/tasks.py b/authentik/events/tasks.py index 8f6309da7..aa9793bf5 100644 --- a/authentik/events/tasks.py +++ b/authentik/events/tasks.py @@ -30,7 +30,11 @@ def event_notification_handler(event_uuid: str): @CELERY_APP.task() def event_trigger_handler(event_uuid: str, trigger_name: str): """Check if policies attached to NotificationRule match event""" - event: Event = Event.objects.get(event_uuid=event_uuid) + events = Event.objects.filter(event_uuid=event_uuid) + if not events.exists(): + LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid) + return + event: Event = events.first() trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name) if "policy_uuid" in event.context: diff --git a/authentik/flows/tests/test_transfer_docs.py b/authentik/flows/tests/test_transfer_docs.py index debd3a1d9..8fc4ecdb7 100644 --- a/authentik/flows/tests/test_transfer_docs.py +++ b/authentik/flows/tests/test_transfer_docs.py @@ -24,6 +24,6 @@ def pbflow_tester(file_name: str) -> Callable: return tester -for flow_file in glob("website/static/flows/*.pbflow"): +for flow_file in glob("website/static/flows/*.akflow"): method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_") setattr(TestTransferDocs, f"test_flow_{method_name}", pbflow_tester(flow_file)) diff --git a/authentik/policies/forms.py b/authentik/policies/forms.py index 36f7d183f..f6c63b080 100644 --- a/authentik/policies/forms.py +++ b/authentik/policies/forms.py @@ -2,6 +2,7 @@ from django import forms +from authentik.core.models import Group from authentik.lib.widgets import GroupedModelChoiceField from authentik.policies.models import Policy, PolicyBinding, PolicyBindingModel @@ -16,6 +17,9 @@ class PolicyBindingForm(forms.ModelForm): policy = GroupedModelChoiceField( queryset=Policy.objects.all().select_subclasses(), required=False ) + group = forms.ModelChoiceField( + queryset=Group.objects.all().order_by("name"), required=False + ) def __init__(self, *args, **kwargs): # pragma: no cover super().__init__(*args, **kwargs) diff --git a/authentik/policies/group_membership/forms.py b/authentik/policies/group_membership/forms.py index 6fa3e8761..bccc7b6b3 100644 --- a/authentik/policies/group_membership/forms.py +++ b/authentik/policies/group_membership/forms.py @@ -2,6 +2,7 @@ from django import forms +from authentik.core.models import Group from authentik.policies.forms import PolicyForm from authentik.policies.group_membership.models import GroupMembershipPolicy @@ -9,6 +10,8 @@ from authentik.policies.group_membership.models import GroupMembershipPolicy class GroupMembershipPolicyForm(PolicyForm): """GroupMembershipPolicy Form""" + group = forms.ModelChoiceField(queryset=Group.objects.all().order_by("name")) + class Meta: model = GroupMembershipPolicy diff --git a/authentik/providers/oauth2/models.py b/authentik/providers/oauth2/models.py index c3863a343..53a1b78b3 100644 --- a/authentik/providers/oauth2/models.py +++ b/authentik/providers/oauth2/models.py @@ -4,6 +4,7 @@ import binascii import json import time from dataclasses import asdict, dataclass, field +from datetime import datetime from hashlib import sha256 from typing import Any, Optional, Type from urllib.parse import urlparse @@ -480,10 +481,14 @@ class RefreshToken(ExpiringModel, BaseGrantModel): now + timedelta_from_string(self.provider.token_validity).seconds ) # We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time - auth_event = Event.objects.filter( + auth_events = Event.objects.filter( action=EventAction.LOGIN, user=get_user(user) - ).latest("created") - auth_time = int(dateformat.format(auth_event.created, "U")) + ).order_by("-created") + # Fallback in case we can't find any login events + auth_time = datetime.now() + if auth_events.exists(): + auth_time = auth_events.first().created + auth_time = int(dateformat.format(auth_time, "U")) token = IDToken( iss=self.provider.get_issuer(request), diff --git a/website/docs/flow/stages/dummy/dummy.png b/website/docs/flow/stages/dummy/dummy.png deleted file mode 100644 index 7040e66cb..000000000 Binary files a/website/docs/flow/stages/dummy/dummy.png and /dev/null differ diff --git a/website/docs/flow/stages/dummy/index.md b/website/docs/flow/stages/dummy/index.md deleted file mode 100644 index 6513b9515..000000000 --- a/website/docs/flow/stages/dummy/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Dummy stage ---- - -This stage is used for development and has no function. It presents the user with a form which requires a single confirmation. - -![](dummy.png) diff --git a/website/docs/troubleshooting/login.md b/website/docs/troubleshooting/login.md new file mode 100644 index 000000000..07136f0bc --- /dev/null +++ b/website/docs/troubleshooting/login.md @@ -0,0 +1,23 @@ +--- +title: Troubleshooting Login problems +--- + +In case you can't login anymore, perhaps due to an incorrectly configured stage or a failed flow import, you can create a recovery key. + +:::caution +This recovery key will give whoever has the link direct access to your instances. Keep this key safe. +::: + +To create the key, run the following command: + +``` +docker-compose run --rm server create_recovery_key 10 akadmin +``` + +or, for Kubernetes, run + +``` +kubectl exec -it authentik-web- -- ./manage.py create_recovery_key 10 akadmin +``` + +This will output a link, that can be used to instantly gain access to authentik as the user specified above. The link is valid for amount of years specified above, in this case, 10 years. diff --git a/website/sidebars.js b/website/sidebars.js index 2037f2be0..1ecace20e 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -50,7 +50,6 @@ module.exports = { "flow/stages/authenticator_totp/index", "flow/stages/authenticator_validate/index", "flow/stages/captcha/index", - "flow/stages/dummy/index", "flow/stages/email/index", "flow/stages/identification/index", "flow/stages/invitation/index", @@ -150,6 +149,7 @@ module.exports = { items: [ "troubleshooting/access", "troubleshooting/emails", + "troubleshooting/login", ], }, {