(core) uncheck FullCopy special when copying/forking a document

Summary:
When a document has an exception to allow copies,
unset that option on any copies of the document.

Test Plan: added test

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2794
pull/21/head
Paul Fitzpatrick 3 years ago
parent 729774552f
commit 0e22716761

@ -15,6 +15,7 @@ import { docSessionFromRequest, makeExceptionalDocSession, OptDocSession } from
import { DocWorker } from "app/server/lib/DocWorker";
import { IDocWorkerMap } from "app/server/lib/DocWorkerMap";
import { expressWrap } from 'app/server/lib/expressWrap';
import { filterDocumentInPlace } from "app/server/lib/filterUtils";
import { GristServer } from 'app/server/lib/GristServer';
import { HashUtil } from 'app/server/lib/HashUtil';
import { makeForkIds } from "app/server/lib/idUtils";
@ -218,7 +219,8 @@ export class DocWorkerApi {
const docId = stringParam(req.params.docId);
const srcDocId = stringParam(req.body.srcDocId);
if (srcDocId !== req.specialPermit?.otherDocId) { throw new Error('access denied'); }
await this._docManager.storageManager.prepareFork(srcDocId, docId);
const fname = await this._docManager.storageManager.prepareFork(srcDocId, docId);
await filterDocumentInPlace(docSessionFromRequest(req), fname);
res.json({srcDocId, docId});
}));

@ -90,8 +90,10 @@ export class DocStorageManager implements IDocStorageManager {
// nothing to do
}
public async prepareFork(srcDocName: string, destDocName: string): Promise<void> {
// nothing to do
public async prepareFork(srcDocName: string, destDocName: string): Promise<string> {
// This is implemented only to support old tests.
await fse.copy(this.getPath(srcDocName), this.getPath(destDocName));
return this.getPath(destDocName);
}
/**

@ -7,7 +7,8 @@ import {ActionHistoryImpl} from 'app/server/lib/ActionHistoryImpl';
import {assertAccess, getOrSetDocAuth, getUserId, RequestWithLogin} from 'app/server/lib/Authorizer';
import {Client} from 'app/server/lib/Client';
import * as Comm from 'app/server/lib/Comm';
import {DocSession} from 'app/server/lib/DocSession';
import {DocSession, docSessionFromRequest} from 'app/server/lib/DocSession';
import {filterDocumentInPlace} from 'app/server/lib/filterUtils';
import {IDocStorageManager} from 'app/server/lib/IDocStorageManager';
import * as log from 'app/server/lib/log';
import {integerParam, optStringParam, stringParam} from 'app/server/lib/requestUtils';
@ -75,6 +76,7 @@ export class DocWorker {
// If template flag is on, remove data and history from the download.
await removeData(tmpPath);
}
await filterDocumentInPlace(docSessionFromRequest(mreq), tmpPath);
// NOTE: We may want to reconsider the mimeType used for Grist files.
return res.type('application/x-sqlite3')
.download(tmpPath, (optStringParam(req.query.title) || docTitle || 'document') + ".grist", async (err: any) => {

@ -248,8 +248,9 @@ export class HostedStorageManager implements IDocStorageManager {
* Initialize one document from another, associating the result with the current
* worker.
*/
public async prepareFork(srcDocName: string, destDocName: string): Promise<void> {
public async prepareFork(srcDocName: string, destDocName: string): Promise<string> {
await this.prepareLocalDoc(destDocName, srcDocName);
return this.getPath(destDocName);
}
// Gets a copy of the document, eg. for downloading. Returns full file path.

@ -12,7 +12,7 @@ export interface IDocStorageManager {
// AsyncCreate[docName].
prepareLocalDoc(docName: string): Promise<boolean>;
prepareToCreateDoc(docName: string): Promise<void>;
prepareFork(srcDocName: string, destDocName: string): Promise<void>;
prepareFork(srcDocName: string, destDocName: string): Promise<string>; // Returns filename.
listDocs(): Promise<DocEntry[]>;
deleteDoc(docName: string, deletePermanently?: boolean): Promise<void>;

@ -0,0 +1,28 @@
import { OpenMode, SQLiteDB } from 'app/server/lib/SQLiteDB';
import { OptDocSession } from "app/server/lib/DocSession";
/**
* Filter a Grist document when it is copied or downloaded. Changes made:
* - Any FullCopies special rules are removed.
* In the future, the changes could be made conditional on the user. This would
* allow us for example to permit downloads of documents with row-level filters
* in place.
*/
export async function filterDocumentInPlace(docSession: OptDocSession, filename: string) {
// We ignore docSession for now, since no changes are user-dependent yet.
// The change we need to make is simple, so we open the doc as a SQLite DB.
// Note: the change is not entered in document history.
const db = await SQLiteDB.openDBRaw(filename, OpenMode.OPEN_EXISTING);
// Fetch ids of any special resources mentioning FullCopies (ideally there would be
// at most one).
const resourceIds = (await db.all("SELECT id FROM _grist_ACLResources " +
"WHERE tableId='*SPECIAL' AND colIds='FullCopies'"))
.map(row => row.id as number);
if (resourceIds.length > 0) {
// Remove any related rules.
await db.run(`DELETE FROM _grist_ACLRules WHERE resource IN (${resourceIds})`);
// Remove the resources.
await db.run(`DELETE FROM _grist_ACLResources WHERE id IN (${resourceIds})`);
}
await db.close();
}
Loading…
Cancel
Save