stages/identification: make shown sources configurable
closes #918 Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
parent
250e23408e
commit
c4453f38a2
|
@ -17,6 +17,7 @@ class IdentificationStageSerializer(StageSerializer):
|
||||||
"show_matched_user",
|
"show_matched_user",
|
||||||
"enrollment_flow",
|
"enrollment_flow",
|
||||||
"recovery_flow",
|
"recovery_flow",
|
||||||
|
"sources",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
# Generated by Django 3.2.3 on 2021-05-25 14:01
|
||||||
|
|
||||||
|
from django.apps.registry import Apps
|
||||||
|
from django.db import migrations, models
|
||||||
|
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||||
|
|
||||||
|
|
||||||
|
def assign_sources(apps: Apps, schema_editor: BaseDatabaseSchemaEditor):
|
||||||
|
db_alias = schema_editor.connection.alias
|
||||||
|
|
||||||
|
IdentificationStage = apps.get_model(
|
||||||
|
"authentik_stages_identification", "identificationstage"
|
||||||
|
)
|
||||||
|
Source = apps.get_model("authentik_core", "source")
|
||||||
|
|
||||||
|
sources = Source.objects.all()
|
||||||
|
for stage in IdentificationStage.objects.all().using(db_alias):
|
||||||
|
stage.sources.set(sources)
|
||||||
|
stage.save()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("authentik_core", "0021_alter_application_slug"),
|
||||||
|
(
|
||||||
|
"authentik_stages_identification",
|
||||||
|
"0008_alter_identificationstage_user_fields",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="identificationstage",
|
||||||
|
name="sources",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
default=list,
|
||||||
|
help_text="Specify which sources should be shown.",
|
||||||
|
to="authentik_core.Source",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RunPython(assign_sources),
|
||||||
|
]
|
|
@ -7,6 +7,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views import View
|
from django.views import View
|
||||||
from rest_framework.serializers import BaseSerializer
|
from rest_framework.serializers import BaseSerializer
|
||||||
|
|
||||||
|
from authentik.core.models import Source
|
||||||
from authentik.flows.models import Flow, Stage
|
from authentik.flows.models import Flow, Stage
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +72,10 @@ class IdentificationStage(Stage):
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
sources = models.ManyToManyField(
|
||||||
|
Source, default=list, help_text=_("Specify which sources should be shown.")
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serializer(self) -> BaseSerializer:
|
def serializer(self) -> BaseSerializer:
|
||||||
from authentik.stages.identification.api import IdentificationStageSerializer
|
from authentik.stages.identification.api import IdentificationStageSerializer
|
||||||
|
|
|
@ -111,7 +111,9 @@ class IdentificationStageView(ChallengeStageView):
|
||||||
# Check all enabled source, add them if they have a UI Login button.
|
# Check all enabled source, add them if they have a UI Login button.
|
||||||
ui_sources = []
|
ui_sources = []
|
||||||
sources: list[Source] = (
|
sources: list[Source] = (
|
||||||
Source.objects.filter(enabled=True).order_by("name").select_subclasses()
|
current_stage.sources.filter(enabled=True)
|
||||||
|
.order_by("name")
|
||||||
|
.select_subclasses()
|
||||||
)
|
)
|
||||||
for source in sources:
|
for source in sources:
|
||||||
ui_login_button = source.ui_login_button
|
ui_login_button = source.ui_login_button
|
||||||
|
|
|
@ -18,6 +18,9 @@ class TestIdentificationStage(TestCase):
|
||||||
self.user = User.objects.create(username="unittest", email="test@beryju.org")
|
self.user = User.objects.create(username="unittest", email="test@beryju.org")
|
||||||
self.client = Client()
|
self.client = Client()
|
||||||
|
|
||||||
|
# OAuthSource for the login view
|
||||||
|
source = OAuthSource.objects.create(name="test", slug="test")
|
||||||
|
|
||||||
self.flow = Flow.objects.create(
|
self.flow = Flow.objects.create(
|
||||||
name="test-identification",
|
name="test-identification",
|
||||||
slug="test-identification",
|
slug="test-identification",
|
||||||
|
@ -27,15 +30,14 @@ class TestIdentificationStage(TestCase):
|
||||||
name="identification",
|
name="identification",
|
||||||
user_fields=[UserFields.E_MAIL],
|
user_fields=[UserFields.E_MAIL],
|
||||||
)
|
)
|
||||||
|
self.stage.sources.set([source])
|
||||||
|
self.stage.save()
|
||||||
FlowStageBinding.objects.create(
|
FlowStageBinding.objects.create(
|
||||||
target=self.flow,
|
target=self.flow,
|
||||||
stage=self.stage,
|
stage=self.stage,
|
||||||
order=0,
|
order=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
# OAuthSource for the login view
|
|
||||||
OAuthSource.objects.create(name="test", slug="test")
|
|
||||||
|
|
||||||
def test_valid_render(self):
|
def test_valid_render(self):
|
||||||
"""Test that View renders correctly"""
|
"""Test that View renders correctly"""
|
||||||
response = self.client.get(
|
response = self.client.get(
|
||||||
|
|
18
schema.yml
18
schema.yml
|
@ -17489,6 +17489,12 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optional recovery flow, which is linked at the bottom of the
|
description: Optional recovery flow, which is linked at the bottom of the
|
||||||
page.
|
page.
|
||||||
|
sources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: Specify which sources should be shown.
|
||||||
required:
|
required:
|
||||||
- component
|
- component
|
||||||
- name
|
- name
|
||||||
|
@ -17531,6 +17537,12 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optional recovery flow, which is linked at the bottom of the
|
description: Optional recovery flow, which is linked at the bottom of the
|
||||||
page.
|
page.
|
||||||
|
sources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: Specify which sources should be shown.
|
||||||
required:
|
required:
|
||||||
- name
|
- name
|
||||||
IntentEnum:
|
IntentEnum:
|
||||||
|
@ -21952,6 +21964,12 @@ components:
|
||||||
nullable: true
|
nullable: true
|
||||||
description: Optional recovery flow, which is linked at the bottom of the
|
description: Optional recovery flow, which is linked at the bottom of the
|
||||||
page.
|
page.
|
||||||
|
sources:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
|
format: uuid
|
||||||
|
description: Specify which sources should be shown.
|
||||||
PatchedInvitationRequest:
|
PatchedInvitationRequest:
|
||||||
type: object
|
type: object
|
||||||
description: Invitation Serializer
|
description: Invitation Serializer
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { FlowsApi, IdentificationStage, UserFieldsEnum, StagesApi, FlowsInstancesListDesignationEnum } from "authentik-api";
|
import { FlowsApi, IdentificationStage, UserFieldsEnum, StagesApi, FlowsInstancesListDesignationEnum, SourcesApi } from "authentik-api";
|
||||||
import { t } from "@lingui/macro";
|
import { t } from "@lingui/macro";
|
||||||
import { customElement } from "lit-element";
|
import { customElement } from "lit-element";
|
||||||
import { html, TemplateResult } from "lit-html";
|
import { html, TemplateResult } from "lit-html";
|
||||||
|
@ -85,6 +85,23 @@ export class IdentificationStageForm extends ModelForm<IdentificationStage, stri
|
||||||
</div>
|
</div>
|
||||||
<p class="pf-c-form__helper-text">${t`When enabled, user fields are matched regardless of their casing.`}</p>
|
<p class="pf-c-form__helper-text">${t`When enabled, user fields are matched regardless of their casing.`}</p>
|
||||||
</ak-form-element-horizontal>
|
</ak-form-element-horizontal>
|
||||||
|
<ak-form-element-horizontal
|
||||||
|
label=${t`Sources`}
|
||||||
|
?required=${true}
|
||||||
|
name="sources">
|
||||||
|
<select name="users" class="pf-c-form-control" multiple>
|
||||||
|
${until(new SourcesApi(DEFAULT_CONFIG).sourcesAllList({}).then(sources => {
|
||||||
|
return sources.results.map(source => {
|
||||||
|
const selected = Array.from(this.instance?.sources || []).some(su => {
|
||||||
|
return su == source.pk;
|
||||||
|
});
|
||||||
|
return html`<option value=${ifDefined(source.pk)} ?selected=${selected}>${source.name}</option>`;
|
||||||
|
});
|
||||||
|
}), html`<option>${t`Loading...`}</option>`)}
|
||||||
|
</select>
|
||||||
|
<p class="pf-c-form__helper-text">${t`Select sources should be shown for users to authenticate with. This only affects web-based sources, not LDAP.`}</p>
|
||||||
|
<p class="pf-c-form__helper-text">${t`Hold control/command to select multiple items.`}</p>
|
||||||
|
</ak-form-element-horizontal>
|
||||||
<ak-form-element-horizontal name="showMatchedUser">
|
<ak-form-element-horizontal name="showMatchedUser">
|
||||||
<div class="pf-c-check">
|
<div class="pf-c-check">
|
||||||
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.showMatchedUser, true)}>
|
<input type="checkbox" class="pf-c-check__input" ?checked=${first(this.instance?.showMatchedUser, true)}>
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
title: Next
|
||||||
|
---
|
||||||
|
|
||||||
|
## Headline Changes
|
||||||
|
|
||||||
|
- Duo two-factor support
|
||||||
|
|
||||||
|
You can now add the new `authenticator_duo` stage to configure Duo authenticators. Duo has also been added as device class to the `authenticator_validation` stage.
|
||||||
|
|
||||||
|
Currently, only Duo push notifications are supported. Because no additional input is required, Duo also works with the LDAP Outpost.
|
||||||
|
|
||||||
|
## Minor changes
|
||||||
|
|
||||||
|
- You can now specify which sources should be shown on an Identification stage.
|
||||||
|
|
||||||
|
## Upgrading
|
||||||
|
|
||||||
|
This release does not introduce any new requirements.
|
||||||
|
|
||||||
|
### docker-compose
|
||||||
|
|
||||||
|
Download the docker-compose file for 2021.6 from [here](https://raw.githubusercontent.com/goauthentik/authentik/version-2021.6/docker-compose.yml). Afterwards, simply run `docker-compose up -d`.
|
||||||
|
|
||||||
|
### Kubernetes
|
||||||
|
|
||||||
|
Upgrade to the latest chart version to get the new images.
|
Reference in New Issue