diff --git a/lib/core/bucketEntries/usecase.ts b/lib/core/bucketEntries/usecase.ts index a6ca8e09..d846dac4 100644 --- a/lib/core/bucketEntries/usecase.ts +++ b/lib/core/bucketEntries/usecase.ts @@ -47,14 +47,14 @@ export class BucketEntriesUsecase { return count; } - async removeFileFromUser(bucketId: string, fileId: string, userId: string) { + async removeFileFromUser(bucketId: string, fileId: string, userId: User['uuid']) { const bucket = await this.bucketsRepository.findOne({ id: bucketId }); if(!bucket) { throw new BucketNotFoundError(); } - if (bucket.user !== userId) { + if (bucket.userId !== userId) { throw new BucketForbiddenError(); } @@ -86,11 +86,11 @@ export class BucketEntriesUsecase { await this.removeFilesV2([ bucketEntry ]); const bucket = await this.bucketsRepository.findOne({ id: bucketEntry.bucket }); - if (bucket?.user) { - const user = await this.usersRepository.findById(bucket.user); + if (bucket?.userId) { + const user = await this.usersRepository.findByUuid(bucket.userId); if (user) { - await this.usersRepository.addTotalUsedSpaceBytes(bucket.user, - bucketEntry.size!); + await this.usersRepository.addTotalUsedSpaceBytes(user.uuid, - bucketEntry.size!); } } } else { @@ -113,8 +113,8 @@ export class BucketEntriesUsecase { const bucketEntriesGroupedByBucket = lodash.groupBy(bucketEntriesV2, (b) => b.bucket); const buckets = await this.bucketsRepository.findByIds(Object.keys(bucketEntriesGroupedByBucket)); - const bucketsGroupedByUsers = lodash.groupBy(buckets, (b) => b.user); - const storageToModifyPerUser: Record = {}; + const bucketsGroupedByUsers = lodash.groupBy(buckets, (b) => b.userId); + const storageToModifyPerUser: Record = {}; Object.keys(bucketsGroupedByUsers).map((userId) => { storageToModifyPerUser[userId] = 0; diff --git a/lib/core/buckets/MongoDBBucketsRepository.ts b/lib/core/buckets/MongoDBBucketsRepository.ts index c408417d..0c4bb1ec 100644 --- a/lib/core/buckets/MongoDBBucketsRepository.ts +++ b/lib/core/buckets/MongoDBBucketsRepository.ts @@ -23,7 +23,7 @@ export class MongoDBBucketsRepository implements BucketsRepository { } async findByUser(userId: string, limit: number, skip: number): Promise { - const buckets = await this.model.find({ user: userId }).skip(skip).limit(limit).exec(); + const buckets = await this.model.find({ userId }).skip(skip).limit(limit).exec(); return buckets; } @@ -48,9 +48,9 @@ export class MongoDBBucketsRepository implements BucketsRepository { return formatFromMongoToBucket(rawModel); } - destroyByUser(userId: string): Promise { + destroyByUser(userId: Bucket['userId']): Promise { return this.model.deleteMany({ - user: userId, + userId, }); } diff --git a/lib/core/buckets/Repository.ts b/lib/core/buckets/Repository.ts index db3b1f6d..950d1c1c 100644 --- a/lib/core/buckets/Repository.ts +++ b/lib/core/buckets/Repository.ts @@ -1,11 +1,10 @@ -import { User } from '../users/User'; import { Bucket } from './Bucket'; export interface BucketsRepository { findOne(where: Partial): Promise; - findByUser(userId: User['id'], limit: number, skip: number): Promise; + findByUser(userId: Bucket['userId'], limit: number, skip: number): Promise; findByIds(ids: Bucket['id'][]): Promise; find(where: Partial): Promise; - destroyByUser(userId: User['id']): Promise; + destroyByUser(userId: Bucket['userId']): Promise; removeAll(where: Partial): Promise; } diff --git a/lib/core/buckets/usecase.ts b/lib/core/buckets/usecase.ts index 6cb95c7c..accb8dff 100644 --- a/lib/core/buckets/usecase.ts +++ b/lib/core/buckets/usecase.ts @@ -275,7 +275,7 @@ export class BucketsUsecase { } async startUpload( - userId: string, + userId: User['uuid'], bucketId: string, cluster: string[], uploads: { index: number; size: number }[], @@ -284,7 +284,7 @@ export class BucketsUsecase { ) { const [bucket, user] = await Promise.all([ this.bucketsRepository.findOne({ id: bucketId }), - this.usersRepository.findById(userId), + this.usersRepository.findByUuid(userId), ]); if (!user) { @@ -295,7 +295,9 @@ export class BucketsUsecase { throw new BucketNotFoundError(); } - if (bucket.user !== userId) { + const isBucketOwnedByUser = bucket.userId === user.uuid; + + if (!isBucketOwnedByUser) { throw new BucketForbiddenError(); } @@ -318,7 +320,7 @@ export class BucketsUsecase { throw new MaxSpaceUsedError(); } } else { - const usedSpaceBytes = await this.getUserUsage(user.id); + const usedSpaceBytes = await this.getUserUsage(user.email); if ( user.maxSpaceBytes < @@ -418,15 +420,15 @@ export class BucketsUsecase { } async completeUpload( - userId: string, - bucketId: string, + userId: User['uuid'], + bucketId: Bucket['id'], fileIndex: string, shards: ShardWithPossibleMultiUpload[], auth: { username: string; password: string } ): Promise { const [bucket, user, uploads] = await Promise.all([ this.bucketsRepository.findOne({ id: bucketId }), - this.usersRepository.findById(userId), + this.usersRepository.findByUuid(userId), this.uploadsRepository.findByUuids(shards.map((s) => s.uuid)), ]); @@ -438,7 +440,9 @@ export class BucketsUsecase { throw new BucketNotFoundError(); } - if (bucket.user !== userId) { + const isBucketOwnedByUser = bucket.userId === user.uuid; + + if (!isBucketOwnedByUser) { throw new BucketForbiddenError(); } @@ -465,7 +469,7 @@ export class BucketsUsecase { throw new MaxSpaceUsedError(); } } else { - const usedSpaceBytes = await this.getUserUsage(user.id); + const usedSpaceBytes = await this.getUserUsage(user.email); if ( user.maxSpaceBytes < @@ -514,7 +518,7 @@ export class BucketsUsecase { }); await this.bucketEntryShardsRepository.insertMany(bucketEntryShards); - await this.usersRepository.addTotalUsedSpaceBytes(userId, bucketEntrySize); + await this.usersRepository.addTotalUsedSpaceBytes(user.uuid, bucketEntrySize); this.uploadsRepository .deleteManyByUuids(uploads.map((u) => u.uuid)) .catch((err) => { @@ -651,13 +655,13 @@ export class BucketsUsecase { return newShard; } - async listByUserId(userId: User['id'], limit: number, offset: number): Promise { + async listByUserId(userId: User['uuid'], limit: number, offset: number): Promise { const buckets = await this.bucketsRepository.findByUser(userId, limit, offset); return buckets; } - async destroyByUser(userId: User['id']) { + async destroyByUser(userId: User['uuid']) { await this.bucketsRepository.destroyByUser(userId); } } diff --git a/lib/core/frames/MongoDBFramesRepository.ts b/lib/core/frames/MongoDBFramesRepository.ts index d9e93f44..82af4d0a 100644 --- a/lib/core/frames/MongoDBFramesRepository.ts +++ b/lib/core/frames/MongoDBFramesRepository.ts @@ -1,4 +1,3 @@ -import { User } from '../users/User'; import { Frame } from './Frame'; import { FramesRepository } from './Repository'; diff --git a/lib/core/users/MongoDBUsersRepository.ts b/lib/core/users/MongoDBUsersRepository.ts index 3abc39e8..2cdfc929 100644 --- a/lib/core/users/MongoDBUsersRepository.ts +++ b/lib/core/users/MongoDBUsersRepository.ts @@ -52,6 +52,12 @@ export class MongoDBUsersRepository implements UsersRepository { return users.map(formatFromMongoToUser); } + async findByUuid(uuid: string): Promise { + const user = await this.userModel.findOne({ uuid }); + + return user ? formatFromMongoToUser(user) : null; + } + async findOne(where: Partial): Promise { const user: DatabaseUser = await this.userModel.findOne(where); @@ -116,11 +122,11 @@ export class MongoDBUsersRepository implements UsersRepository { } addTotalUsedSpaceBytes( - id: string, + uuid: string, totalUsedSpaceBytes: number ): Promise { return this.userModel.updateOne( - { _id: id }, + { uuid }, { $inc: { totalUsedSpaceBytes } } ); } diff --git a/lib/core/users/Repository.ts b/lib/core/users/Repository.ts index be2abd7c..4adb37aa 100644 --- a/lib/core/users/Repository.ts +++ b/lib/core/users/Repository.ts @@ -2,11 +2,12 @@ import { BasicUser, CreateUserData, User } from "./User"; export interface UsersRepository { findById(id: User['id']): Promise; + findByUuid(uuid: User['uuid']): Promise; findOne(where: Partial): Promise; findByEmail(email: User['email']): Promise; findByIds(ids: User['id'][]): Promise; create(data: CreateUserData): Promise; - addTotalUsedSpaceBytes(id: User['id'], totalUsedSpaceBytes: number): Promise; + addTotalUsedSpaceBytes(uuid: User['uuid'], totalUsedSpaceBytes: User['totalUsedSpaceBytes']): Promise; updateById(id: User['id'], update: Partial): Promise; updateByEmail(email: User['email'], update: Partial): Promise; updateByUuid(uuid: User['uuid'], update: Partial): Promise; diff --git a/lib/core/users/usecase.ts b/lib/core/users/usecase.ts index 97160a86..9bbeede0 100644 --- a/lib/core/users/usecase.ts +++ b/lib/core/users/usecase.ts @@ -65,7 +65,7 @@ export class UsersUsecase { ) {} async findOrCreateUser(email: string, password: string): Promise { - const user = await this.usersRepository.findById(email); + const user = await this.usersRepository.findByEmail(email); if (user) { const newHassPass = createHash('sha256').update(password).digest('hex'); @@ -219,8 +219,8 @@ export class UsersUsecase { } await Promise.all([ - this.bucketsRepository.removeAll({ user: user.id }), - this.framesRepository.removeAll({ user: user.id }) + this.bucketsRepository.removeAll({ userId: user.uuid }), + this.framesRepository.removeAll({ user: user.email }) ]); await this.usersRepository.removeById(user.id); diff --git a/lib/server/routes/buckets.js b/lib/server/routes/buckets.js index 5416c6d7..aebba963 100644 --- a/lib/server/routes/buckets.js +++ b/lib/server/routes/buckets.js @@ -134,7 +134,7 @@ BucketsRouter.prototype._validate = function (req, res, next) { * @param {Function} next */ BucketsRouter.prototype.getBuckets = function (req, res, next) { - let findQuery = { user: req.user._id }; + let findQuery = { userId: req.user.uuid }; const startDate = utils.parseTimestamp(req.query.startDate); if (startDate) { findQuery.created = { $gt: startDate }; @@ -166,7 +166,7 @@ BucketsRouter.prototype.getBucketById = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, function (err, bucket) { if (err) { return next(new errors.InternalError(err.message)); @@ -184,7 +184,7 @@ BucketsRouter.prototype.getBucketId = function (req, res, next) { const Bucket = this.storage.models.Bucket; Bucket.findOne({ - user: req.user._id, + userId: req.user.uuid, name: req.params.name }, '_id', { lean: true }, function (err, bucket) { if (err) { @@ -261,7 +261,7 @@ BucketsRouter.prototype.destroyBucketById = function (req, res, next) { return next(new errors.ConflictError('This user has bucket deletion disabled')); } - Bucket.findOne({ _id: req.params.id, user: req.user._id }, (err, bucket) => { + Bucket.findOne({ _id: req.params.id, userId: req.user.uuid }, (err, bucket) => { if (err) { return next(new errors.InternalError(err.message)); } @@ -291,7 +291,7 @@ BucketsRouter.prototype.updateBucketById = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, (err, bucket) => { if (err) { return next(new errors.InternalError(err.message)); @@ -377,7 +377,7 @@ BucketsRouter.prototype._getBucketUnregistered = function (req, res, next) { } if (req.user) { - query.user = req.user._id; + query.userId = req.user.uuid; } Bucket.findOne(query, function (err, bucket) { @@ -410,7 +410,7 @@ BucketsRouter.prototype.createBucketToken = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, function (err, bucket) { if (err) { return next(err); @@ -475,13 +475,13 @@ BucketsRouter.prototype.ensureCreateEntryFromFrame = async function (req, res, n return next(new errors.NotFoundError('Bucket not found')); } - const isBucketOwner = bucket.user === req.user._id; + const isBucketOwner = bucket.userId === req.user.uuid; if (!isBucketOwner) { return next(new errors.ForbiddenError()); } - const frame = await Frame.findOne({ _id: req.body.frame, user: req.user._id }); + const frame = await Frame.findOne({ _id: req.body.frame, user: req.user.email }); if (!frame) { return next(new errors.NotFoundError('Frame not found')); @@ -540,7 +540,7 @@ BucketsRouter.prototype.ensureCreateEntryFromFrame = async function (req, res, n if (err && err.response && err.response.status === 404) { log.error( 'ensureCreateEntryFromFrame/user %s tried to confirm an upload of a non-existent object', - req.user._id + req.user.uuid ); return next(new errors.UnprocessableEntityError('File can not be created, object is missing')); @@ -548,7 +548,7 @@ BucketsRouter.prototype.ensureCreateEntryFromFrame = async function (req, res, n log.error( 'ensureCreateEntryFromFrame/error confirming upload for user %s: %s. %s', - req.user._id, + req.user.uuid, err.message, err.stack || 'No stack trace' ); @@ -580,7 +580,7 @@ BucketsRouter.prototype.createEntryFromFrame = function (req, res, next) { }); Bucket.findOne({ - user: req.user._id, + userId: req.user.uuid, _id: req.params.id }, function (err, bucket) { if (err) { @@ -593,7 +593,7 @@ BucketsRouter.prototype.createEntryFromFrame = function (req, res, next) { Frame.findOne({ _id: req.body.frame, - user: req.user._id + user: req.user.email }, function (err, frame) { if (err) { return next(new errors.InternalError(err.message)); @@ -638,7 +638,7 @@ BucketsRouter.prototype.createEntryFromFrame = function (req, res, next) { /** * Returns the bucket by ID * @param {String|ObjectId} bucketId - The unique _id for the bucket - * @param {String} [userId] - The email address for the user + * @param {String} [userId] - User's uuid * @param {BucketsRouter~_getBucketByIdCallback} */ BucketsRouter.prototype._getBucketById = function (bucketId, userId, callback) { @@ -650,7 +650,7 @@ BucketsRouter.prototype._getBucketById = function (bucketId, userId, callback) { } if (userId) { - query.user = userId; + query.userId = userId; } this.storage.models.Bucket.findOne(query, function (err, bucket) { @@ -871,7 +871,7 @@ BucketsRouter.prototype.listMirrorsForFile = function (req, res, next) { } async.waterfall([ - this._getBucketById.bind(this, req.params.id, req.user._id), + this._getBucketById.bind(this, req.params.id, req.user.uuid), _getFrameForFile.bind(this, req.params.file), _getHashesFromFrame, _getMirrorsFromHashes @@ -1076,7 +1076,7 @@ BucketsRouter.prototype.getFile = function (req, res, next) { const query = { _id: req.params.id }; if (req.user) { - query.user = req.user._id; + query.userId = req.user.uuid; } else { if (req.params.id !== req.token.bucket.toString()) { return next(new errors.NotAuthorizedError()); @@ -1093,7 +1093,7 @@ BucketsRouter.prototype.getFile = function (req, res, next) { } User.findOne({ - _id: bucket.user + uuid: bucket.userId }, function (err, user) { if (err) { return next(new errors.InternalError(err.message)); @@ -1215,7 +1215,7 @@ BucketsRouter.prototype.listFilesInBucket = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, (err, bucket) => { if (err) { return next(new errors.InternalError(err.message)); @@ -1275,7 +1275,7 @@ BucketsRouter.prototype.removeFile = async function (req, res, next) { } try { - const userId = user._id; + const userId = user.uuid; await this.bucketEntriesUsecase.removeFileFromUser( bucketId, @@ -1356,7 +1356,7 @@ BucketsRouter.prototype.renameFile = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, function (err, bucket) { if (err) { return next(new errors.InternalError(err.message)); @@ -1403,7 +1403,7 @@ BucketsRouter.prototype.getFileId = function (req, res, next) { Bucket.findOne({ _id: req.params.id, - user: req.user._id + userId: req.user.uuid }, '_id', { lean: true }, function (err, bucket) { if (err) { return next(new errors.InternalError(err.message)); @@ -1457,64 +1457,6 @@ BucketsRouter.prototype.getFileInfo = function (req, res, next) { }); }; -BucketsRouter.prototype.getStorageUsage = function (req, res) { - const { Bucket } = this.storage.models; - - var agg = Bucket.aggregate([ - { - $match: { - user: req.user._id - } - }, - { - $lookup: { - from: 'bucketentries', - localField: '_id', - foreignField: 'bucket', - as: 'bucketentry' - } - }, - { - $unwind: { - path: '$bucketentry' - } - }, - { - $lookup: { - from: 'frames', - localField: 'bucketentry.frame', - foreignField: '_id', - as: 'frame' - } - }, - { - $unwind: { - path: '$frame' - } - }, - { - $project: { - _id: '$frame._id', - user: '$frame.user', - size: '$frame.size' - } - }, - { - $group: { - _id: '$user', - total: { $sum: '$size' } - } - } - ]).cursor({ batchSize: 1000 }).exec(); - - agg.next().then(data => { - // User.updateOne({ _id: data._id }, { totalUsedSpaceBytes: data.total }, (err) => { }) - res.status(200).send(data); - }).catch(() => { - res.status(400).send({ message: 'Error' }); - }); -}; - //eslint-disable-next-line complexity BucketsRouter.prototype.startUpload = async function (req, res, next) { const bucketId = req.params.id; @@ -1549,7 +1491,7 @@ BucketsRouter.prototype.startUpload = async function (req, res, next) { try { const uploadsResult = await this.usecase.startUpload( - req.user._id, + req.user.uuid, bucketId, this.CLUSTER, uploads, @@ -1587,7 +1529,7 @@ BucketsRouter.prototype.startUpload = async function (req, res, next) { return next(new errors.InternalError(err.message)); } - log.error('startUpload: Error for bucket %s: for user: %s %s. %s', bucketId, req.user._id, err.message, err.stack); + log.error('startUpload: Error for bucket %s: for user: %s %s. %s', bucketId, req.user.uuid, err.message, err.stack); return next(new errors.InternalError()); } @@ -1631,7 +1573,7 @@ BucketsRouter.prototype.finishUpload = async function (req, res, next) { try { const bucketEntry = await this.usecase.completeUpload( - req.user._id, + req.user.uuid, bucketId, index, shards, @@ -1733,7 +1675,6 @@ BucketsRouter.prototype._definitions = function () { ['POST', '/buckets/:id/files', this.getLimiter(limiter(1000)), this._validate, this._verify, this.createEntryFromFrame], ['POST', '/buckets/:id/files/ensure', this.getLimiter(limiter(1000)), this._validate, this._verify, this.ensureCreateEntryFromFrame], ['GET', '/buckets/:id/files/:file/mirrors', this.getLimiter(limiter(1000)), this._validate, this._verify, this.listMirrorsForFile], - ['GET', '/usage', this.getLimiter(limiter(1000)), this._verify, this.getStorageUsage], ['PATCH', '/buckets/:id/files/:file', this.getLimiter(limiter(1000)), this._validate, this._verify, this.renameFile], ['POST', '/v2/buckets/:id/files/start', this.getLimiter(limiter(1000)), this._validate, this._verify, this.startUpload], ['POST', '/v2/buckets/:id/files/finish', this.getLimiter(limiter(1000)), this._validate, this._verify, this.finishUpload], diff --git a/lib/server/routes/frames.js b/lib/server/routes/frames.js index bb853671..2bebac2d 100644 --- a/lib/server/routes/frames.js +++ b/lib/server/routes/frames.js @@ -117,10 +117,10 @@ FramesRouter.prototype.getFrameById = function (req, res, next) { }); }; -function getStorageLimit(storage, user) { +function getStorageLimit(storage, userId) { return new Promise((resolve, reject) => { - storage.models.User.findOne({ _id: user }, function (err, _user) { + storage.models.User.findOne({ uuid: userId }, function (err, _user) { if (err) { reject({ error: 'Internal error', statusCode: 500 }); } @@ -140,7 +140,7 @@ function getStorageLimit(storage, user) { } FramesRouter.prototype.getStorageLimit = function (req, res) { - getStorageLimit(this.storage, req.user._id).then(result => { + getStorageLimit(this.storage, req.user.uuid).then(result => { res.status(result.statusCode).send({ maxSpaceBytes: result.maxSpaceBytes }); }).catch(err => { res.status(err.statusCode).send({ error: err.error }); diff --git a/lib/server/routes/users.js b/lib/server/routes/users.js index 3806a1c6..6a1d4c85 100644 --- a/lib/server/routes/users.js +++ b/lib/server/routes/users.js @@ -456,7 +456,7 @@ UsersRouter.prototype.confirmDestroyUser = function (req, res, next) { Bucket.remove( { - user: user.email, + userId: user.uuid, }, (err) => { if (err) {