Compare commits

...

11 Commits

29 changed files with 111 additions and 59 deletions

View File

@ -1,5 +1,5 @@
[bumpversion] [bumpversion]
current_version = 2021.2.4-stable current_version = 2021.2.5-stable
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

@ -18,11 +18,11 @@ jobs:
- name: Building Docker Image - name: Building Docker Image
run: docker build run: docker build
--no-cache --no-cache
-t beryju/authentik:2021.2.4-stable -t beryju/authentik:2021.2.5-stable
-t beryju/authentik:latest -t beryju/authentik:latest
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik:2021.2.4-stable run: docker push beryju/authentik:2021.2.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik:latest run: docker push beryju/authentik:latest
build-proxy: build-proxy:
@ -48,11 +48,11 @@ jobs:
cd outpost/ cd outpost/
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik-proxy:2021.2.4-stable \ -t beryju/authentik-proxy:2021.2.5-stable \
-t beryju/authentik-proxy:latest \ -t beryju/authentik-proxy:latest \
-f proxy.Dockerfile . -f proxy.Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-proxy:2021.2.4-stable run: docker push beryju/authentik-proxy:2021.2.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-proxy:latest run: docker push beryju/authentik-proxy:latest
build-static: build-static:
@ -69,11 +69,11 @@ jobs:
cd web/ cd web/
docker build \ docker build \
--no-cache \ --no-cache \
-t beryju/authentik-static:2021.2.4-stable \ -t beryju/authentik-static:2021.2.5-stable \
-t beryju/authentik-static:latest \ -t beryju/authentik-static:latest \
-f Dockerfile . -f Dockerfile .
- name: Push Docker Container to Registry (versioned) - name: Push Docker Container to Registry (versioned)
run: docker push beryju/authentik-static:2021.2.4-stable run: docker push beryju/authentik-static:2021.2.5-stable
- name: Push Docker Container to Registry (latest) - name: Push Docker Container to Registry (latest)
run: docker push beryju/authentik-static:latest run: docker push beryju/authentik-static:latest
test-release: test-release:
@ -107,5 +107,5 @@ jobs:
SENTRY_PROJECT: authentik SENTRY_PROJECT: authentik
SENTRY_URL: https://sentry.beryju.org SENTRY_URL: https://sentry.beryju.org
with: with:
tagName: 2021.2.4-stable tagName: 2021.2.5-stable
environment: beryjuorg-prod environment: beryjuorg-prod

View File

@ -1,2 +1,2 @@
"""authentik""" """authentik"""
__version__ = "2021.2.4-stable" __version__ = "2021.2.5-stable"

View File

@ -41,6 +41,9 @@
{% endfor %} {% endfor %}
</ul> </ul>
</ak-dropdown> </ak-dropdown>
<button role="ak-refresh" class="pf-c-button pf-m-primary">
{% trans 'Refresh' %}
</button>
</div> </div>
{% include 'partials/pagination.html' %} {% include 'partials/pagination.html' %}
</div> </div>

View File

@ -9,6 +9,7 @@ from django.http import HttpRequest, HttpResponse
SESSION_IMPERSONATE_USER = "authentik_impersonate_user" SESSION_IMPERSONATE_USER = "authentik_impersonate_user"
SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user" SESSION_IMPERSONATE_ORIGINAL_USER = "authentik_impersonate_original_user"
LOCAL = local() LOCAL = local()
RESPONSE_HEADER_ID = "X-authentik-id"
class ImpersonateMiddleware: class ImpersonateMiddleware:
@ -43,7 +44,7 @@ class RequestIDMiddleware:
setattr(request, "request_id", request_id) setattr(request, "request_id", request_id)
LOCAL.authentik = {"request_id": request_id} LOCAL.authentik = {"request_id": request_id}
response = self.get_response(request) response = self.get_response(request)
response["X-authentik-id"] = request.request_id response[RESPONSE_HEADER_ID] = request.request_id
del LOCAL.authentik["request_id"] del LOCAL.authentik["request_id"]
return response return response

View File

