Compare commits

..

21 Commits

Author SHA1 Message Date
eaad564e23 release: 2022.1.5 2022-02-09 22:31:26 +01:00
511a94975b website/docs: add 2022.1.5 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:31:14 +01:00
015810a2fd internal: fix CSRF error caused by Host header
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:53 +01:00
e70e6b84c2 internal: trace headers and url for backend requests
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:50 +01:00
d0b9c9a26f internal: remove uvicorn server header
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:46 +01:00
3e403fa348 internal: improve error handling for internal reverse proxy
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:41 +01:00
48f4a971ef internal: don't attempt to lookup SNI Certificate if no SNI is sent
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:39 +01:00
6314be14ad core: allow formatting strings to be used for applications' launch URLs
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:29 +01:00
1a072c6c39 web/admin: fix mismatched icons in overview and lists
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:26 +01:00
ef2eed0bdf outposts: fix compare_ports to support both service and container ports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:20 +01:00
91227b1e96 outposts: fix service reconciler re-creating services
closes #2095

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:16 +01:00
67d68629da providers/proxy: fix Host/:Authority not being modified
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:08 +01:00
e875db8f66 stages/authenticator_validate: handle non-existent device_challenges
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:22:02 +01:00
055a76393d outposts: remove node_port on V1ServicePort checks to prevent service creation loops
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

#2095
2022-02-09 22:21:58 +01:00
0754821628 providers/proxy: improve error handling for invalid backend_override
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:55 +01:00
fca88d9896 sources/ldap: log entire exception
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:48 +01:00
dfe0404c51 sources/saml: fix server error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:24 +01:00
fa61696b46 sources/saml: fix incorrect ProtocolBinding being sent
closes #2213

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:15 +01:00
e5773738f4 outposts: fix channel not always having a logger attribute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:12 +01:00
cac8539d79 providers/proxy: fix nil error in claims
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-09 22:21:08 +01:00
cf600f6f26 build(deps): bump uvicorn from 0.17.1 to 0.17.3 (#2229)
Bumps [uvicorn](https://github.com/encode/uvicorn) from 0.17.1 to 0.17.3.
- [Release notes](https://github.com/encode/uvicorn/releases)
- [Changelog](https://github.com/encode/uvicorn/blob/master/CHANGELOG.md)
- [Commits](https://github.com/encode/uvicorn/compare/0.17.1...0.17.3)

---
updated-dependencies:
- dependency-name: uvicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-02-09 17:56:53 +01:00
30 changed files with 248 additions and 56 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 2022.1.4
current_version = 2022.1.5
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)

View File

@ -30,14 +30,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik:2022.1.4,
beryju/authentik:2022.1.5,
beryju/authentik:latest,
ghcr.io/goauthentik/server:2022.1.4,
ghcr.io/goauthentik/server:2022.1.5,
ghcr.io/goauthentik/server:latest
platforms: linux/amd64,linux/arm64
context: .
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2022.1.4', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2022.1.5', 'rc') }}
run: |
docker pull beryju/authentik:latest
docker tag beryju/authentik:latest beryju/authentik:stable
@ -78,14 +78,14 @@ jobs:
with:
push: ${{ github.event_name == 'release' }}
tags: |
beryju/authentik-${{ matrix.type }}:2022.1.4,
beryju/authentik-${{ matrix.type }}:2022.1.5,
beryju/authentik-${{ matrix.type }}:latest,
ghcr.io/goauthentik/${{ matrix.type }}:2022.1.4,
ghcr.io/goauthentik/${{ matrix.type }}:2022.1.5,
ghcr.io/goauthentik/${{ matrix.type }}:latest
file: ${{ matrix.type }}.Dockerfile
platforms: linux/amd64,linux/arm64
- name: Building Docker Image (stable)
if: ${{ github.event_name == 'release' && !contains('2022.1.4', 'rc') }}
if: ${{ github.event_name == 'release' && !contains('2022.1.5', 'rc') }}
run: |
docker pull beryju/authentik-${{ matrix.type }}:latest
docker tag beryju/authentik-${{ matrix.type }}:latest beryju/authentik-${{ matrix.type }}:stable
@ -170,7 +170,7 @@ jobs:
SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org
with:
version: authentik@2022.1.4
version: authentik@2022.1.5
environment: beryjuorg-prod
sourcemaps: './web/dist'
url_prefix: '~/static/dist'

View File

@ -12,7 +12,8 @@
"totp",
"webauthn",
"traefik",
"passwordless"
"passwordless",
"kubernetes"
],
"python.linting.pylintEnabled": true,
"todo-tree.tree.showCountsInTree": true,

