Compare commits

...

23 Commits

Author SHA1 Message Date
5725e54334 release: 2021.2.6-stable 2021-02-27 18:16:46 +01:00
c20856ca17 web: fix colourstyles not being included in common_styles
# Conflicts:
#	authentik/events/geo.py
#	web/src/elements/buttons/TokenCopyButton.ts
2021-02-27 18:16:32 +01:00
9c73e9cf4e web: fix colourstyles not being included in common_styles
# Conflicts:
#	authentik/events/geo.py
#	web/src/elements/buttons/TokenCopyButton.ts
2021-02-27 17:36:07 +01:00
b10c3db13d web: add sentry CaptureConsole
# Conflicts:
#	web/package.json
2021-02-27 17:19:43 +01:00
fe290aa214 sources/ldap: fix password setter on users which are not LDAP 2021-02-27 17:04:16 +01:00
a2e69bd250 sources/ldap: fix API error when source has not synced yet 2021-02-27 17:04:16 +01:00
d2a35eb8de admin: fix missing success_url for clean views 2021-02-27 17:04:16 +01:00
3437d8b4b0 flows: handle error when app cannot be found during import 2021-02-27 17:04:16 +01:00
b862bf4284 providers/oauth2: fix error when no login event could be found 2021-02-27 17:04:16 +01:00
de22a367b1 events: fix error when event can't be loaded into rule task 2021-02-27 17:04:15 +01:00
17ab895652 flows: fix glob pattern for doc flows 2021-02-27 17:04:06 +01:00
a4d5815e1b policies: sort groups in groupmembership policy and binding
closes #595

# Conflicts:
#	authentik/policies/group_membership/forms.py
2021-02-27 17:02:34 +01:00
e81d3dad3e release: 2021.2.5-stable 2021-02-24 09:54:06 +01:00
5aabaebd96 root: fix request_id not being logged for actual asgi requests 2021-02-24 09:45:52 +01:00
7b60bca297 web: fix SiteShell breaking links when handlers are updated twice 2021-02-24 09:45:08 +01:00
a07d7456c8 web: fix outpost edit/delete buttons 2021-02-24 09:44:55 +01:00
f33369bf0c helm: add initial wait for healthcheck 2021-02-24 09:44:39 +01:00
1abcff39c7 outpost: improve logging output, ensure fields match api server 2021-02-24 09:44:24 +01:00
c1caf84d92 events: fix user QuerySet being passed 2021-02-24 09:44:05 +01:00
86c069fe64 admin: fix policy list not having a refresh button 2021-02-24 09:43:57 +01:00
ce0140ef67 events: pass Event's user to Notification policy engine when present 2021-02-24 09:43:50 +01:00
bba43c5109 sources/oauth: fix buttons not being ak-root-link 2021-02-24 09:23:44 +01:00
d99a415502 web: fix library not being full height, again 2021-02-24 09:23:40 +01:00
44 changed files with 217 additions and 105 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -18,7 +18,7 @@ class PolicyCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
"""View to clear Policy cache"""
form_class = PolicyCacheClearForm
success_url = "/"
template_name = "generic/form_non_model.html"
success_message = _("Successfully cleared Policy cache")
@ -36,7 +36,7 @@ class FlowCacheClearView(AdminRequiredMixin, SuccessMessageMixin, FormView):
"""View to clear Flow cache"""
form_class = FlowCacheClearForm
success_url = "/"
template_name = "generic/form_non_model.html"
success_message = _("Successfully cleared Flow cache")

View File

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

View File

