mirror of
https://github.com/paperless-ngx/paperless-ngx.git
synced 2025-07-26 18:14:37 -05:00
Compare commits
No commits in common. "7236b8b682d22969ef0f88a779f284998c95e856" and "0cefdc84759e0c7b1e3d0fb1e37ea41e879e00b4" have entirely different histories.
7236b8b682
...
0cefdc8475
@ -9854,42 +9854,42 @@
|
|||||||
<source>Document already exists.</source>
|
<source>Document already exists.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">24</context>
|
<context context-type="linenumber">23</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5103087968344279314" datatype="html">
|
<trans-unit id="5103087968344279314" datatype="html">
|
||||||
<source>Document already exists. Note: existing document is in the trash.</source>
|
<source>Document already exists. Note: existing document is in the trash.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">25</context>
|
<context context-type="linenumber">24</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="6108404046106249255" datatype="html">
|
<trans-unit id="6108404046106249255" datatype="html">
|
||||||
<source>Document with ASN already exists.</source>
|
<source>Document with ASN already exists.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">26</context>
|
<context context-type="linenumber">25</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="65951081560571094" datatype="html">
|
<trans-unit id="65951081560571094" datatype="html">
|
||||||
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
<source>Document with ASN already exists. Note: existing document is in the trash.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">27</context>
|
<context context-type="linenumber">26</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="148389968432135849" datatype="html">
|
<trans-unit id="148389968432135849" datatype="html">
|
||||||
<source>File not found.</source>
|
<source>File not found.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">28</context>
|
<context context-type="linenumber">27</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1520671543092565667" datatype="html">
|
<trans-unit id="1520671543092565667" datatype="html">
|
||||||
<source>Pre-consume script does not exist.</source>
|
<source>Pre-consume script does not exist.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">29</context>
|
<context context-type="linenumber">28</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9897,7 +9897,7 @@
|
|||||||
<source>Error while executing pre-consume script.</source>
|
<source>Error while executing pre-consume script.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">30</context>
|
<context context-type="linenumber">29</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Pre-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9905,7 +9905,7 @@
|
|||||||
<source>Post-consume script does not exist.</source>
|
<source>Post-consume script does not exist.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">31</context>
|
<context context-type="linenumber">30</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9913,7 +9913,7 @@
|
|||||||
<source>Error while executing post-consume script.</source>
|
<source>Error while executing post-consume script.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">32</context>
|
<context context-type="linenumber">31</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
<note priority="1" from="description">Post-Consume is a term that appears like that in the documentation as well and does not need a specific translation</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
@ -9921,49 +9921,49 @@
|
|||||||
<source>Received new file.</source>
|
<source>Received new file.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">33</context>
|
<context context-type="linenumber">32</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7337565919209746135" datatype="html">
|
<trans-unit id="7337565919209746135" datatype="html">
|
||||||
<source>File type not supported.</source>
|
<source>File type not supported.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">34</context>
|
<context context-type="linenumber">33</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="5002399167376099234" datatype="html">
|
<trans-unit id="5002399167376099234" datatype="html">
|
||||||
<source>Processing document...</source>
|
<source>Processing document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">35</context>
|
<context context-type="linenumber">34</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="1085975194762600381" datatype="html">
|
<trans-unit id="1085975194762600381" datatype="html">
|
||||||
<source>Generating thumbnail...</source>
|
<source>Generating thumbnail...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">36</context>
|
<context context-type="linenumber">35</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="3280851677698431426" datatype="html">
|
<trans-unit id="3280851677698431426" datatype="html">
|
||||||
<source>Retrieving date from document...</source>
|
<source>Retrieving date from document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">37</context>
|
<context context-type="linenumber">36</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="7162102384876037296" datatype="html">
|
<trans-unit id="7162102384876037296" datatype="html">
|
||||||
<source>Saving document...</source>
|
<source>Saving document...</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">38</context>
|
<context context-type="linenumber">37</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="4550450765009165976" datatype="html">
|
<trans-unit id="4550450765009165976" datatype="html">
|
||||||
<source>Finished.</source>
|
<source>Finished.</source>
|
||||||
<context-group purpose="location">
|
<context-group purpose="location">
|
||||||
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
<context context-type="sourcefile">src/app/services/websocket-status.service.ts</context>
|
||||||
<context context-type="linenumber">39</context>
|
<context context-type="linenumber">38</context>
|
||||||
</context-group>
|
</context-group>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
@ -7,6 +7,4 @@ export interface WebsocketProgressMessage {
|
|||||||
message?: string
|
message?: string
|
||||||
document_id: number
|
document_id: number
|
||||||
owner_id?: number
|
owner_id?: number
|
||||||
users_can_view?: number[]
|
|
||||||
groups_can_view?: number[]
|
|
||||||
}
|
}
|
||||||
|
@ -355,50 +355,6 @@ describe('ConsumerStatusService', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should notify user if user can view or is in group', () => {
|
|
||||||
settingsService.currentUser = {
|
|
||||||
id: 1,
|
|
||||||
username: 'testuser',
|
|
||||||
is_superuser: false,
|
|
||||||
groups: [1],
|
|
||||||
}
|
|
||||||
websocketStatusService.connect()
|
|
||||||
server.send({
|
|
||||||
type: WebsocketStatusType.STATUS_UPDATE,
|
|
||||||
data: {
|
|
||||||
task_id: '1234',
|
|
||||||
filename: 'file1.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
docuement_id: 12,
|
|
||||||
owner_id: 2,
|
|
||||||
status: 'WORKING',
|
|
||||||
users_can_view: [1],
|
|
||||||
groups_can_view: [],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
1
|
|
||||||
)
|
|
||||||
server.send({
|
|
||||||
type: WebsocketStatusType.STATUS_UPDATE,
|
|
||||||
data: {
|
|
||||||
task_id: '5678',
|
|
||||||
filename: 'file2.pdf',
|
|
||||||
current_progress: 50,
|
|
||||||
max_progress: 100,
|
|
||||||
docuement_id: 13,
|
|
||||||
owner_id: 2,
|
|
||||||
status: 'WORKING',
|
|
||||||
users_can_view: [],
|
|
||||||
groups_can_view: [1],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
expect(websocketStatusService.getConsumerStatusNotCompleted()).toHaveLength(
|
|
||||||
2
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should trigger deleted subject on document deleted', () => {
|
it('should trigger deleted subject on document deleted', () => {
|
||||||
let deleted = false
|
let deleted = false
|
||||||
websocketStatusService.onDocumentDeleted().subscribe(() => {
|
websocketStatusService.onDocumentDeleted().subscribe(() => {
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Injectable } from '@angular/core'
|
import { Injectable } from '@angular/core'
|
||||||
import { Subject } from 'rxjs'
|
import { Subject } from 'rxjs'
|
||||||
import { environment } from 'src/environments/environment'
|
import { environment } from 'src/environments/environment'
|
||||||
import { User } from '../data/user'
|
|
||||||
import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message'
|
import { WebsocketDocumentsDeletedMessage } from '../data/websocket-documents-deleted-message'
|
||||||
import { WebsocketProgressMessage } from '../data/websocket-progress-message'
|
import { WebsocketProgressMessage } from '../data/websocket-progress-message'
|
||||||
import { SettingsService } from './settings.service'
|
import { SettingsService } from './settings.service'
|
||||||
@ -174,25 +173,13 @@ export class WebsocketStatusService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private canViewMessage(messageData: WebsocketProgressMessage): boolean {
|
|
||||||
// see paperless.consumers.StatusConsumer._can_view
|
|
||||||
const user: User = this.settingsService.currentUser
|
|
||||||
return (
|
|
||||||
!messageData.owner_id ||
|
|
||||||
user.is_superuser ||
|
|
||||||
(messageData.owner_id && messageData.owner_id === user.id) ||
|
|
||||||
(messageData.users_can_view &&
|
|
||||||
messageData.users_can_view.includes(user.id)) ||
|
|
||||||
(messageData.groups_can_view &&
|
|
||||||
messageData.groups_can_view.some((groupId) =>
|
|
||||||
user.groups?.includes(groupId)
|
|
||||||
))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
handleProgressUpdate(messageData: WebsocketProgressMessage) {
|
handleProgressUpdate(messageData: WebsocketProgressMessage) {
|
||||||
// fallback if backend didn't restrict message
|
// fallback if backend didn't restrict message
|
||||||
if (!this.canViewMessage(messageData)) {
|
if (
|
||||||
|
messageData.owner_id &&
|
||||||
|
messageData.owner_id !== this.settingsService.currentUser?.id &&
|
||||||
|
!this.settingsService.currentUser?.is_superuser
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,10 +137,6 @@ class ConsumerPlugin(
|
|||||||
extra_args={
|
extra_args={
|
||||||
"document_id": document_id,
|
"document_id": document_id,
|
||||||
"owner_id": self.metadata.owner_id if self.metadata.owner_id else None,
|
"owner_id": self.metadata.owner_id if self.metadata.owner_id else None,
|
||||||
"users_can_view": (self.metadata.view_users or [])
|
|
||||||
+ (self.metadata.change_users or []),
|
|
||||||
"groups_can_view": (self.metadata.view_groups or [])
|
|
||||||
+ (self.metadata.change_groups or []),
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -10,18 +10,14 @@ class StatusConsumer(WebsocketConsumer):
|
|||||||
def _authenticated(self):
|
def _authenticated(self):
|
||||||
return "user" in self.scope and self.scope["user"].is_authenticated
|
return "user" in self.scope and self.scope["user"].is_authenticated
|
||||||
|
|
||||||
def _can_view(self, data):
|
def _is_owner_or_unowned(self, data):
|
||||||
user = self.scope.get("user") if self.scope.get("user") else None
|
|
||||||
owner_id = data.get("owner_id")
|
|
||||||
users_can_view = data.get("users_can_view", [])
|
|
||||||
groups_can_view = data.get("groups_can_view", [])
|
|
||||||
return (
|
return (
|
||||||
user.is_superuser
|
(
|
||||||
or user.id == owner_id
|
self.scope["user"].is_superuser
|
||||||
or user.id in users_can_view
|
or self.scope["user"].id == data["owner_id"]
|
||||||
or any(
|
|
||||||
user.groups.filter(pk=group_id).exists() for group_id in groups_can_view
|
|
||||||
)
|
)
|
||||||
|
if "owner_id" in data and "user" in self.scope
|
||||||
|
else True
|
||||||
)
|
)
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
@ -44,7 +40,7 @@ class StatusConsumer(WebsocketConsumer):
|
|||||||
if not self._authenticated():
|
if not self._authenticated():
|
||||||
self.close()
|
self.close()
|
||||||
else:
|
else:
|
||||||
if self._can_view(event["data"]):
|
if self._is_owner_or_unowned(event["data"]):
|
||||||
self.send(json.dumps(event))
|
self.send(json.dumps(event))
|
||||||
|
|
||||||
def documents_deleted(self, event):
|
def documents_deleted(self, event):
|
||||||
|
@ -90,52 +90,6 @@ class TestWebSockets(TestCase):
|
|||||||
|
|
||||||
await communicator.disconnect()
|
await communicator.disconnect()
|
||||||
|
|
||||||
async def test_status_update_check_perms(self):
|
|
||||||
communicator = WebsocketCommunicator(application, "/ws/status/")
|
|
||||||
|
|
||||||
communicator.scope["user"] = mock.Mock()
|
|
||||||
communicator.scope["user"].is_authenticated = True
|
|
||||||
communicator.scope["user"].is_superuser = False
|
|
||||||
communicator.scope["user"].id = 1
|
|
||||||
|
|
||||||
connected, subprotocol = await communicator.connect()
|
|
||||||
self.assertTrue(connected)
|
|
||||||
|
|
||||||
# Test as owner
|
|
||||||
message = {"type": "status_update", "data": {"task_id": "test", "owner_id": 1}}
|
|
||||||
channel_layer = get_channel_layer()
|
|
||||||
await channel_layer.group_send(
|
|
||||||
"status_updates",
|
|
||||||
message,
|
|
||||||
)
|
|
||||||
response = await communicator.receive_json_from()
|
|
||||||
self.assertEqual(response, message)
|
|
||||||
|
|
||||||
# Test with a group that the user belongs to
|
|
||||||
communicator.scope["user"].groups.filter.return_value.exists.return_value = True
|
|
||||||
message = {
|
|
||||||
"type": "status_update",
|
|
||||||
"data": {"task_id": "test", "owner_id": 2, "groups_can_view": [1]},
|
|
||||||
}
|
|
||||||
channel_layer = get_channel_layer()
|
|
||||||
await channel_layer.group_send(
|
|
||||||
"status_updates",
|
|
||||||
message,
|
|
||||||
)
|
|
||||||
response = await communicator.receive_json_from()
|
|
||||||
self.assertEqual(response, message)
|
|
||||||
|
|
||||||
# Test with a different owner_id
|
|
||||||
message = {"type": "status_update", "data": {"task_id": "test", "owner_id": 2}}
|
|
||||||
channel_layer = get_channel_layer()
|
|
||||||
await channel_layer.group_send(
|
|
||||||
"status_updates",
|
|
||||||
message,
|
|
||||||
)
|
|
||||||
response = await communicator.receive_nothing()
|
|
||||||
self.assertNotEqual(response, message)
|
|
||||||
await communicator.disconnect()
|
|
||||||
|
|
||||||
@mock.patch("paperless.consumers.StatusConsumer._authenticated")
|
@mock.patch("paperless.consumers.StatusConsumer._authenticated")
|
||||||
async def test_receive_documents_deleted(self, _authenticated):
|
async def test_receive_documents_deleted(self, _authenticated):
|
||||||
_authenticated.return_value = True
|
_authenticated.return_value = True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user