Compare commits
	
		
			4 Commits
		
	
	
		
			version/20
			...
			root/more-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 06337283e8 | |||
| 5d28114a4b | |||
| b7c154ccd2 | |||
| d1fbf2ed65 | 
| @ -209,7 +209,7 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): | ||||
|     @extend_schema( | ||||
|         parameters=[ | ||||
|             OpenApiParameter( | ||||
|                 name="superuser_full_list", | ||||
|                 name="list_rbac", | ||||
|                 location=OpenApiParameter.QUERY, | ||||
|                 type=OpenApiTypes.BOOL, | ||||
|             ), | ||||
| @ -229,10 +229,8 @@ class ApplicationViewSet(UsedByMixin, ModelViewSet): | ||||
|         """Custom list method that checks Policy based access instead of guardian""" | ||||
|         should_cache = request.query_params.get("search", "") == "" | ||||
|  | ||||
|         superuser_full_list = ( | ||||
|             str(request.query_params.get("superuser_full_list", "false")).lower() == "true" | ||||
|         ) | ||||
|         if superuser_full_list and request.user.is_superuser: | ||||
|         list_rbac = str(request.query_params.get("list_rbac", "false")).lower() == "true" | ||||
|         if list_rbac: | ||||
|             return super().list(request) | ||||
|  | ||||
|         only_with_launch_url = str( | ||||
|  | ||||
| @ -4,7 +4,7 @@ from typing import Any | ||||
|  | ||||
| from django.utils.timezone import now | ||||
| from drf_spectacular.utils import OpenApiResponse, extend_schema, inline_serializer | ||||
| from guardian.shortcuts import assign_perm, get_anonymous_user | ||||
| from guardian.shortcuts import assign_perm | ||||
| from rest_framework.decorators import action | ||||
| from rest_framework.exceptions import ValidationError | ||||
| from rest_framework.fields import CharField | ||||
| @ -138,13 +138,8 @@ class TokenViewSet(UsedByMixin, ModelViewSet): | ||||
|     owner_field = "user" | ||||
|     rbac_allow_create_without_perm = True | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         user = self.request.user if self.request else get_anonymous_user() | ||||
|         if user.is_superuser: | ||||
|             return super().get_queryset() | ||||
|         return super().get_queryset().filter(user=user.pk) | ||||
|  | ||||
|     def perform_create(self, serializer: TokenSerializer): | ||||
|         # TODO: better permission check | ||||
|         if not self.request.user.is_superuser: | ||||
|             instance = serializer.save( | ||||
|                 user=self.request.user, | ||||
|  | ||||
							
								
								
									
										57
									
								
								authentik/core/migrations/0043_alter_user_options.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								authentik/core/migrations/0043_alter_user_options.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| # Generated by Django 5.0.10 on 2025-01-08 17:39 | ||||
|  | ||||
| from django.db import migrations | ||||
| from django.apps.registry import Apps | ||||
| from django.db.backends.base.schema import BaseDatabaseSchemaEditor | ||||
|  | ||||
|  | ||||
| def migrate_user_debug_attribute(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): | ||||
|     from django.apps import apps as real_apps | ||||
|     from django.contrib.auth.management import create_permissions | ||||
|  | ||||
|     db_alias = schema_editor.connection.alias | ||||
|  | ||||
|     User = apps.get_model("authentik_core", "User") | ||||
|     USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug" | ||||
|  | ||||
|     # Permissions are only created _after_ migrations are run | ||||
|     # - https://github.com/django/django/blob/43cdfa8b20e567a801b7d0a09ec67ddd062d5ea4/django/contrib/auth/apps.py#L19 | ||||
|     # - https://stackoverflow.com/a/72029063/1870445 | ||||
|     create_permissions(real_apps.get_app_config("authentik_core"), using=db_alias) | ||||
|  | ||||
|     Permission = apps.get_model("auth", "Permission") | ||||
|  | ||||
|     new_prem = Permission.objects.using(db_alias).get(codename="user_view_debug") | ||||
|  | ||||
|     db_alias = schema_editor.connection.alias | ||||
|     for user in User.objects.using(db_alias).filter( | ||||
|         **{f"attributes__{USER_ATTRIBUTE_DEBUG}": True} | ||||
|     ): | ||||
|         user.permissions.add(new_prem) | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
|  | ||||
|     dependencies = [ | ||||
|         ("authentik_core", "0042_authenticatedsession_authentik_c_expires_08251d_idx_and_more"), | ||||
|     ] | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterModelOptions( | ||||
|             name="user", | ||||
|             options={ | ||||
|                 "permissions": [ | ||||
|                     ("reset_user_password", "Reset Password"), | ||||
|                     ("impersonate", "Can impersonate other users"), | ||||
|                     ("assign_user_permissions", "Can assign permissions to users"), | ||||
|                     ("unassign_user_permissions", "Can unassign permissions from users"), | ||||
|                     ("preview_user", "Can preview user data sent to providers"), | ||||
|                     ("view_user_applications", "View applications the user has access to"), | ||||
|                     ("user_view_debug", "User receives additional details for error messages"), | ||||
|                 ], | ||||
|                 "verbose_name": "User", | ||||
|                 "verbose_name_plural": "Users", | ||||
|             }, | ||||
|         ), | ||||
|         migrations.RunPython(migrate_user_debug_attribute), | ||||
|     ] | ||||
| @ -41,7 +41,6 @@ from authentik.tenants.models import DEFAULT_TOKEN_DURATION, DEFAULT_TOKEN_LENGT | ||||
| from authentik.tenants.utils import get_current_tenant, get_unique_identifier | ||||
|  | ||||
| LOGGER = get_logger() | ||||
| USER_ATTRIBUTE_DEBUG = "goauthentik.io/user/debug" | ||||
| USER_ATTRIBUTE_GENERATED = "goauthentik.io/user/generated" | ||||
| USER_ATTRIBUTE_EXPIRES = "goauthentik.io/user/expires" | ||||
| USER_ATTRIBUTE_DELETE_ON_LOGOUT = "goauthentik.io/user/delete-on-logout" | ||||
| @ -282,6 +281,7 @@ class User(SerializerModel, GuardianUserMixin, AttributesMixin, AbstractUser): | ||||
|             ("unassign_user_permissions", _("Can unassign permissions from users")), | ||||
|             ("preview_user", _("Can preview user data sent to providers")), | ||||
|             ("view_user_applications", _("View applications the user has access to")), | ||||
|             ("user_view_debug", _("User receives additional details for error messages")), | ||||
|         ] | ||||
|         indexes = [ | ||||
|             models.Index(fields=["last_login"]), | ||||
|  | ||||
| @ -96,7 +96,7 @@ class EndpointViewSet(UsedByMixin, ModelViewSet): | ||||
|                 OpenApiTypes.STR, | ||||
|             ), | ||||
|             OpenApiParameter( | ||||
|                 name="superuser_full_list", | ||||
|                 name="list_rbac", | ||||
|                 location=OpenApiParameter.QUERY, | ||||
|                 type=OpenApiTypes.BOOL, | ||||
|             ), | ||||
| @ -110,8 +110,8 @@ class EndpointViewSet(UsedByMixin, ModelViewSet): | ||||
|         """List accessible endpoints""" | ||||
|         should_cache = request.GET.get("search", "") == "" | ||||
|  | ||||
|         superuser_full_list = str(request.GET.get("superuser_full_list", "false")).lower() == "true" | ||||
|         if superuser_full_list and request.user.is_superuser: | ||||
|         list_rbac = str(request.GET.get("list_rbac", "false")).lower() == "true" | ||||
|         if list_rbac: | ||||
|             return super().list(request) | ||||
|  | ||||
|         queryset = self._filter_queryset_for_list(self.get_queryset()) | ||||
|  | ||||
| @ -97,12 +97,9 @@ class FlowErrorChallenge(Challenge): | ||||
|         if not request or not error: | ||||
|             return | ||||
|         self.initial_data["request_id"] = request.request_id | ||||
|         from authentik.core.models import USER_ATTRIBUTE_DEBUG | ||||
|  | ||||
|         if request.user and request.user.is_authenticated: | ||||
|             if request.user.is_superuser or request.user.group_attributes(request).get( | ||||
|                 USER_ATTRIBUTE_DEBUG, False | ||||
|             ): | ||||
|             if request.user.has_perm("authentik_core.user_view_debug"): | ||||
|                 self.initial_data["error"] = str(error) | ||||
|                 self.initial_data["traceback"] = exception_to_string(error) | ||||
|  | ||||
|  | ||||
| @ -13,6 +13,7 @@ from paramiko.ssh_exception import SSHException | ||||
| from structlog.stdlib import get_logger | ||||
| from yaml import safe_dump | ||||
|  | ||||
| from authentik import __version__ | ||||
| from authentik.outposts.apps import MANAGED_OUTPOST | ||||
| from authentik.outposts.controllers.base import BaseClient, BaseController, ControllerException | ||||
| from authentik.outposts.docker_ssh import DockerInlineSSH, SSHManagedExternallyException | ||||
| @ -182,10 +183,16 @@ class DockerController(BaseController): | ||||
|         `outposts.container_image_base`, but fall back to known-good images""" | ||||
|         image = self.get_container_image() | ||||
|         try: | ||||
|             self.client.images.pull(image) | ||||
|         except DockerException:  # pragma: no cover | ||||
|             image = f"ghcr.io/goauthentik/{self.outpost.type}:latest" | ||||
|             self.client.images.pull(image) | ||||
|             # See if the image exists... | ||||
|             self.client.images.get(image) | ||||
|         except DockerException: | ||||
|             try: | ||||
|                 # ...otherwise try to pull it... | ||||
|                 self.client.images.pull(image) | ||||
|             except DockerException: | ||||
|                 # ...and as a fallback to that default to a sane standard | ||||
|                 image = f"ghcr.io/goauthentik/{self.outpost.type}:{__version__}" | ||||
|                 self.client.images.pull(image) | ||||
|         return image | ||||
|  | ||||
|     def _get_container(self) -> tuple[Container, bool]: | ||||
|  | ||||
| @ -7,7 +7,7 @@ from django.template.response import TemplateResponse | ||||
| from django.urls import reverse | ||||
| from django.utils.translation import gettext as _ | ||||
|  | ||||
| from authentik.core.models import USER_ATTRIBUTE_DEBUG | ||||
| from authentik.core.models import User | ||||
| from authentik.policies.types import PolicyResult | ||||
|  | ||||
|  | ||||
| @ -31,12 +31,11 @@ class AccessDeniedResponse(TemplateResponse): | ||||
|         if self.error_message: | ||||
|             context["error"] = self.error_message | ||||
|         # Only show policy result if user is authenticated and | ||||
|         # either superuser or has USER_ATTRIBUTE_DEBUG set | ||||
|         # has permissions to see them | ||||
|         if self.policy_result: | ||||
|             if self._request.user and self._request.user.is_authenticated: | ||||
|                 if self._request.user.is_superuser or self._request.user.group_attributes( | ||||
|                     self._request | ||||
|                 ).get(USER_ATTRIBUTE_DEBUG, False): | ||||
|                 user: User = self._request.user | ||||
|                 if user.has_perm("authentik_core.user_view_debug"): | ||||
|                     context["policy_result"] = self.policy_result | ||||
|         context["cancel"] = reverse("authentik_flows:cancel") | ||||
|         return context | ||||
|  | ||||
| @ -2,11 +2,8 @@ | ||||
|  | ||||
| from json import dumps | ||||
|  | ||||
| from django_filters.rest_framework import DjangoFilterBackend | ||||
| from guardian.utils import get_anonymous_user | ||||
| from rest_framework import mixins | ||||
| from rest_framework.fields import CharField, ListField, SerializerMethodField | ||||
| from rest_framework.filters import OrderingFilter, SearchFilter | ||||
| from rest_framework.viewsets import GenericViewSet | ||||
|  | ||||
| from authentik.core.api.used_by import UsedByMixin | ||||
| @ -66,17 +63,7 @@ class AuthorizationCodeViewSet( | ||||
|     serializer_class = ExpiringBaseGrantModelSerializer | ||||
|     filterset_fields = ["user", "provider"] | ||||
|     ordering = ["provider", "expires"] | ||||
|     filter_backends = [ | ||||
|         DjangoFilterBackend, | ||||
|         OrderingFilter, | ||||
|         SearchFilter, | ||||
|     ] | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         user = self.request.user if self.request else get_anonymous_user() | ||||
|         if user.is_superuser: | ||||
|             return super().get_queryset() | ||||
|         return super().get_queryset().filter(user=user.pk) | ||||
|     owner_field = "user" | ||||
|  | ||||
|  | ||||
| class RefreshTokenViewSet( | ||||
| @ -92,17 +79,7 @@ class RefreshTokenViewSet( | ||||
|     serializer_class = TokenModelSerializer | ||||
|     filterset_fields = ["user", "provider"] | ||||
|     ordering = ["provider", "expires"] | ||||
|     filter_backends = [ | ||||
|         DjangoFilterBackend, | ||||
|         OrderingFilter, | ||||
|         SearchFilter, | ||||
|     ] | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         user = self.request.user if self.request else get_anonymous_user() | ||||
|         if user.is_superuser: | ||||
|             return super().get_queryset() | ||||
|         return super().get_queryset().filter(user=user.pk) | ||||
|     owner_field = "user" | ||||
|  | ||||
|  | ||||
| class AccessTokenViewSet( | ||||
| @ -118,14 +95,4 @@ class AccessTokenViewSet( | ||||
|     serializer_class = TokenModelSerializer | ||||
|     filterset_fields = ["user", "provider"] | ||||
|     ordering = ["provider", "expires"] | ||||
|     filter_backends = [ | ||||
|         DjangoFilterBackend, | ||||
|         OrderingFilter, | ||||
|         SearchFilter, | ||||
|     ] | ||||
|  | ||||
|     def get_queryset(self): | ||||
|         user = self.request.user if self.request else get_anonymous_user() | ||||
|         if user.is_superuser: | ||||
|             return super().get_queryset() | ||||
|         return super().get_queryset().filter(user=user.pk) | ||||
|     owner_field = "user" | ||||
|  | ||||
| @ -6445,6 +6445,7 @@ | ||||
|                             "authentik_core.remove_user_from_group", | ||||
|                             "authentik_core.reset_user_password", | ||||
|                             "authentik_core.unassign_user_permissions", | ||||
|                             "authentik_core.user_view_debug", | ||||
|                             "authentik_core.view_application", | ||||
|                             "authentik_core.view_applicationentitlement", | ||||
|                             "authentik_core.view_authenticatedsession", | ||||
| @ -12694,6 +12695,7 @@ | ||||
|                             "authentik_core.remove_user_from_group", | ||||
|                             "authentik_core.reset_user_password", | ||||
|                             "authentik_core.unassign_user_permissions", | ||||
|                             "authentik_core.user_view_debug", | ||||
|                             "authentik_core.view_application", | ||||
|                             "authentik_core.view_applicationentitlement", | ||||
|                             "authentik_core.view_authenticatedsession", | ||||
| @ -13202,6 +13204,7 @@ | ||||
|                             "unassign_user_permissions", | ||||
|                             "preview_user", | ||||
|                             "view_user_applications", | ||||
|                             "user_view_debug", | ||||
|                             "add_user", | ||||
|                             "change_user", | ||||
|                             "delete_user", | ||||
|  | ||||
| @ -4,6 +4,9 @@ version = "2024.12.2" | ||||
| description = "" | ||||
| authors = ["authentik Team <hello@goauthentik.io>"] | ||||
|  | ||||
| [tool.poetry.requires-plugins] | ||||
| poetry-plugin-export = ">1.8" | ||||
|  | ||||
| [tool.black] | ||||
| line-length = 100 | ||||
| target-version = ['py312'] | ||||
|  | ||||
							
								
								
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								schema.yml
									
									
									
									
									
								
							| @ -3391,6 +3391,10 @@ paths: | ||||
|         name: group | ||||
|         schema: | ||||
|           type: string | ||||
|       - in: query | ||||
|         name: list_rbac | ||||
|         schema: | ||||
|           type: boolean | ||||
|       - in: query | ||||
|         name: meta_description | ||||
|         schema: | ||||
| @ -3439,10 +3443,6 @@ paths: | ||||
|         name: slug | ||||
|         schema: | ||||
|           type: string | ||||
|       - in: query | ||||
|         name: superuser_full_list | ||||
|         schema: | ||||
|           type: boolean | ||||
|       tags: | ||||
|       - core | ||||
|       security: | ||||
| @ -23204,6 +23204,10 @@ paths: | ||||
|       operationId: rac_endpoints_list | ||||
|       description: List accessible endpoints | ||||
|       parameters: | ||||
|       - in: query | ||||
|         name: list_rbac | ||||
|         schema: | ||||
|           type: boolean | ||||
|       - in: query | ||||
|         name: name | ||||
|         schema: | ||||
| @ -23234,10 +23238,6 @@ paths: | ||||
|         name: search | ||||
|         schema: | ||||
|           type: string | ||||
|       - in: query | ||||
|         name: superuser_full_list | ||||
|         schema: | ||||
|           type: boolean | ||||
|       tags: | ||||
|       - rac | ||||
|       security: | ||||
|  | ||||
| @ -66,7 +66,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>) | ||||
|     async apiEndpoint(): Promise<PaginatedResponse<Application>> { | ||||
|         return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({ | ||||
|             ...(await this.defaultEndpointConfig()), | ||||
|             superuserFullList: true, | ||||
|             listRbac: true, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -156,7 +156,7 @@ export class BrandForm extends ModelForm<Brand, string> { | ||||
|                             .fetchObjects=${async (query?: string): Promise<Application[]> => { | ||||
|                                 const args: CoreApplicationsListRequest = { | ||||
|                                     ordering: "name", | ||||
|                                     superuserFullList: true, | ||||
|                                     listRbac: true, | ||||
|                                 }; | ||||
|                                 if (query !== undefined) { | ||||
|                                     args.search = query; | ||||
|  | ||||
| @ -46,7 +46,7 @@ export class EndpointListPage extends Table<Endpoint> { | ||||
|         return new RacApi(DEFAULT_CONFIG).racEndpointsList({ | ||||
|             ...(await this.defaultEndpointConfig()), | ||||
|             provider: this.provider?.pk, | ||||
|             superuserFullList: true, | ||||
|             listRbac: true, | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	