]> git.ipfire.org Git - thirdparty/paperless-ngx.git/commitdiff
Note creation / deletion should respect doc permissions 3903/head
authorshamoon <4887959+shamoon@users.noreply.github.com>
Tue, 1 Aug 2023 03:33:18 +0000 (20:33 -0700)
committershamoon <4887959+shamoon@users.noreply.github.com>
Wed, 2 Aug 2023 05:28:27 +0000 (22:28 -0700)
- Disable add note button on frontend
- Explicitly disable add / delete via api

src-ui/src/app/components/document-detail/document-detail.component.html
src-ui/src/app/components/document-notes/document-notes.component.html
src-ui/src/app/components/document-notes/document-notes.component.ts
src/documents/tests/test_api.py
src/documents/views.py

index 6b42fade86a47b2648b8b9c2d13e22084bc6b442..41e7a78d146a923fccb4494d504b22f355b24c2a 100644 (file)
                 <li [ngbNavItem]="DocumentDetailNavIDs.Notes" *ngIf="notesEnabled">
                     <a ngbNavLink i18n>Notes <span *ngIf="document?.notes.length" class="badge text-bg-secondary ms-1">{{document.notes.length}}</span></a>
                     <ng-template ngbNavContent>
-                        <app-document-notes [documentId]="documentId" [notes]="document?.notes" (updated)="notesUpdated($event)"></app-document-notes>
+                        <app-document-notes [documentId]="documentId" [notes]="document?.notes" [addDisabled]="!userCanEdit" (updated)="notesUpdated($event)"></app-document-notes>
                     </ng-template>
                 </li>
 
index f6c46cd47ae6067ddbc02e0291e23258c2366829..c76660e30734d6c60880b0c96bdd042e41a7d8ad 100644 (file)
@@ -8,7 +8,7 @@
         </div>
         <div class="form-group mt-2 d-flex justify-content-end align-items-center">
             <div *ngIf="networkActive" class="spinner-border spinner-border-sm fw-normal me-auto" role="status"></div>
-            <button type="button" class="btn btn-primary btn-sm" [disabled]="networkActive" (click)="addNote()" i18n>Add note</button>
+            <button type="button" class="btn btn-primary btn-sm" [disabled]="networkActive || addDisabled" (click)="addNote()" i18n>Add note</button>
         </div>
     </form>
     <hr>
index b8c7d6fd965025c9e3013f8d45481ab31aa3a2d5..01a104fb2bcce2fd19d1f06151141c82e5648dd8 100644 (file)
@@ -26,6 +26,9 @@ export class DocumentNotesComponent extends ComponentWithPermissions {
   @Input()
   notes: PaperlessDocumentNote[] = []
 
+  @Input()
+  addDisabled: boolean = false
+
   @Output()
   updated: EventEmitter<PaperlessDocumentNote[]> = new EventEmitter()
   users: PaperlessUser[]
@@ -61,7 +64,9 @@ export class DocumentNotesComponent extends ComponentWithPermissions {
       error: (e) => {
         this.networkActive = false
         this.toastService.showError(
-          $localize`Error saving note: ${e.toString()}`
+          $localize`Error saving note`,
+          10000,
+          JSON.stringify(e)
         )
       },
     })
index 40a1ca4a3dd45f2be7ec269ab81d7977aa77ab80..d788cf6a478f896077fe920515b6c8b5b840a91e 100644 (file)
@@ -2369,6 +2369,62 @@ class TestDocumentApi(DirectoriesMixin, DocumentConsumeDelayMixin, APITestCase):
 
         self.assertEqual(resp_data["note"], "this is a posted note")
 
+    def test_notes_permissions_aware(self):
+        """
+        GIVEN:
+            - Existing document owned by user2 but with granted view perms for user1
+        WHEN:
+            - API request is made by user1 to add a note or delete
+        THEN:
+            - Notes are neither created nor deleted
+        """
+        user1 = User.objects.create_user(username="test1")
+        user1.user_permissions.add(*Permission.objects.all())
+        user1.save()
+
+        user2 = User.objects.create_user(username="test2")
+        user2.save()
+
+        doc = Document.objects.create(
+            title="test",
+            mime_type="application/pdf",
+            content="this is a document which will have notes added",
+        )
+        doc.owner = user2
+        doc.save()
+
+        self.client.force_authenticate(user1)
+
+        resp = self.client.get(
+            f"/api/documents/{doc.pk}/notes/",
+            format="json",
+        )
+        self.assertEqual(resp.content, b"Insufficient permissions to view")
+        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
+
+        assign_perm("view_document", user1, doc)
+
+        resp = self.client.post(
+            f"/api/documents/{doc.pk}/notes/",
+            data={"note": "this is a posted note"},
+        )
+        self.assertEqual(resp.content, b"Insufficient permissions to create")
+        self.assertEqual(resp.status_code, status.HTTP_403_FORBIDDEN)
+
+        note = Note.objects.create(
+            note="This is a note.",
+            document=doc,
+            user=user2,
+        )
+
+        response = self.client.delete(
+            f"/api/documents/{doc.pk}/notes/?id={note.pk}",
+            format="json",
+        )
+
+        self.assertEqual(response.content, b"Insufficient permissions to delete")
+        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
+
     def test_delete_note(self):
         """
         GIVEN:
index cd69095fea8e1117d64e81f55bbf89eb6861c1ec..d57ad4eeae6c32897e78c0378e4a9e4a2956ee22 100644 (file)
@@ -502,19 +502,18 @@ class DocumentViewSet(
 
     @action(methods=["get", "post", "delete"], detail=True)
     def notes(self, request, pk=None):
+        currentUser = request.user
         try:
             doc = Document.objects.get(pk=pk)
-            if request.user is not None and not has_perms_owner_aware(
-                request.user,
+            if currentUser is not None and not has_perms_owner_aware(
+                currentUser,
                 "view_document",
                 doc,
             ):
-                return HttpResponseForbidden("Insufficient permissions")
+                return HttpResponseForbidden("Insufficient permissions to view")
         except Document.DoesNotExist:
             raise Http404
 
-        currentUser = request.user
-
         if request.method == "GET":
             try:
                 return Response(self.getNotes(doc))
@@ -525,6 +524,13 @@ class DocumentViewSet(
                 )
         elif request.method == "POST":
             try:
+                if currentUser is not None and not has_perms_owner_aware(
+                    currentUser,
+                    "change_document",
+                    doc,
+                ):
+                    return HttpResponseForbidden("Insufficient permissions to create")
+
                 c = Note.objects.create(
                     document=doc,
                     note=request.data["note"],
@@ -545,6 +551,13 @@ class DocumentViewSet(
                     },
                 )
         elif request.method == "DELETE":
+            if currentUser is not None and not has_perms_owner_aware(
+                currentUser,
+                "change_document",
+                doc,
+            ):
+                return HttpResponseForbidden("Insufficient permissions to delete")
+
             note = Note.objects.get(id=int(request.GET.get("id")))
             note.delete()