Compare commits

..

19 Commits

Author SHA1 Message Date
f33190caa5 release: 2021.12.1 2021-12-16 15:48:59 +01:00
741822424a Merge branch 'master' into version-2021.12 2021-12-16 15:48:53 +01:00
0ca6fbb224 website/docs: final 2021.12.1 release notes
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 15:48:47 +01:00
Sem
f72b652b24 website/integrations: Updated bookstack integration docs page (#1942)
In some cases one might need to define the full SAML property to enable proper group sync. (see: https://github.com/BookStackApp/BookStack/issues/3109 )
2021-12-16 14:23:00 +01:00
0a2c1eb419 web/elements: fix linting error
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:27:08 +01:00
eb9593a847 web/elements: close notification drawer when clearing all notifications
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:23:44 +01:00
7c71c52791 web/admin: add sidebar to applications
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:23:30 +01:00
59493c02c4 web/elements: pass full Markdown object to ak-markdown, get title from metadata
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:18:43 +01:00
83089b47d3 web/elements: add Markdown component to improve rendering
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:10:46 +01:00
103e723d8c web/elements: add support for sidebar on table page
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 12:10:28 +01:00
7d6e88061f outposts: check if hub from context is set and fallback
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 11:19:57 +01:00
f8aab40e3e internal: cleanup duplicate and redundant code, properly set sentry SDK scope settings
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 11:00:19 +01:00
5123bc1316 root: add sponsors to readme
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 10:30:13 +01:00
30e8408e85 web/admin: fix notification unread colours not matching on user and admin interface
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 10:17:17 +01:00
bb34474101 web/admin: fix stage related flows not being shown in a list
closes #1941

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-16 10:13:10 +01:00
a105760123 events: improve app lookup for event creation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-15 16:46:02 +01:00
f410a77010 lifecycle: add -Ofair to celery
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-15 16:44:09 +01:00
6ff8fdcc49 root: enable threading integration in sentry
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-15 15:49:08 +01:00
50ca3dc772 core: fix error when attempting to provider from cached application
closes #1940

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-15 15:11:13 +01:00
38 changed files with 259 additions and 152 deletions

View File

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

View File

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

View File

@ -38,3 +38,23 @@ See [Development Documentation](https://goauthentik.io/developer-docs/?utm_sourc
## Security ## Security
See [SECURITY.md](SECURITY.md) See [SECURITY.md](SECURITY.md)
## Sponsors
This project is proudly sponsored by:
<p>
<a href="https://www.digitalocean.com/?utm_medium=opensource&utm_source=goauthentik.io">
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
</a>
</p>
DigitalOcean provides development and testing resources for authentik.
<p>
<a href="https://www.netlify.com">
<img src="https://www.netlify.com/img/global/badges/netlify-color-accent.svg" alt="Deploys by Netlify" />
</a>
</p>
Netlify hosts the [goauthentik.io](goauthentik.io) site.

View File

@ -1,3 +1,3 @@
"""authentik""" """authentik"""
__version__ = "2021.12.1-rc5" __version__ = "2021.12.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH" ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"

View File

@ -278,7 +278,13 @@ class Application(PolicyBindingModel):
"""Get casted provider instance""" """Get casted provider instance"""
if not self.provider: if not self.provider:
return None return None
return Provider.objects.get_subclass(pk=self.provider.pk) # if the Application class has been cache, self.provider is set
# but doing a direct query lookup will fail.
# In that case, just return None
try:
return Provider.objects.get_subclass(pk=self.provider.pk)
except Provider.DoesNotExist:
return None
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -2,7 +2,7 @@
import time import time
from collections import Counter from collections import Counter
from datetime import timedelta from datetime import timedelta
from inspect import getmodule, stack from inspect import currentframe
from smtplib import SMTPException from smtplib import SMTPException
from typing import TYPE_CHECKING, Optional, Type, Union from typing import TYPE_CHECKING, Optional, Type, Union
from uuid import uuid4 from uuid import uuid4
@ -192,14 +192,15 @@ class Event(ExpiringModel):
def new( def new(
action: Union[str, EventAction], action: Union[str, EventAction],
app: Optional[str] = None, app: Optional[str] = None,
_inspect_offset: int = 1,
**kwargs, **kwargs,
) -> "Event": ) -> "Event":
"""Create new Event instance from arguments. Instance is NOT saved.""" """Create new Event instance from arguments. Instance is NOT saved."""
if not isinstance(action, EventAction): if not isinstance(action, EventAction):
action = EventAction.CUSTOM_PREFIX + action action = EventAction.CUSTOM_PREFIX + action
if not app: if not app:
app = getmodule(stack()[_inspect_offset][0]).__name__ current = currentframe()
parent = current.f_back
app = parent.f_globals["__name__"]
cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs)) cleaned_kwargs = cleanse_dict(sanitize_dict(kwargs))
event = Event(action=action, app=app, context=cleaned_kwargs) event = Event(action=action, app=app, context=cleaned_kwargs)
return event return event

View File

@ -28,6 +28,7 @@ from sentry_sdk.integrations.boto3 import Boto3Integration
from sentry_sdk.integrations.celery import CeleryIntegration from sentry_sdk.integrations.celery import CeleryIntegration
from sentry_sdk.integrations.django import DjangoIntegration from sentry_sdk.integrations.django import DjangoIntegration
from sentry_sdk.integrations.redis import RedisIntegration from sentry_sdk.integrations.redis import RedisIntegration
from sentry_sdk.integrations.threading import ThreadingIntegration
from authentik import ENV_GIT_HASH_KEY, __version__ from authentik import ENV_GIT_HASH_KEY, __version__
from authentik.core.middleware import structlog_add_request_id from authentik.core.middleware import structlog_add_request_id
@ -424,6 +425,7 @@ if _ERROR_REPORTING:
CeleryIntegration(), CeleryIntegration(),
RedisIntegration(), RedisIntegration(),
Boto3Integration(), Boto3Integration(),
ThreadingIntegration(propagate_hub=True),
], ],
before_send=before_send, before_send=before_send,
release=f"authentik@{__version__}", release=f"authentik@{__version__}",

