mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-26 03:36:08 -05:00 
			
		
		
		
	Compare commits
	
		
			5 Commits
		
	
	
		
			dependabot
			...
			chore/upda
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | b0f483e275 | ||
|   | d6602c2f54 | ||
|   | 63dab0ab09 | ||
|   | 276dc31abe | ||
|   | a11a2ec13f | 
| @@ -41,7 +41,7 @@ dependencies = [ | ||||
|   "djangorestframework~=3.16", | ||||
|   "djangorestframework-guardian~=0.4.0", | ||||
|   "drf-spectacular~=0.28", | ||||
|   "drf-spectacular-sidecar~=2025.10.1", | ||||
|   "drf-spectacular-sidecar~=2025.9.1", | ||||
|   "drf-writable-nested~=0.7.1", | ||||
|   "filelock~=3.20.0", | ||||
|   "flower~=2.0.1", | ||||
|   | ||||
| @@ -4539,32 +4539,32 @@ | ||||
|         <source>Create new user account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">70</context> | ||||
|           <context context-type="linenumber">72</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="2887331217965896363" datatype="html"> | ||||
|         <source>Edit user account</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">74</context> | ||||
|           <context context-type="linenumber">76</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="5872286584705575476" datatype="html"> | ||||
|         <source>Totp deactivated</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">130</context> | ||||
|           <context context-type="linenumber">132</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="6439190193788239059" datatype="html"> | ||||
|         <source>Totp deactivation failed</source> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">133</context> | ||||
|           <context context-type="linenumber">135</context> | ||||
|         </context-group> | ||||
|         <context-group purpose="location"> | ||||
|           <context context-type="sourcefile">src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts</context> | ||||
|           <context context-type="linenumber">138</context> | ||||
|           <context context-type="linenumber">140</context> | ||||
|         </context-group> | ||||
|       </trans-unit> | ||||
|       <trans-unit id="8419515490539218007" datatype="html"> | ||||
|   | ||||
| @@ -14,6 +14,7 @@ import { GroupService } from 'src/app/services/rest/group.service' | ||||
| import { UserService } from 'src/app/services/rest/user.service' | ||||
| import { SettingsService } from 'src/app/services/settings.service' | ||||
| import { ToastService } from 'src/app/services/toast.service' | ||||
| import { ConfirmButtonComponent } from '../../confirm-button/confirm-button.component' | ||||
| import { PasswordComponent } from '../../input/password/password.component' | ||||
| import { SelectComponent } from '../../input/select/select.component' | ||||
| import { TextComponent } from '../../input/text/text.component' | ||||
| @@ -28,6 +29,7 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions | ||||
|     SelectComponent, | ||||
|     TextComponent, | ||||
|     PasswordComponent, | ||||
|     ConfirmButtonComponent, | ||||
|     FormsModule, | ||||
|     ReactiveFormsModule, | ||||
|   ], | ||||
|   | ||||
| @@ -3,7 +3,6 @@ import logging | ||||
|  | ||||
| from django.db import migrations | ||||
| from django.db import models | ||||
| from django.db import transaction | ||||
|  | ||||
| from documents.templating.utils import convert_format_str_to_template_format | ||||
|  | ||||
| @@ -13,19 +12,32 @@ logger = logging.getLogger("paperless.migrations") | ||||
| def convert_from_format_to_template(apps, schema_editor): | ||||
|     WorkflowActions = apps.get_model("documents", "WorkflowAction") | ||||
|  | ||||
|     with transaction.atomic(): | ||||
|         for WorkflowAction in WorkflowActions.objects.all(): | ||||
|             if not WorkflowAction.assign_title: | ||||
|                 continue | ||||
|             WorkflowAction.assign_title = convert_format_str_to_template_format( | ||||
|                 WorkflowAction.assign_title, | ||||
|             ) | ||||
|             logger.debug( | ||||
|                 "Converted WorkflowAction id %d title to template format: %s", | ||||
|                 WorkflowAction.id, | ||||
|                 WorkflowAction.assign_title, | ||||
|             ) | ||||
|             WorkflowAction.save() | ||||
|     batch_size = 500 | ||||
|     actions_to_update = [] | ||||
|  | ||||
|     queryset = ( | ||||
|         WorkflowActions.objects.filter(assign_title__isnull=False) | ||||
|         .exclude(assign_title="") | ||||
|         .only("id", "assign_title") | ||||
|     ) | ||||
|  | ||||
|     for action in queryset: | ||||
|         action.assign_title = convert_format_str_to_template_format( | ||||
|             action.assign_title, | ||||
|         ) | ||||
|         logger.debug( | ||||
|             "Converted WorkflowAction id %d title to template format: %s", | ||||
|             action.id, | ||||
|             action.assign_title, | ||||
|         ) | ||||
|         actions_to_update.append(action) | ||||
|  | ||||
|     if actions_to_update: | ||||
|         WorkflowActions.objects.bulk_update( | ||||
|             actions_to_update, | ||||
|             ["assign_title"], | ||||
|             batch_size=batch_size, | ||||
|         ) | ||||
|  | ||||
|  | ||||
| class Migration(migrations.Migration): | ||||
| @@ -35,15 +47,13 @@ class Migration(migrations.Migration): | ||||
|  | ||||
|     operations = [ | ||||
|         migrations.AlterField( | ||||
|             model_name="WorkflowAction", | ||||
|             model_name="workflowaction", | ||||
|             name="assign_title", | ||||
|             field=models.TextField( | ||||
|                 null=True, | ||||
|                 blank=True, | ||||
|                 help_text=( | ||||
|                     "Assign a document title, can be a JINJA2 template, " | ||||
|                     "see documentation.", | ||||
|                 ), | ||||
|                 help_text="Assign a document title, must  be a Jinja2 template, see documentation.", | ||||
|                 null=True, | ||||
|                 verbose_name="assign title", | ||||
|             ), | ||||
|         ), | ||||
|         migrations.RunPython( | ||||
|   | ||||
| @@ -2,9 +2,11 @@ import types | ||||
| from unittest.mock import patch | ||||
|  | ||||
| from django.contrib.admin.sites import AdminSite | ||||
| from django.contrib.auth.models import Permission | ||||
| from django.contrib.auth.models import User | ||||
| from django.test import TestCase | ||||
| from django.utils import timezone | ||||
| from rest_framework import status | ||||
|  | ||||
| from documents import index | ||||
| from documents.admin import DocumentAdmin | ||||
| @@ -125,3 +127,36 @@ class TestPaperlessAdmin(DirectoriesMixin, TestCase): | ||||
|         form.request = types.SimpleNamespace(user=superuser) | ||||
|         self.assertTrue(form.is_valid()) | ||||
|         self.assertEqual({}, form.errors) | ||||
|  | ||||
|     def test_superuser_can_only_be_modified_by_superuser(self): | ||||
|         superuser = User.objects.create_superuser(username="superuser", password="test") | ||||
|         user = User.objects.create( | ||||
|             username="test", | ||||
|             is_superuser=False, | ||||
|             is_staff=True, | ||||
|         ) | ||||
|         change_user_perm = Permission.objects.get(codename="change_user") | ||||
|         user.user_permissions.add(change_user_perm) | ||||
|  | ||||
|         self.client.force_login(user) | ||||
|         response = self.client.patch( | ||||
|             f"/api/users/{superuser.pk}/", | ||||
|             {"first_name": "Updated"}, | ||||
|             content_type="application/json", | ||||
|         ) | ||||
|         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) | ||||
|         self.assertEqual( | ||||
|             response.content.decode(), | ||||
|             "Superusers can only be modified by other superusers", | ||||
|         ) | ||||
|  | ||||
|         self.client.logout() | ||||
|         self.client.force_login(superuser) | ||||
|         response = self.client.patch( | ||||
|             f"/api/users/{superuser.pk}/", | ||||
|             {"first_name": "Updated"}, | ||||
|             content_type="application/json", | ||||
|         ) | ||||
|         self.assertEqual(response.status_code, status.HTTP_200_OK) | ||||
|         superuser.refresh_from_db() | ||||
|         self.assertEqual(superuser.first_name, "Updated") | ||||
|   | ||||
| @@ -125,6 +125,10 @@ class UserViewSet(ModelViewSet): | ||||
|  | ||||
|     def update(self, request, *args, **kwargs): | ||||
|         user_to_update: User = self.get_object() | ||||
|         if not request.user.is_superuser and user_to_update.is_superuser: | ||||
|             return HttpResponseForbidden( | ||||
|                 "Superusers can only be modified by other superusers", | ||||
|             ) | ||||
|         if ( | ||||
|             not request.user.is_superuser | ||||
|             and request.data.get("is_superuser") is not None | ||||
|   | ||||
							
								
								
									
										10
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							| @@ -959,14 +959,14 @@ wheels = [ | ||||
|  | ||||
| [[package]] | ||||
| name = "drf-spectacular-sidecar" | ||||
| version = "2025.10.1" | ||||
| version = "2025.9.1" | ||||
| source = { registry = "https://pypi.org/simple" } | ||||
| dependencies = [ | ||||
|     { name = "django", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, | ||||
| ] | ||||
| sdist = { url = "https://files.pythonhosted.org/packages/c3/e4/99cd1b1c8c69788bd6cb6a2459674f8c75728e79df23ac7beddd094bf805/drf_spectacular_sidecar-2025.10.1.tar.gz", hash = "sha256:506a5a21ce1ad7211c28acb4e2112e213f6dc095a2052ee6ed6db1ffe8eb5a7b", size = 2420998, upload-time = "2025-10-01T11:23:27.092Z" } | ||||
| sdist = { url = "https://files.pythonhosted.org/packages/51/e2/85a0b8dbed8631165a6b49b2aee57636da8e4e710c444566636ffd972a7b/drf_spectacular_sidecar-2025.9.1.tar.gz", hash = "sha256:da2aa45da48fff76de7a1e357b84d1eb0b9df40ca89ec19d5fe94ad1037bb3c8", size = 2420902, upload-time = "2025-09-01T11:23:24.156Z" } | ||||
| wheels = [ | ||||
|     { url = "https://files.pythonhosted.org/packages/ab/87/70c67391e4ce68715d4dfae8dd33caeda2552af22f436ba55b8867a040fe/drf_spectacular_sidecar-2025.10.1-py3-none-any.whl", hash = "sha256:f1de343184d1a938179ce363d318258fe1e5f02f2f774625272364835f1c42bd", size = 2440241, upload-time = "2025-10-01T11:23:25.743Z" }, | ||||
|     { url = "https://files.pythonhosted.org/packages/96/24/db59146ba89491fe1d44ca8aef239c94bf3c7fd41523976090f099430312/drf_spectacular_sidecar-2025.9.1-py3-none-any.whl", hash = "sha256:8e80625209b8a23ff27616db305b9ab71c2e2d1069dacd99720a9c11e429af50", size = 2440255, upload-time = "2025-09-01T11:23:22.822Z" }, | ||||
| ] | ||||
|  | ||||
| [[package]] | ||||
| @@ -2262,7 +2262,7 @@ requires-dist = [ | ||||
|     { name = "concurrent-log-handler", specifier = "~=0.9.25" }, | ||||
|     { name = "dateparser", specifier = "~=1.2" }, | ||||
|     { name = "django", specifier = "~=5.2.5" }, | ||||
|     { name = "django-allauth", extras = ["mfa", "socialaccount"], specifier = "~=65.4.0" }, | ||||
|     { name = "django-allauth", extras = ["socialaccount", "mfa"], specifier = "~=65.4.0" }, | ||||
|     { name = "django-auditlog", specifier = "~=3.2.1" }, | ||||
|     { name = "django-cachalot", specifier = "~=2.8.0" }, | ||||
|     { name = "django-celery-results", specifier = "~=2.6.0" }, | ||||
| @@ -2277,7 +2277,7 @@ requires-dist = [ | ||||
|     { name = "djangorestframework", specifier = "~=3.16" }, | ||||
|     { name = "djangorestframework-guardian", specifier = "~=0.4.0" }, | ||||
|     { name = "drf-spectacular", specifier = "~=0.28" }, | ||||
|     { name = "drf-spectacular-sidecar", specifier = "~=2025.10.1" }, | ||||
|     { name = "drf-spectacular-sidecar", specifier = "~=2025.9.1" }, | ||||
|     { name = "drf-writable-nested", specifier = "~=0.7.1" }, | ||||
|     { name = "filelock", specifier = "~=3.20.0" }, | ||||
|     { name = "flower", specifier = "~=2.0.1" }, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user