Merge branch 'dev' into feature-remote-ocr-2

This commit is contained in:
shamoon 2025-10-24 16:48:07 -07:00 committed by GitHub
commit cc73ed8b86
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 46 additions and 5 deletions

View File

@ -4539,32 +4539,32 @@
<source>Create new user account</source> <source>Create new user account</source>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="2887331217965896363" datatype="html"> <trans-unit id="2887331217965896363" datatype="html">
<source>Edit user account</source> <source>Edit user account</source>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="5872286584705575476" datatype="html"> <trans-unit id="5872286584705575476" datatype="html">
<source>Totp deactivated</source> <source>Totp deactivated</source>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="6439190193788239059" datatype="html"> <trans-unit id="6439190193788239059" datatype="html">
<source>Totp deactivation failed</source> <source>Totp deactivation failed</source>
<context-group purpose="location"> <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="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>
<context-group purpose="location"> <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="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> </context-group>
</trans-unit> </trans-unit>
<trans-unit id="8419515490539218007" datatype="html"> <trans-unit id="8419515490539218007" datatype="html">

View File

@ -14,6 +14,7 @@ import { GroupService } from 'src/app/services/rest/group.service'
import { UserService } from 'src/app/services/rest/user.service' import { UserService } from 'src/app/services/rest/user.service'
import { SettingsService } from 'src/app/services/settings.service' import { SettingsService } from 'src/app/services/settings.service'
import { ToastService } from 'src/app/services/toast.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 { PasswordComponent } from '../../input/password/password.component'
import { SelectComponent } from '../../input/select/select.component' import { SelectComponent } from '../../input/select/select.component'
import { TextComponent } from '../../input/text/text.component' import { TextComponent } from '../../input/text/text.component'
@ -28,6 +29,7 @@ import { PermissionsSelectComponent } from '../../permissions-select/permissions
SelectComponent, SelectComponent,
TextComponent, TextComponent,
PasswordComponent, PasswordComponent,
ConfirmButtonComponent,
FormsModule, FormsModule,
ReactiveFormsModule, ReactiveFormsModule,
], ],

View File

@ -2,9 +2,11 @@ import types
from unittest.mock import patch from unittest.mock import patch
from django.contrib.admin.sites import AdminSite from django.contrib.admin.sites import AdminSite
from django.contrib.auth.models import Permission
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.test import TestCase from django.test import TestCase
from django.utils import timezone from django.utils import timezone
from rest_framework import status
from documents import index from documents import index
from documents.admin import DocumentAdmin from documents.admin import DocumentAdmin
@ -125,3 +127,36 @@ class TestPaperlessAdmin(DirectoriesMixin, TestCase):
form.request = types.SimpleNamespace(user=superuser) form.request = types.SimpleNamespace(user=superuser)
self.assertTrue(form.is_valid()) self.assertTrue(form.is_valid())
self.assertEqual({}, form.errors) 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")

View File

@ -125,6 +125,10 @@ class UserViewSet(ModelViewSet):
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
user_to_update: User = self.get_object() 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 ( if (
not request.user.is_superuser not request.user.is_superuser
and request.data.get("is_superuser") is not None and request.data.get("is_superuser") is not None