View File

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

View File

@ -17,4 +17,4 @@ func OutpostUserAgent() string {
return fmt.Sprintf("authentik-outpost@%s (build=%s)", VERSION, BUILD()) return fmt.Sprintf("authentik-outpost@%s (build=%s)", VERSION, BUILD())
} }
const VERSION = "2021.12.1-rc5" const VERSION = "2021.12.1"

View File

@ -60,6 +60,7 @@ type FlowExecutor struct {
func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Configuration, logFields log.Fields) *FlowExecutor { func NewFlowExecutor(ctx context.Context, flowSlug string, refConfig *api.Configuration, logFields log.Fields) *FlowExecutor {
rsp := sentry.StartSpan(ctx, "authentik.outposts.flow_executor") rsp := sentry.StartSpan(ctx, "authentik.outposts.flow_executor")
rsp.Description = flowSlug
l := log.WithField("flow", flowSlug).WithFields(logFields) l := log.WithField("flow", flowSlug).WithFields(logFields)
jar, err := cookiejar.New(nil) jar, err := cookiejar.New(nil)
@ -153,8 +154,8 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
} }
ch := challenge.GetActualInstance().(ChallengeInt) ch := challenge.GetActualInstance().(ChallengeInt)
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got challenge") fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got challenge")
gcsp.SetTag("ak_challenge", string(ch.GetType())) gcsp.SetTag("authentik.flow.challenge", string(ch.GetType()))
gcsp.SetTag("ak_component", ch.GetComponent()) gcsp.SetTag("authentik.flow.component", ch.GetComponent())
gcsp.Finish() gcsp.Finish()
FlowTimingGet.With(prometheus.Labels{ FlowTimingGet.With(prometheus.Labels{
"stage": ch.GetComponent(), "stage": ch.GetComponent(),
@ -202,8 +203,8 @@ func (fe *FlowExecutor) solveFlowChallenge(depth int) (bool, error) {
response, _, err := responseReq.Execute() response, _, err := responseReq.Execute()
ch = response.GetActualInstance().(ChallengeInt) ch = response.GetActualInstance().(ChallengeInt)
fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response") fe.log.WithField("component", ch.GetComponent()).WithField("type", ch.GetType()).Debug("Got response")
scsp.SetTag("ak_challenge", string(ch.GetType())) scsp.SetTag("authentik.flow.challenge", string(ch.GetType()))
scsp.SetTag("ak_component", ch.GetComponent()) scsp.SetTag("authentik.flow.component", ch.GetComponent())
scsp.Finish() scsp.Finish()
switch ch.GetComponent() { switch ch.GetComponent() {

View File

@ -23,9 +23,18 @@ type Request struct {
func NewRequest(bindDN string, bindPW string, conn net.Conn) (*Request, *sentry.Span) { func NewRequest(bindDN string, bindPW string, conn net.Conn) (*Request, *sentry.Span) {
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind", span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.bind",
sentry.TransactionName("authentik.providers.ldap.bind")) sentry.TransactionName("authentik.providers.ldap.bind"))
span.Description = bindDN
rid := uuid.New().String() rid := uuid.New().String()
span.SetTag("request_uid", rid) span.SetTag("request_uid", rid)
span.SetTag("user.username", bindDN) hub := sentry.GetHubFromContext(span.Context())
if hub == nil {
hub = sentry.CurrentHub()
}
hub.Scope().SetUser(sentry.User{
Username: bindDN,
ID: bindDN,
IPAddress: utils.GetIP(conn.RemoteAddr()),
})
bindDN = strings.ToLower(bindDN) bindDN = strings.ToLower(bindDN)
return &Request{ return &Request{

View File

@ -2,6 +2,7 @@ package search
import ( import (
"context" "context"
"fmt"
"net" "net"
"strings" "strings"
@ -27,10 +28,19 @@ func NewRequest(bindDN string, searchReq ldap.SearchRequest, conn net.Conn) (*Re
bindDN = strings.ToLower(bindDN) bindDN = strings.ToLower(bindDN)
searchReq.BaseDN = strings.ToLower(searchReq.BaseDN) searchReq.BaseDN = strings.ToLower(searchReq.BaseDN)
span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search")) span := sentry.StartSpan(context.TODO(), "authentik.providers.ldap.search", sentry.TransactionName("authentik.providers.ldap.search"))
span.Description = fmt.Sprintf("%s (%s)", searchReq.BaseDN, ldap.ScopeMap[searchReq.Scope])
span.SetTag("request_uid", rid) span.SetTag("request_uid", rid)
span.SetTag("user.username", bindDN) hub := sentry.GetHubFromContext(span.Context())
span.SetTag("ak_filter", searchReq.Filter) if hub == nil {
span.SetTag("ak_base_dn", searchReq.BaseDN) hub = sentry.CurrentHub()
}
hub.Scope().SetUser(sentry.User{
Username: bindDN,
ID: bindDN,
IPAddress: utils.GetIP(conn.RemoteAddr()),
})
span.SetTag("ldap_filter", searchReq.Filter)
span.SetTag("ldap_base_dn", searchReq.BaseDN)
return &Request{ return &Request{
SearchRequest: searchReq, SearchRequest: searchReq,
BindDN: bindDN, BindDN: bindDN,

View File

@ -12,6 +12,8 @@ import (
"time" "time"
"github.com/coreos/go-oidc" "github.com/coreos/go-oidc"
"github.com/getsentry/sentry-go"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gorilla/sessions" "github.com/gorilla/sessions"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -109,6 +111,15 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore
user := "" user := ""
if c != nil { if c != nil {
user = c.PreferredUsername user = c.PreferredUsername
hub := sentry.GetHubFromContext(r.Context())
if hub == nil {
hub = sentry.CurrentHub()
}
hub.Scope().SetUser(sentry.User{
Username: user,
ID: c.Sub,
IPAddress: r.RemoteAddr,
})
} }
before := time.Now() before := time.Now()
inner.ServeHTTP(rw, r) inner.ServeHTTP(rw, r)
@ -124,6 +135,7 @@ func NewApplication(p api.ProxyOutpostConfig, c *http.Client, cs *ak.CryptoStore
}).Observe(float64(after)) }).Observe(float64(after))
}) })
}) })
mux.Use(sentryhttp.New(sentryhttp.Options{}).Handle)
// Support /start and /sign_in for backwards compatibility // Support /start and /sign_in for backwards compatibility
mux.HandleFunc("/akprox/start", a.handleRedirect) mux.HandleFunc("/akprox/start", a.handleRedirect)

View File

@ -10,6 +10,7 @@ import (
"sync" "sync"
"time" "time"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pires/go-proxyproto" "github.com/pires/go-proxyproto"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -52,6 +53,7 @@ func NewProxyServer(ac *ak.APIController, portOffset int) *ProxyServer {
globalMux := rootMux.NewRoute().Subrouter() globalMux := rootMux.NewRoute().Subrouter()
globalMux.Use(web.NewLoggingHandler(l.WithField("logger", "authentik.outpost.proxyv2.http"), nil)) globalMux.Use(web.NewLoggingHandler(l.WithField("logger", "authentik.outpost.proxyv2.http"), nil))
globalMux.Use(sentryhttp.New(sentryhttp.Options{}).Handle)
s := &ProxyServer{ s := &ProxyServer{
Listen: "0.0.0.0:%d", Listen: "0.0.0.0:%d",
PortOffset: portOffset, PortOffset: portOffset,

View File

@ -99,8 +99,8 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
h.handler.ServeHTTP(responseLogger, req) h.handler.ServeHTTP(responseLogger, req)
duration := float64(time.Since(t)) / float64(time.Millisecond) duration := float64(time.Since(t)) / float64(time.Millisecond)
h.afterHandler(h.logger.WithFields(log.Fields{ h.afterHandler(h.logger.WithFields(log.Fields{
"host": req.RemoteAddr, "remote": req.RemoteAddr,
"vhost": GetHost(req), "host": GetHost(req),
"request_protocol": req.Proto, "request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration), "runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method, "method": req.Method,

View File

@ -1,29 +0,0 @@
package web
import (
"net/http"
"time"
"github.com/getsentry/sentry-go"
log "github.com/sirupsen/logrus"
"goauthentik.io/internal/utils/web"
)
func loggingMiddleware(l *log.Entry) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
span := sentry.StartSpan(r.Context(), "authentik.go.request")
before := time.Now()
// Call the next handler, which can be another middleware in the chain, or the final handler.
next.ServeHTTP(w, r)
after := time.Now()
l.WithFields(log.Fields{
"remote": r.RemoteAddr,
"method": r.Method,
"took": after.Sub(before),
"host": web.GetHost(r),
}).Info(r.RequestURI)
span.Finish()
})
}
}

View File

@ -1,44 +0,0 @@
package web
import (
"encoding/json"
"net/http"
sentryhttp "github.com/getsentry/sentry-go/http"
log "github.com/sirupsen/logrus"
)
func recoveryMiddleware() func(next http.Handler) http.Handler {
sentryHandler := sentryhttp.New(sentryhttp.Options{})
l := log.WithField("logger", "authentik.router.sentry")
return func(next http.Handler) http.Handler {
sentryHandler.Handle(next)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
next.ServeHTTP(w, r)
defer func() {
re := recover()
if re == nil {
return
}
err := re.(error)
if err != nil {
l.WithError(err).Warning("global panic handler")
jsonBody, _ := json.Marshal(struct {
Successful bool
Error string
}{
Successful: false,
Error: err.Error(),
})
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusInternalServerError)
_, err := w.Write(jsonBody)
if err != nil {
l.WithError(err).Warning("Failed to write sentry error body")
}
}
}()
})
}
}

View File

@ -6,6 +6,7 @@ import (
"net" "net"
"net/http" "net/http"
sentryhttp "github.com/getsentry/sentry-go/http"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/pires/go-proxyproto" "github.com/pires/go-proxyproto"
@ -13,6 +14,7 @@ import (
"goauthentik.io/internal/config" "goauthentik.io/internal/config"
"goauthentik.io/internal/gounicorn" "goauthentik.io/internal/gounicorn"
"goauthentik.io/internal/outpost/proxyv2" "goauthentik.io/internal/outpost/proxyv2"
"goauthentik.io/internal/utils/web"
) )
type WebServer struct { type WebServer struct {
@ -34,13 +36,11 @@ type WebServer struct {
func NewWebServer(g *gounicorn.GoUnicorn) *WebServer { func NewWebServer(g *gounicorn.GoUnicorn) *WebServer {
l := log.WithField("logger", "authentik.router") l := log.WithField("logger", "authentik.router")
mainHandler := mux.NewRouter() mainHandler := mux.NewRouter()
if config.G.ErrorReporting.Enabled { mainHandler.Use(sentryhttp.New(sentryhttp.Options{}).Handle)
mainHandler.Use(recoveryMiddleware())
}
mainHandler.Use(handlers.ProxyHeaders) mainHandler.Use(handlers.ProxyHeaders)
mainHandler.Use(handlers.CompressHandler) mainHandler.Use(handlers.CompressHandler)
logginRouter := mainHandler.NewRoute().Subrouter() logginRouter := mainHandler.NewRoute().Subrouter()
logginRouter.Use(loggingMiddleware(l)) logginRouter.Use(web.NewLoggingHandler(l, nil))
ws := &WebServer{ ws := &WebServer{
LegacyProxy: true, LegacyProxy: true,
@ -72,7 +72,7 @@ func (ws *WebServer) Shutdown() {
func (ws *WebServer) listenPlain() { func (ws *WebServer) listenPlain() {
ln, err := net.Listen("tcp", config.G.Web.Listen) ln, err := net.Listen("tcp", config.G.Web.Listen)
if err != nil { if err != nil {
ws.log.WithError(err).Fatalf("failed to listen") ws.log.WithError(err).Fatal("failed to listen")
} }
ws.log.WithField("listen", config.G.Web.Listen).Info("Listening") ws.log.WithField("listen", config.G.Web.Listen).Info("Listening")
@ -83,7 +83,7 @@ func (ws *WebServer) listenPlain() {
err = http.ListenAndServe(config.G.Web.Listen, ws.m) err = http.ListenAndServe(config.G.Web.Listen, ws.m)
if err != nil && !errors.Is(err, http.ErrServerClosed) { if err != nil && !errors.Is(err, http.ErrServerClosed) {
ws.log.Errorf("ERROR: http.Serve() - %s", err) ws.log.WithError(err).Error("failed to listen")
} }
} }
@ -100,14 +100,14 @@ func (ws *WebServer) serve(listener net.Listener) {
// We received an interrupt signal, shut down. // We received an interrupt signal, shut down.
if err := srv.Shutdown(context.Background()); err != nil { if err := srv.Shutdown(context.Background()); err != nil {
// Error from closing listeners, or context timeout: // Error from closing listeners, or context timeout:
ws.log.Printf("HTTP server Shutdown: %v", err) ws.log.WithError(err).Warning("HTTP server Shutdown")
} }
close(idleConnsClosed) close(idleConnsClosed)
}() }()
err := srv.Serve(listener) err := srv.Serve(listener)
if err != nil && !errors.Is(err, http.ErrServerClosed) { if err != nil && !errors.Is(err, http.ErrServerClosed) {
ws.log.Errorf("ERROR: http.Serve() - %s", err) ws.log.WithError(err).Error("ERROR: http.Serve()")
} }
<-idleConnsClosed <-idleConnsClosed
} }

View File

@ -67,7 +67,7 @@ if [[ "$1" == "server" ]]; then
/authentik-proxy /authentik-proxy
elif [[ "$1" == "worker" ]]; then elif [[ "$1" == "worker" ]]; then
echo "worker" > $MODE_FILE echo "worker" > $MODE_FILE
check_if_root "celery -A authentik.root.celery worker --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events" check_if_root "celery -A authentik.root.celery worker -Ofair --autoscale 3,1 -E -B -s /tmp/celerybeat-schedule -Q authentik,authentik_scheduled,authentik_events"
elif [[ "$1" == "flower" ]]; then elif [[ "$1" == "flower" ]]; then
echo "flower" > $MODE_FILE echo "flower" > $MODE_FILE
celery -A authentik.root.celery flower celery -A authentik.root.celery flower

View File

@ -1,7 +1,7 @@
openapi: 3.0.3 openapi: 3.0.3
info: info:
title: authentik title: authentik
version: 2021.12.1-rc5 version: 2021.12.1
description: Making authentication simple. description: Making authentication simple.
contact: contact:
email: hello@beryju.org 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 ERROR_CLASS = "pf-m-danger";
export const PROGRESS_CLASS = "pf-m-in-progress"; export const PROGRESS_CLASS = "pf-m-in-progress";
export const CURRENT_CLASS = "pf-m-current"; export const CURRENT_CLASS = "pf-m-current";
export const VERSION = "2021.12.1-rc5"; export const VERSION = "2021.12.1";
export const TITLE_DEFAULT = "authentik"; export const TITLE_DEFAULT = "authentik";
export const ROUTE_SEPARATOR = ";"; export const ROUTE_SEPARATOR = ";";

View File

@ -0,0 +1,32 @@
import { CSSResult, LitElement, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import { unsafeHTML } from "lit/directives/unsafe-html.js";
import AKGlobal from "../authentik.css";
import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFList from "@patternfly/patternfly/components/List/list.css";
export interface MarkdownDocument {
html: string;
metadata: { [key: string]: string };
filename: string;
}
@customElement("ak-markdown")
export class Markdown extends LitElement {
@property({ attribute: false })
md?: MarkdownDocument;
static get styles(): CSSResult[] {
return [PFList, PFContent, AKGlobal];
}
render(): TemplateResult {
if (!this.md) {
return html``;
}
const finalHTML = this.md?.html.replace("<ul>", "<ul class='pf-c-list'>");
return html`${this.md?.metadata.title ? html`<h2>${this.md.metadata.title}</h2>` : html``}
${unsafeHTML(finalHTML)}`;
}
}

View File

@ -81,7 +81,7 @@ export class PageHeader extends LitElement {
font-size: 24px; font-size: 24px;
} }
.notification-trigger.has-notifications { .notification-trigger.has-notifications {
color: #2b9af3; color: var(--pf-global--active-color--100);
} }
`, `,
]; ];

View File

@ -183,9 +183,15 @@ export class NotificationDrawer extends LitElement {
composed: true, composed: true,
}), }),
); );
this.dispatchEvent(
new CustomEvent(EVENT_NOTIFICATION_DRAWER_TOGGLE, {
bubbles: true,
composed: true,
}),
);
}); });
}} }}
class="pf-c-button pf-m-secondary pf-m-block" class="pf-c-button pf-m-primary pf-m-block"
type="button" type="button"
aria-label=${t`Clear all`} aria-label=${t`Clear all`}
> >

