Merge pull request #3103 from paperless-ngx/fix/issue-3101

Fix: respect permissions for matching suggestions
This commit is contained in:
shamoon 2023-04-17 07:52:30 -07:00 committed by GitHub
commit d457f66e8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 22 deletions

View File

@ -56,8 +56,6 @@ describe('settings', () => {
'GET', 'GET',
'http://localhost:8000/api/mail_accounts/*', 'http://localhost:8000/api/mail_accounts/*',
(req) => { (req) => {
console.log(req, this.newMailAccounts)
let response = { ...mailAccountsJson } let response = { ...mailAccountsJson }
if (this.newMailAccounts.length) { if (this.newMailAccounts.length) {
response.results = response.results.concat(this.newMailAccounts) response.results = response.results.concat(this.newMailAccounts)
@ -142,7 +140,7 @@ describe('settings', () => {
cy.get('app-saved-view-widget').contains('Inbox').should('not.exist') cy.get('app-saved-view-widget').contains('Inbox').should('not.exist')
}) })
it('should show a list of mail accounts & rules & support creation', () => { it('should show a list of mail accounts & support creation', () => {
cy.contains('a', 'Mail').click() cy.contains('a', 'Mail').click()
cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule
cy.contains('button', 'Add Account').click() cy.contains('button', 'Add Account').click()
@ -162,6 +160,13 @@ describe('settings', () => {
.wait('@getAccounts') .wait('@getAccounts')
cy.contains('Saved account') cy.contains('Saved account')
cy.get('app-settings .tab-content ul li').its('length').should('eq', 6)
})
it('should show a list of mail rules & support creation', () => {
cy.contains('a', 'Mail').click()
cy.get('app-settings .tab-content ul li').its('length').should('eq', 5) // 2 headers, 2 accounts, 1 rule
cy.wait(1000) cy.wait(1000)
cy.contains('button', 'Add Rule').click() cy.contains('button', 'Add Rule').click()
cy.contains('Create new mail rule') cy.contains('Create new mail rule')
@ -177,6 +182,6 @@ describe('settings', () => {
.wait('@getRules') .wait('@getRules')
cy.contains('Saved rule').wait(1000) cy.contains('Saved rule').wait(1000)
cy.get('app-settings .tab-content ul li').its('length').should('eq', 7) cy.get('app-settings .tab-content ul li').its('length').should('eq', 6)
}) })
}) })

View File

@ -27,21 +27,31 @@ export class SelectComponent extends AbstractInputComponent<number> {
} }
_items: any[] _items: any[]
privateItems: any[] = []
@Input() @Input()
set items(items) { set items(items) {
if (this.value && items.find((i) => i.id === this.value) === undefined) { this._items = items
items.push({ if (this.value) this.checkForPrivateItem(this.value)
id: this.value, }
writeValue(newValue: any): void {
if (newValue && this._items) this.checkForPrivateItem(newValue)
super.writeValue(newValue)
}
checkForPrivateItem(value) {
if (this._items.find((i) => i.id === value) === undefined) {
this.privateItems.push({
id: value,
name: $localize`Private`, name: $localize`Private`,
private: true, private: true,
}) })
} }
this._items = items
} }
get items(): any[] { get items(): any[] {
return this._items return this._items?.concat(this.privateItems)
} }
@Input() @Input()

View File

@ -6,6 +6,7 @@ from documents.models import DocumentType
from documents.models import MatchingModel from documents.models import MatchingModel
from documents.models import StoragePath from documents.models import StoragePath
from documents.models import Tag from documents.models import Tag
from documents.permissions import get_objects_for_user_owner_aware
logger = logging.getLogger("paperless.matching") logger = logging.getLogger("paperless.matching")
@ -19,9 +20,16 @@ def log_reason(matching_model, document, reason):
) )
def match_correspondents(document, classifier): def match_correspondents(document, classifier, user=None):
pred_id = classifier.predict_correspondent(document.content) if classifier else None pred_id = classifier.predict_correspondent(document.content) if classifier else None
if user is not None:
correspondents = get_objects_for_user_owner_aware(
user,
"documents.view_correspondent",
Correspondent,
)
else:
correspondents = Correspondent.objects.all() correspondents = Correspondent.objects.all()
return list( return list(
@ -29,9 +37,16 @@ def match_correspondents(document, classifier):
) )
def match_document_types(document, classifier): def match_document_types(document, classifier, user=None):
pred_id = classifier.predict_document_type(document.content) if classifier else None pred_id = classifier.predict_document_type(document.content) if classifier else None
if user is not None:
document_types = get_objects_for_user_owner_aware(
user,
"documents.view_documenttype",
DocumentType,
)
else:
document_types = DocumentType.objects.all() document_types = DocumentType.objects.all()
return list( return list(
@ -39,9 +54,12 @@ def match_document_types(document, classifier):
) )
def match_tags(document, classifier): def match_tags(document, classifier, user=None):
predicted_tag_ids = classifier.predict_tags(document.content) if classifier else [] predicted_tag_ids = classifier.predict_tags(document.content) if classifier else []
if user is not None:
tags = get_objects_for_user_owner_aware(user, "documents.view_tag", Tag)
else:
tags = Tag.objects.all() tags = Tag.objects.all()
return list( return list(
@ -49,9 +67,16 @@ def match_tags(document, classifier):
) )
def match_storage_paths(document, classifier): def match_storage_paths(document, classifier, user=None):
pred_id = classifier.predict_storage_path(document.content) if classifier else None pred_id = classifier.predict_storage_path(document.content) if classifier else None
if user is not None:
storage_paths = get_objects_for_user_owner_aware(
user,
"documents.view_storagepath",
StoragePath,
)
else:
storage_paths = StoragePath.objects.all() storage_paths = StoragePath.objects.all()
return list( return list(

View File

@ -4,6 +4,7 @@ from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from guardian.models import GroupObjectPermission from guardian.models import GroupObjectPermission
from guardian.shortcuts import assign_perm from guardian.shortcuts import assign_perm
from guardian.shortcuts import get_objects_for_user
from guardian.shortcuts import get_users_with_perms from guardian.shortcuts import get_users_with_perms
from guardian.shortcuts import remove_perm from guardian.shortcuts import remove_perm
from rest_framework.permissions import BasePermission from rest_framework.permissions import BasePermission
@ -101,3 +102,15 @@ def set_permissions_for_object(permissions, object):
group, group,
object, object,
) )
def get_objects_for_user_owner_aware(user, perms, Model):
objects_owned = Model.objects.filter(owner=user)
objects_unowned = Model.objects.filter(owner__isnull=True)
objects_with_perms = get_objects_for_user(
user=user,
perms=perms,
klass=Model,
accept_global_perms=False,
)
return objects_owned | objects_unowned | objects_with_perms

View File

@ -401,12 +401,16 @@ class DocumentViewSet(
return Response( return Response(
{ {
"correspondents": [c.id for c in match_correspondents(doc, classifier)], "correspondents": [
"tags": [t.id for t in match_tags(doc, classifier)], c.id for c in match_correspondents(doc, classifier, request.user)
"document_types": [ ],
dt.id for dt in match_document_types(doc, classifier) "tags": [t.id for t in match_tags(doc, classifier, request.user)],
"document_types": [
dt.id for dt in match_document_types(doc, classifier, request.user)
],
"storage_paths": [
dt.id for dt in match_storage_paths(doc, classifier, request.user)
], ],
"storage_paths": [dt.id for dt in match_storage_paths(doc, classifier)],
"dates": [ "dates": [
date.strftime("%Y-%m-%d") for date in dates if date is not None date.strftime("%Y-%m-%d") for date in dates if date is not None
], ],