Compare commits
10 Commits
files-in-d
...
deny-unaut
Author | SHA1 | Date | |
---|---|---|---|
7d40e00263 | |||
42501f6d1e | |||
2759b1c089 | |||
ce6d76babe | |||
5cc2bd5b36 | |||
bad8a8ead5 | |||
1f7a2d5194 | |||
5e328403d6 | |||
f03e56af93 | |||
516aa9d9b1 |
@ -32,6 +32,8 @@ jobs:
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
- name: Setup authentik env
|
||||
uses: ./.github/actions/setup
|
||||
- name: Generate API
|
||||
run: make gen-client-ts
|
||||
- name: run extract
|
||||
run: |
|
||||
poetry run make i18n-extract
|
||||
|
@ -8,7 +8,13 @@ from uuid import UUID
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import models
|
||||
from django.http import JsonResponse
|
||||
from rest_framework.fields import BooleanField, CharField, ChoiceField, DictField
|
||||
from rest_framework.fields import (
|
||||
BooleanField,
|
||||
CharField,
|
||||
ChoiceField,
|
||||
DictField,
|
||||
ListField,
|
||||
)
|
||||
from rest_framework.request import Request
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
@ -39,6 +45,12 @@ class ErrorDetailSerializer(PassiveSerializer):
|
||||
code = CharField()
|
||||
|
||||
|
||||
class MessageSerializer(PassiveSerializer):
|
||||
message = CharField()
|
||||
level = CharField()
|
||||
tags = ListField(child=CharField())
|
||||
|
||||
|
||||
class ContextualFlowInfo(PassiveSerializer):
|
||||
"""Contextual flow information for a challenge"""
|
||||
|
||||
@ -55,6 +67,7 @@ class Challenge(PassiveSerializer):
|
||||
flow_info = ContextualFlowInfo(required=False)
|
||||
component = CharField(default="")
|
||||
|
||||
messages = ListField(child=MessageSerializer(), allow_empty=True, required=False)
|
||||
response_errors = DictField(
|
||||
child=ErrorDetailSerializer(many=True), allow_empty=True, required=False
|
||||
)
|
||||
@ -170,7 +183,6 @@ class FrameChallenge(Challenge):
|
||||
|
||||
|
||||
class FrameChallengeResponse(ChallengeResponse):
|
||||
|
||||
component = CharField(default="xak-flow-frame")
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.messages import get_messages
|
||||
from django.http import HttpRequest
|
||||
from django.http.request import QueryDict
|
||||
from django.http.response import HttpResponse
|
||||
@ -21,6 +22,7 @@ from authentik.flows.challenge import (
|
||||
ChallengeResponse,
|
||||
ContextualFlowInfo,
|
||||
HttpChallengeResponse,
|
||||
MessageSerializer,
|
||||
RedirectChallenge,
|
||||
SessionEndChallenge,
|
||||
WithUserInfoChallenge,
|
||||
@ -191,6 +193,22 @@ class ChallengeStageView(StageView):
|
||||
)
|
||||
flow_info.is_valid()
|
||||
challenge.initial_data["flow_info"] = flow_info.data
|
||||
if "messages" not in challenge.initial_data and not isinstance(
|
||||
challenge, RedirectStage
|
||||
):
|
||||
messages = MessageSerializer(
|
||||
data=[
|
||||
{
|
||||
"message": message.message,
|
||||
"level": message.level_tag,
|
||||
"tags": message.tags,
|
||||
}
|
||||
for message in get_messages(self.request)
|
||||
],
|
||||
many=True,
|
||||
)
|
||||
messages.is_valid()
|
||||
challenge.initial_data["messages"] = messages.data
|
||||
if isinstance(challenge, WithUserInfoChallenge):
|
||||
# If there's a pending user, update the `username` field
|
||||
# this field is only used by password managers.
|
||||
|
@ -55,6 +55,7 @@ class TestFlowInspector(APITestCase):
|
||||
"layout": "stacked",
|
||||
},
|
||||
"flow_designation": "authentication",
|
||||
"messages": [],
|
||||
"password_fields": False,
|
||||
"primary_action": "Log in",
|
||||
"sources": [],
|
||||
|
@ -88,6 +88,7 @@ class TesOAuth2DeviceInit(OAuthTestCase):
|
||||
"layout": "stacked",
|
||||
"title": self.device_flow.title,
|
||||
},
|
||||
"messages": [],
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
"""websocket Message consumer"""
|
||||
|
||||
from channels.exceptions import DenyConnection
|
||||
from channels.generic.websocket import JsonWebsocketConsumer
|
||||
from django.core.cache import cache
|
||||
|
||||
@ -13,6 +14,8 @@ class MessageConsumer(JsonWebsocketConsumer):
|
||||
session_key: str
|
||||
|
||||
def connect(self):
|
||||
if not self.scope["user"].is_authenticated():
|
||||
raise DenyConnection()
|
||||
self.accept()
|
||||
self.session_key = self.scope["session"].session_key
|
||||
if not self.session_key:
|
||||
|
@ -7,7 +7,6 @@ from django.contrib.messages.storage.session import SessionStorage
|
||||
from django.core.cache import cache
|
||||
from django.http.request import HttpRequest
|
||||
|
||||
SESSION_KEY = "_messages"
|
||||
CACHE_PREFIX = "goauthentik.io/root/messages_"
|
||||
|
||||
|
||||
|
@ -145,9 +145,9 @@ class EmailStageView(ChallengeStageView):
|
||||
user.save()
|
||||
return self.executor.stage_ok()
|
||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
self.logger.debug("No pending user")
|
||||
messages.error(self.request, _("No pending user."))
|
||||
return self.executor.stage_invalid()
|
||||
message = _("No pending user")
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid(message)
|
||||
# Check if we've already sent the initial e-mail
|
||||
if PLAN_CONTEXT_EMAIL_SENT not in self.executor.plan.context:
|
||||
try:
|
||||
|
@ -1,6 +1,5 @@
|
||||
"""Delete stage logic"""
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import logout
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
@ -17,9 +16,8 @@ class UserDeleteStageView(StageView):
|
||||
user = self.get_pending_user()
|
||||
if not user.is_authenticated:
|
||||
message = _("No Pending User.")
|
||||
messages.error(request, message)
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid()
|
||||
return self.executor.stage_invalid(message)
|
||||
logout(self.request)
|
||||
user.delete()
|
||||
self.logger.debug("Deleted user", user=user)
|
||||
|
@ -81,9 +81,8 @@ class UserLoginStageView(ChallengeStageView):
|
||||
"""Attach the currently pending user to the current session"""
|
||||
if PLAN_CONTEXT_PENDING_USER not in self.executor.plan.context:
|
||||
message = _("No Pending user to login.")
|
||||
messages.error(request, message)
|
||||
self.logger.debug(message)
|
||||
return self.executor.stage_invalid()
|
||||
return self.executor.stage_invalid(message)
|
||||
backend = self.executor.plan.context.get(
|
||||
PLAN_CONTEXT_AUTHENTICATION_BACKEND, BACKEND_INBUILT
|
||||
)
|
||||
|
120
schema.yml
120
schema.yml
@ -39432,6 +39432,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-access-denied
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -39546,6 +39550,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-source-oauth-apple
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -39873,6 +39881,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-duo
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40032,6 +40044,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-email
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40288,6 +40304,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-sms
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40451,6 +40471,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-static
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40572,6 +40596,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-totp
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40799,6 +40827,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-validate
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -40852,6 +40884,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-authenticator-webauthn
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -41001,6 +41037,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-autosubmit
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -41264,6 +41304,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-captcha
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -41663,6 +41707,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-consent
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -42464,6 +42512,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-dummy
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -42666,6 +42718,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-email
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -43593,6 +43649,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-flow-error
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -43921,6 +43981,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-frame
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -44731,6 +44795,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-identification
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -46324,6 +46392,22 @@ components:
|
||||
- strict
|
||||
- regex
|
||||
type: string
|
||||
Message:
|
||||
type: object
|
||||
description: Base serializer class which doesn't implement create/update methods
|
||||
properties:
|
||||
message:
|
||||
type: string
|
||||
level:
|
||||
type: string
|
||||
tags:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
required:
|
||||
- level
|
||||
- message
|
||||
- tags
|
||||
Metadata:
|
||||
type: object
|
||||
description: Serializer for blueprint metadata
|
||||
@ -47209,6 +47293,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-provider-oauth2-device-code
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -47237,6 +47325,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-provider-oauth2-device-code-finish
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -49387,6 +49479,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-password
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -52942,6 +53038,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-source-plex
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -53467,6 +53567,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-prompt
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -54663,6 +54767,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-redirect
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -56528,6 +56636,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-session-end
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -56662,6 +56774,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: xak-flow-shell
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
@ -57943,6 +58059,10 @@ components:
|
||||
component:
|
||||
type: string
|
||||
default: ak-stage-user-login
|
||||
messages:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Message'
|
||||
response_errors:
|
||||
type: object
|
||||
additionalProperties:
|
||||
|
@ -5,12 +5,13 @@ import {
|
||||
TITLE_DEFAULT,
|
||||
} from "@goauthentik/common/constants";
|
||||
import { globalAK } from "@goauthentik/common/global";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import { configureSentry } from "@goauthentik/common/sentry";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import { WebsocketClient } from "@goauthentik/common/ws";
|
||||
import { Interface } from "@goauthentik/elements/Interface";
|
||||
import "@goauthentik/elements/LoadingOverlay";
|
||||
import "@goauthentik/elements/ak-locale-context";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand";
|
||||
import { themeImage } from "@goauthentik/elements/utils/images";
|
||||
import "@goauthentik/flow/components/ak-brand-footer";
|
||||
@ -44,6 +45,7 @@ import {
|
||||
FlowErrorChallenge,
|
||||
FlowLayoutEnum,
|
||||
FlowsApi,
|
||||
Message,
|
||||
ResponseError,
|
||||
ShellChallenge,
|
||||
UiThemeEnum,
|
||||
@ -83,8 +85,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
@state()
|
||||
flowInfo?: ContextualFlowInfo;
|
||||
|
||||
ws: WebsocketClient;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFLogin, PFDrawer, PFButton, PFTitle, PFList, PFBackgroundImage].concat(css`
|
||||
:host {
|
||||
@ -174,7 +174,6 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ws = new WebsocketClient();
|
||||
const inspector = new URL(window.location.toString()).searchParams.get("inspector");
|
||||
if (inspector === "" || inspector === "open") {
|
||||
this.inspectorOpen = true;
|
||||
@ -233,6 +232,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
if (this.challenge.flowInfo) {
|
||||
this.flowInfo = this.challenge.flowInfo;
|
||||
}
|
||||
this.showMessages(this.challenge.messages);
|
||||
return !this.challenge.responseErrors;
|
||||
} catch (exc: unknown) {
|
||||
this.errorMessage(exc as Error | ResponseError | FetchError);
|
||||
@ -265,6 +265,7 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
if (this.challenge.flowInfo) {
|
||||
this.flowInfo = this.challenge.flowInfo;
|
||||
}
|
||||
this.showMessages(this.challenge.messages);
|
||||
} catch (exc: unknown) {
|
||||
// Catch JSON or Update errors
|
||||
this.errorMessage(exc as Error | ResponseError | FetchError);
|
||||
@ -273,6 +274,15 @@ export class FlowExecutor extends Interface implements StageHost {
|
||||
}
|
||||
}
|
||||
|
||||
showMessages(messages: Array<Message> | undefined) {
|
||||
for (const message of (messages ??= [])) {
|
||||
showMessage({
|
||||
level: message.level as MessageLevel,
|
||||
message: message.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async errorMessage(error: Error | ResponseError | FetchError): Promise<void> {
|
||||
let body = "";
|
||||
if (error instanceof FetchError) {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import "@goauthentik/elements/messages/MessageContainer";
|
||||
import "@goauthentik/flow/FlowExecutor";
|
||||
// Statically import some stages to speed up load speed
|
||||
import "@goauthentik/flow/stages/access_denied/AccessDeniedStage";
|
||||
|
Reference in New Issue
Block a user