mirror of
				https://github.com/paperless-ngx/paperless-ngx.git
				synced 2025-10-31 10:37:12 -04:00 
			
		
		
		
	Dont perform permissions queries by default
This commit is contained in:
		
							parent
							
								
									15fb3e5328
								
							
						
					
					
						commit
						88a5a2049b
					
				| @ -5,11 +5,15 @@ describe('document-detail', () => { | ||||
|     this.modifiedDocuments = [] | ||||
| 
 | ||||
|     cy.fixture('documents/documents.json').then((documentsJson) => { | ||||
|       cy.intercept('GET', 'http://localhost:8000/api/documents/1/', (req) => { | ||||
|         let response = { ...documentsJson } | ||||
|         response = response.results.find((d) => d.id == 1) | ||||
|         req.reply(response) | ||||
|       }) | ||||
|       cy.intercept( | ||||
|         'GET', | ||||
|         'http://localhost:8000/api/documents/1/?full_perms=true', | ||||
|         (req) => { | ||||
|           let response = { ...documentsJson } | ||||
|           response = response.results.find((d) => d.id == 1) | ||||
|           req.reply(response) | ||||
|         } | ||||
|       ) | ||||
|     }) | ||||
| 
 | ||||
|     cy.intercept('PUT', 'http://localhost:8000/api/documents/1/', (req) => { | ||||
|  | ||||
| @ -21,6 +21,7 @@ | ||||
|             "original_file_name": "2022-03-22 no latin title.pdf", | ||||
|             "archived_file_name": "2022-03-22 no latin title.pdf", | ||||
|             "owner": null, | ||||
|             "user_can_change": true, | ||||
|             "permissions": { | ||||
|                 "view": { | ||||
|                     "users": [], | ||||
| @ -68,6 +69,7 @@ | ||||
|             "original_file_name": "2022-03-23 lorem ipsum dolor sit amet.pdf", | ||||
|             "archived_file_name": "2022-03-23 llorem ipsum dolor sit amet.pdf", | ||||
|             "owner": null, | ||||
|             "user_can_change": true, | ||||
|             "permissions": { | ||||
|                 "view": { | ||||
|                     "users": [], | ||||
| @ -98,6 +100,7 @@ | ||||
|             "original_file_name": "2022-03-24 dolor.pdf", | ||||
|             "archived_file_name": "2022-03-24 dolor.pdf", | ||||
|             "owner": null, | ||||
|             "user_can_change": true, | ||||
|             "permissions": { | ||||
|                 "view": { | ||||
|                     "users": [], | ||||
| @ -128,6 +131,7 @@ | ||||
|             "original_file_name": "2022-06-01 sit amet.pdf", | ||||
|             "archived_file_name": "2022-06-01 sit amet.pdf", | ||||
|             "owner": null, | ||||
|             "user_can_change": true, | ||||
|             "permissions": { | ||||
|                 "view": { | ||||
|                     "users": [], | ||||
|  | ||||
| @ -122,7 +122,8 @@ export abstract class ManagementListComponent<T extends ObjectWithId> | ||||
|         null, | ||||
|         this.sortField, | ||||
|         this.sortReverse, | ||||
|         this._nameFilter | ||||
|         this._nameFilter, | ||||
|         true | ||||
|       ) | ||||
|       .subscribe((c) => { | ||||
|         this.data = c.results | ||||
|  | ||||
| @ -16,4 +16,6 @@ export interface ObjectWithPermissions extends ObjectWithId { | ||||
|   owner?: number | ||||
| 
 | ||||
|   permissions?: PermissionsObject | ||||
| 
 | ||||
|   user_can_change?: boolean | ||||
| } | ||||
|  | ||||
| @ -58,17 +58,24 @@ export class PermissionsService { | ||||
|     action: string, | ||||
|     object: ObjectWithPermissions | ||||
|   ): boolean { | ||||
|     let actionObject = null | ||||
|     if (action === PermissionAction.View) actionObject = object.permissions.view | ||||
|     else if (action === PermissionAction.Change) | ||||
|       actionObject = object.permissions.change | ||||
|     if (!actionObject) return false | ||||
|     return ( | ||||
|       this.currentUserOwnsObject(object) || | ||||
|       actionObject.users.includes(this.currentUser.id) || | ||||
|       actionObject.groups.filter((g) => this.currentUser.groups.includes(g)) | ||||
|         .length > 0 | ||||
|     ) | ||||
|     if (action === PermissionAction.View) { | ||||
|       return ( | ||||
|         this.currentUserOwnsObject(object) || | ||||
|         object.permissions?.view.users.includes(this.currentUser.id) || | ||||
|         object.permissions?.view.groups.filter((g) => | ||||
|           this.currentUser.groups.includes(g) | ||||
|         ).length > 0 | ||||
|       ) | ||||
|     } else if (action === PermissionAction.Change) { | ||||
|       return ( | ||||
|         this.currentUserOwnsObject(object) || | ||||
|         object.user_can_change || | ||||
|         object.permissions?.change.users.includes(this.currentUser.id) || | ||||
|         object.permissions?.change.groups.filter((g) => | ||||
|           this.currentUser.groups.includes(g) | ||||
|         ).length > 0 | ||||
|       ) | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   public getPermissionCode( | ||||
|  | ||||
| @ -9,11 +9,15 @@ export abstract class AbstractNameFilterService< | ||||
|     pageSize?: number, | ||||
|     sortField?: string, | ||||
|     sortReverse?: boolean, | ||||
|     nameFilter?: string | ||||
|     nameFilter?: string, | ||||
|     fullPerms?: boolean | ||||
|   ) { | ||||
|     let params = {} | ||||
|     if (nameFilter) { | ||||
|       params = { name__icontains: nameFilter } | ||||
|       params['name__icontains'] = nameFilter | ||||
|     } | ||||
|     if (fullPerms) { | ||||
|       params['full_perms'] = true | ||||
|     } | ||||
|     return this.list(page, pageSize, sortField, sortReverse, params) | ||||
|   } | ||||
|  | ||||
| @ -113,6 +113,14 @@ export class DocumentService extends AbstractPaperlessService<PaperlessDocument> | ||||
|     }).pipe(map((response) => response.results.map((doc) => doc.id))) | ||||
|   } | ||||
| 
 | ||||
|   get(id: number): Observable<PaperlessDocument> { | ||||
|     return this.http.get<PaperlessDocument>(this.getResourceUrl(id), { | ||||
|       params: { | ||||
|         full_perms: true, | ||||
|       }, | ||||
|     }) | ||||
|   } | ||||
| 
 | ||||
|   getPreviewUrl(id: number, original: boolean = false): string { | ||||
|     let url = this.getResourceUrl(id, 'preview') | ||||
|     if (this._searchQuery) url += `#search="${this._searchQuery}"` | ||||
|  | ||||
| @ -14,6 +14,7 @@ from django.contrib.auth.models import Group | ||||
| from django.contrib.auth.models import User | ||||
| from django.utils.text import slugify | ||||
| from django.utils.translation import gettext as _ | ||||
| from guardian.core import ObjectPermissionChecker | ||||
| from guardian.shortcuts import get_users_with_perms | ||||
| from rest_framework import serializers | ||||
| from rest_framework.fields import SerializerMethodField | ||||
| @ -149,11 +150,21 @@ class SetPermissionsMixin: | ||||
| class OwnedObjectSerializer(serializers.ModelSerializer, SetPermissionsMixin): | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         self.user = kwargs.pop("user", None) | ||||
|         full_perms = kwargs.pop("full_perms", False) | ||||
|         super().__init__(*args, **kwargs) | ||||
| 
 | ||||
|         try: | ||||
|             if full_perms: | ||||
|                 self.fields.pop("user_can_change") | ||||
|             else: | ||||
|                 self.fields.pop("permissions") | ||||
|         except KeyError: | ||||
|             pass | ||||
| 
 | ||||
|     def get_permissions(self, obj): | ||||
|         view_codename = f"view_{obj.__class__.__name__.lower()}" | ||||
|         change_codename = f"change_{obj.__class__.__name__.lower()}" | ||||
| 
 | ||||
|         return { | ||||
|             "view": { | ||||
|                 "users": get_users_with_perms( | ||||
| @ -179,7 +190,19 @@ class OwnedObjectSerializer(serializers.ModelSerializer, SetPermissionsMixin): | ||||
|             }, | ||||
|         } | ||||
| 
 | ||||
|     def get_user_can_change(self, obj): | ||||
|         checker = ObjectPermissionChecker(self.user) if self.user is not None else None | ||||
|         return ( | ||||
|             obj.owner is None | ||||
|             or obj.owner == self.user | ||||
|             or ( | ||||
|                 self.user is not None | ||||
|                 and checker.has_perm(f"change_{obj.__class__.__name__.lower()}", obj) | ||||
|             ) | ||||
|         ) | ||||
| 
 | ||||
|     permissions = SerializerMethodField(read_only=True) | ||||
|     user_can_change = SerializerMethodField(read_only=True) | ||||
| 
 | ||||
|     set_permissions = serializers.DictField( | ||||
|         label="Set permissions", | ||||
| @ -235,6 +258,7 @@ class CorrespondentSerializer(MatchingModelSerializer, OwnedObjectSerializer): | ||||
|             "last_correspondence", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ) | ||||
| 
 | ||||
| @ -252,6 +276,7 @@ class DocumentTypeSerializer(MatchingModelSerializer, OwnedObjectSerializer): | ||||
|             "document_count", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ) | ||||
| 
 | ||||
| @ -303,6 +328,7 @@ class TagSerializerVersion1(MatchingModelSerializer, OwnedObjectSerializer): | ||||
|             "document_count", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ) | ||||
| 
 | ||||
| @ -338,6 +364,7 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer): | ||||
|             "document_count", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ) | ||||
| 
 | ||||
| @ -437,6 +464,7 @@ class DocumentSerializer(OwnedObjectSerializer, DynamicFieldsModelSerializer): | ||||
|             "archived_file_name", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|             "notes", | ||||
|         ) | ||||
| @ -464,6 +492,7 @@ class SavedViewSerializer(OwnedObjectSerializer): | ||||
|             "filter_rules", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ] | ||||
| 
 | ||||
| @ -783,6 +812,7 @@ class StoragePathSerializer(MatchingModelSerializer, OwnedObjectSerializer): | ||||
|             "document_count", | ||||
|             "owner", | ||||
|             "permissions", | ||||
|             "user_can_change", | ||||
|             "set_permissions", | ||||
|         ) | ||||
| 
 | ||||
|  | ||||
| @ -3396,6 +3396,36 @@ class TestApiAuth(DirectoriesMixin, APITestCase): | ||||
|             status.HTTP_404_NOT_FOUND, | ||||
|         ) | ||||
| 
 | ||||
|     def test_dynamic_permissions_fields(self): | ||||
|         Document.objects.create(title="Test", content="content 1", checksum="1") | ||||
| 
 | ||||
|         user1 = User.objects.create_superuser(username="test1") | ||||
|         self.client.force_authenticate(user1) | ||||
| 
 | ||||
|         response = self.client.get( | ||||
|             "/api/documents/", | ||||
|             format="json", | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual(response.status_code, status.HTTP_200_OK) | ||||
| 
 | ||||
|         resp_data = response.json() | ||||
| 
 | ||||
|         self.assertNotIn("permissions", resp_data["results"][0]) | ||||
|         self.assertIn("user_can_change", resp_data["results"][0]) | ||||
| 
 | ||||
|         response = self.client.get( | ||||
|             "/api/documents/?full_perms=true", | ||||
|             format="json", | ||||
|         ) | ||||
| 
 | ||||
|         self.assertEqual(response.status_code, status.HTTP_200_OK) | ||||
| 
 | ||||
|         resp_data = response.json() | ||||
| 
 | ||||
|         self.assertIn("permissions", resp_data["results"][0]) | ||||
|         self.assertNotIn("user_can_change", resp_data["results"][0]) | ||||
| 
 | ||||
| 
 | ||||
| class TestApiRemoteVersion(DirectoriesMixin, APITestCase): | ||||
|     ENDPOINT = "/api/remote_version/" | ||||
|  | ||||
| @ -157,6 +157,10 @@ class PassUserMixin(CreateModelMixin): | ||||
| 
 | ||||
|     def get_serializer(self, *args, **kwargs): | ||||
|         kwargs.setdefault("user", self.request.user) | ||||
|         kwargs.setdefault( | ||||
|             "full_perms", | ||||
|             self.request.query_params.get("full_perms", False), | ||||
|         ) | ||||
|         return super().get_serializer(*args, **kwargs) | ||||
| 
 | ||||
| 
 | ||||
| @ -274,6 +278,10 @@ class DocumentViewSet( | ||||
|         kwargs.setdefault("context", self.get_serializer_context()) | ||||
|         kwargs.setdefault("fields", fields) | ||||
|         kwargs.setdefault("truncate_content", truncate_content.lower() in ["true", "1"]) | ||||
|         kwargs.setdefault( | ||||
|             "full_perms", | ||||
|             self.request.query_params.get("full_perms", False), | ||||
|         ) | ||||
|         return serializer_class(*args, **kwargs) | ||||
| 
 | ||||
|     def update(self, request, *args, **kwargs): | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user