View File

@ -4,6 +4,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
import PFContent from "@patternfly/patternfly/components/Content/content.css"; import PFContent from "@patternfly/patternfly/components/Content/content.css";
import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css";
import PFSidebar from "@patternfly/patternfly/components/Sidebar/sidebar.css";
import "../../elements/PageHeader"; import "../../elements/PageHeader";
import { Table } from "./Table"; import { Table } from "./Table";
@ -14,7 +15,15 @@ export abstract class TablePage<T> extends Table<T> {
abstract pageIcon(): string; abstract pageIcon(): string;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat(PFPage, PFContent); return super.styles.concat(PFPage, PFContent, PFSidebar);
}
renderSidebarBefore(): TemplateResult {
return html``;
}
renderSidebarAfter(): TemplateResult {
return html``;
} }
render(): TemplateResult { render(): TemplateResult {
@ -25,7 +34,15 @@ export abstract class TablePage<T> extends Table<T> {
> >
</ak-page-header> </ak-page-header>
<section class="pf-c-page__main-section pf-m-no-padding-mobile"> <section class="pf-c-page__main-section pf-m-no-padding-mobile">
<div class="pf-c-card">${this.renderTable()}</div> <div class="pf-c-sidebar pf-m-gutter">
<div class="pf-c-sidebar__main">
${this.renderSidebarBefore()}
<div class="pf-c-sidebar__content">
<div class="pf-c-card">${this.renderTable()}</div>
</div>
${this.renderSidebarAfter()}
</div>
</div>
</section>`; </section>`;
} }
} }