View File

@ -2,7 +2,7 @@
from os import environ
from typing import Optional
__version__ = "2022.1.4"
__version__ = "2022.1.5"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -1,13 +1,16 @@
"""Application API Views"""
from typing import Optional
from django.core.cache import cache
from django.db.models import QuerySet
from django.http.response import HttpResponseBadRequest
from django.shortcuts import get_object_or_404
from django.utils.functional import SimpleLazyObject
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import OpenApiParameter, OpenApiResponse, extend_schema
from guardian.shortcuts import get_objects_for_user
from rest_framework.decorators import action
from rest_framework.fields import ReadOnlyField
from rest_framework.fields import ReadOnlyField, SerializerMethodField
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
from rest_framework.response import Response
@ -39,11 +42,22 @@ def user_app_cache_key(user_pk: str) -> str:
class ApplicationSerializer(ModelSerializer):
"""Application Serializer"""
launch_url = ReadOnlyField(source="get_launch_url")
launch_url = SerializerMethodField()
provider_obj = ProviderSerializer(source="get_provider", required=False)
meta_icon = ReadOnlyField(source="get_meta_icon")
def get_launch_url(self, app: Application) -> Optional[str]:
"""Allow formatting of launch URL"""
url = app.get_launch_url()
if not url:
return url
user = self.context["request"].user
if isinstance(user, SimpleLazyObject):
user._setup()
user = user._wrapped
return url % user.__dict__
class Meta:
model = Application

View File

@ -13,7 +13,9 @@ class TestApplicationsAPI(APITestCase):
def setUp(self) -> None:
self.user = create_test_admin_user()
self.allowed = Application.objects.create(name="allowed", slug="allowed")
self.allowed = Application.objects.create(
name="allowed", slug="allowed", meta_launch_url="https://goauthentik.io/%(username)s"
)
self.denied = Application.objects.create(name="denied", slug="denied")
PolicyBinding.objects.create(
target=self.denied,
@ -64,8 +66,8 @@ class TestApplicationsAPI(APITestCase):
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"launch_url": f"https://goauthentik.io/{self.user.username}",
"meta_launch_url": "https://goauthentik.io/%(username)s",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",
@ -100,8 +102,8 @@ class TestApplicationsAPI(APITestCase):
"slug": "allowed",
"provider": None,
"provider_obj": None,
"launch_url": None,
"meta_launch_url": "",
"launch_url": f"https://goauthentik.io/{self.user.username}",
"meta_launch_url": "https://goauthentik.io/%(username)s",
"meta_icon": None,
"meta_description": "",
"meta_publisher": "",

View File

@ -55,6 +55,10 @@ class OutpostConsumer(AuthJsonConsumer):
first_msg = False
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.logger = get_logger()
def connect(self):
super().connect()
uuid = self.scope["url_route"]["kwargs"]["pk"]
@ -65,7 +69,7 @@ class OutpostConsumer(AuthJsonConsumer):
)
if not outpost:
raise DenyConnection()
self.logger = get_logger().bind(outpost=outpost)
self.logger = self.logger.bind(outpost=outpost)
try:
self.accept()
except RuntimeError as exc:

View File

