Merge branch 'master' into version-2021.3
# Conflicts: # web/src/constants.ts
10
README.md
|
@ -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
|
## Screenshots
|
||||||
|
|
||||||
![](https://goauthentik.io/img/screen_apps.png)
|
Light | Dark
|
||||||
![](https://goauthentik.io/img/screen_admin.png)
|
--- | ---
|
||||||
|
![](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
|
## Development
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
|
|
||||||
| Version | Supported |
|
| Version | Supported |
|
||||||
| ---------- | ------------------ |
|
| ---------- | ------------------ |
|
||||||
| 0.13.x | :white_check_mark: |
|
|
||||||
| 0.14.x | :white_check_mark: |
|
|
||||||
| 2021.1.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
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,9 @@
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<footer class="pf-c-modal-box__footer">
|
<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>
|
||||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
|
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
|
||||||
</footer>
|
</footer>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -6,6 +6,7 @@ from rest_framework.response import Response
|
||||||
class Pagination(pagination.PageNumberPagination):
|
class Pagination(pagination.PageNumberPagination):
|
||||||
"""Pagination which includes total pages and current page"""
|
"""Pagination which includes total pages and current page"""
|
||||||
|
|
||||||
|
page_query_param = "page"
|
||||||
page_size_query_param = "page_size"
|
page_size_query_param = "page_size"
|
||||||
|
|
||||||
def get_paginated_response(self, data):
|
def get_paginated_response(self, data):
|
||||||
|
|
|
@ -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 %}
|
|
|
@ -1,4 +1,4 @@
|
||||||
{% extends 'base/page.html' %}
|
{% extends 'base/skeleton.html' %}
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load authentik_utils %}
|
{% load authentik_utils %}
|
||||||
|
|
|
@ -7,7 +7,6 @@ from django.contrib.auth.mixins import (
|
||||||
)
|
)
|
||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.http.response import HttpResponse
|
from django.http.response import HttpResponse
|
||||||
from django.urls import reverse_lazy
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.views.generic import UpdateView
|
from django.views.generic import UpdateView
|
||||||
from django.views.generic.base import TemplateView
|
from django.views.generic.base import TemplateView
|
||||||
|
@ -35,7 +34,7 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
||||||
form_class = UserDetailForm
|
form_class = UserDetailForm
|
||||||
|
|
||||||
success_message = _("Successfully updated user.")
|
success_message = _("Successfully updated user.")
|
||||||
success_url = reverse_lazy("authentik_core:user-details")
|
success_url = "/"
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return self.request.user
|
return self.request.user
|
||||||
|
@ -62,7 +61,7 @@ class TokenCreateView(
|
||||||
permission_required = "authentik_core.add_token"
|
permission_required = "authentik_core.add_token"
|
||||||
|
|
||||||
template_name = "generic/create.html"
|
template_name = "generic/create.html"
|
||||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
success_url = "/"
|
||||||
success_message = _("Successfully created Token")
|
success_message = _("Successfully created Token")
|
||||||
|
|
||||||
def form_valid(self, form: UserTokenForm) -> HttpResponse:
|
def form_valid(self, form: UserTokenForm) -> HttpResponse:
|
||||||
|
@ -80,7 +79,7 @@ class TokenUpdateView(
|
||||||
form_class = UserTokenForm
|
form_class = UserTokenForm
|
||||||
permission_required = "authentik_core.change_token"
|
permission_required = "authentik_core.change_token"
|
||||||
template_name = "generic/update.html"
|
template_name = "generic/update.html"
|
||||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
success_url = "/"
|
||||||
success_message = _("Successfully updated Token")
|
success_message = _("Successfully updated Token")
|
||||||
|
|
||||||
def get_object(self) -> Token:
|
def get_object(self) -> Token:
|
||||||
|
@ -100,7 +99,7 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
|
||||||
model = Token
|
model = Token
|
||||||
permission_required = "authentik_core.delete_token"
|
permission_required = "authentik_core.delete_token"
|
||||||
template_name = "generic/delete.html"
|
template_name = "generic/delete.html"
|
||||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
success_url = "/"
|
||||||
success_message = _("Successfully deleted Token")
|
success_message = _("Successfully deleted Token")
|
||||||
|
|
||||||
def get_object(self) -> Token:
|
def get_object(self) -> Token:
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import redirect, reverse
|
from django.shortcuts import redirect
|
||||||
from django.urls import NoReverseMatch
|
from django.urls import NoReverseMatch, reverse
|
||||||
from django.utils.http import urlencode
|
from django.utils.http import urlencode
|
||||||
from structlog.stdlib import get_logger
|
from structlog.stdlib import get_logger
|
||||||
|
|
||||||
|
|
|
@ -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.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -62,11 +62,15 @@ class PolicyAccessView(AccessMixin, View):
|
||||||
return self.handle_no_permission()
|
return self.handle_no_permission()
|
||||||
try:
|
try:
|
||||||
self.resolve_provider_application()
|
self.resolve_provider_application()
|
||||||
except (Application.DoesNotExist, Provider.DoesNotExist):
|
except (Application.DoesNotExist, Provider.DoesNotExist) as exc:
|
||||||
return self.handle_no_permission_authenticated()
|
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
|
# Check if user is unauthenticated, so we pass the application
|
||||||
# for the identification stage
|
# for the identification stage
|
||||||
if not request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
|
LOGGER.warning("user not authenticated")
|
||||||
return self.handle_no_permission()
|
return self.handle_no_permission()
|
||||||
# Check permissions
|
# Check permissions
|
||||||
result = self.user_has_access()
|
result = self.user_has_access()
|
||||||
|
|
|
@ -45,6 +45,7 @@ class OAuth2ProviderSetupURLs(Serializer):
|
||||||
token = ReadOnlyField()
|
token = ReadOnlyField()
|
||||||
user_info = ReadOnlyField()
|
user_info = ReadOnlyField()
|
||||||
provider_info = ReadOnlyField()
|
provider_info = ReadOnlyField()
|
||||||
|
logout = ReadOnlyField()
|
||||||
|
|
||||||
def create(self, request: Request) -> Response:
|
def create(self, request: Request) -> Response:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
@ -83,6 +84,7 @@ class OAuth2ProviderViewSet(ModelViewSet):
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"provider_info": None,
|
"provider_info": None,
|
||||||
|
"logout": None,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
data["provider_info"] = request.build_absolute_uri(
|
data["provider_info"] = request.build_absolute_uri(
|
||||||
|
@ -91,6 +93,12 @@ class OAuth2ProviderViewSet(ModelViewSet):
|
||||||
kwargs={"application_slug": provider.application.slug},
|
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
|
except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member
|
||||||
pass
|
pass
|
||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
"""Sync LDAP Users into authentik"""
|
"""Sync LDAP Users into authentik"""
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import ldap3
|
import ldap3
|
||||||
import ldap3.core.exceptions
|
import ldap3.core.exceptions
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
|
from pytz import UTC
|
||||||
|
|
||||||
from authentik.core.models import User
|
from authentik.core.models import User
|
||||||
from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer
|
from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer
|
||||||
|
@ -53,11 +56,21 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if created:
|
|
||||||
ak_user.set_unusable_password()
|
|
||||||
ak_user.save()
|
|
||||||
self._logger.debug(
|
self._logger.debug(
|
||||||
"Synced User", user=ak_user.username, created=created
|
"Synced User", user=ak_user.username, created=created
|
||||||
)
|
)
|
||||||
user_count += 1
|
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
|
return user_count
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
"""Webauthn stage forms"""
|
"""Webauthn stage forms"""
|
||||||
from django import 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):
|
class AuthenticateWebAuthnStageForm(forms.ModelForm):
|
||||||
|
@ -15,3 +18,16 @@ class AuthenticateWebAuthnStageForm(forms.ModelForm):
|
||||||
widgets = {
|
widgets = {
|
||||||
"name": forms.TextInput(),
|
"name": forms.TextInput(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceEditForm(forms.ModelForm):
|
||||||
|
"""Form to edit webauthn device"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
model = WebAuthnDevice
|
||||||
|
fields = ["name"]
|
||||||
|
|
||||||
|
widgets = {
|
||||||
|
"name": forms.TextInput(),
|
||||||
|
}
|
||||||
|
|
|
@ -79,3 +79,8 @@ class WebAuthnDevice(Device):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name or str(self.user)
|
return self.name or str(self.user)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
verbose_name = _("WebAuthn Device")
|
||||||
|
verbose_name_plural = _("WebAuthn Devices")
|
||||||
|
|
|
@ -17,6 +17,20 @@
|
||||||
Created {{ created_on }}
|
Created {{ created_on }}
|
||||||
{% endblocktrans %}
|
{% endblocktrans %}
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
"""WebAuthn urls"""
|
"""WebAuthn urls"""
|
||||||
from django.urls import path
|
from django.urls import path
|
||||||
|
|
||||||
from authentik.stages.authenticator_webauthn.views import UserSettingsView
|
from authentik.stages.authenticator_webauthn.views import (
|
||||||
|
DeviceDeleteView,
|
||||||
|
DeviceUpdateView,
|
||||||
|
UserSettingsView,
|
||||||
|
)
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path(
|
path(
|
||||||
"<uuid:stage_uuid>/settings/", UserSettingsView.as_view(), name="user-settings"
|
"<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"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
"""webauthn views"""
|
"""webauthn views"""
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
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.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 (
|
from authentik.stages.authenticator_webauthn.models import (
|
||||||
AuthenticateWebAuthnStage,
|
AuthenticateWebAuthnStage,
|
||||||
WebAuthnDevice,
|
WebAuthnDevice,
|
||||||
|
@ -22,3 +27,34 @@ class UserSettingsView(LoginRequiredMixin, TemplateView):
|
||||||
)
|
)
|
||||||
kwargs["stage"] = stage
|
kwargs["stage"] = stage
|
||||||
return kwargs
|
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
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
{% extends "base/page.html" %}
|
|
||||||
|
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load authentik_utils %}
|
{% load authentik_utils %}
|
||||||
|
|
||||||
{% block body %}
|
|
||||||
<div class="pf-c-card">
|
<div class="pf-c-card">
|
||||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||||
{% trans 'Reset your password' %}
|
{% trans 'Reset your password' %}
|
||||||
|
@ -14,4 +11,3 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -10684,6 +10684,10 @@ definitions:
|
||||||
title: Provider info
|
title: Provider info
|
||||||
type: string
|
type: string
|
||||||
readOnly: true
|
readOnly: true
|
||||||
|
logout:
|
||||||
|
title: Logout
|
||||||
|
type: string
|
||||||
|
readOnly: true
|
||||||
ProxyProvider:
|
ProxyProvider:
|
||||||
description: ProxyProvider Serializer
|
description: ProxyProvider Serializer
|
||||||
required:
|
required:
|
||||||
|
|
|
@ -4,7 +4,9 @@ import { NotFoundError, RequestError } from "./Error";
|
||||||
export const VERSION = "v2beta";
|
export const VERSION = "v2beta";
|
||||||
|
|
||||||
export interface QueryArguments {
|
export interface QueryArguments {
|
||||||
[key: string]: number | string | boolean | null;
|
page?: number;
|
||||||
|
page_size?: number;
|
||||||
|
[key: string]: number | string | boolean | undefined | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseInheritanceModel {
|
export interface BaseInheritanceModel {
|
||||||
|
|
|
@ -8,6 +8,7 @@ export interface OAuth2SetupURLs {
|
||||||
token: string;
|
token: string;
|
||||||
user_info: string;
|
user_info: string;
|
||||||
provider_info?: string;
|
provider_info?: string;
|
||||||
|
logout?: string;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,6 @@ select[multiple] {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix pre elements within alerts */
|
|
||||||
.pf-c-alert pre {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pf-c-content h1 {
|
.pf-c-content h1 {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
|
@ -85,6 +80,12 @@ select[multiple] {
|
||||||
z-index: auto !important;
|
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 */
|
/* Fix spacing between messages */
|
||||||
ak-message {
|
ak-message {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -4,3 +4,4 @@ export const ERROR_CLASS = "pf-m-danger";
|
||||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||||
export const CURRENT_CLASS = "pf-m-current";
|
export const CURRENT_CLASS = "pf-m-current";
|
||||||
export const VERSION = "2021.3.1-rc1";
|
export const VERSION = "2021.3.1-rc1";
|
||||||
|
export const PAGE_SIZE = 20;
|
||||||
|
|
|
@ -59,9 +59,9 @@ export class SpinnerButton extends LitElement {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.form) {
|
if (this.form) {
|
||||||
// Because safari we can't just extend HTMLButtonElement, hence I have to implement
|
// Since the form= attribute is only used within a modal button,
|
||||||
// these attributes by myself here, sigh...
|
// we can assume the form is always two levels up
|
||||||
document.querySelector<HTMLFormElement>(`#${this.form}`)?.submit();
|
this.parentElement?.parentElement?.querySelector < HTMLFormElement>(`#${this.form}`)?.dispatchEvent(new Event("submit"));
|
||||||
}
|
}
|
||||||
this.setLoading();
|
this.setLoading();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,9 +40,7 @@ export class NotificationDrawer extends LitElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderItem(item: Notification): TemplateResult {
|
renderItem(item: Notification): TemplateResult {
|
||||||
const delta = Date.now() - (parseInt(item.created, 10) * 1000);
|
const created = new Date(parseInt(item.created, 10) * 1000);
|
||||||
// TODO: more flexible display, minutes and seconds
|
|
||||||
const age = `${Math.round(delta / 1000 / 3600)} Hours ago`;
|
|
||||||
let level = "";
|
let level = "";
|
||||||
switch (item.severity) {
|
switch (item.severity) {
|
||||||
case "notice":
|
case "notice":
|
||||||
|
@ -76,7 +74,7 @@ export class NotificationDrawer extends LitElement {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="pf-c-notification-drawer__list-item-description">${item.body}</p>
|
<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>`;
|
</li>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/Dropdown";
|
import "../../elements/buttons/Dropdown";
|
||||||
import { Policy } from "../../api/Policies";
|
import { Policy } from "../../api/Policies";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-bound-policies-list")
|
@customElement("ak-bound-policies-list")
|
||||||
export class BoundPoliciesList extends Table<PolicyBinding> {
|
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||||
|
@ -22,6 +23,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||||
target: this.target || "",
|
target: this.target || "",
|
||||||
ordering: "order",
|
ordering: "order",
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
|
||||||
import "../../elements/buttons/ModalButton";
|
import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-application-list")
|
@customElement("ak-application-list")
|
||||||
export class ApplicationListPage extends TablePage<Application> {
|
export class ApplicationListPage extends TablePage<Application> {
|
||||||
|
@ -30,6 +31,7 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||||
return Application.list({
|
return Application.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { CertificateKeyPair } from "../../api/CertificateKeyPair";
|
import { CertificateKeyPair } from "../../api/CertificateKeyPair";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-crypto-certificatekeypair-list")
|
@customElement("ak-crypto-certificatekeypair-list")
|
||||||
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||||
|
@ -32,6 +33,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||||
return CertificateKeyPair.list({
|
return CertificateKeyPair.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { gettext } from "django";
|
||||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||||
import { AKResponse } from "../../api/Client";
|
import { AKResponse } from "../../api/Client";
|
||||||
import { Event } from "../../api/Events";
|
import { Event } from "../../api/Events";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { TablePage } from "../../elements/table/TablePage";
|
import { TablePage } from "../../elements/table/TablePage";
|
||||||
import { time } from "../../utils";
|
import { time } from "../../utils";
|
||||||
|
@ -31,6 +32,7 @@ export class EventListPage extends TablePage<Event> {
|
||||||
return Event.list({
|
return Event.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE * 3,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Rule } from "../../api/EventRules";
|
import { Rule } from "../../api/EventRules";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-event-rule-list")
|
@customElement("ak-event-rule-list")
|
||||||
export class RuleListPage extends TablePage<Rule> {
|
export class RuleListPage extends TablePage<Rule> {
|
||||||
|
@ -33,6 +34,7 @@ export class RuleListPage extends TablePage<Rule> {
|
||||||
return Rule.list({
|
return Rule.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Transport } from "../../api/EventTransports";
|
import { Transport } from "../../api/EventTransports";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-event-transport-list")
|
@customElement("ak-event-transport-list")
|
||||||
export class TransportListPage extends TablePage<Transport> {
|
export class TransportListPage extends TablePage<Transport> {
|
||||||
|
@ -31,6 +32,7 @@ export class TransportListPage extends TablePage<Transport> {
|
||||||
return Transport.list({
|
return Transport.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import "../../elements/buttons/Dropdown";
|
||||||
import "../../elements/policies/BoundPoliciesList";
|
import "../../elements/policies/BoundPoliciesList";
|
||||||
import { FlowStageBinding, Stage } from "../../api/Flows";
|
import { FlowStageBinding, Stage } from "../../api/Flows";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-bound-stages-list")
|
@customElement("ak-bound-stages-list")
|
||||||
export class BoundStagesList extends Table<FlowStageBinding> {
|
export class BoundStagesList extends Table<FlowStageBinding> {
|
||||||
|
@ -24,6 +25,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||||
target: this.target || "",
|
target: this.target || "",
|
||||||
ordering: "order",
|
ordering: "order",
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
|
||||||
import "../../elements/buttons/ModalButton";
|
import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-flow-list")
|
@customElement("ak-flow-list")
|
||||||
export class FlowListPage extends TablePage<Flow> {
|
export class FlowListPage extends TablePage<Flow> {
|
||||||
|
@ -30,6 +31,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||||
return Flow.list({
|
return Flow.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Group } from "../../api/Groups";
|
import { Group } from "../../api/Groups";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-group-list")
|
@customElement("ak-group-list")
|
||||||
export class GroupListPage extends TablePage<Group> {
|
export class GroupListPage extends TablePage<Group> {
|
||||||
|
@ -30,6 +31,7 @@ export class GroupListPage extends TablePage<Group> {
|
||||||
return Group.list({
|
return Group.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import "./OutpostHealth";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/ModalButton";
|
import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/TokenCopyButton";
|
import "../../elements/buttons/TokenCopyButton";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-outpost-list")
|
@customElement("ak-outpost-list")
|
||||||
export class OutpostListPage extends TablePage<Outpost> {
|
export class OutpostListPage extends TablePage<Outpost> {
|
||||||
|
@ -29,6 +30,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||||
return Outpost.list({
|
return Outpost.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/ModalButton";
|
import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/Dropdown";
|
import "../../elements/buttons/Dropdown";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-outpost-service-connection-list")
|
@customElement("ak-outpost-service-connection-list")
|
||||||
export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> {
|
export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> {
|
||||||
|
@ -31,6 +32,7 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo
|
||||||
return OutpostServiceConnection.list({
|
return OutpostServiceConnection.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Policy } from "../../api/Policies";
|
import { Policy } from "../../api/Policies";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-policy-list")
|
@customElement("ak-policy-list")
|
||||||
export class PolicyListPage extends TablePage<Policy> {
|
export class PolicyListPage extends TablePage<Policy> {
|
||||||
|
@ -32,6 +33,7 @@ export class PolicyListPage extends TablePage<Policy> {
|
||||||
return Policy.list({
|
return Policy.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import "../../elements/buttons/Dropdown";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-property-mapping-list")
|
@customElement("ak-property-mapping-list")
|
||||||
export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||||
|
@ -35,6 +36,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||||
return PropertyMapping.list({
|
return PropertyMapping.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
managed__isnull: this.hideManaged,
|
managed__isnull: this.hideManaged,
|
||||||
});
|
});
|
||||||
|
|
|
@ -148,10 +148,16 @@ export class OAuth2ProviderViewPage extends Page {
|
||||||
</div>
|
</div>
|
||||||
<div class="pf-c-form__group">
|
<div class="pf-c-form__group">
|
||||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
<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>
|
</label>
|
||||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.user_info || "-"}" />
|
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.user_info || "-"}" />
|
||||||
</div>
|
</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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/Dropdown";
|
import "../../elements/buttons/Dropdown";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-provider-list")
|
@customElement("ak-provider-list")
|
||||||
export class ProviderListPage extends TablePage<Provider> {
|
export class ProviderListPage extends TablePage<Provider> {
|
||||||
|
@ -32,6 +33,7 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||||
return Provider.list({
|
return Provider.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/Dropdown";
|
import "../../elements/buttons/Dropdown";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-source-list")
|
@customElement("ak-source-list")
|
||||||
export class SourceListPage extends TablePage<Source> {
|
export class SourceListPage extends TablePage<Source> {
|
||||||
|
@ -32,6 +33,7 @@ export class SourceListPage extends TablePage<Source> {
|
||||||
return Source.list({
|
return Source.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Invitation } from "../../api/Invitations";
|
import { Invitation } from "../../api/Invitations";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-stage-invitation-list")
|
@customElement("ak-stage-invitation-list")
|
||||||
export class InvitationListPage extends TablePage<Invitation> {
|
export class InvitationListPage extends TablePage<Invitation> {
|
||||||
|
@ -30,6 +31,7 @@ export class InvitationListPage extends TablePage<Invitation> {
|
||||||
return Invitation.list({
|
return Invitation.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||||
import "../../elements/buttons/SpinnerButton";
|
import "../../elements/buttons/SpinnerButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Prompt } from "../../api/Prompts";
|
import { Prompt } from "../../api/Prompts";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-stage-prompt-list")
|
@customElement("ak-stage-prompt-list")
|
||||||
export class PromptListPage extends TablePage<Prompt> {
|
export class PromptListPage extends TablePage<Prompt> {
|
||||||
|
@ -30,6 +31,7 @@ export class PromptListPage extends TablePage<Prompt> {
|
||||||
return Prompt.list({
|
return Prompt.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||||
import "../../elements/buttons/Dropdown";
|
import "../../elements/buttons/Dropdown";
|
||||||
import { until } from "lit-html/directives/until";
|
import { until } from "lit-html/directives/until";
|
||||||
import { Stage } from "../../api/Flows";
|
import { Stage } from "../../api/Flows";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-stage-list")
|
@customElement("ak-stage-list")
|
||||||
export class StageListPage extends TablePage<Stage> {
|
export class StageListPage extends TablePage<Stage> {
|
||||||
|
@ -32,6 +33,7 @@ export class StageListPage extends TablePage<Stage> {
|
||||||
return Stage.list({
|
return Stage.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
|
||||||
import "../../elements/buttons/TokenCopyButton";
|
import "../../elements/buttons/TokenCopyButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { Token } from "../../api/Tokens";
|
import { Token } from "../../api/Tokens";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-token-list")
|
@customElement("ak-token-list")
|
||||||
export class TokenListPage extends TablePage<Token> {
|
export class TokenListPage extends TablePage<Token> {
|
||||||
|
@ -31,6 +32,7 @@ export class TokenListPage extends TablePage<Token> {
|
||||||
return Token.list({
|
return Token.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import "../../elements/buttons/Dropdown";
|
||||||
import "../../elements/buttons/TokenCopyButton";
|
import "../../elements/buttons/TokenCopyButton";
|
||||||
import { Table, TableColumn } from "../../elements/table/Table";
|
import { Table, TableColumn } from "../../elements/table/Table";
|
||||||
import { Token } from "../../api/Tokens";
|
import { Token } from "../../api/Tokens";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-token-user-list")
|
@customElement("ak-token-user-list")
|
||||||
export class UserTokenList extends Table<Token> {
|
export class UserTokenList extends Table<Token> {
|
||||||
|
@ -21,6 +22,7 @@ export class UserTokenList extends Table<Token> {
|
||||||
return Token.list({
|
return Token.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
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[] {
|
row(item: Token): TemplateResult[] {
|
||||||
return [
|
return [
|
||||||
html`${item.identifier}`,
|
html`${item.identifier}`,
|
||||||
|
|
|
@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
|
||||||
import "../../elements/buttons/ActionButton";
|
import "../../elements/buttons/ActionButton";
|
||||||
import { TableColumn } from "../../elements/table/Table";
|
import { TableColumn } from "../../elements/table/Table";
|
||||||
import { User } from "../../api/Users";
|
import { User } from "../../api/Users";
|
||||||
|
import { PAGE_SIZE } from "../../constants";
|
||||||
|
|
||||||
@customElement("ak-user-list")
|
@customElement("ak-user-list")
|
||||||
export class UserListPage extends TablePage<User> {
|
export class UserListPage extends TablePage<User> {
|
||||||
|
@ -31,6 +32,7 @@ export class UserListPage extends TablePage<User> {
|
||||||
return User.list({
|
return User.list({
|
||||||
ordering: this.order,
|
ordering: this.order,
|
||||||
page: page,
|
page: page,
|
||||||
|
page_size: PAGE_SIZE,
|
||||||
search: this.search || "",
|
search: this.search || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,5 +13,7 @@ See [Docker-compose](installation/docker-compose) or [Kubernetes](installation/k
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
||||||
![](/img/screen_apps.png)
|
Light | Dark
|
||||||
![](/img/screen_admin.png)
|
--- | ---
|
||||||
|
![](/img/screen_apps_light.png) | ![](/img/screen_apps_dark.png)
|
||||||
|
![](/img/screen_admin_light.png) | ![](/img/screen_admin_dark.png)
|
||||||
|
|
|
@ -123,7 +123,7 @@ The integrations affected are:
|
||||||
|
|
||||||
### docker-compose
|
### 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
|
### Kubernetes
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ This release does not introduce any new requirements.
|
||||||
|
|
||||||
### docker-compose
|
### 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
|
### Kubernetes
|
||||||
|
|
||||||
|
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 9.6 KiB |
|
@ -100,7 +100,7 @@ function Home() {
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col col--5">
|
<div className="col col--5">
|
||||||
<div>
|
<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>
|
</div>
|
||||||
<div className="col col--5 col--offset-2 padding-vert--xl">
|
<div className="col col--5 col--offset-2 padding-vert--xl">
|
||||||
|
@ -131,7 +131,7 @@ function Home() {
|
||||||
</div>
|
</div>
|
||||||
<div className="col col--5">
|
<div className="col col--5">
|
||||||
<div>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Before Width: | Height: | Size: 331 KiB |
After Width: | Height: | Size: 399 KiB |
After Width: | Height: | Size: 382 KiB |
Before Width: | Height: | Size: 534 KiB |
After Width: | Height: | Size: 747 KiB |
After Width: | Height: | Size: 747 KiB |