events: catch unhandled exceptions from request as event, add button to open github issue
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
0e02925a3d
commit
e584fd1344
|
@ -12,6 +12,7 @@ from authentik.core.models import User
|
||||||
from authentik.events.models import Event, EventAction, Notification
|
from authentik.events.models import Event, EventAction, Notification
|
||||||
from authentik.events.signals import EventNewThread
|
from authentik.events.signals import EventNewThread
|
||||||
from authentik.events.utils import model_to_dict
|
from authentik.events.utils import model_to_dict
|
||||||
|
from authentik.lib.utils.errors import exception_to_string
|
||||||
|
|
||||||
|
|
||||||
class AuditMiddleware:
|
class AuditMiddleware:
|
||||||
|
@ -54,7 +55,14 @@ class AuditMiddleware:
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def process_exception(self, request: HttpRequest, exception: Exception):
|
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"])
|
post_save.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
||||||
pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
pre_delete.disconnect(dispatch_uid=LOCAL.authentik["request_id"])
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,7 @@ class EventAction(models.TextChoices):
|
||||||
|
|
||||||
SYSTEM_TASK_EXECUTION = "system_task_execution"
|
SYSTEM_TASK_EXECUTION = "system_task_execution"
|
||||||
SYSTEM_TASK_EXCEPTION = "system_task_exception"
|
SYSTEM_TASK_EXCEPTION = "system_task_exception"
|
||||||
|
SYSTEM_EXCEPTION = "system_exception"
|
||||||
|
|
||||||
CONFIGURATION_ERROR = "configuration_error"
|
CONFIGURATION_ERROR = "configuration_error"
|
||||||
|
|
||||||
|
|
10
authentik/lib/utils/errors.py
Normal file
10
authentik/lib/utils/errors.py
Normal file
|
@ -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)
|
|
@ -1,7 +1,6 @@
|
||||||
"""authentik policy task"""
|
"""authentik policy task"""
|
||||||
from multiprocessing import get_context
|
from multiprocessing import get_context
|
||||||
from multiprocessing.connection import Connection
|
from multiprocessing.connection import Connection
|
||||||
from traceback import format_tb
|
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
@ -11,12 +10,12 @@ from sentry_sdk.tracing import Span
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
from authentik.events.models import Event, EventAction
|
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.exceptions import PolicyException
|
||||||
from authentik.policies.models import PolicyBinding
|
from authentik.policies.models import PolicyBinding
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
TRACEBACK_HEADER = "Traceback (most recent call last):\n"
|
|
||||||
|
|
||||||
FORK_CTX = get_context("fork")
|
FORK_CTX = get_context("fork")
|
||||||
PROCESS_CLASS = FORK_CTX.Process
|
PROCESS_CLASS = FORK_CTX.Process
|
||||||
|
@ -106,11 +105,7 @@ class PolicyProcess(PROCESS_CLASS):
|
||||||
except PolicyException as exc:
|
except PolicyException as exc:
|
||||||
# Either use passed original exception or whatever we have
|
# Either use passed original exception or whatever we have
|
||||||
src_exc = exc.src_exc if exc.src_exc else exc
|
src_exc = exc.src_exc if exc.src_exc else exc
|
||||||
error_string = (
|
error_string = exception_to_string(src_exc)
|
||||||
TRACEBACK_HEADER
|
|
||||||
+ "".join(format_tb(src_exc.__traceback__))
|
|
||||||
+ str(src_exc)
|
|
||||||
)
|
|
||||||
# Create policy exception event, only when we're not debugging
|
# Create policy exception event, only when we're not debugging
|
||||||
if not self.request.debug:
|
if not self.request.debug:
|
||||||
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
|
self.create_event(EventAction.POLICY_EXCEPTION, message=error_string)
|
||||||
|
|
|
@ -19153,6 +19153,7 @@ components:
|
||||||
- property_mapping_exception
|
- property_mapping_exception
|
||||||
- system_task_execution
|
- system_task_execution
|
||||||
- system_task_exception
|
- system_task_exception
|
||||||
|
- system_exception
|
||||||
- configuration_error
|
- configuration_error
|
||||||
- model_created
|
- model_created
|
||||||
- model_updated
|
- model_updated
|
||||||
|
|
|
@ -1391,6 +1391,7 @@ msgstr "Event {0}"
|
||||||
msgid "Events"
|
msgid "Events"
|
||||||
msgstr "Events"
|
msgstr "Events"
|
||||||
|
|
||||||
|
#: src/pages/events/EventInfo.ts
|
||||||
#: src/pages/events/EventInfo.ts
|
#: src/pages/events/EventInfo.ts
|
||||||
#: src/pages/events/EventInfo.ts
|
#: src/pages/events/EventInfo.ts
|
||||||
msgid "Exception"
|
msgid "Exception"
|
||||||
|
@ -2497,6 +2498,10 @@ msgstr "Only send notification once, for example when sending a webhook into a c
|
||||||
msgid "Open application"
|
msgid "Open application"
|
||||||
msgstr "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
|
#: src/pages/providers/oauth2/OAuth2ProviderViewPage.ts
|
||||||
msgid "OpenID Configuration Issuer"
|
msgid "OpenID Configuration Issuer"
|
||||||
msgstr "OpenID Configuration Issuer"
|
msgstr "OpenID Configuration Issuer"
|
||||||
|
|
|
@ -1383,6 +1383,7 @@ msgstr ""
|
||||||
msgid "Events"
|
msgid "Events"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
#:
|
#:
|
||||||
#:
|
#:
|
||||||
msgid "Exception"
|
msgid "Exception"
|
||||||
|
@ -2489,6 +2490,10 @@ msgstr ""
|
||||||
msgid "Open application"
|
msgid "Open application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
|
msgid "Open issue on GitHub..."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
#:
|
#:
|
||||||
msgid "OpenID Configuration Issuer"
|
msgid "OpenID Configuration Issuer"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -7,11 +7,12 @@ import "../../elements/Expand";
|
||||||
import { PFSize } from "../../elements/Spinner";
|
import { PFSize } from "../../elements/Spinner";
|
||||||
import { EventContext, EventWithContext } from "../../api/Events";
|
import { EventContext, EventWithContext } from "../../api/Events";
|
||||||
import { DEFAULT_CONFIG } from "../../api/Config";
|
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 PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
|
||||||
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
|
import PFFlex from "@patternfly/patternfly/layouts/Flex/flex.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
import PFList from "@patternfly/patternfly/components/List/list.css";
|
import PFList from "@patternfly/patternfly/components/List/list.css";
|
||||||
|
import { VERSION } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-event-info")
|
@customElement("ak-event-info")
|
||||||
export class EventInfo extends LitElement {
|
export class EventInfo extends LitElement {
|
||||||
|
@ -20,7 +21,7 @@ export class EventInfo extends LitElement {
|
||||||
event!: EventWithContext;
|
event!: EventWithContext;
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult[] {
|
||||||
return [PFBase, PFFlex, PFList, PFDescriptionList,
|
return [PFBase, PFButton, PFFlex, PFList, PFDescriptionList,
|
||||||
css`
|
css`
|
||||||
code {
|
code {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -137,6 +138,45 @@ export class EventInfo extends LitElement {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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**
|
||||||
|
<details>
|
||||||
|
<summary>Stacktrace from authentik</summary>
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
${body}
|
||||||
|
\`\`\`
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
**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 {
|
render(): TemplateResult {
|
||||||
if (!this.event) {
|
if (!this.event) {
|
||||||
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
|
return html`<ak-spinner size=${PFSize.Medium}></ak-spinner>`;
|
||||||
|
@ -176,6 +216,24 @@ export class EventInfo extends LitElement {
|
||||||
return html`
|
return html`
|
||||||
<h3>${t`Secret:`}</h3>
|
<h3>${t`Secret:`}</h3>
|
||||||
${this.getModelInfo(this.event.context.secret as EventContext)}`;
|
${this.getModelInfo(this.event.context.secret as EventContext)}`;
|
||||||
|
case EventMatcherPolicyActionEnum.SystemException:
|
||||||
|
return html`
|
||||||
|
<a
|
||||||
|
class="pf-c-button pf-m-primary"
|
||||||
|
target="_blank"
|
||||||
|
href=${this.buildGitHubIssueUrl(
|
||||||
|
"",
|
||||||
|
this.event.context.message as string
|
||||||
|
)}>
|
||||||
|
${t`Open issue on GitHub...`}
|
||||||
|
</a>
|
||||||
|
<div class="pf-l-flex">
|
||||||
|
<div class="pf-l-flex__item">
|
||||||
|
<h3>${t`Exception`}</h3>
|
||||||
|
<code>${this.event.context.message}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||||
case EventMatcherPolicyActionEnum.PropertyMappingException:
|
case EventMatcherPolicyActionEnum.PropertyMappingException:
|
||||||
return html`<div class="pf-l-flex">
|
return html`<div class="pf-l-flex">
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
|
|
Reference in a new issue