@ -2,6 +2,7 @@
from pathlib import Path
from kubernetes.client.models.v1_container_port import V1ContainerPort
from kubernetes.client.models.v1_service_port import V1ServicePort
from kubernetes.config.incluster_config import SERVICE_TOKEN_FILENAME
from authentik.outposts.controllers.k8s.triggers import NeedsRecreate
@ -16,10 +17,31 @@ def get_namespace() -> str:
return "default"
def compare_ports(current: list[V1ContainerPort], reference: list[V1ContainerPort]):
def compare_port(
current: V1ServicePort | V1ContainerPort, reference: V1ServicePort | V1ContainerPort
) -> bool:
"""Compare a single port"""
if current.name != reference.name:
return False
if current.protocol != reference.protocol:
return False
if isinstance(current, V1ServicePort) and isinstance(reference, V1ServicePort):
# We only care about the target port
if current.target_port != reference.target_port:
return False
if isinstance(current, V1ContainerPort) and isinstance(reference, V1ContainerPort):
# We only care about the target port
if current.container_port != reference.container_port:
return False
return True
def compare_ports(
current: list[V1ServicePort | V1ContainerPort], reference: list[V1ServicePort | V1ContainerPort]
):
"""Compare ports of a list"""
if len(current) != len(reference):
raise NeedsRecreate()
for port in reference:
if port not in current:
if not any(compare_port(port, current_port) for current_port in current):
raise NeedsRecreate()

View File

@ -15,6 +15,7 @@ from authentik.providers.saml.processors.request_parser import AuthNRequestParse
from authentik.sources.saml.exceptions import MismatchedRequestID
from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.processors.constants import (
SAML_BINDING_REDIRECT,
SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_UNSPECIFIED,
)
@ -98,6 +99,9 @@ class TestAuthNRequest(TestCase):
# First create an AuthNRequest
request_proc = RequestProcessor(self.source, http_request, "test_state")
auth_n = request_proc.get_auth_n()
self.assertEqual(auth_n.attrib["ProtocolBinding"], SAML_BINDING_REDIRECT)
request = request_proc.build_auth_n()
# Now we check the ID and signature
parsed_request = AuthNRequestParser(self.provider).parse(

View File

@ -3,6 +3,7 @@ from ldap3.core.exceptions import LDAPException
from structlog.stdlib import get_logger
from authentik.events.monitored_tasks import MonitoredTask, TaskResult, TaskResultStatus
from authentik.lib.utils.errors import exception_to_string
from authentik.lib.utils.reflection import class_to_path, path_to_class
from authentik.root.celery import CELERY_APP
from authentik.sources.ldap.models import LDAPSource
@ -52,5 +53,5 @@ def ldap_sync(self: MonitoredTask, source_pk: str, sync_class: str):
)
except LDAPException as exc:
# No explicit event is created here as .set_status with an error will do that
LOGGER.debug(exc)
LOGGER.warning(exception_to_string(exc))
self.set_status(TaskResult(TaskResultStatus.ERROR).with_error(exc))

View File

