Compare commits

...

4 Commits

Author SHA1 Message Date
06337283e8 fix migrations correctly
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-01-16 16:50:46 +01:00
5d28114a4b remove USER_ATTRIBUTE_DEBUG
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-01-16 15:49:05 +01:00
b7c154ccd2 dont always try to pull the image
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-01-16 15:49:05 +01:00
d1fbf2ed65 remove leftover code
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-01-16 15:49:03 +01:00
15 changed files with 102 additions and 76 deletions

View File

@ -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(

View File

@ -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,

View 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),
]

View File

@ -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"]),

View File

@ -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())

View File

@ -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)

View File

@ -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]:

View File

@ -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

View File

@ -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"

View File

@ -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",

View File

@ -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']

View File

@ -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:

View File

@ -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,
});
}

View File

@ -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;

View File

@ -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,
});
}