Compare commits
19 Commits
version/20
...
version/20
Author | SHA1 | Date | |
---|---|---|---|
63cfbb721c | |||
2b74a1f03b | |||
093573f89a | |||
d842fc4958 | |||
19f5e6e07e | |||
acfa9c76d1 | |||
bff34cc5dc | |||
7f009f6d02 | |||
dfb9ae548c | |||
7d6b573f8b | |||
ade397fc24 | |||
d945d30cda | |||
c8c401e2c5 | |||
e4ca20bfc6 | |||
6347716815 | |||
859b6cc60e | |||
06a1a7f076 | |||
b6c120f555 | |||
6cc363bc5d |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 2023.2.1
|
||||
current_version = 2023.2.3
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)
|
||||
|
3
.github/workflows/ci-main.yml
vendored
3
.github/workflows/ci-main.yml
vendored
@ -80,6 +80,7 @@ jobs:
|
||||
run: poetry run python -m lifecycle.migrate
|
||||
test-unittest:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup authentik env
|
||||
@ -94,6 +95,7 @@ jobs:
|
||||
flags: unit
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup authentik env
|
||||
@ -111,6 +113,7 @@ jobs:
|
||||
test-e2e:
|
||||
name: test-e2e (${{ matrix.job.name }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
@ -31,7 +31,7 @@ RUN pip install --no-cache-dir poetry && \
|
||||
poetry export -f requirements.txt --dev --output requirements-dev.txt
|
||||
|
||||
# Stage 4: Build go proxy
|
||||
FROM docker.io/golang:1.20.0-bullseye AS go-builder
|
||||
FROM docker.io/golang:1.20.1-bullseye AS go-builder
|
||||
|
||||
WORKDIR /work
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
from os import environ
|
||||
from typing import Optional
|
||||
|
||||
__version__ = "2023.2.1"
|
||||
__version__ = "2023.2.3"
|
||||
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@ from rest_framework.serializers import CharField
|
||||
|
||||
from authentik.core.api.utils import PassiveSerializer
|
||||
from authentik.core.models import User
|
||||
from authentik.flows.models import Flow, FlowStageBinding
|
||||
from authentik.flows.models import Flow, FlowAuthenticationRequirement, FlowStageBinding
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -160,12 +160,37 @@ class FlowDiagram:
|
||||
)
|
||||
return stages + elements
|
||||
|
||||
def get_flow_auth_requirement(self) -> list[DiagramElement]:
|
||||
"""Get flow authentication requirement"""
|
||||
end_el = DiagramElement(
|
||||
"done",
|
||||
_("End of the flow"),
|
||||
_("Requirement not fulfilled"),
|
||||
style=["[[", "]]"],
|
||||
)
|
||||
elements = []
|
||||
if self.flow.authentication == FlowAuthenticationRequirement.NONE:
|
||||
return []
|
||||
auth = DiagramElement(
|
||||
"flow_auth_requirement",
|
||||
_("Flow authentication requirement") + "\n" + self.flow.authentication,
|
||||
)
|
||||
elements.append(auth)
|
||||
end_el.source = [auth]
|
||||
elements.append(end_el)
|
||||
elements.append(
|
||||
DiagramElement("flow_start", "placeholder", _("Requirement fulfilled"), source=[auth])
|
||||
)
|
||||
return elements
|
||||
|
||||
def build(self) -> str:
|
||||
"""Build flowchart"""
|
||||
all_elements = [
|
||||
"graph TD",
|
||||
]
|
||||
|
||||
all_elements.extend(self.get_flow_auth_requirement())
|
||||
|
||||
pre_flow_policies_element = DiagramElement(
|
||||
"flow_pre", _("Pre-flow policies"), style=["[[", "]]"]
|
||||
)
|
||||
@ -179,6 +204,7 @@ class FlowDiagram:
|
||||
_("End of the flow"),
|
||||
_("Policy denied"),
|
||||
flow_policies,
|
||||
style=["[[", "]]"],
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -162,7 +162,7 @@ class FlowExecutorView(APIView):
|
||||
token.delete()
|
||||
if not isinstance(plan, FlowPlan):
|
||||
return None
|
||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = True
|
||||
plan.context[PLAN_CONTEXT_IS_RESTORED] = token
|
||||
self._logger.debug("f(exec): restored flow plan from token", plan=plan)
|
||||
return plan
|
||||
|
||||
|
@ -86,7 +86,7 @@ def generate_colors(text: str) -> tuple[str, str]:
|
||||
@cache
|
||||
# pylint: disable=too-many-arguments,too-many-locals
|
||||
def generate_avatar_from_name(
|
||||
user: "User",
|
||||
name: str,
|
||||
length: int = 2,
|
||||
size: int = 64,
|
||||
rounded: bool = False,
|
||||
@ -98,8 +98,6 @@ def generate_avatar_from_name(
|
||||
|
||||
Inspired from: https://github.com/LasseRafn/ui-avatars
|
||||
"""
|
||||
name = user.name if user.name != "" else "a k"
|
||||
|
||||
name_parts = name.split()
|
||||
# Only abbreviate first and last name
|
||||
if len(name_parts) > 2:
|
||||
@ -152,7 +150,7 @@ def generate_avatar_from_name(
|
||||
|
||||
def avatar_mode_generated(user: "User", mode: str) -> Optional[str]:
|
||||
"""Wrapper that converts generated avatar to base64 svg"""
|
||||
svg = generate_avatar_from_name(user)
|
||||
svg = generate_avatar_from_name(user.name if user.name != "" else "a k")
|
||||
return f"data:image/svg+xml;base64,{b64encode(svg.encode('utf-8')).decode('utf-8')}"
|
||||
|
||||
|
||||
|
@ -7,7 +7,6 @@ from urllib.parse import urlparse
|
||||
|
||||
import yaml
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
from django.core.cache import cache
|
||||
from django.db import DatabaseError, InternalError, ProgrammingError
|
||||
from django.db.models.base import Model
|
||||
@ -43,6 +42,7 @@ from authentik.providers.ldap.controllers.kubernetes import LDAPKubernetesContro
|
||||
from authentik.providers.proxy.controllers.docker import ProxyDockerController
|
||||
from authentik.providers.proxy.controllers.kubernetes import ProxyKubernetesController
|
||||
from authentik.root.celery import CELERY_APP
|
||||
from authentik.root.messages.storage import closing_send
|
||||
|
||||
LOGGER = get_logger()
|
||||
CACHE_KEY_OUTPOST_DOWN = "outpost_teardown_%s"
|
||||
@ -217,26 +217,23 @@ def outpost_post_save(model_class: str, model_pk: Any):
|
||||
def outpost_send_update(model_instace: Model):
|
||||
"""Send outpost update to all registered outposts, regardless to which authentik
|
||||
instance they are connected"""
|
||||
channel_layer = get_channel_layer()
|
||||
if isinstance(model_instace, OutpostModel):
|
||||
for outpost in model_instace.outpost_set.all():
|
||||
_outpost_single_update(outpost, channel_layer)
|
||||
_outpost_single_update(outpost)
|
||||
elif isinstance(model_instace, Outpost):
|
||||
_outpost_single_update(model_instace, channel_layer)
|
||||
_outpost_single_update(model_instace)
|
||||
|
||||
|
||||
def _outpost_single_update(outpost: Outpost, layer=None):
|
||||
def _outpost_single_update(outpost: Outpost):
|
||||
"""Update outpost instances connected to a single outpost"""
|
||||
# Ensure token again, because this function is called when anything related to an
|
||||
# OutpostModel is saved, so we can be sure permissions are right
|
||||
_ = outpost.token
|
||||
outpost.build_user_permissions(outpost.user)
|
||||
if not layer: # pragma: no cover
|
||||
layer = get_channel_layer()
|
||||
for state in OutpostState.for_outpost(outpost):
|
||||
for channel in state.channel_ids:
|
||||
LOGGER.debug("sending update", channel=channel, instance=state.uid, outpost=outpost)
|
||||
async_to_sync(layer.send)(channel, {"type": "event.update"})
|
||||
async_to_sync(closing_send)(channel, {"type": "event.update"})
|
||||
|
||||
|
||||
@CELERY_APP.task()
|
||||
|
@ -1,6 +1,7 @@
|
||||
"""Channels Messages storage"""
|
||||
from asgiref.sync import async_to_sync
|
||||
from channels.layers import get_channel_layer
|
||||
from channels import DEFAULT_CHANNEL_LAYER
|
||||
from channels.layers import channel_layers
|
||||
from django.contrib.messages.storage.base import Message
|
||||
from django.contrib.messages.storage.session import SessionStorage
|
||||
from django.core.cache import cache
|
||||
@ -10,13 +11,21 @@ SESSION_KEY = "_messages"
|
||||
CACHE_PREFIX = "goauthentik.io/root/messages_"
|
||||
|
||||
|
||||
async def closing_send(channel, message):
|
||||
"""Wrapper around layer send that closes the connection"""
|
||||
# See https://github.com/django/channels_redis/issues/332
|
||||
# TODO: Remove this after channels_redis 4.1 is released
|
||||
channel_layer = channel_layers.make_backend(DEFAULT_CHANNEL_LAYER)
|
||||
await channel_layer.send(channel, message)
|
||||
await channel_layer.close_pools()
|
||||
|
||||
|
||||
class ChannelsStorage(SessionStorage):
|
||||
"""Send contrib.messages over websocket"""
|
||||
|
||||
def __init__(self, request: HttpRequest) -> None:
|
||||
# pyright: reportGeneralTypeIssues=false
|
||||
super().__init__(request)
|
||||
self.channel = get_channel_layer()
|
||||
|
||||
def _store(self, messages: list[Message], response, *args, **kwargs):
|
||||
prefix = f"{CACHE_PREFIX}{self.request.session.session_key}_messages_"
|
||||
@ -28,7 +37,7 @@ class ChannelsStorage(SessionStorage):
|
||||
for key in keys:
|
||||
uid = key.replace(prefix, "")
|
||||
for message in messages:
|
||||
async_to_sync(self.channel.send)(
|
||||
async_to_sync(closing_send)(
|
||||
uid,
|
||||
{
|
||||
"type": "event.update",
|
||||
|
@ -15,7 +15,7 @@ from authentik.flows.challenge import Challenge, ChallengeResponse, ChallengeTyp
|
||||
from authentik.flows.models import FlowToken
|
||||
from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDING_USER
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN, SESSION_KEY_GET
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -103,12 +103,14 @@ class EmailStageView(ChallengeStageView):
|
||||
|
||||
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
# Check if the user came back from the email link to verify
|
||||
if QS_KEY_TOKEN in request.session.get(
|
||||
SESSION_KEY_GET, {}
|
||||
) and self.executor.plan.context.get(PLAN_CONTEXT_IS_RESTORED, False):
|
||||
restore_token: FlowToken = self.executor.plan.context.get(PLAN_CONTEXT_IS_RESTORED, None)
|
||||
user = self.get_pending_user()
|
||||
if restore_token:
|
||||
if restore_token.user != user:
|
||||
self.logger.warning("Flow token for non-matching user, denying request")
|
||||
return self.executor.stage_invalid()
|
||||
messages.success(request, _("Successfully verified Email."))
|
||||
if self.executor.current_stage.activate_user_on_success:
|
||||
user = self.get_pending_user()
|
||||
user.is_active = True
|
||||
user.save()
|
||||
return self.executor.stage_ok()
|
||||
|
@ -7,10 +7,9 @@ from django.core.mail.backends.smtp import EmailBackend as SMTPEmailBackend
|
||||
from django.urls import reverse
|
||||
from django.utils.http import urlencode
|
||||
|
||||
from authentik.core.models import Token
|
||||
from authentik.core.tests.utils import create_test_admin_user, create_test_flow
|
||||
from authentik.flows.markers import StageMarker
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding
|
||||
from authentik.flows.models import FlowDesignation, FlowStageBinding, FlowToken
|
||||
from authentik.flows.planner import PLAN_CONTEXT_PENDING_USER, FlowPlan
|
||||
from authentik.flows.tests import FlowTestCase
|
||||
from authentik.flows.views.executor import SESSION_KEY_PLAN
|
||||
@ -134,7 +133,7 @@ class TestEmailStage(FlowTestCase):
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
token: Token = Token.objects.get(user=self.user)
|
||||
token: FlowToken = FlowToken.objects.get(user=self.user)
|
||||
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
# Call the executor shell to preseed the session
|
||||
@ -165,3 +164,43 @@ class TestEmailStage(FlowTestCase):
|
||||
plan: FlowPlan = session[SESSION_KEY_PLAN]
|
||||
self.assertEqual(plan.context[PLAN_CONTEXT_PENDING_USER], self.user)
|
||||
self.assertTrue(plan.context[PLAN_CONTEXT_PENDING_USER].is_active)
|
||||
|
||||
def test_token_invalid_user(self):
|
||||
"""Test with token with invalid user"""
|
||||
# Make sure token exists
|
||||
self.test_pending_user()
|
||||
self.user.is_active = False
|
||||
self.user.save()
|
||||
plan = FlowPlan(flow_pk=self.flow.pk.hex, bindings=[self.binding], markers=[StageMarker()])
|
||||
session = self.client.session
|
||||
session[SESSION_KEY_PLAN] = plan
|
||||
session.save()
|
||||
# Set flow token user to a different user
|
||||
token: FlowToken = FlowToken.objects.get(user=self.user)
|
||||
token.user = create_test_admin_user()
|
||||
token.save()
|
||||
|
||||
with patch("authentik.flows.views.executor.FlowExecutorView.cancel", MagicMock()):
|
||||
# Call the executor shell to preseed the session
|
||||
url = reverse(
|
||||
"authentik_api:flow-executor",
|
||||
kwargs={"flow_slug": self.flow.slug},
|
||||
)
|
||||
url_query = urlencode(
|
||||
{
|
||||
QS_KEY_TOKEN: token.key,
|
||||
}
|
||||
)
|
||||
url += f"?query={url_query}"
|
||||
self.client.get(url)
|
||||
|
||||
# Call the actual executor to get the JSON Response
|
||||
response = self.client.get(
|
||||
reverse(
|
||||
"authentik_api:flow-executor",
|
||||
kwargs={"flow_slug": self.flow.slug},
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertStageResponse(response, component="ak-stage-access-denied")
|
||||
|
@ -154,6 +154,7 @@ entries:
|
||||
policy: !KeyOf default-recovery-skip-if-restored
|
||||
target: !KeyOf flow-binding-email
|
||||
order: 0
|
||||
state: absent
|
||||
model: authentik_policies.policybinding
|
||||
attrs:
|
||||
negate: false
|
||||
|
@ -32,7 +32,7 @@ services:
|
||||
volumes:
|
||||
- redis:/data
|
||||
server:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.2.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.2.3}
|
||||
restart: unless-stopped
|
||||
command: server
|
||||
environment:
|
||||
@ -50,7 +50,7 @@ services:
|
||||
- "${AUTHENTIK_PORT_HTTP:-9000}:9000"
|
||||
- "${AUTHENTIK_PORT_HTTPS:-9443}:9443"
|
||||
worker:
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.2.1}
|
||||
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:${AUTHENTIK_TAG:-2023.2.3}
|
||||
restart: unless-stopped
|
||||
command: worker
|
||||
environment:
|
||||
|
2
go.mod
2
go.mod
@ -25,7 +25,7 @@ require (
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.8.1
|
||||
goauthentik.io/api/v3 v3.2023012.5
|
||||
goauthentik.io/api/v3 v3.2023021.1
|
||||
golang.org/x/exp v0.0.0-20230210204819-062eb4c674ab
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
|
||||
|
9
go.sum
9
go.sum
@ -380,10 +380,8 @@ go.opentelemetry.io/otel/sdk v1.11.1 h1:F7KmQgoHljhUuJyA+9BiU+EkJfyX5nVVF4wyzWZp
|
||||
go.opentelemetry.io/otel/trace v1.11.1 h1:ofxdnzsNrGBYXbP7t7zpUK281+go5rF7dvdIZXF8gdQ=
|
||||
go.opentelemetry.io/otel/trace v1.11.1/go.mod h1:f/Q9G7vzk5u91PhbmKbg1Qn0rzH1LJ4vbPHFGkTPtOk=
|
||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
||||
goauthentik.io/api/v3 v3.2023012.4 h1:Vn85T5WQOEZfyimcAA6Anf4f+S68Lw3Ap5+v9d4+hZA=
|
||||
goauthentik.io/api/v3 v3.2023012.4/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
|
||||
goauthentik.io/api/v3 v3.2023012.5 h1:MRFw5LuRboeb3tD3gl1yRBuAwBLWfOdO9EGXcn/5yIk=
|
||||
goauthentik.io/api/v3 v3.2023012.5/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
|
||||
goauthentik.io/api/v3 v3.2023021.1 h1:UH9MiG9tRvbsV4UeCqvtjxxdl69Z3nIMsulwk9Sn1zM=
|
||||
goauthentik.io/api/v3 v3.2023021.1/go.mod h1:QM9J32HgYE4gL71lWAfAoXSPdSmLVLW08itfLI3Mo10=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
||||
@ -528,8 +526,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
|
||||
golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@ -590,7 +586,6 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064 h1:BmCFkEH4nJrYcAc2L08yX5RhYGD4j58PTMkEUDkpz2I=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -29,4 +29,4 @@ func UserAgent() string {
|
||||
return fmt.Sprintf("authentik@%s", FullVersion())
|
||||
}
|
||||
|
||||
const VERSION = "2023.2.1"
|
||||
const VERSION = "2023.2.3"
|
||||
|
@ -25,6 +25,7 @@ func (fe *FlowExecutor) solveChallenge_Identification(challenge *api.ChallengeTy
|
||||
}
|
||||
|
||||
func (fe *FlowExecutor) solveChallenge_Password(challenge *api.ChallengeTypes, req api.ApiFlowsExecutorSolveRequest) (api.FlowChallengeResponseRequest, error) {
|
||||
fe.checkPasswordMFA()
|
||||
r := api.NewPasswordChallengeResponseRequest(fe.getAnswer(StagePassword))
|
||||
return api.PasswordChallengeResponseRequestAsFlowChallengeResponseRequest(r), nil
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ func (a *Application) attemptBasicAuth(username, password string) *Claims {
|
||||
return nil
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
res, err := a.httpClient.Do(req)
|
||||
res, err := a.publicHostHTTPClient.Do(req)
|
||||
if err != nil || res.StatusCode > 200 {
|
||||
b, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
|
@ -39,7 +39,7 @@ func (a *Application) attemptBearerAuth(token string) *TokenIntrospectionRespons
|
||||
return nil
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
res, err := a.httpClient.Do(req)
|
||||
res, err := a.publicHostHTTPClient.Do(req)
|
||||
if err != nil || res.StatusCode > 200 {
|
||||
a.log.WithError(err).Warning("failed to send introspection request")
|
||||
return nil
|
||||
|
@ -1,6 +1,8 @@
|
||||
package codecs
|
||||
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/gorilla/securecookie"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@ -12,6 +14,7 @@ type Codec struct {
|
||||
func New(maxAge int, hashKey, blockKey []byte) *Codec {
|
||||
cookie := securecookie.New(hashKey, blockKey)
|
||||
cookie.MaxAge(maxAge)
|
||||
cookie.MaxLength(math.MaxInt)
|
||||
return &Codec{
|
||||
SecureCookie: cookie,
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
# Stage 1: Build
|
||||
FROM docker.io/golang:1.20.0-bullseye AS builder
|
||||
FROM docker.io/golang:1.20.1-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
|
@ -7,7 +7,7 @@ ENV NODE_ENV=production
|
||||
RUN cd /static && npm ci && npm run build-proxy
|
||||
|
||||
# Stage 2: Build
|
||||
FROM docker.io/golang:1.20.0-bullseye AS builder
|
||||
FROM docker.io/golang:1.20.1-bullseye AS builder
|
||||
|
||||
WORKDIR /go/src/goauthentik.io
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
[tool.pyright]
|
||||
ignore = [
|
||||
"**/migrations/**",
|
||||
"**/node_modules/**"
|
||||
]
|
||||
ignore = ["**/migrations/**", "**/node_modules/**"]
|
||||
reportMissingTypeStubs = false
|
||||
strictParameterNoneValue = true
|
||||
strictDictionaryInference = true
|
||||
@ -63,14 +60,7 @@ exclude_lines = [
|
||||
show_missing = true
|
||||
|
||||
[tool.pylint.basic]
|
||||
good-names = [
|
||||
"pk",
|
||||
"id",
|
||||
"i",
|
||||
"j",
|
||||
"k",
|
||||
"_",
|
||||
]
|
||||
good-names = ["pk", "id", "i", "j", "k", "_"]
|
||||
|
||||
[tool.pylint.master]
|
||||
disable = [
|
||||
@ -85,6 +75,7 @@ disable = [
|
||||
"protected-access",
|
||||
"unused-argument",
|
||||
"raise-missing-from",
|
||||
"fixme",
|
||||
# To preserve django's translation function we need to use %-formatting
|
||||
"consider-using-f-string",
|
||||
]
|
||||
@ -114,13 +105,13 @@ filterwarnings = [
|
||||
|
||||
[tool.poetry]
|
||||
name = "authentik"
|
||||
version = "2023.2.1"
|
||||
version = "2023.2.3"
|
||||
description = ""
|
||||
authors = ["authentik Team <hello@goauthentik.io>"]
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
celery = "*"
|
||||
channels = {version = "*", extras = ["daphne"]}
|
||||
channels = { version = "*", extras = ["daphne"] }
|
||||
channels-redis = "*"
|
||||
codespell = "*"
|
||||
colorama = "*"
|
||||
@ -147,7 +138,7 @@ gunicorn = "*"
|
||||
kubernetes = "*"
|
||||
ldap3 = "*"
|
||||
lxml = "*"
|
||||
opencontainers = {extras = ["reggie"],version = "*"}
|
||||
opencontainers = { extras = ["reggie"], version = "*" }
|
||||
packaging = "*"
|
||||
paramiko = "*"
|
||||
psycopg2-binary = "*"
|
||||
@ -163,8 +154,8 @@ swagger-spec-validator = "*"
|
||||
twilio = "*"
|
||||
twisted = "*"
|
||||
ua-parser = "*"
|
||||
urllib3 = {extras = ["secure"],version = "*"}
|
||||
uvicorn = {extras = ["standard"],version = "*"}
|
||||
urllib3 = { extras = ["secure"], version = "*" }
|
||||
uvicorn = { extras = ["standard"], version = "*" }
|
||||
webauthn = "*"
|
||||
wsproto = "*"
|
||||
xmlsec = "*"
|
||||
@ -176,7 +167,7 @@ bandit = "*"
|
||||
black = "*"
|
||||
bump2version = "*"
|
||||
colorama = "*"
|
||||
coverage = {extras = ["toml"],version = "*"}
|
||||
coverage = { extras = ["toml"], version = "*" }
|
||||
importlib-metadata = "*"
|
||||
pylint = "*"
|
||||
pylint-django = "*"
|
||||
|
@ -1,7 +1,7 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: authentik
|
||||
version: 2023.2.1
|
||||
version: 2023.2.3
|
||||
description: Making authentication simple.
|
||||
contact:
|
||||
email: hello@goauthentik.io
|
||||
|
14
web/package-lock.json
generated
14
web/package-lock.json
generated
@ -22,7 +22,7 @@
|
||||
"@codemirror/theme-one-dark": "^6.1.0",
|
||||
"@formatjs/intl-listformat": "^7.1.7",
|
||||
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||
"@goauthentik/api": "^2023.2.0-1676387781",
|
||||
"@goauthentik/api": "^2023.2.1-1676400495",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
"@lingui/cli": "^3.17.1",
|
||||
@ -1975,9 +1975,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@goauthentik/api": {
|
||||
"version": "2023.2.0-1676387781",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.2.0-1676387781.tgz",
|
||||
"integrity": "sha512-iPNxpL6ej3irMORdQuiOmKsDqI2oc0Mrt86M+UCA6Sm16vxBjiQl8Pq9JFmQ0GLosVXQZXh7NrWM5lXJprByPA=="
|
||||
"version": "2023.2.1-1676400495",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.2.1-1676400495.tgz",
|
||||
"integrity": "sha512-NBP17SLYOE+AXIAyhQWdTNm6wkcruzGG1jlGaQQSGa8ShDl6g98bO7F+gLMIyOZTXeKx6+bLlKzsL+QKQ34AeA=="
|
||||
},
|
||||
"node_modules/@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
@ -11502,9 +11502,9 @@
|
||||
"integrity": "sha512-qVtd5i1Cc7cdrqnTWqTObKQHjPWAiRwjUPaXObaeNPcy7+WKxJumGBx66rfSFgK6LNpIasVKkEgW8oyf0tmPLA=="
|
||||
},
|
||||
"@goauthentik/api": {
|
||||
"version": "2023.2.0-1676387781",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.2.0-1676387781.tgz",
|
||||
"integrity": "sha512-iPNxpL6ej3irMORdQuiOmKsDqI2oc0Mrt86M+UCA6Sm16vxBjiQl8Pq9JFmQ0GLosVXQZXh7NrWM5lXJprByPA=="
|
||||
"version": "2023.2.1-1676400495",
|
||||
"resolved": "https://registry.npmjs.org/@goauthentik/api/-/api-2023.2.1-1676400495.tgz",
|
||||
"integrity": "sha512-NBP17SLYOE+AXIAyhQWdTNm6wkcruzGG1jlGaQQSGa8ShDl6g98bO7F+gLMIyOZTXeKx6+bLlKzsL+QKQ34AeA=="
|
||||
},
|
||||
"@hcaptcha/types": {
|
||||
"version": "1.0.3",
|
||||
|
@ -66,7 +66,7 @@
|
||||
"@codemirror/theme-one-dark": "^6.1.0",
|
||||
"@formatjs/intl-listformat": "^7.1.7",
|
||||
"@fortawesome/fontawesome-free": "^6.3.0",
|
||||
"@goauthentik/api": "^2023.2.0-1676387781",
|
||||
"@goauthentik/api": "^2023.2.1-1676400495",
|
||||
"@hcaptcha/types": "^1.0.3",
|
||||
"@jackfranklin/rollup-plugin-markdown": "^0.4.0",
|
||||
"@lingui/cli": "^3.17.1",
|
||||
|
@ -51,7 +51,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
patchedGroupRequest: data,
|
||||
});
|
||||
} else {
|
||||
data.users = Array.from(this.instance?.users || []);
|
||||
data.users = [];
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsCreate({
|
||||
groupRequest: data,
|
||||
});
|
||||
|
@ -48,6 +48,7 @@ export class UserForm extends ModelForm<User, number> {
|
||||
patchedUserRequest: data,
|
||||
});
|
||||
} else {
|
||||
data.groups = [];
|
||||
return new CoreApi(DEFAULT_CONFIG).coreUsersCreate({
|
||||
userRequest: data,
|
||||
});
|
||||
|
@ -3,7 +3,7 @@ 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 = "2023.2.1";
|
||||
export const VERSION = "2023.2.3";
|
||||
export const TITLE_DEFAULT = "authentik";
|
||||
export const ROUTE_SEPARATOR = ";";
|
||||
|
||||
|
@ -29,6 +29,20 @@ export function convertToTitle(text: string): string {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a string based on maximum word count
|
||||
*/
|
||||
export function truncateWords(string: string, length = 10): string {
|
||||
string = string || "";
|
||||
const array = string.trim().split(" ");
|
||||
const ellipsis = array.length > length ? "..." : "";
|
||||
|
||||
return array.slice(0, length).join(" ") + ellipsis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate a string based on character count
|
||||
*/
|
||||
export function truncate(string: string, length = 10): string {
|
||||
return string.length > length ? `${string.substring(0, length)}...` : string;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { uiConfig } from "@goauthentik/common/ui/config";
|
||||
import { me } from "@goauthentik/common/users";
|
||||
import { truncate } from "@goauthentik/common/utils";
|
||||
import { truncateWords } from "@goauthentik/common/utils";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { t } from "@lingui/macro";
|
||||
@ -127,7 +127,7 @@ export class LibraryApplication extends AKElement {
|
||||
</div>
|
||||
</div>
|
||||
<div class="pf-c-card__body">
|
||||
${truncate(this.application.metaDescription || "", 35)}
|
||||
${truncateWords(this.application.metaDescription || "", 35)}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
@ -4,11 +4,30 @@ title: Terminology
|
||||
slug: /terminology
|
||||
---
|
||||
|
||||

|
||||
```mermaid
|
||||
graph LR
|
||||
source_ldap((LDAP Source)) <-->|Synchronizes| datasource_ldap["FreeIPA/
|
||||
Active Directory"]
|
||||
datasource_oauth1(Twtitter) --> source_oauth((OAuth/SAML\nSource))
|
||||
datasource_oauth2(GitHub) --> source_oauth((OAuth/SAML\nSource))
|
||||
source_oauth --> authentik_db(authentik Database)
|
||||
source_ldap --> authentik_db(authentik Database)
|
||||
|
||||
### System tasks
|
||||
app_sso(Gitlab) --> authentik_provider[Provider]
|
||||
authentik_provider --> authentik_db
|
||||
authentik_provider --> authentik_app["Application
|
||||
(Stores permissions and UI details)"]
|
||||
authentik_app --> authentik_policy_engine[Policy Engine]
|
||||
authentik_policy_engine --> authentik_db
|
||||
|
||||
These are longer-running tasks which authentik runs in the background. This is used to sync LDAP sources, backup the database, and other various tasks.
|
||||
app_ldap("Applications that only
|
||||
support LDAP (e.g. pfSense)") --> authentik_outpost_ldap[LDAP Outpost]
|
||||
app_proxy("Applications that don't
|
||||
support any SSO (e.g. Plex)") --> authentik_outpost_proxy[Proxy Outpost]
|
||||
authentik_outpost_ldap --> authentik_outposts[Outposts]
|
||||
authentik_outpost_proxy --> authentik_outposts[Outposts]
|
||||
authentik_outposts --> authentik_provider
|
||||
```
|
||||
|
||||
### Application
|
||||
|
||||
@ -26,7 +45,7 @@ A Provider is a way for other applications to authenticate against authentik. Co
|
||||
|
||||
At a base level a policy is a yes/no gate. It will either evaluate to True or False depending on the Policy Kind and settings. For example, a "Group Membership Policy" evaluates to True if the user is member of the specified Group and False if not. This can be used to conditionally apply Stages, grant/deny access to various objects, and for other custom logic.
|
||||
|
||||
See [Policies](./policies/)
|
||||
See [Policies](../policies/index.md)
|
||||
|
||||
### Flows & Stages
|
||||
|
||||
@ -34,16 +53,20 @@ Flows are an ordered sequence of stages. These flows can be used to define how a
|
||||
|
||||
A stage represents a single verification or logic step. They are used to authenticate users, enroll users, and more. These stages can optionally be applied to a flow via policies.
|
||||
|
||||
See [Flows](./flow/)
|
||||
See [Flows](../flow/index.md)
|
||||
|
||||
### Property Mappings
|
||||
|
||||
Property Mappings allow you to make information available for external applications. For example, if you want to login to AWS with authentik, you'd use Property Mappings to set the user's roles in AWS based on their group memberships in authentik.
|
||||
|
||||
See [Property Mappings](./property-mappings/)
|
||||
See [Property Mappings](../property-mappings/index.md)
|
||||
|
||||
### Outpost
|
||||
|
||||
An outpost is a separate component of authentik, which can be deployed anywhere, regardless of the authentik deployment. The outpost offers services that aren't implemented directly into the authentik core, e.g. Reverse Proxying.
|
||||
|
||||
See [Outposts](./outposts/)
|
||||
See [Outposts](../outposts/index.mdx)
|
||||
|
||||
### System tasks
|
||||
|
||||
These are longer-running tasks which authentik runs in the background. This is used to sync LDAP sources, backup the database, and other various tasks.
|
||||
|
@ -4,30 +4,305 @@ title: Events
|
||||
|
||||
Events are authentik's built-in logging system. Whenever any of the following actions occur, an event is created:
|
||||
|
||||
- A user logs in/logs out (including the source, if available)
|
||||
- A user fails to login
|
||||
- A user sets their password
|
||||
|
||||
- A user views a token
|
||||
|
||||
- An invitation is used
|
||||
|
||||
- A user object is written to during a flow
|
||||
|
||||
- A user authorizes an application
|
||||
- A user links a source to their account
|
||||
|
||||
- A user starts/ends impersonation, including the user that was impersonated
|
||||
|
||||
- A policy is executed (when a policy has "Execution Logging" enabled)
|
||||
- A policy or property mapping causes an exception
|
||||
|
||||
- A configuration error occurs, for example during the authorization of an application
|
||||
|
||||
- Any objects is created/updated/deleted
|
||||
|
||||
- An update is available
|
||||
|
||||
Certain information is stripped from events, to ensure no passwords or other credentials are saved in the log.
|
||||
|
||||
If you want to forward these events to another application, simply forward the log output of all authentik containers. Every event creation is logged there.
|
||||
If you want to forward these events to another application, forward the log output of all authentik containers. Every event creation is logged with the log level "info".
|
||||
|
||||
### `login`
|
||||
|
||||
A user logs in (including the source, if available)
|
||||
|
||||
<details><summary>Example</summary>
|
||||
<p>
|
||||
|
||||
```json
|
||||
{
|
||||
"pk": "f00f54e7-2b38-421f-bc78-e61f950048d6",
|
||||
"user": {
|
||||
"pk": 1,
|
||||
"email": "root@localhost",
|
||||
"username": "akadmin"
|
||||
},
|
||||
"action": "login",
|
||||
"app": "authentik.events.signals",
|
||||
"context": {
|
||||
"auth_method": "password",
|
||||
"http_request": {
|
||||
"args": {
|
||||
"query": "next=%2F"
|
||||
},
|
||||
"path": "/api/v3/flows/executor/default-authentication-flow/",
|
||||
"method": "GET"
|
||||
},
|
||||
"auth_method_args": {}
|
||||
},
|
||||
"client_ip": "::1",
|
||||
"created": "2023-02-15T15:33:42.771091Z",
|
||||
"expires": "2024-02-15T15:33:42.770425Z",
|
||||
"tenant": {
|
||||
"pk": "fcba828076b94dedb2d5a6b4c5556fa1",
|
||||
"app": "authentik_tenants",
|
||||
"name": "Default tenant",
|
||||
"model_name": "tenant"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### `login_failed`
|
||||
|
||||
A failed login attempt
|
||||
|
||||
<details><summary>Example</summary>
|
||||
<p>
|
||||
|
||||
```json
|
||||
{
|
||||
"pk": "2779b173-eb2a-4c2b-a1a4-8283eda308d7",
|
||||
"user": {
|
||||
"pk": 2,
|
||||
"email": "",
|
||||
"username": "AnonymousUser"
|
||||
},
|
||||
"action": "login_failed",
|
||||
"app": "authentik.events.signals",
|
||||
"context": {
|
||||
"stage": {
|
||||
"pk": "7e88f4a991c442c1a1335d80f0827d7f",
|
||||
"app": "authentik_stages_password",
|
||||
"name": "default-authentication-password",
|
||||
"model_name": "passwordstage"
|
||||
},
|
||||
"password": "********************",
|
||||
"username": "akadmin",
|
||||
"http_request": {
|
||||
"args": {
|
||||
"query": "next=%2F"
|
||||
},
|
||||
"path": "/api/v3/flows/executor/default-authentication-flow/",
|
||||
"method": "POST"
|
||||
}
|
||||
},
|
||||
"client_ip": "::1",
|
||||
"created": "2023-02-15T15:32:55.319608Z",
|
||||
"expires": "2024-02-15T15:32:55.314581Z",
|
||||
"tenant": {
|
||||
"pk": "fcba828076b94dedb2d5a6b4c5556fa1",
|
||||
"app": "authentik_tenants",
|
||||
"name": "Default tenant",
|
||||
"model_name": "tenant"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### `logout`
|
||||
|
||||
A user logs out.
|
||||
|
||||
<details><summary>Example</summary>
|
||||
<p>
|
||||
|
||||
```json
|
||||
{
|
||||
"pk": "474ffb6b-77e3-401c-b681-7d618962440f",
|
||||
"user": {
|
||||
"pk": 1,
|
||||
"email": "root@localhost",
|
||||
"username": "akadmin"
|
||||
},
|
||||
"action": "logout",
|
||||
"app": "authentik.events.signals",
|
||||
"context": {
|
||||
"http_request": {
|
||||
"args": {
|
||||
"query": ""
|
||||
},
|
||||
"path": "/api/v3/flows/executor/default-invalidation-flow/",
|
||||
"method": "GET"
|
||||
}
|
||||
},
|
||||
"client_ip": "::1",
|
||||
"created": "2023-02-15T15:39:55.976243Z",
|
||||
"expires": "2024-02-15T15:39:55.975535Z",
|
||||
"tenant": {
|
||||
"pk": "fcba828076b94dedb2d5a6b4c5556fa1",
|
||||
"app": "authentik_tenants",
|
||||
"name": "Default tenant",
|
||||
"model_name": "tenant"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### `user_write`
|
||||
|
||||
A user is written to during a flow execution.
|
||||
|
||||
<details><summary>Example</summary>
|
||||
<p>
|
||||
|
||||
```json
|
||||
{
|
||||
"pk": "d012e8af-cb94-4fa2-9e92-961e4eebc060",
|
||||
"user": {
|
||||
"pk": 1,
|
||||
"email": "root@localhost",
|
||||
"username": "akadmin"
|
||||
},
|
||||
"action": "user_write",
|
||||
"app": "authentik.events.signals",
|
||||
"context": {
|
||||
"name": "authentik Default Admin",
|
||||
"email": "root@localhost",
|
||||
"created": false,
|
||||
"username": "akadmin",
|
||||
"attributes": {
|
||||
"settings": {
|
||||
"locale": ""
|
||||
}
|
||||
},
|
||||
"http_request": {
|
||||
"args": {
|
||||
"query": ""
|
||||
},
|
||||
"path": "/api/v3/flows/executor/default-user-settings-flow/",
|
||||
"method": "GET"
|
||||
}
|
||||
},
|
||||
"client_ip": "::1",
|
||||
"created": "2023-02-15T15:41:18.411017Z",
|
||||
"expires": "2024-02-15T15:41:18.410276Z",
|
||||
"tenant": {
|
||||
"pk": "fcba828076b94dedb2d5a6b4c5556fa1",
|
||||
"app": "authentik_tenants",
|
||||
"name": "Default tenant",
|
||||
"model_name": "tenant"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### `suspicious_request`
|
||||
|
||||
A suspicious request has been received (for example, a revoked token was used).
|
||||
|
||||
### `password_set`
|
||||
|
||||
A user sets their password.
|
||||
|
||||
### `secret_view`
|
||||
|
||||
A user views a token's/certificate's data.
|
||||
|
||||
### `secret_rotate`
|
||||
|
||||
A token was rotated automatically by authentik.
|
||||
|
||||
### `invitation_used`
|
||||
|
||||
An invitation is used.
|
||||
|
||||
### `authorize_application`
|
||||
|
||||
A user authorizes an application.
|
||||
|
||||
<details><summary>Example</summary>
|
||||
<p>
|
||||
|
||||
```json
|
||||
{
|
||||
"pk": "f52f9eb9-dc2a-4f1e-afea-ad5af90bf680",
|
||||
"user": {
|
||||
"pk": 1,
|
||||
"email": "root@localhost",
|
||||
"username": "akadmin"
|
||||
},
|
||||
"action": "authorize_application",
|
||||
"app": "authentik.providers.oauth2.views.authorize",
|
||||
"context": {
|
||||
"geo": {
|
||||
"lat": 42.0,
|
||||
"city": "placeholder",
|
||||
"long": 42.0,
|
||||
"country": "placeholder",
|
||||
"continent": "placeholder"
|
||||
},
|
||||
"flow": "53287faa8a644b6cb124cb602a84282f",
|
||||
"scopes": "ak_proxy profile openid email",
|
||||
"http_request": {
|
||||
"args": {
|
||||
"query": "[...]"
|
||||
},
|
||||
"path": "/api/v3/flows/executor/default-provider-authorization-implicit-consent/",
|
||||
"method": "GET"
|
||||
},
|
||||
"authorized_application": {
|
||||
"pk": "bed6a2495fdc4b2e8c3f93cb2ed7e021",
|
||||
"app": "authentik_core",
|
||||
"name": "Alertmanager",
|
||||
"model_name": "application"
|
||||
}
|
||||
},
|
||||
"client_ip": "::1",
|
||||
"created": "2023-02-15T10:02:48.615499Z",
|
||||
"expires": "2023-04-26T10:02:48.612809Z",
|
||||
"tenant": {
|
||||
"pk": "10800be643d44842ab9d97cb5f898ce9",
|
||||
"app": "authentik_tenants",
|
||||
"name": "Default tenant",
|
||||
"model_name": "tenant"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</p>
|
||||
</details>
|
||||
|
||||
### `source_linked`
|
||||
|
||||
A user links a source to their account
|
||||
|
||||
### `impersonation_started` / `impersonation_ended`
|
||||
|
||||
A user starts/ends impersonation, including the user that was impersonated
|
||||
|
||||
### `policy_execution`
|
||||
|
||||
A policy is executed (when a policy has "Execution Logging" enabled).
|
||||
|
||||
### `policy_exception` / `property_mapping_exception`
|
||||
|
||||
A policy or property mapping causes an exception
|
||||
|
||||
### `system_task_exception`
|
||||
|
||||
An exception occurred in a system task.
|
||||
|
||||
### `system_exception`
|
||||
|
||||
A general exception in authentik occurred.
|
||||
|
||||
### `configuration_error`
|
||||
|
||||
A configuration error occurs, for example during the authorization of an application
|
||||
|
||||
### `model_created` / `model_updated` / `model_deleted`
|
||||
|
||||
Logged when any model is created/updated/deleted, including the user that sent the request.
|
||||
|
||||
### `email_sent`
|
||||
|
||||
An email has been sent. Included is the email that was sent.
|
||||
|
||||
### `update_available`
|
||||
|
||||
An update is available
|
||||
|
@ -99,7 +99,7 @@ This includes the following:
|
||||
- `context['application']`: The application the user is in the process of authorizing. (Optional)
|
||||
- `context['source']`: The source the user is authenticating/enrolling with. (Optional)
|
||||
- `context['pending_user']`: The currently pending user, see [User](../user-group/user.md#object-attributes)
|
||||
- `context['is_restored']`: Set to `True` when the flow plan has been restored from a flow token, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
|
||||
- `context['is_restored']`: Contains the flow token when the flow plan was restored from a link, for example the user clicked a link to a flow which was sent by an email stage. (Optional)
|
||||
- `context['auth_method']`: Authentication method (this value is set by password stages) (Optional)
|
||||
|
||||
Depending on method, `context['auth_method_args']` is also set.
|
||||
|
@ -1,28 +1,94 @@
|
||||
---
|
||||
title: Overview
|
||||
title: Proxy Provider
|
||||
---
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant u as User accesses service
|
||||
participant rp as Reverse proxy
|
||||
participant ak as authentik
|
||||
participant s as Service
|
||||
|
||||
u->>rp: Initial request
|
||||
rp->>ak: Checks authentication
|
||||
alt User is authenticated
|
||||
ak ->> rp: Successful response
|
||||
rp ->> s: Initial request is forwarded
|
||||
else User needs to be authenticated
|
||||
ak ->> rp: Redirect to the login page
|
||||
rp ->> u: Redirect is passed to enduser
|
||||
end
|
||||
```
|
||||
|
||||
## Headers
|
||||
|
||||
The proxy outpost sets the following user-specific headers:
|
||||
|
||||
- X-authentik-username: `akadmin`
|
||||
### `X-authentik-username`
|
||||
|
||||
The username of the currently logged in user
|
||||
Example value: `akadmin`
|
||||
|
||||
- X-authentik-groups: `foo|bar|baz`
|
||||
The username of the currently logged in user
|
||||
|
||||
The groups the user is member of, separated by a pipe
|
||||
### `X-authentik-groups`
|
||||
|
||||
- X-authentik-email: `root@localhost`
|
||||
Example value: `foo|bar|baz`
|
||||
|
||||
The email address of the currently logged in user
|
||||
The groups the user is member of, separated by a pipe
|
||||
|
||||
- X-authentik-name: `authentik Default Admin`
|
||||
### `X-authentik-email`
|
||||
|
||||
Full name of the current user
|
||||
Example value: `root@localhost`
|
||||
|
||||
- X-authentik-uid: `900347b8a29876b45ca6f75722635ecfedf0e931c6022e3a29a8aa13fb5516fb`
|
||||
The email address of the currently logged in user
|
||||
|
||||
The hashed identifier of the currently logged in user.
|
||||
### `X-authentik-name`
|
||||
|
||||
Example value: `authentik Default Admin`
|
||||
|
||||
Full name of the current user
|
||||
|
||||
### `X-authentik-uid`
|
||||
|
||||
Example value: `900347b8a29876b45ca6f75722635ecfedf0e931c6022e3a29a8aa13fb5516fb`
|
||||
|
||||
The hashed identifier of the currently logged in user.
|
||||
|
||||
Besides these user-specific headers, some application specific headers are also set:
|
||||
|
||||
### `X-authentik-meta-outpost`
|
||||
|
||||
Example value: `authentik Embedded Outpost`
|
||||
|
||||
The authentik outpost's name.
|
||||
|
||||
### `X-authentik-meta-provider`
|
||||
|
||||
Example value: `test`
|
||||
|
||||
The authentik provider's name.
|
||||
|
||||
### `X-authentik-meta-app`
|
||||
|
||||
Example value: `test`
|
||||
|
||||
The authentik application's slug.
|
||||
|
||||
### `X-authentik-meta-version`
|
||||
|
||||
Example value: `goauthentik.io/outpost/1.2.3`
|
||||
|
||||
The authentik outpost's version.
|
||||
|
||||
### `X-Forwarded-Host`
|
||||
|
||||
:::info
|
||||
Only set in proxy mode
|
||||
:::
|
||||
|
||||
The original Host header sent by the client. This is set as the `Host` header is set to the host of the configured backend.
|
||||
|
||||
### Additional headers
|
||||
|
||||
Additionally, you can set `additionalHeaders` attribute on groups or users to set additional headers:
|
||||
|
||||
@ -31,30 +97,6 @@ additionalHeaders:
|
||||
X-test-header: test-value
|
||||
```
|
||||
|
||||
Besides these user-specific headers, some application specific headers are also set:
|
||||
|
||||
- X-authentik-meta-outpost: `authentik Embedded Outpost`
|
||||
|
||||
The authentik outpost's name.
|
||||
|
||||
- X-authentik-meta-provider: `test`
|
||||
|
||||
The authentik provider's name.
|
||||
|
||||
- X-authentik-meta-app: `test`
|
||||
|
||||
The authentik application's slug.
|
||||
|
||||
- X-authentik-meta-version: `goauthentik.io/outpost/1.2.3`
|
||||
|
||||
The authentik outpost's version.
|
||||
|
||||
### Only in proxy mode
|
||||
|
||||
- X-Forwarded-Host:
|
||||
|
||||
The original Host header sent by the client. This is set as the `Host` header is set to the host of the configured backend.
|
||||
|
||||
## HTTPS
|
||||
|
||||
The outpost listens on both 9000 for HTTP and 9443 for HTTPS.
|
||||
|
27
website/docs/security/CVE-2023-26481.md
Normal file
27
website/docs/security/CVE-2023-26481.md
Normal file
@ -0,0 +1,27 @@
|
||||
# CVE-2023-26481
|
||||
|
||||
_Reported by [@fuomag9](https://github.com/fuomag9)_
|
||||
|
||||
## Insufficient user check in FlowTokens by Email stage
|
||||
|
||||
### Summary
|
||||
|
||||
Due to an insufficient access check, a recovery flow link that is created by an admin (or sent via email by an admin) can be used to set the password for any arbitrary user.
|
||||
|
||||
### Patches
|
||||
|
||||
authentik 2022.12.3, 2023.1.3, 2023.2.3 fix this issue.
|
||||
|
||||
### Impact
|
||||
|
||||
This attack is only possible if a recovery flow exists, which has both an Identification and an Email stage bound to it. If the flow has policies on the identification stage to skip it when the flow is restored (by checking `request.context['is_restored']`), the flow is not affected by this. With this flow in place, an administrator must create a recovery Link or send a recovery URL to the attacker, who can, due to the improper validation of the token create, set the password for any account.
|
||||
|
||||
### Workaround
|
||||
|
||||
It is recommended to upgrade to the patched version of authentik. Regardless, for custom recovery flows it is recommended to add a policy that checks if the flow is restored, and skips the identification stage.
|
||||
|
||||
### For more information
|
||||
|
||||
If you have any questions or comments about this advisory:
|
||||
|
||||
- Email us at [security@goauthentik.io](mailto:security@goauthentik.io)
|
@ -48,15 +48,15 @@ module.exports = {
|
||||
},
|
||||
{
|
||||
type: "dropdown",
|
||||
label: `Version: latest`,
|
||||
label: `Version: ${releases[0].replace(
|
||||
/releases\/\d+\/v/,
|
||||
""
|
||||
)}`,
|
||||
position: "right",
|
||||
items: releases.map((release) => {
|
||||
const subdomain = release
|
||||
.replace(/releases\/\d+\/v/, "")
|
||||
.replace(".", "-");
|
||||
const label =
|
||||
"Version: " +
|
||||
release.replace(/releases\/\d+\/v/, "");
|
||||
const version = release.replace(/releases\/\d+\/v/, "");
|
||||
const subdomain = version.replace(".", "-");
|
||||
const label = `Version: ${version}`;
|
||||
return {
|
||||
label: label,
|
||||
href: `https://version-${subdomain}.goauthentik.io`,
|
||||
@ -169,6 +169,10 @@ module.exports = {
|
||||
},
|
||||
],
|
||||
],
|
||||
markdown: {
|
||||
mermaid: true,
|
||||
},
|
||||
themes: ["@docusaurus/theme-mermaid"],
|
||||
scripts: [
|
||||
{
|
||||
src: "https://goauthentik.io/js/script.js",
|
||||
|
891
website/package-lock.json
generated
891
website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,6 +18,7 @@
|
||||
"dependencies": {
|
||||
"@docusaurus/plugin-client-redirects": "2.3.1",
|
||||
"@docusaurus/preset-classic": "2.3.1",
|
||||
"@docusaurus/theme-mermaid": "^2.3.1",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"disqus-react": "^1.1.5",
|
||||
|
@ -300,9 +300,10 @@ module.exports = {
|
||||
},
|
||||
items: [
|
||||
"security/policy",
|
||||
"security/CVE-2022-23555",
|
||||
"security/CVE-2022-46145",
|
||||
"security/CVE-2022-46172",
|
||||
"security/CVE-2022-23555",
|
||||
"security/CVE-2023-26481",
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -24,6 +24,7 @@
|
||||
}
|
||||
|
||||
.hero--primary {
|
||||
padding-bottom: 5.3rem !important;
|
||||
-webkit-clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 3vw));
|
||||
clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 3vw));
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 24 KiB |
Reference in New Issue
Block a user