stages/identification: make shown sources configurable

closes #918

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens Langhammer 2021-05-25 16:26:39 +02:00
parent 250e23408e
commit c4453f38a2
8 changed files with 120 additions and 5 deletions

View File

@ -17,6 +17,7 @@ class IdentificationStageSerializer(StageSerializer):
"show_matched_user", "show_matched_user",
"enrollment_flow", "enrollment_flow",
"recovery_flow", "recovery_flow",
"sources",
] ]

View File

@ -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),
]

View File

@ -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

View File

@ -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

View File

@ -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(

View File

@ -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

View File

@ -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)}>

View File

@ -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.