Skip to content

Commit

Permalink
Merge branch 'main' into dev
Browse files Browse the repository at this point in the history
* main:
  website/docs: Add note about single group per role (#12169)
  website/docs: Fix documentation about attribute merging for indirect membership (#12168)
  root: support running authentik in subpath (#8675)
  docs: fix contribution link (#12189)
  core, web: update translations (#12190)
  core: Bump msgraph-sdk from 1.12.0 to 1.13.0 (#12191)
  core: Bump selenium from 4.26.1 to 4.27.0 (#12192)
  • Loading branch information
kensternberg-authentik committed Nov 26, 2024
2 parents 67b3274 + 1daa531 commit 20b66f8
Show file tree
Hide file tree
Showing 49 changed files with 259 additions and 115 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
4 changes: 2 additions & 2 deletions authentik/brands/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ class CurrentBrandSerializer(PassiveSerializer):

matched_domain = CharField(source="domain")
branding_title = CharField()
branding_logo = CharField()
branding_favicon = CharField()
branding_logo = CharField(source="branding_logo_url")
branding_favicon = CharField(source="branding_favicon_url")
ui_footer_links = ListField(
child=FooterLinkSerializer(),
read_only=True,
Expand Down
13 changes: 13 additions & 0 deletions authentik/brands/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from authentik.crypto.models import CertificateKeyPair
from authentik.flows.models import Flow
from authentik.lib.config import CONFIG
from authentik.lib.models import SerializerModel

LOGGER = get_logger()
Expand Down Expand Up @@ -71,6 +72,18 @@ class Brand(SerializerModel):
)
attributes = models.JSONField(default=dict, blank=True)

def branding_logo_url(self) -> str:
"""Get branding_logo with the correct prefix"""
if self.branding_logo.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_logo
return self.branding_logo

def branding_favicon_url(self) -> str:
"""Get branding_favicon with the correct prefix"""
if self.branding_favicon.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.branding_favicon
return self.branding_favicon

@property
def serializer(self) -> Serializer:
from authentik.brands.api import BrandSerializer
Expand Down
3 changes: 3 additions & 0 deletions authentik/core/templates/base/header_js.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
versionFamily: "{{ version_family }}",
versionSubdomain: "{{ version_subdomain }}",
build: "{{ build }}",
api: {
base: "{{ base_url }}",
},
};
window.addEventListener("DOMContentLoaded", function () {
{% for message in messages %}
Expand Down
4 changes: 2 additions & 2 deletions authentik/core/templates/base/skeleton.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon }}">
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/authentik.css' %}">
Expand Down
6 changes: 3 additions & 3 deletions authentik/core/templates/login/base_full.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% load i18n %}

{% block head_before %}
<link rel="prefetch" href="/static/dist/assets/images/flow_background.jpg" />
<link rel="prefetch" href="{% static 'dist/assets/images/flow_background.jpg' %}" />
<link rel="stylesheet" type="text/css" href="{% static 'dist/patternfly.min.css' %}">
<link rel="stylesheet" type="text/css" href="{% static 'dist/theme-dark.css' %}" media="(prefers-color-scheme: dark)">
{% include "base/header_js.html" %}
Expand All @@ -13,7 +13,7 @@
{% block head %}
<style>
:root {
--ak-flow-background: url("/static/dist/assets/images/flow_background.jpg");
--ak-flow-background: url("{% static 'dist/assets/images/flow_background.jpg' %}");
--pf-c-background-image--BackgroundImage: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage-2x: var(--ak-flow-background);
--pf-c-background-image--BackgroundImage--sm: var(--ak-flow-background);
Expand Down Expand Up @@ -50,7 +50,7 @@
<div class="ak-login-container">
<main class="pf-c-login__main">
<div class="pf-c-login__main-header pf-c-brand ak-brand">
<img src="{{ brand.branding_logo }}" alt="authentik Logo" />
<img src="{{ brand.branding_logo_url }}" alt="authentik Logo" />
</div>
<header class="pf-c-login__main-header">
<h1 class="pf-c-title pf-m-3xl">
Expand Down
2 changes: 2 additions & 0 deletions authentik/core/views/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from authentik.brands.api import CurrentBrandSerializer
from authentik.brands.models import Brand
from authentik.core.models import UserTypes
from authentik.lib.config import CONFIG
from authentik.policies.denied import AccessDeniedResponse


Expand Down Expand Up @@ -51,6 +52,7 @@ def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
kwargs["version_subdomain"] = f"version-{LOCAL_VERSION.major}-{LOCAL_VERSION.minor}"
kwargs["build"] = get_build_hash()
kwargs["url_kwargs"] = self.kwargs
kwargs["base_url"] = self.request.build_absolute_uri(CONFIG.get("web.path", "/"))
return super().get_context_data(**kwargs)


Expand Down
4 changes: 2 additions & 2 deletions authentik/enterprise/providers/rac/templates/if/rac.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<script src="{% versioned_script 'dist/enterprise/rac/index-%v.js' %}" type="module"></script>
<meta name="theme-color" content="#18191a" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
<link rel="icon" href="{{ tenant.branding_favicon }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon }}">
<link rel="icon" href="{{ tenant.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ tenant.branding_favicon_url }}">
{% include "base/header_js.html" %}
{% endblock %}

Expand Down
9 changes: 7 additions & 2 deletions authentik/flows/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from authentik.core.models import Token
from authentik.core.types import UserSettingSerializer
from authentik.flows.challenge import FlowLayout
from authentik.lib.config import CONFIG
from authentik.lib.models import InheritanceForeignKey, SerializerModel
from authentik.lib.utils.reflection import class_to_path
from authentik.policies.models import PolicyBindingModel
Expand Down Expand Up @@ -177,9 +178,13 @@ def background_url(self) -> str:
"""Get the URL to the background image. If the name is /static or starts with http
it is returned as-is"""
if not self.background:
return "/static/dist/assets/images/flow_background.jpg"
if self.background.name.startswith("http") or self.background.name.startswith("/static"):
return (
CONFIG.get("web.path", "/")[:-1] + "/static/dist/assets/images/flow_background.jpg"
)
if self.background.name.startswith("http"):
return self.background.name
if self.background.name.startswith("/static"):
return CONFIG.get("web.path", "/")[:-1] + self.background.name
return self.background.url

stages = models.ManyToManyField(Stage, through="FlowStageBinding", blank=True)
Expand Down
4 changes: 2 additions & 2 deletions authentik/flows/templates/if/flow-sfe.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>{% block title %}{% trans title|default:brand.branding_title %}{% endblock %}</title>
<link rel="icon" href="{{ brand.branding_favicon }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon }}">
<link rel="icon" href="{{ brand.branding_favicon_url }}">
<link rel="shortcut icon" href="{{ brand.branding_favicon_url }}">
{% block head_before %}
{% endblock %}
<link rel="stylesheet" type="text/css" href="{% static 'dist/sfe/bootstrap.min.css' %}">
Expand Down
1 change: 1 addition & 0 deletions authentik/lib/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ web:
# No default here as it's set dynamically
# workers: 2
threads: 4
path: /

worker:
concurrency: 2
Expand Down
3 changes: 2 additions & 1 deletion authentik/lib/sentry.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
from authentik.lib.utils.reflection import get_env

LOGGER = get_logger()
_root_path = CONFIG.get("web.path", "/")


class SentryIgnoredException(Exception):
Expand Down Expand Up @@ -90,7 +91,7 @@ def traces_sampler(sampling_context: dict) -> float:
path = sampling_context.get("asgi_scope", {}).get("path", "")
_type = sampling_context.get("asgi_scope", {}).get("type", "")
# Ignore all healthcheck routes
if path.startswith("/-/health") or path.startswith("/-/metrics"):
if path.startswith(f"{_root_path}-/health") or path.startswith(f"{_root_path}-/metrics"):
return 0
if _type == "websocket":
return 0
Expand Down
4 changes: 3 additions & 1 deletion authentik/root/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
# Custom user model
AUTH_USER_MODEL = "authentik_core.User"

CSRF_COOKIE_PATH = LANGUAGE_COOKIE_PATH = SESSION_COOKIE_PATH = CONFIG.get("web.path", "/")

CSRF_COOKIE_NAME = "authentik_csrf"
CSRF_HEADER_NAME = "HTTP_X_AUTHENTIK_CSRF"
LANGUAGE_COOKIE_NAME = "authentik_language"
Expand Down Expand Up @@ -427,7 +429,7 @@
# https://docs.djangoproject.com/en/2.1/howto/static-files/

STATICFILES_DIRS = [BASE_DIR / Path("web")]
STATIC_URL = "/static/"
STATIC_URL = CONFIG.get("web.path", "/") + "static/"

STORAGES = {
"staticfiles": {
Expand Down
9 changes: 6 additions & 3 deletions authentik/root/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from structlog.stdlib import get_logger

from authentik.core.views import error
from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_apps
from authentik.root.monitoring import LiveView, MetricsView, ReadyView

Expand All @@ -14,7 +15,7 @@
handler404 = error.NotFoundView.as_view()
handler500 = error.ServerErrorView.as_view()

urlpatterns = []
_urlpatterns = []

for _authentik_app in get_apps():
mountpoints = None
Expand All @@ -35,16 +36,18 @@
namespace=namespace,
),
)
urlpatterns.append(_path)
_urlpatterns.append(_path)
LOGGER.debug(
"Mounted URLs",
app_name=_authentik_app.name,
app_mountpoint=mountpoint,
namespace=namespace,
)

urlpatterns += [
_urlpatterns += [
path("-/metrics/", MetricsView.as_view(), name="metrics"),
path("-/health/live/", LiveView.as_view(), name="health-live"),
path("-/health/ready/", ReadyView.as_view(), name="health-ready"),
]

urlpatterns = [path(CONFIG.get("web.path", "/")[1:], include(_urlpatterns))]
14 changes: 12 additions & 2 deletions authentik/root/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

from importlib import import_module

from channels.routing import URLRouter
from django.urls import path
from structlog.stdlib import get_logger

from authentik.lib.config import CONFIG
from authentik.lib.utils.reflection import get_apps

LOGGER = get_logger()

websocket_urlpatterns = []
_websocket_urlpatterns = []
for _authentik_app in get_apps():
try:
api_urls = import_module(f"{_authentik_app.name}.urls")
Expand All @@ -17,8 +20,15 @@
if not hasattr(api_urls, "websocket_urlpatterns"):
continue
urls: list = api_urls.websocket_urlpatterns
websocket_urlpatterns.extend(urls)
_websocket_urlpatterns.extend(urls)
LOGGER.debug(
"Mounted Websocket URLs",
app_name=_authentik_app.name,
)

websocket_urlpatterns = [
path(
CONFIG.get("web.path", "/")[1:],
URLRouter(_websocket_urlpatterns),
),
]
2 changes: 1 addition & 1 deletion cmd/server/healthcheck.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func checkServer() int {
h := &http.Client{
Transport: web.NewUserAgentTransport("goauthentik.io/healthcheck", http.DefaultTransport),
}
url := fmt.Sprintf("http://%s/-/health/live/", config.Get().Listen.HTTP)
url := fmt.Sprintf("http://%s%s-/health/live/", config.Get().Listen.HTTP, config.Get().Web.Path)
res, err := h.Head(url)
if err != nil {
log.WithError(err).Warning("failed to send healthcheck request")
Expand Down
2 changes: 1 addition & 1 deletion cmd/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ var rootCmd = &cobra.Command{
ex := common.Init()
defer common.Defer()

u, err := url.Parse(fmt.Sprintf("http://%s", config.Get().Listen.HTTP))
u, err := url.Parse(fmt.Sprintf("http://%s%s", config.Get().Listen.HTTP, config.Get().Web.Path))
if err != nil {
panic(err)
}
Expand Down
5 changes: 5 additions & 0 deletions internal/config/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Config struct {
// Config for both core and outposts
Debug bool `yaml:"debug" env:"AUTHENTIK_DEBUG, overwrite"`
Listen ListenConfig `yaml:"listen" env:", prefix=AUTHENTIK_LISTEN__"`
Web WebConfig `yaml:"web" env:", prefix=AUTHENTIK_WEB__"`

// Outpost specific config
// These are only relevant for proxy/ldap outposts, and cannot be set via YAML
Expand Down Expand Up @@ -72,3 +73,7 @@ type OutpostConfig struct {
Discover bool `yaml:"discover" env:"DISCOVER, overwrite"`
DisableEmbeddedOutpost bool `yaml:"disable_embedded_outpost" env:"DISABLE_EMBEDDED_OUTPOST, overwrite"`
}

type WebConfig struct {
Path string `yaml:"path" env:"PATH, overwrite"`
}
17 changes: 11 additions & 6 deletions internal/outpost/ak/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ type APIController struct {
func NewAPIController(akURL url.URL, token string) *APIController {
rsp := sentry.StartSpan(context.Background(), "authentik.outposts.init")

config := api.NewConfiguration()
config.Host = akURL.Host
config.Scheme = akURL.Scheme
config.HTTPClient = &http.Client{
apiConfig := api.NewConfiguration()
apiConfig.Host = akURL.Host
apiConfig.Scheme = akURL.Scheme
apiConfig.HTTPClient = &http.Client{
Transport: web.NewUserAgentTransport(
constants.OutpostUserAgent(),
web.NewTracingTransport(
Expand All @@ -68,10 +68,15 @@ func NewAPIController(akURL url.URL, token string) *APIController {
),
),
}
config.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))
apiConfig.Servers = api.ServerConfigurations{
{
URL: fmt.Sprintf("%sapi/v3", akURL.Path),
},
}
apiConfig.AddDefaultHeader("Authorization", fmt.Sprintf("Bearer %s", token))

// create the API client, with the transport
apiClient := api.NewAPIClient(config)
apiClient := api.NewAPIClient(apiConfig)

log := log.WithField("logger", "authentik.outpost.ak-api-controller")

Expand Down
5 changes: 3 additions & 2 deletions internal/outpost/ak/api_ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
pathTemplate := "%s://%s/ws/outpost/%s/?%s"
pathTemplate := "%s://%s%sws/outpost/%s/?%s"
query := akURL.Query()
query.Set("instance_uuid", ac.instanceUUID.String())
scheme := strings.ReplaceAll(akURL.Scheme, "http", "ws")
Expand All @@ -37,7 +37,7 @@ func (ac *APIController) initWS(akURL url.URL, outpostUUID string) error {
},
}

ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, outpostUUID, akURL.Query().Encode()), header)
ws, _, err := dialer.Dial(fmt.Sprintf(pathTemplate, scheme, akURL.Host, akURL.Path, outpostUUID, akURL.Query().Encode()), header)
if err != nil {
ac.logger.WithError(err).Warning("failed to connect websocket")
return err
Expand Down Expand Up @@ -83,6 +83,7 @@ func (ac *APIController) reconnectWS() {
u := url.URL{
Host: ac.Client.GetConfig().Host,
Scheme: ac.Client.GetConfig().Scheme,
Path: strings.ReplaceAll(ac.Client.GetConfig().Servers[0].URL, "api/v3", ""),
}
attempt := 1
for {
Expand Down
2 changes: 1 addition & 1 deletion internal/web/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (ws *WebServer) runMetricsServer() {
).ServeHTTP(rw, r)

// Get upstream metrics
re, err := http.NewRequest("GET", fmt.Sprintf("%s/-/metrics/", ws.ul.String()), nil)
re, err := http.NewRequest("GET", fmt.Sprintf("%s%s-/metrics/", ws.upstreamURL.String(), config.Get().Web.Path), nil)
if err != nil {
l.WithError(err).Warning("failed to get upstream metrics")
return
Expand Down
Loading

0 comments on commit 20b66f8

Please sign in to comment.