stages/identification: fix challenges not being annotated correctly and API client not loading data correctly

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-06-14 22:24:34 +02:00
parent ec4c3f44cb
commit 53100a72fe
6 changed files with 120 additions and 78 deletions

View file

@ -2,7 +2,7 @@
from dataclasses import dataclass
from typing import Optional
from rest_framework.fields import CharField, DictField
from rest_framework.fields import CharField
from authentik.core.api.utils import PassiveSerializer
from authentik.flows.challenge import Challenge
@ -22,14 +22,6 @@ class UILoginButton:
icon_url: Optional[str] = None
class UILoginButtonSerializer(PassiveSerializer):
"""Serializer for Login buttons of sources"""
name = CharField()
challenge = DictField()
icon_url = CharField(required=False, allow_null=True)
class UserSettingSerializer(PassiveSerializer):
"""Serializer for User settings for stages and sources"""

View file

@ -174,7 +174,7 @@ class FlowExecutorView(APIView):
@extend_schema(
responses={
200: PolymorphicProxySerializer(
component_name="FlowChallengeRequest",
component_name="ChallengeTypes",
serializers=challenge_types(),
resource_type_field_name="component",
),
@ -214,7 +214,7 @@ class FlowExecutorView(APIView):
@extend_schema(
responses={
200: PolymorphicProxySerializer(
component_name="FlowChallengeRequest",
component_name="ChallengeTypes",
serializers=challenge_types(),
resource_type_field_name="component",
),

View file

@ -8,19 +8,25 @@ from django.db.models import Q
from django.http import HttpResponse
from django.urls import reverse
from django.utils.translation import gettext as _
from rest_framework.fields import BooleanField, CharField, ListField
from drf_spectacular.utils import PolymorphicProxySerializer, extend_schema_field
from rest_framework.fields import (
BooleanField,
CharField,
DictField,
ListField,
)
from rest_framework.serializers import ValidationError
from structlog.stdlib import get_logger
from authentik.core.api.utils import PassiveSerializer
from authentik.core.models import Application, Source, User
from authentik.core.types import UILoginButtonSerializer
from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTypes
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER
from authentik.flows.stage import (
PLAN_CONTEXT_PENDING_USER_IDENTIFIER,
ChallengeStageView,
)
from authentik.flows.views import SESSION_KEY_APPLICATION_PRE
from authentik.flows.views import SESSION_KEY_APPLICATION_PRE, challenge_types
from authentik.stages.identification.models import IdentificationStage
from authentik.stages.identification.signals import identification_failed
from authentik.stages.password.stage import authenticate
@ -28,6 +34,26 @@ from authentik.stages.password.stage import authenticate
LOGGER = get_logger()
@extend_schema_field(
PolymorphicProxySerializer(
component_name="ChallengeTypes",
serializers=challenge_types(),
resource_type_field_name="component",
)
)
class ChallengeDictWrapper(DictField):
"""Wrapper around DictField that annotates itself as challenge proxy"""
class LoginSourceSerializer(PassiveSerializer):
"""Serializer for Login buttons of sources"""
name = CharField()
icon_url = CharField(required=False, allow_null=True)
challenge = ChallengeDictWrapper()
class IdentificationChallenge(Challenge):
"""Identification challenges with all UI elements"""
@ -38,7 +64,7 @@ class IdentificationChallenge(Challenge):
enroll_url = CharField(required=False)
recovery_url = CharField(required=False)
primary_action = CharField()
sources = UILoginButtonSerializer(many=True, required=False)
sources = LoginSourceSerializer(many=True, required=False)
component = CharField(default="ak-stage-identification")
@ -154,6 +180,7 @@ class IdentificationStageView(ChallengeStageView):
button = asdict(ui_login_button)
button["challenge"] = ui_login_button.challenge.data
ui_sources.append(button)
print(ui_sources)
challenge.initial_data["sources"] = ui_sources
return challenge

View file

@ -4608,7 +4608,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/FlowChallengeRequest'
$ref: '#/components/schemas/ChallengeTypes'
description: ''
'404':
description: No Token found
@ -4654,7 +4654,7 @@ paths:
content:
application/json:
schema:
$ref: '#/components/schemas/FlowChallengeRequest'
$ref: '#/components/schemas/ChallengeTypes'
description: ''
'400':
$ref: '#/components/schemas/ValidationError'
@ -18412,12 +18412,75 @@ components:
required:
- certificate_data
- name
ChallT:
type: object
description: |-
Challenge that gets sent to the client based on which stage
is currently active
properties:
type:
$ref: '#/components/schemas/ChallengeChoices'
flow_info:
$ref: '#/components/schemas/ContextualFlowInfo'
component:
type: string
default: ''
response_errors:
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
required:
- type
ChallengeChoices:
enum:
- native
- shell
- redirect
type: string
ChallengeTypes:
oneOf:
- $ref: '#/components/schemas/AccessDeniedChallenge'
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
- $ref: '#/components/schemas/AuthenticatorStaticChallenge'
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
- $ref: '#/components/schemas/AutosubmitChallenge'
- $ref: '#/components/schemas/CaptchaChallenge'
- $ref: '#/components/schemas/ChallT'
- $ref: '#/components/schemas/ConsentChallenge'
- $ref: '#/components/schemas/DummyChallenge'
- $ref: '#/components/schemas/EmailChallenge'
- $ref: '#/components/schemas/IdentificationChallenge'
- $ref: '#/components/schemas/PasswordChallenge'
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
- $ref: '#/components/schemas/PromptChallenge'
- $ref: '#/components/schemas/RedirectChallenge'
- $ref: '#/components/schemas/ShellChallenge'
discriminator:
propertyName: component
mapping:
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
? ''
: '#/components/schemas/ChallT'
ak-stage-consent: '#/components/schemas/ConsentChallenge'
ak-stage-dummy: '#/components/schemas/DummyChallenge'
ak-stage-email: '#/components/schemas/EmailChallenge'
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
ak-stage-password: '#/components/schemas/PasswordChallenge'
ak-flow-sources-plex: '#/components/schemas/PlexAuthenticationChallenge'
ak-stage-prompt: '#/components/schemas/PromptChallenge'
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
xak-flow-shell: '#/components/schemas/ShellChallenge'
ClientTypeEnum:
enum:
- confidential
@ -19388,45 +19451,6 @@ components:
- slug
- stages
- title
FlowChallengeRequest:
oneOf:
- $ref: '#/components/schemas/AccessDeniedChallenge'
- $ref: '#/components/schemas/AuthenticatorDuoChallenge'
- $ref: '#/components/schemas/AuthenticatorStaticChallenge'
- $ref: '#/components/schemas/AuthenticatorTOTPChallenge'
- $ref: '#/components/schemas/AuthenticatorValidationChallenge'
- $ref: '#/components/schemas/AuthenticatorWebAuthnChallenge'
- $ref: '#/components/schemas/AutosubmitChallenge'
- $ref: '#/components/schemas/CaptchaChallenge'
- $ref: '#/components/schemas/ConsentChallenge'
- $ref: '#/components/schemas/DummyChallenge'
- $ref: '#/components/schemas/EmailChallenge'
- $ref: '#/components/schemas/IdentificationChallenge'
- $ref: '#/components/schemas/PasswordChallenge'
- $ref: '#/components/schemas/PlexAuthenticationChallenge'
- $ref: '#/components/schemas/PromptChallenge'
- $ref: '#/components/schemas/RedirectChallenge'
- $ref: '#/components/schemas/ShellChallenge'
discriminator:
propertyName: component
mapping:
ak-stage-access-denied: '#/components/schemas/AccessDeniedChallenge'
ak-stage-authenticator-duo: '#/components/schemas/AuthenticatorDuoChallenge'
ak-stage-authenticator-static: '#/components/schemas/AuthenticatorStaticChallenge'
ak-stage-authenticator-totp: '#/components/schemas/AuthenticatorTOTPChallenge'
ak-stage-authenticator-validate: '#/components/schemas/AuthenticatorValidationChallenge'
ak-stage-authenticator-webauthn: '#/components/schemas/AuthenticatorWebAuthnChallenge'
ak-stage-autosubmit: '#/components/schemas/AutosubmitChallenge'
ak-stage-captcha: '#/components/schemas/CaptchaChallenge'
ak-stage-consent: '#/components/schemas/ConsentChallenge'
ak-stage-dummy: '#/components/schemas/DummyChallenge'
ak-stage-email: '#/components/schemas/EmailChallenge'
ak-stage-identification: '#/components/schemas/IdentificationChallenge'
ak-stage-password: '#/components/schemas/PasswordChallenge'
ak-flow-sources-plex: '#/components/schemas/PlexAuthenticationChallenge'
ak-stage-prompt: '#/components/schemas/PromptChallenge'
xak-flow-redirect: '#/components/schemas/RedirectChallenge'
xak-flow-shell: '#/components/schemas/ShellChallenge'
FlowChallengeResponseRequest:
oneOf:
- $ref: '#/components/schemas/AuthenticatorDuoChallengeResponseRequest'
@ -19776,7 +19800,7 @@ components:
sources:
type: array
items:
$ref: '#/components/schemas/UILoginButton'
$ref: '#/components/schemas/LoginSource'
required:
- password_fields
- primary_action
@ -20472,6 +20496,20 @@ components:
required:
- logins_failed_per_1h
- logins_per_1h
LoginSource:
type: object
description: Serializer for Login buttons of sources
properties:
name:
type: string
icon_url:
type: string
nullable: true
challenge:
$ref: '#/components/schemas/ChallengeTypes'
required:
- challenge
- name
NameIdPolicyEnum:
enum:
- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
@ -27526,21 +27564,6 @@ components:
- description
- model_name
- name
UILoginButton:
type: object
description: Serializer for Login buttons of sources
properties:
name:
type: string
challenge:
type: object
additionalProperties: {}
icon_url:
type: string
nullable: true
required:
- challenge
- name
UsedBy:
type: object
description: A list of all objects referencing the queried object

View file

@ -26,7 +26,7 @@ import "./stages/password/PasswordStage";
import "./stages/prompt/PromptStage";
import "./sources/plex/PlexLoginInit";
import { StageHost } from "./stages/base";
import { ChallengeChoices, CurrentTenant, FlowChallengeRequest, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
import { ChallengeChoices, CurrentTenant, ChallengeTypes, FlowChallengeResponseRequest, FlowsApi, RedirectChallenge, ShellChallenge } from "authentik-api";
import { DEFAULT_CONFIG, tenant } from "../api/Config";
import { ifDefined } from "lit-html/directives/if-defined";
import { until } from "lit-html/directives/until";
@ -40,7 +40,7 @@ export class FlowExecutor extends LitElement implements StageHost {
flowSlug: string;
@property({attribute: false})
challenge?: FlowChallengeRequest;
challenge?: ChallengeTypes;
@property({type: Boolean})
loading = false;
@ -163,7 +163,7 @@ export class FlowExecutor extends LitElement implements StageHost {
</li>
</ul>
</footer>`
} as FlowChallengeRequest;
} as ChallengeTypes;
}
renderLoading(): TemplateResult {

View file

@ -11,7 +11,7 @@ import PFAlert from "@patternfly/patternfly/components/Alert/alert.css";
import AKGlobal from "../../../authentik.css";
import "../../../elements/forms/FormElement";
import "../../../elements/EmptyState";
import { FlowChallengeRequest, IdentificationChallenge, IdentificationChallengeResponseRequest, UILoginButton, UserFieldsEnum } from "authentik-api";
import { IdentificationChallenge, IdentificationChallengeResponseRequest, LoginSource, UserFieldsEnum } from "authentik-api";
export const PasswordManagerPrefill: {
password: string | undefined;
@ -110,7 +110,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
wrapperForm.appendChild(totp);
}
renderSource(source: UILoginButton): TemplateResult {
renderSource(source: LoginSource): TemplateResult {
let icon = html`<i class="fas fas fa-share-square" title="${source.name}"></i>`;
if (source.iconUrl) {
icon = html`<img src="${source.iconUrl}" alt="${source.name}">`;
@ -118,7 +118,7 @@ export class IdentificationStage extends BaseStage<IdentificationChallenge, Iden
return html`<li class="pf-c-login__main-footer-links-item">
<button type="button" @click=${() => {
if (!this.host) return;
this.host.challenge = source.challenge as FlowChallengeRequest;
this.host.challenge = source.challenge;
}}>
${icon}
</button>