mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-08 12:45:55 +08:00
fix(server): sync permission check (#15123)
fix #15121 #### PR Dependency Tree * **PR #15123** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Security Improvements** * Enforced document-level `Doc.Read`/`Doc.Update` checks for key sync websocket operations, including filtering workspace doc timestamp results to only readable documents. * Improved remote permission handling: once a remote denies access, syncing stops for the affected document and retry behavior is suppressed. * **Improvements** * `delete-doc` now relies on server acknowledgment and returns an explicit `{ success: true }`. * Websocket acknowledgment errors are now normalized for consistent error details. * **Tests** * Expanded permission-denied and websocket error-handling coverage, including timestamp filtering and no-retry behavior after permission denial. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -22,6 +22,12 @@ interface CloudDocStorageOptions extends DocStorageOptions {
|
||||
type: SpaceType;
|
||||
}
|
||||
|
||||
function createWebsocketError(error: { name: string; message: string }) {
|
||||
const err = new Error(error.message);
|
||||
err.name = error.name;
|
||||
return err;
|
||||
}
|
||||
|
||||
export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
static readonly identifier = 'CloudDocStorage';
|
||||
|
||||
@@ -88,7 +94,7 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
return null;
|
||||
}
|
||||
// TODO: use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -111,7 +117,7 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
return null;
|
||||
}
|
||||
// TODO: use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -132,7 +138,7 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
|
||||
if ('error' in response) {
|
||||
// TODO(@forehalo): use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -153,7 +159,7 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
|
||||
if ('error' in response) {
|
||||
// TODO: use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -174,7 +180,7 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
|
||||
if ('error' in response) {
|
||||
// TODO(@forehalo): use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return Object.entries(response.data).reduce((ret, [docId, timestamp]) => {
|
||||
@@ -184,11 +190,16 @@ export class CloudDocStorage extends DocStorageBase<CloudDocStorageOptions> {
|
||||
}
|
||||
|
||||
override async deleteDoc(docId: string) {
|
||||
this.socket.emit('space:delete-doc', {
|
||||
const response = await this.socket.emitWithAck('space:delete-doc', {
|
||||
spaceType: this.spaceType,
|
||||
spaceId: this.spaceId,
|
||||
docId: this.idConverter.newIdToOldId(docId),
|
||||
});
|
||||
|
||||
if ('error' in response) {
|
||||
// TODO(@forehalo): use [UserFriendlyError]
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
protected async setDocSnapshot() {
|
||||
@@ -224,7 +235,7 @@ class CloudDocStorageConnection extends SocketConnection {
|
||||
});
|
||||
|
||||
if ('error' in res) {
|
||||
throw new Error(res.error.message);
|
||||
throw createWebsocketError(res.error);
|
||||
}
|
||||
|
||||
if (!this.idConverter) {
|
||||
@@ -272,7 +283,7 @@ class CloudDocStorageConnection extends SocketConnection {
|
||||
return null;
|
||||
}
|
||||
// TODO: use [UserFriendlyError]
|
||||
throw new Error(response.error.message);
|
||||
throw createWebsocketError(response.error);
|
||||
}
|
||||
|
||||
return base64ToUint8Array(response.data.missing);
|
||||
|
||||
@@ -121,7 +121,10 @@ interface ClientEvents {
|
||||
timestamp: number;
|
||||
},
|
||||
];
|
||||
'space:delete-doc': { spaceType: string; spaceId: string; docId: string };
|
||||
'space:delete-doc': [
|
||||
{ spaceType: string; spaceId: string; docId: string },
|
||||
{ success?: true },
|
||||
];
|
||||
|
||||
'telemetry:batch': [TelemetryBatch, TelemetryAck];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user