@ -2,6 +2,7 @@
from guardian.shortcuts import get_anonymous_user
from structlog import get_logger
from authentik.core.models import User
from authentik.events.models import (
Event,
Notification,
@ -29,7 +30,11 @@ def event_notification_handler(event_uuid: str):
@CELERY_APP.task()
def event_trigger_handler(event_uuid: str, trigger_name: str):
"""Check if policies attached to NotificationRule match event"""
event: Event = Event.objects.get(event_uuid=event_uuid)
events = Event.objects.filter(event_uuid=event_uuid)
if not events.exists():
LOGGER.warning("event doesn't exist yet or anymore", event_uuid=event_uuid)
return
event: Event = events.first()
trigger: NotificationRule = NotificationRule.objects.get(name=trigger_name)
if "policy_uuid" in event.context:
@ -53,7 +58,8 @@ def event_trigger_handler(event_uuid: str, trigger_name: str):
return
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.empty_result = False
policy_engine.use_cache = False

View File

@ -24,6 +24,6 @@ def pbflow_tester(file_name: str) -> Callable:
return tester
for flow_file in glob("website/static/flows/*.pbflow"):
for flow_file in glob("website/static/flows/*.akflow"):
method_name = Path(flow_file).stem.replace("-", "_").replace(".", "_")
setattr(TestTransferDocs, f"test_flow_{method_name}", pbflow_tester(flow_file))

View File

@ -152,7 +152,13 @@ class FlowImporter:
entries = deepcopy(self.__import.entries)
for entry in entries:
model_app_label, model_name = entry.model.split(".")
model: SerializerModel = apps.get_model(model_app_label, model_name)
try:
model: SerializerModel = apps.get_model(model_app_label, model_name)
except LookupError:
self.logger.error(
"app or model does not exist", app=model_app_label, model=model_name
)
return False
# Validate each single entry
try:
serializer = self._validate_single(entry)

View File

@ -2,6 +2,7 @@
from django import forms
from authentik.core.models import Group
from authentik.policies.forms import GENERAL_FIELDS
from authentik.policies.group_membership.models import GroupMembershipPolicy
@ -9,6 +10,8 @@ from authentik.policies.group_membership.models import GroupMembershipPolicy
class GroupMembershipPolicyForm(forms.ModelForm):
"""GroupMembershipPolicy Form"""
group = forms.ModelChoiceField(queryset=Group.objects.all().order_by("name"))
class Meta:
model = GroupMembershipPolicy

View File

@ -4,6 +4,7 @@ import binascii
import json
import time
from dataclasses import asdict, dataclass, field
from datetime import datetime
from hashlib import sha256
from typing import Any, Dict, List, Optional, Type
from urllib.parse import urlparse
@ -480,10 +481,14 @@ class RefreshToken(ExpiringModel, BaseGrantModel):
now + timedelta_from_string(self.provider.token_validity).seconds
)
# We use the timestamp of the user's last successful login (EventAction.LOGIN) for auth_time
auth_event = Event.objects.filter(
auth_events = Event.objects.filter(
action=EventAction.LOGIN, user=get_user(user)
).latest("created")
auth_time = int(dateformat.format(auth_event.created, "U"))
).order_by("-created")
# Fallback in case we can't find any login events
auth_time = datetime.now()
if auth_events.exists():
auth_time = auth_events.first().created
auth_time = int(dateformat.format(auth_time, "U"))
token = IDToken(
iss=self.provider.get_issuer(request),

View File

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

View File

@ -1,5 +1,6 @@
"""Source API Views"""
from datetime import datetime
from time import time
from django.core.cache import cache
from django.db.models.base import Model
@ -68,7 +69,7 @@ class LDAPSourceViewSet(ModelViewSet):
def sync_status(self, request: Request, slug: str) -> Response:
"""Get source's sync status"""
source = self.get_object()
last_sync = cache.get(source.state_cache_prefix("last_sync"), None)
last_sync = cache.get(source.state_cache_prefix("last_sync"), time())
return Response(
LDAPSourceSyncStatusSerializer(
{"last_sync": datetime.fromtimestamp(last_sync)}

View File

@ -77,7 +77,8 @@ class LDAPPasswordChanger:
"""Change user's password"""
user_dn = user.attributes.get(LDAP_DISTINGUISHED_NAME, None)
if not user_dn:
raise AttributeError(f"User has no {LDAP_DISTINGUISHED_NAME} set.")
LOGGER.info(f"User has no {LDAP_DISTINGUISHED_NAME} set.")
return
self._source.connection.extend.microsoft.modify_password(user_dn, password)
def _ad_check_password_existing(self, password: str, user_dn: str) -> bool:

View File

@ -9,13 +9,13 @@
<div class="pf-c-card__body">
{% if connections.exists %}
<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 %}">
{% trans 'Disconnect' %}
</a>
{% else %}
<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 %}">
{% trans 'Connect' %}
</a>

View File

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

View File

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

View File

@ -4,7 +4,7 @@
|-----------------------------------|-------------------------|-------------|
| 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.tag | 2021.2.4-stable | Image tag |
| image.tag | 2021.2.6-stable | Image tag |
| image.pullPolicy | IfNotPresent | Image Pull Policy used for all deployments |
| serverReplicas | 1 | Replicas for the Server deployment |
| workerReplicas | 1 | Replicas for the Worker deployment |

View File

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

View File

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

View File

@ -49,12 +49,14 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
// create the API client, with the transport
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
// The service account this token belongs to should only have access to a single outpost
outposts, err := apiClient.Outposts.OutpostsOutpostsList(outposts.NewOutpostsOutpostsListParams(), auth)
if err != nil {
panic(err)
log.WithError(err).Panic("Failed to fetch configuration")
}
outpost := outposts.Payload.Results[0]
doGlobalSetup(outpost.Config.(map[string]interface{}))
@ -64,7 +66,7 @@ func NewAPIController(pbURL url.URL, token string) *APIController {
Auth: auth,
token: token,
logger: log.WithField("component", "ak-api-controller"),
logger: log,
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)
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
// Send hello message with our version
@ -52,7 +52,7 @@ func (ac *APIController) initWS(pbURL url.URL, outpostUUID strfmt.UUID) {
}
err := ws.WriteJSON(msg)
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{}) {
log.SetFormatter(&log.JSONFormatter{})
log.SetFormatter(&log.JSONFormatter{
FieldMap: log.FieldMap{
log.FieldKeyMsg: "event",
log.FieldKeyTime: "timestamp",
},
})
switch config[ConfigLogLevel].(string) {
case "debug":
log.SetLevel(log.DebugLevel)

View File

@ -31,7 +31,7 @@ func (s *Server) bundleProviders(providers []*models.ProxyOutpostConfig) []*prov
bundles[idx] = &providerBundle{
s: s,
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)
}

View File

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

View File

@ -95,7 +95,7 @@ type loggingHandler struct {
func LoggingHandler(h http.Handler) http.Handler {
return loggingHandler{
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
responseLogger := &responseLogger{w: w}
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{
"Client": req.RemoteAddr,
"Host": req.Host,
"Protocol": req.Proto,
"RequestDuration": fmt.Sprintf("%0.3f", duration),
"RequestMethod": req.Method,
"ResponseSize": responseLogger.Size(),
"StatusCode": responseLogger.Status(),
"Timestamp": t,
"Upstream": responseLogger.upstream,
"UserAgent": req.UserAgent(),
"Username": responseLogger.authInfo,
"host": req.RemoteAddr,
"vhost": req.Host,
"request_protocol": req.Proto,
"runtime": fmt.Sprintf("%0.3f", duration),
"method": req.Method,
"size": responseLogger.Size(),
"status": responseLogger.Status(),
"upstream": responseLogger.upstream,
"request_useragent": req.UserAgent(),
"request_username": responseLogger.authInfo,
}).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/upstream"
"github.com/oauth2-proxy/oauth2-proxy/providers"
"goauthentik.io/outpost/pkg/models"
log "github.com/sirupsen/logrus"
)
@ -92,8 +93,8 @@ type OAuthProxy struct {
}
// NewOAuthProxy creates a new instance of OAuthProxy from the options provided
func NewOAuthProxy(opts *options.Options) (*OAuthProxy, error) {
logger := log.WithField("component", "proxy").WithField("client-id", opts.ClientID)
func NewOAuthProxy(opts *options.Options, provider *models.ProxyOutpostConfig) (*OAuthProxy, error) {
logger := log.WithField("logger", "authentik.outpost.proxy").WithField("provider", provider.Name)
sessionStore, err := sessions.NewSessionStore(&opts.Session, &opts.Cookie)
if err != nil {
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))
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
if additionalHeaders, ok := userAttributes["additionalHeaders"].(map[string]string); ok {
if additionalHeaders == nil {

View File

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

View File

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

39
web/package-lock.json generated
View File

@ -245,6 +245,24 @@
}
}
},
"@sentry/integrations": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@sentry/integrations/-/integrations-6.2.0.tgz",
"integrity": "sha512-gvAhP61qs2fog2xCTDs94ZT8cZbWEjFZmOWfT1VXlZDIVopIporj5Qe6IgrLTiCWL61Yko5h5nFnPZ4mpjf+0w==",
"requires": {
"@sentry/types": "6.2.0",
"@sentry/utils": "6.2.0",
"localforage": "^1.8.1",
"tslib": "^1.9.3"
},
"dependencies": {
"tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
}
}
},
"@sentry/minimal": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-6.1.0.tgz",
@ -1756,6 +1774,11 @@
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
"integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw=="
},
"immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
},
"import-fresh": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
@ -2010,6 +2033,14 @@
"type-check": "~0.4.0"
}
},
"lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
"integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=",
"requires": {
"immediate": "~3.0.5"
}
},
"lit-analyzer": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/lit-analyzer/-/lit-analyzer-1.2.1.tgz",
@ -2191,6 +2222,14 @@
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-1.3.0.tgz",
"integrity": "sha512-0Q1bwmaFH9O14vycPHw8C/IeHMk/uSDldVLIefu/kfbTBGIc44KGH6A8p1bDfxUfHdc8q6Ct7kQklWoHgr4t1Q=="
},
"localforage": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.9.0.tgz",
"integrity": "sha512-rR1oyNrKulpe+VM9cYmcFn6tsHuokyVHFaCM3+osEmxaHTbEk8oQu6eGDfS6DQLWi/N67XRmB8ECG37OES368g==",
"requires": {
"lie": "3.1.1"
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",

View File

@ -14,7 +14,8 @@
"@patternfly/patternfly": "^4.80.3",
"@sentry/browser": "^6.1.0",
"@sentry/tracing": "^6.1.0",
"@types/chart.js": "^2.9.30",
"@sentry/integrations": "^6.2.0",
"@types/chart.js": "^2.9.31",
"@types/codemirror": "0.0.108",
"chart.js": "^2.9.4",
"codemirror": "^5.59.2",

View File

@ -3,6 +3,7 @@ import * as Sentry from "@sentry/browser";
import { Integrations } from "@sentry/tracing";
import { VERSION } from "../constants";
import { SentryIgnoredError } from "../common/errors";
import { CaptureConsole as CaptureConsoleIntegration } from "@sentry/integrations";
export class Config {
branding_logo: string;
@ -22,7 +23,10 @@ export class Config {
Sentry.init({
dsn: "https://a579bb09306d4f8b8d8847c052d3a1d3@sentry.beryju.org/8",
release: `authentik@${VERSION}`,
integrations: [new Integrations.BrowserTracing()],
integrations: [
new Integrations.BrowserTracing(),
new CaptureConsoleIntegration(),
],
tracesSampleRate: 0.6,
environment: config.error_reporting_environment,
beforeSend(event: Sentry.Event, hint: Sentry.EventHint) {

View File

@ -1,3 +1,4 @@
import { css, CSSResult } from "lit-element";
// @ts-ignore
import PF from "@patternfly/patternfly/patternfly.css";
// @ts-ignore
@ -6,10 +7,31 @@ import PFAddons from "@patternfly/patternfly/patternfly-addons.css";
import FA from "@fortawesome/fontawesome-free/css/fontawesome.css";
// @ts-ignore
import AKGlobal from "../authentik.css";
import { CSSResult } from "lit-element";
// @ts-ignore
import CodeMirrorStyle from "codemirror/lib/codemirror.css";
// @ts-ignore
import CodeMirrorTheme from "codemirror/theme/monokai.css";
export const COMMON_STYLES: CSSResult[] = [PF, PFAddons, FA, AKGlobal, CodeMirrorStyle, CodeMirrorTheme];
export const ColorStyles = css`
.pf-m-success {
color: var(--pf-global--success-color--100);
}
.pf-c-button.pf-m-success {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--success-color--100);
}
.pf-m-warning {
color: var(--pf-global--warning-color--100);
}
.pf-c-button.pf-m-warning {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--warning-color--100);
}
.pf-m-danger {
color: var(--pf-global--danger-color--100);
}
.pf-c-button.pf-m-danger {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--danger-color--100);
}
`;
export const COMMON_STYLES: CSSResult[] = [PF, PFAddons, FA, AKGlobal, CodeMirrorStyle, CodeMirrorTheme, ColorStyles];

View File

@ -1,31 +1,6 @@
import { css } from "lit-element";
export const PRIMARY_CLASS = "pf-m-primary";
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 ColorStyles = css`
.pf-m-success {
color: var(--pf-global--success-color--100);
}
.pf-c-button.pf-m-success {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--success-color--100);
}
.pf-m-warning {
color: var(--pf-global--warning-color--100);
}
.pf-c-button.pf-m-warning {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--warning-color--100);
}
.pf-m-danger {
color: var(--pf-global--danger-color--100);
}
.pf-c-button.pf-m-danger {
color: var(--pf-c-button--m-primary--Color);
background-color: var(--pf-global--danger-color--100);
}
`;
export const VERSION = "2021.2.4-stable";
export const VERSION = "2021.2.6-stable";

View File

@ -5,7 +5,8 @@ import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
// @ts-ignore
import SpinnerStyle from "@patternfly/patternfly/components/Spinner/spinner.css";
import { ColorStyles, PRIMARY_CLASS, PROGRESS_CLASS } from "../../constants";
import { PRIMARY_CLASS, PROGRESS_CLASS } from "../../constants";
import { ColorStyles } from "../../common/styles";
@customElement("ak-spinner-button")
export class SpinnerButton extends LitElement {

View File

@ -4,7 +4,8 @@ import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
// @ts-ignore
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
import { tokenByIdentifier } from "../../api/Tokens";
import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
import { ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
import { ColorStyles } from "../../common/styles";
@customElement("ak-token-copy-button")
export class TokenCopyButton extends LitElement {

View File

@ -2,7 +2,6 @@ import { gettext } from "django";
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
import { ifDefined } from "lit-html/directives/if-defined";
import { COMMON_STYLES } from "../../common/styles";
import { ColorStyles } from "../../constants";
@customElement("ak-aggregate-card")
export class AggregateCard extends LitElement {
@ -25,7 +24,7 @@ export class AggregateCard extends LitElement {
text-align: center;
color: var(--pf-global--Color--100);
}
`, ColorStyles]);
`]);
}
renderInner(): TemplateResult {

View File

@ -3,7 +3,6 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
import CodeMirrorStyle from "codemirror/lib/codemirror.css";
// @ts-ignore
import CodeMirrorTheme from "codemirror/theme/monokai.css";
import { ColorStyles } from "../../constants";
import { COMMON_STYLES } from "../../common/styles";
import { Route } from "./Route";
import { ROUTES } from "../../routes";
@ -23,7 +22,6 @@ export class RouterOutlet extends LitElement {
return [
CodeMirrorStyle,
CodeMirrorTheme,
ColorStyles,
css`
:host {
height: 100vh;

View File

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

View File

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

View File

@ -52,13 +52,13 @@ export class OutpostListPage extends TablePage<Outpost> {
})}</ul>`,
html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`,
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">
${gettext("Edit")}
</ak-spinner-button>
<div slot="modal"></div>
</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">
${gettext("Delete")}
</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 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.6-stable >> .env`
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_static: beryju/authentik-static
name_outposts: beryju/authentik # Prefix used for Outpost deployments, Outpost type and version is appended
tag: 2021.2.4-stable
tag: 2021.2.6-stable
serverReplicas: 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.