@ -2,6 +2,7 @@
from guardian.shortcuts import get_anonymous_user from guardian.shortcuts import get_anonymous_user
from structlog import get_logger from structlog import get_logger
from authentik.core.models import User
from authentik.events.models import ( from authentik.events.models import (
Event, Event,
Notification, Notification,
@ -53,7 +54,8 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
return return
LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger) LOGGER.debug("e(trigger): checking if trigger applies", trigger=trigger)
policy_engine = PolicyEngine(trigger, get_anonymous_user()) user = User.objects.filter(pk=event.user.get("pk")).first() or get_anonymous_user()
policy_engine = PolicyEngine(trigger, user)
policy_engine.mode = PolicyEngineMode.MODE_OR policy_engine.mode = PolicyEngineMode.MODE_OR
policy_engine.empty_result = False policy_engine.empty_result = False
policy_engine.use_cache = False policy_engine.use_cache = False

View File

@ -18,6 +18,8 @@ from django.core.asgi import get_asgi_application
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from structlog.stdlib import get_logger from structlog.stdlib import get_logger
from authentik.core.middleware import RESPONSE_HEADER_ID
# DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py # DJANGO_SETTINGS_MODULE is set in gunicorn.conf.py
defuse_stdlib() defuse_stdlib()
@ -67,6 +69,7 @@ class ASGILogger:
status_code: int status_code: int
start: float start: float
content_length: int content_length: int
request_id: str
def __init__(self, app: ASGIApp): def __init__(self, app: ASGIApp):
self.app = app self.app = app
@ -75,23 +78,29 @@ class ASGILogger:
self.scope = scope self.scope = scope
self.content_length = 0 self.content_length = 0
self.headers = dict(scope.get("headers", [])) self.headers = dict(scope.get("headers", []))
self.request_id = ""
async def send_hooked(message: Message) -> None: async def send_hooked(message: Message) -> None:
"""Hooked send method, which records status code and content-length, and for the final """Hooked send method, which records status code and content-length, and for the final
requests logs it""" requests logs it"""
headers = dict(message.get("headers", [])) headers = dict(message.get("headers", []))
if "status" in message: if "status" in message:
self.status_code = message["status"] self.status_code = message["status"]
if b"Content-Length" in headers: if b"Content-Length" in headers:
self.content_length += int(headers.get(b"Content-Length", b"0")) self.content_length += int(headers.get(b"Content-Length", b"0"))
if message["type"] == "http.response.start":
response_headers = dict(message["headers"])
self.request_id = response_headers.get(
RESPONSE_HEADER_ID.encode(), b""
).decode()
if message["type"] == "http.response.body" and not message.get( if message["type"] == "http.response.body" and not message.get(
"more_body", None "more_body", True
): ):
runtime = int((time() - self.start) * 1000) runtime = int((time() - self.start) * 1000)
self.log(runtime) self.log(runtime, request_id=self.request_id)
await send(message) await send(message)
self.start = time() self.start = time()
@ -111,7 +120,7 @@ class ASGILogger:
# Check if header has multiple values, and use the first one # Check if header has multiple values, and use the first one
return client_ip.split(", ")[0] return client_ip.split(", ")[0]
def log(self, runtime: float): def log(self, runtime: float, **kwargs):
"""Outpot access logs in a structured format""" """Outpot access logs in a structured format"""
host = self._get_ip() host = self._get_ip()
query_string = "" query_string = ""
@ -125,6 +134,7 @@ class ASGILogger:
status=self.status_code, status=self.status_code,
size=self.content_length / 1000 if self.content_length > 0 else 0, size=self.content_length / 1000 if self.content_length > 0 else 0,
runtime=runtime, runtime=runtime,
**kwargs,
) )

View File

@ -9,13 +9,13 @@
<div class="pf-c-card__body"> <div class="pf-c-card__body">
{% if connections.exists %} {% if connections.exists %}
<p>{% trans 'Connected.' %}</p> <p>{% trans 'Connected.' %}</p>
<a class="pf-c-button pf-m-danger" <a class="pf-c-button pf-m-danger ak-root-link"
href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}"> href="{% url 'authentik_sources_oauth:oauth-client-disconnect' source_slug=source.slug %}">
{% trans 'Disconnect' %} {% trans 'Disconnect' %}
</a> </a>
{% else %} {% else %}
<p>Not connected.</p> <p>Not connected.</p>
<a class="pf-c-button pf-m-primary" <a class="pf-c-button pf-m-primary ak-root-link"
href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}"> href="{% url 'authentik_sources_oauth:oauth-client-login' source_slug=source.slug %}">
{% trans 'Connect' %} {% trans 'Connect' %}
</a> </a>

View File

