Merge branch 'master' into version-2021.3

# Conflicts:
#	web/src/constants.ts
This commit is contained in:
Jens Langhammer 2021-03-02 21:39:30 +01:00
commit 0018fbacd3
59 changed files with 310 additions and 62 deletions

View File

@ -1,4 +1,6 @@
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="250" alt="authentik logo">
<p align="center">
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="150" alt="authentik logo">
</p>
---
@ -22,8 +24,10 @@ For bigger setups, there is a Helm Chart in the `helm/` directory. This is docum
## Screenshots
![](https://goauthentik.io/img/screen_apps.png)
![](https://goauthentik.io/img/screen_admin.png)
Light | Dark
--- | ---
![](https://goauthentik.io/img/screen_apps_light.png) | ![](https://goauthentik.io/img/screen_apps_dark.png)
![](https://goauthentik.io/img/screen_admin_light.png) | ![](https://goauthentik.io/img/screen_admin_dark.png)
## Development

View File

@ -4,9 +4,9 @@
| Version | Supported |
| ---------- | ------------------ |
| 0.13.x | :white_check_mark: |
| 0.14.x | :white_check_mark: |
| 2021.1.x | :white_check_mark: |
| 2021.2.x | :white_check_mark: |
| 2021.3.x | :white_check_mark: |
## Reporting a Vulnerability

View File

@ -27,7 +27,9 @@
</div>
</section>
<footer class="pf-c-modal-box__footer">
<input class="pf-c-button pf-m-primary" type="submit" form="main-form" value="{% block action %}{% endblock %}" />
<ak-spinner-button form="main-form">
{% block action %}{% endblock %}
</ak-spinner-button>&nbsp;
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
</footer>
{% endblock %}

View File

@ -6,6 +6,7 @@ from rest_framework.response import Response
class Pagination(pagination.PageNumberPagination):
"""Pagination which includes total pages and current page"""
page_query_param = "page"
page_size_query_param = "page_size"
def get_paginated_response(self, data):

View File

@ -90,7 +90,7 @@ class UserManager(DjangoUserManager):
class User(GuardianUserMixin, AbstractUser):
"""Custom User model to allow easier adding o f user-based settings"""
"""Custom User model to allow easier adding of user-based settings"""
uuid = models.UUIDField(default=uuid4, editable=False)
name = models.TextField(help_text=_("User's display name."))

View File

@ -1,12 +0,0 @@
{% extends "base/skeleton.html" %}
{% load i18n %}
{% block body %}
<ak-message-container></ak-message-container>
<div class="pf-c-page">
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">{% trans 'Skip to content' %}</a>
{% block page_content %}
{% endblock %}
</div>
{% endblock %}

View File

@ -1,4 +1,4 @@
{% extends 'base/page.html' %}
{% extends 'base/skeleton.html' %}
{% load i18n %}
{% load authentik_utils %}

View File

@ -7,7 +7,6 @@ from django.contrib.auth.mixins import (
)
from django.contrib.messages.views import SuccessMessageMixin
from django.http.response import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import UpdateView
from django.views.generic.base import TemplateView
@ -35,7 +34,7 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
form_class = UserDetailForm
success_message = _("Successfully updated user.")
success_url = reverse_lazy("authentik_core:user-details")
success_url = "/"
def get_object(self):
return self.request.user
@ -62,7 +61,7 @@ class TokenCreateView(
permission_required = "authentik_core.add_token"
template_name = "generic/create.html"
success_url = reverse_lazy("authentik_core:user-tokens")
success_url = "/"
success_message = _("Successfully created Token")
def form_valid(self, form: UserTokenForm) -> HttpResponse:
@ -80,7 +79,7 @@ class TokenUpdateView(
form_class = UserTokenForm
permission_required = "authentik_core.change_token"
template_name = "generic/update.html"
success_url = reverse_lazy("authentik_core:user-tokens")
success_url = "/"
success_message = _("Successfully updated Token")
def get_object(self) -> Token:
@ -100,7 +99,7 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
model = Token
permission_required = "authentik_core.delete_token"
template_name = "generic/delete.html"
success_url = reverse_lazy("authentik_core:user-tokens")
success_url = "/"
success_message = _("Successfully deleted Token")
def get_object(self) -> Token:

View File

@ -2,8 +2,8 @@
from urllib.parse import urlparse
from django.http import HttpResponse
from django.shortcuts import redirect, reverse
from django.urls import NoReverseMatch
from django.shortcuts import redirect
from django.urls import NoReverseMatch, reverse
from django.utils.http import urlencode
from structlog.stdlib import get_logger

View File

@ -0,0 +1,87 @@
# Generated by Django 3.1.7 on 2021-03-02 08:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("authentik_policies_event_matcher", "0010_auto_20210222_1821"),
]
operations = [
migrations.AlterField(
model_name="eventmatcherpolicy",
name="app",
field=models.TextField(
blank=True,
choices=[
("authentik.admin", "authentik Admin"),
("authentik.api", "authentik API"),
("authentik.events", "authentik Events"),
("authentik.crypto", "authentik Crypto"),
("authentik.flows", "authentik Flows"),
("authentik.outposts", "authentik Outpost"),
("authentik.lib", "authentik lib"),
("authentik.policies", "authentik Policies"),
("authentik.policies.dummy", "authentik Policies.Dummy"),
(
"authentik.policies.event_matcher",
"authentik Policies.Event Matcher",
),
("authentik.policies.expiry", "authentik Policies.Expiry"),
("authentik.policies.expression", "authentik Policies.Expression"),
(
"authentik.policies.group_membership",
"authentik Policies.Group Membership",
),
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
("authentik.policies.password", "authentik Policies.Password"),
("authentik.policies.reputation", "authentik Policies.Reputation"),
("authentik.providers.proxy", "authentik Providers.Proxy"),
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
("authentik.providers.saml", "authentik Providers.SAML"),
("authentik.recovery", "authentik Recovery"),
("authentik.sources.ldap", "authentik Sources.LDAP"),
("authentik.sources.oauth", "authentik Sources.OAuth"),
("authentik.sources.saml", "authentik Sources.SAML"),
(
"authentik.stages.authenticator_static",
"authentik Stages.Authenticator.Static",
),
(
"authentik.stages.authenticator_totp",
"authentik Stages.Authenticator.TOTP",
),
(
"authentik.stages.authenticator_validate",
"authentik Stages.Authenticator.Validate",
),
(
"authentik.stages.authenticator_webauthn",
"authentik Stages.Authenticator.WebAuthn",
),
("authentik.stages.captcha", "authentik Stages.Captcha"),
("authentik.stages.consent", "authentik Stages.Consent"),
("authentik.stages.deny", "authentik Stages.Deny"),
("authentik.stages.dummy", "authentik Stages.Dummy"),
("authentik.stages.email", "authentik Stages.Email"),
(
"authentik.stages.identification",
"authentik Stages.Identification",
),
("authentik.stages.invitation", "authentik Stages.User Invitation"),
("authentik.stages.password", "authentik Stages.Password"),
("authentik.stages.prompt", "authentik Stages.Prompt"),
("authentik.stages.user_delete", "authentik Stages.User Delete"),
("authentik.stages.user_login", "authentik Stages.User Login"),
("authentik.stages.user_logout", "authentik Stages.User Logout"),
("authentik.stages.user_write", "authentik Stages.User Write"),
("authentik.managed", "authentik Managed"),
("authentik.core", "authentik Core"),
],
default="",
help_text="Match events created by selected application. When left empty, all applications are matched.",
),
),
]

View File

@ -62,11 +62,15 @@ class PolicyAccessView(AccessMixin, View):
return self.handle_no_permission()
try:
self.resolve_provider_application()
except (Application.DoesNotExist, Provider.DoesNotExist):
return self.handle_no_permission_authenticated()
except (Application.DoesNotExist, Provider.DoesNotExist) as exc:
LOGGER.warning("failed to resolve application", exc=exc)
return self.handle_no_permission_authenticated(
PolicyResult(False, _("Failed to resolve application"))
)
# Check if user is unauthenticated, so we pass the application
# for the identification stage
if not request.user.is_authenticated:
LOGGER.warning("user not authenticated")
return self.handle_no_permission()
# Check permissions
result = self.user_has_access()

View File

@ -45,6 +45,7 @@ class OAuth2ProviderSetupURLs(Serializer):
token = ReadOnlyField()
user_info = ReadOnlyField()
provider_info = ReadOnlyField()
logout = ReadOnlyField()
def create(self, request: Request) -> Response:
raise NotImplementedError
@ -83,6 +84,7 @@ class OAuth2ProviderViewSet(ModelViewSet):
)
),
"provider_info": None,
"logout": None,
}
try:
data["provider_info"] = request.build_absolute_uri(
@ -91,6 +93,12 @@ class OAuth2ProviderViewSet(ModelViewSet):
kwargs={"application_slug": provider.application.slug},
)
)
data["logout"] = request.build_absolute_uri(
reverse(
"authentik_providers_oauth2:end-session",
kwargs={"application_slug": provider.application.slug},
)
)
except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member
pass
return Response(data)

View File

@ -1,7 +1,10 @@
"""Sync LDAP Users into authentik"""
from datetime import datetime
import ldap3
import ldap3.core.exceptions
from django.db.utils import IntegrityError
from pytz import UTC
from authentik.core.models import User
from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer
@ -53,11 +56,21 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
)
)
else:
if created:
ak_user.set_unusable_password()
ak_user.save()
self._logger.debug(
"Synced User", user=ak_user.username, created=created
)
user_count += 1
# pylint: disable=no-value-for-parameter
pwd_last_set = UTC.localize(
attributes.get("pwdLastSet", datetime.now())
)
if created or pwd_last_set >= ak_user.password_change_date:
self._logger.debug(
"Reset user's password",
user=ak_user.username,
created=created,
pwd_last_set=pwd_last_set,
)
ak_user.set_unusable_password()
ak_user.save()
return user_count

