diff --git a/app/authorization/server/functions/canAccessRoom.js b/app/authorization/server/functions/canAccessRoom.js index af2035593a5..246fbada423 100644 --- a/app/authorization/server/functions/canAccessRoom.js +++ b/app/authorization/server/functions/canAccessRoom.js @@ -1,5 +1,5 @@ import { settings } from '../../../settings'; -import { Subscriptions } from '../../../models'; +import Subscriptions from '../../../models/server/models/Subscriptions'; import { hasPermission } from './hasPermission'; export const roomAccessValidators = [ diff --git a/app/authorization/server/functions/hasPermission.js b/app/authorization/server/functions/hasPermission.js index 27da342c3a5..90226dd8498 100644 --- a/app/authorization/server/functions/hasPermission.js +++ b/app/authorization/server/functions/hasPermission.js @@ -1,4 +1,5 @@ -import { Roles, Permissions } from '../../../models'; +import Roles from '../../../models/server/models/Roles'; +import Permissions from '../../../models/server/models/Permissions'; function atLeastOne(userId, permissions = [], scope) { return permissions.some((permissionId) => { diff --git a/app/file-upload/client/lib/fileUploadHandler.js b/app/file-upload/client/lib/fileUploadHandler.js index f090356be40..9804c0ecca3 100644 --- a/app/file-upload/client/lib/fileUploadHandler.js +++ b/app/file-upload/client/lib/fileUploadHandler.js @@ -4,22 +4,15 @@ import { Tracker } from 'meteor/tracker'; import { UploadFS } from 'meteor/jalik:ufs'; import { FileUploadBase } from '../../lib/FileUploadBase'; import { Uploads, Avatars } from '../../../models'; -import { FileUpload } from '../../lib/FileUpload'; new UploadFS.Store({ collection: Uploads.model, name: 'Uploads', - filter: new UploadFS.Filter({ - onCheck: FileUpload.validateFileUpload, - }), }); new UploadFS.Store({ collection: Avatars.model, name: 'Avatars', - filter: new UploadFS.Filter({ - onCheck: FileUpload.validateFileUpload, - }), }); export const fileUploadHandler = (directive, meta, file) => { diff --git a/app/file-upload/lib/FileUpload.js b/app/file-upload/lib/FileUpload.js deleted file mode 100644 index e8baa971549..00000000000 --- a/app/file-upload/lib/FileUpload.js +++ /dev/null @@ -1,61 +0,0 @@ -import { Meteor } from 'meteor/meteor'; -import { Match } from 'meteor/check'; -import { TAPi18n } from 'meteor/tap:i18n'; -import { Rooms, Settings } from '../../models'; -import { settings } from '../../settings'; -import { fileUploadIsValidContentType } from '../../utils'; -import { canAccessRoom } from '../../authorization'; -import filesize from 'filesize'; - -let maxFileSize = 0; - -export const FileUpload = { - validateFileUpload(file) { - if (!Match.test(file.rid, String)) { - return false; - } - - // livechat users can upload files but they don't have an userId - const user = file.userId ? Meteor.users.findOne(file.userId) : null; - - const room = Rooms.findOneById(file.rid); - const directMessageAllow = settings.get('FileUpload_Enabled_Direct'); - const fileUploadAllowed = settings.get('FileUpload_Enabled'); - if (canAccessRoom(room, user, file) !== true) { - return false; - } - const language = user ? user.language : 'en'; - if (!fileUploadAllowed) { - const reason = TAPi18n.__('FileUpload_Disabled', language); - throw new Meteor.Error('error-file-upload-disabled', reason); - } - - if (!directMessageAllow && room.t === 'd') { - const reason = TAPi18n.__('File_not_allowed_direct_messages', language); - throw new Meteor.Error('error-direct-message-file-upload-not-allowed', reason); - } - - // -1 maxFileSize means there is no limit - if (maxFileSize > -1 && file.size > maxFileSize) { - const reason = TAPi18n.__('File_exceeds_allowed_size_of_bytes', { - size: filesize(maxFileSize), - }, language); - throw new Meteor.Error('error-file-too-large', reason); - } - - if (!fileUploadIsValidContentType(file.type)) { - const reason = TAPi18n.__('File_type_is_not_accepted', language); - throw new Meteor.Error('error-invalid-file-type', reason); - } - - return true; - }, -}; - -settings.get('FileUpload_MaxFileSize', function(key, value) { - try { - maxFileSize = parseInt(value); - } catch (e) { - maxFileSize = Settings.findOneById('FileUpload_MaxFileSize').packageValue; - } -}); diff --git a/app/file-upload/lib/FileUploadBase.js b/app/file-upload/lib/FileUploadBase.js index a8ffd7fa572..e3e08b49d1b 100644 --- a/app/file-upload/lib/FileUploadBase.js +++ b/app/file-upload/lib/FileUploadBase.js @@ -35,7 +35,6 @@ UploadFS.config.defaultStorePermissions = new UploadFS.StorePermissions({ }, }); - export class FileUploadBase { constructor(store, meta, file) { this.id = Random.id(); diff --git a/app/file-upload/server/lib/FileUpload.js b/app/file-upload/server/lib/FileUpload.js index 792df542467..d07f5e2a8f9 100644 --- a/app/file-upload/server/lib/FileUpload.js +++ b/app/file-upload/server/lib/FileUpload.js @@ -7,15 +7,34 @@ import Future from 'fibers/future'; import sharp from 'sharp'; import { Cookies } from 'meteor/ostrio:cookies'; import { UploadFS } from 'meteor/jalik:ufs'; +import { Match } from 'meteor/check'; +import { TAPi18n } from 'meteor/tap:i18n'; import { settings } from '../../../settings'; -import * as Models from '../../../models'; -import { FileUpload as _FileUpload } from '../../lib/FileUpload'; -import { roomTypes } from '../../../utils'; -import { hasPermission } from '../../../authorization'; +import Uploads from '../../../models/server/models/Uploads'; +import UserDataFiles from '../../../models/server/models/UserDataFiles'; +import Avatars from '../../../models/server/models/Avatars'; +import Users from '../../../models/server/models/Users'; +import Rooms from '../../../models/server/models/Rooms'; +import Settings from '../../../models/server/models/Settings'; +import { roomTypes } from '../../../utils/server/lib/roomTypes'; +import { hasPermission } from '../../../authorization/server/functions/hasPermission'; +import { canAccessRoom } from '../../../authorization/server/functions/canAccessRoom'; +import { fileUploadIsValidContentType } from '../../../utils/lib/fileUploadRestrictions'; +import filesize from 'filesize'; const cookie = new Cookies(); +let maxFileSize = 0; -export const FileUpload = Object.assign(_FileUpload, { +settings.get('FileUpload_MaxFileSize', function(key, value) { + try { + maxFileSize = parseInt(value); + } catch (e) { + maxFileSize = Settings.findOneById('FileUpload_MaxFileSize').packageValue; + } +}); + + +export const FileUpload = { handlers: {}, configureUploadsStore(store, name, options) { @@ -28,9 +47,50 @@ export const FileUpload = Object.assign(_FileUpload, { }, options, FileUpload[`default${ type }`]())); }, + validateFileUpload(file) { + if (!Match.test(file.rid, String)) { + return false; + } + + // livechat users can upload files but they don't have an userId + const user = file.userId ? Meteor.users.findOne(file.userId) : null; + + const room = Rooms.findOneById(file.rid); + const directMessageAllow = settings.get('FileUpload_Enabled_Direct'); + const fileUploadAllowed = settings.get('FileUpload_Enabled'); + if (canAccessRoom(room, user, file) !== true) { + return false; + } + const language = user ? user.language : 'en'; + if (!fileUploadAllowed) { + const reason = TAPi18n.__('FileUpload_Disabled', language); + throw new Meteor.Error('error-file-upload-disabled', reason); + } + + if (!directMessageAllow && room.t === 'd') { + const reason = TAPi18n.__('File_not_allowed_direct_messages', language); + throw new Meteor.Error('error-direct-message-file-upload-not-allowed', reason); + } + + // -1 maxFileSize means there is no limit + if (maxFileSize > -1 && file.size > maxFileSize) { + const reason = TAPi18n.__('File_exceeds_allowed_size_of_bytes', { + size: filesize(maxFileSize), + }, language); + throw new Meteor.Error('error-file-too-large', reason); + } + + if (!fileUploadIsValidContentType(file.type)) { + const reason = TAPi18n.__('File_type_is_not_accepted', language); + throw new Meteor.Error('error-invalid-file-type', reason); + } + + return true; + }, + defaultUploads() { return { - collection: Models.Uploads.model, + collection: Uploads.model, filter: new UploadFS.Filter({ onCheck: FileUpload.validateFileUpload, }), @@ -52,7 +112,7 @@ export const FileUpload = Object.assign(_FileUpload, { defaultAvatars() { return { - collection: Models.Avatars.model, + collection: Avatars.model, // filter: new UploadFS.Filter({ // onCheck: FileUpload.validateFileUpload // }), @@ -66,7 +126,7 @@ export const FileUpload = Object.assign(_FileUpload, { defaultUserDataFiles() { return { - collection: Models.UserDataFiles.model, + collection: UserDataFiles.model, getPath(file) { return `${ settings.get('uniqueID') }/uploads/userData/${ file.userId }`; }, @@ -140,7 +200,7 @@ export const FileUpload = Object.assign(_FileUpload, { }, resizeImagePreview(file) { - file = Models.Uploads.findOneById(file._id); + file = Uploads.findOneById(file._id); file = FileUpload.addExtensionTo(file); const image = FileUpload.getStore('Uploads')._store.getReadStream(file._id, file); @@ -215,12 +275,12 @@ export const FileUpload = Object.assign(_FileUpload, { throw new Meteor.Error('error-not-allowed', 'Change avatar is not allowed'); } // update file record to match user's username - const user = Models.Users.findOneById(file.userId); - const oldAvatar = Models.Avatars.findOneByName(user.username); + const user = Users.findOneById(file.userId); + const oldAvatar = Avatars.findOneByName(user.username); if (oldAvatar) { - Models.Avatars.deleteFile(oldAvatar._id); + Avatars.deleteFile(oldAvatar._id); } - Models.Avatars.updateFileNameById(file._id, user.username); + Avatars.updateFileNameById(file._id, user.username); // console.log('upload finished ->', file); }, @@ -238,8 +298,8 @@ export const FileUpload = Object.assign(_FileUpload, { rc_room_type = cookie.get('rc_room_type', headers.cookie); } - const isAuthorizedByCookies = rc_uid && rc_token && Models.Users.findOneByIdAndLoginToken(rc_uid, rc_token); - const isAuthorizedByHeaders = headers['x-user-id'] && headers['x-auth-token'] && Models.Users.findOneByIdAndLoginToken(headers['x-user-id'], headers['x-auth-token']); + const isAuthorizedByCookies = rc_uid && rc_token && Users.findOneByIdAndLoginToken(rc_uid, rc_token); + const isAuthorizedByHeaders = headers['x-user-id'] && headers['x-auth-token'] && Users.findOneByIdAndLoginToken(headers['x-user-id'], headers['x-auth-token']); const isAuthorizedByRoom = rc_room_type && roomTypes.getConfig(rc_room_type).canAccessUploadedFile({ rc_uid, rc_rid, rc_token }); return isAuthorizedByCookies || isAuthorizedByHeaders || isAuthorizedByRoom; }, @@ -311,7 +371,7 @@ export const FileUpload = Object.assign(_FileUpload, { return false; }, -}); +}; export class FileUploadClass { constructor({ name, model, store, get, insert, getStore, copy }) { @@ -345,7 +405,16 @@ export class FileUploadClass { } getModelFromName() { - return Models[this.name.split(':')[1]]; + const modelsAvailable = { + Avatars, + Uploads, + UserDataFiles, + }; + const modelName = this.name.split(':')[1]; + if (!modelsAvailable[modelName]) { + throw new Error('Invalid Model for FileUpload'); + } + return modelsAvailable[modelName]; } delete(fileId) { diff --git a/app/models/server/models/Messages.js b/app/models/server/models/Messages.js index 7cbbba4d16c..ec617d87db6 100644 --- a/app/models/server/models/Messages.js +++ b/app/models/server/models/Messages.js @@ -4,6 +4,7 @@ import { Base } from './_Base'; import Rooms from './Rooms'; import Users from './Users'; import { settings } from '../../../settings/server/functions/settings'; +import { FileUpload } from '../../../file-upload/server/lib/FileUpload'; import _ from 'underscore'; export class Messages extends Base { @@ -903,10 +904,6 @@ export class Messages extends Base { } async removeFilesByRoomId(roomId) { - if (!this.FileUpload) { - const { FileUpload } = await import('../../../file-upload'); - this.FileUpload = FileUpload; - } this.find({ rid: roomId, 'file._id': { @@ -916,7 +913,7 @@ export class Messages extends Base { fields: { 'file._id': 1, }, - }).fetch().forEach((document) => this.FileUpload.getStore('Uploads').deleteById(document.file._id)); + }).fetch().forEach((document) => FileUpload.getStore('Uploads').deleteById(document.file._id)); } getMessageByFileId(fileID) { diff --git a/app/settings/server/functions/settings.js b/app/settings/server/functions/settings.js index a7caf4fe6cc..a0a53a69ceb 100644 --- a/app/settings/server/functions/settings.js +++ b/app/settings/server/functions/settings.js @@ -1,6 +1,6 @@ import { Meteor } from 'meteor/meteor'; import { settings } from '../../lib/settings'; -import { Settings } from '../../../models'; +import Settings from '../../../models/server/models/Settings'; import _ from 'underscore'; const blockedSettings = {}; diff --git a/app/utils/lib/RoomTypeConfig.js b/app/utils/lib/RoomTypeConfig.js index f315a2a1941..1c29a5b1d22 100644 --- a/app/utils/lib/RoomTypeConfig.js +++ b/app/utils/lib/RoomTypeConfig.js @@ -1,8 +1,12 @@ import { Meteor } from 'meteor/meteor'; import { Random } from 'meteor/random'; -import { Users } from '../../models'; import { settings } from '../../settings'; +let Users; +if (Meteor.isServer) { + Users = require('../../models/server/models/Users').default; +} + export const RoomSettingsEnum = { NAME: 'roomName', TOPIC: 'roomTopic', @@ -232,7 +236,10 @@ export class RoomTypeConfig { * @return {object} Sender's object from db */ getMsgSender(senderId) { - return Meteor.isServer ? Users.findOneById(senderId) : {}; + if (Meteor.isServer && Users) { + return Users.findOneById(senderId); + } + return {}; } /**