Compare commits
43 Commits
version/20
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
32e5ebb8a3 | |||
597e00dd86 | |||
dd31191845 | |||
e9d95b1311 | |||
3319547a0e | |||
1a00730cdd | |||
466723573c | |||
ea784d47f4 | |||
77d5ba2862 | |||
f4580a1097 | |||
9e3d1f0baa | |||
c002c4b610 | |||
dde5e910cf | |||
5218332bce | |||
28cd08bbba | |||
3cb0575a1e | |||
dc1c1b9569 | |||
662d117b66 | |||
b2449757f9 | |||
a0753bfc88 | |||
e2a771bdaa | |||
23de9df2a5 | |||
5c739ebed2 | |||
d3f8d7120f | |||
21fd251edf | |||
28cededb90 | |||
d420719649 | |||
0018fbacd3 | |||
8c41d2f4cb | |||
3941590d0c | |||
dc4a7c35da | |||
e8c9b70ae8 | |||
74d240dfd4 | |||
7d296b2119 | |||
373793ce9a | |||
5c0ec7554b | |||
792fa45dca | |||
743aaea15e | |||
de03ed0aec | |||
e68ec16a34 | |||
68a0219d0f | |||
38d9533afd | |||
7538af5e09 |
@ -1,9 +1,11 @@
|
||||
[bumpversion]
|
||||
current_version = 2021.3.1-rc1
|
||||
current_version = 2021.3.1
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
serialize = {major}.{minor}.{patch}-{release}
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-?(?P<release>.*)
|
||||
serialize =
|
||||
{major}.{minor}.{patch}-{release}
|
||||
{major}.{minor}.{patch}
|
||||
message = release: {new_version}
|
||||
tag_name = version/{new_version}
|
||||
|
||||
|
14
.github/workflows/release.yml
vendored
14
.github/workflows/release.yml
vendored
@ -18,11 +18,11 @@ jobs:
|
||||
- name: Building Docker Image
|
||||
run: docker build
|
||||
--no-cache
|
||||
-t beryju/authentik:2021.3.1-rc1
|
||||
-t beryju/authentik:2021.3.1
|
||||
-t beryju/authentik:latest
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik:2021.3.1-rc1
|
||||
run: docker push beryju/authentik:2021.3.1
|
||||
- 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.3.1-rc1 \
|
||||
-t beryju/authentik-proxy:2021.3.1 \
|
||||
-t beryju/authentik-proxy:latest \
|
||||
-f proxy.Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik-proxy:2021.3.1-rc1
|
||||
run: docker push beryju/authentik-proxy:2021.3.1
|
||||
- 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.3.1-rc1 \
|
||||
-t beryju/authentik-static:2021.3.1 \
|
||||
-t beryju/authentik-static:latest \
|
||||
-f Dockerfile .
|
||||
- name: Push Docker Container to Registry (versioned)
|
||||
run: docker push beryju/authentik-static:2021.3.1-rc1
|
||||
run: docker push beryju/authentik-static:2021.3.1
|
||||
- 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.3.1-rc1
|
||||
tagName: 2021.3.1
|
||||
environment: beryjuorg-prod
|
||||
|
24
Pipfile.lock
generated
24
Pipfile.lock
generated
@ -95,10 +95,10 @@
|
||||
},
|
||||
"autobahn": {
|
||||
"hashes": [
|
||||
"sha256:884f79c50fdc55ade2c315946a9caa145e8b10075eee9d2c2594ea5e8f5226aa",
|
||||
"sha256:bf7a9d302a34d0f719d43c57f65ca1f2f5c982dd6ea0c11e1e190ef6f43710fe"
|
||||
"sha256:9195df8af03b0ff29ccd4b7f5abbde957ee90273465942205f9a1bad6c3f07ac",
|
||||
"sha256:e126c1f583e872fb59e79d36977cfa1f2d0a8a79f90ae31f406faae7664b8e03"
|
||||
],
|
||||
"version": "==21.2.2"
|
||||
"version": "==21.3.1"
|
||||
},
|
||||
"automat": {
|
||||
"hashes": [
|
||||
@ -116,18 +116,18 @@
|
||||
},
|
||||
"boto3": {
|
||||
"hashes": [
|
||||
"sha256:3570a3c0fbd80bcb30449f87cf9d2f7abb67fac2a5e317d002f9921c59be9b17",
|
||||
"sha256:ceff2f32ba05acc9ee35a6dd82e29ea285d63e889bed39a6ba7a700146f43749"
|
||||
"sha256:c9513a9ea00f8d17ecdc02c391ae956bf0f990aa07deec11c421607c09b294e1",
|
||||
"sha256:f84ca60e9605af69022f039c035b33d519531eeaac52724b9223a5465f4a8b6b"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.17.18"
|
||||
"version": "==1.17.19"
|
||||
},
|
||||
"botocore": {
|
||||
"hashes": [
|
||||
"sha256:51900b10da4ae45be4b16045e5b2ff7d1158a7955d9d7cc5e5a9ba3170f10586",
|
||||
"sha256:b181f32d9075e5419a89fa9636ce95946c15459c9bfadfabb53ca902fc8072b8"
|
||||
"sha256:135b5f30e6662b46d804f993bf31d9c7769c6c0848321ed0aa0393f5b9c19a94",
|
||||
"sha256:8e42c78d2eb888551635309158c04ef2648a96d8c2c70dbce7712c6ce8629759"
|
||||
],
|
||||
"version": "==1.20.18"
|
||||
"version": "==1.20.19"
|
||||
},
|
||||
"cachetools": {
|
||||
"hashes": [
|
||||
@ -1249,10 +1249,10 @@
|
||||
},
|
||||
"websocket-client": {
|
||||
"hashes": [
|
||||
"sha256:0fc45c961324d79c781bab301359d5a1b00b13ad1b10415a4780229ef71a5549",
|
||||
"sha256:d735b91d6d1692a6a181f2a8c9e0238e5f6373356f561bb9dc4c7af36f452010"
|
||||
"sha256:44b5df8f08c74c3d82d28100fdc81f4536809ce98a17f0757557813275fbb663",
|
||||
"sha256:63509b41d158ae5b7f67eb4ad20fecbb4eee99434e73e140354dc3ff8e09716f"
|
||||
],
|
||||
"version": "==0.57.0"
|
||||
"version": "==0.58.0"
|
||||
},
|
||||
"websockets": {
|
||||
"hashes": [
|
||||
|
10
README.md
10
README.md
@ -1,4 +1,6 @@
|
||||
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="250" alt="authentik logo">
|
||||
<p align="center">
|
||||
<img src="https://goauthentik.io/img/icon_top_brand_colour.svg" height="150" alt="authentik logo">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
@ -22,8 +24,10 @@ For bigger setups, there is a Helm Chart in the `helm/` directory. This is docum
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
Light | Dark
|
||||
--- | ---
|
||||
 | 
|
||||
 | 
|
||||
|
||||
## Development
|
||||
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | ------------------ |
|
||||
| 0.13.x | :white_check_mark: |
|
||||
| 0.14.x | :white_check_mark: |
|
||||
| 2021.1.x | :white_check_mark: |
|
||||
| 2021.2.x | :white_check_mark: |
|
||||
| 2021.3.x | :white_check_mark: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
"""authentik"""
|
||||
__version__ = "2021.3.1-rc1"
|
||||
__version__ = "2021.3.1"
|
||||
|
@ -7,14 +7,14 @@ from django.http.response import Http404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.fields import CharField, DateTimeField, IntegerField, ListField
|
||||
from rest_framework.fields import CharField, ChoiceField, DateTimeField, ListField
|
||||
from rest_framework.permissions import IsAdminUser
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.events.monitored_tasks import TaskInfo
|
||||
from authentik.events.monitored_tasks import TaskInfo, TaskResultStatus
|
||||
|
||||
|
||||
class TaskSerializer(Serializer):
|
||||
@ -24,7 +24,10 @@ class TaskSerializer(Serializer):
|
||||
task_description = CharField()
|
||||
task_finish_timestamp = DateTimeField(source="finish_timestamp")
|
||||
|
||||
status = IntegerField(source="result.status.value")
|
||||
status = ChoiceField(
|
||||
source="result.status.name",
|
||||
choices=[(x.name, x.name) for x in TaskResultStatus],
|
||||
)
|
||||
messages = ListField(source="result.messages")
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
|
@ -55,7 +55,7 @@ class VersionViewSet(ListModelMixin, GenericViewSet):
|
||||
def get_queryset(self): # pragma: no cover
|
||||
return None
|
||||
|
||||
@swagger_auto_schema(responses={200: VersionSerializer(many=True)})
|
||||
@swagger_auto_schema(responses={200: VersionSerializer(many=False)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Get running and latest version."""
|
||||
return Response(VersionSerializer(True).data)
|
||||
|
@ -27,7 +27,9 @@
|
||||
</div>
|
||||
</section>
|
||||
<footer class="pf-c-modal-box__footer">
|
||||
<input class="pf-c-button pf-m-primary" type="submit" form="main-form" value="{% block action %}{% endblock %}" />
|
||||
<ak-spinner-button form="main-form">
|
||||
{% block action %}{% endblock %}
|
||||
</ak-spinner-button>
|
||||
<a class="pf-c-button pf-m-secondary" href="{% back %}">{% trans "Cancel" %}</a>
|
||||
</footer>
|
||||
{% endblock %}
|
||||
|
@ -6,6 +6,7 @@ from rest_framework.response import Response
|
||||
class Pagination(pagination.PageNumberPagination):
|
||||
"""Pagination which includes total pages and current page"""
|
||||
|
||||
page_query_param = "page"
|
||||
page_size_query_param = "page_size"
|
||||
|
||||
def get_paginated_response(self, data):
|
||||
|
97
authentik/api/pagination_schema.py
Normal file
97
authentik/api/pagination_schema.py
Normal file
@ -0,0 +1,97 @@
|
||||
"""Swagger Pagination Schema class"""
|
||||
from typing import OrderedDict
|
||||
|
||||
from drf_yasg2 import openapi
|
||||
from drf_yasg2.inspectors import PaginatorInspector
|
||||
|
||||
|
||||
class PaginationInspector(PaginatorInspector):
|
||||
"""Swagger Pagination Schema class"""
|
||||
|
||||
def get_paginated_response(self, paginator, response_schema):
|
||||
"""
|
||||
:param BasePagination paginator: the paginator
|
||||
:param openapi.Schema response_schema: the response schema that must be paged.
|
||||
:rtype: openapi.Schema
|
||||
"""
|
||||
|
||||
return openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties=OrderedDict(
|
||||
(
|
||||
(
|
||||
"pagination",
|
||||
openapi.Schema(
|
||||
type=openapi.TYPE_OBJECT,
|
||||
properties=OrderedDict(
|
||||
(
|
||||
("next", openapi.Schema(type=openapi.TYPE_NUMBER)),
|
||||
(
|
||||
"previous",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
("count", openapi.Schema(type=openapi.TYPE_NUMBER)),
|
||||
(
|
||||
"current",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"total_pages",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"start_index",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
(
|
||||
"end_index",
|
||||
openapi.Schema(type=openapi.TYPE_NUMBER),
|
||||
),
|
||||
)
|
||||
),
|
||||
required=[
|
||||
"next",
|
||||
"previous",
|
||||
"count",
|
||||
"current",
|
||||
"total_pages",
|
||||
"start_index",
|
||||
"end_index",
|
||||
],
|
||||
),
|
||||
),
|
||||
("results", response_schema),
|
||||
)
|
||||
),
|
||||
required=["results", "pagination"],
|
||||
)
|
||||
|
||||
def get_paginator_parameters(self, paginator):
|
||||
"""
|
||||
Get the pagination parameters for a single paginator **instance**.
|
||||
|
||||
Should return :data:`.NotHandled` if this inspector
|
||||
does not know how to handle the given `paginator`.
|
||||
|
||||
:param BasePagination paginator: the paginator
|
||||
:rtype: list[openapi.Parameter]
|
||||
"""
|
||||
|
||||
return [
|
||||
openapi.Parameter(
|
||||
"page",
|
||||
openapi.IN_QUERY,
|
||||
"Page Index",
|
||||
False,
|
||||
None,
|
||||
openapi.TYPE_INTEGER,
|
||||
),
|
||||
openapi.Parameter(
|
||||
"page_size",
|
||||
openapi.IN_QUERY,
|
||||
"Page Size",
|
||||
False,
|
||||
None,
|
||||
openapi.TYPE_INTEGER,
|
||||
),
|
||||
]
|
@ -1,10 +1,11 @@
|
||||
"""core Configs API"""
|
||||
from django.db.models import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.fields import BooleanField, CharField
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ReadOnlyField, Serializer
|
||||
from rest_framework.serializers import Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
from authentik.lib.config import CONFIG
|
||||
@ -13,12 +14,12 @@ from authentik.lib.config import CONFIG
|
||||
class ConfigSerializer(Serializer):
|
||||
"""Serialize authentik Config into DRF Object"""
|
||||
|
||||
branding_logo = ReadOnlyField()
|
||||
branding_title = ReadOnlyField()
|
||||
branding_logo = CharField(read_only=True)
|
||||
branding_title = CharField(read_only=True)
|
||||
|
||||
error_reporting_enabled = ReadOnlyField()
|
||||
error_reporting_environment = ReadOnlyField()
|
||||
error_reporting_send_pii = ReadOnlyField()
|
||||
error_reporting_enabled = BooleanField(read_only=True)
|
||||
error_reporting_environment = CharField(read_only=True)
|
||||
error_reporting_send_pii = BooleanField(read_only=True)
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
@ -32,7 +33,7 @@ class ConfigsViewSet(ViewSet):
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(responses={200: ConfigSerializer(many=True)})
|
||||
@swagger_auto_schema(responses={200: ConfigSerializer(many=False)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""Retrive public configuration options"""
|
||||
config = ConfigSerializer(
|
||||
|
@ -1,37 +0,0 @@
|
||||
"""core messages API"""
|
||||
from django.contrib.messages import get_messages
|
||||
from django.db.models import Model
|
||||
from drf_yasg2.utils import swagger_auto_schema
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.serializers import ReadOnlyField, Serializer
|
||||
from rest_framework.viewsets import ViewSet
|
||||
|
||||
|
||||
class MessageSerializer(Serializer):
|
||||
"""Serialize Django Message into DRF Object"""
|
||||
|
||||
message = ReadOnlyField()
|
||||
level = ReadOnlyField()
|
||||
tags = ReadOnlyField()
|
||||
extra_tags = ReadOnlyField()
|
||||
level_tag = ReadOnlyField()
|
||||
|
||||
def create(self, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
def update(self, instance: Model, validated_data: dict) -> Model:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class MessagesViewSet(ViewSet):
|
||||
"""Read-only view set that returns the current session's messages"""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
@swagger_auto_schema(responses={200: MessageSerializer(many=True)})
|
||||
def list(self, request: Request) -> Response:
|
||||
"""List current messages and pass into Serializer"""
|
||||
all_messages = list(get_messages(request))
|
||||
return Response(MessageSerializer(all_messages, many=True).data)
|
@ -10,7 +10,6 @@ from authentik.admin.api.tasks import TaskViewSet
|
||||
from authentik.admin.api.version import VersionViewSet
|
||||
from authentik.admin.api.workers import WorkerViewSet
|
||||
from authentik.api.v2.config import ConfigsViewSet
|
||||
from authentik.api.v2.messages import MessagesViewSet
|
||||
from authentik.core.api.applications import ApplicationViewSet
|
||||
from authentik.core.api.groups import GroupViewSet
|
||||
from authentik.core.api.propertymappings import PropertyMappingViewSet
|
||||
@ -77,7 +76,6 @@ from authentik.stages.user_write.api import UserWriteStageViewSet
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
|
||||
router.register("root/messages", MessagesViewSet, basename="messages")
|
||||
router.register("root/config", ConfigsViewSet, basename="configs")
|
||||
|
||||
router.register("admin/version", VersionViewSet, basename="admin_version")
|
||||
|
@ -90,7 +90,7 @@ class UserManager(DjangoUserManager):
|
||||
|
||||
|
||||
class User(GuardianUserMixin, AbstractUser):
|
||||
"""Custom User model to allow easier adding o f user-based settings"""
|
||||
"""Custom User model to allow easier adding of user-based settings"""
|
||||
|
||||
uuid = models.UUIDField(default=uuid4, editable=False)
|
||||
name = models.TextField(help_text=_("User's display name."))
|
||||
|
@ -46,8 +46,7 @@ def backup_database(self: MonitoredTask): # pragma: no cover
|
||||
TaskResult(
|
||||
TaskResultStatus.SUCCESSFUL,
|
||||
[
|
||||
f"Successfully finished database backup {naturaltime(start)}",
|
||||
out.getvalue(),
|
||||
f"Successfully finished database backup {naturaltime(start)} {out.getvalue()}",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
@ -1,12 +0,0 @@
|
||||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block body %}
|
||||
<ak-message-container></ak-message-container>
|
||||
<div class="pf-c-page">
|
||||
<a class="pf-c-skip-to-content pf-c-button pf-m-primary" href="#main-content">{% trans 'Skip to content' %}</a>
|
||||
{% block page_content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,4 +1,4 @@
|
||||
{% extends 'base/page.html' %}
|
||||
{% extends 'base/skeleton.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
@ -13,7 +13,7 @@
|
||||
<p>{% trans "Configure settings relevant to your user profile." %}</p>
|
||||
</div>
|
||||
</section>
|
||||
<ak-tabs>
|
||||
<ak-tabs vertical="true">
|
||||
<section slot="page-1" data-tab-title="{% trans 'User details' %}" class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<div class="pf-u-display-flex pf-u-justify-content-center">
|
||||
<div class="pf-u-w-75">
|
||||
|
@ -7,7 +7,6 @@ from django.contrib.auth.mixins import (
|
||||
)
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http.response import HttpResponse
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.base import TemplateView
|
||||
@ -35,7 +34,7 @@ class UserDetailsView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
||||
form_class = UserDetailForm
|
||||
|
||||
success_message = _("Successfully updated user.")
|
||||
success_url = reverse_lazy("authentik_core:user-details")
|
||||
success_url = "/"
|
||||
|
||||
def get_object(self):
|
||||
return self.request.user
|
||||
@ -62,7 +61,7 @@ class TokenCreateView(
|
||||
permission_required = "authentik_core.add_token"
|
||||
|
||||
template_name = "generic/create.html"
|
||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
||||
success_url = "/"
|
||||
success_message = _("Successfully created Token")
|
||||
|
||||
def form_valid(self, form: UserTokenForm) -> HttpResponse:
|
||||
@ -80,7 +79,7 @@ class TokenUpdateView(
|
||||
form_class = UserTokenForm
|
||||
permission_required = "authentik_core.change_token"
|
||||
template_name = "generic/update.html"
|
||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
||||
success_url = "/"
|
||||
success_message = _("Successfully updated Token")
|
||||
|
||||
def get_object(self) -> Token:
|
||||
@ -100,7 +99,7 @@ class TokenDeleteView(LoginRequiredMixin, PermissionRequiredMixin, DeleteMessage
|
||||
model = Token
|
||||
permission_required = "authentik_core.delete_token"
|
||||
template_name = "generic/delete.html"
|
||||
success_url = reverse_lazy("authentik_core:user-tokens")
|
||||
success_url = "/"
|
||||
success_message = _("Successfully deleted Token")
|
||||
|
||||
def get_object(self) -> Token:
|
||||
|
@ -29,7 +29,7 @@ class EventSerializer(ModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class EventTopPerUserSerialier(Serializer):
|
||||
class EventTopPerUserSerializer(Serializer):
|
||||
"""Response object of Event's top_per_user"""
|
||||
|
||||
application = DictField()
|
||||
@ -60,7 +60,7 @@ class EventViewSet(ReadOnlyModelViewSet):
|
||||
filterset_fields = ["action"]
|
||||
|
||||
@swagger_auto_schema(
|
||||
method="GET", responses={200: EventTopPerUserSerialier(many=True)}
|
||||
method="GET", responses={200: EventTopPerUserSerializer(many=True)}
|
||||
)
|
||||
@action(detail=False, methods=["GET"])
|
||||
def top_per_user(self, request: Request):
|
||||
|
@ -2,8 +2,8 @@
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect, reverse
|
||||
from django.urls import NoReverseMatch
|
||||
from django.shortcuts import redirect
|
||||
from django.urls import NoReverseMatch, reverse
|
||||
from django.utils.http import urlencode
|
||||
from structlog.stdlib import get_logger
|
||||
|
||||
|
@ -5,6 +5,7 @@ from typing import Type
|
||||
from kubernetes.client import OpenApiException
|
||||
from kubernetes.client.api_client import ApiClient
|
||||
from structlog.testing import capture_logs
|
||||
from urllib3.exceptions import HTTPError
|
||||
from yaml import dump_all
|
||||
|
||||
from authentik.outposts.controllers.base import BaseController, ControllerException
|
||||
@ -42,7 +43,7 @@ class KubernetesController(BaseController):
|
||||
reconciler = self.reconcilers[reconcile_key](self)
|
||||
reconciler.up()
|
||||
|
||||
except OpenApiException as exc:
|
||||
except (OpenApiException, HTTPError) as exc:
|
||||
raise ControllerException from exc
|
||||
|
||||
def up_with_logs(self) -> list[str]:
|
||||
@ -54,7 +55,7 @@ class KubernetesController(BaseController):
|
||||
reconciler.up()
|
||||
all_logs += [f"{reconcile_key.title()}: {x['event']}" for x in logs]
|
||||
return all_logs
|
||||
except OpenApiException as exc:
|
||||
except (OpenApiException, HTTPError) as exc:
|
||||
raise ControllerException from exc
|
||||
|
||||
def down(self):
|
||||
|
@ -0,0 +1,87 @@
|
||||
# Generated by Django 3.1.7 on 2021-03-02 08:56
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_policies_event_matcher", "0010_auto_20210222_1821"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="eventmatcherpolicy",
|
||||
name="app",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("authentik.admin", "authentik Admin"),
|
||||
("authentik.api", "authentik API"),
|
||||
("authentik.events", "authentik Events"),
|
||||
("authentik.crypto", "authentik Crypto"),
|
||||
("authentik.flows", "authentik Flows"),
|
||||
("authentik.outposts", "authentik Outpost"),
|
||||
("authentik.lib", "authentik lib"),
|
||||
("authentik.policies", "authentik Policies"),
|
||||
("authentik.policies.dummy", "authentik Policies.Dummy"),
|
||||
(
|
||||
"authentik.policies.event_matcher",
|
||||
"authentik Policies.Event Matcher",
|
||||
),
|
||||
("authentik.policies.expiry", "authentik Policies.Expiry"),
|
||||
("authentik.policies.expression", "authentik Policies.Expression"),
|
||||
(
|
||||
"authentik.policies.group_membership",
|
||||
"authentik Policies.Group Membership",
|
||||
),
|
||||
("authentik.policies.hibp", "authentik Policies.HaveIBeenPwned"),
|
||||
("authentik.policies.password", "authentik Policies.Password"),
|
||||
("authentik.policies.reputation", "authentik Policies.Reputation"),
|
||||
("authentik.providers.proxy", "authentik Providers.Proxy"),
|
||||
("authentik.providers.oauth2", "authentik Providers.OAuth2"),
|
||||
("authentik.providers.saml", "authentik Providers.SAML"),
|
||||
("authentik.recovery", "authentik Recovery"),
|
||||
("authentik.sources.ldap", "authentik Sources.LDAP"),
|
||||
("authentik.sources.oauth", "authentik Sources.OAuth"),
|
||||
("authentik.sources.saml", "authentik Sources.SAML"),
|
||||
(
|
||||
"authentik.stages.authenticator_static",
|
||||
"authentik Stages.Authenticator.Static",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_totp",
|
||||
"authentik Stages.Authenticator.TOTP",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_validate",
|
||||
"authentik Stages.Authenticator.Validate",
|
||||
),
|
||||
(
|
||||
"authentik.stages.authenticator_webauthn",
|
||||
"authentik Stages.Authenticator.WebAuthn",
|
||||
),
|
||||
("authentik.stages.captcha", "authentik Stages.Captcha"),
|
||||
("authentik.stages.consent", "authentik Stages.Consent"),
|
||||
("authentik.stages.deny", "authentik Stages.Deny"),
|
||||
("authentik.stages.dummy", "authentik Stages.Dummy"),
|
||||
("authentik.stages.email", "authentik Stages.Email"),
|
||||
(
|
||||
"authentik.stages.identification",
|
||||
"authentik Stages.Identification",
|
||||
),
|
||||
("authentik.stages.invitation", "authentik Stages.User Invitation"),
|
||||
("authentik.stages.password", "authentik Stages.Password"),
|
||||
("authentik.stages.prompt", "authentik Stages.Prompt"),
|
||||
("authentik.stages.user_delete", "authentik Stages.User Delete"),
|
||||
("authentik.stages.user_login", "authentik Stages.User Login"),
|
||||
("authentik.stages.user_logout", "authentik Stages.User Logout"),
|
||||
("authentik.stages.user_write", "authentik Stages.User Write"),
|
||||
("authentik.managed", "authentik Managed"),
|
||||
("authentik.core", "authentik Core"),
|
||||
],
|
||||
default="",
|
||||
help_text="Match events created by selected application. When left empty, all applications are matched.",
|
||||
),
|
||||
),
|
||||
]
|
@ -62,11 +62,15 @@ class PolicyAccessView(AccessMixin, View):
|
||||
return self.handle_no_permission()
|
||||
try:
|
||||
self.resolve_provider_application()
|
||||
except (Application.DoesNotExist, Provider.DoesNotExist):
|
||||
return self.handle_no_permission_authenticated()
|
||||
except (Application.DoesNotExist, Provider.DoesNotExist) as exc:
|
||||
LOGGER.warning("failed to resolve application", exc=exc)
|
||||
return self.handle_no_permission_authenticated(
|
||||
PolicyResult(False, _("Failed to resolve application"))
|
||||
)
|
||||
# Check if user is unauthenticated, so we pass the application
|
||||
# for the identification stage
|
||||
if not request.user.is_authenticated:
|
||||
LOGGER.warning("user not authenticated")
|
||||
return self.handle_no_permission()
|
||||
# Check permissions
|
||||
result = self.user_has_access()
|
||||
|
@ -45,6 +45,7 @@ class OAuth2ProviderSetupURLs(Serializer):
|
||||
token = ReadOnlyField()
|
||||
user_info = ReadOnlyField()
|
||||
provider_info = ReadOnlyField()
|
||||
logout = ReadOnlyField()
|
||||
|
||||
def create(self, request: Request) -> Response:
|
||||
raise NotImplementedError
|
||||
@ -83,6 +84,7 @@ class OAuth2ProviderViewSet(ModelViewSet):
|
||||
)
|
||||
),
|
||||
"provider_info": None,
|
||||
"logout": None,
|
||||
}
|
||||
try:
|
||||
data["provider_info"] = request.build_absolute_uri(
|
||||
@ -91,6 +93,12 @@ class OAuth2ProviderViewSet(ModelViewSet):
|
||||
kwargs={"application_slug": provider.application.slug},
|
||||
)
|
||||
)
|
||||
data["logout"] = request.build_absolute_uri(
|
||||
reverse(
|
||||
"authentik_providers_oauth2:end-session",
|
||||
kwargs={"application_slug": provider.application.slug},
|
||||
)
|
||||
)
|
||||
except Provider.application.RelatedObjectDoesNotExist: # pylint: disable=no-member
|
||||
pass
|
||||
return Response(data)
|
||||
|
@ -139,6 +139,9 @@ GUARDIAN_MONKEY_PATCH = False
|
||||
|
||||
SWAGGER_SETTINGS = {
|
||||
"DEFAULT_INFO": "authentik.api.v2.urls.info",
|
||||
"DEFAULT_PAGINATOR_INSPECTORS": [
|
||||
"authentik.api.pagination_schema.PaginationInspector",
|
||||
],
|
||||
"SECURITY_DEFINITIONS": {
|
||||
"token": {"type": "apiKey", "name": "Authorization", "in": "header"}
|
||||
},
|
||||
@ -147,7 +150,6 @@ SWAGGER_SETTINGS = {
|
||||
REST_FRAMEWORK = {
|
||||
"DEFAULT_PAGINATION_CLASS": "authentik.api.pagination.Pagination",
|
||||
"PAGE_SIZE": 100,
|
||||
"DATETIME_FORMAT": "%s",
|
||||
"DEFAULT_FILTER_BACKENDS": [
|
||||
"rest_framework_guardian.filters.ObjectPermissionsFilter",
|
||||
"django_filters.rest_framework.DjangoFilterBackend",
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Sync LDAP Users into authentik"""
|
||||
from datetime import datetime
|
||||
|
||||
import ldap3
|
||||
import ldap3.core.exceptions
|
||||
from django.db.utils import IntegrityError
|
||||
from pytz import UTC
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.sources.ldap.sync.base import LDAP_UNIQUENESS, BaseLDAPSynchronizer
|
||||
@ -53,11 +56,21 @@ class UserLDAPSynchronizer(BaseLDAPSynchronizer):
|
||||
)
|
||||
)
|
||||
else:
|
||||
if created:
|
||||
ak_user.set_unusable_password()
|
||||
ak_user.save()
|
||||
self._logger.debug(
|
||||
"Synced User", user=ak_user.username, created=created
|
||||
)
|
||||
user_count += 1
|
||||
# pylint: disable=no-value-for-parameter
|
||||
pwd_last_set = UTC.localize(
|
||||
attributes.get("pwdLastSet", datetime.now())
|
||||
)
|
||||
if created or pwd_last_set >= ak_user.password_change_date:
|
||||
self._logger.debug(
|
||||
"Reset user's password",
|
||||
user=ak_user.username,
|
||||
created=created,
|
||||
pwd_last_set=pwd_last_set,
|
||||
)
|
||||
ak_user.set_unusable_password()
|
||||
ak_user.save()
|
||||
return user_count
|
||||
|
@ -1,9 +1,6 @@
|
||||
"""OTP Validate stage forms"""
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_otp import match_token
|
||||
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.models import NotConfiguredAction
|
||||
from authentik.stages.authenticator_validate.models import (
|
||||
AuthenticatorValidateStage,
|
||||
@ -11,35 +8,6 @@ from authentik.stages.authenticator_validate.models import (
|
||||
)
|
||||
|
||||
|
||||
class ValidationForm(forms.Form):
|
||||
"""OTP Validate stage forms"""
|
||||
|
||||
user: User
|
||||
|
||||
code = forms.CharField(
|
||||
label=_("Please enter the token from your device."),
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"autocomplete": "one-time-code",
|
||||
"placeholder": "123456",
|
||||
"autofocus": "autofocus",
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.user = user
|
||||
|
||||
def clean_code(self):
|
||||
"""Validate code against all confirmed devices"""
|
||||
code = self.cleaned_data.get("code")
|
||||
device = match_token(self.user, code)
|
||||
if not device:
|
||||
raise forms.ValidationError(_("Invalid Token"))
|
||||
return code
|
||||
|
||||
|
||||
class AuthenticatorValidateStageForm(forms.ModelForm):
|
||||
"""OTP Validate stage forms"""
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
"""Webauthn stage forms"""
|
||||
from django import forms
|
||||
|
||||
from authentik.stages.authenticator_webauthn.models import AuthenticateWebAuthnStage
|
||||
from authentik.stages.authenticator_webauthn.models import (
|
||||
AuthenticateWebAuthnStage,
|
||||
WebAuthnDevice,
|
||||
)
|
||||
|
||||
|
||||
class AuthenticateWebAuthnStageForm(forms.ModelForm):
|
||||
@ -15,3 +18,16 @@ class AuthenticateWebAuthnStageForm(forms.ModelForm):
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
}
|
||||
|
||||
|
||||
class DeviceEditForm(forms.ModelForm):
|
||||
"""Form to edit webauthn device"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = WebAuthnDevice
|
||||
fields = ["name"]
|
||||
|
||||
widgets = {
|
||||
"name": forms.TextInput(),
|
||||
}
|
||||
|
@ -79,3 +79,8 @@ class WebAuthnDevice(Device):
|
||||
|
||||
def __str__(self):
|
||||
return self.name or str(self.user)
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _("WebAuthn Device")
|
||||
verbose_name_plural = _("WebAuthn Devices")
|
||||
|
@ -17,6 +17,20 @@
|
||||
Created {{ created_on }}
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<div class="pf-c-data-list__cell">
|
||||
<ak-modal-button href="{% url 'authentik_stages_authenticator_webauthn:device-update' pk=device.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
{% trans 'Update' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="{% url 'authentik_stages_authenticator_webauthn:device-delete' pk=device.pk %}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
{% trans 'Delete' %}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
@ -1,10 +1,16 @@
|
||||
"""WebAuthn urls"""
|
||||
from django.urls import path
|
||||
|
||||
from authentik.stages.authenticator_webauthn.views import UserSettingsView
|
||||
from authentik.stages.authenticator_webauthn.views import (
|
||||
DeviceDeleteView,
|
||||
DeviceUpdateView,
|
||||
UserSettingsView,
|
||||
)
|
||||
|
||||
urlpatterns = [
|
||||
path(
|
||||
"<uuid:stage_uuid>/settings/", UserSettingsView.as_view(), name="user-settings"
|
||||
),
|
||||
path("devices/<int:pk>/delete/", DeviceDeleteView.as_view(), name="device-delete"),
|
||||
path("devices/<int:pk>/update/", DeviceUpdateView.as_view(), name="device-update"),
|
||||
]
|
||||
|
@ -1,8 +1,13 @@
|
||||
"""webauthn views"""
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.http.response import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.views.generic import TemplateView
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import TemplateView, UpdateView
|
||||
|
||||
from authentik.admin.views.utils import DeleteMessageView
|
||||
from authentik.stages.authenticator_webauthn.forms import DeviceEditForm
|
||||
from authentik.stages.authenticator_webauthn.models import (
|
||||
AuthenticateWebAuthnStage,
|
||||
WebAuthnDevice,
|
||||
@ -22,3 +27,34 @@ class UserSettingsView(LoginRequiredMixin, TemplateView):
|
||||
)
|
||||
kwargs["stage"] = stage
|
||||
return kwargs
|
||||
|
||||
|
||||
class DeviceUpdateView(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
|
||||
"""Update device"""
|
||||
|
||||
model = WebAuthnDevice
|
||||
form_class = DeviceEditForm
|
||||
template_name = "generic/update.html"
|
||||
success_url = "/"
|
||||
success_message = _("Successfully updated Device")
|
||||
|
||||
def get_object(self) -> WebAuthnDevice:
|
||||
device: WebAuthnDevice = super().get_object()
|
||||
if device.user != self.request.user:
|
||||
raise Http404
|
||||
return device
|
||||
|
||||
|
||||
class DeviceDeleteView(LoginRequiredMixin, DeleteMessageView):
|
||||
"""Delete device"""
|
||||
|
||||
model = WebAuthnDevice
|
||||
template_name = "generic/delete.html"
|
||||
success_url = "/"
|
||||
success_message = _("Successfully deleted Device")
|
||||
|
||||
def get_object(self) -> WebAuthnDevice:
|
||||
device: WebAuthnDevice = super().get_object()
|
||||
if device.user != self.request.user:
|
||||
raise Http404
|
||||
return device
|
||||
|
@ -1,9 +1,6 @@
|
||||
{% extends "base/page.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load authentik_utils %}
|
||||
|
||||
{% block body %}
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__header pf-c-title pf-m-md">
|
||||
{% trans 'Reset your password' %}
|
||||
@ -14,4 +11,3 @@
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -378,8 +378,8 @@ stages:
|
||||
python ./scripts/az_do_set_branch.py
|
||||
- task: Docker@2
|
||||
inputs:
|
||||
containerRegistry: 'GHCR'
|
||||
repository: 'beryju/authentik'
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/server'
|
||||
command: 'buildAndPush'
|
||||
Dockerfile: 'Dockerfile'
|
||||
tags: "gh-$(branchName)"
|
||||
|
@ -19,7 +19,7 @@ services:
|
||||
networks:
|
||||
- internal
|
||||
server:
|
||||
image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.1-rc1}
|
||||
image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.1}
|
||||
command: server
|
||||
environment:
|
||||
AUTHENTIK_REDIS__HOST: redis
|
||||
@ -45,7 +45,7 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
worker:
|
||||
image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.1-rc1}
|
||||
image: beryju/authentik:${AUTHENTIK_TAG:-2021.3.1}
|
||||
command: worker
|
||||
networks:
|
||||
- internal
|
||||
@ -62,7 +62,7 @@ services:
|
||||
env_file:
|
||||
- .env
|
||||
static:
|
||||
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.3.1-rc1}
|
||||
image: beryju/authentik-static:${AUTHENTIK_TAG:-2021.3.1}
|
||||
networks:
|
||||
- internal
|
||||
labels:
|
||||
|
@ -4,7 +4,7 @@ name: authentik
|
||||
home: https://goauthentik.io
|
||||
sources:
|
||||
- https://github.com/BeryJu/authentik
|
||||
version: "2021.3.1-rc1"
|
||||
version: "2021.3.1"
|
||||
icon: https://raw.githubusercontent.com/BeryJu/authentik/master/web/icons/icon.svg
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
|
@ -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.3.1-rc1 | Image tag |
|
||||
| image.tag | 2021.3.1 | 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 |
|
||||
|
@ -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.3.1-rc1
|
||||
tag: 2021.3.1
|
||||
pullPolicy: IfNotPresent
|
||||
|
||||
serverReplicas: 1
|
||||
|
@ -98,8 +98,8 @@ stages:
|
||||
python ./scripts/az_do_set_branch.py
|
||||
- task: Docker@2
|
||||
inputs:
|
||||
containerRegistry: 'GHCR'
|
||||
repository: 'beryju/authentik-proxy'
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/proxy'
|
||||
command: 'buildAndPush'
|
||||
Dockerfile: 'outpost/proxy.Dockerfile'
|
||||
buildContext: 'outpost/'
|
||||
|
@ -1,3 +1,3 @@
|
||||
package pkg
|
||||
|
||||
const VERSION = "2021.3.1-rc1"
|
||||
const VERSION = "2021.3.1"
|
||||
|
2620
swagger.yaml
2620
swagger.yaml
File diff suppressed because it is too large
Load Diff
@ -78,8 +78,8 @@ stages:
|
||||
python ./scripts/az_do_set_branch.py
|
||||
- task: Docker@2
|
||||
inputs:
|
||||
containerRegistry: 'GHCR'
|
||||
repository: 'beryju/authentik-static'
|
||||
containerRegistry: 'beryjuorg-harbor'
|
||||
repository: 'authentik/static'
|
||||
command: 'buildAndPush'
|
||||
Dockerfile: 'web/Dockerfile'
|
||||
tags: "gh-$(branchName)"
|
||||
|
@ -1,10 +1,14 @@
|
||||
import { gettext } from "django";
|
||||
import { showMessage } from "../elements/messages/MessageContainer";
|
||||
import { getCookie } from "../utils";
|
||||
import { NotFoundError, RequestError } from "./Error";
|
||||
|
||||
export const VERSION = "v2beta";
|
||||
|
||||
export interface QueryArguments {
|
||||
[key: string]: number | string | boolean | null;
|
||||
page?: number;
|
||||
page_size?: number;
|
||||
[key: string]: number | string | boolean | undefined | null;
|
||||
}
|
||||
|
||||
export interface BaseInheritanceModel {
|
||||
@ -45,6 +49,13 @@ export class Client {
|
||||
}
|
||||
return r;
|
||||
})
|
||||
.catch((e) => {
|
||||
showMessage({
|
||||
level_tag: "error",
|
||||
message: gettext(`Unexpected error while fetching: ${e.toString()}`),
|
||||
});
|
||||
return e;
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((r) => <T>r);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ export interface OAuth2SetupURLs {
|
||||
token: string;
|
||||
user_info: string;
|
||||
provider_info?: string;
|
||||
logout?: string;
|
||||
|
||||
}
|
||||
|
||||
|
@ -61,11 +61,6 @@ select[multiple] {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
/* Fix pre elements within alerts */
|
||||
.pf-c-alert pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.pf-c-content h1 {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
@ -85,6 +80,12 @@ select[multiple] {
|
||||
z-index: auto !important;
|
||||
}
|
||||
|
||||
/* ensure background on non-flow pages match */
|
||||
.pf-c-background-image::before {
|
||||
background-image: url("dist/assets/images/flow_background.jpg");
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
/* Fix spacing between messages */
|
||||
ak-message {
|
||||
display: block;
|
||||
|
@ -3,4 +3,5 @@ export const SUCCESS_CLASS = "pf-m-success";
|
||||
export const ERROR_CLASS = "pf-m-danger";
|
||||
export const PROGRESS_CLASS = "pf-m-in-progress";
|
||||
export const CURRENT_CLASS = "pf-m-current";
|
||||
export const VERSION = "2021.3.1-rc1";
|
||||
export const VERSION = "2021.3.1";
|
||||
export const PAGE_SIZE = 20;
|
||||
|
@ -1,16 +1,21 @@
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import Chart from "chart.js";
|
||||
import { showMessage } from "./messages/MessageContainer";
|
||||
import { DefaultClient } from "../api/Client";
|
||||
|
||||
interface TickValue {
|
||||
value: number;
|
||||
major: boolean;
|
||||
}
|
||||
|
||||
export interface LoginMetrics {
|
||||
logins_failed_per_1h: { x: number, y: number }[];
|
||||
logins_per_1h: { x: number, y: number }[];
|
||||
}
|
||||
|
||||
@customElement("ak-admin-logins-chart")
|
||||
export class AdminLoginsChart extends LitElement {
|
||||
@property()
|
||||
url = "";
|
||||
@property({type: Array})
|
||||
url: string[] = [];
|
||||
|
||||
chart?: Chart;
|
||||
|
||||
@ -40,15 +45,7 @@ export class AdminLoginsChart extends LitElement {
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
fetch(this.url)
|
||||
.then((r) => r.json())
|
||||
.catch((e) => {
|
||||
showMessage({
|
||||
level_tag: "error",
|
||||
message: "Unexpected error"
|
||||
});
|
||||
console.error(e);
|
||||
})
|
||||
DefaultClient.fetch<LoginMetrics>(this.url)
|
||||
.then((r) => {
|
||||
const canvas = <HTMLCanvasElement>this.shadowRoot?.querySelector("canvas");
|
||||
if (!canvas) {
|
||||
|
@ -12,10 +12,17 @@ export class Tabs extends LitElement {
|
||||
@property()
|
||||
currentPage?: string;
|
||||
|
||||
@property({type: Boolean})
|
||||
vertical = false;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [GlobalsStyle, TabsStyle, css`
|
||||
::slotted(*) {
|
||||
height: 100%;
|
||||
flex-grow: 2;
|
||||
}
|
||||
:host([vertical]) {
|
||||
display: flex;
|
||||
}
|
||||
`];
|
||||
}
|
||||
@ -39,7 +46,7 @@ export class Tabs extends LitElement {
|
||||
}
|
||||
this.currentPage = pages[0].attributes.getNamedItem("slot")?.value;
|
||||
}
|
||||
return html`<div class="pf-c-tabs">
|
||||
return html`<div class="pf-c-tabs ${this.vertical ? "pf-m-vertical pf-m-box" : ""}">
|
||||
<ul class="pf-c-tabs__list">
|
||||
${pages.map((page) => this.renderTab(page))}
|
||||
</ul>
|
||||
|
@ -59,9 +59,9 @@ export class SpinnerButton extends LitElement {
|
||||
return;
|
||||
}
|
||||
if (this.form) {
|
||||
// Because safari we can't just extend HTMLButtonElement, hence I have to implement
|
||||
// these attributes by myself here, sigh...
|
||||
document.querySelector<HTMLFormElement>(`#${this.form}`)?.submit();
|
||||
// Since the form= attribute is only used within a modal button,
|
||||
// we can assume the form is always two levels up
|
||||
this.parentElement?.parentElement?.querySelector < HTMLFormElement>(`#${this.form}`)?.dispatchEvent(new Event("submit"));
|
||||
}
|
||||
this.setLoading();
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { gettext } from "django";
|
||||
import { LitElement, html, customElement, TemplateResult, property } from "lit-element";
|
||||
import { DefaultClient } from "../../api/Client";
|
||||
import "./Message";
|
||||
import { APIMessage } from "./Message";
|
||||
|
||||
@ -15,7 +14,6 @@ export function showMessage(message: APIMessage): void {
|
||||
|
||||
@customElement("ak-message-container")
|
||||
export class MessageContainer extends LitElement {
|
||||
url = DefaultClient.makeUrl(["root", "messages"]);
|
||||
|
||||
@property({attribute: false})
|
||||
messages: APIMessage[] = [];
|
||||
@ -36,10 +34,6 @@ export class MessageContainer extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
this.fetchMessages();
|
||||
}
|
||||
|
||||
connect(): void {
|
||||
const wsUrl = `${window.location.protocol.replace("http", "ws")}//${
|
||||
window.location.host
|
||||
@ -74,21 +68,6 @@ export class MessageContainer extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
/* Fetch messages which were stored in the session.
|
||||
* This mostly gets messages which were created when the user arrives/leaves the site
|
||||
* and especially the login flow */
|
||||
fetchMessages(): Promise<void> {
|
||||
console.debug("authentik/messages: fetching messages over direct api");
|
||||
return fetch(this.url)
|
||||
.then((r) => r.json())
|
||||
.then((r: APIMessage[]) => {
|
||||
r.forEach((m: APIMessage) => {
|
||||
this.messages.push(m);
|
||||
this.requestUpdate();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<ul class="pf-c-alert-group pf-m-toast">
|
||||
${this.messages.map((m) => {
|
||||
|
@ -40,9 +40,7 @@ export class NotificationDrawer extends LitElement {
|
||||
}
|
||||
|
||||
renderItem(item: Notification): TemplateResult {
|
||||
const delta = Date.now() - (parseInt(item.created, 10) * 1000);
|
||||
// TODO: more flexible display, minutes and seconds
|
||||
const age = `${Math.round(delta / 1000 / 3600)} Hours ago`;
|
||||
const created = new Date(parseInt(item.created, 10) * 1000);
|
||||
let level = "";
|
||||
switch (item.severity) {
|
||||
case "notice":
|
||||
@ -76,7 +74,7 @@ export class NotificationDrawer extends LitElement {
|
||||
</button>
|
||||
</div>
|
||||
<p class="pf-c-notification-drawer__list-item-description">${item.body}</p>
|
||||
<small class="pf-c-notification-drawer__list-item-timestamp">${age}</small>
|
||||
<small class="pf-c-notification-drawer__list-item-timestamp">${created.toLocaleString()}</small>
|
||||
</li>`;
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { Policy } from "../../api/Policies";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-bound-policies-list")
|
||||
export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
@ -22,6 +23,7 @@ export class BoundPoliciesList extends Table<PolicyBinding> {
|
||||
target: this.target || "",
|
||||
ordering: "order",
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -119,13 +119,15 @@ export class AuthenticatorValidateStage extends BaseStage implements StageHost {
|
||||
return html`<ak-stage-authenticator-validate-code
|
||||
.host=${this}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}>
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}
|
||||
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
|
||||
</ak-stage-authenticator-validate-code>`;
|
||||
case DeviceClasses.WEBAUTHN:
|
||||
return html`<ak-stage-authenticator-validate-webauthn
|
||||
.host=${this}
|
||||
.challenge=${this.challenge}
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}>
|
||||
.deviceChallenge=${this.selectedDeviceChallenge}
|
||||
.showBackButton=${(this.challenge?.device_challenges.length || []) > 1}>
|
||||
</ak-stage-authenticator-validate-webauthn>`;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,9 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
|
||||
@property({ attribute: false })
|
||||
deviceChallenge?: DeviceChallenge;
|
||||
|
||||
@property({ type: Boolean })
|
||||
showBackButton = false;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
@ -61,14 +64,16 @@ export class AuthenticatorValidateStageWebCode extends BaseStage {
|
||||
</div>
|
||||
<footer class="pf-c-login__main-footer">
|
||||
<ul class="pf-c-login__main-footer-links">
|
||||
<li class="pf-c-login__main-footer-links-item">
|
||||
<button class="pf-c-button pf-m-secondary pf-m-block" @click=${() => {
|
||||
if (!this.host) return;
|
||||
(this.host as AuthenticatorValidateStage).selectedDeviceChallenge = undefined;
|
||||
}}>
|
||||
${gettext("Return to device picker")}
|
||||
</button>
|
||||
</li>
|
||||
${this.showBackButton ?
|
||||
html`<li class="pf-c-login__main-footer-links-item">
|
||||
<button class="pf-c-button pf-m-secondary pf-m-block" @click=${() => {
|
||||
if (!this.host) return;
|
||||
(this.host as AuthenticatorValidateStage).selectedDeviceChallenge = undefined;
|
||||
}}>
|
||||
${gettext("Return to device picker")}
|
||||
</button>
|
||||
</li>`:
|
||||
html``}
|
||||
</ul>
|
||||
</footer>`;
|
||||
}
|
||||
|
@ -21,6 +21,9 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
@property()
|
||||
authenticateMessage = "";
|
||||
|
||||
@property({type: Boolean})
|
||||
showBackButton = false;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
@ -98,14 +101,16 @@ export class AuthenticatorValidateStageWebAuthn extends BaseStage {
|
||||
</div>
|
||||
<footer class="pf-c-login__main-footer">
|
||||
<ul class="pf-c-login__main-footer-links">
|
||||
<li class="pf-c-login__main-footer-links-item">
|
||||
<button class="pf-c-button pf-m-secondary pf-m-block" @click=${() => {
|
||||
if (!this.host) return;
|
||||
(this.host as AuthenticatorValidateStage).selectedDeviceChallenge = undefined;
|
||||
}}>
|
||||
${gettext("Return to device picker")}
|
||||
</button>
|
||||
</li>
|
||||
${this.showBackButton ?
|
||||
html`<li class="pf-c-login__main-footer-links-item">
|
||||
<button class="pf-c-button pf-m-secondary pf-m-block" @click=${() => {
|
||||
if (!this.host) return;
|
||||
(this.host as AuthenticatorValidateStage).selectedDeviceChallenge = undefined;
|
||||
}}>
|
||||
${gettext("Return to device picker")}
|
||||
</button>
|
||||
</li>`:
|
||||
html``}
|
||||
</ul>
|
||||
</footer>`;
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { gettext } from "django";
|
||||
import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import { DefaultClient } from "../../api/Client";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
import "../../elements/AdminLoginsChart";
|
||||
@ -31,7 +30,7 @@ export class AdminOverviewPage extends LitElement {
|
||||
<section class="pf-c-page__main-section">
|
||||
<div class="pf-l-gallery pf-m-gutter">
|
||||
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Logins over the last 24 hours" style="grid-column-end: span 3;grid-row-end: span 2;">
|
||||
<ak-admin-logins-chart url="${DefaultClient.makeUrl(["admin", "metrics"])}"></ak-admin-logins-chart>
|
||||
<ak-admin-logins-chart .url="${["admin", "metrics"]}"></ak-admin-logins-chart>
|
||||
</ak-aggregate-card>
|
||||
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;">
|
||||
<ak-top-applications-table></ak-top-applications-table>
|
||||
|
@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-application-list")
|
||||
export class ApplicationListPage extends TablePage<Application> {
|
||||
@ -30,6 +31,7 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
return Application.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { gettext } from "django";
|
||||
import { css, CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { Application } from "../../api/Applications";
|
||||
import { DefaultClient } from "../../api/Client";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
import "../../elements/Tabs";
|
||||
@ -71,7 +70,7 @@ export class ApplicationViewPage extends LitElement {
|
||||
<div class="pf-c-card__body">
|
||||
${this.application ? html`
|
||||
<ak-admin-logins-chart
|
||||
url="${DefaultClient.makeUrl(["core", "applications", this.application?.slug, "metrics"])}">
|
||||
.url="${["core", "applications", this.application?.slug, "metrics"]}">
|
||||
</ak-admin-logins-chart>`: ""}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { CertificateKeyPair } from "../../api/CertificateKeyPair";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-crypto-certificatekeypair-list")
|
||||
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
@ -32,6 +33,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
return CertificateKeyPair.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { Event } from "../../api/Events";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
import { time } from "../../utils";
|
||||
@ -31,6 +32,7 @@ export class EventListPage extends TablePage<Event> {
|
||||
return Event.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE * 3,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Rule } from "../../api/EventRules";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-event-rule-list")
|
||||
export class RuleListPage extends TablePage<Rule> {
|
||||
@ -33,6 +34,7 @@ export class RuleListPage extends TablePage<Rule> {
|
||||
return Rule.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Transport } from "../../api/EventTransports";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-event-transport-list")
|
||||
export class TransportListPage extends TablePage<Transport> {
|
||||
@ -31,6 +32,7 @@ export class TransportListPage extends TablePage<Transport> {
|
||||
return Transport.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/policies/BoundPoliciesList";
|
||||
import { FlowStageBinding, Stage } from "../../api/Flows";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-bound-stages-list")
|
||||
export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
@ -24,6 +25,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
target: this.target || "",
|
||||
ordering: "order",
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import { TablePage } from "../../elements/table/TablePage";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-flow-list")
|
||||
export class FlowListPage extends TablePage<Flow> {
|
||||
@ -30,6 +31,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
return Flow.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Group } from "../../api/Groups";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-group-list")
|
||||
export class GroupListPage extends TablePage<Group> {
|
||||
@ -30,6 +31,7 @@ export class GroupListPage extends TablePage<Group> {
|
||||
return Group.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import "./OutpostHealth";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/TokenCopyButton";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-outpost-list")
|
||||
export class OutpostListPage extends TablePage<Outpost> {
|
||||
@ -29,6 +30,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
return Outpost.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-outpost-service-connection-list")
|
||||
export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> {
|
||||
@ -31,6 +32,7 @@ export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceCo
|
||||
return OutpostServiceConnection.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Policy } from "../../api/Policies";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-policy-list")
|
||||
export class PolicyListPage extends TablePage<Policy> {
|
||||
@ -31,7 +32,8 @@ export class PolicyListPage extends TablePage<Policy> {
|
||||
apiEndpoint(page: number): Promise<AKResponse<Policy>> {
|
||||
return Policy.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-property-mapping-list")
|
||||
export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
@ -35,6 +36,7 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
return PropertyMapping.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
managed__isnull: this.hideManaged,
|
||||
});
|
||||
|
@ -148,10 +148,16 @@ export class OAuth2ProviderViewPage extends Page {
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${gettext("Userinfo Endpoint")}</span>
|
||||
<span class="pf-c-form__label-text">${gettext("Userinfo URL")}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.user_info || "-"}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">${gettext("Logout URL")}</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${this.providerUrls?.logout || "-"}" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-provider-list")
|
||||
export class ProviderListPage extends TablePage<Provider> {
|
||||
@ -32,6 +33,7 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
return Provider.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-source-list")
|
||||
export class SourceListPage extends TablePage<Source> {
|
||||
@ -32,6 +33,7 @@ export class SourceListPage extends TablePage<Source> {
|
||||
return Source.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Invitation } from "../../api/Invitations";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-stage-invitation-list")
|
||||
export class InvitationListPage extends TablePage<Invitation> {
|
||||
@ -30,6 +31,7 @@ export class InvitationListPage extends TablePage<Invitation> {
|
||||
return Invitation.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Prompt } from "../../api/Prompts";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-stage-prompt-list")
|
||||
export class PromptListPage extends TablePage<Prompt> {
|
||||
@ -30,6 +31,7 @@ export class PromptListPage extends TablePage<Prompt> {
|
||||
return Prompt.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Stage } from "../../api/Flows";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-stage-list")
|
||||
export class StageListPage extends TablePage<Stage> {
|
||||
@ -32,6 +33,7 @@ export class StageListPage extends TablePage<Stage> {
|
||||
return Stage.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/TokenCopyButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Token } from "../../api/Tokens";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-token-list")
|
||||
export class TokenListPage extends TablePage<Token> {
|
||||
@ -31,6 +32,7 @@ export class TokenListPage extends TablePage<Token> {
|
||||
return Token.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/TokenCopyButton";
|
||||
import { Table, TableColumn } from "../../elements/table/Table";
|
||||
import { Token } from "../../api/Tokens";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-token-user-list")
|
||||
export class UserTokenList extends Table<Token> {
|
||||
@ -21,6 +22,7 @@ export class UserTokenList extends Table<Token> {
|
||||
return Token.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
@ -35,6 +37,18 @@ export class UserTokenList extends Table<Token> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href="-/user/tokens/create/">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Create")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
|
||||
row(item: Token): TemplateResult[] {
|
||||
return [
|
||||
html`${item.identifier}`,
|
||||
|
@ -8,6 +8,7 @@ import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/ActionButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { User } from "../../api/Users";
|
||||
import { PAGE_SIZE } from "../../constants";
|
||||
|
||||
@customElement("ak-user-list")
|
||||
export class UserListPage extends TablePage<User> {
|
||||
@ -31,6 +32,7 @@ export class UserListPage extends TablePage<User> {
|
||||
return User.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
page_size: PAGE_SIZE,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
@ -12,8 +12,8 @@ These functions/objects are available wherever expressions are used. For more sp
|
||||
|
||||
## Global objects
|
||||
|
||||
- `ak_logger`: structlog BoundLogger. ([ref](https://www.structlog.org/en/stable/api.html#structlog.BoundLogger))
|
||||
- `requests`: requests Session object. ([ref](https://requests.readthedocs.io/en/master/user/advanced/))
|
||||
- `ak_logger`: structlog BoundLogger. ([ref](https://www.structlog.org/en/stable/api.html#structlog.BoundLogger))
|
||||
- `requests`: requests Session object. ([ref](https://requests.readthedocs.io/en/master/user/advanced/))
|
||||
|
||||
## Generally available functions
|
||||
|
||||
|
@ -4,16 +4,16 @@ title: User Object
|
||||
|
||||
The User object has the following attributes:
|
||||
|
||||
- `username`: User's username.
|
||||
- `email` User's email.
|
||||
- `name` User's display name.
|
||||
- `is_staff` Boolean field if user is staff.
|
||||
- `is_active` Boolean field if user is active.
|
||||
- `date_joined` Date user joined/was created.
|
||||
- `password_change_date` Date password was last changed.
|
||||
- `attributes` Dynamic attributes.
|
||||
- `group_attributes` Merged attributes of all groups the user is member of and the user's own attributes.
|
||||
- `ak_groups` This is a queryset of all the user's groups.
|
||||
- `username`: User's username.
|
||||
- `email` User's email.
|
||||
- `name` User's display name.
|
||||
- `is_staff` Boolean field if user is staff.
|
||||
- `is_active` Boolean field if user is active.
|
||||
- `date_joined` Date user joined/was created.
|
||||
- `password_change_date` Date password was last changed.
|
||||
- `attributes` Dynamic attributes.
|
||||
- `group_attributes` Merged attributes of all groups the user is member of and the user's own attributes.
|
||||
- `ak_groups` This is a queryset of all the user's groups.
|
||||
|
||||
You can do additional filtering like `user.ak_groups.filter(name__startswith='test')`, see [here](https://docs.djangoproject.com/en/3.1/ref/models/querysets/#id4)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Static Authenticator stage
|
||||
title: Static Authentication Setup stage
|
||||
---
|
||||
|
||||
This stage configures static OTP Tokens, which can be used as a backup method to time-based OTP tokens.
|
||||
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: TOTP stage
|
||||
title: TOTP Authentication Setup stage
|
||||
---
|
||||
|
||||
This stage configures a time-based OTP Device, such as Google Authenticator or Authy.
|
||||
|
||||
You can configure how many digest should be used for the OTP Token.
|
||||
You can configure how many digits should be used for the OTP Token.
|
||||
|
@ -2,7 +2,16 @@
|
||||
title: Authenticator Validation Stage
|
||||
---
|
||||
|
||||
This stage validates an already configured OTP Device. This device has to be configured using any of the other authenticator stages:
|
||||
This stage validates an already configured Authenticator Device. This device has to be configured using any of the other authenticator stages:
|
||||
|
||||
- [TOTP authenticator stage](../authenticator_totp/index.md)
|
||||
- [Static authenticator stage](../authenticator_static/index.md).
|
||||
- [WebAuth authenticator stage](../authenticator_webauthn/index.md).
|
||||
|
||||
You can select which type of device classes are allowed.
|
||||
|
||||
Using the `Not configured action`, you can choose what happens when a user does not have any matching devices.
|
||||
|
||||
- Skip: Validation is skipped and the flow continues
|
||||
- Deny: Access is denied, the flow execution ends
|
||||
- Configure: This option requires a *Configuration stage* to be set. The validation stage will be marked as successful, and the configuration stage will be injected into the flow.
|
||||
|
7
website/docs/flow/stages/authenticator_webauthn/index.md
Normal file
7
website/docs/flow/stages/authenticator_webauthn/index.md
Normal file
@ -0,0 +1,7 @@
|
||||
---
|
||||
title: WebAuthn Authentication Setup stage
|
||||
---
|
||||
|
||||
This stage configures a WebAuthn-based Authenticator. This can either be a browser, biometrics or a Security stick like a YubiKey.
|
||||
|
||||
There are no stage-specific settings.
|
Binary file not shown.
Before Width: | Height: | Size: 140 KiB After Width: | Height: | Size: 115 KiB |
10
website/docs/flow/stages/deny.md
Normal file
10
website/docs/flow/stages/deny.md
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Deny stage
|
||||
---
|
||||
|
||||
This stage stops the execution of a flow. This can be used to conditionally deny users access to a flow,
|
||||
even if they are not signed in (and permissions can't be checked via groups).
|
||||
|
||||
:::caution
|
||||
To effectively use this stage, make sure to **disable** *Evaluate on plan* on the Stage binding.
|
||||
:::
|
@ -11,8 +11,8 @@ This stage provides a ready-to-go form for users to identify themselves.
|
||||
Select which fields the user can use to identify themselves. Multiple fields can be specified and separated with a comma.
|
||||
Valid choices:
|
||||
|
||||
- email
|
||||
- username
|
||||
- email
|
||||
- username
|
||||
|
||||
### Template
|
||||
|
||||
|
@ -13,5 +13,7 @@ See [Docker-compose](installation/docker-compose) or [Kubernetes](installation/k
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||
Light | Dark
|
||||
--- | ---
|
||||
 | 
|
||||
 | 
|
||||
|
@ -16,7 +16,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.3.1-rc1 >> .env`
|
||||
To optionally deploy a different version run `echo AUTHENTIK_TAG=2021.3.1 >> .env`
|
||||
|
||||
If this is a fresh authentik install run the following commands to generate a password:
|
||||
|
||||
|
@ -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.3.1-rc1
|
||||
tag: 2021.3.1
|
||||
|
||||
serverReplicas: 1
|
||||
workerReplicas: 1
|
||||
|
@ -0,0 +1,62 @@
|
||||
---
|
||||
title: Apache Guacamole™
|
||||
---
|
||||
|
||||
## What is Apache Guacamole™
|
||||
|
||||
From https://guacamole.apache.org/
|
||||
|
||||
:::note
|
||||
Apache Guacamole is a clientless remote desktop gateway. It supports standard protocols like VNC, RDP, and SSH.
|
||||
:::
|
||||
|
||||
## Preparation
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `guacamole.company` is the FQDN of the Guacamole install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an OAuth2/OpenID provider with the following parameters:
|
||||
|
||||
- Client Type: `Confidential`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Redirect URIs: `https://guacamole.company/` (depending on your Tomcat setup, you might have to add `/guacamole/` if the application runs in a subfolder)
|
||||
- Scopes: OpenID, Email and Profile
|
||||
|
||||
Note the Client ID value. Create an application, using the provider you've created above.
|
||||
|
||||
## Guacamole
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<Tabs
|
||||
defaultValue="docker"
|
||||
values={[
|
||||
{label: 'Docker', value: 'docker'},
|
||||
{label: 'Standalone', value: 'standalone'},
|
||||
]}>
|
||||
<TabItem value="docker">
|
||||
The docker containers are configured via environment variables. The following variables are required:
|
||||
|
||||
```yaml
|
||||
OPENID_AUTHORIZATION_ENDPOINT: https://authentik.company/application/o/authorize/
|
||||
OPENID_CLIENT_ID: # client ID from above
|
||||
OPENID_ISSUER: https://authentik.company/application/o/apache-guacamole/
|
||||
OPENID_JWKS_ENDPOINT: https://authentik.company/application/o/apache-guacamole/jwks/
|
||||
OPENID_REDIRECT_URI: https://guacamole.company/ # This must match the redirect URI above
|
||||
```
|
||||
</TabItem>
|
||||
<TabItem value="standalone">
|
||||
Standalone Guacamole is configured using the `guacamole.properties` file. Add the following settings:
|
||||
|
||||
```
|
||||
openid-authorization-endpoint=https://authentik.company/application/o/authorize/
|
||||
openid-client-id=# client ID from above
|
||||
openid-issuer=https://authentik.company/application/o/apache-guacamole/
|
||||
openid-jwks-endpoint=https://authentik.company/application/o/apache-guacamole/jwks/
|
||||
openid-redirect-uri=https://guacamole.company/ # This must match the redirect URI above
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
@ -12,14 +12,14 @@ Amazon Web Services (AWS) is the world’s most comprehensive and broadly adopte
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an application in authentik and note the slug, as this will be used later. Create a SAML provider with the following parameters:
|
||||
|
||||
- ACS URL: `https://signin.aws.amazon.com/saml`
|
||||
- Audience: `urn:amazon:webservices`
|
||||
- Issuer: `authentik`
|
||||
- Binding: `Post`
|
||||
- ACS URL: `https://signin.aws.amazon.com/saml`
|
||||
- Audience: `urn:amazon:webservices`
|
||||
- Issuer: `authentik`
|
||||
- Binding: `Post`
|
||||
|
||||
You can of course use a custom signing certificate, and adjust durations.
|
||||
|
||||
|
@ -20,15 +20,15 @@ AWX is the open-source version of Tower. The term "AWX" will be used interchange
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `awx.company` is the FQDN of the AWX/Tower install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `awx.company` is the FQDN of the AWX/Tower install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an application in authentik and note the slug, as this will be used later. Create a SAML provider with the following parameters:
|
||||
|
||||
- ACS URL: `https://awx.company/sso/complete/saml/`
|
||||
- Audience: `awx`
|
||||
- Service Provider Binding: Post
|
||||
- Issuer: `https://awx.company/sso/metadata/saml/`
|
||||
- ACS URL: `https://awx.company/sso/complete/saml/`
|
||||
- Audience: `awx`
|
||||
- Service Provider Binding: Post
|
||||
- Issuer: `https://awx.company/sso/metadata/saml/`
|
||||
|
||||
You can of course use a custom signing certificate, and adjust durations.
|
||||
|
||||
|
@ -14,15 +14,15 @@ GitLab is a complete DevOps platform, delivered as a single application. This ma
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `gitlab.company` is the FQDN of the GitLab Install
|
||||
- `authentik.company` is the FQDN of the authentik Install
|
||||
- `gitlab.company` is the FQDN of the GitLab Install
|
||||
- `authentik.company` is the FQDN of the authentik Install
|
||||
|
||||
Create an application in authentik and note the slug, as this will be used later. Create a SAML provider with the following parameters:
|
||||
|
||||
- ACS URL: `https://gitlab.company/users/auth/saml/callback`
|
||||
- Audience: `https://gitlab.company`
|
||||
- Issuer: `https://gitlab.company`
|
||||
- Binding: `Post`
|
||||
- ACS URL: `https://gitlab.company/users/auth/saml/callback`
|
||||
- Audience: `https://gitlab.company`
|
||||
- Issuer: `https://gitlab.company`
|
||||
- Binding: `Post`
|
||||
|
||||
You can of course use a custom signing certificate, and adjust durations. To get the value for `idp_cert_fingerprint`, you can use a tool like [this](https://www.samltool.com/fingerprint.php).
|
||||
|
||||
|
@ -14,21 +14,31 @@ Grafana is a multi-platform open source analytics and interactive visualization
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `grafana.company` is the FQDN of the Grafana install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `grafana.company` is the FQDN of the Grafana install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an application in authentik. Create an OAuth2/OpenID provider with the following parameters:
|
||||
|
||||
- Client Type: `Confidential`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Scopes: OpenID, Email and Profile
|
||||
- RSA Key: Select any available key
|
||||
- Redirect URIs: `https://grafana.company/login/generic_oauth`
|
||||
- Client Type: `Confidential`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Scopes: OpenID, Email and Profile
|
||||
- RSA Key: Select any available key
|
||||
- Redirect URIs: `https://grafana.company/login/generic_oauth`
|
||||
|
||||
Note the Client ID and Client Secret values. Create an application, using the provider you've created above. Note the slug of the application you've created.
|
||||
|
||||
## Grafana
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<Tabs
|
||||
defaultValue="docker"
|
||||
values={[
|
||||
{label: 'Docker', value: 'docker'},
|
||||
{label: 'Standalone', value: 'standalone'},
|
||||
]}>
|
||||
<TabItem value="docker">
|
||||
If your Grafana is running in docker, set the following environment variables:
|
||||
|
||||
```yaml
|
||||
@ -45,7 +55,8 @@ environment:
|
||||
# Optionally enable auto-login
|
||||
GF_AUTH_OAUTH_AUTO_LOGIN: "true"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="standalone">
|
||||
If you are using a config-file instead, you have to set these options:
|
||||
|
||||
```ini
|
||||
@ -64,3 +75,5 @@ auth_url = https://authentik.company/application/o/authorize/
|
||||
token_url = https://authentik.company/application/o/token/
|
||||
api_url = https://authentik.company/application/o/userinfo/
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
Binary file not shown.
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 118 KiB |
@ -14,15 +14,15 @@ Harbor is an open source container image registry that secures images with role-
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `harbor.company` is the FQDN of the Harbor install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `harbor.company` is the FQDN of the Harbor install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
Create an OAuth2/OpenID provider with the following parameters:
|
||||
|
||||
- Client Type: `Confidential`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Redirect URIs: `https://harbor.company/c/oidc/callback`
|
||||
- Scopes: OpenID, Email and Profile
|
||||
- Client Type: `Confidential`
|
||||
- JWT Algorithm: `RS256`
|
||||
- Redirect URIs: `https://harbor.company/c/oidc/callback`
|
||||
- Scopes: OpenID, Email and Profile
|
||||
|
||||
Note the Client ID and Client Secret values. Create an application, using the provider you've created above.
|
||||
|
||||
|
@ -14,8 +14,8 @@ Open source home automation that puts local control and privacy first. Powered b
|
||||
|
||||
The following placeholders will be used:
|
||||
|
||||
- `hass.company` is the FQDN of the Home-Assistant install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
- `hass.company` is the FQDN of the Home-Assistant install.
|
||||
- `authentik.company` is the FQDN of the authentik install.
|
||||
|
||||
:::note
|
||||
This setup uses https://github.com/BeryJu/hass-auth-header and the authentik proxy for authentication. When this [PR](https://github.com/home-assistant/core/pull/32926) is merged, this will no longer be necessary.
|
||||
@ -42,13 +42,13 @@ additionalHeaders:
|
||||
|
||||
Create a Proxy Provider with the following values
|
||||
|
||||
- Internal host
|
||||
- Internal host
|
||||
|
||||
If Home-Assistant is running in docker, and you're deploying the authentik proxy on the same host, set the value to `http://homeassistant:8123`, where Home-Assistant is the name of your container.
|
||||
|
||||
If Home-Assistant is running on a different server than where you are deploying the authentik proxy, set the value to `http://hass.company:8123`.
|
||||
|
||||
- External host
|
||||
- External host
|
||||
|
||||
Set this to the external URL you will be accessing Home-Assistant from.
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user