events: add tenant to event
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
e584fd1344
commit
74e578c2bf
|
@ -36,6 +36,7 @@ class EventSerializer(ModelSerializer):
|
||||||
"client_ip",
|
"client_ip",
|
||||||
"created",
|
"created",
|
||||||
"expires",
|
"expires",
|
||||||
|
"tenant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,6 +77,11 @@ class EventsFilter(django_filters.FilterSet):
|
||||||
field_name="action",
|
field_name="action",
|
||||||
lookup_expr="icontains",
|
lookup_expr="icontains",
|
||||||
)
|
)
|
||||||
|
tenant_name = django_filters.CharFilter(
|
||||||
|
field_name="tenant",
|
||||||
|
lookup_expr="name",
|
||||||
|
label="Tenant name",
|
||||||
|
)
|
||||||
|
|
||||||
# pylint: disable=unused-argument
|
# pylint: disable=unused-argument
|
||||||
def filter_context_model_pk(self, queryset, name, value):
|
def filter_context_model_pk(self, queryset, name, value):
|
||||||
|
|
|
@ -40,9 +40,9 @@ class GeoIPReader:
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
reader = Reader(path)
|
reader = Reader(path)
|
||||||
LOGGER.info("Loaded GeoIP database")
|
|
||||||
self.__reader = reader
|
self.__reader = reader
|
||||||
self.__last_mtime = stat(path).st_mtime
|
self.__last_mtime = stat(path).st_mtime
|
||||||
|
LOGGER.info("Loaded GeoIP database", last_write=self.__last_mtime)
|
||||||
except OSError as exc:
|
except OSError as exc:
|
||||||
LOGGER.warning("Failed to load GeoIP database", exc=exc)
|
LOGGER.warning("Failed to load GeoIP database", exc=exc)
|
||||||
|
|
||||||
|
|
55
authentik/events/migrations/0016_add_tenant.py
Normal file
55
authentik/events/migrations/0016_add_tenant.py
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-06-14 15:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
import authentik.events.models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_events", "0015_alter_event_action"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="event",
|
||||||
|
name="tenant",
|
||||||
|
field=models.JSONField(
|
||||||
|
blank=True, default=authentik.events.models.default_tenant
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="event",
|
||||||
|
name="action",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("login", "Login"),
|
||||||
|
("login_failed", "Login Failed"),
|
||||||
|
("logout", "Logout"),
|
||||||
|
("user_write", "User Write"),
|
||||||
|
("suspicious_request", "Suspicious Request"),
|
||||||
|
("password_set", "Password Set"),
|
||||||
|
("secret_view", "Secret View"),
|
||||||
|
("invitation_used", "Invite Used"),
|
||||||
|
("authorize_application", "Authorize Application"),
|
||||||
|
("source_linked", "Source Linked"),
|
||||||
|
("impersonation_started", "Impersonation Started"),
|
||||||
|
("impersonation_ended", "Impersonation Ended"),
|
||||||
|
("policy_execution", "Policy Execution"),
|
||||||
|
("policy_exception", "Policy Exception"),
|
||||||
|
("property_mapping_exception", "Property Mapping Exception"),
|
||||||
|
("system_task_execution", "System Task Execution"),
|
||||||
|
("system_task_exception", "System Task Exception"),
|
||||||
|
("system_exception", "System Exception"),
|
||||||
|
("configuration_error", "Configuration Error"),
|
||||||
|
("model_created", "Model Created"),
|
||||||
|
("model_updated", "Model Updated"),
|
||||||
|
("model_deleted", "Model Deleted"),
|
||||||
|
("email_sent", "Email Sent"),
|
||||||
|
("update_available", "Update Available"),
|
||||||
|
("custom_", "Custom Prefix"),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -21,11 +21,12 @@ from authentik.core.middleware import (
|
||||||
)
|
)
|
||||||
from authentik.core.models import ExpiringModel, Group, User
|
from authentik.core.models import ExpiringModel, Group, User
|
||||||
from authentik.events.geo import GEOIP_READER
|
from authentik.events.geo import GEOIP_READER
|
||||||
from authentik.events.utils import cleanse_dict, get_user, sanitize_dict
|
from authentik.events.utils import cleanse_dict, get_user, model_to_dict, sanitize_dict
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
from authentik.lib.utils.http import get_client_ip
|
from authentik.lib.utils.http import get_client_ip
|
||||||
from authentik.policies.models import PolicyBindingModel
|
from authentik.policies.models import PolicyBindingModel
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
|
from authentik.tenants.utils import DEFAULT_TENANT
|
||||||
|
|
||||||
LOGGER = get_logger("authentik.events")
|
LOGGER = get_logger("authentik.events")
|
||||||
GAUGE_EVENTS = Gauge(
|
GAUGE_EVENTS = Gauge(
|
||||||
|
@ -40,6 +41,11 @@ def default_event_duration():
|
||||||
return now() + timedelta(days=365)
|
return now() + timedelta(days=365)
|
||||||
|
|
||||||
|
|
||||||
|
def default_tenant():
|
||||||
|
"""Get a default value for tenant"""
|
||||||
|
return sanitize_dict(model_to_dict(DEFAULT_TENANT))
|
||||||
|
|
||||||
|
|
||||||
class NotificationTransportError(SentryIgnoredException):
|
class NotificationTransportError(SentryIgnoredException):
|
||||||
"""Error raised when a notification fails to be delivered"""
|
"""Error raised when a notification fails to be delivered"""
|
||||||
|
|
||||||
|
@ -95,6 +101,7 @@ class Event(ExpiringModel):
|
||||||
context = models.JSONField(default=dict, blank=True)
|
context = models.JSONField(default=dict, blank=True)
|
||||||
client_ip = models.GenericIPAddressField(null=True)
|
client_ip = models.GenericIPAddressField(null=True)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
|
tenant = models.JSONField(default=default_tenant, blank=True)
|
||||||
|
|
||||||
# Shadow the expires attribute from ExpiringModel to override the default duration
|
# Shadow the expires attribute from ExpiringModel to override the default duration
|
||||||
expires = models.DateTimeField(default=default_event_duration)
|
expires = models.DateTimeField(default=default_event_duration)
|
||||||
|
@ -133,6 +140,13 @@ class Event(ExpiringModel):
|
||||||
"""Add data from a Django-HttpRequest, allowing the creation of
|
"""Add data from a Django-HttpRequest, allowing the creation of
|
||||||
Events independently from requests.
|
Events independently from requests.
|
||||||
`user` arguments optionally overrides user from requests."""
|
`user` arguments optionally overrides user from requests."""
|
||||||
|
if request:
|
||||||
|
self.context["http_request"] = {
|
||||||
|
"path": request.get_full_path(),
|
||||||
|
"method": request.method,
|
||||||
|
}
|
||||||
|
if hasattr(request, "tenant"):
|
||||||
|
self.tenant = sanitize_dict(model_to_dict(request.tenant))
|
||||||
if hasattr(request, "user"):
|
if hasattr(request, "user"):
|
||||||
original_user = None
|
original_user = None
|
||||||
if hasattr(request, "session"):
|
if hasattr(request, "session"):
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-06-14 15:32
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_policies_event_matcher", "0016_alter_eventmatcherpolicy_action"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="eventmatcherpolicy",
|
||||||
|
name="action",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
choices=[
|
||||||
|
("login", "Login"),
|
||||||
|
("login_failed", "Login Failed"),
|
||||||
|
("logout", "Logout"),
|
||||||
|
("user_write", "User Write"),
|
||||||
|
("suspicious_request", "Suspicious Request"),
|
||||||
|
("password_set", "Password Set"),
|
||||||
|
("secret_view", "Secret View"),
|
||||||
|
("invitation_used", "Invite Used"),
|
||||||
|
("authorize_application", "Authorize Application"),
|
||||||
|
("source_linked", "Source Linked"),
|
||||||
|
("impersonation_started", "Impersonation Started"),
|
||||||
|
("impersonation_ended", "Impersonation Ended"),
|
||||||
|
("policy_execution", "Policy Execution"),
|
||||||
|
("policy_exception", "Policy Exception"),
|
||||||
|
("property_mapping_exception", "Property Mapping Exception"),
|
||||||
|
("system_task_execution", "System Task Execution"),
|
||||||
|
("system_task_exception", "System Task Exception"),
|
||||||
|
("system_exception", "System Exception"),
|
||||||
|
("configuration_error", "Configuration Error"),
|
||||||
|
("model_created", "Model Created"),
|
||||||
|
("model_updated", "Model Updated"),
|
||||||
|
("model_deleted", "Model Deleted"),
|
||||||
|
("email_sent", "Email Sent"),
|
||||||
|
("update_available", "Update Available"),
|
||||||
|
("custom_", "Custom Prefix"),
|
||||||
|
],
|
||||||
|
help_text="Match created events with this action type. When left empty, all action types will be matched.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -3,11 +3,13 @@ from django.core.cache import cache
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
from structlog import get_logger
|
||||||
|
|
||||||
from authentik.lib.utils.http import get_client_ip
|
from authentik.lib.utils.http import get_client_ip
|
||||||
from authentik.policies.models import Policy
|
from authentik.policies.models import Policy
|
||||||
from authentik.policies.types import PolicyRequest, PolicyResult
|
from authentik.policies.types import PolicyRequest, PolicyResult
|
||||||
|
|
||||||
|
LOGGER = get_logger()
|
||||||
CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_"
|
CACHE_KEY_IP_PREFIX = "authentik_reputation_ip_"
|
||||||
CACHE_KEY_USER_PREFIX = "authentik_reputation_user_"
|
CACHE_KEY_USER_PREFIX = "authentik_reputation_user_"
|
||||||
|
|
||||||
|
@ -34,9 +36,13 @@ class ReputationPolicy(Policy):
|
||||||
passing = True
|
passing = True
|
||||||
if self.check_ip:
|
if self.check_ip:
|
||||||
score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
|
score = cache.get_or_set(CACHE_KEY_IP_PREFIX + remote_ip, 0)
|
||||||
|
LOGGER.debug("Score for IP", ip=remote_ip, score=score)
|
||||||
passing = passing and score <= self.threshold
|
passing = passing and score <= self.threshold
|
||||||
if self.check_username:
|
if self.check_username:
|
||||||
score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
|
score = cache.get_or_set(CACHE_KEY_USER_PREFIX + request.user.username, 0)
|
||||||
|
LOGGER.debug(
|
||||||
|
"Score for Username", username=request.user.username, score=score
|
||||||
|
)
|
||||||
passing = passing and score <= self.threshold
|
passing = passing and score <= self.threshold
|
||||||
return PolicyResult(passing)
|
return PolicyResult(passing)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
"""test reputation signals and policy"""
|
"""test reputation signals and policy"""
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.policies.reputation.models import (
|
from authentik.policies.reputation.models import (
|
||||||
|
@ -19,7 +19,9 @@ class TestReputationPolicy(TestCase):
|
||||||
"""test reputation signals and policy"""
|
"""test reputation signals and policy"""
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.test_ip = "255.255.255.255"
|
self.request_factory = RequestFactory()
|
||||||
|
self.request = self.request_factory.get("/")
|
||||||
|
self.test_ip = "127.0.0.1"
|
||||||
self.test_username = "test"
|
self.test_username = "test"
|
||||||
cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip)
|
cache.delete(CACHE_KEY_IP_PREFIX + self.test_ip)
|
||||||
cache.delete(CACHE_KEY_USER_PREFIX + self.test_username)
|
cache.delete(CACHE_KEY_USER_PREFIX + self.test_username)
|
||||||
|
@ -29,7 +31,9 @@ class TestReputationPolicy(TestCase):
|
||||||
def test_ip_reputation(self):
|
def test_ip_reputation(self):
|
||||||
"""test IP reputation"""
|
"""test IP reputation"""
|
||||||
# Trigger negative reputation
|
# Trigger negative reputation
|
||||||
authenticate(None, username=self.test_username, password=self.test_username)
|
authenticate(
|
||||||
|
self.request, username=self.test_username, password=self.test_username
|
||||||
|
)
|
||||||
# Test value in cache
|
# Test value in cache
|
||||||
self.assertEqual(cache.get(CACHE_KEY_IP_PREFIX + self.test_ip), -1)
|
self.assertEqual(cache.get(CACHE_KEY_IP_PREFIX + self.test_ip), -1)
|
||||||
# Save cache and check db values
|
# Save cache and check db values
|
||||||
|
@ -39,7 +43,9 @@ class TestReputationPolicy(TestCase):
|
||||||
def test_user_reputation(self):
|
def test_user_reputation(self):
|
||||||
"""test User reputation"""
|
"""test User reputation"""
|
||||||
# Trigger negative reputation
|
# Trigger negative reputation
|
||||||
authenticate(None, username=self.test_username, password=self.test_username)
|
authenticate(
|
||||||
|
self.request, username=self.test_username, password=self.test_username
|
||||||
|
)
|
||||||
# Test value in cache
|
# Test value in cache
|
||||||
self.assertEqual(cache.get(CACHE_KEY_USER_PREFIX + self.test_username), -1)
|
self.assertEqual(cache.get(CACHE_KEY_USER_PREFIX + self.test_username), -1)
|
||||||
# Save cache and check db values
|
# Save cache and check db values
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
# Generated by Django 3.2.4 on 2021-06-14 15:32
|
||||||
|
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_stages_identification", "0010_identificationstage_password_stage"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="identificationstage",
|
||||||
|
name="user_fields",
|
||||||
|
field=django.contrib.postgres.fields.ArrayField(
|
||||||
|
base_field=models.CharField(
|
||||||
|
choices=[
|
||||||
|
("email", "E Mail"),
|
||||||
|
("username", "Username"),
|
||||||
|
("upn", "Upn"),
|
||||||
|
],
|
||||||
|
max_length=100,
|
||||||
|
),
|
||||||
|
blank=True,
|
||||||
|
help_text="Fields of the user object to match against. (Hold shift to select multiple options)",
|
||||||
|
size=None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -9,6 +9,7 @@ from authentik.lib.config import CONFIG
|
||||||
from authentik.tenants.models import Tenant
|
from authentik.tenants.models import Tenant
|
||||||
|
|
||||||
_q_default = Q(default=True)
|
_q_default = Q(default=True)
|
||||||
|
DEFAULT_TENANT = Tenant(domain="fallback")
|
||||||
|
|
||||||
|
|
||||||
def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
||||||
|
@ -17,13 +18,13 @@ def get_tenant_for_request(request: HttpRequest) -> Tenant:
|
||||||
Q(domain__iendswith=request.get_host()) | _q_default
|
Q(domain__iendswith=request.get_host()) | _q_default
|
||||||
)
|
)
|
||||||
if not db_tenants.exists():
|
if not db_tenants.exists():
|
||||||
return Tenant(domain="fallback")
|
return DEFAULT_TENANT
|
||||||
return db_tenants.first()
|
return db_tenants.first()
|
||||||
|
|
||||||
|
|
||||||
def context_processor(request: HttpRequest) -> dict[str, Any]:
|
def context_processor(request: HttpRequest) -> dict[str, Any]:
|
||||||
"""Context Processor that injects tenant object into every template"""
|
"""Context Processor that injects tenant object into every template"""
|
||||||
tenant = getattr(request, "tenant", Tenant(domain="fallback"))
|
tenant = getattr(request, "tenant", DEFAULT_TENANT)
|
||||||
return {
|
return {
|
||||||
"tenant": tenant,
|
"tenant": tenant,
|
||||||
"ak_version": __version__,
|
"ak_version": __version__,
|
||||||
|
|
16
schema.yml
16
schema.yml
|
@ -3418,6 +3418,11 @@ paths:
|
||||||
description: A search term.
|
description: A search term.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: tenant_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Tenant name
|
||||||
- in: query
|
- in: query
|
||||||
name: username
|
name: username
|
||||||
schema:
|
schema:
|
||||||
|
@ -3534,6 +3539,11 @@ paths:
|
||||||
description: A search term.
|
description: A search term.
|
||||||
schema:
|
schema:
|
||||||
type: string
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: tenant_name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
description: Tenant name
|
||||||
- in: query
|
- in: query
|
||||||
name: top_n
|
name: top_n
|
||||||
schema:
|
schema:
|
||||||
|
@ -19081,6 +19091,9 @@ components:
|
||||||
expires:
|
expires:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
tenant:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
required:
|
required:
|
||||||
- action
|
- action
|
||||||
- app
|
- app
|
||||||
|
@ -19207,6 +19220,9 @@ components:
|
||||||
expires:
|
expires:
|
||||||
type: string
|
type: string
|
||||||
format: date-time
|
format: date-time
|
||||||
|
tenant:
|
||||||
|
type: object
|
||||||
|
additionalProperties: {}
|
||||||
required:
|
required:
|
||||||
- action
|
- action
|
||||||
- app
|
- app
|
||||||
|
|
|
@ -8,10 +8,22 @@ export interface EventUser {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventContext {
|
export interface EventContext {
|
||||||
[key: string]: EventContext | string | number | string[];
|
[key: string]: EventContext | EventModel | string | number | string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventWithContext extends Event {
|
export interface EventWithContext extends Event {
|
||||||
user: EventUser;
|
user: EventUser;
|
||||||
context: EventContext;
|
context: EventContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EventModel {
|
||||||
|
pk: string;
|
||||||
|
name: string;
|
||||||
|
app: string;
|
||||||
|
model_name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EventRequest {
|
||||||
|
path: string;
|
||||||
|
method: string;
|
||||||
|
}
|
||||||
|
|
|
@ -3768,6 +3768,7 @@ msgstr "Task finished with warnings"
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr "Template"
|
msgstr "Template"
|
||||||
|
|
||||||
|
#: src/pages/events/EventListPage.ts
|
||||||
#: src/pages/tenants/TenantListPage.ts
|
#: src/pages/tenants/TenantListPage.ts
|
||||||
msgid "Tenant"
|
msgid "Tenant"
|
||||||
msgstr "Tenant"
|
msgstr "Tenant"
|
||||||
|
|
|
@ -3760,6 +3760,7 @@ msgstr ""
|
||||||
msgid "Template"
|
msgid "Template"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#:
|
||||||
#:
|
#:
|
||||||
msgid "Tenant"
|
msgid "Tenant"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { EventMatcherPolicyActionEnum, FlowsApi } from "authentik-api";
|
||||||
import "../../elements/Spinner";
|
import "../../elements/Spinner";
|
||||||
import "../../elements/Expand";
|
import "../../elements/Expand";
|
||||||
import { PFSize } from "../../elements/Spinner";
|
import { PFSize } from "../../elements/Spinner";
|
||||||
import { EventContext, EventWithContext } from "../../api/Events";
|
import { EventContext, EventModel, 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 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";
|
||||||
|
@ -41,7 +41,7 @@ export class EventInfo extends LitElement {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
getModelInfo(context: EventContext): TemplateResult {
|
getModelInfo(context: EventModel): TemplateResult {
|
||||||
if (context === null) {
|
if (context === null) {
|
||||||
return html`<span>-</span>`;
|
return html`<span>-</span>`;
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ export class EventInfo extends LitElement {
|
||||||
<span class="pf-c-description-list__text">${t`UID`}</span>
|
<span class="pf-c-description-list__text">${t`UID`}</span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">${context.pk as string}</div>
|
<div class="pf-c-description-list__text">${context.pk}</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-description-list__group">
|
<div class="pf-c-description-list__group">
|
||||||
|
@ -59,7 +59,7 @@ export class EventInfo extends LitElement {
|
||||||
<span class="pf-c-description-list__text">${t`Name`}</span>
|
<span class="pf-c-description-list__text">${t`Name`}</span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">${context.name as string}</div>
|
<div class="pf-c-description-list__text">${context.name}</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-description-list__group">
|
<div class="pf-c-description-list__group">
|
||||||
|
@ -67,7 +67,7 @@ export class EventInfo extends LitElement {
|
||||||
<span class="pf-c-description-list__text">${t`App`}</span>
|
<span class="pf-c-description-list__text">${t`App`}</span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">${context.app as string}</div>
|
<div class="pf-c-description-list__text">${context.app}</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-description-list__group">
|
<div class="pf-c-description-list__group">
|
||||||
|
@ -75,7 +75,7 @@ export class EventInfo extends LitElement {
|
||||||
<span class="pf-c-description-list__text">${t`Model Name`}</span>
|
<span class="pf-c-description-list__text">${t`Model Name`}</span>
|
||||||
</dt>
|
</dt>
|
||||||
<dd class="pf-c-description-list__description">
|
<dd class="pf-c-description-list__description">
|
||||||
<div class="pf-c-description-list__text">${context.model_name as string}</div>
|
<div class="pf-c-description-list__text">${context.model_name}</div>
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
</dl>`;
|
</dl>`;
|
||||||
|
@ -138,7 +138,12 @@ export class EventInfo extends LitElement {
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildGitHubIssueUrl(title: string, body: string): string {
|
buildGitHubIssueUrl(context: EventContext): string {
|
||||||
|
const httpRequest = this.event.context.http_request as EventContext;
|
||||||
|
let title = "";
|
||||||
|
if (httpRequest) {
|
||||||
|
title = `${httpRequest?.method} ${httpRequest?.path}`;
|
||||||
|
}
|
||||||
// https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-issues/about-automation-for-issues-and-pull-requests-with-query-parameters
|
// 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 = `
|
const fullBody = `
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
|
@ -162,7 +167,7 @@ If applicable, add screenshots to help explain your problem.
|
||||||
<summary>Stacktrace from authentik</summary>
|
<summary>Stacktrace from authentik</summary>
|
||||||
|
|
||||||
\`\`\`
|
\`\`\`
|
||||||
${body}
|
${context.message as string}
|
||||||
\`\`\`
|
\`\`\`
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -174,7 +179,9 @@ ${body}
|
||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context about the problem here.
|
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)}`;
|
return `https://github.com/goauthentik/authentik/issues/
|
||||||
|
new?labels=bug+from_authentik&title=${encodeURIComponent(title)}
|
||||||
|
&body=${encodeURIComponent(fullBody)}`.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): TemplateResult {
|
render(): TemplateResult {
|
||||||
|
@ -187,13 +194,13 @@ Add any other context about the problem here.
|
||||||
case EventMatcherPolicyActionEnum.ModelDeleted:
|
case EventMatcherPolicyActionEnum.ModelDeleted:
|
||||||
return html`
|
return html`
|
||||||
<h3>${t`Affected model:`}</h3>
|
<h3>${t`Affected model:`}</h3>
|
||||||
${this.getModelInfo(this.event.context?.model as EventContext)}
|
${this.getModelInfo(this.event.context?.model as EventModel)}
|
||||||
`;
|
`;
|
||||||
case EventMatcherPolicyActionEnum.AuthorizeApplication:
|
case EventMatcherPolicyActionEnum.AuthorizeApplication:
|
||||||
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">
|
||||||
<h3>${t`Authorized application:`}</h3>
|
<h3>${t`Authorized application:`}</h3>
|
||||||
${this.getModelInfo(this.event.context.authorized_application as EventContext)}
|
${this.getModelInfo(this.event.context.authorized_application as EventModel)}
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Using flow`}</h3>
|
<h3>${t`Using flow`}</h3>
|
||||||
|
@ -215,15 +222,14 @@ Add any other context about the problem here.
|
||||||
case EventMatcherPolicyActionEnum.SecretView:
|
case EventMatcherPolicyActionEnum.SecretView:
|
||||||
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 EventModel)}`;
|
||||||
case EventMatcherPolicyActionEnum.SystemException:
|
case EventMatcherPolicyActionEnum.SystemException:
|
||||||
return html`
|
return html`
|
||||||
<a
|
<a
|
||||||
class="pf-c-button pf-m-primary"
|
class="pf-c-button pf-m-primary"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href=${this.buildGitHubIssueUrl(
|
href=${this.buildGitHubIssueUrl(
|
||||||
"",
|
this.event.context
|
||||||
this.event.context.message as string
|
|
||||||
)}>
|
)}>
|
||||||
${t`Open issue on GitHub...`}
|
${t`Open issue on GitHub...`}
|
||||||
</a>
|
</a>
|
||||||
|
@ -250,12 +256,12 @@ Add any other context about the problem here.
|
||||||
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">
|
||||||
<h3>${t`Binding`}</h3>
|
<h3>${t`Binding`}</h3>
|
||||||
${this.getModelInfo(this.event.context.binding as EventContext)}
|
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Request`}</h3>
|
<h3>${t`Request`}</h3>
|
||||||
<ul class="pf-c-list">
|
<ul class="pf-c-list">
|
||||||
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventContext)}</li>
|
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
|
||||||
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -269,12 +275,12 @@ Add any other context about the problem here.
|
||||||
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">
|
||||||
<h3>${t`Binding`}</h3>
|
<h3>${t`Binding`}</h3>
|
||||||
${this.getModelInfo(this.event.context.binding as EventContext)}
|
${this.getModelInfo(this.event.context.binding as EventModel)}
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-l-flex__item">
|
<div class="pf-l-flex__item">
|
||||||
<h3>${t`Request`}</h3>
|
<h3>${t`Request`}</h3>
|
||||||
<ul class="pf-c-list">
|
<ul class="pf-c-list">
|
||||||
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventContext)}</li>
|
<li>${t`Object`}: ${this.getModelInfo((this.event.context.request as EventContext).obj as EventModel)}</li>
|
||||||
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
<li><span>${t`Context`}: <code>${JSON.stringify((this.event.context.request as EventContext).context, null, 4)}</code></span></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -310,7 +316,7 @@ Add any other context about the problem here.
|
||||||
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">
|
||||||
<h3>${t`Using source`}</h3>
|
<h3>${t`Using source`}</h3>
|
||||||
${this.getModelInfo(this.event.context.using_source as EventContext)}
|
${this.getModelInfo(this.event.context.using_source as EventModel)}
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ export class EventListPage extends TablePage<Event> {
|
||||||
new TableColumn(t`User`, "user"),
|
new TableColumn(t`User`, "user"),
|
||||||
new TableColumn(t`Creation Date`, "created"),
|
new TableColumn(t`Creation Date`, "created"),
|
||||||
new TableColumn(t`Client IP`, "client_ip"),
|
new TableColumn(t`Client IP`, "client_ip"),
|
||||||
|
new TableColumn(t`Tenant`, "tenant_name"),
|
||||||
new TableColumn(""),
|
new TableColumn(""),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -62,6 +63,7 @@ export class EventListPage extends TablePage<Event> {
|
||||||
html`-`,
|
html`-`,
|
||||||
html`<span>${item.created?.toLocaleString()}</span>`,
|
html`<span>${item.created?.toLocaleString()}</span>`,
|
||||||
html`<span>${item.clientIp || "-"}</span>`,
|
html`<span>${item.clientIp || "-"}</span>`,
|
||||||
|
html`<span>${item.tenant?.name || "-"}</span>`,
|
||||||
html`<a href="#/events/log/${item.pk}">
|
html`<a href="#/events/log/${item.pk}">
|
||||||
<i class="fas fas fa-share-square"></i>
|
<i class="fas fas fa-share-square"></i>
|
||||||
</a>`,
|
</a>`,
|
||||||
|
|
Reference in a new issue