Prevents accidental overwriting of another users work

This commit is contained in:
Tom Moor
2017-11-25 13:04:54 -08:00
parent 7945abbe54
commit 98f317c74a
6 changed files with 36 additions and 2 deletions

View File

@ -39,6 +39,7 @@ class Document extends BaseModel {
updatedBy: User; updatedBy: User;
url: string; url: string;
views: number; views: number;
revision: number;
data: Object; data: Object;
@ -168,6 +169,7 @@ class Document extends BaseModel {
id: this.id, id: this.id,
title: this.title, title: this.title,
text: this.text, text: this.text,
lastRevision: this.revision,
}); });
} else { } else {
if (!this.title) { if (!this.title) {

View File

@ -232,7 +232,7 @@ class DocumentScene extends Component {
message={DISCARD_CHANGES} message={DISCARD_CHANGES}
/> />
<Editor <Editor
key={document.id} key={`${document.id}-${document.revision}`}
text={document.text} text={document.text}
emoji={document.emoji} emoji={document.emoji}
onImageUploadStart={this.onImageUploadStart} onImageUploadStart={this.onImageUploadStart}

View File

@ -45,6 +45,15 @@ Object {
} }
`; `;
exports[`#documents.update should fail if document lastRevision does not match 1`] = `
Object {
"error": "document_has_changed_since_last_revision",
"message": "Document has changed since last revision",
"ok": false,
"status": 400,
}
`;
exports[`#documents.viewed should require authentication 1`] = ` exports[`#documents.viewed should require authentication 1`] = `
Object { Object {
"error": "authentication_required", "error": "authentication_required",

View File

@ -242,7 +242,7 @@ router.post('documents.create', auth(), async ctx => {
}); });
router.post('documents.update', auth(), async ctx => { router.post('documents.update', auth(), async ctx => {
const { id, title, text } = ctx.body; const { id, title, text, lastRevision } = ctx.body;
ctx.assertPresent(id, 'id is required'); ctx.assertPresent(id, 'id is required');
ctx.assertPresent(title || text, 'title or text is required'); ctx.assertPresent(title || text, 'title or text is required');
@ -250,6 +250,10 @@ router.post('documents.update', auth(), async ctx => {
const document = await Document.findById(id); const document = await Document.findById(id);
const collection = document.collection; const collection = document.collection;
if (lastRevision && lastRevision !== document.revisionCount) {
throw httpErrors.BadRequest('Document has changed since last revision');
}
authDocumentForUser(ctx, document); authDocumentForUser(ctx, document);
// Update document // Update document

View File

@ -277,6 +277,7 @@ describe('#documents.update', async () => {
id: document.id, id: document.id,
title: 'Updated title', title: 'Updated title',
text: 'Updated text', text: 'Updated text',
lastRevision: document.revision,
}, },
}); });
const body = await res.json(); const body = await res.json();
@ -287,6 +288,23 @@ describe('#documents.update', async () => {
expect(body.data.collection.documents[1].title).toBe('Updated title'); expect(body.data.collection.documents[1].title).toBe('Updated title');
}); });
it('should fail if document lastRevision does not match', async () => {
const { user, document } = await seed();
const res = await server.post('/api/documents.update', {
body: {
token: user.getJwtToken(),
id: document.id,
text: 'Updated text',
lastRevision: 123,
},
});
const body = await res.json();
expect(res.status).toEqual(400);
expect(body).toMatchSnapshot();
});
it('should update document details for children', async () => { it('should update document details for children', async () => {
const { user, document, collection } = await seed(); const { user, document, collection } = await seed();
collection.documentStructure = [ collection.documentStructure = [

View File

@ -36,6 +36,7 @@ async function present(ctx: Object, document: Document, options: ?Options) {
team: document.teamId, team: document.teamId,
collaborators: [], collaborators: [],
starred: !!(document.starred && document.starred.length), starred: !!(document.starred && document.starred.length),
revision: document.revisionCount,
collectionId: document.atlasId, collectionId: document.atlasId,
collaboratorCount: undefined, collaboratorCount: undefined,
collection: undefined, collection: undefined,