@ -19,7 +19,7 @@ services:
networks: networks:
- internal - internal
server: server:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.4-stable} image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: server command: server
environment: environment:
AUTHENTIK_REDIS__HOST: redis AUTHENTIK_REDIS__HOST: redis
@ -45,7 +45,7 @@ services:
env_file: env_file:
- .env - .env
worker: worker:
image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.4-stable} image: beryju/authentik:${AUTHENTIK_TAG:-2021.2.5-stable}
command: worker command: worker
networks: networks:
- internal - internal
@ -62,7 +62,7 @@ services:
env_file: env_file:
- .env - .env
static: static:
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.4-stable} image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.2.5-stable}
networks: networks:
- internal - internal
labels: labels:

View File

@ -4,7 +4,7 @@ name: authentik
home: https://goauthentik.io home: https://goauthentik.io
sources: sources:
- https://github.com/BeryJu/authentik - https://github.com/BeryJu/authentik
version: "2021.2.4-stable" version: "2021.2.5-stable"
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
dependencies: dependencies:
- name: postgresql - name: postgresql

View File

@ -4,7 +4,7 @@
|-----------------------------------|-------------------------|-------------| |-----------------------------------|-------------------------|-------------|
| image.name | beryju/authentik | Image used to run the authentik server and worker | | image.name | beryju/authentik | Image used to run the authentik server and worker |
| image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) | | image.name_static | beryju/authentik-static | Image used to run the authentik static server (CSS and JS Files) |
| image.tag | 2021.2.4-stable | Image tag | | image.tag | 2021.2.5-stable | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments | | image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment | | serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment | | workerReplicas | 1 | Replicas for the Worker deployment |

View File

@ -99,10 +99,12 @@ spec:
httpGet: httpGet:
path: /-/health/live/ path: /-/health/live/
port: http port: http
initialDelaySeconds: 15
readinessProbe: readinessProbe:
httpGet: httpGet:
path: /-/health/ready/ path: /-/health/ready/
port: http port: http
initialDelaySeconds: 15
resources: resources:
requests: requests:
cpu: 100m cpu: 100m

View File

@ -5,7 +5,7 @@ image:
name: beryju/authentik name: beryju/authentik
name_static: beryju/authentik-static name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 2021.2.4-stable tag: 2021.2.5-stable
pullPolicy: IfNotPresent pullPolicy: IfNotPresent
serverReplicas: 1 serverReplicas: 1

View File

@ -49,12 +49,14 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
// create the API client, with the transport // create the API client, with the transport
apiClient := client.New(transport, strfmt.Default) apiClient := client.New(transport, strfmt.Default)
log := log.WithField("logger", "authentik.outpost.ak-api-controller")
// Because we don't know the outpost UUID, we simply do a list and pick the first // Because we don't know the outpost UUID, we simply do a list and pick the first
// The service account this token belongs to should only have access to a single outpost // The service account this token belongs to should only have access to a single outpost
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth) outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
if err != nil { if err != nil {
panic(err) log.WithError(err).Panic("Failed to fetch configuration")
} }
outpost := outposts.Payload.Results[0] outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{})) doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -64,7 +66,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
Auth: auth, Auth: auth,
token: token, token: token,
logger: log.WithField("component", "ak-api-controller"), logger: log,
reloadOffset: time.Duration(rand.Intn(10)) * time.Second, reloadOffset: time.Duration(rand.Intn(10)) * time.Second,

View File

@ -40,7 +40,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
} }
ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header) ws.Dial(fmt.Sprintf(pathTemplate, scheme, pbURL.Host, outpostUUID.String()), header)
ac.logger.WithField("component", "ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik") ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithField("outpost", outpostUUID.String()).Debug("connecting to authentik")
ac.wsConn = ws ac.wsConn = ws
// Send hello message with our version // Send hello message with our version
@ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
} }
err := ws.WriteJSON(msg) err := ws.WriteJSON(msg)
if err != nil { if err != nil {
ac.logger.WithField("component", "ak-ws").WithError(err).Warning("Failed to hello to authentik") ac.logger.WithField("logger", "authentik.outpost.ak-ws").WithError(err).Warning("Failed to hello to authentik")
} }
} }

View File

@ -13,7 +13,12 @@ import (
) )
func doGlobalSetup(config map[string]interface{}) { func doGlobalSetup(config map[string]interface{}) {
log.SetFormatter(&log.JSONFormatter{}) log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
})
switch config[ConfigLogLevel].(string) { switch config[ConfigLogLevel].(string) {
case "debug": case "debug":
log.SetLevel(log.DebugLevel) log.SetLevel(log.DebugLevel)

View File

@ -31,7 +31,7 @@ func (s *Server) bundleProviders(providers []*models.ProxyOutpostConfig) []*prov
bundles[idx] = &providerBundle{ bundles[idx] = &providerBundle{
s: s, s: s,
Host: externalHost.Host, Host: externalHost.Host,
log: log.WithField("component", "proxy-bundle").WithField("provider", provider.Name), log: log.WithField("logger", "authentik.outpost.proxy-bundle").WithField("provider", provider.Name),
} }
bundles[idx].Build(provider) bundles[idx].Build(provider)
} }

