ATH-01-009: migrate impersonation to use API
Signed-off-by: Jens Langhammer <jens@goauthentik.io> # Conflicts: # authentik/core/urls.py # web/src/admin/AdminInterface.ts # web/src/admin/users/RelatedUserList.ts # web/src/admin/users/UserListPage.ts # web/src/admin/users/UserViewPage.ts # web/src/user/UserInterface.ts
This commit is contained in:
parent
d69d84e48c
commit
7e75a48fd0
|
@ -1,5 +1,4 @@
|
||||||
"""authentik administration overview"""
|
"""authentik administration overview"""
|
||||||
import os
|
|
||||||
import platform
|
import platform
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from sys import version as python_version
|
from sys import version as python_version
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""API Authentication"""
|
"""API Authentication"""
|
||||||
from typing import Any, Optional
|
|
||||||
from hmac import compare_digest
|
from hmac import compare_digest
|
||||||
|
from typing import Any, Optional
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
from rest_framework.authentication import BaseAuthentication, get_authorization_header
|
||||||
from rest_framework.exceptions import AuthenticationFailed
|
from rest_framework.exceptions import AuthenticationFailed
|
||||||
|
|
|
@ -67,11 +67,12 @@ from authentik.core.models import (
|
||||||
TokenIntents,
|
TokenIntents,
|
||||||
User,
|
User,
|
||||||
)
|
)
|
||||||
from authentik.events.models import EventAction
|
from authentik.events.models import Event, EventAction
|
||||||
from authentik.flows.exceptions import FlowNonApplicableException
|
from authentik.flows.exceptions import FlowNonApplicableException
|
||||||
from authentik.flows.models import FlowToken
|
from authentik.flows.models import FlowToken
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner
|
||||||
from authentik.flows.views.executor import QS_KEY_TOKEN
|
from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||||
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.stages.email.models import EmailStage
|
from authentik.stages.email.models import EmailStage
|
||||||
from authentik.stages.email.tasks import send_mails
|
from authentik.stages.email.tasks import send_mails
|
||||||
from authentik.stages.email.utils import TemplateEmailMessage
|
from authentik.stages.email.utils import TemplateEmailMessage
|
||||||
|
@ -543,6 +544,58 @@ class UserViewSet(UsedByMixin, ModelViewSet):
|
||||||
send_mails(email_stage, message)
|
send_mails(email_stage, message)
|
||||||
return Response(status=204)
|
return Response(status=204)
|
||||||
|
|
||||||
|
@permission_required("authentik_core.impersonate")
|
||||||
|
@extend_schema(
|
||||||
|
request=OpenApiTypes.NONE,
|
||||||
|
responses={
|
||||||
|
"204": OpenApiResponse(description="Successfully started impersonation"),
|
||||||
|
"401": OpenApiResponse(description="Access denied"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@action(detail=True, methods=["POST"])
|
||||||
|
def impersonate(self, request: Request, pk: int) -> Response:
|
||||||
|
"""Impersonate a user"""
|
||||||
|
if not CONFIG.y_bool("impersonation"):
|
||||||
|
LOGGER.debug("User attempted to impersonate", user=request.user)
|
||||||
|
return Response(status=401)
|
||||||
|
if not request.user.has_perm("impersonate"):
|
||||||
|
LOGGER.debug("User attempted to impersonate without permissions", user=request.user)
|
||||||
|
return Response(status=401)
|
||||||
|
|
||||||
|
user_to_be = self.get_object()
|
||||||
|
|
||||||
|
request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user
|
||||||
|
request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be
|
||||||
|
|
||||||
|
Event.new(EventAction.IMPERSONATION_STARTED).from_http(request, user_to_be)
|
||||||
|
|
||||||
|
return Response(status=201)
|
||||||
|
|
||||||
|
@extend_schema(
|
||||||
|
request=OpenApiTypes.NONE,
|
||||||
|
responses={
|
||||||
|
"204": OpenApiResponse(description="Successfully started impersonation"),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
@action(detail=False, methods=["GET"])
|
||||||
|
def impersonate_end(self, request: Request) -> Response:
|
||||||
|
"""End Impersonation a user"""
|
||||||
|
if (
|
||||||
|
SESSION_KEY_IMPERSONATE_USER not in request.session
|
||||||
|
or SESSION_KEY_IMPERSONATE_ORIGINAL_USER not in request.session
|
||||||
|
):
|
||||||
|
LOGGER.debug("Can't end impersonation", user=request.user)
|
||||||
|
return Response(status=204)
|
||||||
|
|
||||||
|
original_user = request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER]
|
||||||
|
|
||||||
|
del request.session[SESSION_KEY_IMPERSONATE_USER]
|
||||||
|
del request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER]
|
||||||
|
|
||||||
|
Event.new(EventAction.IMPERSONATION_ENDED).from_http(request, original_user)
|
||||||
|
|
||||||
|
return Response(status=204)
|
||||||
|
|
||||||
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
|
def _filter_queryset_for_list(self, queryset: QuerySet) -> QuerySet:
|
||||||
"""Custom filter_queryset method which ignores guardian, but still supports sorting"""
|
"""Custom filter_queryset method which ignores guardian, but still supports sorting"""
|
||||||
for backend in list(self.filter_backends):
|
for backend in list(self.filter_backends):
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
"""impersonation tests"""
|
"""impersonation tests"""
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
from django.test.testcases import TestCase
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.core.tests.utils import create_test_admin_user
|
from authentik.core.tests.utils import create_test_admin_user
|
||||||
|
|
||||||
|
|
||||||
class TestImpersonation(TestCase):
|
class TestImpersonation(APITestCase):
|
||||||
"""impersonation tests"""
|
"""impersonation tests"""
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
@ -23,10 +23,10 @@ class TestImpersonation(TestCase):
|
||||||
self.other_user.save()
|
self.other_user.save()
|
||||||
self.client.force_login(self.user)
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
self.client.get(
|
self.client.post(
|
||||||
reverse(
|
reverse(
|
||||||
"authentik_core:impersonate-init",
|
"authentik_api:user-impersonate",
|
||||||
kwargs={"user_id": self.other_user.pk},
|
kwargs={"pk": self.other_user.pk},
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class TestImpersonation(TestCase):
|
||||||
self.assertEqual(response_body["user"]["username"], self.other_user.username)
|
self.assertEqual(response_body["user"]["username"], self.other_user.username)
|
||||||
self.assertEqual(response_body["original"]["username"], self.user.username)
|
self.assertEqual(response_body["original"]["username"], self.user.username)
|
||||||
|
|
||||||
self.client.get(reverse("authentik_core:impersonate-end"))
|
self.client.get(reverse("authentik_api:user-impersonate-end"))
|
||||||
|
|
||||||
response = self.client.get(reverse("authentik_api:user-me"))
|
response = self.client.get(reverse("authentik_api:user-me"))
|
||||||
response_body = loads(response.content.decode())
|
response_body = loads(response.content.decode())
|
||||||
|
@ -46,9 +46,7 @@ class TestImpersonation(TestCase):
|
||||||
"""test impersonation without permissions"""
|
"""test impersonation without permissions"""
|
||||||
self.client.force_login(self.other_user)
|
self.client.force_login(self.other_user)
|
||||||
|
|
||||||
self.client.get(
|
self.client.get(reverse("authentik_api:user-impersonate", kwargs={"pk": self.user.pk}))
|
||||||
reverse("authentik_core:impersonate-init", kwargs={"user_id": self.user.pk})
|
|
||||||
)
|
|
||||||
|
|
||||||
response = self.client.get(reverse("authentik_api:user-me"))
|
response = self.client.get(reverse("authentik_api:user-me"))
|
||||||
response_body = loads(response.content.decode())
|
response_body = loads(response.content.decode())
|
||||||
|
@ -58,5 +56,5 @@ class TestImpersonation(TestCase):
|
||||||
"""test un-impersonation without impersonating first"""
|
"""test un-impersonation without impersonating first"""
|
||||||
self.client.force_login(self.other_user)
|
self.client.force_login(self.other_user)
|
||||||
|
|
||||||
response = self.client.get(reverse("authentik_core:impersonate-end"))
|
response = self.client.get(reverse("authentik_api:user-impersonate-end"))
|
||||||
self.assertRedirects(response, reverse("authentik_core:if-user"))
|
self.assertEqual(response.status_code, 204)
|
||||||
|
|
|
@ -7,7 +7,7 @@ from django.urls import path
|
||||||
from django.views.decorators.csrf import ensure_csrf_cookie
|
from django.views.decorators.csrf import ensure_csrf_cookie
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
from authentik.core.views import apps, impersonate
|
from authentik.core.views import apps
|
||||||
from authentik.core.views.debug import AccessDeniedView
|
from authentik.core.views.debug import AccessDeniedView
|
||||||
from authentik.core.views.interface import FlowInterfaceView, InterfaceView
|
from authentik.core.views.interface import FlowInterfaceView, InterfaceView
|
||||||
from authentik.core.views.session import EndSessionView
|
from authentik.core.views.session import EndSessionView
|
||||||
|
@ -28,17 +28,6 @@ urlpatterns = [
|
||||||
apps.RedirectToAppLaunch.as_view(),
|
apps.RedirectToAppLaunch.as_view(),
|
||||||
name="application-launch",
|
name="application-launch",
|
||||||
),
|
),
|
||||||
# Impersonation
|
|
||||||
path(
|
|
||||||
"-/impersonation/<int:user_id>/",
|
|
||||||
impersonate.ImpersonateInitView.as_view(),
|
|
||||||
name="impersonate-init",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"-/impersonation/end/",
|
|
||||||
impersonate.ImpersonateEndView.as_view(),
|
|
||||||
name="impersonate-end",
|
|
||||||
),
|
|
||||||
# Interfaces
|
# Interfaces
|
||||||
path(
|
path(
|
||||||
"if/admin/",
|
"if/admin/",
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
"""authentik impersonation views"""
|
|
||||||
|
|
||||||
from django.http import HttpRequest, HttpResponse
|
|
||||||
from django.shortcuts import get_object_or_404, redirect
|
|
||||||
from django.views import View
|
|
||||||
from structlog.stdlib import get_logger
|
|
||||||
|
|
||||||
from authentik.core.middleware import (
|
|
||||||
SESSION_KEY_IMPERSONATE_ORIGINAL_USER,
|
|
||||||
SESSION_KEY_IMPERSONATE_USER,
|
|
||||||
)
|
|
||||||
from authentik.core.models import User
|
|
||||||
from authentik.events.models import Event, EventAction
|
|
||||||
from authentik.lib.config import CONFIG
|
|
||||||
|
|
||||||
LOGGER = get_logger()
|
|
||||||
|
|
||||||
|
|
||||||
class ImpersonateInitView(View):
|
|
||||||
"""Initiate Impersonation"""
|
|
||||||
|
|
||||||
def get(self, request: HttpRequest, user_id: int) -> HttpResponse:
|
|
||||||
"""Impersonation handler, checks permissions"""
|
|
||||||
if not CONFIG.y_bool("impersonation"):
|
|
||||||
LOGGER.debug("User attempted to impersonate", user=request.user)
|
|
||||||
return HttpResponse("Unauthorized", status=401)
|
|
||||||
if not request.user.has_perm("impersonate"):
|
|
||||||
LOGGER.debug("User attempted to impersonate without permissions", user=request.user)
|
|
||||||
return HttpResponse("Unauthorized", status=401)
|
|
||||||
|
|
||||||
user_to_be = get_object_or_404(User, pk=user_id)
|
|
||||||
|
|
||||||
request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER] = request.user
|
|
||||||
request.session[SESSION_KEY_IMPERSONATE_USER] = user_to_be
|
|
||||||
|
|
||||||
Event.new(EventAction.IMPERSONATION_STARTED).from_http(request, user_to_be)
|
|
||||||
|
|
||||||
return redirect("authentik_core:if-user")
|
|
||||||
|
|
||||||
|
|
||||||
class ImpersonateEndView(View):
|
|
||||||
"""End User impersonation"""
|
|
||||||
|
|
||||||
def get(self, request: HttpRequest) -> HttpResponse:
|
|
||||||
"""End Impersonation handler"""
|
|
||||||
if (
|
|
||||||
SESSION_KEY_IMPERSONATE_USER not in request.session
|
|
||||||
or SESSION_KEY_IMPERSONATE_ORIGINAL_USER not in request.session
|
|
||||||
):
|
|
||||||
LOGGER.debug("Can't end impersonation", user=request.user)
|
|
||||||
return redirect("authentik_core:if-user")
|
|
||||||
|
|
||||||
original_user = request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER]
|
|
||||||
|
|
||||||
del request.session[SESSION_KEY_IMPERSONATE_USER]
|
|
||||||
del request.session[SESSION_KEY_IMPERSONATE_ORIGINAL_USER]
|
|
||||||
|
|
||||||
Event.new(EventAction.IMPERSONATION_ENDED).from_http(request, original_user)
|
|
||||||
|
|
||||||
return redirect("authentik_core:root-redirect")
|
|
55
schema.yml
55
schema.yml
|
@ -4783,6 +4783,38 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/core/users/{id}/impersonate/:
|
||||||
|
post:
|
||||||
|
operationId: core_users_impersonate_create
|
||||||
|
description: Impersonate a user
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: id
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
description: A unique integer value identifying this User.
|
||||||
|
required: true
|
||||||
|
tags:
|
||||||
|
- core
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Successfully started impersonation
|
||||||
|
'401':
|
||||||
|
description: Access denied
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/core/users/{id}/metrics/:
|
/core/users/{id}/metrics/:
|
||||||
get:
|
get:
|
||||||
operationId: core_users_metrics_retrieve
|
operationId: core_users_metrics_retrieve
|
||||||
|
@ -4962,6 +4994,29 @@ paths:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/GenericError'
|
$ref: '#/components/schemas/GenericError'
|
||||||
description: ''
|
description: ''
|
||||||
|
/core/users/impersonate_end/:
|
||||||
|
get:
|
||||||
|
operationId: core_users_impersonate_end_retrieve
|
||||||
|
description: End Impersonation a user
|
||||||
|
tags:
|
||||||
|
- core
|
||||||
|
security:
|
||||||
|
- authentik: []
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: Successfully started impersonation
|
||||||
|
'400':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ValidationError'
|
||||||
|
description: ''
|
||||||
|
'403':
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/GenericError'
|
||||||
|
description: ''
|
||||||
/core/users/me/:
|
/core/users/me/:
|
||||||
get:
|
get:
|
||||||
operationId: core_users_me_retrieve
|
operationId: core_users_me_retrieve
|
||||||
|
|
|
@ -31,7 +31,7 @@ import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css";
|
||||||
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
|
|
||||||
import { AdminApi, SessionUser, Version } from "@goauthentik/api";
|
import { AdminApi, CoreApi, SessionUser, Version } from "@goauthentik/api";
|
||||||
|
|
||||||
autoDetectLanguage();
|
autoDetectLanguage();
|
||||||
|
|
||||||
|
@ -175,10 +175,11 @@ export class AdminInterface extends Interface {
|
||||||
${this.user?.original
|
${this.user?.original
|
||||||
? html`<ak-sidebar-item
|
? html`<ak-sidebar-item
|
||||||
?highlight=${true}
|
?highlight=${true}
|
||||||
?isAbsoluteLink=${true}
|
@click=${() => {
|
||||||
path=${`/-/impersonation/end/?back=${encodeURIComponent(
|
new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve().then(() => {
|
||||||
`${window.location.pathname}#${window.location.hash}`,
|
window.location.reload();
|
||||||
)}`}
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span slot="label"
|
<span slot="label"
|
||||||
>${t`You're currently impersonating ${this.user.user.username}. Click to stop.`}</span
|
>${t`You're currently impersonating ${this.user.user.username}. Click to stop.`}</span
|
||||||
|
|
|
@ -191,12 +191,20 @@ export class RelatedUserList extends Table<User> {
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
||||||
? html`
|
? html`
|
||||||
<a
|
<ak-action-button
|
||||||
class="pf-c-button pf-m-tertiary"
|
class="pf-m-tertiary"
|
||||||
href="${`/-/impersonation/${item.pk}/`}"
|
.apiRequest=${() => {
|
||||||
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
|
.coreUsersImpersonateCreate({
|
||||||
|
id: item.pk,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = "/";
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
${t`Impersonate`}
|
${t`Impersonate`}
|
||||||
</a>
|
</ak-action-button>
|
||||||
`
|
`
|
||||||
: html``}`,
|
: html``}`,
|
||||||
];
|
];
|
||||||
|
|
|
@ -151,12 +151,20 @@ export class UserListPage extends TablePage<User> {
|
||||||
</ak-forms-modal>
|
</ak-forms-modal>
|
||||||
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
${rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.Impersonate)
|
||||||
? html`
|
? html`
|
||||||
<a
|
<ak-action-button
|
||||||
class="pf-c-button pf-m-tertiary"
|
class="pf-m-tertiary"
|
||||||
href="${`/-/impersonation/${item.pk}/`}"
|
.apiRequest=${() => {
|
||||||
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
|
.coreUsersImpersonateCreate({
|
||||||
|
id: item.pk,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = "/";
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
${t`Impersonate`}
|
${t`Impersonate`}
|
||||||
</a>
|
</ak-action-button>
|
||||||
`
|
`
|
||||||
: html``}`,
|
: html``}`,
|
||||||
];
|
];
|
||||||
|
|
|
@ -201,12 +201,20 @@ export class UserViewPage extends AKElement {
|
||||||
)
|
)
|
||||||
? html`
|
? html`
|
||||||
<div class="pf-c-card__footer">
|
<div class="pf-c-card__footer">
|
||||||
<a
|
<ak-action-button
|
||||||
class="pf-c-button pf-m-tertiary"
|
class="pf-m-tertiary"
|
||||||
href="${`/-/impersonation/${this.user?.pk}/`}"
|
.apiRequest=${() => {
|
||||||
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
|
.coreUsersImpersonateCreate({
|
||||||
|
id: this.user?.pk || 0,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
window.location.href = "/";
|
||||||
|
});
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
${t`Impersonate`}
|
${t`Impersonate`}
|
||||||
</a>
|
</ak-action-button>
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: html``}
|
: html``}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { me } from "@goauthentik/common/users";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||||
import { Interface } from "@goauthentik/elements/Base";
|
import { Interface } from "@goauthentik/elements/Base";
|
||||||
|
import "@goauthentik/elements/buttons/ActionButton";
|
||||||
import "@goauthentik/elements/messages/MessageContainer";
|
import "@goauthentik/elements/messages/MessageContainer";
|
||||||
import "@goauthentik/elements/notifications/APIDrawer";
|
import "@goauthentik/elements/notifications/APIDrawer";
|
||||||
import "@goauthentik/elements/notifications/NotificationDrawer";
|
import "@goauthentik/elements/notifications/NotificationDrawer";
|
||||||
|
@ -36,7 +37,7 @@ import PFPage from "@patternfly/patternfly/components/Page/page.css";
|
||||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||||
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css";
|
||||||
|
|
||||||
import { EventsApi, SessionUser } from "@goauthentik/api";
|
import { CoreApi, EventsApi, SessionUser } from "@goauthentik/api";
|
||||||
|
|
||||||
autoDetectLanguage();
|
autoDetectLanguage();
|
||||||
|
|
||||||
|
@ -234,18 +235,23 @@ export class UserInterface extends Interface {
|
||||||
: html``}
|
: html``}
|
||||||
</div>
|
</div>
|
||||||
${this.me.original
|
${this.me.original
|
||||||
? html`<div class="pf-c-page__header-tools">
|
? html`
|
||||||
<div class="pf-c-page__header-tools-group">
|
<div class="pf-c-page__header-tools">
|
||||||
<a
|
<div class="pf-c-page__header-tools-group">
|
||||||
class="pf-c-button pf-m-warning pf-m-small"
|
<ak-action-button
|
||||||
href=${`/-/impersonation/end/?back=${encodeURIComponent(
|
class="pf-m-warning pf-m-small"
|
||||||
`${window.location.pathname}#${window.location.hash}`,
|
.apiRequest=${() => {
|
||||||
)}`}
|
return new CoreApi(DEFAULT_CONFIG)
|
||||||
>
|
.coreUsersImpersonateEndRetrieve()
|
||||||
${t`Stop impersonation`}
|
.then(() => {
|
||||||
</a>
|
window.location.reload();
|
||||||
</div>
|
});
|
||||||
</div>`
|
}}
|
||||||
|
>
|
||||||
|
${t`Stop impersonation`}
|
||||||
|
</ak-action-button>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
: html``}
|
: html``}
|
||||||
<div class="pf-c-page__header-tools-group">
|
<div class="pf-c-page__header-tools-group">
|
||||||
<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-md">
|
<div class="pf-c-page__header-tools-item pf-m-hidden pf-m-visible-on-md">
|
||||||
|
|
Reference in New Issue