Compare commits

...

18 Commits

Author SHA1 Message Date
aef5c60a7b release: 0.13.0-rc3 2020-12-13 00:57:36 +01:00
d4c9c667c9 tests: fix URLs to use user-details instead of user-settings 2020-12-13 00:48:46 +01:00
96f0d582f0 core: load user detail form in an inner SiteShell so update doesn't reload entire page 2020-12-13 00:18:36 +01:00
7e8702a71e web: fix user detail form not working 2020-12-13 00:03:37 +01:00
1524061480 web: only auto-update slug when slug and name are already in sync 2020-12-12 23:45:47 +01:00
434922f702 web: make most client/network errors ignored by sentry 2020-12-12 23:32:55 +01:00
d2862ddc93 lifecycle: clean full redis as part of system migration 2020-12-12 23:30:49 +01:00
6e55431d4c stages/*: fix redirects not pointing to user_settings 2020-12-12 23:14:07 +01:00
01548c5e9c stages/*: fix links opening in SiteShell 2020-12-12 23:14:02 +01:00
bf1dae2dbe helm: make imagePullPolicy configurable 2020-12-12 23:13:58 +01:00
59c93defcf release: 0.13.0-rc2 2020-12-12 21:50:10 +01:00
a2a1a27502 web: fix icons not being included in static container 2020-12-12 21:49:00 +01:00
e3227e7d54 core: remove remaining references to old font 2020-12-12 21:41:12 +01:00
1f4a8fffdb docs: fix minor markdown and syntax errors 2020-12-12 21:30:05 +01:00
86b1183883 helm: bump version in readme 2020-12-12 21:27:05 +01:00
f781f4848c ci: fix release not depending on proxy build 2020-12-12 21:10:13 +01:00
19824d693c core: fix permission check for applications API 2020-12-12 21:00:35 +01:00
0694b911a4 docs: add changelog for 0.13 2020-12-12 21:00:23 +01:00
50 changed files with 250 additions and 137 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.13.0-rc1
current_version = 0.13.0-rc3
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
@ -23,6 +23,8 @@ values =
[bumpversion:file:helm/values.yaml]
[bumpversion:file:helm/README.md]
[bumpversion:file:helm/Chart.yaml]
[bumpversion:file:.github/workflows/release.yml]

View File

@ -18,11 +18,11 @@ jobs:
- name: Building Docker Image
run: docker build
--no-cache
-t beryju/authentik:0.13.0-rc1
-t beryju/authentik:0.13.0-rc3
-t beryju/authentik:latest
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik:0.13.0-rc1
run: docker push beryju/authentik:0.13.0-rc3
- name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik:latest
build-proxy:
@ -48,11 +48,11 @@ jobs:
cd proxy/
docker build \
--no-cache \
-t beryju/authentik-proxy:0.13.0-rc1 \
-t beryju/authentik-proxy:0.13.0-rc3 \
-t beryju/authentik-proxy:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-proxy:0.13.0-rc1
run: docker push beryju/authentik-proxy:0.13.0-rc3
- name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-proxy:latest
build-static:
@ -69,17 +69,18 @@ jobs:
cd web/
docker build \
--no-cache \
-t beryju/authentik-static:0.13.0-rc1 \
-t beryju/authentik-static:0.13.0-rc3 \
-t beryju/authentik-static:latest \
-f Dockerfile .
- name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-static:0.13.0-rc1
run: docker push beryju/authentik-static:0.13.0-rc3
- name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-static:latest
test-release:
needs:
- build-server
- build-static
- build-proxy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
@ -106,5 +107,5 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
tagName: 0.13.0-rc1
tagName: 0.13.0-rc3
environment: beryjuorg-prod

View File

@ -1,4 +1,4 @@
<img src="icons/icon_top_brand.svg" height="250" alt="authentik logo">
<img src="web/icons/icon_top_brand.svg" height="250" alt="authentik logo">
---

View File

@ -1,2 +1,2 @@
"""authentik"""
__version__ = "0.13.0-rc1"
__version__ = "0.13.0-rc3"

View File

@ -1,7 +1,10 @@
"""Application API Views"""
from django.db.models import QuerySet
from django.http.response import Http404
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import SerializerMethodField
from rest_framework.generics import get_object_or_404
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.serializers import ModelSerializer
@ -71,8 +74,12 @@ class ApplicationViewSet(ModelViewSet):
@action(detail=True)
def metrics(self, request: Request, slug: str):
"""Metrics for application logins"""
# TODO: Check app read and audit read perms
app = Application.objects.get(slug=slug)
app = get_object_or_404(
get_objects_for_user(request.user, "authentik_core.view_application"),
slug=slug,
)
if not request.user.has_perm("authentik_audit.view_event"):
raise Http404
return Response(
get_events_per_1h(
action=EventAction.AUTHORIZE_APPLICATION,

View File

@ -6,8 +6,6 @@
<html lang="en">
<head>
<link rel="preload" href="{% static 'dist/assets/fonts/DINEngschriftStd.woff2' %}" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="{% static 'dist/assets/fonts/DINEngschriftStd.woff' %}" as="font" type="font/woff" crossorigin>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{% block title %}{% trans title|default:config.authentik.branding.title %}{% endblock %}</title>

View File

@ -0,0 +1,26 @@
{% load i18n %}
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-title pf-m-md">
{% trans 'Update details' %}
</div>
<div class="pf-c-card__body">
<form action="" method="post" class="pf-c-form pf-m-horizontal">
{% include 'partials/form_horizontal.html' with form=form %}
{% block beneath_form %}
{% endblock %}
<div class="pf-c-form__group pf-m-action">
<div class="pf-c-form__horizontal-group">
<div class="pf-c-form__actions">
<input class="pf-c-button pf-m-primary" type="submit" value="{% trans 'Update' %}" />
{% if unenrollment_enabled %}
<a class="pf-c-button pf-m-danger"
href="{% url 'authentik_flows:default-unenrollment' %}?back={{ request.get_full_path }}">{%
trans "Delete account" %}</a>
{% endif %}
</div>
</div>
</div>
</form>
</div>
</div>

View File

@ -15,29 +15,9 @@
<section class="pf-c-page__main-section">
<div class="pf-u-display-flex pf-u-justify-content-center">
<div class="pf-u-w-75">
<div class="pf-c-card">
<div class="pf-c-card__header pf-c-title pf-m-md">
{% trans 'Update details' %}
</div>
<div class="pf-c-card__body">
<form action="" method="post" class="pf-c-form pf-m-horizontal">
{% include 'partials/form_horizontal.html' with form=form %}
{% block beneath_form %}
{% endblock %}
<div class="pf-c-form__group pf-m-action">
<div class="pf-c-form__horizontal-group">
<div class="pf-c-form__actions">
<input class="pf-c-button pf-m-primary" type="submit" value="{% trans 'Update' %}" />
{% if unenrollment_enabled %}
<a class="pf-c-button pf-m-danger"
href="{% url 'authentik_flows:default-unenrollment' %}?back={{ request.get_full_path }}">{% trans "Delete account" %}</a>
{% endif %}
</div>
</div>
</div>
</form>
</div>
</div>
<ak-site-shell url="{% url 'authentik_core:user-details' %}">
<div slot="body"></div>
</ak-site-shell>
</div>
</div>
</section>

View File

@ -34,9 +34,3 @@ class TestOverviewViews(TestCase):
self.assertEqual(
self.client.get(reverse("authentik_core:overview")).status_code, 200
)
def test_user_settings(self):
"""Test user settings"""
self.assertEqual(
self.client.get(reverse("authentik_core:user-settings")).status_code, 200
)

View File

@ -28,3 +28,9 @@ class TestUserViews(TestCase):
self.assertEqual(
self.client.get(reverse("authentik_core:user-settings")).status_code, 200
)
def test_user_details(self):
"""Test UserDetailsView"""
self.assertEqual(
self.client.get(reverse("authentik_core:user-details")).status_code, 200
)

View File

@ -7,6 +7,7 @@ urlpatterns = [
path("", shell.ShellView.as_view(), name="shell"),
# User views
path("-/user/", user.UserSettingsView.as_view(), name="user-settings"),
path("-/user/details/", user.UserDetailsView.as_view(), name="user-details"),
path("-/user/tokens/", user.TokenListView.as_view(), name="user-tokens"),
path(
"-/user/tokens/create/",

View File

@ -11,6 +11,7 @@ from django.http.response import HttpResponse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _
from django.views.generic import ListView, UpdateView
from django.views.generic.base import TemplateView
from guardian.mixins import PermissionListMixin, PermissionRequiredMixin
from guardian.shortcuts import get_objects_for_user
@ -26,14 +27,20 @@ from authentik.flows.models import Flow, FlowDesignation
from authentik.lib.views import CreateAssignPermView
class UserSettingsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Update User settings"""
class UserSettingsView(TemplateView):
"""Multiple SiteShells for user details and all stages"""
template_name = "user/settings.html"
class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Update User details"""
template_name = "user/details.html"
form_class = UserDetailForm
success_message = _("Successfully updated user.")
success_url = reverse_lazy("authentik_core:user-settings")
success_url = reverse_lazy("authentik_core:user-details")
def get_object(self):
return self.request.user

View File

@ -22,10 +22,10 @@
</ul>
{% if not state %}
{% if stage.configure_flow %}
<a href="{% url 'authentik_flows:configure' stage_uuid=stage.stage_uuid %}?next={{ request.get_full_path }}" class="pf-c-button pf-m-primary">{% trans "Enable Static Tokens" %}</a>
<a href="{% url 'authentik_flows:configure' stage_uuid=stage.stage_uuid %}?next={% url 'authentik_core:user-settings' %}" class="ak-root-link pf-c-button pf-m-primary">{% trans "Enable Static Tokens" %}</a>
{% endif %}
{% else %}
<a href="{% url 'authentik_stages_otp_static:disable' stage_uuid=stage.stage_uuid %}" class="pf-c-button pf-m-danger">{% trans "Disable Static Tokens" %}</a>
<a href="{% url 'authentik_stages_otp_static:disable' stage_uuid=stage.stage_uuid %}" class="ak-root-pf-c-button pf-m-danger">{% trans "Disable Static Tokens" %}</a>
{% endif %}
</div>
</div>

View File

@ -41,4 +41,4 @@ class DisableView(LoginRequiredMixin, View):
Event.new(
"static_otp_disable", message="User disabled Static OTP Tokens."
).from_http(request)
return redirect("authentik_stages_otp:otp-user-settings")
return redirect("authentik_core:user-settings")

View File

@ -18,10 +18,10 @@
<p>
{% if not state %}
{% if stage.configure_flow %}
<a href="{% url 'authentik_flows:configure' stage_uuid=stage.stage_uuid %}?next={{ request.get_full_path }}" class="pf-c-button pf-m-primary">{% trans "Enable Time-based OTP" %}</a>
<a href="{% url 'authentik_flows:configure' stage_uuid=stage.stage_uuid %}?next={% url 'authentik_core:user-settings' %}" class="ak-root-link pf-c-button pf-m-primary">{% trans "Enable Time-based OTP" %}</a>
{% endif %}
{% else %}
<a href="{% url 'authentik_stages_otp_time:disable' stage_uuid=stage.stage_uuid %}" class="pf-c-button pf-m-danger">{% trans "Disable Time-based OTP" %}</a>
<a href="{% url 'authentik_stages_otp_time:disable' stage_uuid=stage.stage_uuid %}" class="ak-root-pf-c-button pf-m-danger">{% trans "Disable Time-based OTP" %}</a>
{% endif %}
</p>
</div>

View File

@ -38,4 +38,4 @@ class DisableView(LoginRequiredMixin, View):
Event.new("totp_disable", message="User disabled Time-based OTP.").from_http(
request
)
return redirect("authentik_stages_otp:otp-user-settings")
return redirect("authentik_core:user-settings")

View File

@ -9,7 +9,7 @@
{% trans 'Reset your password' %}
</div>
<div class="pf-c-card__body">
<a class="pf-c-button pf-m-primary" href="{{ url }}">
<a class="pf-c-button pf-m-primary ak-root-link" href="{{ url }}">
{% trans 'Change password' %}
</a>
</div>

View File

@ -19,7 +19,7 @@ services:
networks:
- internal
server:
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc1}
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc3}
command: server
environment:
AUTHENTIK_REDIS__HOST: redis
@ -42,7 +42,7 @@ services:
env_file:
- .env
worker:
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc1}
image: beryju/authentik:${AUTHENTIK_TAG:-0.13.0-rc3}
command: worker
networks:
- internal
@ -56,7 +56,7 @@ services:
env_file:
- .env
static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.0-rc1}
image: beryju/authentik-static:${AUTHENTIK_TAG:-0.13.0-rc3}
networks:
- internal
labels:

View File

@ -4,8 +4,8 @@ name: authentik
home: https://goauthentik.io
sources:
- https://github.com/BeryJu/authentik
version: "0.13.0-rc1"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/icons/icon.svg
version: "0.13.0-rc3"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
dependencies:
- name: postgresql
version: 9.4.1

View File

@ -4,7 +4,8 @@
|-----------------------------------|-------------------------|-------------|
| image.name | beryju/authentik | Image used to run the authentik server and worker |
| image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) |
| image.tag | 0.12.5-stable | Image tag |
| image.tag | 0.13.0-rc3 | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment |
| kubernetesIntegration | true | Enable/disable the Kubernetes integration for authentik. This will create a service account for authentik to create and update outposts in authentik |

View File

@ -24,7 +24,7 @@ spec:
containers:
- name: {{ .Chart.Name }}-static
image: "{{ .Values.image.name_static }}:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
ports:
- name: http
containerPort: 80

View File

@ -45,6 +45,7 @@ spec:
initContainers:
- name: authentik-database-migrations
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
args: [migrate]
envFrom:
- configMapRef:
@ -69,6 +70,7 @@ spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
args: [server]
envFrom:
- configMapRef:

View File

@ -48,7 +48,7 @@ spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.name }}:{{ .Values.image.tag }}"
imagePullPolicy: IfNotPresent
imagePullPolicy: "{{ .Values.image.pullPolicy }}"
args: [worker]
envFrom:
- configMapRef:

View File

@ -1,5 +1,6 @@
image:
tag: gh-master
pullPolicy: Always
serverReplicas: 1
workerReplicas: 1

View File

@ -5,7 +5,8 @@ image:
name: beryju/authentik
name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 0.13.0-rc1
tag: 0.13.0-rc3
pullPolicy: IfNotPresent
serverReplicas: 1
workerReplicas: 1

View File

@ -1,4 +1,7 @@
# flake8: noqa
from redis import Redis
from authentik.lib.config import CONFIG
from lifecycle.migrate import BaseMigration
SQL_STATEMENT = """BEGIN TRANSACTION;
@ -103,3 +106,16 @@ class Migration(BaseMigration):
def run(self):
self.cur.execute(SQL_STATEMENT)
self.con.commit()
# We also need to clean the cache to make sure no pickeled objects still exist
for db in [
CONFIG.y("redis.message_queue_db"),
CONFIG.y("redis.cache_db"),
CONFIG.y("redis.ws_db"),
]:
redis = Redis(
host=CONFIG.y("redis.host"),
port=6379,
db=db,
password=CONFIG.y("redis.password"),
)
redis.flushall()

View File

@ -1,3 +1,3 @@
package pkg
const VERSION = "0.13.0-rc1"
const VERSION = "0.13.0-rc3"

View File

@ -142,7 +142,7 @@ class TestSourceOAuth2(SeleniumTestCase):
# Wait until we've logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
self.assertEqual(
self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo"
@ -224,7 +224,7 @@ class TestSourceOAuth2(SeleniumTestCase):
# Wait until we've logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
self.assertEqual(
self.driver.find_element(By.ID, "id_username").get_attribute("value"), "foo"
@ -317,7 +317,7 @@ class TestSourceOAuth1(SeleniumTestCase):
sleep(2)
# Wait until we've logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
self.assertEqual(
self.driver.find_element(By.ID, "id_username").get_attribute("value"),

View File

@ -134,7 +134,7 @@ class TestSourceSAML(SeleniumTestCase):
# Wait until we're logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
# Wait until we've loaded the user info page
self.assertNotEqual(
@ -185,7 +185,7 @@ class TestSourceSAML(SeleniumTestCase):
# Wait until we're logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
# Wait until we've loaded the user info page
self.assertNotEqual(
@ -234,7 +234,7 @@ class TestSourceSAML(SeleniumTestCase):
# Wait until we're logged in
self.wait_for_url(self.shell_url("authentik_core:overview"))
self.driver.get(self.url("authentik_core:user-settings"))
self.driver.get(self.url("authentik_core:user-details"))
# Wait until we've loaded the user info page
self.assertNotEqual(

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -16,7 +16,7 @@ const resources = [
{ src: "src/index.html", dest: "dist" },
{ src: "src/authentik.css", dest: "dist" },
{ src: "src/assets/*", dest: "dist/assets" },
{ src: "../icons/*", dest: "dist/assets/icons" },
{ src: "./icons/*", dest: "dist/assets/icons" },
];
export default [

View File

@ -2,6 +2,7 @@ import { DefaultClient } from "./client";
import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants";
import { SentryIgnoredError } from "../common/errors";
export class Config {
branding_logo: string;
@ -24,6 +25,12 @@ export class Config {
integrations: [new Integrations.BrowserTracing()],
tracesSampleRate: 1.0,
environment: config.error_reporting_environment,
beforeSend(event: Sentry.Event, hint: Sentry.EventHint) {
if (hint.originalException instanceof SentryIgnoredError) {
return null;
}
return event;
},
});
console.debug("authentik/config: Sentry enabled.");
}

1
web/src/common/errors.ts Normal file
View File

@ -0,0 +1 @@
export class SentryIgnoredError extends Error {}

View File

@ -28,4 +28,4 @@ export const ColorStyles = css`
background-color: var(--pf-global--danger-color--100);
}
`;
export const VERSION = "0.13.0-rc1";
export const VERSION = "0.13.0-rc3";

View File

@ -1,5 +1,6 @@
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import Chart from "chart.js";
import { showMessage } from "./messages/MessageContainer";
interface TickValue {
value: number;
@ -41,7 +42,13 @@ export class AdminLoginsChart extends LitElement {
firstUpdated(): void {
fetch(this.url)
.then((r) => r.json())
.catch((e) => console.error(e))
.catch((e) => {
showMessage({
level_tag: "error",
message: "Unexpected error"
});
console.log(e);
})
.then((r) => {
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
if (!canvas) {

View File

@ -13,6 +13,7 @@ import fa from "@fortawesome/fontawesome-free/css/solid.css";
import { convertToSlug } from "../../utils";
import { SpinnerButton } from "./SpinnerButton";
import { PRIMARY_CLASS } from "../../constants";
import { showMessage } from "../messages/MessageContainer";
@customElement("ak-modal-button")
export class ModalButton extends LitElement {
@ -63,15 +64,21 @@ export class ModalButton extends LitElement {
});
// Make name field update slug field
this.querySelectorAll<HTMLInputElement>("input[name=name]").forEach((input) => {
const form = input.closest("form");
if (form === null) {
return;
}
const slugField = form.querySelector<HTMLInputElement>("input[name=slug]");
if (!slugField) {
return;
}
// Only attach handler if the slug is already equal to the name
// if not, they are probably completely different and shouldn't update
// each other
if (convertToSlug(input.value) !== slugField.value) {
return;
}
input.addEventListener("input", () => {
const form = input.closest("form");
if (form === null) {
return;
}
const slugField = form.querySelector<HTMLInputElement>("input[name=slug]");
if (!slugField) {
return;
}
slugField.value = convertToSlug(input.value);
});
});
@ -110,7 +117,11 @@ export class ModalButton extends LitElement {
}
})
.catch((e) => {
console.error(e);
showMessage({
level_tag: "error",
message: "Unexpected error"
});
console.log(e);
});
});
});
@ -139,7 +150,11 @@ export class ModalButton extends LitElement {
});
})
.catch((e) => {
console.error(e);
showMessage({
level_tag: "error",
message: "Unexpected error"
});
console.log(e);
});
}
}

View File

@ -1,20 +1,6 @@
<!DOCTYPE html>
<html lang="en">
<head>
<link
rel="preload"
href="/static/authentik/fonts/DINEngschriftStd.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="/static/authentik/fonts/DINEngschriftStd.woff"
as="font"
type="font/woff"
crossorigin
/>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>authentik</title>

View File

@ -1,4 +1,5 @@
import { LitElement, html, customElement, property, TemplateResult } from "lit-element";
import { SentryIgnoredError } from "../../common/errors";
enum ResponseType {
redirect = "redirect",
@ -30,7 +31,7 @@ export class FlowShellCard extends LitElement {
// Fallback when the flow does not exist, just redirect to the root
window.location.pathname = "/";
} else if (!r.ok) {
throw Error(r.statusText);
throw new SentryIgnoredError(r.statusText);
}
return r;
})

View File

@ -8,6 +8,7 @@ import BackdropStyle from "@patternfly/patternfly/components/Backdrop/backdrop.c
import { SpinnerSize } from "../../elements/Spinner";
import { showMessage } from "../../elements/messages/MessageContainer";
import { gettext } from "django";
import { SentryIgnoredError } from "../../common/errors";
@customElement("ak-site-shell")
export class SiteShell extends LitElement {
@ -55,9 +56,16 @@ export class SiteShell extends LitElement {
}
loadContent(): void {
const bodySlot = this.querySelector("[slot=body]");
if (!bodySlot) {
return;
}
if (!this._url) {
return;
}
if (this.loading) {
return;
}
this.loading = true;
fetch(this._url)
.then((r) => {
@ -70,52 +78,82 @@ export class SiteShell extends LitElement {
level_tag: "error",
message: gettext(`Request failed: ${r.statusText}`),
});
throw new Error("Request failed");
this.loading = false;
throw new SentryIgnoredError("Request failed");
})
.then((r) => r.text())
.then((t) => {
const bodySlot = this.querySelector("[slot=body]");
if (!bodySlot) {
return;
}
bodySlot.innerHTML = t;
.then((text) => {
bodySlot.innerHTML = text;
this.updateHandlers();
})
.then(() => {
// Ensure anchors only change the hash
this.querySelectorAll<HTMLAnchorElement>("a:not(.ak-root-link)").forEach((a) => {
if (a.href === "") {
return;
}
try {
const url = new URL(a.href);
const qs = url.search || "";
a.href = `#${url.pathname}${qs}`;
} catch (e) {
console.debug(`authentik/site-shell: error ${e}`);
a.href = `#${a.href}`;
}
});
// Create refresh buttons
this.querySelectorAll("[role=ak-refresh]").forEach((rt) => {
rt.addEventListener("click", () => {
this.loadContent();
});
});
// Make get forms (search bar) notify us on submit so we can change the hash
this.querySelectorAll("form").forEach((f) => {
f.addEventListener("submit", (e) => {
e.preventDefault();
const formData = new FormData(f);
const qs = new URLSearchParams((<any>formData)).toString(); // eslint-disable-line
window.location.hash = `#${this._url}?${qs}`;
});
});
setTimeout(() => {
this.loading = false;
}, 100);
});
}
updateHandlers(): void {
// Ensure anchors only change the hash
this.querySelectorAll<HTMLAnchorElement>("a:not(.ak-root-link)").forEach((a) => {
if (a.href === "") {
return;
}
try {
const url = new URL(a.href);
const qs = url.search || "";
a.href = `#${url.pathname}${qs}`;
} catch (e) {
console.debug(`authentik/site-shell: error ${e}`);
a.href = `#${a.href}`;
}
});
// Create refresh buttons
this.querySelectorAll("[role=ak-refresh]").forEach((rt) => {
rt.addEventListener("click", () => {
this.loadContent();
});
});
// Make get forms (search bar) notify us on submit so we can change the hash
this.querySelectorAll<HTMLFormElement>("form[method=get]").forEach((form) => {
form.addEventListener("submit", (e) => {
e.preventDefault();
const formData = new FormData(form);
const qs = new URLSearchParams((<any>formData)).toString(); // eslint-disable-line
window.location.hash = `#${this._url}?${qs}`;
});
});
// Make forms with POST Method have a correct action set
this.querySelectorAll<HTMLFormElement>("form[method=post]").forEach((form) => {
form.addEventListener("submit", (e) => {
e.preventDefault();
const formData = new FormData(form);
fetch(this._url ? this._url : form.action, {
method: form.method,
body: formData,
})
.then((response) => {
return response.text();
})
.then((data) => {
const bodySlot = this.querySelector("[slot=body]");
if (!bodySlot) {
return;
}
bodySlot.innerHTML = data;
this.updateHandlers();
})
.catch((e) => {
showMessage({
level_tag: "error",
message: "Unexpected error"
});
console.log(e);
});
});
});
}
render(): TemplateResult {
return html` ${this.loading ?
html`<div class="pf-c-backdrop">

View File

@ -15,7 +15,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=0.13.0-rc1 >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=0.13.0-rc3 >> .env`
If this is a fresh authentik install run the following commands to generate a password:

View File

@ -22,7 +22,7 @@ image:
name: beryju/authentik
name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 0.13.0-rc1
tag: 0.13.0-rc3
serverReplicas: 1
workerReplicas: 1

View File

@ -4,6 +4,21 @@ title: Upgrading to 0.13 (passbook -> authentik)
After a long back and forth, we've finally switched to a more permanent name. Whilst the upgrade is pretty much seamless, there are some things you have to change before upgrading.
# Headline changes
- New name (https://github.com/BeryJu/authentik/pull/361)
- The web interface is now a semi-SPA Experience. This means that most operations are done through Asynchronous requests
In this initial release, this brings features such as a refresh button, a generally better User experience due to shorter loading times
and fewer visual context changes.
- The web interface now has a darkmode, which is enabled automatically based on your Operating system.
## Smaller changes
- Add better support for Docker Service Connections with Certificates
- Fix application API not returning the same format as other APIs
## Upgrading
### docker-compose
@ -30,7 +45,7 @@ helm repo remove passbook
helm repo add authentik https://docker.beryju.org/chartrepo/authentik
```
:::notice
:::note
If you've set any custom image names in your values file, make sure to change them to authentik before upgrading.
:::
@ -51,7 +66,7 @@ helm upgrade passbook authentik/authentik --devel -f values.yaml
- Some default values change, for example the SAML Provider's default issuer.
This only makes a difference for newly created objects.
This only makes a difference for newly created providers.
- Expression Policies variables change