@ -18,6 +18,8 @@ from authentik.sources.saml.processors.constants import (
RSA_SHA256,
RSA_SHA384,
RSA_SHA512,
SAML_BINDING_POST,
SAML_BINDING_REDIRECT,
SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT,
SAML_NAME_ID_FORMAT_TRANSIENT,
@ -37,6 +39,15 @@ class SAMLBindingTypes(models.TextChoices):
POST = "POST", _("POST Binding")
POST_AUTO = "POST_AUTO", _("POST Binding with auto-confirmation")
@property
def uri(self) -> str:
"""Convert database field to URI"""
return {
SAMLBindingTypes.POST: SAML_BINDING_POST,
SAMLBindingTypes.POST_AUTO: SAML_BINDING_POST,
SAMLBindingTypes.REDIRECT: SAML_BINDING_REDIRECT,
}[self]
class SAMLNameIDPolicy(models.TextChoices):
"""SAML NameID Policies"""

View File

@ -10,7 +10,7 @@ from lxml.etree import Element # nosec
from authentik.providers.saml.utils import get_random_id
from authentik.providers.saml.utils.encoding import deflate_and_base64_encode
from authentik.providers.saml.utils.time import get_time_string
from authentik.sources.saml.models import SAMLSource
from authentik.sources.saml.models import SAMLBindingTypes, SAMLSource
from authentik.sources.saml.processors.constants import (
DIGEST_ALGORITHM_TRANSLATION_MAP,
NS_MAP,
@ -62,7 +62,7 @@ class RequestProcessor:
auth_n_request.attrib["Destination"] = self.source.sso_url
auth_n_request.attrib["ID"] = self.request_id
auth_n_request.attrib["IssueInstant"] = self.issue_instant
auth_n_request.attrib["ProtocolBinding"] = self.source.binding_type
auth_n_request.attrib["ProtocolBinding"] = SAMLBindingTypes(self.source.binding_type).uri
auth_n_request.attrib["Version"] = "2.0"
# Create issuer object
auth_n_request.append(self.get_issuer())

View File

@ -196,7 +196,10 @@ class AuthenticatorValidateStageView(ChallengeStageView):
return super().get(request, *args, **kwargs)
def get_challenge(self) -> AuthenticatorValidationChallenge:
challenges = self.request.session["device_challenges"]
challenges = self.request.session.get("device_challenges")
if not challenges:
LOGGER.debug("Authenticator Validation stage ran without challenges")
return self.executor.stage_invalid()
return AuthenticatorValidationChallenge(
data={
"type": ChallengeTypes.NATIVE.value,

View File

@ -17,7 +17,7 @@ services:
image: redis:alpine
restart: unless-stopped
server:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.4}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.5}
restart: unless-stopped
command: server
environment:
@ -38,7 +38,7 @@ services:
- "0.0.0.0:${AUTHENTIK_PORT_HTTP:-9000}:9000"
- "0.0.0.0:${AUTHENTIK_PORT_HTTPS:-9443}:9443"
worker:
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.4}
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2022.1.5}
restart: unless-stopped
command: worker
environment:

View File

@ -25,4 +25,4 @@ func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s", FullVersion())
}
const VERSION = "2022.1.4"
const VERSION = "2022.1.5"

View File

