Skip to content

Commit

Permalink
Merge branch 'main' into dev
Browse files Browse the repository at this point in the history
* main: (43 commits)
  stages/authenticator_webauthn: Update FIDO MDS3 & Passkey aaguid blobs (#9535)
  web: bump the rollup group across 1 directory with 3 updates (#9532)
  website/developer-docs: Add note for custom YAML tags in an IDE (#9528)
  lifecycle: close database connection after migrating (#9516)
  web: bump the babel group in /web with 3 updates (#9520)
  core: bump node from 21 to 22 (#9521)
  web: bump @codemirror/lang-python from 6.1.5 to 6.1.6 in /web (#9523)
  providers/rac: bump guacd to 1.5.5 (#9514)
  core: only prefetch related objects when required (#9476)
  website/integrations: move Fortimanager to Networking (#9505)
  website: bump react-tooltip from 5.26.3 to 5.26.4 in /website (#9494)
  web: bump the rollup group in /web with 3 updates (#9497)
  web: bump yaml from 2.4.1 to 2.4.2 in /web (#9499)
  core: bump goauthentik.io/api/v3 from 3.2024040.1 to 3.2024041.1 (#9503)
  core: bump pytest from 8.1.1 to 8.2.0 (#9501)
  website: bump react-dom from 18.3.0 to 18.3.1 in /website (#9495)
  website: bump react and @types/react in /website (#9496)
  web: bump react-dom from 18.3.0 to 18.3.1 in /web (#9498)
  core: bump sentry-sdk from 2.0.0 to 2.0.1 (#9502)
  web/flows: fix missing fallback for flow logo (#9487)
  ...
  • Loading branch information
kensternberg-authentik committed May 2, 2024
2 parents ca42506 + d2b8bd3 commit 2a96900
Show file tree
Hide file tree
Showing 76 changed files with 916 additions and 356 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 2024.4.0
current_version = 2024.4.1
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)(?:-(?P<rc_t>[a-zA-Z-]+)(?P<rc_n>[1-9]\\d*))?
Expand Down
12 changes: 6 additions & 6 deletions .github/actions/docker-push-variables/push_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@
image_tags_rendered = ",".join(image_tags)

with open(os.environ["GITHUB_OUTPUT"], "a+", encoding="utf-8") as _output:
print("shouldBuild=%s" % should_build, file=_output)
print("sha=%s" % sha, file=_output)
print("version=%s" % version, file=_output)
print("prerelease=%s" % prerelease, file=_output)
print("imageTags=%s" % image_tags_rendered, file=_output)
print("imageMainTag=%s" % image_main_tag, file=_output)
print(f"shouldBuild={should_build}", file=_output)
print(f"sha={sha}", file=_output)
print(f"version={version}", file=_output)
print(f"prerelease={prerelease}", file=_output)
print(f"imageTags={image_tags_rendered}", file=_output)
print(f"imageMainTag={image_main_tag}", file=_output)
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# syntax=docker/dockerfile:1

# Stage 1: Build website
FROM --platform=${BUILDPLATFORM} docker.io/node:21 as website-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:22 as website-builder

ENV NODE_ENV=production

Expand All @@ -20,7 +20,7 @@ COPY ./SECURITY.md /work/
RUN npm run build-bundled

# Stage 2: Build webui
FROM --platform=${BUILDPLATFORM} docker.io/node:21 as web-builder
FROM --platform=${BUILDPLATFORM} docker.io/node:22 as web-builder

ENV NODE_ENV=production

Expand Down
2 changes: 1 addition & 1 deletion authentik/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from os import environ

__version__ = "2024.4.0"
__version__ = "2024.4.1"
ENV_GIT_HASH_KEY = "GIT_BUILD_HASH"


Expand Down
8 changes: 7 additions & 1 deletion authentik/core/api/groups.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,18 @@ class UserAccountSerializer(PassiveSerializer):

pk = IntegerField(required=True)

queryset = Group.objects.all().select_related("parent").prefetch_related("users")
queryset = Group.objects.none()
serializer_class = GroupSerializer
search_fields = ["name", "is_superuser"]
filterset_class = GroupFilter
ordering = ["name"]

def get_queryset(self):
base_qs = Group.objects.all().select_related("parent").prefetch_related("roles")
if self.serializer_class(context={"request": self.request})._should_include_users:
base_qs = base_qs.prefetch_related("users")
return base_qs

@extend_schema(
parameters=[
OpenApiParameter("include_users", bool, default=True),
Expand Down
7 changes: 5 additions & 2 deletions authentik/core/api/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,11 @@ class UserViewSet(UsedByMixin, ModelViewSet):
search_fields = ["username", "name", "is_active", "email", "uuid"]
filterset_class = UsersFilter

def get_queryset(self): # pragma: no cover
return User.objects.all().exclude_anonymous().prefetch_related("ak_groups")
def get_queryset(self):
base_qs = User.objects.all().exclude_anonymous()
if self.serializer_class(context={"request": self.request})._should_include_groups:
base_qs = base_qs.prefetch_related("ak_groups")
return base_qs

@extend_schema(
parameters=[
Expand Down
2 changes: 1 addition & 1 deletion authentik/core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ def serializer(self) -> type[Serializer]:
raise NotImplementedError

def __str__(self) -> str:
return f"User-source connection (user={self.user.username}, source={self.source.slug})"
return f"User-source connection (user={self.user_id}, source={self.source_id})"

class Meta:
unique_together = (("user", "source"),)
Expand Down
9 changes: 8 additions & 1 deletion authentik/core/tests/test_groups_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework.test import APITestCase

from authentik.core.models import Group, User
from authentik.core.tests.utils import create_test_user
from authentik.core.tests.utils import create_test_admin_user, create_test_user
from authentik.lib.generators import generate_id


Expand All @@ -16,6 +16,13 @@ def setUp(self) -> None:
self.login_user = create_test_user()
self.user = User.objects.create(username="test-user")

def test_list_with_users(self):
"""Test listing with users"""
admin = create_test_admin_user()
self.client.force_login(admin)
response = self.client.get(reverse("authentik_api:group-list"), {"include_users": "true"})
self.assertEqual(response.status_code, 200)

def test_add_user(self):
"""Test add_user"""
group = Group.objects.create(name=generate_id())
Expand Down
7 changes: 2 additions & 5 deletions authentik/core/tests/test_property_mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,11 @@ def test_call_policy(self):
expression="return request.http_request.path",
)
http_request = self.factory.get("/")
tmpl = (
"""
res = ak_call_policy('%s')
tmpl = f"""
res = ak_call_policy('{expr.name}')
result = [request.http_request.path, res.raw_result]
return result
"""
% expr.name
)
evaluator = PropertyMapping(expression=tmpl, name=generate_id())
res = evaluator.evaluate(self.user, http_request)
self.assertEqual(res, ["/", "/"])
6 changes: 6 additions & 0 deletions authentik/core/tests/test_users_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ def test_filter_type(self):
)
self.assertEqual(response.status_code, 200)

def test_list_with_groups(self):
"""Test listing with groups"""
self.client.force_login(self.admin)
response = self.client.get(reverse("authentik_api:user-list"), {"include_groups": "true"})
self.assertEqual(response.status_code, 200)

def test_metrics(self):
"""Test user's metrics"""
self.client.force_login(self.admin)
Expand Down
2 changes: 0 additions & 2 deletions authentik/core/tests/test_users_avatars.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from authentik.core.models import User
from authentik.core.tests.utils import create_test_admin_user
from authentik.lib.config import CONFIG
from authentik.tenants.utils import get_current_tenant


Expand All @@ -25,7 +24,6 @@ def set_avatar_mode(self, mode: str):
tenant.avatars = mode
tenant.save()

@CONFIG.patch("avatars", "none")
def test_avatars_none(self):
"""Test avatars none"""
self.set_avatar_mode("none")
Expand Down
5 changes: 1 addition & 4 deletions authentik/enterprise/providers/rac/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,7 @@ def mapping_evaluator(mappings: QuerySet):
return settings

def __str__(self):
return (
f"RAC Connection token {self.session.user} to "
f"{self.endpoint.provider.name}/{self.endpoint.name}"
)
return f"RAC Connection token {self.session_id} to {self.provider_id}/{self.endpoint_id}"

class Meta:
verbose_name = _("RAC Connection token")
Expand Down
2 changes: 1 addition & 1 deletion authentik/events/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,12 @@ def get_user(self, request: HttpRequest) -> User:
return user
user = getattr(request, "user", self.anonymous_user)
if not user.is_authenticated:
self._ensure_fallback_user()
return self.anonymous_user
return user

def connect(self, request: HttpRequest):
"""Connect signal for automatic logging"""
self._ensure_fallback_user()
if not hasattr(request, "request_id"):
return
post_save.connect(
Expand Down
2 changes: 1 addition & 1 deletion authentik/events/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ def __str__(self) -> str:
if len(self.body) > NOTIFICATION_SUMMARY_LENGTH
else self.body
)
return f"Notification for user {self.user}: {body_trunc}"
return f"Notification for user {self.user_id}: {body_trunc}"

class Meta:
verbose_name = _("Notification")
Expand Down
35 changes: 35 additions & 0 deletions authentik/events/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
"""authentik event models tests"""

from collections.abc import Callable

from django.db.models import Model
from django.test import TestCase

from authentik.core.models import default_token_key
from authentik.lib.utils.reflection import get_apps


class TestModels(TestCase):
"""Test Models"""


def model_tester_factory(test_model: type[Model]) -> Callable:
"""Test models' __str__ and __repr__"""

def tester(self: TestModels):
allowed = 0
# Token-like objects need to lookup the current tenant to get the default token length
for field in test_model._meta.fields:
if field.default == default_token_key:
allowed += 1
with self.assertNumQueries(allowed):
str(test_model())
with self.assertNumQueries(allowed):
repr(test_model())

return tester


for app in get_apps():
for model in app.get_models():
setattr(TestModels, f"test_{app.label}_{model.__name__}", model_tester_factory(model))
2 changes: 1 addition & 1 deletion authentik/flows/api/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def set_background_url(self, request: Request, slug: str):
},
)
@action(detail=True, pagination_class=None, filter_backends=[])
def execute(self, request: Request, _slug: str):
def execute(self, request: Request, slug: str):
"""Execute flow for current user"""
# Because we pre-plan the flow here, and not in the planner, we need to manually clear
# the history of the inspector
Expand Down
19 changes: 19 additions & 0 deletions authentik/flows/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.api.stages import StageSerializer, StageViewSet
from authentik.flows.models import Flow, FlowDesignation, FlowStageBinding, Stage
from authentik.lib.generators import generate_id
from authentik.policies.dummy.models import DummyPolicy
from authentik.policies.models import PolicyBinding
from authentik.stages.dummy.models import DummyStage
Expand Down Expand Up @@ -101,3 +102,21 @@ def test_types(self):
reverse("authentik_api:stage-types"),
)
self.assertEqual(response.status_code, 200)

def test_execute(self):
"""Test execute endpoint"""
user = create_test_admin_user()
self.client.force_login(user)

flow = Flow.objects.create(
name=generate_id(),
slug=generate_id(),
designation=FlowDesignation.AUTHENTICATION,
)
FlowStageBinding.objects.create(
target=flow, stage=DummyStage.objects.create(name=generate_id()), order=0
)
response = self.client.get(
reverse("authentik_api:flow-execute", kwargs={"slug": flow.slug})
)
self.assertEqual(response.status_code, 200)
7 changes: 2 additions & 5 deletions authentik/policies/expression/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,16 +96,13 @@ def test_call_policy(self):
execution_logging=True,
expression="ak_message(request.http_request.path)\nreturn True",
)
tmpl = (
"""
tmpl = f"""
ak_message(request.http_request.path)
res = ak_call_policy('%s')
res = ak_call_policy('{expr.name}')
ak_message(request.http_request.path)
for msg in res.messages:
ak_message(msg)
"""
% expr.name
)
evaluator = PolicyEvaluator("test")
evaluator.set_policy_request(self.request)
res = evaluator.evaluate(tmpl)
Expand Down
8 changes: 4 additions & 4 deletions authentik/providers/oauth2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ class Meta:
verbose_name_plural = _("Authorization Codes")

def __str__(self):
return f"Authorization code for {self.provider} for user {self.user}"
return f"Authorization code for {self.provider_id} for user {self.user_id}"

@property
def serializer(self) -> Serializer:
Expand Down Expand Up @@ -356,7 +356,7 @@ class Meta:
verbose_name_plural = _("OAuth2 Access Tokens")

def __str__(self):
return f"Access Token for {self.provider} for user {self.user}"
return f"Access Token for {self.provider_id} for user {self.user_id}"

@property
def id_token(self) -> IDToken:
Expand Down Expand Up @@ -399,7 +399,7 @@ class Meta:
verbose_name_plural = _("OAuth2 Refresh Tokens")

def __str__(self):
return f"Refresh Token for {self.provider} for user {self.user}"
return f"Refresh Token for {self.provider_id} for user {self.user_id}"

@property
def id_token(self) -> IDToken:
Expand Down Expand Up @@ -443,4 +443,4 @@ class Meta:
verbose_name_plural = _("Device Tokens")

def __str__(self):
return f"Device Token for {self.provider}"
return f"Device Token for {self.provider_id}"
4 changes: 2 additions & 2 deletions authentik/providers/scim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class Meta:
unique_together = (("id", "user", "provider"),)

def __str__(self) -> str:
return f"SCIM User {self.user.username} to {self.provider.name}"
return f"SCIM User {self.user_id} to {self.provider_id}"


class SCIMGroup(models.Model):
Expand All @@ -119,4 +119,4 @@ class Meta:
unique_together = (("id", "group", "provider"),)

def __str__(self) -> str:
return f"SCIM Group {self.group.name} to {self.provider.name}"
return f"SCIM Group {self.group_id} to {self.provider_id}"
2 changes: 1 addition & 1 deletion authentik/root/storages.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def _normalize_name(self, name):

return safe_join(self.location, connection.schema_name, name)
except ValueError:
raise SuspiciousOperation("Attempted access to '%s' denied." % name) from None
raise SuspiciousOperation(f"Attempted access to '{name}' denied.") from None

# This is a fix for https://github.com/jschneier/django-storages/pull/839
def url(self, name, parameters=None, expire=None, http_method=None):
Expand Down
2 changes: 1 addition & 1 deletion authentik/sources/oauth/clients/oauth2.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def get_access_token(self, **request_kwargs) -> dict[str, Any] | None:
access_token_url = self.source.source_type.access_token_url or ""
if self.source.source_type.urls_customizable and self.source.access_token_url:
access_token_url = self.source.access_token_url
response = self.session.request(
response = self.do_request(
"post", access_token_url, data=args, headers=self._default_headers, **request_kwargs
)
response.raise_for_status()
Expand Down
4 changes: 2 additions & 2 deletions authentik/sources/scim/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class Meta:
unique_together = (("id", "user", "source"),)

def __str__(self) -> str:
return f"SCIM User {self.user.username} to {self.source.name}"
return f"SCIM User {self.user_id} to {self.source_id}"


class SCIMSourceGroup(SerializerModel):
Expand All @@ -81,4 +81,4 @@ class Meta:
unique_together = (("id", "group", "source"),)

def __str__(self) -> str:
return f"SCIM Group {self.group.name} to {self.source.name}"
return f"SCIM Group {self.group_id} to {self.source_id}"
5 changes: 4 additions & 1 deletion authentik/sources/scim/signals.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
from django.db.models.signals import pre_delete, pre_save
from django.dispatch import receiver

from authentik.core.models import Token, TokenIntents, User, UserTypes
from authentik.core.models import USER_PATH_SYSTEM_PREFIX, Token, TokenIntents, User, UserTypes
from authentik.sources.scim.models import SCIMSource

USER_PATH_SOURCE_SCIM = USER_PATH_SYSTEM_PREFIX + "/sources/scim"


@receiver(pre_save, sender=SCIMSource)
def scim_source_pre_save(sender: type[Model], instance: SCIMSource, **_):
Expand All @@ -16,6 +18,7 @@ def scim_source_pre_save(sender: type[Model], instance: SCIMSource, **_):
username=identifier,
name=f"SCIM Source {instance.name} Service-Account",
type=UserTypes.INTERNAL_SERVICE_ACCOUNT,
path=USER_PATH_SOURCE_SCIM,
)
token = Token.objects.create(
user=user,
Expand Down
Loading

0 comments on commit 2a96900

Please sign in to comment.