View File

@ -129,7 +129,7 @@ func (pb *providerBundle) Build(provider *models.ProxyOutpostConfig) {
log.Printf("%s", err) log.Printf("%s", err)
os.Exit(1) os.Exit(1)
} }
oauthproxy, err := NewOAuthProxy(opts) oauthproxy, err := NewOAuthProxy(opts, provider)
if err != nil { if err != nil {
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err) log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
os.Exit(1) os.Exit(1)

View File

@ -95,7 +95,7 @@ type loggingHandler struct {
func LoggingHandler(h http.Handler) http.Handler { func LoggingHandler(h http.Handler) http.Handler {
return loggingHandler{ return loggingHandler{
handler: h, handler: h,
logger: log.WithField("component", "proxy-http-server"), logger: log.WithField("logger", "authentik.outpost.proxy-http-server"),
} }
} }
@ -104,19 +104,17 @@ func (h loggingHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
url := *req.URL url := *req.URL
responseLogger := &responseLogger{w: w} responseLogger := &responseLogger{w: w}
h.handler.ServeHTTP(responseLogger, req) h.handler.ServeHTTP(responseLogger, req)
duration := float64(time.Since(t)) / float64(time.Second) duration := float64(time.Since(t)) / float64(time.Millisecond)
h.logger.WithFields(log.Fields{ h.logger.WithFields(log.Fields{
"Client": req.RemoteAddr, "host": req.RemoteAddr,
"Host": req.Host, "vhost": req.Host,
"Protocol": req.Proto, "request_protocol": req.Proto,
"RequestDuration": fmt.Sprintf("%0.3f", duration), "runtime": fmt.Sprintf("%0.3f", duration),
"RequestMethod": req.Method, "method": req.Method,
"ResponseSize": responseLogger.Size(), "size": responseLogger.Size(),
"StatusCode": responseLogger.Status(), "status": responseLogger.Status(),
"Timestamp": t, "upstream": responseLogger.upstream,
"Upstream": responseLogger.upstream, "request_useragent": req.UserAgent(),
"UserAgent": req.UserAgent(), "request_username": responseLogger.authInfo,
"Username": responseLogger.authInfo,
}).Info(url.RequestURI()) }).Info(url.RequestURI())
// logger.PrintReq(responseLogger.authInfo, responseLogger.upstream, req, url, t, , )
} }

View File

@ -21,6 +21,7 @@ import (
"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions" "github.com/oauth2-proxy/oauth2-proxy/pkg/sessions"
"github.com/oauth2-proxy/oauth2-proxy/pkg/upstream" "github.com/oauth2-proxy/oauth2-proxy/pkg/upstream"
"github.com/oauth2-proxy/oauth2-proxy/providers" "github.com/oauth2-proxy/oauth2-proxy/providers"
"goauthentik.io/outpost/pkg/models"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -92,8 +93,8 @@ type OAuthProxy struct {
} }
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided // NewOAuthProxy creates a new instance of OAuthProxy from the options provided
func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) { func NewOAuthProxy(opts *options.Options, provider *models.ProxyOutpostConfig) (*OAuthProxy, error) {
logger := log.WithField("component", "proxy").WithField("client-id", opts.ClientID) logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name)
sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie) sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie)
if err != nil { if err != nil {
return nil, fmt.Errorf("error initialising session store: %v", err) return nil, fmt.Errorf("error initialising session store: %v", err)
@ -434,6 +435,7 @@ func (p *OAuthProxy) addHeadersForProxying(rw http.ResponseWriter, req *http.Req
authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password)) authVal := b64.StdEncoding.EncodeToString([]byte(username + ":" + password))
req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)} req.Header["Authorization"] = []string{fmt.Sprintf("Basic %s", authVal)}
} }
rw.Header().Set("GAP-Auth", session.PreferredUsername)
// Check if user has additional headers set that we should sent // Check if user has additional headers set that we should sent
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok { if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok {
if additionalHeaders == nil { if additionalHeaders == nil {

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"net" "net"
"net/http" "net/http"
"strings"
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -30,7 +31,7 @@ func NewServer(ac *ak.APIController) *Server {
} }
return &Server{ return &Server{
Handlers: make(map[string]*providerBundle), Handlers: make(map[string]*providerBundle),
logger: log.WithField("component", "proxy-http-server"), logger: log.WithField("logger", "authentik.outpost.proxy-http-server"),
defaultCert: defaultCert, defaultCert: defaultCert,
ak: ac, ak: ac,
} }
@ -50,12 +51,15 @@ func (s *Server) handler(w http.ResponseWriter, r *http.Request) {
return return
} }
} }
s.logger.WithField("host", r.Host).Debug("Host header does not match any we know of") // Get a list of all host keys we know
s.logger.Printf("%v+\n", s.Handlers) hostKeys := make([]string, 0, len(s.Handlers))
w.WriteHeader(400) for k := range s.Handlers {
hostKeys = append(hostKeys, k)
}
s.logger.WithField("host", r.Host).WithField("known-hosts", strings.Join(hostKeys, ", ")).Debug("Host header does not match any we know of")
w.WriteHeader(404)
return return
} }
s.logger.WithField("host", r.Host).Debug("passing request from host head")
handler.ServeHTTP(w, r) handler.ServeHTTP(w, r)
} }