View File

@ -1,7 +1,10 @@
"""Webauthn stage forms"""
from django import forms
from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage
from authentik.stages.authenticator_webauthn.models import (
AuthenticateWebAuthnStage,
WebAuthnDevice,
)
class AuthenticateWebAuthnStageForm(forms.ModelForm):
@ -15,3 +18,16 @@ class AuthenticateWebAuthnStageForm(forms.ModelForm):
widgets = {
"name": forms.TextInput(),
}
class DeviceEditForm(forms.ModelForm):
"""Form to edit webauthn device"""
class Meta:
model = WebAuthnDevice
fields = ["name"]
widgets = {
"name": forms.TextInput(),
}

View File

@ -79,3 +79,8 @@ class WebAuthnDevice(Device):
def __str__(self):
return self.name or str(self.user)
class Meta:
verbose_name = _("WebAuthn Device")
verbose_name_plural = _("WebAuthn Devices")

View File

@ -17,6 +17,20 @@
Created {{ created_on }}
{% endblocktrans %}
</div>
<div class="pf-c-data-list__cell">
<ak-modal-button href="{% url 'authentik_stages_authenticator_webauthn:device-update' pk=device.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-primary">
{% trans 'Update' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
<ak-modal-button href="{% url 'authentik_stages_authenticator_webauthn:device-delete' pk=device.pk %}">
<ak-spinner-button slot="trigger" class="pf-m-danger">
{% trans 'Delete' %}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
</div>
</div>
</div>
</li>

View File

@ -1,10 +1,16 @@
"""WebAuthn urls"""
from django.urls import path
from authentik.stages.authenticator_webauthn.views import UserSettingsView
from authentik.stages.authenticator_webauthn.views import (
DeviceDeleteView,
DeviceUpdateView,
UserSettingsView,
)
urlpatterns = [
path(
"<uuid:stage_uuid>/settings/", UserSettingsView.as_view(), name="user-settings"
),
path("devices/<int:pk>/delete/", DeviceDeleteView.as_view(), name="device-delete"),
path("devices/<int:pk>/update/", DeviceUpdateView.as_view(), name="device-update"),
]

View File

@ -1,8 +1,13 @@
"""webauthn views"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.http.response import Http404
from django.shortcuts import get_object_or_404
from django.views.generic import TemplateView
from django.utils.translation import gettext as _
from django.views.generic import TemplateView, UpdateView
from authentik.admin.views.utils import DeleteMessageView
from authentik.stages.authenticator_webauthn.forms import DeviceEditForm
from authentik.stages.authenticator_webauthn.models import (
AuthenticateWebAuthnStage,
WebAuthnDevice,
@ -22,3 +27,34 @@ class UserSettingsView(LoginRequiredMixin, TemplateView):
)
kwargs["stage"] = stage
return kwargs
class DeviceUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Update device"""
model = WebAuthnDevice
form_class = DeviceEditForm
template_name = "generic/update.html"
success_url = "/"
success_message = _("Successfully updated Device")
def get_object(self) -> WebAuthnDevice:
device: WebAuthnDevice = super().get_object()
if device.user != self.request.user:
raise Http404
return device
class DeviceDeleteView(LoginRequiredMixin, DeleteMessageView):
"""Delete device"""
model = WebAuthnDevice
template_name = "generic/delete.html"
success_url = "/"
success_message = _("Successfully deleted Device")
def get_object(self) -> WebAuthnDevice:
device: WebAuthnDevice = super().get_object()
if device.user != self.request.user:
raise Http404
return device

View File

@ -1,9 +1,6 @@
{% extends "base/page.html" %}
{% load i18n %}
{% load authentik_utils %}
{% block body %}
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-title pf-m-md">
{% trans 'Reset your password' %}
@ -14,4 +11,3 @@
</a>
</div>
</div>
{% endblock %}

View File

@ -9657,7 +9657,7 @@ definitions:
readOnly: true
readOnly: true
user:
description: Custom User model to allow easier adding o f user-based settings
description: Custom User model to allow easier adding of user-based settings
required:
- password
- username
@ -10684,6 +10684,10 @@ definitions:
title: Provider info
type: string
readOnly: true
logout:
title: Logout
type: string
readOnly: true
ProxyProvider:
description: ProxyProvider Serializer
required:
@ -11867,7 +11871,7 @@ definitions:
description: Optional fixed data to enforce on user enrollment.
type: object
created_by:
description: Custom User model to allow easier adding o f user-based settings
description: Custom User model to allow easier adding of user-based settings
required:
- password
- username

View File

@ -4,7 +4,9 @@ import { NotFoundError, RequestError } from "./Error";
export const VERSION = "v2beta";
export interface QueryArguments {
[key: string]: number | string | boolean | null;
page?: number;
page_size?: number;
[key: string]: number | string | boolean | undefined | null;
}
export interface BaseInheritanceModel {

View File

@ -8,6 +8,7 @@ export interface OAuth2SetupURLs {
token: string;
user_info: string;
provider_info?: string;
logout?: string;
}

View File

@ -61,11 +61,6 @@ select[multiple] {
font-family: monospace;
}
/* Fix pre elements within alerts */
.pf-c-alert pre {
white-space: pre-wrap;
}
.pf-c-content h1 {
display: flex;
align-items: flex-start;
@ -85,6 +80,12 @@ select[multiple] {
z-index: auto !important;
}
/* ensure background on non-flow pages match */
.pf-c-background-image::before {
background-image: url("dist/assets/images/flow_background.jpg");
background-position: center;
}
/* Fix spacing between messages */
ak-message {
display: block;

View File

@ -4,3 +4,4 @@ export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2021.3.1-rc1";
export const PAGE_SIZE = 20;

View File

@ -59,9 +59,9 @@ export class SpinnerButton extends LitElement {
return;
}
if (this.form) {
// Because safari we can't just extend HTMLButtonElement, hence I have to implement
// these attributes by myself here, sigh...
document.querySelector<HTMLFormElement>(`#${this.form}`)?.submit();
// Since the form= attribute is only used within a modal button,
// we can assume the form is always two levels up
this.parentElement?.parentElement?.querySelector < HTMLFormElement>(`#${this.form}`)?.dispatchEvent(new Event("submit"));
}
this.setLoading();
}

View File

@ -40,9 +40,7 @@ export class NotificationDrawer extends LitElement {
}
renderItem(item: Notification): TemplateResult {
const delta = Date.now() - (parseInt(item.created, 10) * 1000);
// TODO: more flexible display, minutes and seconds
const age = `${Math.round(delta / 1000 / 3600)} Hours ago`;
const created = new Date(parseInt(item.created, 10) * 1000);
let level = "";
switch (item.severity) {
case "notice":
@ -76,7 +74,7 @@ export class NotificationDrawer extends LitElement {
</button>
</div>
<p class="pf-c-notification-drawer__list-item-description">${item.body}</p>
<small class="pf-c-notification-drawer__list-item-timestamp">${age}</small>
<small class="pf-c-notification-drawer__list-item-timestamp">${created.toLocaleString()}</small>
</li>`;
}

View File

@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/Dropdown";
import { Policy } from "../../api/Policies";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-bound-policies-list")
export class BoundPoliciesList extends Table<PolicyBinding> {
@ -22,6 +23,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
target: this.target || "",
ordering: "order",
page: page,
page_size: PAGE_SIZE,
});
}

View File

@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-application-list")
export class ApplicationListPage extends TablePage<Application> {
@ -30,6 +31,7 @@ export class ApplicationListPage extends TablePage<Application> {
return Application.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { CertificateKeyPair } from "../../api/CertificateKeyPair";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-crypto-certificatekeypair-list")
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
@ -32,6 +33,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
return CertificateKeyPair.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -2,6 +2,7 @@ import { gettext } from "django";
import { customElement, html, property, TemplateResult } from "lit-element";
import { AKResponse } from "../../api/Client";
import { Event } from "../../api/Events";
import { PAGE_SIZE } from "../../constants";
import { TableColumn } from "../../elements/table/Table";
import { TablePage } from "../../elements/table/TablePage";
import { time } from "../../utils";
@ -31,6 +32,7 @@ export class EventListPage extends TablePage<Event> {
return Event.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE * 3,
search: this.search || "",
});
}

View File

@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Rule } from "../../api/EventRules";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-event-rule-list")
export class RuleListPage extends TablePage<Rule> {
@ -33,6 +34,7 @@ export class RuleListPage extends TablePage<Rule> {
return Rule.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Transport } from "../../api/EventTransports";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-event-transport-list")
export class TransportListPage extends TablePage<Transport> {
@ -31,6 +32,7 @@ export class TransportListPage extends TablePage<Transport> {
return Transport.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -11,6 +11,7 @@ import "../../elements/buttons/Dropdown";
import "../../elements/policies/BoundPoliciesList";
import { FlowStageBinding, Stage } from "../../api/Flows";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-bound-stages-list")
export class BoundStagesList extends Table<FlowStageBinding> {
@ -24,6 +25,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
target: this.target || "",
ordering: "order",
page: page,
page_size: PAGE_SIZE,
});
}

View File

@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-flow-list")
export class FlowListPage extends TablePage<Flow> {
@ -30,6 +31,7 @@ export class FlowListPage extends TablePage<Flow> {
return Flow.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Group } from "../../api/Groups";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-group-list")
export class GroupListPage extends TablePage<Group> {
@ -30,6 +31,7 @@ export class GroupListPage extends TablePage<Group> {
return Group.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -10,6 +10,7 @@ import "./OutpostHealth";
import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/TokenCopyButton";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-outpost-list")
export class OutpostListPage extends TablePage<Outpost> {
@ -29,6 +30,7 @@ export class OutpostListPage extends TablePage<Outpost> {
return Outpost.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/ModalButton";
import "../../elements/buttons/Dropdown";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-outpost-service-connection-list")
export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> {
@ -31,6 +32,7 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo
return OutpostServiceConnection.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Policy } from "../../api/Policies";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-policy-list")
export class PolicyListPage extends TablePage<Policy> {
@ -31,7 +32,8 @@ export class PolicyListPage extends TablePage<Policy> {
apiEndpoint(page: number): Promise<AKResponse<Policy>> {
return Policy.list({
ordering: this.order,
page: page,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -9,6 +9,7 @@ import "../../elements/buttons/Dropdown";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-property-mapping-list")
export class PropertyMappingListPage extends TablePage<PropertyMapping> {
@ -35,6 +36,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
return PropertyMapping.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
managed__isnull: this.hideManaged,
});

View File

@ -148,10 +148,16 @@ export class OAuth2ProviderViewPage extends Page {
</div>
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="help-text-simple-form-name">
<span class="pf-c-form__label-text">${gettext("Userinfo Endpoint")}</span>
<span class="pf-c-form__label-text">${gettext("Userinfo URL")}</span>
</label>
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.user_info || "-"}" />
</div>
<div class="pf-c-form__group">
<label class="pf-c-form__label" for="help-text-simple-form-name">
<span class="pf-c-form__label-text">${gettext("Logout URL")}</span>
</label>
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.logout || "-"}" />
</div>
</form>
</div>
</div>

View File

@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/Dropdown";
import { TableColumn } from "../../elements/table/Table";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-provider-list")
export class ProviderListPage extends TablePage<Provider> {
@ -32,6 +33,7 @@ export class ProviderListPage extends TablePage<Provider> {
return Provider.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -9,6 +9,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/Dropdown";
import { until } from "lit-html/directives/until";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-source-list")
export class SourceListPage extends TablePage<Source> {
@ -32,6 +33,7 @@ export class SourceListPage extends TablePage<Source> {
return Source.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Invitation } from "../../api/Invitations";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-stage-invitation-list")
export class InvitationListPage extends TablePage<Invitation> {
@ -30,6 +31,7 @@ export class InvitationListPage extends TablePage<Invitation> {
return Invitation.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
import "../../elements/buttons/SpinnerButton";
import { TableColumn } from "../../elements/table/Table";
import { Prompt } from "../../api/Prompts";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-stage-prompt-list")
export class PromptListPage extends TablePage<Prompt> {
@ -30,6 +31,7 @@ export class PromptListPage extends TablePage<Prompt> {
return Prompt.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
import "../../elements/buttons/Dropdown";
import { until } from "lit-html/directives/until";
import { Stage } from "../../api/Flows";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-stage-list")
export class StageListPage extends TablePage<Stage> {
@ -32,6 +33,7 @@ export class StageListPage extends TablePage<Stage> {
return Stage.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
import "../../elements/buttons/TokenCopyButton";
import { TableColumn } from "../../elements/table/Table";
import { Token } from "../../api/Tokens";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-token-list")
export class TokenListPage extends TablePage<Token> {
@ -31,6 +32,7 @@ export class TokenListPage extends TablePage<Token> {
return Token.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -7,6 +7,7 @@ import "../../elements/buttons/Dropdown";
import "../../elements/buttons/TokenCopyButton";
import { Table, TableColumn } from "../../elements/table/Table";
import { Token } from "../../api/Tokens";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-token-user-list")
export class UserTokenList extends Table<Token> {
@ -21,6 +22,7 @@ export class UserTokenList extends Table<Token> {
return Token.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}
@ -35,6 +37,18 @@ export class UserTokenList extends Table<Token> {
];
}
renderToolbar(): TemplateResult {
return html`
<ak-modal-button href="-/user/tokens/create/">
<ak-spinner-button slot="trigger" class="pf-m-primary">
${gettext("Create")}
</ak-spinner-button>
<div slot="modal"></div>
</ak-modal-button>
${super.renderToolbar()}
`;
}
row(item: Token): TemplateResult[] {
return [
html`${item.identifier}`,

View File

@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
import "../../elements/buttons/ActionButton";
import { TableColumn } from "../../elements/table/Table";
import { User } from "../../api/Users";
import { PAGE_SIZE } from "../../constants";
@customElement("ak-user-list")
export class UserListPage extends TablePage<User> {
@ -31,6 +32,7 @@ export class UserListPage extends TablePage<User> {
return User.list({
ordering: this.order,
page: page,
page_size: PAGE_SIZE,
search: this.search || "",
});
}

View File

@ -13,5 +13,7 @@ See [Docker-compose](installation/docker-compose) or [Kubernetes](installation/k
## Screenshots
![](/img/screen_apps.png)
![](/img/screen_admin.png)
Light | Dark
--- | ---
![](/img/screen_apps_light.png) | ![](/img/screen_apps_dark.png)
![](/img/screen_admin_light.png) | ![](/img/screen_admin_dark.png)

View File

@ -123,7 +123,7 @@ The integrations affected are:
### docker-compose
Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-2021.1/docker-compose.yml). Afterwards, simply run `docker-compose up -d` and then the standard upgrade command of `docker-compose run --rm server migrate`.
Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-2021.2/docker-compose.yml). Afterwards, simply run `docker-compose up -d` and then the standard upgrade command of `docker-compose run --rm server migrate`.
### Kubernetes

View File

@ -45,7 +45,7 @@ This release does not introduce any new requirements.
### docker-compose
Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-2021.1/docker-compose.yml). Afterwards, simply run `docker-compose up -d` and then the standard upgrade command of `docker-compose run --rm server migrate`.
Download the latest docker-compose file from [here](https://raw.githubusercontent.com/BeryJu/authentik/version-2021.3/docker-compose.yml). Afterwards, simply run `docker-compose up -d` and then the standard upgrade command of `docker-compose run --rm server migrate`.
### Kubernetes

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -100,7 +100,7 @@ function Home() {
<div className="row">
<div className="col col--5">
<div>
<img className={styles.featureImage} src={useBaseUrl('img/screen_apps.png')} alt="library screenshot"/>
<img className={styles.featureImage} src={useBaseUrl('img/screen_apps_light.png')} alt="library screenshot"/>
</div>
</div>
<div className="col col--5 col--offset-2 padding-vert--xl">
@ -131,7 +131,7 @@ function Home() {
</div>
<div className="col col--5">
<div>
<img className={styles.featureImage} src={useBaseUrl('img/screen_admin.png')} alt="library screenshot" />
<img className={styles.featureImage} src={useBaseUrl('img/screen_admin_light.png')} alt="library screenshot" />
</div>
</div>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 534 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 747 KiB