2
web/src/global.d.ts vendored
View File

@ -1,7 +1,7 @@
declare module "*.css"; declare module "*.css";
declare module "*.md" { declare module "*.md" {
const html: string; const html: string;
const metadata: object; const metadata: { [key: string]: string };
const filename: string; const filename: string;
} }

View File

@ -122,6 +122,10 @@ msgstr "API Token (can be used to access the API programmatically)"
msgid "API request failed" msgid "API request failed"
msgstr "API request failed" msgstr "API request failed"
#: src/pages/applications/ApplicationListPage.ts
msgid "About applications"
msgstr "About applications"
#: src/pages/sources/oauth/OAuthSourceViewPage.ts #: src/pages/sources/oauth/OAuthSourceViewPage.ts
msgid "Access Key" msgid "Access Key"
msgstr "Access Key" msgstr "Access Key"

View File

@ -128,6 +128,10 @@ msgstr "Jeton d'API (peut être utilisé pour accéder à l'API via un programme
msgid "API request failed" msgid "API request failed"
msgstr "Requête d'API échouée" msgstr "Requête d'API échouée"
#: src/pages/applications/ApplicationListPage.ts
msgid "About applications"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceViewPage.ts #: src/pages/sources/oauth/OAuthSourceViewPage.ts
msgid "Access Key" msgid "Access Key"
msgstr "Clé d'accès" msgstr "Clé d'accès"