View File

@ -1,3 +1,3 @@
package pkg package pkg
const VERSION = "2021.2.4-stable" const VERSION = "2021.2.5-stable"

View File

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

View File

@ -55,7 +55,12 @@ export class LibraryPage extends LitElement {
apps?: AKResponse<Application>; apps?: AKResponse<Application>;
static get styles(): CSSResult[] { static get styles(): CSSResult[] {
return COMMON_STYLES; return COMMON_STYLES.concat(css`
:host,
main {
height: 100%;
}
`);
} }
firstUpdated(): void { firstUpdated(): void {

View File

@ -89,10 +89,14 @@ export class SiteShell extends LitElement {
if (a.href === "") { if (a.href === "") {
return; return;
} }
if (a.href.startsWith("#")) {
return;
}
try { try {
const url = new URL(a.href); const url = new URL(a.href);
const qs = url.search || ""; const qs = url.search || "";
a.href = `#${url.pathname}${qs}`; const hash = (url.hash || "#").substring(2, Infinity);
a.href = `#${url.pathname}${qs}${hash}`;
} catch (e) { } catch (e) {
console.debug(`authentik/site-shell: error ${e}`); console.debug(`authentik/site-shell: error ${e}`);
a.href = `#${a.href}`; a.href = `#${a.href}`;

View File

@ -52,13 +52,13 @@ export class OutpostListPage extends TablePage<Outpost> {
})}</ul>`, })}</ul>`,
html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`, html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`,
html` html`
<ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update`)}"> <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/update/`)}">
<ak-spinner-button slot="trigger" class="pf-m-secondary"> <ak-spinner-button slot="trigger" class="pf-m-secondary">
${gettext("Edit")} ${gettext("Edit")}
</ak-spinner-button> </ak-spinner-button>
<div slot="modal"></div> <div slot="modal"></div>
</ak-modal-button>&nbsp; </ak-modal-button>&nbsp;
<ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete`)}"> <ak-modal-button href="${Outpost.adminUrl(`${item.pk}/delete/`)}">
<ak-spinner-button slot="trigger" class="pf-m-danger"> <ak-spinner-button slot="trigger" class="pf-m-danger">
${gettext("Delete")} ${gettext("Delete")}
</ak-spinner-button> </ak-spinner-button>

View File

@ -15,7 +15,7 @@ Download the latest `docker-compose.yml` from [here](https://raw.githubuserconte
To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env` To optionally enable error-reporting, run `echo AUTHENTIK_ERROR_REPORTING__ENABLED=true >> .env`
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.2.4-stable >> .env` To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.2.5-stable >> .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

@ -24,7 +24,7 @@ image:
name: beryju/authentik name: beryju/authentik
name_static: beryju/authentik-static name_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 2021.2.4-stable tag: 2021.2.5-stable
serverReplicas: 1 serverReplicas: 1
workerReplicas: 1 workerReplicas: 1

View File

@ -0,0 +1,14 @@
---
title: Next release
---
## Headline Changes
- Simplify role-based access
Instead of having to create a Group Membership policy for every group you want to use, you can now select a Group and even a User directly in a binding.
When a group is selected, the binding behaves the same as if a Group Membership policy exists.
When a user is selected, the binding checks the user of the request, and denies the request when the user doesn't match.