diff --git a/authentik/events/middleware.py b/authentik/events/middleware.py index bb130cb02..58be17c06 100644 --- a/authentik/events/middleware.py +++ b/authentik/events/middleware.py @@ -12,6 +12,7 @@ from authentik.core.models import User from authentik.events.models import Event, EventAction, Notification from authentik.events.signals import EventNewThread from authentik.events.utils import model_to_dict +from authentik.lib.utils.errors import exception_to_string class AuditMiddleware: @@ -54,7 +55,14 @@ class AuditMiddleware: # pylint: disable=unused-argument def process_exception(self, request: HttpRequest, exception: Exception): - """Unregister handlers in case of exception""" + """Disconnect handlers in case of exception""" + thread = EventNewThread( + EventAction.SYSTEM_EXCEPTION, + request, + message=exception_to_string(exception), + ) + thread.run() + post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"]) pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"]) diff --git a/authentik/events/models.py b/authentik/events/models.py index 7c35d9731..136bf79d3 100644 --- a/authentik/events/models.py +++ b/authentik/events/models.py @@ -71,6 +71,7 @@ class EventAction(models.TextChoices): SYSTEM_TASK_EXECUTION = "system_task_execution" SYSTEM_TASK_EXCEPTION = "system_task_exception" + SYSTEM_EXCEPTION = "system_exception" CONFIGURATION_ERROR = "configuration_error" diff --git a/authentik/lib/utils/errors.py b/authentik/lib/utils/errors.py new file mode 100644 index 000000000..e2b210571 --- /dev/null +++ b/authentik/lib/utils/errors.py @@ -0,0 +1,10 @@ +"""error utils""" +from traceback import format_tb + +TRACEBACK_HEADER = "Traceback (most recent call last):\n" + + +def exception_to_string(exc: Exception) -> str: + """Convert exception to string stackrace""" + # Either use passed original exception or whatever we have + return TRACEBACK_HEADER + "".join(format_tb(exc.__traceback__)) + str(exc) diff --git a/authentik/policies/process.py b/authentik/policies/process.py index 36a6a3cf7..013de618f 100644 --- a/authentik/policies/process.py +++ b/authentik/policies/process.py @@ -1,7 +1,6 @@ """authentik policy task""" from multiprocessing import get_context from multiprocessing.connection import Connection -from traceback import format_tb from typing import Optional from django.core.cache import cache @@ -11,12 +10,12 @@ from sentry_sdk.tracing import Span from structlog.stdlib import get_logger from authentik.events.models import Event, EventAction +from authentik.lib.utils.errors import exception_to_string from authentik.policies.exceptions import PolicyException from authentik.policies.models import PolicyBinding from authentik.policies.types import PolicyRequest, PolicyResult LOGGER = get_logger() -TRACEBACK_HEADER = "Traceback (most recent call last):\n" FORK_CTX = get_context("fork") PROCESS_CLASS = FORK_CTX.Process @@ -106,11 +105,7 @@ class PolicyProcess(PROCESS_CLASS): except PolicyException as exc: # Either use passed original exception or whatever we have src_exc = exc.src_exc if exc.src_exc else exc - error_string = ( - TRACEBACK_HEADER - + "".join(format_tb(src_exc.__traceback__)) - + str(src_exc) - ) + error_string = exception_to_string(src_exc) # Create policy exception event, only when we're not debugging if not self.request.debug: self.create_event(EventAction.POLICY_EXCEPTION, message=error_string) diff --git a/schema.yml b/schema.yml index 1d38e7bc4..47b00675d 100644 --- a/schema.yml +++ b/schema.yml @@ -19153,6 +19153,7 @@ components: - property_mapping_exception - system_task_execution - system_task_exception + - system_exception - configuration_error - model_created - model_updated diff --git a/web/src/locales/en.po b/web/src/locales/en.po index 51b0aa61d..026cf7bfa 100644 --- a/web/src/locales/en.po +++ b/web/src/locales/en.po @@ -1391,6 +1391,7 @@ msgstr "Event {0}" msgid "Events" msgstr "Events" +#: src/pages/events/EventInfo.ts #: src/pages/events/EventInfo.ts #: src/pages/events/EventInfo.ts msgid "Exception" @@ -2497,6 +2498,10 @@ msgstr "Only send notification once, for example when sending a webhook into a c msgid "Open application" msgstr "Open application" +#: src/pages/events/EventInfo.ts +msgid "Open issue on GitHub..." +msgstr "Open issue on GitHub..." + #: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts msgid "OpenID Configuration Issuer" msgstr "OpenID Configuration Issuer" diff --git a/web/src/locales/pseudo-LOCALE.po b/web/src/locales/pseudo-LOCALE.po index 6df2b473c..cd3732266 100644 --- a/web/src/locales/pseudo-LOCALE.po +++ b/web/src/locales/pseudo-LOCALE.po @@ -1383,6 +1383,7 @@ msgstr "" msgid "Events" msgstr "" +#: #: #: msgid "Exception" @@ -2489,6 +2490,10 @@ msgstr "" msgid "Open application" msgstr "" +#: +msgid "Open issue on GitHub..." +msgstr "" + #: msgid "OpenID Configuration Issuer" msgstr "" diff --git a/web/src/pages/events/EventInfo.ts b/web/src/pages/events/EventInfo.ts index 4b61e04cd..19e7088e2 100644 --- a/web/src/pages/events/EventInfo.ts +++ b/web/src/pages/events/EventInfo.ts @@ -7,11 +7,12 @@ import "../../elements/Expand"; import { PFSize } from "../../elements/Spinner"; import { EventContext, EventWithContext } from "../../api/Events"; import { DEFAULT_CONFIG } from "../../api/Config"; - +import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css"; import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFList from "@patternfly/patternfly/components/List/list.css"; +import { VERSION } from "../../constants"; @customElement("ak-event-info") export class EventInfo extends LitElement { @@ -20,7 +21,7 @@ export class EventInfo extends LitElement { event!: EventWithContext; static get styles(): CSSResult[] { - return [PFBase, PFFlex, PFList, PFDescriptionList, + return [PFBase, PFButton, PFFlex, PFList, PFDescriptionList, css` code { display: block; @@ -137,6 +138,45 @@ export class EventInfo extends LitElement { `; } + buildGitHubIssueUrl(title: string, body: string): string { + // https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/about-automation-for-issues-and-pull-requests-with-query-parameters + const fullBody = ` +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Logs** +
+ Stacktrace from authentik + +\`\`\` +${body} +\`\`\` +
+ + +**Version and Deployment (please complete the following information):** +- authentik version: ${VERSION} +- Deployment: [e.g. docker-compose, helm] + +**Additional context** +Add any other context about the problem here. + `; + return `https://github.com/goauthentik/authentik/issues/new?labels=bug+from_authentik&title=${encodeURIComponent(title)}&body=${encodeURIComponent(fullBody)}`; + } + render(): TemplateResult { if (!this.event) { return html``; @@ -176,6 +216,24 @@ export class EventInfo extends LitElement { return html`

${t`Secret:`}

${this.getModelInfo(this.event.context.secret as EventContext)}`; + case EventMatcherPolicyActionEnum.SystemException: + return html` + + ${t`Open issue on GitHub...`} + +
+
+

${t`Exception`}

+ ${this.event.context.message} +
+
+ ${this.defaultResponse()}`; case EventMatcherPolicyActionEnum.PropertyMappingException: return html`