*: backport CVE-2022-46145 fix
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
17ee076f3d
commit
e1a6dede54
|
@ -71,6 +71,7 @@ class FlowSerializer(ModelSerializer):
|
||||||
"export_url",
|
"export_url",
|
||||||
"layout",
|
"layout",
|
||||||
"denied_action",
|
"denied_action",
|
||||||
|
"authentication",
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"background": {"read_only": True},
|
"background": {"read_only": True},
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
"""flow exceptions"""
|
"""flow exceptions"""
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from authentik.lib.sentry import SentryIgnoredException
|
from authentik.lib.sentry import SentryIgnoredException
|
||||||
|
@ -6,15 +8,15 @@ from authentik.policies.types import PolicyResult
|
||||||
|
|
||||||
|
|
||||||
class FlowNonApplicableException(SentryIgnoredException):
|
class FlowNonApplicableException(SentryIgnoredException):
|
||||||
"""Flow does not apply to current user (denied by policy)."""
|
"""Flow does not apply to current user (denied by policy, or otherwise)."""
|
||||||
|
|
||||||
policy_result: PolicyResult
|
policy_result: Optional[PolicyResult] = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def messages(self) -> str:
|
def messages(self) -> str:
|
||||||
"""Get messages from policy result, fallback to generic reason"""
|
"""Get messages from policy result, fallback to generic reason"""
|
||||||
if len(self.policy_result.messages) < 1:
|
if not self.policy_result or len(self.policy_result.messages) < 1:
|
||||||
return _("Flow does not apply to current user (denied by policy).")
|
return _("Flow does not apply to current user.")
|
||||||
return "\n".join(self.policy_result.messages)
|
return "\n".join(self.policy_result.messages)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Generated by Django 4.1.3 on 2022-11-30 09:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_flows", "0023_flow_denied_action"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="flow",
|
||||||
|
name="authentication",
|
||||||
|
field=models.TextField(
|
||||||
|
choices=[
|
||||||
|
("none", "None"),
|
||||||
|
("require_authenticated", "Require Authenticated"),
|
||||||
|
("require_unauthenticated", "Require Unauthenticated"),
|
||||||
|
("require_superuser", "Require Superuser"),
|
||||||
|
],
|
||||||
|
default="none",
|
||||||
|
help_text="Required level of authentication and authorization to access a flow.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -23,6 +23,15 @@ if TYPE_CHECKING:
|
||||||
LOGGER = get_logger()
|
LOGGER = get_logger()
|
||||||
|
|
||||||
|
|
||||||
|
class FlowAuthenticationRequirement(models.TextChoices):
|
||||||
|
"""Required level of authentication and authorization to access a flow"""
|
||||||
|
|
||||||
|
NONE = "none"
|
||||||
|
REQUIRE_AUTHENTICATED = "require_authenticated"
|
||||||
|
REQUIRE_UNAUTHENTICATED = "require_unauthenticated"
|
||||||
|
REQUIRE_SUPERUSER = "require_superuser"
|
||||||
|
|
||||||
|
|
||||||
class NotConfiguredAction(models.TextChoices):
|
class NotConfiguredAction(models.TextChoices):
|
||||||
"""Decides how the FlowExecutor should proceed when a stage isn't configured"""
|
"""Decides how the FlowExecutor should proceed when a stage isn't configured"""
|
||||||
|
|
||||||
|
@ -152,6 +161,12 @@ class Flow(SerializerModel, PolicyBindingModel):
|
||||||
help_text=_("Configure what should happen when a flow denies access to a user."),
|
help_text=_("Configure what should happen when a flow denies access to a user."),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
authentication = models.TextField(
|
||||||
|
choices=FlowAuthenticationRequirement.choices,
|
||||||
|
default=FlowAuthenticationRequirement.NONE,
|
||||||
|
help_text=_("Required level of authentication and authorization to access a flow."),
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def background_url(self) -> str:
|
def background_url(self) -> str:
|
||||||
"""Get the URL to the background image. If the name is /static or starts with http
|
"""Get the URL to the background image. If the name is /static or starts with http
|
||||||
|
|
|
@ -13,7 +13,14 @@ from authentik.events.models import cleanse_dict
|
||||||
from authentik.flows.apps import HIST_FLOWS_PLAN_TIME
|
from authentik.flows.apps import HIST_FLOWS_PLAN_TIME
|
||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||||
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage, in_memory_stage
|
from authentik.flows.models import (
|
||||||
|
Flow,
|
||||||
|
FlowAuthenticationRequirement,
|
||||||
|
FlowDesignation,
|
||||||
|
FlowStageBinding,
|
||||||
|
Stage,
|
||||||
|
in_memory_stage,
|
||||||
|
)
|
||||||
from authentik.lib.config import CONFIG
|
from authentik.lib.config import CONFIG
|
||||||
from authentik.policies.engine import PolicyEngine
|
from authentik.policies.engine import PolicyEngine
|
||||||
|
|
||||||
|
@ -117,11 +124,30 @@ class FlowPlanner:
|
||||||
self.flow = flow
|
self.flow = flow
|
||||||
self._logger = get_logger().bind(flow_slug=flow.slug)
|
self._logger = get_logger().bind(flow_slug=flow.slug)
|
||||||
|
|
||||||
|
def _check_authentication(self, request: HttpRequest):
|
||||||
|
"""Check the flow's authentication level is matched by `request`"""
|
||||||
|
if (
|
||||||
|
self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_AUTHENTICATED
|
||||||
|
and not request.user.is_authenticated
|
||||||
|
):
|
||||||
|
raise FlowNonApplicableException()
|
||||||
|
if (
|
||||||
|
self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_UNAUTHENTICATED
|
||||||
|
and request.user.is_authenticated
|
||||||
|
):
|
||||||
|
raise FlowNonApplicableException()
|
||||||
|
if (
|
||||||
|
self.flow.authentication == FlowAuthenticationRequirement.REQUIRE_SUPERUSER
|
||||||
|
and not request.user.is_superuser
|
||||||
|
):
|
||||||
|
raise FlowNonApplicableException()
|
||||||
|
|
||||||
def plan(
|
def plan(
|
||||||
self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None
|
self, request: HttpRequest, default_context: Optional[dict[str, Any]] = None
|
||||||
) -> FlowPlan:
|
) -> FlowPlan:
|
||||||
"""Check each of the flows' policies, check policies for each stage with PolicyBinding
|
"""Check each of the flows' policies, check policies for each stage with PolicyBinding
|
||||||
and return ordered list"""
|
and return ordered list"""
|
||||||
|
self._check_authentication(request)
|
||||||
with Hub.current.start_span(
|
with Hub.current.start_span(
|
||||||
op="authentik.flow.planner.plan", description=self.flow.slug
|
op="authentik.flow.planner.plan", description=self.flow.slug
|
||||||
) as span:
|
) as span:
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
"""flow planner tests"""
|
"""flow planner tests"""
|
||||||
from unittest.mock import MagicMock, Mock, PropertyMock, patch
|
from unittest.mock import MagicMock, Mock, PropertyMock, patch
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser
|
||||||
from django.contrib.sessions.middleware import SessionMiddleware
|
from django.contrib.sessions.middleware import SessionMiddleware
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.test import RequestFactory, TestCase
|
from django.test import RequestFactory, TestCase
|
||||||
|
@ -8,10 +9,10 @@ from django.urls import reverse
|
||||||
from guardian.shortcuts import get_anonymous_user
|
from guardian.shortcuts import get_anonymous_user
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.core.tests.utils import create_test_flow
|
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||||
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
from authentik.flows.exceptions import EmptyFlowException, FlowNonApplicableException
|
||||||
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
from authentik.flows.markers import ReevaluateMarker, StageMarker
|
||||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
from authentik.flows.models import FlowAuthenticationRequirement, FlowDesignation, FlowStageBinding
|
||||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
|
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlanner, cache_key
|
||||||
from authentik.lib.tests.utils import dummy_get_response
|
from authentik.lib.tests.utils import dummy_get_response
|
||||||
from authentik.policies.dummy.models import DummyPolicy
|
from authentik.policies.dummy.models import DummyPolicy
|
||||||
|
@ -43,6 +44,30 @@ class TestFlowPlanner(TestCase):
|
||||||
planner = FlowPlanner(flow)
|
planner = FlowPlanner(flow)
|
||||||
planner.plan(request)
|
planner.plan(request)
|
||||||
|
|
||||||
|
def test_authentication(self):
|
||||||
|
"""Test flow authentication"""
|
||||||
|
flow = create_test_flow()
|
||||||
|
flow.authentication = FlowAuthenticationRequirement.NONE
|
||||||
|
request = self.request_factory.get(
|
||||||
|
reverse("authentik_api:flow-executor", kwargs={"flow_slug": flow.slug}),
|
||||||
|
)
|
||||||
|
request.user = AnonymousUser()
|
||||||
|
planner = FlowPlanner(flow)
|
||||||
|
planner.allow_empty_flows = True
|
||||||
|
planner.plan(request)
|
||||||
|
|
||||||
|
with self.assertRaises(FlowNonApplicableException):
|
||||||
|
flow.authentication = FlowAuthenticationRequirement.REQUIRE_AUTHENTICATED
|
||||||
|
FlowPlanner(flow).plan(request)
|
||||||
|
with self.assertRaises(FlowNonApplicableException):
|
||||||
|
flow.authentication = FlowAuthenticationRequirement.REQUIRE_SUPERUSER
|
||||||
|
FlowPlanner(flow).plan(request)
|
||||||
|
|
||||||
|
request.user = create_test_admin_user()
|
||||||
|
planner = FlowPlanner(flow)
|
||||||
|
planner.allow_empty_flows = True
|
||||||
|
planner.plan(request)
|
||||||
|
|
||||||
@patch(
|
@patch(
|
||||||
"authentik.policies.engine.PolicyEngine.result",
|
"authentik.policies.engine.PolicyEngine.result",
|
||||||
POLICY_RETURN_FALSE,
|
POLICY_RETURN_FALSE,
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: Change Password
|
name: Change Password
|
||||||
title: Change password
|
title: Change password
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-password-change
|
slug: default-password-change
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -11,6 +11,7 @@ entries:
|
||||||
designation: authentication
|
designation: authentication
|
||||||
name: Welcome to authentik!
|
name: Welcome to authentik!
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
|
authentication: require_unauthenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-authentication-flow
|
slug: default-authentication-flow
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: invalidation
|
designation: invalidation
|
||||||
name: Logout
|
name: Logout
|
||||||
title: Default Invalidation Flow
|
title: Default Invalidation Flow
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-invalidation-flow
|
slug: default-invalidation-flow
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: default-authenticator-static-setup
|
name: default-authenticator-static-setup
|
||||||
title: Setup Static OTP Tokens
|
title: Setup Static OTP Tokens
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-authenticator-static-setup
|
slug: default-authenticator-static-setup
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: default-authenticator-totp-setup
|
name: default-authenticator-totp-setup
|
||||||
title: Setup Two-Factor authentication
|
title: Setup Two-Factor authentication
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-authenticator-totp-setup
|
slug: default-authenticator-totp-setup
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: default-authenticator-webauthn-setup
|
name: default-authenticator-webauthn-setup
|
||||||
title: Setup WebAuthn
|
title: Setup WebAuthn
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-authenticator-webauthn-setup
|
slug: default-authenticator-webauthn-setup
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: authorization
|
designation: authorization
|
||||||
name: Authorize Application
|
name: Authorize Application
|
||||||
title: Redirecting to %(app)s
|
title: Redirecting to %(app)s
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-provider-authorization-explicit-consent
|
slug: default-provider-authorization-explicit-consent
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: authorization
|
designation: authorization
|
||||||
name: Authorize Application
|
name: Authorize Application
|
||||||
title: Redirecting to %(app)s
|
title: Redirecting to %(app)s
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-provider-authorization-implicit-consent
|
slug: default-provider-authorization-implicit-consent
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: authentication
|
designation: authentication
|
||||||
name: Welcome to authentik!
|
name: Welcome to authentik!
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
|
authentication: require_unauthenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-source-authentication
|
slug: default-source-authentication
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: enrollment
|
designation: enrollment
|
||||||
name: Welcome to authentik! Please select a username.
|
name: Welcome to authentik! Please select a username.
|
||||||
title: Welcome to authentik! Please select a username.
|
title: Welcome to authentik! Please select a username.
|
||||||
|
authentication: none
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-source-enrollment
|
slug: default-source-enrollment
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: Pre-Authentication
|
name: Pre-Authentication
|
||||||
title: Pre-authentication
|
title: Pre-authentication
|
||||||
|
authentication: none
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-source-pre-authentication
|
slug: default-source-pre-authentication
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -6,6 +6,7 @@ entries:
|
||||||
designation: stage_configuration
|
designation: stage_configuration
|
||||||
name: User settings
|
name: User settings
|
||||||
title: Update your info
|
title: Update your info
|
||||||
|
authentication: require_authenticated
|
||||||
identifiers:
|
identifiers:
|
||||||
slug: default-user-settings-flow
|
slug: default-user-settings-flow
|
||||||
model: authentik_flows.flow
|
model: authentik_flows.flow
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default enrollment Flow
|
name: Default enrollment Flow
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
designation: enrollment
|
designation: enrollment
|
||||||
|
authentication: require_unauthenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
field_key: username
|
field_key: username
|
||||||
label: Username
|
label: Username
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default enrollment Flow
|
name: Default enrollment Flow
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
designation: enrollment
|
designation: enrollment
|
||||||
|
authentication: require_unauthenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
field_key: username
|
field_key: username
|
||||||
label: Username
|
label: Username
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default Authentication Flow
|
name: Default Authentication Flow
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
designation: authentication
|
designation: authentication
|
||||||
|
authentication: require_unauthenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
name: test-not-app-password
|
name: test-not-app-password
|
||||||
id: test-not-app-password
|
id: test-not-app-password
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default Authentication Flow
|
name: Default Authentication Flow
|
||||||
title: Welcome to authentik!
|
title: Welcome to authentik!
|
||||||
designation: authentication
|
designation: authentication
|
||||||
|
authentication: require_unauthenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
name: default-authentication-login
|
name: default-authentication-login
|
||||||
id: default-authentication-login
|
id: default-authentication-login
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default recovery flow
|
name: Default recovery flow
|
||||||
title: Reset your password
|
title: Reset your password
|
||||||
designation: recovery
|
designation: recovery
|
||||||
|
authentication: require_unauthenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
field_key: password
|
field_key: password
|
||||||
label: Password
|
label: Password
|
||||||
|
|
|
@ -12,6 +12,7 @@ entries:
|
||||||
name: Default unenrollment flow
|
name: Default unenrollment flow
|
||||||
title: Delete your account
|
title: Delete your account
|
||||||
designation: unenrollment
|
designation: unenrollment
|
||||||
|
authentication: require_authenticated
|
||||||
- identifiers:
|
- identifiers:
|
||||||
name: default-unenrollment-user-delete
|
name: default-unenrollment-user-delete
|
||||||
id: default-unenrollment-user-delete
|
id: default-unenrollment-user-delete
|
||||||
|
|
22
schema.yml
22
schema.yml
|
@ -25269,6 +25269,13 @@ components:
|
||||||
- last_used
|
- last_used
|
||||||
- user
|
- user
|
||||||
- user_agent
|
- user_agent
|
||||||
|
AuthenticationEnum:
|
||||||
|
enum:
|
||||||
|
- none
|
||||||
|
- require_authenticated
|
||||||
|
- require_unauthenticated
|
||||||
|
- require_superuser
|
||||||
|
type: string
|
||||||
AuthenticatorAttachmentEnum:
|
AuthenticatorAttachmentEnum:
|
||||||
enum:
|
enum:
|
||||||
- platform
|
- platform
|
||||||
|
@ -27578,6 +27585,11 @@ components:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
description: Configure what should happen when a flow denies access to a
|
description: Configure what should happen when a flow denies access to a
|
||||||
user.
|
user.
|
||||||
|
authentication:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AuthenticationEnum'
|
||||||
|
description: Required level of authentication and authorization to access
|
||||||
|
a flow.
|
||||||
required:
|
required:
|
||||||
- background
|
- background
|
||||||
- cache_count
|
- cache_count
|
||||||
|
@ -27774,6 +27786,11 @@ components:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
description: Configure what should happen when a flow denies access to a
|
description: Configure what should happen when a flow denies access to a
|
||||||
user.
|
user.
|
||||||
|
authentication:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AuthenticationEnum'
|
||||||
|
description: Required level of authentication and authorization to access
|
||||||
|
a flow.
|
||||||
required:
|
required:
|
||||||
- designation
|
- designation
|
||||||
- name
|
- name
|
||||||
|
@ -33651,6 +33668,11 @@ components:
|
||||||
- $ref: '#/components/schemas/DeniedActionEnum'
|
- $ref: '#/components/schemas/DeniedActionEnum'
|
||||||
description: Configure what should happen when a flow denies access to a
|
description: Configure what should happen when a flow denies access to a
|
||||||
user.
|
user.
|
||||||
|
authentication:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/components/schemas/AuthenticationEnum'
|
||||||
|
description: Required level of authentication and authorization to access
|
||||||
|
a flow.
|
||||||
PatchedFlowStageBindingRequest:
|
PatchedFlowStageBindingRequest:
|
||||||
type: object
|
type: object
|
||||||
description: FlowStageBinding Serializer
|
description: FlowStageBinding Serializer
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
|
import { DesignationToLabel, LayoutToLabel } from "@goauthentik/admin/flows/utils";
|
||||||
|
import { AuthenticationEnum } from "@goauthentik/api/dist/models/AuthenticationEnum";
|
||||||
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
import { DEFAULT_CONFIG, config } from "@goauthentik/common/api/config";
|
||||||
import { first } from "@goauthentik/common/utils";
|
import { first } from "@goauthentik/common/utils";
|
||||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||||
|
@ -141,6 +142,37 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||||
</option>`;
|
</option>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderAuthentication(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<option
|
||||||
|
value=${AuthenticationEnum.None}
|
||||||
|
?selected=${this.instance?.authentication === AuthenticationEnum.None}
|
||||||
|
>
|
||||||
|
${t`No requirement`}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value=${AuthenticationEnum.RequireAuthenticated}
|
||||||
|
?selected=${this.instance?.authentication ===
|
||||||
|
AuthenticationEnum.RequireAuthenticated}
|
||||||
|
>
|
||||||
|
${t`Require authentication`}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value=${AuthenticationEnum.RequireUnauthenticated}
|
||||||
|
?selected=${this.instance?.authentication ===
|
||||||
|
AuthenticationEnum.RequireUnauthenticated}
|
||||||
|
>
|
||||||
|
${t`Require no authentication.`}
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
value=${AuthenticationEnum.RequireSuperuser}
|
||||||
|
?selected=${this.instance?.authentication === AuthenticationEnum.RequireSuperuser}
|
||||||
|
>
|
||||||
|
${t`Require superuser.`}
|
||||||
|
</option>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
renderLayout(): TemplateResult {
|
renderLayout(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<option
|
<option
|
||||||
|
@ -224,6 +256,18 @@ export class FlowForm extends ModelForm<Flow, string> {
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Authentication`}
|
||||||
|
?required=${true}
|
||||||
|
name="authentication"
|
||||||
|
>
|
||||||
|
<select class="pf-c-form-control">
|
||||||
|
${this.renderAuthentication()}
|
||||||
|
</select>
|
||||||
|
<p class="pf-c-form__helper-text">
|
||||||
|
${t`Required authentication level for this flow.`}
|
||||||
|
</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal
|
<ak-form-element-horizontal
|
||||||
label=${t`Designation`}
|
label=${t`Designation`}
|
||||||
?required=${true}
|
?required=${true}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# CVE-2022-46145
|
||||||
|
|
||||||
|
## Unauthorized user creation and potential account takeover
|
||||||
|
|
||||||
|
### Impact
|
||||||
|
|
||||||
|
With the default flows, unauthenticated users can create new accounts in authentik. If a flow exists that allows for email-verified password recovery, this can be used to overwrite the email address of admin accounts and take over their accounts
|
||||||
|
|
||||||
|
### Patches
|
||||||
|
|
||||||
|
authentik 2022.11.2 and 2022.10.2 fix this issue, for other versions the workaround can be used.
|
||||||
|
|
||||||
|
### Workarounds
|
||||||
|
|
||||||
|
A policy can be created and bound to the `default-user-settings-flow` flow with the following contents
|
||||||
|
|
||||||
|
```python
|
||||||
|
return request.user.is_authenticated
|
||||||
|
```
|
|
@ -290,7 +290,7 @@ module.exports = {
|
||||||
title: "Security",
|
title: "Security",
|
||||||
slug: "security",
|
slug: "security",
|
||||||
},
|
},
|
||||||
items: ["security/policy"],
|
items: ["security/policy", "security/CVE-2022-46145"],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
Reference in New Issue