authentik
authentik copied to clipboard
website/docs: updates nginx doc
Details
Updates the nginx proxy config based on problem reports.
Checklist
If applicable
- [x] The documentation has been updated
- [x] The documentation has been formatted (
make website)
Deploy Preview for authentik-docs ready!
| Name | Link |
|---|---|
| Latest commit | c90456e53ddf5f15b8194ecb87fc080aa2a5a48c |
| Latest deploy log | https://app.netlify.com/projects/authentik-docs/deploys/684af1994b98ce0008ef1a06 |
| Deploy Preview | https://deploy-preview-15020--authentik-docs.netlify.app |
| Preview on mobile | Toggle QR Code...Use your smartphone camera to open QR code link. |
To edit notification comments on pull requests, go to your Netlify project configuration.
Deploy Preview for authentik-storybook canceled.
| Name | Link |
|---|---|
| Latest commit | c90456e53ddf5f15b8194ecb87fc080aa2a5a48c |
| Latest deploy log | https://app.netlify.com/projects/authentik-storybook/deploys/684af199a9597d00083ea7ff |
:x: 3 Tests Failed:
| Tests completed | Failed | Passed | Skipped |
|---|---|---|---|
| 1822 | 3 | 1819 | 2 |
View the top 2 failed test(s) by shortest run time
authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles::test_listStack Traces | 0.915s run time
self = <unittest.case._Outcome object at 0x7f2e03a9f000> test_case = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list> subTest = False @contextlib.contextmanager def testPartExecutor(self, test_case, subTest=False): old_success = self.success self.success = True try: > yield .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:58: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list> result = <TestCaseFunction test_list> def run(self, result=None): if result is None: result = self.defaultTestResult() startTestRun = getattr(result, 'startTestRun', None) stopTestRun = getattr(result, 'stopTestRun', None) if startTestRun is not None: startTestRun() else: stopTestRun = None result.startTest(self) try: testMethod = getattr(self, self._testMethodName) if (getattr(self.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False)): # If the class or method was skipped. skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or getattr(testMethod, '__unittest_skip_why__', '')) _addSkip(result, self, skip_why) return result expecting_failure = ( getattr(self, "__unittest_expecting_failure__", False) or getattr(testMethod, "__unittest_expecting_failure__", False) ) outcome = _Outcome(result) start_time = time.perf_counter() try: self._outcome = outcome with outcome.testPartExecutor(self): self._callSetUp() if outcome.success: outcome.expecting_failure = expecting_failure with outcome.testPartExecutor(self): > self._callTestMethod(testMethod) .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:651: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list> method = <bound method TestRBACPermissionRoles.test_list of <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list>> def _callTestMethod(self, method): > if method() is not None: .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:606: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list> def test_list(self): """Test list of all permissions""" self.client.force_login(self.superuser) inv = Invitation.objects.create( name=generate_id(), created_by=self.superuser, ) self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) > res = self.client.get(reverse("authentik_api:permissions-roles-list")) .../rbac/tests/test_api_permissions_roles.py:34: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> path = '.../rbac/permissions/roles/', data = None, follow = False extra = {} def get(self, path, data=None, follow=False, **extra): > response = super().get(path, data=data, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:292: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> path = '.../rbac/permissions/roles/', data = None, extra = {} r = {'QUERY_STRING': ''} def get(self, path, data=None, **extra): r = { 'QUERY_STRING': urlencode(data or {}, doseq=True), } if not data and '?' in path: # Fix to support old behavior where you have the arguments in the # url. See #1461. query_string = force_bytes(path.split('?')[1]) query_string = query_string.decode('iso-8859-1') r['QUERY_STRING'] = query_string r.update(extra) > return self.generic('GET', path, **r) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:209: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410>, method = 'GET' path = '.../rbac/permissions/roles/', data = '' content_type = 'application/octet-stream', secure = False extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': ''} def generic(self, method, path, data='', content_type='application/octet-stream', secure=False, **extra): # Include the CONTENT_TYPE, regardless of whether or not data is empty. if content_type is not None: extra['CONTENT_TYPE'] = str(content_type) > return super().generic( method, path, data, content_type, secure, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:237: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410>, method = 'GET' path = '.../rbac/permissions/roles/', data = b'' content_type = 'application/octet-stream', secure = False, headers = None query_params = None extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': ''} parsed = ParseResult(scheme='', netloc='', path='.../rbac/permissions/roles/', params='', query='', fragment='') r = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} query_string = '' def generic( self, method, path, data="", content_type="application/octet-stream", secure=False, *, headers=None, query_params=None, **extra, ): """Construct an arbitrary HTTP request.""" parsed = urlparse(str(path)) # path can be lazy data = force_bytes(data, settings.DEFAULT_CHARSET) r = { "PATH_INFO": self._get_path(parsed), "REQUEST_METHOD": method, "SERVER_PORT": "443" if secure else "80", "wsgi.url_scheme": "https" if secure else "http", } if data: r.update( { "CONTENT_LENGTH": str(len(data)), "CONTENT_TYPE": content_type, "wsgi.input": FakePayload(data), } ) if headers: extra.update(HttpHeaders.to_wsgi_names(headers)) if query_params: extra["QUERY_STRING"] = urlencode(query_params, doseq=True) r.update(extra) # If QUERY_STRING is absent or empty, we want to extract it from the URL. if not r.get("QUERY_STRING"): # WSGI requires latin-1 encoded strings. See get_path_info(). query_string = parsed[4].encode().decode("iso-8859-1") r["QUERY_STRING"] = query_string > return self.request(**r) .venv/lib/python3.13.../django/test/client.py:676: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): # Ensure that any credentials set get added to every request. kwargs.update(self._credentials) > return super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:289: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): > request = super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:241: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> request = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} environ = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_COOKIE': 'authentik_session=m8jhakkec5mc3rxpcrldkqpf9m48qr5h', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': '', ...} data = {'context': [[{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: <function csrf.<locals>....se/ske...">, <Template template_string="{% load static %}{%...">, <Template template_string="{% load i18n %}{% g...">]} on_template_render = functools.partial(<function store_rendered_templates at 0x7f2e0f7b1120>, {'templates': [<Template template_string="{% ...<IncludeNode: template=<FilterExpression '"base/header_js.html"'>>, <TextNode: '\n'>]>}, {'LANGUAGE_CODE': 'en-us'}]]}) signal_uid = 'template-render-139835553850368' exception_uid = 'request-exception-139835553850368' response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def request(self, **request): """ Make a generic request. Compose the environment dictionary and pass to the handler, return the result of the handler. Assume defaults for the query environment, which can be overridden using the arguments to the request. """ environ = self._base_environ(**request) # Curry a data dictionary into an instance of the template renderer # callback function. data = {} on_template_render = partial(store_rendered_templates, data) signal_uid = "template-render-%s" % id(request) signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) # Capture exceptions created by the handler. exception_uid = "request-exception-%s" % id(request) got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) try: response = self.handler(environ) finally: signals.template_rendered.disconnect(dispatch_uid=signal_uid) got_request_exception.disconnect(dispatch_uid=exception_uid) # Check for signaled exceptions. > self.check_exception(response) .venv/lib/python3.13.../django/test/client.py:1092: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077e410> response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def check_exception(self, response): """ Look for a signaled exception, clear the current context exception data, re-raise the signaled exception, and clear the signaled exception from the local cache. """ response.exc_info = self.exc_info if self.exc_info: _, exc_value, _ = self.exc_info self.exc_info = None if self.raise_request_exception: > raise exc_value .venv/lib/python3.13.../django/test/client.py:805: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/'> @wraps(get_response) def inner(request): try: > response = get_response(request) .venv/lib/python3.13.../core/handlers/exception.py:55: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.ForceAuthClientHandler object at 0x7f2e00de7050> request = <WSGIRequest: GET '.../rbac/permissions/roles/'> def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. """ response = None callback, callback_args, callback_kwargs = self.resolve_request(request) # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method( request, callback, callback_args, callback_kwargs ) if response: break if response is None: wrapped_callback = self.make_view_atomic(callback) # If it is an asynchronous view, run it in a subthread. if iscoroutinefunction(wrapped_callback): wrapped_callback = async_to_sync(wrapped_callback) try: > response = wrapped_callback(request, *callback_args, **callback_kwargs) .venv/lib/python3.13.../core/handlers/base.py:197: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/'>, args = () kwargs = {} current_scope = <Scope id=0x7f2e14e29ee0 name=None type=ScopeType.CURRENT> sentry_scope = <Scope id=0x7f2e14e2ace0 name=None type=ScopeType.ISOLATION> @functools.wraps(callback) def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() # set the active thread id to the handler thread for sync views # this isn't necessary for async views since that runs on main if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() with sentry_sdk.start_span( op=OP.VIEW_RENDER, name=request.resolver_match.view_name, origin=DjangoIntegration.origin, ): > return callback(request, *args, **kwargs) .venv/lib/python3.13.../integrations/django/views.py:94: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/'>, args = () kwargs = {} def _view_wrapper(request, *args, **kwargs): > return view_func(request, *args, **kwargs) .venv/lib/python3.13.../views/decorators/csrf.py:65: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/'>, args = () kwargs = {} self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> method = 'head', action = 'list' handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850>> def view(request, *args, **kwargs): self = cls(**initkwargs) if 'get' in actions and 'head' not in actions: actions['head'] = actions['get'] # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) self.request = request self.args = args self.kwargs = kwargs # And continue as usual > return self.dispatch(request, *args, **kwargs) .venv/lib/python3.13.../site-packages/rest_framework/viewsets.py:125: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/'> args = (), kwargs = {} handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: > response = self.handle_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:515: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> exc = MixedContentTypeError('Content type for given perms and klass differs') def handle_exception(self, exc): """ Handle any exception that occurs, by returning an appropriate response, or re-raising the error. """ if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)): # WWW-Authenticate header for 401 responses, else coerce to 403 auth_header = self.get_authenticate_header(self.request) if auth_header: exc.auth_header = auth_header else: exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() context = self.get_exception_handler_context() response = exception_handler(exc, context) if response is None: > self.raise_uncaught_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:475: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> exc = MixedContentTypeError('Content type for given perms and klass differs') def raise_uncaught_exception(self, exc): if settings.DEBUG: request = self.request renderer_format = getattr(request.accepted_renderer, 'format') use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') request.force_plaintext_errors(use_plaintext_traceback) > raise exc .venv/lib/python3.13............/site-packages/rest_framework/views.py:486: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/'> args = (), kwargs = {} handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed > response = handler(request, *args, **kwargs) .venv/lib/python3.13............/site-packages/rest_framework/views.py:512: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e063e2850> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/'> args = (), kwargs = {}, queryset = <QuerySet []> page = [<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created by 127 | pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ | view_invitation>] serializer = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) > return self.get_paginated_response(serializer.data) .venv/lib/python3.13.../site-packages/rest_framework/mixins.py:43: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() @property def data(self): > ret = super().data .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:797: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() @property def data(self): if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): msg = ( 'When a serializer is passed a `data` keyword argument you ' 'must call `.is_valid()` before attempting to access the ' 'serialized `.data` representation.\n' 'You should either call `.is_valid()` first, ' 'or access `.initial_data` instead.' ) raise AssertionError(msg) if not hasattr(self, '_data'): if self.instance is not None and not getattr(self, '_errors', None): > self._data = self.to_representation(self.instance) .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:251: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() data = [<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created by 127 | pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ | view_invitation>] def to_representation(self, data): """ List of object instances -> List of dicts of primitive datatypes. """ # Dealing with nested relationships, data can be a Manager, # so, first get a queryset from the Manager if needed iterable = data.all() if isinstance(data, models.manager.BaseManager) else data return [ > self.child.to_representation(item) for item in iterable ] .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:716: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() instance = <GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created by 127 | pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ | view_invitation> def to_representation(self, instance): """ Object instance -> Dict of primitive datatypes. """ ret = {} fields = self._readable_fields for field in fields: try: attribute = field.get_attribute(instance) except SkipField: continue # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: ret[field.field_name] = None else: > ret[field.field_name] = field.to_representation(attribute) .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:540: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = SerializerMethodField() value = <GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created by 127 | pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ | view_invitation> def to_representation(self, value): method = getattr(self.parent, self.method_name) > return method(value) .venv/lib/python3.13.../site-packages/rest_framework/fields.py:1870: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() instance = <GroupObjectPermission: Invitation 88a4ecf3-63d6-4d89-add8-5634156b6bfc created by 127 | pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ | view_invitation> def get_object_description(self, instance: GroupObjectPermission) -> str | None: """Get model description from attached model. This operation takes at least one additional query, and the description is only shown if the user/role has the view_ permission on the object""" app_label = instance.content_type.app_label model = instance.content_type.model try: model_class = apps.get_model(app_label, model) except LookupError: return None > objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class) .../rbac/api/rbac_roles.py:55: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ group = <Group: pizPibjMQuNL8UWlCTCNlbwlLxoxv1vfm6CnESmQ> perms = ['authentik_stages_invitation.view_invitation'] klass = <class 'authentik.stages.invitation.models.Invitation'> any_perm = False, accept_global_perms = True def get_objects_for_group( group: Group, perms: Union[str, list[str]], klass: Union[Model, Manager, QuerySet, None] = None, any_perm: bool = False, accept_global_perms: bool = True, ) -> QuerySet: """Get objects that a group has *all* the supplied permissions for. Parameters: group (Group): `Group` instance for which objects would be returned. perms (str | list[str]): permission(s) which should be checked. If `klass` parameter is not given, those should be full permission names rather than only codenames (i.e. `auth.change_user`). If more than one permission is present within sequence, their content type **must** be the same or `MixedContentTypeError` exception is raised. klass (Model | Manager | QuerySet): If not provided this parameter is computed based on given `params`. any_perm (bool): Whether any of permission in sequence is accepted. accept_global_perms (bool): Whether global permissions are taken into account. If `any_perm` is `False`, then the intersection of matching objects based on global and object-based permissionsis returned. Returns: objects for which a given `group` has *all* permissions in `perms`. Raisess: MixedContentTypeError: when computed content type for `perms` and/or `klass` clashes. WrongAppError: if cannot compute app label for given `perms`/`klass`. Example: Let's assume we have a `Task` model belonging to the `tasker` app with the default add_task, change_task and delete_task permissions provided by Django: ```shell >>> from guardian.shortcuts import get_objects_for_group >>> from tasker import Task # noqa >>> group = Group.objects.create('some group') >>> task = Task.objects.create('some task') >>> get_objects_for_group(group, 'tasker.add_task') [] >>> from guardian.shortcuts import assign_perm >>> assign_perm('tasker.add_task', group, task) >>> get_objects_for_group(group, 'tasker.add_task') [<Task some task>] # The permission string can also be an iterable. Continuing with the previous example: >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [] >>> assign_perm('tasker.delete_task', group, task) >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [<Task some task>] # Global permissions assigned to the group are also taken into account. Continuing with previous example: >>> task_other = Task.objects.create('other task') >>> assign_perm('tasker.change_task', group) >>> get_objects_for_group(group, ['tasker.change_task']) [<Task some task>, <Task other task>] >>> get_objects_for_group(group, ['tasker.change_task'], accept_global_perms=False) [<Task some task>] ``` """ if isinstance(perms, str): perms = [perms] ctype = None app_label = None codenames = set() # Compute the codenames and set ctype if possible for perm in perms: if "." in perm: new_app_label, codename = perm.split(".", 1) if app_label is not None and app_label != new_app_label: raise MixedContentTypeError("Given perms must have same app " "label (%s != %s)" % (app_label, new_app_label)) else: app_label = new_app_label else: codename = perm codenames.add(codename) if app_label is not None: new_ctype = _get_ct_cached(app_label, codename) if ctype is not None and ctype != new_ctype: raise MixedContentTypeError("ContentType was once computed " "to be %s and another one %s" % (ctype, new_ctype)) else: ctype = new_ctype # Compute queryset and ctype if still missing if ctype is None and klass is not None: queryset = _get_queryset(klass) ctype = get_content_type(queryset.model) elif ctype is not None and klass is None: queryset = _get_queryset(ctype.model_class()) elif klass is None: raise WrongAppError("Cannot determine content type") else: queryset = _get_queryset(klass) if ctype != get_content_type(queryset.model): > raise MixedContentTypeError( "Content type for given perms and " "klass differs" ) E guardian.exceptions.MixedContentTypeError: Content type for given perms and klass differs .venv/lib/python3.13.../site-packages/guardian/shortcuts.py:790: MixedContentTypeError
authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles::test_list_roleStack Traces | 0.978s run time
self = <unittest.case._Outcome object at 0x7f2e004aae40> test_case = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list_role> subTest = False @contextlib.contextmanager def testPartExecutor(self, test_case, subTest=False): old_success = self.success self.success = True try: > yield .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:58: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list_role> result = <TestCaseFunction test_list_role> def run(self, result=None): if result is None: result = self.defaultTestResult() startTestRun = getattr(result, 'startTestRun', None) stopTestRun = getattr(result, 'stopTestRun', None) if startTestRun is not None: startTestRun() else: stopTestRun = None result.startTest(self) try: testMethod = getattr(self, self._testMethodName) if (getattr(self.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False)): # If the class or method was skipped. skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or getattr(testMethod, '__unittest_skip_why__', '')) _addSkip(result, self, skip_why) return result expecting_failure = ( getattr(self, "__unittest_expecting_failure__", False) or getattr(testMethod, "__unittest_expecting_failure__", False) ) outcome = _Outcome(result) start_time = time.perf_counter() try: self._outcome = outcome with outcome.testPartExecutor(self): self._callSetUp() if outcome.success: outcome.expecting_failure = expecting_failure with outcome.testPartExecutor(self): > self._callTestMethod(testMethod) .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:651: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list_role> method = <bound method TestRBACPermissionRoles.test_list_role of <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list_role>> def _callTestMethod(self, method): > if method() is not None: .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:606: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.tests.test_api_permissions_roles.TestRBACPermissionRoles testMethod=test_list_role> def test_list_role(self): """Test list of all permissions""" self.client.force_login(self.superuser) inv = Invitation.objects.create( name=generate_id(), created_by=self.superuser, ) self.role.assign_permission("authentik_stages_invitation.view_invitation", obj=inv) > res = self.client.get( reverse("authentik_api:permissions-roles-list") + f"?uuid={self.role.pk}" ) .../rbac/tests/test_api_permissions_roles.py:45: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> path = '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974' data = None, follow = False, extra = {} def get(self, path, data=None, follow=False, **extra): > response = super().get(path, data=data, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:292: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> path = '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974' data = None, extra = {} r = {'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974'} query_string = 'uuid=5389eded-f6af-4187-865d-4c968e48c974' def get(self, path, data=None, **extra): r = { 'QUERY_STRING': urlencode(data or {}, doseq=True), } if not data and '?' in path: # Fix to support old behavior where you have the arguments in the # url. See #1461. query_string = force_bytes(path.split('?')[1]) query_string = query_string.decode('iso-8859-1') r['QUERY_STRING'] = query_string r.update(extra) > return self.generic('GET', path, **r) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:209: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80>, method = 'GET' path = '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974' data = '', content_type = 'application/octet-stream', secure = False extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974'} def generic(self, method, path, data='', content_type='application/octet-stream', secure=False, **extra): # Include the CONTENT_TYPE, regardless of whether or not data is empty. if content_type is not None: extra['CONTENT_TYPE'] = str(content_type) > return super().generic( method, path, data, content_type, secure, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:237: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80>, method = 'GET' path = '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974' data = b'', content_type = 'application/octet-stream', secure = False headers = None, query_params = None extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974'} parsed = ParseResult(scheme='', netloc='', path='.../rbac/permissions/roles/', params='', query='uuid=5389eded-f6af-4187-865d-4c968e48c974', fragment='') r = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974', 'REQUEST_METHOD': 'GET', ...} def generic( self, method, path, data="", content_type="application/octet-stream", secure=False, *, headers=None, query_params=None, **extra, ): """Construct an arbitrary HTTP request.""" parsed = urlparse(str(path)) # path can be lazy data = force_bytes(data, settings.DEFAULT_CHARSET) r = { "PATH_INFO": self._get_path(parsed), "REQUEST_METHOD": method, "SERVER_PORT": "443" if secure else "80", "wsgi.url_scheme": "https" if secure else "http", } if data: r.update( { "CONTENT_LENGTH": str(len(data)), "CONTENT_TYPE": content_type, "wsgi.input": FakePayload(data), } ) if headers: extra.update(HttpHeaders.to_wsgi_names(headers)) if query_params: extra["QUERY_STRING"] = urlencode(query_params, doseq=True) r.update(extra) # If QUERY_STRING is absent or empty, we want to extract it from the URL. if not r.get("QUERY_STRING"): # WSGI requires latin-1 encoded strings. See get_path_info(). query_string = parsed[4].encode().decode("iso-8859-1") r["QUERY_STRING"] = query_string > return self.request(**r) .venv/lib/python3.13.../django/test/client.py:676: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): # Ensure that any credentials set get added to every request. kwargs.update(self._credentials) > return super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:289: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): > request = super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:241: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> request = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974', 'REQUEST_METHOD': 'GET', ...} environ = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_COOKIE': 'authentik_session=6n1sqlfg9jlofxbff7yb4uzd0tq2sm6o', 'PATH_INFO': '.../rbac/permissions/roles/', 'QUERY_STRING': 'uuid=5389eded-f6af-4187-865d-4c968e48c974', ...} data = {'context': [[{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: <function csrf.<locals>....se/ske...">, <Template template_string="{% load static %}{%...">, <Template template_string="{% load i18n %}{% g...">]} on_template_render = functools.partial(<function store_rendered_templates at 0x7f2e0f7b1120>, {'templates': [<Template template_string="{% ...<IncludeNode: template=<FilterExpression '"base/header_js.html"'>>, <TextNode: '\n'>]>}, {'LANGUAGE_CODE': 'en-us'}]]}) signal_uid = 'template-render-139835559060224' exception_uid = 'request-exception-139835559060224' response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def request(self, **request): """ Make a generic request. Compose the environment dictionary and pass to the handler, return the result of the handler. Assume defaults for the query environment, which can be overridden using the arguments to the request. """ environ = self._base_environ(**request) # Curry a data dictionary into an instance of the template renderer # callback function. data = {} on_template_render = partial(store_rendered_templates, data) signal_uid = "template-render-%s" % id(request) signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) # Capture exceptions created by the handler. exception_uid = "request-exception-%s" % id(request) got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) try: response = self.handler(environ) finally: signals.template_rendered.disconnect(dispatch_uid=signal_uid) got_request_exception.disconnect(dispatch_uid=exception_uid) # Check for signaled exceptions. > self.check_exception(response) .venv/lib/python3.13.../django/test/client.py:1092: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e0077dc80> response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def check_exception(self, response): """ Look for a signaled exception, clear the current context exception data, re-raise the signaled exception, and clear the signaled exception from the local cache. """ response.exc_info = self.exc_info if self.exc_info: _, exc_value, _ = self.exc_info self.exc_info = None if self.raise_request_exception: > raise exc_value .venv/lib/python3.13.../django/test/client.py:805: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> @wraps(get_response) def inner(request): try: > response = get_response(request) .venv/lib/python3.13.../core/handlers/exception.py:55: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.ForceAuthClientHandler object at 0x7f2e00a881d0> request = <WSGIRequest: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. """ response = None callback, callback_args, callback_kwargs = self.resolve_request(request) # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method( request, callback, callback_args, callback_kwargs ) if response: break if response is None: wrapped_callback = self.make_view_atomic(callback) # If it is an asynchronous view, run it in a subthread. if iscoroutinefunction(wrapped_callback): wrapped_callback = async_to_sync(wrapped_callback) try: > response = wrapped_callback(request, *callback_args, **callback_kwargs) .venv/lib/python3.13.../core/handlers/base.py:197: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {} current_scope = <Scope id=0x7f2e14e29ee0 name=None type=ScopeType.CURRENT> sentry_scope = <Scope id=0x7f2e14e2ace0 name=None type=ScopeType.ISOLATION> @functools.wraps(callback) def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() # set the active thread id to the handler thread for sync views # this isn't necessary for async views since that runs on main if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() with sentry_sdk.start_span( op=OP.VIEW_RENDER, name=request.resolver_match.view_name, origin=DjangoIntegration.origin, ): > return callback(request, *args, **kwargs) .venv/lib/python3.13.../integrations/django/views.py:94: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {} def _view_wrapper(request, *args, **kwargs): > return view_func(request, *args, **kwargs) .venv/lib/python3.13.../views/decorators/csrf.py:65: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {} self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> method = 'head', action = 'list' handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90>> def view(request, *args, **kwargs): self = cls(**initkwargs) if 'get' in actions and 'head' not in actions: actions['head'] = actions['get'] # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) self.request = request self.args = args self.kwargs = kwargs # And continue as usual > return self.dispatch(request, *args, **kwargs) .venv/lib/python3.13.../site-packages/rest_framework/viewsets.py:125: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {} handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: > response = self.handle_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:515: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> exc = MixedContentTypeError('Content type for given perms and klass differs') def handle_exception(self, exc): """ Handle any exception that occurs, by returning an appropriate response, or re-raising the error. """ if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)): # WWW-Authenticate header for 401 responses, else coerce to 403 auth_header = self.get_authenticate_header(self.request) if auth_header: exc.auth_header = auth_header else: exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() context = self.get_exception_handler_context() response = exception_handler(exc, context) if response is None: > self.raise_uncaught_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:475: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> exc = MixedContentTypeError('Content type for given perms and klass differs') def raise_uncaught_exception(self, exc): if settings.DEBUG: request = self.request renderer_format = getattr(request.accepted_renderer, 'format') use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') request.force_plaintext_errors(use_plaintext_traceback) > raise exc .venv/lib/python3.13............/site-packages/rest_framework/views.py:486: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {} handler = <bound method ListModelMixin.list of <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed > response = handler(request, *args, **kwargs) .venv/lib/python3.13............/site-packages/rest_framework/views.py:512: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.rbac.api.rbac_roles.RolePermissionViewSet object at 0x7f2e03d72f90> request = <rest_framework.request.Request: GET '.../rbac/permissions/roles/?uuid=5389eded-f6af-4187-865d-4c968e48c974'> args = (), kwargs = {}, queryset = <QuerySet []> page = [<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created by 125 | OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh | view_invitation>] serializer = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() def list(self, request, *args, **kwargs): queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) > return self.get_paginated_response(serializer.data) .venv/lib/python3.13.../site-packages/rest_framework/mixins.py:43: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() @property def data(self): > ret = super().data .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:797: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() @property def data(self): if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): msg = ( 'When a serializer is passed a `data` keyword argument you ' 'must call `.is_valid()` before attempting to access the ' 'serialized `.data` representation.\n' 'You should either call `.is_valid()` first, ' 'or access `.initial_data` instead.' ) raise AssertionError(msg) if not hasattr(self, '_data'): if self.instance is not None and not getattr(self, '_errors', None): > self._data = self.to_representation(self.instance) .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:251: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() data = [<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created by 125 | OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh | view_invitation>] def to_representation(self, data): """ List of object instances -> List of dicts of primitive datatypes. """ # Dealing with nested relationships, data can be a Manager, # so, first get a queryset from the Manager if needed iterable = data.all() if isinstance(data, models.manager.BaseManager) else data return [ > self.child.to_representation(item) for item in iterable ] .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:716: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() instance = <GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created by 125 | OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh | view_invitation> def to_representation(self, instance): """ Object instance -> Dict of primitive datatypes. """ ret = {} fields = self._readable_fields for field in fields: try: attribute = field.get_attribute(instance) except SkipField: continue # We skip `to_representation` for `None` values so that fields do # not have to explicitly deal with that case. # # For related fields with `use_pk_only_optimization` we need to # resolve the pk value. check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute if check_for_none is None: ret[field.field_name] = None else: > ret[field.field_name] = field.to_representation(attribute) .venv/lib/python3.13............/site-packages/rest_framework/serializers.py:540: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = SerializerMethodField() value = <GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created by 125 | OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh | view_invitation> def to_representation(self, value): method = getattr(self.parent, self.method_name) > return method(value) .venv/lib/python3.13.../site-packages/rest_framework/fields.py:1870: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = ExtraRoleObjectPermissionSerializer([<GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created b... = SerializerMethodField() model_verbose = SerializerMethodField() object_description = SerializerMethodField() instance = <GroupObjectPermission: Invitation 325d7bda-4e83-4e16-adf0-d9a9f6a36ba6 created by 125 | OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh | view_invitation> def get_object_description(self, instance: GroupObjectPermission) -> str | None: """Get model description from attached model. This operation takes at least one additional query, and the description is only shown if the user/role has the view_ permission on the object""" app_label = instance.content_type.app_label model = instance.content_type.model try: model_class = apps.get_model(app_label, model) except LookupError: return None > objects = get_objects_for_group(instance.group, f"{app_label}.view_{model}", model_class) .../rbac/api/rbac_roles.py:55: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ group = <Group: OeyCjy3dohMsj1X81hxTzShrPBITJzwzNuKRLYYh> perms = ['authentik_stages_invitation.view_invitation'] klass = <class 'authentik.stages.invitation.models.Invitation'> any_perm = False, accept_global_perms = True def get_objects_for_group( group: Group, perms: Union[str, list[str]], klass: Union[Model, Manager, QuerySet, None] = None, any_perm: bool = False, accept_global_perms: bool = True, ) -> QuerySet: """Get objects that a group has *all* the supplied permissions for. Parameters: group (Group): `Group` instance for which objects would be returned. perms (str | list[str]): permission(s) which should be checked. If `klass` parameter is not given, those should be full permission names rather than only codenames (i.e. `auth.change_user`). If more than one permission is present within sequence, their content type **must** be the same or `MixedContentTypeError` exception is raised. klass (Model | Manager | QuerySet): If not provided this parameter is computed based on given `params`. any_perm (bool): Whether any of permission in sequence is accepted. accept_global_perms (bool): Whether global permissions are taken into account. If `any_perm` is `False`, then the intersection of matching objects based on global and object-based permissionsis returned. Returns: objects for which a given `group` has *all* permissions in `perms`. Raisess: MixedContentTypeError: when computed content type for `perms` and/or `klass` clashes. WrongAppError: if cannot compute app label for given `perms`/`klass`. Example: Let's assume we have a `Task` model belonging to the `tasker` app with the default add_task, change_task and delete_task permissions provided by Django: ```shell >>> from guardian.shortcuts import get_objects_for_group >>> from tasker import Task # noqa >>> group = Group.objects.create('some group') >>> task = Task.objects.create('some task') >>> get_objects_for_group(group, 'tasker.add_task') [] >>> from guardian.shortcuts import assign_perm >>> assign_perm('tasker.add_task', group, task) >>> get_objects_for_group(group, 'tasker.add_task') [<Task some task>] # The permission string can also be an iterable. Continuing with the previous example: >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [] >>> assign_perm('tasker.delete_task', group, task) >>> get_objects_for_group(group, ['tasker.add_task', 'tasker.delete_task']) [<Task some task>] # Global permissions assigned to the group are also taken into account. Continuing with previous example: >>> task_other = Task.objects.create('other task') >>> assign_perm('tasker.change_task', group) >>> get_objects_for_group(group, ['tasker.change_task']) [<Task some task>, <Task other task>] >>> get_objects_for_group(group, ['tasker.change_task'], accept_global_perms=False) [<Task some task>] ``` """ if isinstance(perms, str): perms = [perms] ctype = None app_label = None codenames = set() # Compute the codenames and set ctype if possible for perm in perms: if "." in perm: new_app_label, codename = perm.split(".", 1) if app_label is not None and app_label != new_app_label: raise MixedContentTypeError("Given perms must have same app " "label (%s != %s)" % (app_label, new_app_label)) else: app_label = new_app_label else: codename = perm codenames.add(codename) if app_label is not None: new_ctype = _get_ct_cached(app_label, codename) if ctype is not None and ctype != new_ctype: raise MixedContentTypeError("ContentType was once computed " "to be %s and another one %s" % (ctype, new_ctype)) else: ctype = new_ctype # Compute queryset and ctype if still missing if ctype is None and klass is not None: queryset = _get_queryset(klass) ctype = get_content_type(queryset.model) elif ctype is not None and klass is None: queryset = _get_queryset(ctype.model_class()) elif klass is None: raise WrongAppError("Cannot determine content type") else: queryset = _get_queryset(klass) if ctype != get_content_type(queryset.model): > raise MixedContentTypeError( "Content type for given perms and " "klass differs" ) E guardian.exceptions.MixedContentTypeError: Content type for given perms and klass differs .venv/lib/python3.13.../site-packages/guardian/shortcuts.py:790: MixedContentTypeError
View the full list of 1 :snowflake: flaky tests
authentik.core.tests.test_source_api.TestSourceAPI::test_builtin_source_used_byFlake rate in main: 20.00% (Passed 20 times, Failed 5 times)
Stack Traces | 0.67s run time
self = <unittest.case._Outcome object at 0x7f2e0556eba0> test_case = <authentik.core.tests.test_source_api.TestSourceAPI testMethod=test_builtin_source_used_by> subTest = False @contextlib.contextmanager def testPartExecutor(self, test_case, subTest=False): old_success = self.success self.success = True try: > yield .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:58: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.tests.test_source_api.TestSourceAPI testMethod=test_builtin_source_used_by> result = <TestCaseFunction test_builtin_source_used_by> def run(self, result=None): if result is None: result = self.defaultTestResult() startTestRun = getattr(result, 'startTestRun', None) stopTestRun = getattr(result, 'stopTestRun', None) if startTestRun is not None: startTestRun() else: stopTestRun = None result.startTest(self) try: testMethod = getattr(self, self._testMethodName) if (getattr(self.__class__, "__unittest_skip__", False) or getattr(testMethod, "__unittest_skip__", False)): # If the class or method was skipped. skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or getattr(testMethod, '__unittest_skip_why__', '')) _addSkip(result, self, skip_why) return result expecting_failure = ( getattr(self, "__unittest_expecting_failure__", False) or getattr(testMethod, "__unittest_expecting_failure__", False) ) outcome = _Outcome(result) start_time = time.perf_counter() try: self._outcome = outcome with outcome.testPartExecutor(self): self._callSetUp() if outcome.success: outcome.expecting_failure = expecting_failure with outcome.testPartExecutor(self): > self._callTestMethod(testMethod) .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:651: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.tests.test_source_api.TestSourceAPI testMethod=test_builtin_source_used_by> method = <bound method TestSourceAPI.test_builtin_source_used_by of <authentik.core.tests.test_source_api.TestSourceAPI testMethod=test_builtin_source_used_by>> def _callTestMethod(self, method): > if method() is not None: .../hostedtoolcache/Python/3.13.4........./x64/lib/python3.13/unittest/case.py:606: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.tests.test_source_api.TestSourceAPI testMethod=test_builtin_source_used_by> def test_builtin_source_used_by(self): """Test Providers's types endpoint""" apps.get_app_config("authentik_core").source_inbuilt() > response = self.client.get( reverse("authentik_api:source-used-by", kwargs={"slug": "authentik-built-in"}), ) .../core/tests/test_source_api.py:16: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> path = '.../all/authentik-built-in/used_by/', data = None follow = False, extra = {} def get(self, path, data=None, follow=False, **extra): > response = super().get(path, data=data, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:292: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> path = '.../all/authentik-built-in/used_by/', data = None extra = {}, r = {'QUERY_STRING': ''} def get(self, path, data=None, **extra): r = { 'QUERY_STRING': urlencode(data or {}, doseq=True), } if not data and '?' in path: # Fix to support old behavior where you have the arguments in the # url. See #1461. query_string = force_bytes(path.split('?')[1]) query_string = query_string.decode('iso-8859-1') r['QUERY_STRING'] = query_string r.update(extra) > return self.generic('GET', path, **r) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:209: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410>, method = 'GET' path = '.../all/authentik-built-in/used_by/', data = '' content_type = 'application/octet-stream', secure = False extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': ''} def generic(self, method, path, data='', content_type='application/octet-stream', secure=False, **extra): # Include the CONTENT_TYPE, regardless of whether or not data is empty. if content_type is not None: extra['CONTENT_TYPE'] = str(content_type) > return super().generic( method, path, data, content_type, secure, **extra) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:237: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410>, method = 'GET' path = '.../all/authentik-built-in/used_by/', data = b'' content_type = 'application/octet-stream', secure = False, headers = None query_params = None extra = {'CONTENT_TYPE': 'application/octet-stream', 'QUERY_STRING': ''} parsed = ParseResult(scheme='', netloc='', path='.../all/authentik-built-in/used_by/', params='', query='', fragment='') r = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../all/authentik-built-in/used_by/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} query_string = '' def generic( self, method, path, data="", content_type="application/octet-stream", secure=False, *, headers=None, query_params=None, **extra, ): """Construct an arbitrary HTTP request.""" parsed = urlparse(str(path)) # path can be lazy data = force_bytes(data, settings.DEFAULT_CHARSET) r = { "PATH_INFO": self._get_path(parsed), "REQUEST_METHOD": method, "SERVER_PORT": "443" if secure else "80", "wsgi.url_scheme": "https" if secure else "http", } if data: r.update( { "CONTENT_LENGTH": str(len(data)), "CONTENT_TYPE": content_type, "wsgi.input": FakePayload(data), } ) if headers: extra.update(HttpHeaders.to_wsgi_names(headers)) if query_params: extra["QUERY_STRING"] = urlencode(query_params, doseq=True) r.update(extra) # If QUERY_STRING is absent or empty, we want to extract it from the URL. if not r.get("QUERY_STRING"): # WSGI requires latin-1 encoded strings. See get_path_info(). query_string = parsed[4].encode().decode("iso-8859-1") r["QUERY_STRING"] = query_string > return self.request(**r) .venv/lib/python3.13.../django/test/client.py:676: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../all/authentik-built-in/used_by/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): # Ensure that any credentials set get added to every request. kwargs.update(self._credentials) > return super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:289: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> kwargs = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../all/authentik-built-in/used_by/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} def request(self, **kwargs): > request = super().request(**kwargs) .venv/lib/python3.13.............../site-packages/rest_framework/test.py:241: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> request = {'CONTENT_TYPE': 'application/octet-stream', 'PATH_INFO': '.../all/authentik-built-in/used_by/', 'QUERY_STRING': '', 'REQUEST_METHOD': 'GET', ...} environ = {'CONTENT_TYPE': 'application/octet-stream', 'HTTP_COOKIE': 'authentik_session=jhzqq0obgzh6or6pov18rhpnhdel27hq', 'PATH_INFO': '.../all/authentik-built-in/used_by/', 'QUERY_STRING': '', ...} data = {'context': [[{'True': True, 'False': False, 'None': None}, {'csrf_token': <SimpleLazyObject: <function csrf.<locals>....se/ske...">, <Template template_string="{% load static %}{%...">, <Template template_string="{% load i18n %}{% g...">]} on_template_render = functools.partial(<function store_rendered_templates at 0x7f2e0f7b1120>, {'templates': [<Template template_string="{% ...<IncludeNode: template=<FilterExpression '"base/header_js.html"'>>, <TextNode: '\n'>]>}, {'LANGUAGE_CODE': 'en-us'}]]}) signal_uid = 'template-render-139835559988800' exception_uid = 'request-exception-139835559988800' response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def request(self, **request): """ Make a generic request. Compose the environment dictionary and pass to the handler, return the result of the handler. Assume defaults for the query environment, which can be overridden using the arguments to the request. """ environ = self._base_environ(**request) # Curry a data dictionary into an instance of the template renderer # callback function. data = {} on_template_render = partial(store_rendered_templates, data) signal_uid = "template-render-%s" % id(request) signals.template_rendered.connect(on_template_render, dispatch_uid=signal_uid) # Capture exceptions created by the handler. exception_uid = "request-exception-%s" % id(request) got_request_exception.connect(self.store_exc_info, dispatch_uid=exception_uid) try: response = self.handler(environ) finally: signals.template_rendered.disconnect(dispatch_uid=signal_uid) got_request_exception.disconnect(dispatch_uid=exception_uid) # Check for signaled exceptions. > self.check_exception(response) .venv/lib/python3.13.../django/test/client.py:1092: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.APIClient object at 0x7f2e00362410> response = <ServerErrorTemplateResponse status_code=500, "text/html; charset=utf-8"> def check_exception(self, response): """ Look for a signaled exception, clear the current context exception data, re-raise the signaled exception, and clear the signaled exception from the local cache. """ response.exc_info = self.exc_info if self.exc_info: _, exc_value, _ = self.exc_info self.exc_info = None if self.raise_request_exception: > raise exc_value .venv/lib/python3.13.../django/test/client.py:805: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../all/authentik-built-in/used_by/'> @wraps(get_response) def inner(request): try: > response = get_response(request) .venv/lib/python3.13.../core/handlers/exception.py:55: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <rest_framework.test.ForceAuthClientHandler object at 0x7f2e039653d0> request = <WSGIRequest: GET '.../all/authentik-built-in/used_by/'> def _get_response(self, request): """ Resolve and call the view, then apply view, exception, and template_response middleware. This method is everything that happens inside the request/response middleware. """ response = None callback, callback_args, callback_kwargs = self.resolve_request(request) # Apply view middleware for middleware_method in self._view_middleware: response = middleware_method( request, callback, callback_args, callback_kwargs ) if response: break if response is None: wrapped_callback = self.make_view_atomic(callback) # If it is an asynchronous view, run it in a subthread. if iscoroutinefunction(wrapped_callback): wrapped_callback = async_to_sync(wrapped_callback) try: > response = wrapped_callback(request, *callback_args, **callback_kwargs) .venv/lib/python3.13.../core/handlers/base.py:197: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} current_scope = <Scope id=0x7f2e14e29ee0 name=None type=ScopeType.CURRENT> sentry_scope = <Scope id=0x7f2e14e2ace0 name=None type=ScopeType.ISOLATION> @functools.wraps(callback) def sentry_wrapped_callback(request, *args, **kwargs): # type: (Any, *Any, **Any) -> Any current_scope = sentry_sdk.get_current_scope() if current_scope.transaction is not None: current_scope.transaction.update_active_thread() sentry_scope = sentry_sdk.get_isolation_scope() # set the active thread id to the handler thread for sync views # this isn't necessary for async views since that runs on main if sentry_scope.profile is not None: sentry_scope.profile.update_active_thread_id() with sentry_sdk.start_span( op=OP.VIEW_RENDER, name=request.resolver_match.view_name, origin=DjangoIntegration.origin, ): > return callback(request, *args, **kwargs) .venv/lib/python3.13.../integrations/django/views.py:94: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} def _view_wrapper(request, *args, **kwargs): > return view_func(request, *args, **kwargs) .venv/lib/python3.13.../views/decorators/csrf.py:65: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ request = <WSGIRequest: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> method = 'head', action = 'used_by' handler = <bound method UsedByMixin.used_by of <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70>> def view(request, *args, **kwargs): self = cls(**initkwargs) if 'get' in actions and 'head' not in actions: actions['head'] = actions['get'] # We also store the mapping of request methods to actions, # so that we can later set the action attribute. # eg. `self.action = 'list'` on an incoming GET request. self.action_map = actions # Bind methods to actions # This is the bit that's different to a standard view for method, action in actions.items(): handler = getattr(self, action) setattr(self, method, handler) self.request = request self.args = args self.kwargs = kwargs # And continue as usual > return self.dispatch(request, *args, **kwargs) .venv/lib/python3.13.../site-packages/rest_framework/viewsets.py:125: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> request = <rest_framework.request.Request: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} handler = <bound method UsedByMixin.used_by of <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc: > response = self.handle_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:515: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> exc = MixedContentTypeError('Content type for given perms and klass differs') def handle_exception(self, exc): """ Handle any exception that occurs, by returning an appropriate response, or re-raising the error. """ if isinstance(exc, (exceptions.NotAuthenticated, exceptions.AuthenticationFailed)): # WWW-Authenticate header for 401 responses, else coerce to 403 auth_header = self.get_authenticate_header(self.request) if auth_header: exc.auth_header = auth_header else: exc.status_code = status.HTTP_403_FORBIDDEN exception_handler = self.get_exception_handler() context = self.get_exception_handler_context() response = exception_handler(exc, context) if response is None: > self.raise_uncaught_exception(exc) .venv/lib/python3.13............/site-packages/rest_framework/views.py:475: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> exc = MixedContentTypeError('Content type for given perms and klass differs') def raise_uncaught_exception(self, exc): if settings.DEBUG: request = self.request renderer_format = getattr(request.accepted_renderer, 'format') use_plaintext_traceback = renderer_format not in ('html', 'api', 'admin') request.force_plaintext_errors(use_plaintext_traceback) > raise exc .venv/lib/python3.13............/site-packages/rest_framework/views.py:486: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> request = <rest_framework.request.Request: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} handler = <bound method UsedByMixin.used_by of <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70>> def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed > response = handler(request, *args, **kwargs) .venv/lib/python3.13............/site-packages/rest_framework/views.py:512: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <authentik.core.api.sources.SourceViewSet object at 0x7f2e03e00d70> request = <rest_framework.request.Request: GET '.../all/authentik-built-in/used_by/'> args = (), kwargs = {'slug': 'authentik-built-in'} model = <Source: authentik Built-in>, used_by = [], shadows = [] attr_name = 'user_set' manager = <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x7f2e04626e40> app = 'authentik_core', model_name = 'user', delete_action = 'cascade_many' first_object = True @extend_schema( responses={200: UsedBySerializer(many=True)}, ) @action(detail=True, pagination_class=None, filter_backends=[ObjectFilter]) def used_by(self, request: Request, *args, **kwargs) -> Response: """Get a list of all objects that use this object""" model: Model = self.get_object() used_by = [] shadows = [] for attr_name, manager in getmembers(model, lambda x: isinstance(x, Manager)): if attr_name == "objects": # pragma: no cover continue manager: Manager if manager.model._meta.abstract: continue app = manager.model._meta.app_label model_name = manager.model._meta.model_name delete_action = get_delete_action(manager) # To make sure we only apply shadows when there are any objects, # but so we only apply them once, have a simple flag for the first object first_object = True # TODO: This will only return the used-by references that the user can see # Either we have to leak model information here to not make the list # useless if the user doesn't have all permissions, or we need to double # query and check if there is a difference between modes the user can see # and can't see and add a warning > for obj in get_objects_for_user( request.user, f"{app}.view_{model_name}", manager ).all(): .../core/api/used_by.py:82: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ user = <SimpleLazyObject: <User: dz0MZaUAl91Z0h7exVsZ>> perms = ['authentik_core.view_user'] klass = <django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x7f2e04626e40> use_groups = True, any_perm = False, with_superuser = True accept_global_perms = True def get_objects_for_user( user: Any, perms: Union[str, list[str]], klass: Union[Model, Manager, QuerySet, None] = None, use_groups: bool = True, any_perm: bool = False, with_superuser: bool = True, accept_global_perms: bool = True, ) -> list[Model]: """Get objects that a user has *all* the supplied permissions for. Parameters: user (User | AnonymousUser): user to check for permissions. perms (str | list[str]): permission(s) to be checked. If `klass` parameter is not given, those should be full permission names rather than only codenames (i.e. `auth.change_user`). If more than one permission is present within sequence, their content type **must** be the same or `MixedContentTypeError` exception would be raised. klass (Modal | Manager | QuerySet): If not provided, this parameter would be computed based on given `params`. use_groups (bool): Whether to check user's groups object permissions. any_perm (bool): Whether any of the provided permissions in sequence is accepted. with_superuser (bool): if `user.is_superuser`, whether to return the entire queryset. Otherwise will only return objects the user has explicit permissions. This must be `True` for the `accept_global_perms` parameter to have any affect. accept_global_perms (bool): Whether global permissions are taken into account. Object based permissions are taken into account if more than one permission is provided in in perms and at least one of these perms is not globally set. If `any_perm` is `False` then the intersection of matching object is returned. Note, that if `with_superuser` is `False`, `accept_global_perms` will be ignored, which means that only object permissions will be checked! Raises: MixedContentTypeError: when computed content type for `perms` and/or `klass` clashes. WrongAppError: if cannot compute app label for given `perms` or `klass`. Example: ```shell >>> from django.contrib.auth.models import User >>> from guardian.shortcuts import get_objects_for_user >>> joe = User.objects.get(username='joe') >>> get_objects_for_user(joe, 'auth.change_group') [] >>> from guardian.shortcuts import assign_perm >>> group = Group.objects.create('some group') >>> assign_perm('auth.change_group', joe, group) >>> get_objects_for_user(joe, 'auth.change_group') [<Group some group>] # The permission string can also be an iterable. Continuing with the previous example: >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group']) [] >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group'], any_perm=True) [<Group some group>] >>> assign_perm('auth.delete_group', joe, group) >>> get_objects_for_user(joe, ['auth.change_group', 'auth.delete_group']) [<Group some group>] # Take global permissions into account: >>> jack = User.objects.get(username='jack') >>> assign_perm('auth.change_group', jack) # this will set a global permission >>> get_objects_for_user(jack, 'auth.change_group') [<Group some group>] >>> group2 = Group.objects.create('other group') >>> assign_perm('auth.delete_group', jack, group2) >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group']) # this retrieves intersection [<Group other group>] >>> get_objects_for_user(jack, ['auth.change_group', 'auth.delete_group'], any_perm) # this retrieves union [<Group some group>, <Group other group>] ``` Note: If `accept_global_perms` is set to `True`, then all assigned global permissions will also be taken into account. - Scenario 1: a user has view permissions generally defined on the model 'books' but no object-based permission on a single book instance: - If `accept_global_perms` is `True`: A list of all books will be returned. - If `accept_global_perms` is `False`: The list will be empty. - Scenario 2: a user has view permissions generally defined on the model 'books' and also has an object-based permission to view book 'Whatever': - If `accept_global_perms` is `True`: A list of all books will be returned. - If `accept_global_perms` is `False`: The list will only contain book 'Whatever'. - Scenario 3: a user only has object-based permission on book 'Whatever': - If `accept_global_perms` is `True`: The list will only contain book 'Whatever'. - If `accept_global_perms` is `False`: The list will only contain book 'Whatever'. - Scenario 4: a user does not have any permission: - If `accept_global_perms` is `True`: An empty list is returned. - If `accept_global_perms` is `False`: An empty list is returned. """ if isinstance(perms, str): perms = [perms] ctype = None app_label = None codenames = set() # Compute codenames and set and ctype if possible for perm in perms: if "." in perm: new_app_label, codename = perm.split(".", 1) if app_label is not None and app_label != new_app_label: raise MixedContentTypeError("Given perms must have same app " "label (%s != %s)" % (app_label, new_app_label)) else: app_label = new_app_label else: codename = perm codenames.add(codename) if app_label is not None: new_ctype = new_ctype = _get_ct_cached(app_label, codename) if ctype is not None and ctype != new_ctype: raise MixedContentTypeError("ContentType was once computed " "to be %s and another one %s" % (ctype, new_ctype)) else: ctype = new_ctype # Compute queryset and ctype if still missing if ctype is None and klass is not None: queryset = _get_queryset(klass) ctype = get_content_type(queryset.model) elif ctype is not None and klass is None: queryset = _get_queryset(ctype.model_class()) elif klass is None: raise WrongAppError("Cannot determine content type") else: queryset = _get_queryset(klass) if ctype != get_content_type(queryset.model): > raise MixedContentTypeError( "Content type for given perms and " "klass differs" ) E guardian.exceptions.MixedContentTypeError: Content type for given perms and klass differs .venv/lib/python3.13.../site-packages/guardian/shortcuts.py:567: MixedContentTypeError
To view more test analytics, go to the Test Analytics Dashboard 📋 Got 3 mins? Take this short survey to help us improve Test Analytics.
What exactly was the bug report for this change?
Further testing has shown an issue with this. Changing to draft for now