Dont perform permissions queries by default

This commit is contained in:
shamoon 2023-04-26 03:03:33 -07:00 committed by Trenton H
parent 15fb3e5328
commit 88a5a2049b
10 changed files with 117 additions and 19 deletions

View File

@ -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) => {

View File

@ -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": [],

View File

@ -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

View File

@ -16,4 +16,6 @@ export interface ObjectWithPermissions extends ObjectWithId {
owner?: number
permissions?: PermissionsObject
user_can_change?: boolean
}

View File

@ -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(

View File

@ -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)
}

View File

@ -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}"`

View File

@ -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",
)

View File

@ -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/"

View File

@ -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):