diff --git a/README.md b/README.md index 1c6c3f51f..b624e6d5f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -authentik logo +

+ authentik logo +

--- @@ -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 diff --git a/SECURITY.md b/SECURITY.md index d6f0d6d33..16f1f7f87 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -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 diff --git a/authentik/admin/templates/generic/form.html b/authentik/admin/templates/generic/form.html index d7208c69f..20b78908f 100644 --- a/authentik/admin/templates/generic/form.html +++ b/authentik/admin/templates/generic/form.html @@ -27,7 +27,9 @@ {% endblock %} diff --git a/authentik/api/pagination.py b/authentik/api/pagination.py index 42a5249ec..04bfefd1e 100644 --- a/authentik/api/pagination.py +++ b/authentik/api/pagination.py @@ -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): diff --git a/authentik/core/models.py b/authentik/core/models.py index 24b3687f5..1e556734d 100644 --- a/authentik/core/models.py +++ b/authentik/core/models.py @@ -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.")) diff --git a/authentik/core/templates/base/page.html b/authentik/core/templates/base/page.html deleted file mode 100644 index fa10451ce..000000000 --- a/authentik/core/templates/base/page.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends "base/skeleton.html" %} - -{% load i18n %} - -{% block body %} - -
- {% trans 'Skip to content' %} - {% block page_content %} - {% endblock %} -
-{% endblock %} diff --git a/authentik/core/templates/error/generic.html b/authentik/core/templates/error/generic.html index 17ba3a5a2..ca1d53de5 100644 --- a/authentik/core/templates/error/generic.html +++ b/authentik/core/templates/error/generic.html @@ -1,4 +1,4 @@ -{% extends 'base/page.html' %} +{% extends 'base/skeleton.html' %} {% load i18n %} {% load authentik_utils %} diff --git a/authentik/core/views/user.py b/authentik/core/views/user.py index 13939ca4f..8554a9bc1 100644 --- a/authentik/core/views/user.py +++ b/authentik/core/views/user.py @@ -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: diff --git a/authentik/lib/utils/urls.py b/authentik/lib/utils/urls.py index 302fabd36..5dc92a753 100644 --- a/authentik/lib/utils/urls.py +++ b/authentik/lib/utils/urls.py @@ -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 diff --git a/authentik/policies/event_matcher/migrations/0011_auto_20210302_0856.py b/authentik/policies/event_matcher/migrations/0011_auto_20210302_0856.py new file mode 100644 index 000000000..6d4d58cd8 --- /dev/null +++ b/authentik/policies/event_matcher/migrations/0011_auto_20210302_0856.py @@ -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.", + ), + ), + ] diff --git a/authentik/policies/views.py b/authentik/policies/views.py index c40454b9d..36184b0de 100644 --- a/authentik/policies/views.py +++ b/authentik/policies/views.py @@ -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() diff --git a/authentik/providers/oauth2/api.py b/authentik/providers/oauth2/api.py index 7e6ef24ee..84982d475 100644 --- a/authentik/providers/oauth2/api.py +++ b/authentik/providers/oauth2/api.py @@ -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) diff --git a/authentik/sources/ldap/sync/users.py b/authentik/sources/ldap/sync/users.py index 37031b037..703da2f31 100644 --- a/authentik/sources/ldap/sync/users.py +++ b/authentik/sources/ldap/sync/users.py @@ -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 diff --git a/authentik/stages/authenticator_webauthn/forms.py b/authentik/stages/authenticator_webauthn/forms.py index 78368c190..881bf54d7 100644 --- a/authentik/stages/authenticator_webauthn/forms.py +++ b/authentik/stages/authenticator_webauthn/forms.py @@ -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(), + } diff --git a/authentik/stages/authenticator_webauthn/models.py b/authentik/stages/authenticator_webauthn/models.py index f84c0b799..cc63da205 100644 --- a/authentik/stages/authenticator_webauthn/models.py +++ b/authentik/stages/authenticator_webauthn/models.py @@ -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") diff --git a/authentik/stages/authenticator_webauthn/templates/stages/authenticator_webauthn/user_settings.html b/authentik/stages/authenticator_webauthn/templates/stages/authenticator_webauthn/user_settings.html index 89a26a8a3..38daec51a 100644 --- a/authentik/stages/authenticator_webauthn/templates/stages/authenticator_webauthn/user_settings.html +++ b/authentik/stages/authenticator_webauthn/templates/stages/authenticator_webauthn/user_settings.html @@ -17,6 +17,20 @@ Created {{ created_on }} {% endblocktrans %} +
+ + + {% trans 'Update' %} + +
+
+ + + {% trans 'Delete' %} + +
+
+
diff --git a/authentik/stages/authenticator_webauthn/urls.py b/authentik/stages/authenticator_webauthn/urls.py index d163dcc39..bcd10e496 100644 --- a/authentik/stages/authenticator_webauthn/urls.py +++ b/authentik/stages/authenticator_webauthn/urls.py @@ -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( "/settings/", UserSettingsView.as_view(), name="user-settings" ), + path("devices//delete/", DeviceDeleteView.as_view(), name="device-delete"), + path("devices//update/", DeviceUpdateView.as_view(), name="device-update"), ] diff --git a/authentik/stages/authenticator_webauthn/views.py b/authentik/stages/authenticator_webauthn/views.py index 6881fb571..c8954f586 100644 --- a/authentik/stages/authenticator_webauthn/views.py +++ b/authentik/stages/authenticator_webauthn/views.py @@ -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 diff --git a/authentik/stages/password/templates/stages/password/user-settings-card.html b/authentik/stages/password/templates/stages/password/user-settings-card.html index 48fbfc72a..1011c45a9 100644 --- a/authentik/stages/password/templates/stages/password/user-settings-card.html +++ b/authentik/stages/password/templates/stages/password/user-settings-card.html @@ -1,9 +1,6 @@ -{% extends "base/page.html" %} - {% load i18n %} {% load authentik_utils %} -{% block body %}
{% trans 'Reset your password' %} @@ -14,4 +11,3 @@
-{% endblock %} diff --git a/swagger.yaml b/swagger.yaml index 5b1a3437c..36a416513 100755 --- a/swagger.yaml +++ b/swagger.yaml @@ -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 diff --git a/web/src/api/Client.ts b/web/src/api/Client.ts index dfb74c42f..55c593759 100644 --- a/web/src/api/Client.ts +++ b/web/src/api/Client.ts @@ -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 { diff --git a/web/src/api/providers/OAuth2.ts b/web/src/api/providers/OAuth2.ts index 2e6e28d63..3216d03cc 100644 --- a/web/src/api/providers/OAuth2.ts +++ b/web/src/api/providers/OAuth2.ts @@ -8,6 +8,7 @@ export interface OAuth2SetupURLs { token: string; user_info: string; provider_info?: string; + logout?: string; } diff --git a/web/src/authentik.css b/web/src/authentik.css index de8a26a0e..273b340d1 100644 --- a/web/src/authentik.css +++ b/web/src/authentik.css @@ -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; diff --git a/web/src/constants.ts b/web/src/constants.ts index e8e86f7a7..5c639e8cc 100644 --- a/web/src/constants.ts +++ b/web/src/constants.ts @@ -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; diff --git a/web/src/elements/buttons/SpinnerButton.ts b/web/src/elements/buttons/SpinnerButton.ts index 5213e3e75..dc61ab503 100644 --- a/web/src/elements/buttons/SpinnerButton.ts +++ b/web/src/elements/buttons/SpinnerButton.ts @@ -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(`#${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(); } diff --git a/web/src/elements/notifications/NotificationDrawer.ts b/web/src/elements/notifications/NotificationDrawer.ts index f366ec440..8b8e77ae8 100644 --- a/web/src/elements/notifications/NotificationDrawer.ts +++ b/web/src/elements/notifications/NotificationDrawer.ts @@ -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 {

${item.body}

- ${age} + ${created.toLocaleString()} `; } diff --git a/web/src/elements/policies/BoundPoliciesList.ts b/web/src/elements/policies/BoundPoliciesList.ts index 24c67f844..03d198cbe 100644 --- a/web/src/elements/policies/BoundPoliciesList.ts +++ b/web/src/elements/policies/BoundPoliciesList.ts @@ -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 { @@ -22,6 +23,7 @@ export class BoundPoliciesList extends Table { target: this.target || "", ordering: "order", page: page, + page_size: PAGE_SIZE, }); } diff --git a/web/src/pages/applications/ApplicationListPage.ts b/web/src/pages/applications/ApplicationListPage.ts index 608670c58..4e26216bc 100644 --- a/web/src/pages/applications/ApplicationListPage.ts +++ b/web/src/pages/applications/ApplicationListPage.ts @@ -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 { @@ -30,6 +31,7 @@ export class ApplicationListPage extends TablePage { return Application.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/crypto/CertificateKeyPairListPage.ts b/web/src/pages/crypto/CertificateKeyPairListPage.ts index 98aaa04be..8a3591752 100644 --- a/web/src/pages/crypto/CertificateKeyPairListPage.ts +++ b/web/src/pages/crypto/CertificateKeyPairListPage.ts @@ -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 { @@ -32,6 +33,7 @@ export class CertificateKeyPairListPage extends TablePage { return CertificateKeyPair.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/events/EventListPage.ts b/web/src/pages/events/EventListPage.ts index 4f7edcbe6..228add3e2 100644 --- a/web/src/pages/events/EventListPage.ts +++ b/web/src/pages/events/EventListPage.ts @@ -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 { return Event.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE * 3, search: this.search || "", }); } diff --git a/web/src/pages/events/RuleListPage.ts b/web/src/pages/events/RuleListPage.ts index cc4b9b379..3cd4c0f1a 100644 --- a/web/src/pages/events/RuleListPage.ts +++ b/web/src/pages/events/RuleListPage.ts @@ -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 { @@ -33,6 +34,7 @@ export class RuleListPage extends TablePage { return Rule.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/events/TransportListPage.ts b/web/src/pages/events/TransportListPage.ts index 6918ef551..df21d5954 100644 --- a/web/src/pages/events/TransportListPage.ts +++ b/web/src/pages/events/TransportListPage.ts @@ -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 { @@ -31,6 +32,7 @@ export class TransportListPage extends TablePage { return Transport.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/flows/BoundStagesList.ts b/web/src/pages/flows/BoundStagesList.ts index b0bc4ecb7..2d32d3c57 100644 --- a/web/src/pages/flows/BoundStagesList.ts +++ b/web/src/pages/flows/BoundStagesList.ts @@ -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 { @@ -24,6 +25,7 @@ export class BoundStagesList extends Table { target: this.target || "", ordering: "order", page: page, + page_size: PAGE_SIZE, }); } diff --git a/web/src/pages/flows/FlowListPage.ts b/web/src/pages/flows/FlowListPage.ts index b7ae5a90f..8bcf06955 100644 --- a/web/src/pages/flows/FlowListPage.ts +++ b/web/src/pages/flows/FlowListPage.ts @@ -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 { @@ -30,6 +31,7 @@ export class FlowListPage extends TablePage { return Flow.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/groups/GroupListPage.ts b/web/src/pages/groups/GroupListPage.ts index 5a1ce6063..57140f7e7 100644 --- a/web/src/pages/groups/GroupListPage.ts +++ b/web/src/pages/groups/GroupListPage.ts @@ -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 { @@ -30,6 +31,7 @@ export class GroupListPage extends TablePage { return Group.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/outposts/OutpostListPage.ts b/web/src/pages/outposts/OutpostListPage.ts index f56028356..dbb35852b 100644 --- a/web/src/pages/outposts/OutpostListPage.ts +++ b/web/src/pages/outposts/OutpostListPage.ts @@ -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 { @@ -29,6 +30,7 @@ export class OutpostListPage extends TablePage { return Outpost.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/outposts/OutpostServiceConnectionListPage.ts b/web/src/pages/outposts/OutpostServiceConnectionListPage.ts index 7e8eb4333..86e729769 100644 --- a/web/src/pages/outposts/OutpostServiceConnectionListPage.ts +++ b/web/src/pages/outposts/OutpostServiceConnectionListPage.ts @@ -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 { @@ -31,6 +32,7 @@ export class OutpostServiceConnectionListPage extends TablePage { @@ -31,7 +32,8 @@ export class PolicyListPage extends TablePage { apiEndpoint(page: number): Promise> { return Policy.list({ ordering: this.order, - page: page, + page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/property-mappings/PropertyMappingListPage.ts b/web/src/pages/property-mappings/PropertyMappingListPage.ts index 210059ce1..f9bd61ab0 100644 --- a/web/src/pages/property-mappings/PropertyMappingListPage.ts +++ b/web/src/pages/property-mappings/PropertyMappingListPage.ts @@ -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 { @@ -35,6 +36,7 @@ export class PropertyMappingListPage extends TablePage { return PropertyMapping.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", managed__isnull: this.hideManaged, }); diff --git a/web/src/pages/providers/OAuth2ProviderViewPage.ts b/web/src/pages/providers/OAuth2ProviderViewPage.ts index c58413c92..08ec15ca8 100644 --- a/web/src/pages/providers/OAuth2ProviderViewPage.ts +++ b/web/src/pages/providers/OAuth2ProviderViewPage.ts @@ -148,10 +148,16 @@ export class OAuth2ProviderViewPage extends Page {
+
+ + +
diff --git a/web/src/pages/providers/ProviderListPage.ts b/web/src/pages/providers/ProviderListPage.ts index fe6ed3bab..eb996d0be 100644 --- a/web/src/pages/providers/ProviderListPage.ts +++ b/web/src/pages/providers/ProviderListPage.ts @@ -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 { @@ -32,6 +33,7 @@ export class ProviderListPage extends TablePage { return Provider.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/sources/SourcesListPage.ts b/web/src/pages/sources/SourcesListPage.ts index 76e9b9410..b747f5920 100644 --- a/web/src/pages/sources/SourcesListPage.ts +++ b/web/src/pages/sources/SourcesListPage.ts @@ -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 { @@ -32,6 +33,7 @@ export class SourceListPage extends TablePage { return Source.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/stages/InvitationListPage.ts b/web/src/pages/stages/InvitationListPage.ts index e53cfd293..a5f6cced5 100644 --- a/web/src/pages/stages/InvitationListPage.ts +++ b/web/src/pages/stages/InvitationListPage.ts @@ -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 { @@ -30,6 +31,7 @@ export class InvitationListPage extends TablePage { return Invitation.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/stages/PromptListPage.ts b/web/src/pages/stages/PromptListPage.ts index 37bc2c8f6..ba9926d9f 100644 --- a/web/src/pages/stages/PromptListPage.ts +++ b/web/src/pages/stages/PromptListPage.ts @@ -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 { @@ -30,6 +31,7 @@ export class PromptListPage extends TablePage { return Prompt.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/stages/StageListPage.ts b/web/src/pages/stages/StageListPage.ts index 710512a1d..4d2cddb1e 100644 --- a/web/src/pages/stages/StageListPage.ts +++ b/web/src/pages/stages/StageListPage.ts @@ -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 { @@ -32,6 +33,7 @@ export class StageListPage extends TablePage { return Stage.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/tokens/TokenListPage.ts b/web/src/pages/tokens/TokenListPage.ts index f96016d11..4f6926192 100644 --- a/web/src/pages/tokens/TokenListPage.ts +++ b/web/src/pages/tokens/TokenListPage.ts @@ -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 { @@ -31,6 +32,7 @@ export class TokenListPage extends TablePage { return Token.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/web/src/pages/tokens/UserTokenList.ts b/web/src/pages/tokens/UserTokenList.ts index fba40547b..9d4f41d97 100644 --- a/web/src/pages/tokens/UserTokenList.ts +++ b/web/src/pages/tokens/UserTokenList.ts @@ -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 { @@ -21,6 +22,7 @@ export class UserTokenList extends Table { return Token.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } @@ -35,6 +37,18 @@ export class UserTokenList extends Table { ]; } + renderToolbar(): TemplateResult { + return html` + + + ${gettext("Create")} + +
+
+ ${super.renderToolbar()} + `; + } + row(item: Token): TemplateResult[] { return [ html`${item.identifier}`, diff --git a/web/src/pages/users/UserListPage.ts b/web/src/pages/users/UserListPage.ts index e3580b237..e714a05a1 100644 --- a/web/src/pages/users/UserListPage.ts +++ b/web/src/pages/users/UserListPage.ts @@ -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 { @@ -31,6 +32,7 @@ export class UserListPage extends TablePage { return User.list({ ordering: this.order, page: page, + page_size: PAGE_SIZE, search: this.search || "", }); } diff --git a/website/docs/index.md b/website/docs/index.md index 0b2d63055..b9b5e374e 100755 --- a/website/docs/index.md +++ b/website/docs/index.md @@ -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) diff --git a/website/docs/releases/2021.2.md b/website/docs/releases/2021.2.md index b584a38e2..cd7c8a0aa 100644 --- a/website/docs/releases/2021.2.md +++ b/website/docs/releases/2021.2.md @@ -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 diff --git a/website/docs/releases/2021.3.md b/website/docs/releases/2021.3.md index b0a230e32..869df2eef 100644 --- a/website/docs/releases/2021.3.md +++ b/website/docs/releases/2021.3.md @@ -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 diff --git a/website/docs/troubleshooting/authentik_user_debug.png b/website/docs/troubleshooting/authentik_user_debug.png index 4343d1950..61264836b 100644 Binary files a/website/docs/troubleshooting/authentik_user_debug.png and b/website/docs/troubleshooting/authentik_user_debug.png differ diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 5b529a011..54394e452 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -100,7 +100,7 @@ function Home() {
- library screenshot + library screenshot
@@ -131,7 +131,7 @@ function Home() {
- library screenshot + library screenshot
diff --git a/website/static/img/screen_admin.png b/website/static/img/screen_admin.png deleted file mode 100644 index 97d2cd918..000000000 Binary files a/website/static/img/screen_admin.png and /dev/null differ diff --git a/website/static/img/screen_admin_dark.png b/website/static/img/screen_admin_dark.png new file mode 100644 index 000000000..cf83b1c12 Binary files /dev/null and b/website/static/img/screen_admin_dark.png differ diff --git a/website/static/img/screen_admin_light.png b/website/static/img/screen_admin_light.png new file mode 100644 index 000000000..48ff06552 Binary files /dev/null and b/website/static/img/screen_admin_light.png differ diff --git a/website/static/img/screen_apps.png b/website/static/img/screen_apps.png deleted file mode 100644 index 562c4a438..000000000 Binary files a/website/static/img/screen_apps.png and /dev/null differ diff --git a/website/static/img/screen_apps_dark.png b/website/static/img/screen_apps_dark.png new file mode 100644 index 000000000..cac787322 Binary files /dev/null and b/website/static/img/screen_apps_dark.png differ diff --git a/website/static/img/screen_apps_light.png b/website/static/img/screen_apps_light.png new file mode 100644 index 000000000..de30ae99b Binary files /dev/null and b/website/static/img/screen_apps_light.png differ