View File

@ -122,6 +122,10 @@ msgstr ""
msgid "API request failed" msgid "API request failed"
msgstr "" msgstr ""
#: src/pages/applications/ApplicationListPage.ts
msgid "About applications"
msgstr ""
#: src/pages/sources/oauth/OAuthSourceViewPage.ts #: src/pages/sources/oauth/OAuthSourceViewPage.ts
msgid "Access Key" msgid "Access Key"
msgstr "" msgstr ""

View File

@ -5,12 +5,15 @@ import { customElement, property } from "lit/decorators.js";
import { ifDefined } from "lit/directives/if-defined.js"; import { ifDefined } from "lit/directives/if-defined.js";
import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css"; import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css";
import PFCard from "@patternfly/patternfly/components/Card/card.css";
import { Application, CoreApi } from "@goauthentik/api"; import { Application, CoreApi } from "@goauthentik/api";
import MDApplication from "../../../../website/docs/core/applications.md";
import { AKResponse } from "../../api/Client"; import { AKResponse } from "../../api/Client";
import { DEFAULT_CONFIG } from "../../api/Config"; import { DEFAULT_CONFIG } from "../../api/Config";
import { uiConfig } from "../../common/config"; import { uiConfig } from "../../common/config";
import "../../elements/Markdown";
import "../../elements/buttons/SpinnerButton"; import "../../elements/buttons/SpinnerButton";
import "../../elements/forms/DeleteBulkForm"; import "../../elements/forms/DeleteBulkForm";
import "../../elements/forms/ModalForm"; import "../../elements/forms/ModalForm";
@ -52,6 +55,7 @@ export class ApplicationListPage extends TablePage<Application> {
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return super.styles.concat( return super.styles.concat(
PFAvatar, PFAvatar,
PFCard,
css` css`
tr td:first-child { tr td:first-child {
width: auto; width: auto;
@ -74,6 +78,17 @@ export class ApplicationListPage extends TablePage<Application> {
]; ];
} }
renderSidebarAfter(): TemplateResult {
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
<div class="pf-c-card">
<div class="pf-c-card__title">${t`About applications`}</div>
<div class="pf-c-card__body">
<ak-markdown .md=${MDApplication}></ak-markdown>
</div>
</div>
</div>`;
}
renderToolbarSelected(): TemplateResult { renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1; const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk return html`<ak-forms-delete-bulk

View File

@ -1,7 +1,6 @@
import { t } from "@lingui/macro"; import { t } from "@lingui/macro";
import { CSSResult, LitElement, TemplateResult, html } from "lit"; import { CSSResult, LitElement, TemplateResult, html } from "lit";
import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
import { customElement, property } from "lit/decorators.js"; import { customElement, property } from "lit/decorators.js";
import AKGlobal from "../../../authentik.css"; import AKGlobal from "../../../authentik.css";
@ -28,6 +27,8 @@ import { DEFAULT_CONFIG } from "../../../api/Config";
import { EVENT_REFRESH } from "../../../constants"; import { EVENT_REFRESH } from "../../../constants";
import "../../../elements/CodeMirror"; import "../../../elements/CodeMirror";
import { PFColor } from "../../../elements/Label"; import { PFColor } from "../../../elements/Label";
import "../../../elements/Markdown";
import { MarkdownDocument } from "../../../elements/Markdown";
import "../../../elements/Tabs"; import "../../../elements/Tabs";
import "../../../elements/buttons/ModalButton"; import "../../../elements/buttons/ModalButton";
import "../../../elements/buttons/SpinnerButton"; import "../../../elements/buttons/SpinnerButton";
@ -90,20 +91,19 @@ export class ProxyProviderViewPage extends LitElement {
}); });
} }
renderConfigTemplate(tmpl: string): TemplateResult { renderConfigTemplate(markdown: MarkdownDocument): MarkdownDocument {
// See website/docs/providers/proxy/forward_auth.mdx // See website/docs/providers/proxy/forward_auth.mdx
let final = "";
if (this.provider?.mode === ProxyMode.ForwardSingle) { if (this.provider?.mode === ProxyMode.ForwardSingle) {
final = tmpl markdown.html = markdown.html
.replaceAll("authentik.company", window.location.hostname) .replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company", window.location.hostname) .replaceAll("outpost.company", window.location.hostname)
.replaceAll("app.company", this.provider?.externalHost || ""); .replaceAll("app.company", this.provider?.externalHost || "");
} else if (this.provider?.mode == ProxyMode.ForwardDomain) { } else if (this.provider?.mode == ProxyMode.ForwardDomain) {
final = tmpl markdown.html = markdown.html
.replaceAll("authentik.company", window.location.hostname) .replaceAll("authentik.company", window.location.hostname)
.replaceAll("outpost.company", this.provider?.externalHost || ""); .replaceAll("outpost.company", this.provider?.externalHost || "");
} }
return html`${unsafeHTML(final)}`; return markdown;
} }
render(): TemplateResult { render(): TemplateResult {
@ -250,42 +250,54 @@ export class ProxyProviderViewPage extends LitElement {
data-tab-title="${t`Nginx (Ingress)`}" data-tab-title="${t`Nginx (Ingress)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDNginxIngress.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDNginxIngress)}
></ak-markdown>
</section> </section>
<section <section
slot="page-nginx-proxy-manager" slot="page-nginx-proxy-manager"
data-tab-title="${t`Nginx (Proxy Manager)`}" data-tab-title="${t`Nginx (Proxy Manager)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDNginxPM.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDNginxPM)}
></ak-markdown>
</section> </section>
<section <section
slot="page-nginx-standalone" slot="page-nginx-standalone"
data-tab-title="${t`Nginx (standalone)`}" data-tab-title="${t`Nginx (standalone)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDNginxStandalone.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDNginxStandalone)}
></ak-markdown>
</section> </section>
<section <section
slot="page-traefik-ingress" slot="page-traefik-ingress"
data-tab-title="${t`Traefik (Ingress)`}" data-tab-title="${t`Traefik (Ingress)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDTraefikIngres.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDTraefikIngres)}
></ak-markdown>
</section> </section>
<section <section
slot="page-traefik-compose" slot="page-traefik-compose"
data-tab-title="${t`Traefik (Compose)`}" data-tab-title="${t`Traefik (Compose)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDTraefikCompose.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDTraefikCompose)}
></ak-markdown>
</section> </section>
<section <section
slot="page-traefik-standalone" slot="page-traefik-standalone"
data-tab-title="${t`Traefik (Standalone)`}" data-tab-title="${t`Traefik (Standalone)`}"
class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile" class="pf-c-page__main-section pf-m-light pf-m-no-padding-mobile"
> >
${this.renderConfigTemplate(MDTraefikStandalone.html)} <ak-markdown
.md=${this.renderConfigTemplate(MDTraefikStandalone)}
></ak-markdown>
</section> </section>
</ak-tabs> </ak-tabs>
` `

View File

@ -107,11 +107,15 @@ export class StageListPage extends TablePage<Stage> {
<div>${item.name}</div> <div>${item.name}</div>
<small>${item.verboseName}</small> <small>${item.verboseName}</small>
</div>`, </div>`,
html`${item.flowSet?.map((flow) => { html`<ul class="pf-c-list">
return html`<a href="#/flow/flows/${flow.slug}"> ${item.flowSet?.map((flow) => {
<code>${flow.slug}</code> return html`<li>
</a>`; <a href="#/flow/flows/${flow.slug}">
})}`, <code>${flow.slug}</code>
</a>
</li>`;
})}
</ul>`,
html` <ak-forms-modal> html` <ak-forms-modal>
<span slot="submit"> ${t`Update`} </span> <span slot="submit"> ${t`Update`} </span>
<span slot="header"> ${t`Update ${item.verboseName}`} </span> <span slot="header"> ${t`Update ${item.verboseName}`} </span>

View File

@ -3,7 +3,7 @@ title: Applications
slug: /applications slug: /applications
--- ---
Applications in authentik are the counterpart of providers. They exist in a 1-to-1 relationship, each application needs a provider and every provider can be used with one application. Applications in authentik are the other half of providers. They exist in a 1-to-1 relationship, each application needs a provider and every provider can be used with one application.
Applications are used to configure and separate the authorization / access control and the appearance in the Library page. Applications are used to configure and separate the authorization / access control and the appearance in the Library page.
@ -15,23 +15,23 @@ By default, all users can access applications when no policies are bound.
When multiple policies/groups/users are attached, you can configure the *Policy engine mode* to either When multiple policies/groups/users are attached, you can configure the *Policy engine mode* to either
- Require users to pass all bindings/be member of all groups (ALL), or - Require users to pass all bindings/be member of all groups (ALL), or
- Require users to pass either binding/be member of either group (ANY) - Require users to pass either binding/be member of either group (ANY)
## Appearance ## Appearance
The following aspects can be configured: The following aspects can be configured:
- *Name*: This is the name shown for the application card - *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 - *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
- *Icon (URL)*: Optionally configure an Icon for the application - *Icon (URL)*: Optionally configure an Icon for the application
- *Publisher*: Text shown below the application - *Publisher*: Text shown below the application
- *Description*: Subtext shown on the application card below the publisher - *Description*: Subtext shown on the application card below the publisher
Applications are shown to users when Applications are shown to users when
- The user has access defined via policies (or the application has no policies bound) - The user has access defined via policies (or the application has no policies bound)
- A Valid Launch URL is configured/could be guessed, this consists of URLs starting with http:// and https:// - A Valid Launch URL is configured/could be guessed, this consists of URLs starting with http:// and https://
#### Hiding applications #### Hiding applications

View File

@ -14,7 +14,7 @@ This installation method is for test-setups and small-scale productive setups.
Download the latest `docker-compose.yml` from [here](https://goauthentik.io/docker-compose.yml). Place it in a directory of your choice. Download the latest `docker-compose.yml` from [here](https://goauthentik.io/docker-compose.yml). Place it in a directory of your choice.
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.12.1-rc5 >> .env` To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.12.1 >> .env`
If this is a fresh authentik install run the following commands to generate a password: If this is a fresh authentik install run the following commands to generate a password:

View File

@ -13,7 +13,7 @@ version: "3.5"
services: services:
authentik_proxy: authentik_proxy:
image: goauthentik.io/proxy:2021.12.1-rc5 image: goauthentik.io/proxy:2021.12.1
# Optionally specify which networks the container should be # Optionally specify which networks the container should be
# might be needed to reach the core authentik server # might be needed to reach the core authentik server
# networks: # networks:
@ -40,7 +40,7 @@ version: "3.5"
services: services:
authentik_ldap: authentik_ldap:
image: goauthentik.io/ldap:2021.12.1-rc5 image: goauthentik.io/ldap:2021.12.1
# Optionally specify which networks the container should be # Optionally specify which networks the container should be
# might be needed to reach the core authentik server # might be needed to reach the core authentik server
# networks: # networks:

View File

@ -14,7 +14,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
name: authentik-outpost-api name: authentik-outpost-api
stringData: stringData:
authentik_host: "__AUTHENTIK_URL__" authentik_host: "__AUTHENTIK_URL__"
@ -29,7 +29,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
name: authentik-outpost name: authentik-outpost
spec: spec:
ports: ports:
@ -54,7 +54,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
name: authentik-outpost name: authentik-outpost
spec: spec:
selector: selector:
@ -62,14 +62,14 @@ spec:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
template: template:
metadata: metadata:
labels: labels:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
spec: spec:
containers: containers:
- env: - env:
@ -88,7 +88,7 @@ spec:
secretKeyRef: secretKeyRef:
key: authentik_host_insecure key: authentik_host_insecure
name: authentik-outpost-api name: authentik-outpost-api
image: goauthentik.io/proxy:2021.12.1-rc5 image: goauthentik.io/proxy:2021.12.1
name: proxy name: proxy
ports: ports:
- containerPort: 9000 - containerPort: 9000
@ -110,7 +110,7 @@ metadata:
app.kubernetes.io/instance: __OUTPOST_NAME__ app.kubernetes.io/instance: __OUTPOST_NAME__
app.kubernetes.io/managed-by: goauthentik.io app.kubernetes.io/managed-by: goauthentik.io
app.kubernetes.io/name: authentik-proxy app.kubernetes.io/name: authentik-proxy
app.kubernetes.io/version: 2021.12.1-rc5 app.kubernetes.io/version: 2021.12.1
name: authentik-outpost name: authentik-outpost
spec: spec:
rules: rules:

View File

@ -151,6 +151,19 @@ This release does not have any headline features, and mostly fixes bugs.
- web/admin: update overview page - web/admin: update overview page
- web/flows: fix error when attempting to enroll new webauthn device - web/flows: fix error when attempting to enroll new webauthn device
## Fixed in 2021.12.1
- core: fix error when attempting to provider from cached application
- events: improve app lookup for event creation
- internal: cleanup duplicate and redundant code, properly set sentry SDK scope settings
- lifecycle: add -Ofair to celery
- web/admin: add sidebar to applications
- web/admin: fix notification unread colours not matching on user and admin interface
- web/admin: fix stage related flows not being shown in a list
- web/elements: add Markdown component to improve rendering
- web/elements: add support for sidebar on table page
- web/elements: close notification drawer when clearing all notifications
## Upgrading ## Upgrading
This release does not introduce any new requirements. This release does not introduce any new requirements.

View File

@ -101,3 +101,9 @@ BookStack will attempt to match the SAML user to an existing BookStack user base
:::note :::note
SAML Group Sync is supported by Bookstack. Review the BookStack documentation on the required Environment variables. https://www.bookstackapp.com/docs/admin/saml2-auth/ SAML Group Sync is supported by Bookstack. Review the BookStack documentation on the required Environment variables. https://www.bookstackapp.com/docs/admin/saml2-auth/
::: :::
:::note
In some cases you might need to define the full SAML property name.
i.e.: `SAML2_GROUP_ATTRIBUTE="http://schemas.xmlsoap.org/claims/Group"`
See https://github.com/BookStackApp/BookStack/issues/3109 for more details.
:::