diff --git a/.versions b/.versions index 8a66b95..55ebfad 100644 --- a/.versions +++ b/.versions @@ -21,7 +21,7 @@ fetch@0.1.1 geojson-utils@1.0.10 id-map@1.1.1 inter-process-messaging@0.1.1 -local-test:ostrio:files@2.2.1 +local-test:ostrio:files@2.3.0 logging@1.3.1 meteor@1.10.0 minimongo@1.8.0 @@ -35,7 +35,7 @@ mongo-id@1.0.8 npm-mongo@4.3.1 ordered-dict@1.1.0 ostrio:cookies@2.7.2 -ostrio:files@2.2.1 +ostrio:files@2.3.0 promise@0.12.0 random@1.2.0 react-fast-refresh@0.2.3 diff --git a/client.js b/client.js index 8514f5b..111c058 100644 --- a/client.js +++ b/client.js @@ -8,9 +8,9 @@ import FilesCollectionCore from './core.js'; import { formatFleURL, helpers } from './lib.js'; const NOOP = () => { }; -const allowedParams = ['debug', 'ddp', 'schema', 'public', 'chunkSize', 'downloadRoute', 'collection', 'collectionName', 'namingFunction', 'onBeforeUpload', 'allowClientCode', 'onbeforeunloadMessage', 'disableUpload', 'disableSetTokenCookie', 'allowQueryStringCookies']; +const allowedParams = ['allowClientCode', 'allowQueryStringCookies', 'chunkSize', 'collection', 'collectionName', 'ddp', 'debug', 'disableSetTokenCookie', 'disableUpload', 'downloadRoute', 'namingFunction', 'onBeforeUpload', 'onbeforeunloadMessage', 'public', 'sanitize', 'schema']; -/* +/** * @locus Anywhere * @class FilesCollection * @param config {Object} - [Both] Configuration object with next properties: @@ -31,6 +31,7 @@ const allowedParams = ['debug', 'ddp', 'schema', 'public', 'chunkSize', 'downloa * @param config.disableUpload {Boolean} - Disable file upload, useful for server only solutions * @param config.disableSetTokenCookie {Boolean} - Disable cookie setting. Useful when you use multiple file collections or when you want to implement your own authorization. * @param config.allowQueryStringCookies {Boolean} - Allow passing Cookies in a query string (in URL). Primary should be used only in Cordova environment. Note: this option will be used only on Cordova. Default: `false` + * @param config.sanitize {Function} - Override default sanitize function * @summary Create new instance of FilesCollection */ class FilesCollection extends FilesCollectionCore { @@ -173,7 +174,7 @@ class FilesCollection extends FilesCollectionCore { }; } - /* + /** * @locus Anywhere * @memberOf FilesCollection * @name _getMimeType @@ -194,7 +195,7 @@ class FilesCollection extends FilesCollectionCore { return mime; } - /* + /** * @locus Anywhere * @memberOf FilesCollection * @name _getUser @@ -217,7 +218,7 @@ class FilesCollection extends FilesCollectionCore { return result; } - /* + /** * @locus Client * @memberOf FilesCollection * @name insert @@ -251,13 +252,13 @@ class FilesCollection extends FilesCollectionCore { */ insert(config, autoStart = true) { if (this.disableUpload) { - console.warn('[FilesCollection] [insert()] Upload is disabled with [disableUpload]!'); + Meteor._debug('[FilesCollection] [insert()] Upload is disabled with [disableUpload]!'); return {}; } return (new UploadInstance(config, this))[autoStart ? 'start' : 'manual'](); } - /* + /** * @locus Anywhere * @memberOf FilesCollection * @name remove diff --git a/docs/constructor.md b/docs/constructor.md index 3122132..8446e9d 100644 --- a/docs/constructor.md +++ b/docs/constructor.md @@ -873,6 +873,23 @@ Useful when you use multiple file collections or when you want to implement your own authorization. + + + config.sanitize {Function} + + + Server (*accepted, but no used on the Client*) + + + Sanitizer for sensitive Strings; Overrides default sanitize() method of FilesCollection instance. Primary used for FSName and fileId. Very low-level. Warning: use with caution! + + + Default function + + + Read more in #847, wekan/#4638, and wekan/#4640 + + config._preCollection {Mongo.Collection} diff --git a/package.js b/package.js index a32abfe..9d52290 100755 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'ostrio:files', - version: '2.2.1', + version: '2.3.0', summary: 'Upload files to a server or 3rd party storage: AWS:S3, GridFS, DropBox, and other', git: 'https://github.com/veliovgroup/Meteor-Files', documentation: 'README.md' diff --git a/server.js b/server.js index bf11de6..30dd9b2 100644 --- a/server.js +++ b/server.js @@ -111,6 +111,7 @@ const createIndex = async (collection, keys, opts) => { * @param config.disableDownload {Boolean} - Disable file download (serving), useful for file management only solutions * @param config.allowedOrigins {Regex|Boolean} - [Server] Regex of Origins that are allowed CORS access or `false` to disable completely. Defaults to `/^http:\/\/localhost:12[0-9]{3}$/` for allowing Meteor-Cordova builds access * @param config.allowQueryStringCookies {Boolean} - Allow passing Cookies in a query string (in URL). Primary should be used only in Cordova environment. Note: this option will be used only on Cordova. Default: `false` + * @param config.sanitize {Function} - Override default sanitize function * @param config._preCollection {Mongo.Collection} - [Server] Mongo preCollection Instance * @param config._preCollectionName {String} - [Server] preCollection name * @summary Create new instance of FilesCollection @@ -121,39 +122,40 @@ class FilesCollection extends FilesCollectionCore { let storagePath; if (config) { ({ - storagePath, - debug: this.debug, - schema: this.schema, - public: this.public, - strict: this.strict, - getUser: this.getUser, + _preCollection: this._preCollection, + _preCollectionName: this._preCollectionName, + allowClientCode: this.allowClientCode, + allowedOrigins: this.allowedOrigins, + allowQueryStringCookies: this.allowQueryStringCookies, + cacheControl: this.cacheControl, chunkSize: this.chunkSize, - protected: this.protected, collection: this.collection, - permissions: this.permissions, - cacheControl: this.cacheControl, + collectionName: this.collectionName, + continueUploadTTL: this.continueUploadTTL, + debug: this.debug, + disableDownload: this.disableDownload, + disableUpload: this.disableUpload, + downloadCallback: this.downloadCallback, downloadRoute: this.downloadRoute, - onAfterUpload: this.onAfterUpload, + getUser: this.getUser, + integrityCheck: this.integrityCheck, + interceptDownload: this.interceptDownload, + interceptRequest: this.interceptRequest, + namingFunction: this.namingFunction, onAfterRemove: this.onAfterRemove, - disableUpload: this.disableUpload, + onAfterUpload: this.onAfterUpload, onBeforeRemove: this.onBeforeRemove, - integrityCheck: this.integrityCheck, - collectionName: this.collectionName, onBeforeUpload: this.onBeforeUpload, - namingFunction: this.namingFunction, - responseHeaders: this.responseHeaders, - disableDownload: this.disableDownload, - allowedOrigins: this.allowedOrigins, - allowClientCode: this.allowClientCode, - downloadCallback: this.downloadCallback, onInitiateUpload: this.onInitiateUpload, - interceptRequest: this.interceptRequest, - interceptDownload: this.interceptDownload, - continueUploadTTL: this.continueUploadTTL, parentDirPermissions: this.parentDirPermissions, - allowQueryStringCookies: this.allowQueryStringCookies, - _preCollection: this._preCollection, - _preCollectionName: this._preCollectionName, + permissions: this.permissions, + protected: this.protected, + public: this.public, + responseHeaders: this.responseHeaders, + sanitize: this.sanitize, + schema: this.schema, + storagePath, + strict: this.strict, } = config); } @@ -288,6 +290,10 @@ class FilesCollection extends FilesCollectionCore { this.continueUploadTTL = 10800; } + if (!helpers.isFunction(this.sanitize)) { + this.sanitize = helpers.sanitize; + } + if (!helpers.isFunction(this.responseHeaders)) { this.responseHeaders = (responseCode, fileRef, versionRef) => { const headers = {}; @@ -571,7 +577,7 @@ class FilesCollection extends FilesCollectionCore { if (httpReq.headers['x-start'] !== '1') { // CHUNK UPLOAD SCENARIO: opts = { - fileId: helpers.sanitize(httpReq.headers['x-fileid'], 20, 'a') + fileId: this.sanitize(httpReq.headers['x-fileid'], 20, 'a') }; if (httpReq.headers['x-eof'] === '1') { @@ -647,7 +653,7 @@ class FilesCollection extends FilesCollectionCore { } if (opts.fileId) { - opts.fileId = helpers.sanitize(opts.fileId, 20, 'a'); + opts.fileId = this.sanitize(opts.fileId, 20, 'a'); } this._debug(`[FilesCollection] [File Start HTTP] ${opts.file.name || '[no-name]'} - ${opts.fileId}`); @@ -842,7 +848,7 @@ class FilesCollection extends FilesCollectionCore { check(returnMeta, Match.Optional(Boolean)); - opts.fileId = helpers.sanitize(opts.fileId, 20, 'a'); + opts.fileId = self.sanitize(opts.fileId, 20, 'a'); self._debug(`[FilesCollection] [File Start Method] ${opts.file.name} - ${opts.fileId}`); opts.___s = true; @@ -886,7 +892,7 @@ class FilesCollection extends FilesCollectionCore { chunkId: Match.Optional(Number) }); - opts.fileId = helpers.sanitize(opts.fileId, 20, 'a'); + opts.fileId = self.sanitize(opts.fileId, 20, 'a'); if (opts.binData) { opts.binData = Buffer.from(opts.binData, 'base64'); @@ -984,7 +990,7 @@ class FilesCollection extends FilesCollectionCore { result.ext = extension; result._id = opts.fileId; result.userId = userId || null; - opts.FSName = helpers.sanitize(opts.FSName); + opts.FSName = this.sanitize(opts.FSName); if (this.namingFunction) { opts.FSName = this.namingFunction(opts); @@ -1225,7 +1231,7 @@ class FilesCollection extends FilesCollectionCore { check(callback, Match.Optional(Function)); check(proceedAfterUpload, Match.Optional(Boolean)); - opts.fileId = opts.fileId && helpers.sanitize(opts.fileId, 20, 'a'); + opts.fileId = opts.fileId && this.sanitize(opts.fileId, 20, 'a'); const fileId = opts.fileId || Random.id(); const fsName = this.namingFunction ? this.namingFunction(opts) : fileId; const fileName = (opts.name || opts.fileName) ? (opts.name || opts.fileName) : fsName; @@ -1340,7 +1346,7 @@ class FilesCollection extends FilesCollectionCore { opts.timeout = 360000; } - const fileId = (opts.fileId && helpers.sanitize(opts.fileId, 20, 'a')) || Random.id(); + const fileId = (opts.fileId && this.sanitize(opts.fileId, 20, 'a')) || Random.id(); const fsName = this.namingFunction ? this.namingFunction(opts) : fileId; const pathParts = url.split('/'); const fileName = (opts.name || opts.fileName) ? (opts.name || opts.fileName) : pathParts[pathParts.length - 1].split('?')[0] || fsName; @@ -1556,7 +1562,7 @@ class FilesCollection extends FilesCollectionCore { userId: opts.userId, extension, _storagePath: path.replace(`${nodePath.sep}${opts.fileName}`, ''), - fileId: (opts.fileId && helpers.sanitize(opts.fileId, 20, 'a')) || null + fileId: (opts.fileId && this.sanitize(opts.fileId, 20, 'a')) || null });