core: add configure_url to UserSettings for both stages and sources

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-06-08 17:56:35 +02:00
parent 029d58191e
commit fb8d67a9d9
44 changed files with 164 additions and 168 deletions

View File

@ -24,8 +24,7 @@ from structlog.stdlib import get_logger
from authentik.core.exceptions import PropertyMappingExpressionException
from authentik.core.signals import password_changed
from authentik.core.types import UILoginButton
from authentik.flows.challenge import Challenge
from authentik.core.types import UILoginButton, UserSettingSerializer
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.models import CreatedUpdatedModel, SerializerModel
@ -342,9 +341,9 @@ class Source(ManagedModel, SerializerModel, PolicyBindingModel):
return None
@property
def ui_user_settings(self) -> Optional[Challenge]:
def ui_user_settings(self) -> Optional[UserSettingSerializer]:
"""Entrypoint to integrate with User settings. Can either return None if no
user settings are available, or a challenge."""
user settings are available, or UserSettingSerializer."""
return None
def __str__(self):

View File

@ -36,3 +36,4 @@ class UserSettingSerializer(PassiveSerializer):
object_uid = CharField()
component = CharField()
title = CharField()
configure_url = CharField()

View File

@ -5,7 +5,6 @@ from django.urls.base import reverse
from drf_spectacular.utils import extend_schema
from rest_framework import mixins
from rest_framework.decorators import action
from rest_framework.fields import BooleanField, CharField
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer, SerializerMethodField
@ -21,12 +20,6 @@ from authentik.lib.utils.reflection import all_subclasses
LOGGER = get_logger()
class StageUserSettingSerializer(UserSettingSerializer):
"""User settings but can include a configure flow"""
configure_flow = CharField(required=False)
class StageSerializer(ModelSerializer, MetaNameSerializer):
"""Stage Serializer"""
@ -87,7 +80,7 @@ class StageViewSet(
data = sorted(data, key=lambda x: x["name"])
return Response(TypeCreateSerializer(data, many=True).data)
@extend_schema(responses={200: StageUserSettingSerializer(many=True)})
@extend_schema(responses={200: UserSettingSerializer(many=True)})
@action(detail=False, pagination_class=None, filter_backends=[])
def user_settings(self, request: Request) -> Response:
"""Get all stages the user can configure"""
@ -98,8 +91,8 @@ class StageViewSet(
if not user_settings:
continue
user_settings.initial_data["object_uid"] = str(stage.pk)
if hasattr(stage, "configure_flow"):
user_settings.initial_data["configure_flow"] = reverse(
if hasattr(stage, "configure_url"):
user_settings.initial_data["configure_url"] = reverse(
"authentik_flows:configure",
kwargs={"stage_uuid": stage.uuid.hex},
)

View File

@ -43,7 +43,7 @@ class Challenge(PassiveSerializer):
type = ChoiceField(
choices=[(x.value, x.name) for x in ChallengeTypes],
)
flow_info = ContextualFlowInfo()
flow_info = ContextualFlowInfo(required=False)
component = CharField(default="")
response_errors = DictField(

View File

@ -93,7 +93,11 @@ class TestFlowExecutor(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": FlowNonApplicableException.__doc__,
"title": "",
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value,
},
)
@ -422,10 +426,13 @@ class TestFlowExecutor(TestCase):
self.assertJSONEqual(
force_str(response.content),
{
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-dummy",
"title": binding.stage.name,
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)
@ -453,10 +460,13 @@ class TestFlowExecutor(TestCase):
self.assertJSONEqual(
force_str(response.content),
{
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-dummy",
"title": binding4.stage.name,
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)

View File

@ -8,6 +8,7 @@ from django.http import Http404, HttpRequest, HttpResponse, HttpResponseRedirect
from django.http.request import QueryDict
from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse
from django.urls.base import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import View
@ -309,9 +310,13 @@ class FlowExecutorView(APIView):
AccessDeniedChallenge(
{
"error_message": error_message,
"title": self.flow.title,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-access-denied",
"flow_info": {
"title": self.flow.title,
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
},
}
)
)
@ -413,7 +418,10 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
)
return HttpChallengeResponse(
RedirectChallenge(
{"type": ChallengeTypes.REDIRECT, "to": str(redirect_url)}
{
"type": ChallengeTypes.REDIRECT,
"to": str(redirect_url),
}
)
)
if isinstance(source, TemplateResponse):
@ -425,7 +433,7 @@ def to_stage_response(request: HttpRequest, source: HttpResponse) -> HttpRespons
}
)
)
# Check for actual HttpResponse (without isinstance as we dont want to check inheritance)
# Check for actual HttpResponse (without isinstance as we don't want to check inheritance)
if source.__class__ == HttpResponse:
return HttpChallengeResponse(
ShellChallenge(

View File

@ -87,6 +87,10 @@ class OAuthSource(Source):
data={
"title": f"OAuth {self.name}",
"component": "ak-user-settings-source-oauth",
"configure_url": reverse(
"authentik_sources_oauth:oauth-client-login",
kwargs={"source_slug": self.slug},
),
}
)

View File

@ -47,7 +47,11 @@ class TestUserDenyStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value,
},
)

View File

@ -108,7 +108,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual(
force_str(response.content),
{
"background": self.flow.background_url,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification",
"password_fields": True,
@ -118,6 +117,11 @@ class TestIdentificationStage(TestCase):
{"code": "invalid", "string": "Failed to " "authenticate."}
]
},
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"sources": [
{
"challenge": {
@ -129,7 +133,6 @@ class TestIdentificationStage(TestCase):
"name": "test",
}
],
"title": "",
"user_fields": ["email"],
},
)
@ -181,7 +184,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual(
force_str(response.content),
{
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification",
"user_fields": ["email"],
@ -191,7 +193,11 @@ class TestIdentificationStage(TestCase):
kwargs={"flow_slug": "unique-enrollment-string"},
),
"primary_action": "Log in",
"title": self.flow.title,
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": self.flow.title,
},
"sources": [
{
"icon_url": "/static/authentik/sources/.svg",
@ -229,7 +235,6 @@ class TestIdentificationStage(TestCase):
self.assertJSONEqual(
force_str(response.content),
{
"background": flow.background_url,
"type": ChallengeTypes.NATIVE.value,
"component": "ak-stage-identification",
"user_fields": ["email"],
@ -239,7 +244,11 @@ class TestIdentificationStage(TestCase):
kwargs={"flow_slug": "unique-recovery-string"},
),
"primary_action": "Log in",
"title": self.flow.title,
"flow_info": {
"background": flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": self.flow.title,
},
"sources": [
{
"challenge": {

View File

@ -62,8 +62,12 @@ class TestUserLoginStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)

View File

@ -67,8 +67,12 @@ class TestPasswordStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)
@ -205,7 +209,11 @@ class TestPasswordStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
"type": ChallengeTypes.NATIVE.value,
},
)

View File

@ -54,8 +54,12 @@ class TestUserDeleteStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)

View File

@ -108,7 +108,11 @@ class TestUserLoginStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)

View File

@ -151,8 +151,12 @@ class TestUserWriteStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)
@ -184,8 +188,12 @@ class TestUserWriteStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)
@ -217,7 +225,11 @@ class TestUserWriteStage(TestCase):
{
"component": "ak-stage-access-denied",
"error_message": None,
"title": "",
"type": ChallengeTypes.NATIVE.value,
"flow_info": {
"background": self.flow.background_url,
"cancel_url": reverse("authentik_flows:cancel"),
"title": "",
},
},
)

View File

@ -11170,7 +11170,7 @@ paths:
schema:
type: array
items:
$ref: '#/components/schemas/StageUserSetting'
$ref: '#/components/schemas/UserSetting'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
@ -15361,7 +15361,6 @@ components:
error_message:
type: string
required:
- flow_info
- type
ActionEnum:
enum:
@ -15692,7 +15691,6 @@ components:
required:
- activation_barcode
- activation_code
- flow_info
- pending_user
- pending_user_avatar
- stage_uuid
@ -15801,7 +15799,6 @@ components:
type: string
required:
- codes
- flow_info
- pending_user
- pending_user_avatar
- type
@ -15899,7 +15896,6 @@ components:
type: string
required:
- config_url
- flow_info
- pending_user
- pending_user_avatar
- type
@ -16077,7 +16073,6 @@ components:
$ref: '#/components/schemas/DeviceChallenge'
required:
- device_challenges
- flow_info
- pending_user
- pending_user_avatar
- type
@ -16120,7 +16115,6 @@ components:
type: object
additionalProperties: {}
required:
- flow_info
- pending_user
- pending_user_avatar
- registration
@ -16169,7 +16163,6 @@ components:
type: string
required:
- attrs
- flow_info
- type
- url
BackendsEnum:
@ -16222,7 +16215,6 @@ components:
site_key:
type: string
required:
- flow_info
- pending_user
- pending_user_avatar
- site_key
@ -16430,7 +16422,6 @@ components:
items:
$ref: '#/components/schemas/Permission'
required:
- flow_info
- header_text
- pending_user
- pending_user_avatar
@ -16730,7 +16721,6 @@ components:
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- flow_info
- type
DummyChallengeResponseRequest:
type: object
@ -16889,7 +16879,6 @@ components:
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- flow_info
- type
EmailChallengeResponseRequest:
type: object
@ -17700,7 +17689,6 @@ components:
items:
$ref: '#/components/schemas/UILoginButton'
required:
- flow_info
- password_fields
- primary_action
- type
@ -21644,7 +21632,6 @@ components:
recovery_url:
type: string
required:
- flow_info
- pending_user
- pending_user_avatar
- type
@ -23394,7 +23381,6 @@ components:
type: string
required:
- client_id
- flow_info
- slug
- type
PlexAuthenticationChallengeResponseRequest:
@ -23754,7 +23740,6 @@ components:
$ref: '#/components/schemas/StagePrompt'
required:
- fields
- flow_info
- type
PromptChallengeResponseRequest:
type: object
@ -24210,7 +24195,6 @@ components:
to:
type: string
required:
- flow_info
- to
- type
RefreshTokenModel:
@ -24927,7 +24911,6 @@ components:
type: string
required:
- body
- flow_info
- type
SignatureAlgorithmEnum:
enum:
@ -25093,22 +25076,6 @@ components:
$ref: '#/components/schemas/FlowRequest'
required:
- name
StageUserSetting:
type: object
description: User settings but can include a configure flow
properties:
object_uid:
type: string
component:
type: string
title:
type: string
configure_flow:
type: string
required:
- component
- object_uid
- title
StaticDevice:
type: object
description: Serializer for static authenticator devices
@ -25788,8 +25755,11 @@ components:
type: string
title:
type: string
configure_url:
type: string
required:
- component
- configure_url
- object_uid
- title
UserWriteStage:

View File

@ -1,7 +0,0 @@
export class AppURLManager {
static sourceOAuth(slug: string, action: string): string {
return `/source/oauth/${action}/${slug}/`;
}
}

View File

@ -86,8 +86,8 @@ export class FlowExecutor extends LitElement implements StageHost {
private postUpdate(): void {
tenant().then(tenant => {
if (this.challenge?.flowInfo.title) {
document.title = `${this.challenge.flowInfo.title} - ${tenant.brandingTitle}`;
if (this.challenge?.flowInfo?.title) {
document.title = `${this.challenge.flowInfo?.title} - ${tenant.brandingTitle}`;
} else {
document.title = tenant.brandingTitle || TITLE_DEFAULT;
}
@ -124,7 +124,7 @@ export class FlowExecutor extends LitElement implements StageHost {
}).then((challenge) => {
this.challenge = challenge;
// Only set background on first update, flow won't change throughout execution
if (this.challenge?.flowInfo.background) {
if (this.challenge?.flowInfo?.background) {
this.setBackground(this.challenge.flowInfo.background);
}
this.postUpdate();
@ -271,7 +271,7 @@ export class FlowExecutor extends LitElement implements StageHost {
${this.tenant?.brandingTitle != "authentik" ? html`
<li><a href="https://goauthentik.io">${t`Powered by authentik`}</a></li>
` : html``}
${this.challenge?.flowInfo.background?.startsWith("/static") ? html`
${this.challenge?.flowInfo?.background?.startsWith("/static") ? html`
<li><a href="https://unsplash.com/@danasaki">${t`Background image`}</a></li>
` : html``}
</ul>

View File

@ -28,7 +28,7 @@ export class FlowAccessDenied extends BaseStage<AccessDeniedChallenge, FlowChall
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -14,6 +14,7 @@ import "../../FormStatic";
import { AuthenticatorDuoChallenge, StagesApi } from "authentik-api";
import { DEFAULT_CONFIG } from "../../../api/Config";
import { AuthenticatorDuoChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorDuoChallengeResponseRequest";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-duo")
export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge, AuthenticatorDuoChallengeResponseRequest> {
@ -49,7 +50,7 @@ export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge,
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -59,7 +60,7 @@ export class AuthenticatorDuoStage extends BaseStage<AuthenticatorDuoChallenge,
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<img src=${this.challenge.activationBarcode} />

View File

@ -13,6 +13,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic";
import { AuthenticatorStaticChallenge } from "authentik-api";
import { AuthenticatorStaticChallengeResponseRequest } from "authentik-api/dist/models/AuthenticatorStaticChallengeResponseRequest";
import { ifDefined } from "lit-html/directives/if-defined";
export const STATIC_TOKEN_STYLE = css`
/* Static OTP Tokens */
@ -46,7 +47,7 @@ export class AuthenticatorStaticStage extends BaseStage<AuthenticatorStaticChall
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -56,7 +57,7 @@ export class AuthenticatorStaticStage extends BaseStage<AuthenticatorStaticChall
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<ak-form-element

View File

@ -15,6 +15,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic";
import { MessageLevel } from "../../../elements/messages/Message";
import { AuthenticatorTOTPChallenge, AuthenticatorTOTPChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-totp")
@ -33,7 +34,7 @@ export class AuthenticatorTOTPStage extends BaseStage<AuthenticatorTOTPChallenge
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -43,7 +44,7 @@ export class AuthenticatorTOTPStage extends BaseStage<AuthenticatorTOTPChallenge
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<input type="hidden" name="otp_uri" value=${this.challenge.configUrl} />

View File

@ -162,7 +162,7 @@ export class AuthenticatorValidateStage extends BaseStage<AuthenticatorValidatio
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
${this.selectedDeviceChallenge ? "" : html`<p class="pf-c-login__main-header-desc">
${t`Select an identification method.`}

View File

@ -15,6 +15,7 @@ import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-validate-code")
export class AuthenticatorValidateStageWebCode extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
@ -43,7 +44,7 @@ export class AuthenticatorValidateStageWebCode extends BaseStage<AuthenticatorVa
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<ak-form-element

View File

@ -14,6 +14,7 @@ import "../../../elements/EmptyState";
import "../../FormStatic";
import { AuthenticatorValidationChallenge } from "authentik-api/dist/models/AuthenticatorValidationChallenge";
import { AuthenticatorValidationChallengeResponseRequest, DeviceChallenge } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-authenticator-validate-duo")
export class AuthenticatorValidateStageWebDuo extends BaseStage<AuthenticatorValidationChallenge, AuthenticatorValidationChallengeResponseRequest> {
@ -48,7 +49,7 @@ export class AuthenticatorValidateStageWebDuo extends BaseStage<AuthenticatorVal
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>

View File

@ -85,7 +85,7 @@ export class WebAuthnAuthenticatorRegisterStage extends BaseStage<AuthenticatorW
render(): TemplateResult {
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge?.flowInfo.title}
${this.challenge?.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -32,7 +32,7 @@ export class AutosubmitStage extends BaseStage<AutosubmitChallenge, AutoSubmitCh
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -13,6 +13,7 @@ import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { CaptchaChallenge, CaptchaChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-captcha")
export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeResponseRequest> {
@ -57,7 +58,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -67,7 +68,7 @@ export class CaptchaStage extends BaseStage<CaptchaChallenge, CaptchaChallengeRe
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<div class="ak-loading">

View File

@ -11,6 +11,7 @@ import { BaseStage } from "../base";
import "../../../elements/EmptyState";
import "../../FormStatic";
import { ConsentChallenge, ConsentChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-consent")
@ -29,7 +30,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -39,7 +40,7 @@ export class ConsentStage extends BaseStage<ConsentChallenge, ConsentChallengeRe
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<div class="pf-c-form__group">

View File

@ -28,7 +28,7 @@ export class DummyStage extends BaseStage<DummyChallenge, DummyChallengeResponse
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -27,7 +27,7 @@ export class EmailStage extends BaseStage<EmailChallenge, EmailChallengeResponse
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -206,7 +206,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -13,6 +13,7 @@ import "../../../elements/EmptyState";
import { PasswordManagerPrefill } from "../identification/IdentificationStage";
import "../../FormStatic";
import { PasswordChallenge, PasswordChallengeResponseRequest } from "authentik-api";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-stage-password")
export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChallengeResponseRequest> {
@ -30,7 +31,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">
@ -40,7 +41,7 @@ export class PasswordStage extends BaseStage<PasswordChallenge, PasswordChalleng
userAvatar="${this.challenge.pendingUserAvatar}"
user=${this.challenge.pendingUser}>
<div slot="link">
<a href="${this.challenge.flowInfo.cancelUrl}">${t`Not you?`}</a>
<a href="${ifDefined(this.challenge.flowInfo?.cancelUrl)}">${t`Not you?`}</a>
</div>
</ak-form-static>
<input name="username" autocomplete="username" type="hidden" value="${this.challenge.pendingUser}">

View File

@ -111,7 +111,7 @@ export class PromptStage extends BaseStage<PromptChallenge, PromptChallengeRespo
}
return html`<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
${this.challenge.flowInfo.title}
${this.challenge.flowInfo?.title}
</h1>
</header>
<div class="pf-c-login__main-body">

View File

@ -4169,10 +4169,6 @@ msgstr "Use the user's email address, but deny enrollment when the email address
msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr "Use the user's username, but deny enrollment when the username already exists."
#: src/pages/sources/oauth/OAuthSourceForm.ts
msgid "Use this redirect URL:"
msgstr "Use this redirect URL:"
#: src/pages/tenants/TenantForm.ts
msgid "Use this tenant for each domain that doesn't have a dedicated tenant."
msgstr "Use this tenant for each domain that doesn't have a dedicated tenant."

View File

@ -4157,10 +4157,6 @@ msgstr ""
msgid "Use the user's username, but deny enrollment when the username already exists."
msgstr ""
#:
msgid "Use this redirect URL:"
msgstr ""
#:
msgid "Use this tenant for each domain that doesn't have a dedicated tenant."
msgstr ""

View File

@ -8,7 +8,6 @@ import "../../../elements/forms/HorizontalFormElement";
import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until";
import { first } from "../../../utils";
import { AppURLManager } from "../../../api/legacy";
import { ModelForm } from "../../../elements/forms/ModelForm";
@customElement("ak-source-oauth-form")
@ -95,14 +94,6 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
</ak-form-group>`;
}
getRedirectURI(slug?: string): string {
if (!slug) {
return "";
}
const path = AppURLManager.sourceOAuth(slug, "callback");
return `${window.location.protocol}//${window.location.host}${path}`;
}
renderForm(): TemplateResult {
return html`<form class="pf-c-form pf-m-horizontal">
<ak-form-element-horizontal
@ -115,16 +106,7 @@ export class OAuthSourceForm extends ModelForm<OAuthSource, string> {
label=${t`Slug`}
?required=${true}
name="slug">
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required @input=${(ev: Event) => {
const current = (ev.target as HTMLInputElement).value;
const label = this.shadowRoot?.querySelector<HTMLSpanElement>("#callback-url");
if (!label) return;
label.innerText = this.getRedirectURI(current);
}}>
<p class="pf-c-form__helper-text">
${t`Use this redirect URL:`}
<span id="callback-url">${this.getRedirectURI(this.instance?.slug)}</span>
</p>
<input type="text" value="${ifDefined(this.instance?.slug)}" class="pf-c-form-control" required>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="enabled">
<div class="pf-c-check">

View File

@ -13,7 +13,7 @@ import AKGlobal from "../../authentik.css";
import PFBase from "@patternfly/patternfly/patternfly-base.css";
import PFForm from "@patternfly/patternfly/components/Form/form.css";
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
import { SourcesApi, StagesApi, StageUserSetting, UserSetting } from "authentik-api";
import { SourcesApi, StagesApi, UserSetting } from "authentik-api";
import { DEFAULT_CONFIG } from "../../api/Config";
import { until } from "lit-html/directives/until";
import { ifDefined } from "lit-html/directives/if-defined";
@ -35,22 +35,22 @@ export class UserSettingsPage extends LitElement {
return [PFBase, PFPage, PFFlex, PFDisplay, PFGallery, PFContent, PFCard, PFDescriptionList, PFSizing, PFForm, PFFormControl, AKGlobal];
}
renderStageSettings(stage: StageUserSetting): TemplateResult {
renderStageSettings(stage: UserSetting): TemplateResult {
switch (stage.component) {
case "ak-user-settings-authenticator-webauthn":
return html`<ak-user-settings-authenticator-webauthn objectId=${stage.objectUid} configureFlow=${stage.configureFlow}>
return html`<ak-user-settings-authenticator-webauthn objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-webauthn>`;
case "ak-user-settings-password":
return html`<ak-user-settings-password objectId=${stage.objectUid}>
return html`<ak-user-settings-password objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-password>`;
case "ak-user-settings-authenticator-totp":
return html`<ak-user-settings-authenticator-totp objectId=${stage.objectUid} configureFlow=${stage.configureFlow}>
return html`<ak-user-settings-authenticator-totp objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-totp>`;
case "ak-user-settings-authenticator-static":
return html`<ak-user-settings-authenticator-static objectId=${stage.objectUid} configureFlow=${stage.configureFlow}>
return html`<ak-user-settings-authenticator-static objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-static>`;
case "ak-user-settings-authenticator-duo":
return html`<ak-user-settings-authenticator-duo objectId=${stage.objectUid} configureFlow=${stage.configureFlow}>
return html`<ak-user-settings-authenticator-duo objectId=${stage.objectUid} configureUrl=${stage.configureUrl}>
</ak-user-settings-authenticator-duo>`;
default:
return html`<p>${t`Error: unsupported stage settings: ${stage.component}`}</p>`;
@ -60,7 +60,7 @@ export class UserSettingsPage extends LitElement {
renderSourceSettings(source: UserSetting): TemplateResult {
switch (source.component) {
case "ak-user-settings-source-oauth":
return html`<ak-user-settings-source-oauth objectId=${source.objectUid} title=${source.title}>
return html`<ak-user-settings-source-oauth objectId=${source.objectUid} title=${source.title} configureUrl=${source.configureUrl}>
</ak-user-settings-source-oauth>`;
default:
return html`<p>${t`Error: unsupported source settings: ${source.component}`}</p>`;

View File

@ -11,6 +11,9 @@ export abstract class BaseUserSettings extends LitElement {
@property()
objectId!: string;
@property()
configureUrl?: string;
static get styles(): CSSResult[] {
return [PFBase, PFCard, PFButton, PFForm, PFFormControl, AKGlobal];
}

View File

@ -4,7 +4,7 @@ import { SourcesApi } from "authentik-api";
import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config";
import { t } from "@lingui/macro";
import { AppURLManager } from "../../../api/legacy";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-source-oauth")
export class SourceSettingsOAuth extends BaseUserSettings {
@ -40,7 +40,7 @@ export class SourceSettingsOAuth extends BaseUserSettings {
}
return html`<p>${t`Not connected.`}</p>
<a class="pf-c-button pf-m-primary"
href=${AppURLManager.sourceOAuth(this.objectId, "login")}>
href=${ifDefined(this.configureUrl)}>
${t`Connect`}
</a>`;
}))}`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, html, property, TemplateResult } from "lit-element";
import { customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config";
import { BaseUserSettings } from "./BaseUserSettings";
@ -8,9 +8,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-duo")
export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
@property()
configureFlow?: string;
renderEnabled(): TemplateResult {
return html`<div class="pf-c-card__body">
<p>
@ -48,8 +45,8 @@ export class UserSettingsAuthenticatorDuo extends BaseUserSettings {
</p>
</div>
<div class="pf-c-card__footer">
${this.configureFlow ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser"
${this.configureUrl ?
html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable Static Tokens`}
</a>`: html``}
</div>`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro";
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { CSSResult, customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config";
import { STATIC_TOKEN_STYLE } from "../../../flows/stages/authenticator_static/AuthenticatorStaticStage";
@ -9,9 +9,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-static")
export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
@property()
configureFlow?: string;
static get styles(): CSSResult[] {
return super.styles.concat(STATIC_TOKEN_STYLE);
}
@ -63,8 +60,8 @@ export class UserSettingsAuthenticatorStatic extends BaseUserSettings {
</p>
</div>
<div class="pf-c-card__footer">
${this.configureFlow ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser"
${this.configureUrl ?
html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable Static Tokens`}
</a>`: html``}
</div>`;

View File

@ -1,6 +1,6 @@
import { AuthenticatorsApi } from "authentik-api";
import { t } from "@lingui/macro";
import { customElement, html, property, TemplateResult } from "lit-element";
import { customElement, html, TemplateResult } from "lit-element";
import { until } from "lit-html/directives/until";
import { DEFAULT_CONFIG } from "../../../api/Config";
import { BaseUserSettings } from "./BaseUserSettings";
@ -8,9 +8,6 @@ import { BaseUserSettings } from "./BaseUserSettings";
@customElement("ak-user-settings-authenticator-totp")
export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
@property()
configureFlow?: string;
renderEnabled(): TemplateResult {
return html`<div class="pf-c-card__body">
<p>
@ -48,8 +45,8 @@ export class UserSettingsAuthenticatorTOTP extends BaseUserSettings {
</p>
</div>
<div class="pf-c-card__footer">
${this.configureFlow ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser"
${this.configureUrl ?
html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Enable TOTP`}
</a>`: html``}
</div>`;

View File

@ -1,4 +1,4 @@
import { CSSResult, customElement, html, property, TemplateResult } from "lit-element";
import { CSSResult, customElement, html, TemplateResult } from "lit-element";
import { t } from "@lingui/macro";
import { AuthenticatorsApi, WebAuthnDevice } from "authentik-api";
import { until } from "lit-html/directives/until";
@ -16,9 +16,6 @@ import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-authenticator-webauthn")
export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
@property()
configureFlow?: string;
static get styles(): CSSResult[] {
return super.styles.concat(PFDataList);
}
@ -100,8 +97,8 @@ export class UserSettingsAuthenticatorWebAuthn extends BaseUserSettings {
</ul>
</div>
<div class="pf-c-card__footer">
${this.configureFlow ?
html`<a href="${this.configureFlow}?next=/%23%2Fuser"
${this.configureUrl ?
html`<a href="${this.configureUrl}?next=/%23%2Fuser"
class="pf-c-button pf-m-primary">${t`Configure WebAuthn`}
</a>`: html``}
</div>

View File

@ -1,7 +1,7 @@
import { customElement, html, TemplateResult } from "lit-element";
import { t } from "@lingui/macro";
import { FlowURLManager } from "../../../api/legacy";
import { BaseUserSettings } from "./BaseUserSettings";
import { ifDefined } from "lit-html/directives/if-defined";
@customElement("ak-user-settings-password")
export class UserSettingsPassword extends BaseUserSettings {
@ -14,7 +14,7 @@ export class UserSettingsPassword extends BaseUserSettings {
${t`Change your password`}
</div>
<div class="pf-c-card__body">
<a href="${FlowURLManager.configure(this.objectId, "?next=/%23%2Fuser")}"
<a href="${ifDefined(this.configureUrl)}"
class="pf-c-button pf-m-primary">
${t`Change password`}
</a>