Merge branch 'main' into web/wdio-2
* main: web/admin: use <pre> for order field on bound elements (#7031) blueprints: fix mismatched user-login stage order (#7030) stages/email: rework email templates (#7029) website/docs: add notice for nginx ingress configuration requirement (#7027) translate: Updates for web/xliff/en.xlf in fr web: locales: rename fr_FR to fr to match transifex events: fix error when storing events with date/time/datetime/etc (#7028) stages/invitation: fix mis-matched serializer class for invitation (#7018) web: bump mermaid from 10.4.0 to 10.5.0 in /web (#7026) web: bump core-js from 3.32.2 to 3.33.0 in /web (#7020) core: bump webauthn from 1.10.1 to 1.11.0 (#7021) core: bump pylint from 2.17.6 to 2.17.7 (#7022) core: bump django-redis from 5.3.0 to 5.4.0 (#7023) core: bump packaging from 23.1 to 23.2 (#7024)
This commit is contained in:
commit
84cbbc6642
|
@ -6,6 +6,7 @@ from django.test import TestCase
|
|||
|
||||
from authentik.blueprints.v1.importer import is_model_allowed
|
||||
from authentik.lib.models import SerializerModel
|
||||
from authentik.providers.oauth2.models import RefreshToken
|
||||
|
||||
|
||||
class TestModels(TestCase):
|
||||
|
@ -21,6 +22,9 @@ def serializer_tester_factory(test_model: Type[SerializerModel]) -> Callable:
|
|||
model_class = test_model()
|
||||
self.assertTrue(isinstance(model_class, SerializerModel))
|
||||
self.assertIsNotNone(model_class.serializer)
|
||||
if model_class.serializer.Meta().model == RefreshToken:
|
||||
return
|
||||
self.assertEqual(model_class.serializer.Meta().model, test_model)
|
||||
|
||||
return tester
|
||||
|
||||
|
|
|
@ -436,32 +436,39 @@ class NotificationTransport(SerializerModel):
|
|||
|
||||
def send_email(self, notification: "Notification") -> list[str]:
|
||||
"""Send notification via global email configuration"""
|
||||
subject = "authentik Notification: "
|
||||
key_value = {
|
||||
"user_email": notification.user.email,
|
||||
"user_username": notification.user.username,
|
||||
subject_prefix = "authentik Notification: "
|
||||
context = {
|
||||
"key_value": {
|
||||
"user_email": notification.user.email,
|
||||
"user_username": notification.user.username,
|
||||
},
|
||||
"body": notification.body,
|
||||
"title": "",
|
||||
}
|
||||
if notification.event and notification.event.user:
|
||||
key_value["event_user_email"] = notification.event.user.get("email", None)
|
||||
key_value["event_user_username"] = notification.event.user.get("username", None)
|
||||
context["key_value"]["event_user_email"] = notification.event.user.get("email", None)
|
||||
context["key_value"]["event_user_username"] = notification.event.user.get(
|
||||
"username", None
|
||||
)
|
||||
if notification.event:
|
||||
subject += notification.event.action
|
||||
context["title"] += notification.event.action
|
||||
for key, value in notification.event.context.items():
|
||||
if not isinstance(value, str):
|
||||
continue
|
||||
key_value[key] = value
|
||||
context["key_value"][key] = value
|
||||
else:
|
||||
subject += notification.body[:75]
|
||||
context["title"] += notification.body[:75]
|
||||
# TODO: improve permission check
|
||||
if notification.user.is_superuser:
|
||||
context["source"] = {
|
||||
"from": self.name,
|
||||
}
|
||||
mail = TemplateEmailMessage(
|
||||
subject=subject,
|
||||
subject=subject_prefix + context["title"],
|
||||
to=[notification.user.email],
|
||||
language=notification.user.locale(),
|
||||
template_name="email/generic.html",
|
||||
template_context={
|
||||
"title": subject,
|
||||
"body": notification.body,
|
||||
"key_value": key_value,
|
||||
},
|
||||
template_name="email/event_notification.html",
|
||||
template_context=context,
|
||||
)
|
||||
# Email is sent directly here, as the call to send() should have been from a task.
|
||||
try:
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import re
|
||||
from copy import copy
|
||||
from dataclasses import asdict, is_dataclass
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from types import GeneratorType
|
||||
|
@ -13,6 +14,7 @@ from django.core.handlers.wsgi import WSGIRequest
|
|||
from django.db import models
|
||||
from django.db.models.base import Model
|
||||
from django.http.request import HttpRequest
|
||||
from django.utils import timezone
|
||||
from django.views.debug import SafeExceptionReporterFilter
|
||||
from geoip2.models import City
|
||||
from guardian.utils import get_anonymous_user
|
||||
|
@ -84,7 +86,7 @@ def get_user(user: User, original_user: Optional[User] = None) -> dict[str, Any]
|
|||
return user_data
|
||||
|
||||
|
||||
# pylint: disable=too-many-return-statements
|
||||
# pylint: disable=too-many-return-statements,too-many-branches
|
||||
def sanitize_item(value: Any) -> Any:
|
||||
"""Sanitize a single item, ensure it is JSON parsable"""
|
||||
if is_dataclass(value):
|
||||
|
@ -134,6 +136,23 @@ def sanitize_item(value: Any) -> Any:
|
|||
"type": value.__name__,
|
||||
"module": value.__module__,
|
||||
}
|
||||
# See
|
||||
# https://github.com/encode/django-rest-framework/blob/master/rest_framework/utils/encoders.py
|
||||
# For Date Time string spec, see ECMA 262
|
||||
# https://ecma-international.org/ecma-262/5.1/#sec-15.9.1.15
|
||||
if isinstance(value, datetime):
|
||||
representation = value.isoformat()
|
||||
if representation.endswith("+00:00"):
|
||||
representation = representation[:-6] + "Z"
|
||||
return representation
|
||||
if isinstance(value, date):
|
||||
return value.isoformat()
|
||||
if isinstance(value, time):
|
||||
if timezone and timezone.is_aware(value):
|
||||
raise ValueError("JSON can't represent timezone-aware times.")
|
||||
return value.isoformat()
|
||||
if isinstance(value, timedelta):
|
||||
return str(value.total_seconds())
|
||||
return value
|
||||
|
||||
|
||||
|
|
|
@ -1,205 +0,0 @@
|
|||
/* authentik Email CSS */
|
||||
* {
|
||||
margin: 0;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
body {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-webkit-text-size-adjust: none;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
line-height: 1.6em;
|
||||
}
|
||||
table td {
|
||||
vertical-align: top;
|
||||
}
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
.body-wrap {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
display: block !important;
|
||||
max-width: 600px !important;
|
||||
margin: 0 auto !important;
|
||||
clear: both !important;
|
||||
}
|
||||
.content {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
}
|
||||
.main {
|
||||
background-color: #fff;
|
||||
border: 1px solid #e9e9e9;
|
||||
}
|
||||
.content-wrap {
|
||||
padding: 20px;
|
||||
}
|
||||
.content-block {
|
||||
padding: 0 0 20px;
|
||||
}
|
||||
.header {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.footer {
|
||||
width: 100%;
|
||||
clear: both;
|
||||
color: #999;
|
||||
padding: 20px;
|
||||
}
|
||||
.footer p, .footer a, .footer td {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
h1, h2, h3 {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
color: #000;
|
||||
margin: 40px 0 0;
|
||||
line-height: 1.2em;
|
||||
font-weight: 400;
|
||||
}
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 500;
|
||||
}
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
}
|
||||
h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
p, ul, ol {
|
||||
margin-bottom: 10px;
|
||||
font-weight: normal;
|
||||
}
|
||||
p li, ul li, ol li {
|
||||
margin-left: 5px;
|
||||
list-style-position: inside;
|
||||
}
|
||||
a {
|
||||
color: #348eda;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.btn-primary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #348eda;
|
||||
border: solid #348eda;
|
||||
border-width: 10px 20px;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.btn-primary a {
|
||||
color: #fff;
|
||||
}
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
.alert {
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
font-weight: 500;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.alert a {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
}
|
||||
.alert-brand {
|
||||
background-color: #fd4b2d;
|
||||
}
|
||||
.alert-warning {
|
||||
background-color: #F0AB00;
|
||||
}
|
||||
.alert-danger {
|
||||
background-color: #C9190B;
|
||||
}
|
||||
.alert-success {
|
||||
background-color: #3E8635;
|
||||
}
|
||||
.body {
|
||||
margin: 40px auto;
|
||||
text-align: left;
|
||||
width: 80%;
|
||||
}
|
||||
.body td {
|
||||
padding: 5px 0;
|
||||
}
|
||||
.body .body-items {
|
||||
width: 100%;
|
||||
}
|
||||
.body .body-items td {
|
||||
border-top: #eee 1px solid;
|
||||
}
|
||||
.body .body-items .total td {
|
||||
border-top: 2px solid #333;
|
||||
border-bottom: 2px solid #333;
|
||||
font-weight: 700;
|
||||
}
|
||||
@media only screen and (max-width: 640px) {
|
||||
body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
h1, h2, h3, h4 {
|
||||
font-weight: 800 !important;
|
||||
margin: 20px 0 5px !important;
|
||||
}
|
||||
h1 {
|
||||
font-size: 22px !important;
|
||||
}
|
||||
h2 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
h3 {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
.container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.content-wrap {
|
||||
padding: 10px !important;
|
||||
}
|
||||
.body {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
|
@ -4,35 +4,38 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<td>
|
||||
<h2>
|
||||
{% trans 'Welcome!' %}
|
||||
</h2>
|
||||
<p>
|
||||
{% trans "We're excited to have you get started. First, you need to confirm your account. Just press the button below."%}
|
||||
</p>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<h1>
|
||||
{% trans 'Welcome!' %}
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a id="confirm" href="{{ url }}" rel="noopener noreferrer" target="_blank">{% trans 'Confirm Account' %}</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<td align="center" style="max-width: 300px; padding: 20px 0; color: #212124;">
|
||||
{% trans "We're excited to have you get started. First, you need to confirm your account. Just press the button below."%}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>
|
||||
<tr>
|
||||
<td align="center" class="btn btn-primary">
|
||||
<a id="confirm" href="{{ url }}" rel="noopener noreferrer" target="_blank">{% trans 'Confirm Account' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<td>
|
||||
{% endblock %}
|
||||
|
||||
{% block sub_content %}
|
||||
<tr>
|
||||
<td style="padding: 20px; font-size: 12px; color: #212124;word-break: break-all; overflow-wrap: break-word;" align="center">
|
||||
{% blocktrans with url=url %}
|
||||
If that doesn't work, copy and paste the following link in your browser: {{ url }}
|
||||
{% endblocktrans %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "If you have any questions, just reply to this email—we're always happy to help out." %}
|
||||
</p>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,34 +1,131 @@
|
|||
{% load authentik_stages_email %}
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title></title>
|
||||
<style>{% inline_static_ascii "stages/email/css/base.css" %}</style>
|
||||
</head>
|
||||
<body itemscope itemtype="http://schema.org/EmailMessage">
|
||||
<table class="body-wrap">
|
||||
<html xmlns="http://www.w3.org/1999/xhtm=l">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width">
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #212124;
|
||||
}
|
||||
|
||||
h2 {
|
||||
display: inline-block;
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 28px;
|
||||
line-height: 125%;
|
||||
font-weight: 700;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.flexibleImage {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
img.logo {
|
||||
max-width: 100%;
|
||||
max-height: 35px;
|
||||
}
|
||||
|
||||
.properties-table {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
font-family: Arial, sans-serif;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.properties-table tr:first-child {
|
||||
border-top: 1px solid rgba(196, 196, 196, 0.2);
|
||||
}
|
||||
|
||||
.properties-table tr:first-child>td {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.properties-table tr:last-child {
|
||||
border-bottom: 1px solid rgba(196, 196, 196, 0.2);
|
||||
}
|
||||
|
||||
.properties-table tr:last-child>td {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.properties-table td {
|
||||
line-height: 24px;
|
||||
vertical-align: top;
|
||||
padding: 4px 15px;
|
||||
}
|
||||
|
||||
.td-right {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-primary {
|
||||
text-decoration: none;
|
||||
color: #FFF;
|
||||
background-color: #348eda;
|
||||
border: solid #348eda;
|
||||
width: 100%;
|
||||
line-height: 2em;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
.btn-primary a {
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<center>
|
||||
<div style="-ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; table-layout: fixed; width: 100%; max-width: 448px; padding: 60px 20px; font-size: 14px;">
|
||||
<table border="0" align="center" width="100%">
|
||||
<tr>
|
||||
<td></td>
|
||||
<td class="container" width="600">
|
||||
<div class="content">
|
||||
<table class="main" width="100%" cellpadding="0" cellspacing="0" itemprop="action" itemscope itemtype="http://schema.org/ConfirmAction">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</table>
|
||||
<div class="footer">
|
||||
<table width="100%">
|
||||
<tr>
|
||||
<td class="align-center content-block">Powered by <a href="https://goauthentik.io?utm_source=authentik&utm_medium=email">authentik</a>.</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td></td>
|
||||
<td style="padding: 20px;border: 1px solid #c1c1c1;">
|
||||
<table width="100%" style="background-color: #FFFFFF; border-spacing: 0; margin-top: 15px;">
|
||||
<tr height="80">
|
||||
<td align="center" style="padding: 20px 0;">
|
||||
<img src="{% block logo_url %}cid:logo.png{% endblock %}" border="0=" alt="authentik logo" class="flexibleImage logo">
|
||||
</td>
|
||||
</tr>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
<tr>
|
||||
<td>
|
||||
<table border="0" style="margin-top: 10px;" width="100%">
|
||||
<tr>
|
||||
<td style="background: #FAFBFB;">
|
||||
<table style="width: 100%;">
|
||||
{% block sub_content %}
|
||||
{% endblock %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
Powered by <a href="https://goauthentik.io?utm_source=authentik&utm_medium=email">authentik</a>.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</center>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
{% extends "email/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<tr>
|
||||
<td align="center">
|
||||
<h1>
|
||||
{{ title }}
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td align="center" style="max-width: 300px; padding: 20px 0; color: #212124;">
|
||||
{{ body }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if key_value %}
|
||||
<tr>
|
||||
<td>
|
||||
<table class="properties-table" width="100%">
|
||||
<tbody>
|
||||
{% for key, value in key_value.items %}
|
||||
<tr>
|
||||
<td class="td-right">{{ key }}</td>
|
||||
<td class="td-left">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
||||
{% block sub_content %}
|
||||
{% if source %}
|
||||
<tr>
|
||||
<td style="padding: 20px; font-size: 12px; color: #212124;" align="center">
|
||||
{% blocktranslate with name=source.from %}
|
||||
This email was sent from the notification transport <code>{{name}}</code>.
|
||||
{% endblocktranslate %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
{% endblock %}
|
|
@ -1,45 +0,0 @@
|
|||
{% extends "email/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block content %}
|
||||
<tr>
|
||||
<td class="alert alert-brand">
|
||||
{{ title }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-wrap">
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
{{ body }}
|
||||
</td>
|
||||
</tr>
|
||||
{% if key_value %}
|
||||
<tr>
|
||||
<td class="content-block align-center">
|
||||
<table class="body">
|
||||
<tr>
|
||||
<td>{% trans "Additional Information" %}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table class="body-items" cellpadding="0" cellspacing="0">
|
||||
{% for key, value in key_value.items %}
|
||||
<tr>
|
||||
<td>{{ key }}</td>
|
||||
<td class="align-right">{{ value }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
|
@ -5,49 +5,40 @@
|
|||
|
||||
{% block content %}
|
||||
<tr>
|
||||
<td class="alert alert-success">
|
||||
{% blocktrans with username=user.username %}
|
||||
Hi {{ username }},
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
<td align="center">
|
||||
<h1>
|
||||
{% blocktrans with username=user.username %}
|
||||
Hi {{ username }},
|
||||
{% endblocktrans %}
|
||||
</h1>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-wrap">
|
||||
<table width="100%" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
{% blocktrans %}
|
||||
You recently requested to change your password for your authentik account. Use the button below to set a new password.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0" class="btn btn-primary">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td> <a id="confirm" href="{{ url }}" rel="noopener noreferrer" target="_blank">{% trans 'Reset Password' %}</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="content-block">
|
||||
{% blocktrans with expires=expires|naturaltime %}
|
||||
If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
<td align="center">
|
||||
<table border="0">
|
||||
<tr>
|
||||
<td align="center" style="max-width: 300px; padding: 20px 0; color: #212124;">
|
||||
{% blocktrans %}
|
||||
You recently requested to change your password for your authentik account. Use the button below to set a new password.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" class="btn btn-primary">
|
||||
<a id="confirm" href="{{ url }}" rel="noopener noreferrer" target="_blank">{% trans 'Reset Password' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
||||
{% block sub_content %}
|
||||
<tr>
|
||||
<td style="padding: 20px; font-size: 12px; color: #212124;" align="center">
|
||||
{% blocktrans with expires=expires|naturaltime %}
|
||||
If you did not request a password change, please ignore this Email. The link above is valid for {{ expires }}.
|
||||
{% endblocktrans %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,9 +1,21 @@
|
|||
"""email utils"""
|
||||
from email.mime.image import MIMEImage
|
||||
from functools import lru_cache
|
||||
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils import translation
|
||||
|
||||
|
||||
@lru_cache()
|
||||
def logo_data():
|
||||
"""Get logo as MIME Image for emails"""
|
||||
with open("web/icons/icon_left_brand.png", "rb") as _logo_file:
|
||||
logo = MIMEImage(_logo_file.read())
|
||||
logo.add_header("Content-ID", "logo.png")
|
||||
return logo
|
||||
|
||||
|
||||
class TemplateEmailMessage(EmailMultiAlternatives):
|
||||
"""Wrapper around EmailMultiAlternatives with integrated template rendering"""
|
||||
|
||||
|
@ -12,4 +24,6 @@ class TemplateEmailMessage(EmailMultiAlternatives):
|
|||
html_content = render_to_string(template_name, template_context)
|
||||
super().__init__(**kwargs)
|
||||
self.content_subtype = "html"
|
||||
self.mixed_subtype = "related"
|
||||
self.attach(logo_data())
|
||||
self.attach_alternative(html_content, "text/html")
|
||||
|
|
|
@ -73,12 +73,12 @@ class Invitation(SerializerModel, ExpiringModel):
|
|||
|
||||
@property
|
||||
def serializer(self) -> Serializer:
|
||||
from authentik.stages.consent.api import UserConsentSerializer
|
||||
from authentik.stages.invitation.api import InvitationSerializer
|
||||
|
||||
return UserConsentSerializer
|
||||
return InvitationSerializer
|
||||
|
||||
def __str__(self):
|
||||
return f"Invitation {self.invite_uuid.hex} created by {self.created_by}"
|
||||
return f"Invitation {str(self.invite_uuid)} created by {self.created_by}"
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Invitation")
|
||||
|
|
|
@ -132,5 +132,5 @@ entries:
|
|||
- identifiers:
|
||||
target: !KeyOf flow
|
||||
stage: !KeyOf default-enrollment-user-login
|
||||
order: 40
|
||||
order: 100
|
||||
model: authentik_flows.flowstagebinding
|
||||
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-09-15 09:51+0000\n"
|
||||
"POT-Creation-Date: 2023-10-02 12:46+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
@ -375,63 +375,63 @@ msgstr ""
|
|||
msgid "Event user"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:484
|
||||
#: authentik/events/models.py:491
|
||||
msgid "Notification Transport"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:485
|
||||
#: authentik/events/models.py:492
|
||||
msgid "Notification Transports"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:491
|
||||
#: authentik/events/models.py:498
|
||||
msgid "Notice"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:492
|
||||
#: authentik/events/models.py:499
|
||||
msgid "Warning"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:493
|
||||
#: authentik/events/models.py:500
|
||||
msgid "Alert"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:518
|
||||
#: authentik/events/models.py:525
|
||||
msgid "Notification"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:519
|
||||
#: authentik/events/models.py:526
|
||||
msgid "Notifications"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:529
|
||||
#: authentik/events/models.py:536
|
||||
msgid ""
|
||||
"Select which transports should be used to notify the user. If none are "
|
||||
"selected, the notification will only be shown in the authentik UI."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:537
|
||||
#: authentik/events/models.py:544
|
||||
msgid "Controls which severity level the created notifications will have."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:542
|
||||
#: authentik/events/models.py:549
|
||||
msgid ""
|
||||
"Define which group of users this notification should be sent and shown to. "
|
||||
"If left empty, Notification won't ben sent."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:560
|
||||
#: authentik/events/models.py:567
|
||||
msgid "Notification Rule"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:561
|
||||
#: authentik/events/models.py:568
|
||||
msgid "Notification Rules"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:581
|
||||
#: authentik/events/models.py:588
|
||||
msgid "Webhook Mapping"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/events/models.py:582
|
||||
#: authentik/events/models.py:589
|
||||
msgid "Webhook Mappings"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2099,21 +2099,21 @@ msgstr ""
|
|||
msgid "Email sent."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:9
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:10
|
||||
msgid "Welcome!"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:12
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:19
|
||||
msgid ""
|
||||
"We're excited to have you get started. First, you need to confirm your "
|
||||
"account. Just press the button below."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:21
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:24
|
||||
msgid "Confirm Account"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:30
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:36
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
|
@ -2122,43 +2122,42 @@ msgid ""
|
|||
" "
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/account_confirmation.html:35
|
||||
msgid ""
|
||||
"If you have any questions, just reply to this email—we're always happy to "
|
||||
"help out."
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/generic.html:24
|
||||
msgid "Additional Information"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/password_reset.html:9
|
||||
#: authentik/stages/email/templates/email/event_notification.html:46
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" Hi %(username)s,\n"
|
||||
" "
|
||||
" This email was sent from the notification transport <code>%(name)s</"
|
||||
"code>.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/password_reset.html:19
|
||||
#: authentik/stages/email/templates/email/password_reset.html:10
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" You recently requested to change your password for your "
|
||||
"authentik account. Use the button below to set a new password.\n"
|
||||
" "
|
||||
" Hi %(username)s,\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/password_reset.html:33
|
||||
#: authentik/stages/email/templates/email/password_reset.html:21
|
||||
msgid ""
|
||||
"\n"
|
||||
" You recently requested to change your password for your authentik "
|
||||
"account. Use the button below to set a new password.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/password_reset.html:28
|
||||
msgid "Reset Password"
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/password_reset.html:45
|
||||
#: authentik/stages/email/templates/email/password_reset.html:39
|
||||
#, python-format
|
||||
msgid ""
|
||||
"\n"
|
||||
" If you did not request a password change, please ignore "
|
||||
"this Email. The link above is valid for %(expires)s.\n"
|
||||
" "
|
||||
" If you did not request a password change, please ignore this Email. The "
|
||||
"link above is valid for %(expires)s.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
|
||||
#: authentik/stages/email/templates/email/setup.html:9
|
||||
|
|
36
poetry.lock
generated
36
poetry.lock
generated
|
@ -265,13 +265,13 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "astroid"
|
||||
version = "2.15.7"
|
||||
version = "2.15.8"
|
||||
description = "An abstract syntax tree for Python with inference support."
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "astroid-2.15.7-py3-none-any.whl", hash = "sha256:958f280532e36ca84a13023f15cb1556fb6792d193acb87e1f3ca536b6fa6bd2"},
|
||||
{file = "astroid-2.15.7.tar.gz", hash = "sha256:c522f2832a900e27a7d284b9b6ef670d2495f760ede3c8c0b004a5641d3c5987"},
|
||||
{file = "astroid-2.15.8-py3-none-any.whl", hash = "sha256:1aa149fc5c6589e3d0ece885b4491acd80af4f087baafa3fb5203b113e68cd3c"},
|
||||
{file = "astroid-2.15.8.tar.gz", hash = "sha256:6c107453dffee9055899705de3c9ead36e74119cee151e5a9aaf7f0b0e020a6a"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -1201,13 +1201,13 @@ prometheus-client = ">=0.7"
|
|||
|
||||
[[package]]
|
||||
name = "django-redis"
|
||||
version = "5.3.0"
|
||||
version = "5.4.0"
|
||||
description = "Full featured redis cache backend for Django."
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
files = [
|
||||
{file = "django-redis-5.3.0.tar.gz", hash = "sha256:8bc5793ec06b28ea802aad85ec437e7646511d4e571e07ccad19cfed8b9ddd44"},
|
||||
{file = "django_redis-5.3.0-py3-none-any.whl", hash = "sha256:2d8660d39f586c41c9907d5395693c477434141690fd7eca9d32376af00b0aac"},
|
||||
{file = "django-redis-5.4.0.tar.gz", hash = "sha256:6a02abaa34b0fea8bf9b707d2c363ab6adc7409950b2db93602e6cb292818c42"},
|
||||
{file = "django_redis-5.4.0-py3-none-any.whl", hash = "sha256:ebc88df7da810732e2af9987f7f426c96204bf89319df4c6da6ca9a2942edd5b"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
|
@ -2439,13 +2439,13 @@ attrs = ">=19.2.0"
|
|||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "23.1"
|
||||
version = "23.2"
|
||||
description = "Core utilities for Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
files = [
|
||||
{file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"},
|
||||
{file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"},
|
||||
{file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"},
|
||||
{file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2878,17 +2878,17 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"]
|
|||
|
||||
[[package]]
|
||||
name = "pylint"
|
||||
version = "2.17.6"
|
||||
version = "2.17.7"
|
||||
description = "python code static checker"
|
||||
optional = false
|
||||
python-versions = ">=3.7.2"
|
||||
files = [
|
||||
{file = "pylint-2.17.6-py3-none-any.whl", hash = "sha256:18a1412e873caf8ffb56b760ce1b5643675af23e6173a247b502406b24c716af"},
|
||||
{file = "pylint-2.17.6.tar.gz", hash = "sha256:be928cce5c76bf9acdc65ad01447a1e0b1a7bccffc609fb7fc40f2513045bd05"},
|
||||
{file = "pylint-2.17.7-py3-none-any.whl", hash = "sha256:27a8d4c7ddc8c2f8c18aa0050148f89ffc09838142193fdbe98f172781a3ff87"},
|
||||
{file = "pylint-2.17.7.tar.gz", hash = "sha256:f4fcac7ae74cfe36bc8451e931d8438e4a476c20314b1101c458ad0f05191fad"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
astroid = ">=2.15.7,<=2.17.0-dev0"
|
||||
astroid = ">=2.15.8,<=2.17.0-dev0"
|
||||
colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""}
|
||||
dill = {version = ">=0.3.6", markers = "python_version >= \"3.11\""}
|
||||
isort = ">=4.2.5,<6"
|
||||
|
@ -4069,19 +4069,19 @@ files = [
|
|||
|
||||
[[package]]
|
||||
name = "webauthn"
|
||||
version = "1.10.1"
|
||||
version = "1.11.0"
|
||||
description = "Pythonic WebAuthn"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
files = [
|
||||
{file = "webauthn-1.10.1-py3-none-any.whl", hash = "sha256:1d6b2c0753344df4c067a478f0e7f0e6cbb561f3d40342472f421657cdd16d41"},
|
||||
{file = "webauthn-1.10.1.tar.gz", hash = "sha256:94fc8265cccce88e1e43dc2008f3d8bb1f05fdd547246efa1be8ef684a6c3a7b"},
|
||||
{file = "webauthn-1.11.0-py3-none-any.whl", hash = "sha256:9c8a81f7e310aee022a038ae2c76711bcf0a94521a471225e05c36871f83eeda"},
|
||||
{file = "webauthn-1.11.0.tar.gz", hash = "sha256:1e808de1e3625a4b361e249e1bbb254d2a3a5c6b206e6f7260c4febe51f45276"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
asn1crypto = ">=1.4.0"
|
||||
cbor2 = ">=5.4.2.post1"
|
||||
cryptography = ">=41.0.1"
|
||||
cbor2 = ">=5.4.6"
|
||||
cryptography = ">=41.0.4"
|
||||
pydantic = ">=1.10.11"
|
||||
pyOpenSSL = ">=23.2.0"
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"targetLocales": [
|
||||
"en",
|
||||
"pseudo-LOCALE",
|
||||
"fr_FR",
|
||||
"fr",
|
||||
"tr",
|
||||
"es",
|
||||
"pl",
|
||||
|
|
16
web/package-lock.json
generated
16
web/package-lock.json
generated
|
@ -32,11 +32,11 @@
|
|||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.32.2",
|
||||
"core-js": "^3.33.0",
|
||||
"country-flag-icons": "^1.5.7",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lit": "^2.8.0",
|
||||
"mermaid": "^10.4.0",
|
||||
"mermaid": "^10.5.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
"style-mod": "^4.1.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
|
@ -12095,9 +12095,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.32.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.32.2.tgz",
|
||||
"integrity": "sha512-pxXSw1mYZPDGvTQqEc5vgIb83jGQKFGYWY76z4a7weZXUolw3G+OvpZqSRcfYOoOVUQJYEPsWeQK8pKEnUtWxQ==",
|
||||
"version": "3.33.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.0.tgz",
|
||||
"integrity": "sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw==",
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
|
@ -17181,9 +17181,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "10.4.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.4.0.tgz",
|
||||
"integrity": "sha512-4QCQLp79lvz7UZxow5HUX7uWTPJOaQBVExduo91tliXC7v78i6kssZOPHxLL+Xs30KU72cpPn3g3imw/xm/gaw==",
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.5.0.tgz",
|
||||
"integrity": "sha512-9l0o1uUod78D3/FVYPGSsgV+Z0tSnzLBDiC9rVzvelPxuO80HbN1oDr9ofpPETQy9XpypPQa26fr09VzEPfvWA==",
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.1",
|
||||
"@types/d3-scale": "^4.0.3",
|
||||
|
|
|
@ -50,11 +50,11 @@
|
|||
"chartjs-adapter-moment": "^1.0.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"construct-style-sheets-polyfill": "^3.1.0",
|
||||
"core-js": "^3.32.2",
|
||||
"core-js": "^3.33.0",
|
||||
"country-flag-icons": "^1.5.7",
|
||||
"fuse.js": "^6.6.2",
|
||||
"lit": "^2.8.0",
|
||||
"mermaid": "^10.4.0",
|
||||
"mermaid": "^10.5.0",
|
||||
"rapidoc": "^9.3.4",
|
||||
"style-mod": "^4.1.0",
|
||||
"webcomponent-qr-code": "^1.2.0",
|
||||
|
|
|
@ -73,7 +73,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
|||
|
||||
row(item: FlowStageBinding): TemplateResult[] {
|
||||
return [
|
||||
html`${item.order}`,
|
||||
html`<pre>${item.order}</pre>`,
|
||||
html`${item.stageObj?.name}`,
|
||||
html`${item.stageObj?.verboseName}`,
|
||||
html` <ak-forms-modal>
|
||||
|
|
|
@ -145,7 +145,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
|||
|
||||
row(item: PolicyBinding): TemplateResult[] {
|
||||
return [
|
||||
html`${item.order}`,
|
||||
html`<pre>${item.order}</pre>`,
|
||||
html`${this.getPolicyUserGroupRow(item)}`,
|
||||
html` <ak-label color=${item.enabled ? PFColor.Green : PFColor.Orange}>
|
||||
${item.enabled ? msg("Yes") : msg("No")}
|
||||
|
|
|
@ -29,7 +29,7 @@ export class AKLocaleSensitiveDemoComponent extends LitElement {
|
|||
|
||||
export const InFrench = () =>
|
||||
html`<div style="background: #fff; padding: 4em">
|
||||
<ak-locale-context locale="fr_FR"
|
||||
<ak-locale-context locale="fr"
|
||||
><ak-locale-demo-component
|
||||
>Everything is not ok.</ak-locale-demo-component
|
||||
></ak-locale-context
|
||||
|
@ -39,12 +39,12 @@ export const InFrench = () =>
|
|||
export const SwitchingBackAndForth = () => {
|
||||
let lang = "en";
|
||||
window.setInterval(() => {
|
||||
lang = lang === "en" ? "fr_FR" : "en";
|
||||
lang = lang === "en" ? "fr" : "en";
|
||||
window.dispatchEvent(customEvent(EVENT_LOCALE_REQUEST, { locale: lang }));
|
||||
}, 1000);
|
||||
|
||||
return html`<div style="background: #fff; padding: 4em">
|
||||
<ak-locale-context locale="fr_FR">
|
||||
<ak-locale-context locale="fr">
|
||||
<ak-locale-sensitive-demo-component></ak-locale-sensitive-demo-component
|
||||
></ak-locale-context>
|
||||
</div>`;
|
||||
|
|
|
@ -24,9 +24,6 @@ export { enLocale };
|
|||
// language uses both "regional" and "script" suffixes. The regexes use the language and any region
|
||||
// or script.
|
||||
//
|
||||
// French is currently an oddity; the translator provided the France regional version explicitly,
|
||||
// and we fall back to that regardless of region. Sorry, Québécois.
|
||||
//
|
||||
// Chinese locales usually (but not always) use the script rather than region suffix. The default
|
||||
// (optional) fallback for Chinese (zh) is "Chinese (simplified)", which is why it has that odd
|
||||
// regex syntax at the end which means "match zh as long as it's not followed by a [:word:] token";
|
||||
|
@ -43,7 +40,7 @@ const LOCALE_TABLE: LocaleRow[] = [
|
|||
["en", /^en([_-]|$)/i, () => msg("English"), async () => await import("@goauthentik/locales/en")],
|
||||
["es", /^es([_-]|$)/i, () => msg("Spanish"), async () => await import("@goauthentik/locales/es")],
|
||||
["de", /^de([_-]|$)/i, () => msg("German"), async () => await import("@goauthentik/locales/de")],
|
||||
["fr_FR", /^fr([_-]|$)/i, () => msg("French"), async () => await import("@goauthentik/locales/fr_FR")],
|
||||
["fr", /^fr([_-]|$)/i, () => msg("French"), async () => await import("@goauthentik/locales/fr")],
|
||||
["pl", /^pl([_-]|$)/i, () => msg("Polish"), async () => await import("@goauthentik/locales/pl")],
|
||||
["tr", /^tr([_-]|$)/i, () => msg("Turkish"), async () => await import("@goauthentik/locales/tr")],
|
||||
["zh-Hant", /^zh[_-](HK|Hant)/i, () => msg("Chinese (traditional)"), async () => await import("@goauthentik/locales/zh-Hant")],
|
||||
|
|
|
@ -14,7 +14,7 @@ export const targetLocales = [
|
|||
`de`,
|
||||
`en`,
|
||||
`es`,
|
||||
`fr_FR`,
|
||||
`fr`,
|
||||
`pl`,
|
||||
`pseudo-LOCALE`,
|
||||
`tr`,
|
||||
|
@ -31,7 +31,7 @@ export const allLocales = [
|
|||
`en`,
|
||||
`en`,
|
||||
`es`,
|
||||
`fr_FR`,
|
||||
`fr`,
|
||||
`pl`,
|
||||
`pseudo-LOCALE`,
|
||||
`tr`,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" ?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<?xml version="1.0"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
|
||||
<file target-language="zh-Hans" source-language="en" original="lit-localize-inputs" datatype="plaintext">
|
||||
<body>
|
||||
<trans-unit id="s4caed5b7a7e5d89b">
|
||||
|
@ -613,9 +613,9 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="saa0e2675da69651b">
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
<source>The URL "<x id="0" equiv-text="${this.url}"/>" was not found.</source>
|
||||
<target>未找到 URL "
|
||||
<x id="0" equiv-text="${this.url}"/>"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s58cd9c2fe836d9c6">
|
||||
|
@ -1067,8 +1067,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa8384c9c26731f83">
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
<source>To allow any redirect URI, set this value to ".*". Be aware of the possible security implications this can have.</source>
|
||||
<target>要允许任何重定向 URI,请将此值设置为 ".*"。请注意这可能带来的安全影响。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s55787f4dfcdce52b">
|
||||
|
@ -1814,8 +1814,8 @@
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa90b7809586c35ce">
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
<source>Either input a full URL, a relative path, or use 'fa://fa-test' to use the Font Awesome icon "fa-test".</source>
|
||||
<target>输入完整 URL、相对路径,或者使用 'fa://fa-test' 来使用 Font Awesome 图标 "fa-test"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s0410779cb47de312">
|
||||
|
@ -3238,8 +3238,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s76768bebabb7d543">
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
<source>Field which contains members of a group. Note that if using the "memberUid" field, the value is assumed to contain a relative distinguished name. e.g. 'memberUid=some-user' instead of 'memberUid=cn=some-user,ou=groups,...'</source>
|
||||
<target>包含组成员的字段。请注意,如果使用 "memberUid" 字段,则假定该值包含相对可分辨名称。例如,'memberUid=some-user' 而不是 'memberUid=cn=some-user,ou=groups,...'</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s026555347e589f0e">
|
||||
|
@ -4031,8 +4031,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s7b1fba26d245cb1c">
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
<source>When using an external logging solution for archiving, this can be set to "minutes=5".</source>
|
||||
<target>使用外部日志记录解决方案进行存档时,可以将其设置为 "minutes=5"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s44536d20bb5c8257">
|
||||
|
@ -4041,8 +4041,8 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s3bb51cabb02b997e">
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
<source>Format: "weeks=3;days=2;hours=3,seconds=2".</source>
|
||||
<target>格式:"weeks=3;days=2;hours=3,seconds=2"。</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="s04bfd02201db5ab8">
|
||||
|
@ -4238,10 +4238,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sa95a538bfbb86111">
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<source>Are you sure you want to update <x id="0" equiv-text="${this.objectLabel}"/> "<x id="1" equiv-text="${this.obj?.name}"/>"?</source>
|
||||
<target>您确定要更新
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
<x id="0" equiv-text="${this.objectLabel}"/>"
|
||||
<x id="1" equiv-text="${this.obj?.name}"/>" 吗?</target>
|
||||
|
||||
</trans-unit>
|
||||
<trans-unit id="sc92d7cfb6ee1fec6">
|
||||
|
@ -5342,7 +5342,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="sdf1d8edef27236f0">
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<source>A "roaming" authenticator, like a YubiKey</source>
|
||||
<target>像 YubiKey 这样的“漫游”身份验证器</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -5677,10 +5677,10 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s2d5f69929bb7221d">
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<source><x id="0" equiv-text="${prompt.name}"/> ("<x id="1" equiv-text="${prompt.fieldKey}"/>", of type <x id="2" equiv-text="${prompt.type}"/>)</source>
|
||||
<target>
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="0" equiv-text="${prompt.name}"/>("
|
||||
<x id="1" equiv-text="${prompt.fieldKey}"/>",类型为
|
||||
<x id="2" equiv-text="${prompt.type}"/>)</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -5729,7 +5729,7 @@ doesn't pass when either or both of the selected options are equal or above the
|
|||
|
||||
</trans-unit>
|
||||
<trans-unit id="s1608b2f94fa0dbd4">
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<source>If set to a duration above 0, the user will have the option to choose to "stay signed in", which will extend their session by the time specified here.</source>
|
||||
<target>如果设置时长大于 0,用户可以选择“保持登录”选项,这将使用户的会话延长此处设置的时间。</target>
|
||||
|
||||
</trans-unit>
|
||||
|
@ -7821,4 +7821,4 @@ Bindings to groups/users are checked against the user of the event.</source>
|
|||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
</xliff>
|
||||
|
|
|
@ -24,6 +24,10 @@ This ingress handles authentication requests, and the sign-in flow.
|
|||
|
||||
Add these annotations to the ingress you want to protect
|
||||
|
||||
:::warning
|
||||
This configuration requires that you enable [`allow-snippet-annotations`](https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#allow-snippet-annotations), for example by setting `controller.allowSnippetAnnotations` to `true` in your helm values for the ingress-nginx installation.
|
||||
:::
|
||||
|
||||
```yaml
|
||||
metadata:
|
||||
annotations:
|
||||
|
|
Reference in a new issue