@ -10,7 +10,7 @@ type Claims struct {
Exp int `json:"exp"`
Email string `json:"email"`
Verified bool `json:"email_verified"`
Proxy ProxyClaims `json:"ak_proxy"`
Proxy *ProxyClaims `json:"ak_proxy"`
Name string `json:"name"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"groups"`

View File

@ -70,7 +70,7 @@ func TestForwardHandleNginx_Single_Claims(t *testing.T) {
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: ProxyClaims{
Proxy: &ProxyClaims{
UserAttributes: map[string]interface{}{
"username": "foo",
"password": "bar",

View File

@ -64,7 +64,7 @@ func TestForwardHandleTraefik_Single_Claims(t *testing.T) {
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: ProxyClaims{
Proxy: &ProxyClaims{
UserAttributes: map[string]interface{}{
"username": "foo",
"password": "bar",

View File

@ -74,18 +74,21 @@ func (a *Application) configureProxy() error {
func (a *Application) proxyModifyRequest(ou *url.URL) func(req *http.Request) {
return func(r *http.Request) {
claims, _ := a.getClaims(r)
if claims.Proxy.BackendOverride != "" {
r.URL.Scheme = ou.Scheme
r.URL.Host = ou.Host
r.Host = ou.Host
if claims != nil && claims.Proxy != nil && claims.Proxy.BackendOverride != "" {
u, err := url.Parse(claims.Proxy.BackendOverride)
if err != nil {
a.log.WithField("backend_override", claims.Proxy.BackendOverride).WithError(err).Warning("failed parse user backend override")
}
} else {
r.URL.Scheme = u.Scheme
r.URL.Host = u.Host
} else {
r.URL.Scheme = ou.Scheme
r.URL.Host = ou.Host
r.Host = u.Host
}
}
a.log.WithField("upstream_url", r.URL.String()).Trace("final upstream url")
}
}
func (a *Application) proxyModifyResponse(res *http.Response) error {

View File

@ -0,0 +1,81 @@
package application
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"goauthentik.io/internal/outpost/proxyv2/constants"
)
func TestProxy_ModifyRequest(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "backend:8012", req.URL.Host)
assert.Equal(t, "backend:8012", req.Host)
}
func TestProxy_ModifyRequest_Claims(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
rr := httptest.NewRecorder()
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
BackendOverride: "http://other-backend:8123",
},
}
err = a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "other-backend:8123", req.URL.Host)
assert.Equal(t, "other-backend:8123", req.Host)
}
func TestProxy_ModifyRequest_Claims_Invalid(t *testing.T) {
a := newTestApplication()
req, _ := http.NewRequest("GET", "http://frontend/foo", nil)
u, err := url.Parse("http://backend:8012")
if err != nil {
panic(err)
}
rr := httptest.NewRecorder()
s, _ := a.sessions.Get(req, constants.SeesionName)
s.Values[constants.SessionClaims] = Claims{
Sub: "foo",
Proxy: &ProxyClaims{
BackendOverride: ":qewr",
},
}
err = a.sessions.Save(req, rr, s)
if err != nil {
panic(err)
}
a.proxyModifyRequest(u)(req)
assert.Equal(t, "/foo", req.URL.Path)
assert.Equal(t, "backend:8012", req.URL.Host)
assert.Equal(t, "backend:8012", req.Host)
}

View File

@ -102,7 +102,11 @@ func (ps *ProxyServer) GetCertificate(serverName string) *tls.Certificate {
}
func (ps *ProxyServer) getCertificates(info *tls.ClientHelloInfo) (*tls.Certificate, error) {
appCert := ps.GetCertificate(info.ServerName)
sn := info.ServerName
if sn == "" {
return &ps.defaultCert, nil
}
appCert := ps.GetCertificate(sn)
if appCert == nil {
return &ps.defaultCert, nil
}

View File

@ -1,6 +1,7 @@
package web
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httputil"
@ -24,6 +25,7 @@ func (ws *WebServer) configureProxy() {
if req.TLS != nil {
req.Header.Set("X-Forwarded-Proto", "https")
}
ws.log.WithField("url", req.URL.String()).WithField("headers", req.Header).Trace("tracing request to backend")
}
rp := &httputil.ReverseProxy{Director: director}
rp.ErrorHandler = ws.proxyErrorHandler
@ -65,9 +67,20 @@ func (ws *WebServer) configureProxy() {
}
func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request, err error) {
ws.log.Warning(err.Error())
ws.log.WithError(err).Warning("failed to proxy to backend")
rw.WriteHeader(http.StatusBadGateway)
_, err = rw.Write([]byte("authentik starting..."))
em := fmt.Sprintf("failed to connect to authentik backend: %v", err)
if !ws.p.IsRunning() {
em = "authentik starting..."
}
// return json if the client asks for json
if req.Header.Get("Accept") == "application/json" {
eem, _ := json.Marshal(map[string]string{
"error": em,
})
em = string(eem)
}
_, err = rw.Write([]byte(em))
if err != nil {
ws.log.WithError(err).Warning("failed to write error message")
}
@ -75,5 +88,6 @@ func (ws *WebServer) proxyErrorHandler(rw http.ResponseWriter, req *http.Request
func (ws *WebServer) proxyModifyResponse(r *http.Response) error {
r.Header.Set("X-Powered-By", "authentik")
r.Header.Del("Server")
return nil
}

View File

@ -16,6 +16,9 @@ func (ws *WebServer) GetCertificate() func(ch *tls.ClientHelloInfo) (*tls.Certif
ws.log.WithError(err).Error("failed to generate default cert")
}
return func(ch *tls.ClientHelloInfo) (*tls.Certificate, error) {
if ch.ServerName == "" {
return &cert, nil
}
if ws.ProxyServer != nil {
appCert := ws.ProxyServer.GetCertificate(ch.ServerName)
if appCert != nil {

6
poetry.lock generated
View File

@ -1898,7 +1898,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "uvicorn"
version = "0.17.1"
version = "0.17.3"
description = "The lightning-fast ASGI server."
category = "main"
optional = false
@ -3406,8 +3406,8 @@ urllib3 = [
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
uvicorn = [
{file = "uvicorn-0.17.1-py3-none-any.whl", hash = "sha256:8b16d9ecb76500f7804184f182835fe8a2b54716d3b0b6bb2da0b2b192f62c73"},
{file = "uvicorn-0.17.1.tar.gz", hash = "sha256:dffbacb8cc25d924d68d231d2c478c4fe6727c36537d8de21e5de591b37afc41"},
{file = "uvicorn-0.17.3-py3-none-any.whl", hash = "sha256:3ab1bf48aa512692db93a91c514576a0739a9d3522827e1656a172ee87118fa5"},
{file = "uvicorn-0.17.3.tar.gz", hash = "sha256:3cebddac78c7dd6bfce2f8f838c2c9b0cfcf3dddf417315ba62378ebe7e8fae2"},
]
uvloop = [
{file = "uvloop-0.16.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6224f1401025b748ffecb7a6e2652b17768f30b1a6a3f7b44660e5b5b690b12d"},

View File

@ -92,7 +92,7 @@ addopts = "-p no:celery --junitxml=unittest.xml"
[tool.poetry]
name = "authentik"
version = "2022.1.4"
version = "2022.1.5"
description = ""
authors = ["Jens Langhammer <jens.langhammer@beryju.org>"]

View File

@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: authentik
version: 2022.1.4
version: 2022.1.5
description: Making authentication simple.
contact:
email: hello@beryju.org

View File

@ -3,7 +3,7 @@ export const SUCCESS_CLASS = "pf-m-success";
export const ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2022.1.4";
export const VERSION = "2022.1.5";
export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";";

View File

@ -110,7 +110,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="pf-icon pf-icon-server"
icon="pf-icon pf-icon-process-automation"
header=${t`Flows`}
headerLink="#/flow/flows"
>
@ -121,7 +121,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-zone"
header=${t`Outpost status`}
headerLink="#/outpost/outposts"
>
@ -132,7 +132,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-user"
header=${t`Users`}
headerLink="#/identity/users"
>
@ -143,7 +143,7 @@ export class AdminOverviewPage extends LitElement {
class="pf-l-grid__item pf-m-6-col pf-m-4-col-on-xl pf-m-2-col-on-2xl graph-container"
>
<ak-aggregate-card
icon="fa fa-sync-alt"
icon="pf-icon pf-icon-users"
header=${t`Groups`}
headerLink="#/identity/groups"
>

View File

@ -24,6 +24,9 @@ The following aspects can be configured:
- *Name*: This is the name shown for the application card
- *Launch URL*: The URL that is opened when a user clicks on the application. When left empty, authentik tries to guess it based on the provider
Starting with authentik 2022.2, you can use placeholders in the launch url to build them dynamically based on logged in user. For example, you can set the Launch URL to `https://goauthentik.io/%(username)s`, which will be replaced with the currently logged in user's username.
- *Icon (URL)*: Optionally configure an Icon for the application
- *Publisher*: Text shown below the application
- *Description*: Subtext shown on the application card below the publisher

View File

@ -102,6 +102,28 @@ This release mostly removes legacy fields and features that have been deprecated
- web/flows: fix width on flow container
- web/user: include locale code in locale selection
## Fixed in 2022.1.5
- build(deps): bump uvicorn from 0.17.1 to 0.17.3 (#2229)
- core: allow formatting strings to be used for applications' launch URLs
- internal: don't attempt to lookup SNI Certificate if no SNI is sent
- internal: fix CSRF error caused by Host header
- internal: improve error handling for internal reverse proxy
- internal: remove uvicorn server header
- internal: trace headers and url for backend requests
- outposts: fix channel not always having a logger attribute
- outposts: fix compare_ports to support both service and container ports
- outposts: fix service reconciler re-creating services
- outposts: remove node_port on V1ServicePort checks to prevent service creation loops
- providers/proxy: fix Host/:Authority not being modified
- providers/proxy: fix nil error in claims
- providers/proxy: improve error handling for invalid backend_override
- sources/ldap: log entire exception
- sources/saml: fix incorrect ProtocolBinding being sent
- sources/saml: fix server error
- stages/authenticator_validate: handle non-existent device_challenges
- web/admin: fix mismatched icons in overview and lists
## Upgrading
This release does not introduce any new requirements.