Errors for invalid ws msgs instead of silent drop
This commit is contained in:
parent
b667704aca
commit
6f75e008b8
|
@ -84,8 +84,14 @@ class WebsocketConsumerBase(AsyncJsonWebsocketConsumer):
|
|||
await self.close(code=4443)
|
||||
return
|
||||
|
||||
with history_context(history_user=self.scope.get('user')):
|
||||
return await super().websocket_receive(message)
|
||||
try:
|
||||
with history_context(history_user=self.scope.get('user')):
|
||||
return await super().websocket_receive(message)
|
||||
except ValidationError as ex:
|
||||
await self.send_json({
|
||||
'type': 'error',
|
||||
'message': ex.message,
|
||||
})
|
||||
|
||||
async def websocket_disconnect(self, message):
|
||||
try:
|
||||
|
@ -288,7 +294,7 @@ class NotesConsumerBase(WebsocketConsumerBase):
|
|||
.select_for_update(of=['self'], no_key=True) \
|
||||
.first()
|
||||
if not note:
|
||||
return None, None
|
||||
raise ValidationError('Invalid path: ID not found')
|
||||
return note, path_parts[2]
|
||||
|
||||
@database_sync_to_async
|
||||
|
@ -297,8 +303,6 @@ class NotesConsumerBase(WebsocketConsumerBase):
|
|||
# Validate path and get note
|
||||
valid_paths = {k for k, f in self.get_serializer().fields.items() if not f.read_only} - {'title', 'text'}
|
||||
note, key = self.get_note_for_update(path=content.get('path'), valid_paths=valid_paths)
|
||||
if not note:
|
||||
return None
|
||||
|
||||
# Update in DB
|
||||
serializer = self.get_serializer(instance=note, data={key: content.get('value')}, partial=True)
|
||||
|
@ -325,8 +329,6 @@ class NotesConsumerBase(WebsocketConsumerBase):
|
|||
if not content.get('updates', []):
|
||||
raise ValidationError('No updates')
|
||||
note, key = self.get_note_for_update(path=content.get('path'), valid_paths=['title', 'text'])
|
||||
if not note:
|
||||
return None
|
||||
|
||||
version = content['version']
|
||||
# TODO: reject updates for versions that are too old
|
||||
|
@ -349,7 +351,7 @@ class NotesConsumerBase(WebsocketConsumerBase):
|
|||
for u in e.data.get('updates', [])] for e in over_updates])),
|
||||
)
|
||||
if not updates:
|
||||
return None
|
||||
raise ValidationError('No updates')
|
||||
|
||||
# Update in DB
|
||||
changes = updates[0].changes
|
||||
|
@ -585,7 +587,7 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
.select_for_update(of=['self'], no_key=True) \
|
||||
.first()
|
||||
if not obj:
|
||||
return None, None, None
|
||||
raise ValidationError('Invalid path: ID not found')
|
||||
|
||||
# Validate path in top-level or in field definition
|
||||
if path_parts[2] == 'data':
|
||||
|
@ -605,8 +607,8 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
def collab_update_key(self, content):
|
||||
# Validate path and get section/finding
|
||||
obj, path, definition = self.get_object_for_update(content.get('path'))
|
||||
if not obj or (definition and definition.type in [FieldDataType.MARKDOWN, FieldDataType.STRING]):
|
||||
return None
|
||||
if definition and definition.type in [FieldDataType.MARKDOWN, FieldDataType.STRING]:
|
||||
raise ValidationError('collab.update_key is not supported for text fields. Use collab.update_text instead.')
|
||||
|
||||
# Update data in DB
|
||||
if definition:
|
||||
|
@ -641,8 +643,8 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
@transaction.atomic()
|
||||
def collab_update_text(self, content):
|
||||
obj, path, definition = self.get_object_for_update(content.get('path'))
|
||||
if not obj or not definition or definition.type not in [FieldDataType.MARKDOWN, FieldDataType.STRING]:
|
||||
return None
|
||||
if not definition or definition.type not in [FieldDataType.MARKDOWN, FieldDataType.STRING]:
|
||||
raise ValidationError('collab.update_text is not supported for non-text fields. Use collab.update_key instead.')
|
||||
|
||||
version = content['version']
|
||||
# TODO: reject updates for versions that are too old
|
||||
|
@ -662,7 +664,7 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
for u in e.data.get('updates', [])] for e in over_updates])),
|
||||
)
|
||||
if not updates:
|
||||
return None
|
||||
raise ValidationError('No updates')
|
||||
|
||||
# Update in DB
|
||||
changes = updates[0].changes
|
||||
|
@ -697,8 +699,8 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
@transaction.atomic()
|
||||
def collab_create(self, content):
|
||||
obj, path, definition = self.get_object_for_update(content.get('path'))
|
||||
if not obj or not definition or definition.type != FieldDataType.LIST:
|
||||
return None
|
||||
if not definition or definition.type != FieldDataType.LIST:
|
||||
raise ValidationError('collab.create is only supported for list fields')
|
||||
|
||||
# Update DB
|
||||
updated_data = obj.data
|
||||
|
@ -726,16 +728,16 @@ class ProjectReportingConsumer(WebsocketConsumerBase):
|
|||
@transaction.atomic()
|
||||
def collab_delete(self, content):
|
||||
obj, path, definition = self.get_object_for_update(content.get('path'))
|
||||
if not obj or not definition:
|
||||
return None
|
||||
if not definition:
|
||||
raise ValidationError('collab.delete is only supported for fields')
|
||||
|
||||
updated_data = obj.data
|
||||
lst = get_value_at_path(updated_data, path[1:-1])
|
||||
if not isinstance(lst, list):
|
||||
return None
|
||||
raise ValidationError('collab.delete is only supported for fields')
|
||||
index = int(path[-1][1:-1] if path[-1].startswith('[') and path[-1].endswith(']') else path[-1])
|
||||
if not (0 <= index < len(lst)):
|
||||
return None
|
||||
raise ValidationError('Invalid list index')
|
||||
lst.pop(index)
|
||||
serializer = (ReportSectionSerializer if isinstance(obj, ReportSection) else PentestFindingSerializer)(instance=obj, data={'data': updated_data}, partial=True)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
|
|
@ -130,7 +130,7 @@ export function useCollab<T = any>(storeState: CollabStoreState<T>) {
|
|||
})
|
||||
storeState.websocket.addEventListener('close', (event) => {
|
||||
// Error handling
|
||||
if (event.code === 4443 || (event.code === 1006 && storeState.connectionState === CollabConnectionState.CONNECTING)) {
|
||||
if (event.code === 4443) {
|
||||
storeState.connectionError = { error: event, message: event.reason || 'Permission denied' };
|
||||
} else if (storeState.connectionState === CollabConnectionState.CONNECTING) {
|
||||
storeState.connectionError = { error: event, message: event.reason || 'Failed to establish connection' };
|
||||
|
@ -232,6 +232,9 @@ export function useCollab<T = any>(storeState: CollabStoreState<T>) {
|
|||
}),
|
||||
};
|
||||
}
|
||||
} else if (msgData.type === 'error') {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Received error from websocket:', msgData);
|
||||
} else if (msgData.type === 'ping') {
|
||||
// Do nothing
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue