diff --git a/services/web/app/src/Features/Templates/TemplatesManager.js b/services/web/app/src/Features/Templates/TemplatesManager.js index ab35af787a..b17c242355 100644 --- a/services/web/app/src/Features/Templates/TemplatesManager.js +++ b/services/web/app/src/Features/Templates/TemplatesManager.js @@ -10,7 +10,6 @@ * DS207: Consider shorter variations of null checks * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -let TemplatesManager const { Project } = require('../../models/Project') const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler') const ProjectOptionsHandler = require('../Project/ProjectOptionsHandler') @@ -19,12 +18,15 @@ const ProjectUploadManager = require('../Uploads/ProjectUploadManager') const FileWriter = require('../../infrastructure/FileWriter') const async = require('async') const fs = require('fs') +const util = require('util') const logger = require('logger-sharelatex') const request = require('request') +const requestPromise = require('request-promise-native') const settings = require('settings-sharelatex') const uuid = require('uuid') +const Errors = require('../Errors/Errors') -module.exports = TemplatesManager = { +const TemplatesManager = { createProjectFromV1Template( brandVariationId, compiler, @@ -158,5 +160,42 @@ module.exports = TemplatesManager = { brandVariationId, callback ) + }, + + promises: { + async fetchFromV1(templateId, callback) { + let { body, statusCode } = await requestPromise({ + baseUrl: settings.apis.v1.url, + url: `/api/v2/templates/${templateId}`, + method: 'GET', + auth: { + user: settings.apis.v1.user, + pass: settings.apis.v1.pass, + sendImmediately: true + }, + resolveWithFullResponse: true, + simple: false, + json: true + }) + + if (statusCode === 404) { + throw new Errors.NotFoundError() + } + + if (statusCode !== 200) { + logger.warn( + { templateId }, + "[TemplateMetrics] Couldn't fetch template data from v1" + ) + throw new Error("Couldn't fetch template data from v1") + } + + return body + } } } + +TemplatesManager.fetchFromV1 = util.callbackify( + TemplatesManager.promises.fetchFromV1 +) +module.exports = TemplatesManager diff --git a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js index 0daefe020a..b97aeecc62 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipAuthorization.js @@ -1,360 +1,29 @@ -/* eslint-disable - handle-callback-err, - max-len, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * DS103: Rewrite code to no longer use __guard__ - * DS207: Consider shorter variations of null checks - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -let UserMembershipAuthorization -const AuthenticationController = require('../Authentication/AuthenticationController') -const AuthorizationMiddleware = require('../Authorization/AuthorizationMiddleware') -const UserMembershipHandler = require('./UserMembershipHandler') -const EntityConfigs = require('./UserMembershipEntityConfigs') -const Errors = require('../Errors/Errors') -const logger = require('logger-sharelatex') -const settings = require('settings-sharelatex') -const request = require('request') - -module.exports = UserMembershipAuthorization = { - requireTeamMetricsAccess(req, res, next) { - return requireAccessToEntity( - 'team', - req.params.id, - req, - res, - next, - 'groupMetrics' - ) - }, - - requireGroupManagementAccess(req, res, next) { - return requireAccessToEntity( - 'group', - req.params.id, - req, - res, - next, - 'groupManagement' - ) - }, - - requireGroupMetricsAccess(req, res, next) { - return requireAccessToEntity( - 'group', - req.params.id, - req, - res, - next, - 'groupMetrics' - ) - }, - - requireGroupManagersManagementAccess(req, res, next) { - return requireAccessToEntity( - 'groupManagers', - req.params.id, - req, - res, - next, - 'groupManagement' - ) - }, - - requireInstitutionMetricsAccess(req, res, next) { - return requireAccessToEntity( - 'institution', - req.params.id, - req, - res, - next, - 'institutionMetrics' - ) - }, - - requireInstitutionManagementAccess(req, res, next) { - return requireAccessToEntity( - 'institution', - req.params.id, - req, - res, - next, - 'institutionManagement' - ) - }, - - requireInstitutionManagementStaffAccess(req, res, next) { - return requireAccessToEntity( - 'institution', - req.params.id, - req, - res, - next, - 'institutionManagement', - true - ) - }, - - requirePublisherMetricsAccess(req, res, next) { - return requireAccessToEntity( - 'publisher', - req.params.id, - req, - res, - next, - 'publisherMetrics' - ) - }, - - requirePublisherManagementAccess(req, res, next) { - return requireAccessToEntity( - 'publisher', - req.params.id, - req, - res, - next, - 'publisherManagement' - ) - }, - - requireAdminMetricsStaffAccess(req, res, next) { - return requireAccessToEntity( - 'admin', - 'admin', - req, - res, - next, - 'adminMetrics', - true - ) - }, - - requireTemplateMetricsAccess(req, res, next) { - const templateId = req.params.id - return request( - { - baseUrl: settings.apis.v1.url, - url: `/api/v2/templates/${templateId}`, - method: 'GET', - auth: { - user: settings.apis.v1.user, - pass: settings.apis.v1.pass, - sendImmediately: true - } - }, - (error, response, body) => { - if (response.statusCode === 404) { - return next(new Errors.NotFoundError()) - } - - if (response.statusCode !== 200) { - logger.warn( - { templateId }, - "[TemplateMetrics] Couldn't fetch template data from v1" - ) - return next(new Error("Couldn't fetch template data from v1")) - } - - if (error != null) { - return next(error) - } - try { - body = JSON.parse(body) - } catch (error1) { - error = error1 - return next(error) - } - - req.template = { - id: body.id, - title: body.title - } - if (__guard__(body != null ? body.brand : undefined, x => x.slug)) { - req.params.id = body.brand.slug - return UserMembershipAuthorization.requirePublisherMetricsAccess( - req, - res, - next - ) - } else { - return AuthorizationMiddleware.ensureUserIsSiteAdmin(req, res, next) - } +let UserMembershipAuthorization = { + hasStaffAccess(requiredStaffAccess) { + return req => { + if (!req.user) { + return false } - ) - }, - - requireGraphAccess(req, res, next) { - req.params.id = req.query.resource_id - if (req.query.resource_type === 'template') { - return UserMembershipAuthorization.requireTemplateMetricsAccess( - req, - res, - next - ) - } else if (req.query.resource_type === 'institution') { - return UserMembershipAuthorization.requireInstitutionMetricsAccess( - req, - res, - next - ) - } else if (req.query.resource_type === 'group') { - return UserMembershipAuthorization.requireGroupMetricsAccess( - req, - res, - next - ) - } else if (req.query.resource_type === 'team') { - return UserMembershipAuthorization.requireTeamMetricsAccess( - req, - res, - next - ) - } else if (req.query.resource_type === 'admin') { - return UserMembershipAuthorization.requireAdminMetricsStaffAccess( - req, - res, - next + if (req.user.isAdmin) { + return true + } + return ( + requiredStaffAccess && + req.user.staffAccess && + req.user.staffAccess[requiredStaffAccess] ) } - return requireAccessToEntity( - req.query.resource_type, - req.query.resource_id, - req, - res, - next - ) }, - requireEntityCreationAccess(req, res, next) { - const loggedInUser = AuthenticationController.getSessionUser(req) - if (!loggedInUser || !hasEntityCreationAccess(loggedInUser)) { - return AuthorizationMiddleware.redirectToRestricted(req, res, next) + hasEntityAccess() { + return req => { + if (!req.entity) { + return false + } + return req.entity[req.entityConfig.fields.access].some(accessUserId => + accessUserId.equals(req.user._id) + ) } - return next() } } - -var requireAccessToEntity = function( - entityName, - entityId, - req, - res, - next, - requiredStaffAccess = null, - asStaff -) { - if (asStaff == null) { - asStaff = false - } - const loggedInUser = AuthenticationController.getSessionUser(req) - if (!loggedInUser) { - return AuthorizationMiddleware.redirectToRestricted(req, res, next) - } - - if (asStaff) { - if ( - !loggedInUser.isAdmin && - !(loggedInUser.staffAccess != null - ? loggedInUser.staffAccess[requiredStaffAccess] - : undefined) - ) { - return AuthorizationMiddleware.redirectToRestricted(req, res, next) - } - } - - return getEntity( - entityName, - entityId, - loggedInUser, - requiredStaffAccess, - function(error, entity, entityConfig, entityExists) { - if (error != null) { - return next(error) - } - - if (entity != null) { - req.entity = entity - req.entityConfig = entityConfig - return next() - } - - if (entityExists) { - // user doesn't have access to entity - return AuthorizationMiddleware.redirectToRestricted(req, res, next) - } - - if (hasEntityCreationAccess(loggedInUser) && entityConfig.canCreate) { - // entity doesn't exists, admin can create it - return res.redirect(`/entities/${entityName}/create/${entityId}`) - } - - return next(new Errors.NotFoundError()) - } - ) -} - -var getEntity = function( - entityName, - entityId, - user, - requiredStaffAccess, - callback -) { - if (callback == null) { - callback = function(error, entity, entityConfig, entityExists) {} - } - const entityConfig = EntityConfigs[entityName] - if (!entityConfig) { - return callback(new Errors.NotFoundError(`No such entity: ${entityName}`)) - } - if (!entityConfig.modelName) { - return callback(null, { id: entityName }, entityConfig, true) - } - - return UserMembershipHandler.getEntity( - entityId, - entityConfig, - user, - requiredStaffAccess, - function(error, entity) { - if (error != null) { - return callback(error) - } - if (entity != null) { - return callback(null, entity, entityConfig, true) - } - - // no access to entity. Check if entity exists - return UserMembershipHandler.getEntityWithoutAuthorizationCheck( - entityId, - entityConfig, - function(error, entity) { - if (error != null) { - return callback(error) - } - return callback(null, null, entityConfig, entity != null) - } - ) - } - ) -} - -var hasEntityCreationAccess = user => - user.isAdmin || - (user.staffAccess != null - ? user.staffAccess['institutionManagement'] - : undefined) || - (user.staffAccess != null - ? user.staffAccess['publisherManagement'] - : undefined) - -function __guard__(value, transform) { - return typeof value !== 'undefined' && value !== null - ? transform(value) - : undefined -} +module.exports = UserMembershipAuthorization diff --git a/services/web/app/src/Features/UserMembership/UserMembershipController.js b/services/web/app/src/Features/UserMembership/UserMembershipController.js index ddc9f9a110..af39359cf4 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipController.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipController.js @@ -12,7 +12,6 @@ */ const AuthenticationController = require('../Authentication/AuthenticationController') const UserMembershipHandler = require('./UserMembershipHandler') -const EntityConfigs = require('./UserMembershipEntityConfigs') const Errors = require('../Errors/Errors') const EmailHelper = require('../Helpers/EmailHelper') const logger = require('logger-sharelatex') @@ -161,15 +160,8 @@ module.exports = { }, create(req, res, next) { - const entityName = req.params.name const entityId = req.params.id - const entityConfig = EntityConfigs[entityName] - if (!entityConfig) { - return next(new Errors.NotFoundError(`No such entity: ${entityName}`)) - } - if (!entityConfig.canCreate) { - return next(new Errors.NotFoundError(`Cannot create new ${entityName}`)) - } + const entityConfig = req.entityConfig return UserMembershipHandler.createEntity(entityId, entityConfig, function( error, diff --git a/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js b/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js index bc4e194d9f..329e8bdedb 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipEntityConfigs.js @@ -1,10 +1,3 @@ -// TODO: This file was created by bulk-decaffeinate. -// Sanity-check the conversion and remove this comment. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ module.exports = { group: { modelName: 'Subscription', @@ -74,7 +67,6 @@ module.exports = { institution: { modelName: 'Institution', - canCreate: true, fields: { primaryKey: 'v1Id', read: ['managerIds'], @@ -98,7 +90,6 @@ module.exports = { publisher: { modelName: 'Publisher', - canCreate: true, fields: { primaryKey: 'slug', read: ['managerIds'], @@ -118,19 +109,5 @@ module.exports = { removeMember: `/manage/publishers/${id}/managers` } } - }, - - conversion: { - // for metrics only - modelName: 'Publisher', - fields: { - primaryKey: 'slug', - access: 'managerIds' - } - }, - - admin: { - // for metrics only - modelName: null } } diff --git a/services/web/app/src/Features/UserMembership/UserMembershipHandler.js b/services/web/app/src/Features/UserMembership/UserMembershipHandler.js index da4a6517ef..d5eee7cab8 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipHandler.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipHandler.js @@ -15,6 +15,7 @@ */ const { ObjectId } = require('mongoose').Types const async = require('async') +const { promisifyAll } = require('../../util/promises') const Errors = require('../Errors/Errors') const EntityModels = { Institution: require('../../models/Institution').Institution, @@ -26,29 +27,7 @@ const UserGetter = require('../User/UserGetter') const logger = require('logger-sharelatex') const UserMembershipEntityConfigs = require('./UserMembershipEntityConfigs') -module.exports = { - getEntity( - entityId, - entityConfig, - loggedInUser, - requiredStaffAccess, - callback - ) { - if (callback == null) { - callback = function(error, entity) {} - } - const query = buildEntityQuery(entityId, entityConfig) - if ( - !loggedInUser.isAdmin && - !(loggedInUser.staffAccess != null - ? loggedInUser.staffAccess[requiredStaffAccess] - : undefined) - ) { - query[entityConfig.fields.access] = ObjectId(loggedInUser._id) - } - return EntityModels[entityConfig.modelName].findOne(query, callback) - }, - +const UserMembershipHandler = { getEntityWithoutAuthorizationCheck(entityId, entityConfig, callback) { if (callback == null) { callback = function(error, entity) {} @@ -107,6 +86,9 @@ module.exports = { } } +UserMembershipHandler.promises = promisifyAll(UserMembershipHandler) +module.exports = UserMembershipHandler + var getPopulatedListOfMembers = function(entity, attributes, callback) { if (callback == null) { callback = function(error, users) {} diff --git a/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js b/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js new file mode 100644 index 0000000000..216bd392a7 --- /dev/null +++ b/services/web/app/src/Features/UserMembership/UserMembershipMiddleware.js @@ -0,0 +1,273 @@ +const expressify = require('../../util/expressify') +const async = require('async') +const UserMembershipAuthorization = require('./UserMembershipAuthorization') +const AuthenticationController = require('../Authentication/AuthenticationController') +const UserMembershipHandler = require('./UserMembershipHandler') +const EntityConfigs = require('./UserMembershipEntityConfigs') +const Errors = require('../Errors/Errors') +const HttpErrors = require('@overleaf/o-error/http') +const TemplatesManager = require('../Templates/TemplatesManager') + +// set of middleware arrays or functions that checks user access to an entity +// (publisher, institution, group, template, etc.) +let UserMembershipMiddleware = { + requireTeamMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('team'), + fetchEntity(), + requireEntity(), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('groupMetrics') + ]) + ], + + requireGroupManagementAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('group'), + fetchEntity(), + requireEntity(), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('groupManagement') + ]) + ], + + requireGroupMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('group'), + fetchEntity(), + requireEntity(), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('groupMetrics') + ]) + ], + + requireGroupManagersManagementAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('groupManagers'), + fetchEntity(), + requireEntity(), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('groupManagement') + ]) + ], + + requireInstitutionMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('institution'), + fetchEntity(), + requireEntityOrCreate('institutionManagement'), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('institutionMetrics') + ]) + ], + + requireInstitutionManagementAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('institution'), + fetchEntity(), + requireEntityOrCreate('institutionManagement'), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('institutionManagement') + ]) + ], + + requireInstitutionManagementStaffAccess: [ + AuthenticationController.requireLogin(), + restrictAccess([ + UserMembershipAuthorization.hasStaffAccess('institutionManagement') + ]), + fetchEntityConfig('institution'), + fetchEntity(), + requireEntityOrCreate('institutionManagement') + ], + + requirePublisherMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('publisher'), + fetchEntity(), + requireEntityOrCreate('publisherManagement'), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('publisherMetrics') + ]) + ], + + requirePublisherManagementAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('publisher'), + fetchEntity(), + requireEntityOrCreate('publisherManagement'), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('publisherManagement') + ]) + ], + + requireConversionMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchEntityConfig('publisher'), + fetchEntity(), + requireEntityOrCreate('publisherManagement'), + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('publisherMetrics') + ]) + ], + + requireAdminMetricsAccess: [ + AuthenticationController.requireLogin(), + restrictAccess([UserMembershipAuthorization.hasStaffAccess('adminMetrics')]) + ], + + requireTemplateMetricsAccess: [ + AuthenticationController.requireLogin(), + fetchV1Template(), + requireV1Template(), + fetchEntityConfig('publisher'), + fetchEntity(), // at this point the entity is the template's publisher, if any + restrictAccess([ + UserMembershipAuthorization.hasEntityAccess(), + UserMembershipAuthorization.hasStaffAccess('publisherMetrics') + ]) + ], + + requirePublisherCreationAccess: [ + AuthenticationController.requireLogin(), + restrictAccess([ + UserMembershipAuthorization.hasStaffAccess('publisherManagement') + ]), + fetchEntityConfig('publisher') + ], + + requireInstitutionCreationAccess: [ + AuthenticationController.requireLogin(), + restrictAccess([ + UserMembershipAuthorization.hasStaffAccess('institutionManagement') + ]), + fetchEntityConfig('institution') + ], + + // graphs access is an edge-case: + // - the entity id is in `req.query.resource_id`. It must be set as + // `req.params.id` + // - the entity name is in `req.query.resource_type` and is used to find the + // require middleware depending on the entity name + requireGraphAccess(req, res, next) { + req.params.id = req.query.resource_id + let entityName = req.query.resource_type + entityName = entityName.charAt(0).toUpperCase() + entityName.slice(1) + + // run the list of middleware functions in series. This is essencially + // a poor man's middleware runner + async.eachSeries( + UserMembershipMiddleware[`require${entityName}MetricsAccess`], + (fn, callback) => fn(req, res, callback), + next + ) + } +} + +module.exports = UserMembershipMiddleware + +// fetch entity config and set it in the request +function fetchEntityConfig(entityName) { + return (req, res, next) => { + const entityConfig = EntityConfigs[entityName] + req.entityName = entityName + req.entityConfig = entityConfig + next() + } +} + +// fetch the entity with id and config, and set it in the request +function fetchEntity() { + return expressify(async (req, res, next) => { + let entity = await UserMembershipHandler.promises.getEntityWithoutAuthorizationCheck( + req.params.id, + req.entityConfig + ) + req.entity = entity + next() + }) +} + +// ensure an entity was found, or fail with 404 +function requireEntity() { + return (req, res, next) => { + if (req.entity) { + return next() + } + + throw new Errors.NotFoundError( + `no '${req.entityName}' entity with '${req.params.id}'` + ) + } +} + +// ensure an entity was found or redirect to entity creation page if the user +// has permissions to create the entity, or fail with 404 +function requireEntityOrCreate(creationStaffAccess) { + return (req, res, next) => { + if (req.entity) { + return next() + } + + if (UserMembershipAuthorization.hasStaffAccess(creationStaffAccess)(req)) { + res.redirect(`/entities/${req.entityName}/create/${req.params.id}`) + return + } + + throw new Errors.NotFoundError( + `no '${req.entityName}' entity with '${req.params.id}'` + ) + } +} + +// fetch the template from v1, and set it in the request +function fetchV1Template() { + return expressify(async (req, res, next) => { + const templateId = req.params.id + const body = await TemplatesManager.promises.fetchFromV1(templateId) + req.template = { + id: body.id, + title: body.title, + brand: body.brand + } + if (req.template.brand.slug) { + // set the id as the publisher's id as it's the entity used for access + // control + req.params.id = req.template.brand.slug + } + next() + }) +} + +// ensure a template was found, or fail with 404 +function requireV1Template() { + return (req, res, next) => { + if (req.template.id) { + return next() + } + + throw new Errors.NotFoundError('no template found') + } +} + +// run a serie of synchronous access functions and call `next` if any of the +// retur values is truly. Redirect to restricted otherwise +function restrictAccess(accessFunctions) { + return (req, res, next) => { + for (let accessFunction of accessFunctions) { + if (accessFunction(req)) { + return next() + } + } + next(new HttpErrors.ForbiddenError({})) + } +} diff --git a/services/web/app/src/Features/UserMembership/UserMembershipRouter.js b/services/web/app/src/Features/UserMembership/UserMembershipRouter.js index 702eb34d1b..bea93311f6 100644 --- a/services/web/app/src/Features/UserMembership/UserMembershipRouter.js +++ b/services/web/app/src/Features/UserMembership/UserMembershipRouter.js @@ -5,7 +5,7 @@ * DS102: Remove unnecessary code created because of implicit returns * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md */ -const UserMembershipAuthorization = require('./UserMembershipAuthorization') +const UserMembershipMiddleware = require('./UserMembershipMiddleware') const UserMembershipController = require('./UserMembershipController') const SubscriptionGroupController = require('../Subscription/SubscriptionGroupController') const TeamInvitesController = require('../Subscription/TeamInvitesController') @@ -16,12 +16,12 @@ module.exports = { // group members routes webRouter.get( '/manage/groups/:id/members', - UserMembershipAuthorization.requireGroupManagementAccess, + UserMembershipMiddleware.requireGroupManagementAccess, UserMembershipController.index ) webRouter.post( '/manage/groups/:id/invites', - UserMembershipAuthorization.requireGroupManagementAccess, + UserMembershipMiddleware.requireGroupManagementAccess, RateLimiterMiddleware.rateLimit({ endpointName: 'create-team-invite', maxRequests: 100, @@ -31,17 +31,17 @@ module.exports = { ) webRouter.delete( '/manage/groups/:id/user/:user_id', - UserMembershipAuthorization.requireGroupManagementAccess, + UserMembershipMiddleware.requireGroupManagementAccess, SubscriptionGroupController.removeUserFromGroup ) webRouter.delete( '/manage/groups/:id/invites/:email', - UserMembershipAuthorization.requireGroupManagementAccess, + UserMembershipMiddleware.requireGroupManagementAccess, TeamInvitesController.revokeInvite ) webRouter.get( '/manage/groups/:id/members/export', - UserMembershipAuthorization.requireGroupManagementAccess, + UserMembershipMiddleware.requireGroupManagementAccess, RateLimiterMiddleware.rateLimit({ endpointName: 'export-team-csv', maxRequests: 30, @@ -53,63 +53,75 @@ module.exports = { // group managers routes webRouter.get( '/manage/groups/:id/managers', - UserMembershipAuthorization.requireGroupManagersManagementAccess, + UserMembershipMiddleware.requireGroupManagersManagementAccess, UserMembershipController.index ) webRouter.post( '/manage/groups/:id/managers', - UserMembershipAuthorization.requireGroupManagersManagementAccess, + UserMembershipMiddleware.requireGroupManagersManagementAccess, UserMembershipController.add ) webRouter.delete( '/manage/groups/:id/managers/:userId', - UserMembershipAuthorization.requireGroupManagersManagementAccess, + UserMembershipMiddleware.requireGroupManagersManagementAccess, UserMembershipController.remove ) // institution members routes webRouter.get( '/manage/institutions/:id/managers', - UserMembershipAuthorization.requireInstitutionManagementAccess, + UserMembershipMiddleware.requireInstitutionManagementAccess, UserMembershipController.index ) webRouter.post( '/manage/institutions/:id/managers', - UserMembershipAuthorization.requireInstitutionManagementAccess, + UserMembershipMiddleware.requireInstitutionManagementAccess, UserMembershipController.add ) webRouter.delete( '/manage/institutions/:id/managers/:userId', - UserMembershipAuthorization.requireInstitutionManagementAccess, + UserMembershipMiddleware.requireInstitutionManagementAccess, UserMembershipController.remove ) // publisher members routes webRouter.get( '/manage/publishers/:id/managers', - UserMembershipAuthorization.requirePublisherManagementAccess, + UserMembershipMiddleware.requirePublisherManagementAccess, UserMembershipController.index ) webRouter.post( '/manage/publishers/:id/managers', - UserMembershipAuthorization.requirePublisherManagementAccess, + UserMembershipMiddleware.requirePublisherManagementAccess, UserMembershipController.add ) webRouter.delete( '/manage/publishers/:id/managers/:userId', - UserMembershipAuthorization.requirePublisherManagementAccess, + UserMembershipMiddleware.requirePublisherManagementAccess, UserMembershipController.remove ) - // create new entitites + // publisher creation routes webRouter.get( - '/entities/:name/create/:id', - UserMembershipAuthorization.requireEntityCreationAccess, + '/entities/publisher/create/:id', + UserMembershipMiddleware.requirePublisherCreationAccess, UserMembershipController.new ) - return webRouter.post( - '/entities/:name/create/:id', - UserMembershipAuthorization.requireEntityCreationAccess, + webRouter.post( + '/entities/publisher/create/:id', + UserMembershipMiddleware.requirePublisherCreationAccess, + UserMembershipController.create + ) + + // institution creation routes + webRouter.get( + '/entities/institution/create/:id', + UserMembershipMiddleware.requireInstitutionCreationAccess, + UserMembershipController.new + ) + webRouter.post( + '/entities/institution/create/:id', + UserMembershipMiddleware.requireInstitutionCreationAccess, UserMembershipController.create ) } diff --git a/services/web/app/src/util/expressify.js b/services/web/app/src/util/expressify.js new file mode 100644 index 0000000000..9ced59a66a --- /dev/null +++ b/services/web/app/src/util/expressify.js @@ -0,0 +1,5 @@ +module.exports = function expressify(fn) { + return (req, res, next) => { + fn(req, res, next).catch(next) + } +} diff --git a/services/web/package-lock.json b/services/web/package-lock.json index 5e0e15b4b5..af8935180a 100644 --- a/services/web/package-lock.json +++ b/services/web/package-lock.json @@ -17110,6 +17110,31 @@ "uuid": "^3.1.0" } }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "requires": { + "lodash": "^4.17.11" + }, + "dependencies": { + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + } + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, "requestretry": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", @@ -18581,6 +18606,11 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", diff --git a/services/web/package.json b/services/web/package.json index 60cbeeda82..22e6908dd8 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -98,6 +98,7 @@ "react-dom": "^15.4.2", "redis-sharelatex": "^1.0.9", "request": "^2.69.0", + "request-promise-native": "^1.0.7", "requestretry": "^1.13.0", "rimraf": "2.2.6", "rolling-rate-limiter": "git+https://github.com/ShaneKilkelly/rolling-rate-limiter.git#master", diff --git a/services/web/test/acceptance/src/UserMembershipAuthorizationTests.js b/services/web/test/acceptance/src/UserMembershipAuthorizationTests.js index d6b7de0a4f..a41bfdadf6 100644 --- a/services/web/test/acceptance/src/UserMembershipAuthorizationTests.js +++ b/services/web/test/acceptance/src/UserMembershipAuthorizationTests.js @@ -33,7 +33,7 @@ describe('UserMembershipAuthorization', function() { const url = `/metrics/teams/123` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.subscription.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -62,7 +62,7 @@ describe('UserMembershipAuthorization', function() { const url = `/manage/groups/${this.subscription._id}/members` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.subscription.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -76,7 +76,7 @@ describe('UserMembershipAuthorization', function() { const url = `/metrics/groups/${this.subscription._id}` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.subscription.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -95,7 +95,7 @@ describe('UserMembershipAuthorization', function() { const url = `/manage/groups/${this.subscription._id}/managers` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.subscription.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -151,10 +151,7 @@ describe('UserMembershipAuthorization', function() { it('should not allow users without access', function(done) { const url = `/metrics/institutions/${this.institution.v1Id}` async.series( - [ - this.user.login.bind(this.user), - expectAccess(this.user, url, 302, /\/restricted/) - ], + [this.user.login.bind(this.user), expectAccess(this.user, url, 403)], done ) }) @@ -166,7 +163,7 @@ describe('UserMembershipAuthorization', function() { async.series( [ this.user.login.bind(this.user), - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.institution.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -181,7 +178,7 @@ describe('UserMembershipAuthorization', function() { async.series( [ this.user.login.bind(this.user), - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.institution.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -196,7 +193,7 @@ describe('UserMembershipAuthorization', function() { async.series( [ this.user.login.bind(this.user), - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.user.ensureStaffAccess('institutionManagement', cb), this.user.login.bind(this.user), expectAccess(this.user, url, 200) @@ -224,7 +221,7 @@ describe('UserMembershipAuthorization', function() { const url = `/metrics/conversions/${this.publisher.slug}` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.publisher.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -238,7 +235,7 @@ describe('UserMembershipAuthorization', function() { const url = `/manage/publishers/${this.publisher.slug}/managers` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.publisher.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -266,7 +263,7 @@ describe('UserMembershipAuthorization', function() { const url = `/entities/publisher/create/foo` async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.user.ensureStaffAccess('publisherManagement', cb), this.user.login.bind(this.user), expectAccess(this.user, url, 200) @@ -300,7 +297,7 @@ describe('UserMembershipAuthorization', function() { const url = '/metrics/templates/123' async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), cb => this.publisher.setManagerIds([this.user._id], cb), expectAccess(this.user, url, 200) ], @@ -319,7 +316,7 @@ describe('UserMembershipAuthorization', function() { const url = '/metrics/templates/456' async.series( [ - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), this.user.ensure_admin.bind(this.user), this.user.login.bind(this.user), expectAccess(this.user, url, 200) @@ -347,7 +344,7 @@ describe('UserMembershipAuthorization', function() { async.series( [ this.user.login.bind(this.user), - expectAccess(this.user, url, 302, /\/restricted/), + expectAccess(this.user, url, 403), this.user.ensure_admin.bind(this.user), this.user.login.bind(this.user), expectAccess(this.user, url, 200) @@ -359,14 +356,14 @@ describe('UserMembershipAuthorization', function() { describe('admin metrics', function() { it('should not allow anonymous users', function(done) { - expectAccess(this.user, '/metrics/admin', 302, /\/restricted/)(done) + expectAccess(this.user, '/metrics/admin', 302, /\/login/)(done) }) it('should not allow all users', function(done) { async.series( [ this.user.login.bind(this.user), - expectAccess(this.user, '/metrics/admin', 302, /\/restricted/) + expectAccess(this.user, '/metrics/admin', 403) ], done ) diff --git a/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js b/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js deleted file mode 100644 index aefe9657dd..0000000000 --- a/services/web/test/unit/src/UserMembership/UserMembershipAuthorizationTests.js +++ /dev/null @@ -1,339 +0,0 @@ -/* eslint-disable - handle-callback-err, - max-len, - no-return-assign, - no-unused-vars, -*/ -// TODO: This file was created by bulk-decaffeinate. -// Fix any style issues and re-enable lint. -/* - * decaffeinate suggestions: - * DS102: Remove unnecessary code created because of implicit returns - * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md - */ -const sinon = require('sinon') -const chai = require('chai') -const { expect } = require('chai') -const modulePath = - '../../../../app/src/Features/UserMembership/UserMembershipAuthorization.js' -const SandboxedModule = require('sandboxed-module') -const MockRequest = require('../helpers/MockRequest') -const EntityConfigs = require('../../../../app/src/Features/UserMembership/UserMembershipEntityConfigs') -const Errors = require('../../../../app/src/Features/Errors/Errors') - -describe('UserMembershipAuthorization', function() { - beforeEach(function() { - this.req = new MockRequest() - this.req.params.id = 'mock-entity-id' - this.user = { _id: 'mock-user-id' } - this.subscription = { _id: 'mock-subscription-id' } - - this.AuthenticationController = { - getSessionUser: sinon.stub().returns(this.user) - } - this.UserMembershipHandler = { - getEntity: sinon.stub().yields(null, this.subscription), - getEntityWithoutAuthorizationCheck: sinon - .stub() - .yields(null, this.subscription) - } - this.AuthorizationMiddleware = { - redirectToRestricted: sinon.stub().yields(), - ensureUserIsSiteAdmin: sinon.stub().yields() - } - return (this.UserMembershipAuthorization = SandboxedModule.require( - modulePath, - { - globals: { - console: console - }, - requires: { - '../Authentication/AuthenticationController': this - .AuthenticationController, - '../Authorization/AuthorizationMiddleware': this - .AuthorizationMiddleware, - './UserMembershipHandler': this.UserMembershipHandler, - './EntityConfigs': EntityConfigs, - '../Errors/Errors': Errors, - request: (this.request = sinon.stub().yields(null, null, {})), - 'logger-sharelatex': { - log() {}, - warn() {}, - err() {} - } - } - } - )) - }) - - describe('requireAccessToEntity', function() { - it('get entity', function(done) { - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.params.id, - { modelName: 'Subscription' }, - this.user - ) - expect(this.req.entity).to.equal(this.subscription) - expect(this.req.entityConfig).to.exist - return done() - } - ) - }) - - it('handle entity not found as non-admin', function(done) { - this.UserMembershipHandler.getEntity.yields(null, null) - this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields( - null, - null - ) - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - expect(error).to.exist - expect(error).to.be.instanceof(Error) - expect(error.constructor.name).to.equal('NotFoundError') - sinon.assert.called(this.UserMembershipHandler.getEntity) - expect(this.req.entity).to.not.exist - return done() - } - ) - }) - - it('handle entity not found an admin can create', function(done) { - this.user.isAdmin = true - this.UserMembershipHandler.getEntity.yields(null, null) - this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields( - null, - null - ) - return this.UserMembershipAuthorization.requirePublisherMetricsAccess( - this.req, - { - redirect: path => { - expect(path).to.exist - expect(path).to.match(/create/) - return done() - } - } - ) - }) - - it('handle entity not found a non-admin can create', function(done) { - this.user.staffAccess = { institutionManagement: true } - this.UserMembershipHandler.getEntity.yields(null, null) - this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields( - null, - null - ) - return this.UserMembershipAuthorization.requirePublisherMetricsAccess( - this.req, - { - redirect: path => { - expect(path).to.exist - expect(path).to.match(/create/) - return done() - } - } - ) - }) - - it('handle entity not found an admin cannot create', function(done) { - this.user.isAdmin = true - this.UserMembershipHandler.getEntity.yields(null, null) - this.UserMembershipHandler.getEntityWithoutAuthorizationCheck.yields( - null, - null - ) - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - expect(error).to.exist - expect(error).to.be.instanceof(Error) - expect(error.constructor.name).to.equal('NotFoundError') - return done() - } - ) - }) - - it('handle entity no access', function(done) { - this.UserMembershipHandler.getEntity.yields(null, null) - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted) - return done() - } - ) - }) - - it('handle anonymous user', function(done) { - this.AuthenticationController.getSessionUser.returns(null) - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted) - sinon.assert.notCalled(this.UserMembershipHandler.getEntity) - expect(this.req.entity).to.not.exist - return done() - } - ) - }) - - it('checks user is staff if required', function(done) { - return this.UserMembershipAuthorization.requireInstitutionManagementStaffAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.called(this.AuthorizationMiddleware.redirectToRestricted) - sinon.assert.notCalled(this.UserMembershipHandler.getEntity) - expect(this.req.entity).to.not.exist - return done() - } - ) - }) - }) - - describe('requireEntityAccess', function() { - it('handle team access', function(done) { - return this.UserMembershipAuthorization.requireTeamMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.params.id, - { fields: { primaryKey: 'overleaf.id' } } - ) - return done() - } - ) - }) - - it('handle group access', function(done) { - return this.UserMembershipAuthorization.requireGroupMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.params.id, - { translations: { title: 'group_account' } } - ) - return done() - } - ) - }) - - it('handle group managers access', function(done) { - return this.UserMembershipAuthorization.requireGroupManagersManagementAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.params.id, - { translations: { subtitle: 'managers_management' } } - ) - return done() - } - ) - }) - - it('handle institution access', function(done) { - return this.UserMembershipAuthorization.requireInstitutionMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.params.id, - { modelName: 'Institution' } - ) - return done() - } - ) - }) - - it('handle template with brand access', function(done) { - const templateData = { - id: 123, - title: 'Template Title', - brand: { slug: 'brand-slug' } - } - this.request.yields( - null, - { statusCode: 200 }, - JSON.stringify(templateData) - ) - return this.UserMembershipAuthorization.requireTemplateMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - 'brand-slug', - { modelName: 'Publisher' } - ) - return done() - } - ) - }) - - it('handle template without brand access', function(done) { - const templateData = { - id: 123, - title: 'Template Title', - brand: null - } - this.request.yields( - null, - { statusCode: 200 }, - JSON.stringify(templateData) - ) - return this.UserMembershipAuthorization.requireTemplateMetricsAccess( - this.req, - null, - error => { - expect(error).to.not.exist - sinon.assert.notCalled(this.UserMembershipHandler.getEntity) - sinon.assert.calledOnce( - this.AuthorizationMiddleware.ensureUserIsSiteAdmin - ) - return done() - } - ) - }) - - it('handle graph access', function(done) { - this.req.query.resource_id = 'mock-resource-id' - this.req.query.resource_type = 'institution' - const middleware = this.UserMembershipAuthorization.requireGraphAccess - return middleware(this.req, null, error => { - expect(error).to.not.exist - sinon.assert.calledWithMatch( - this.UserMembershipHandler.getEntity, - this.req.query.resource_id, - { modelName: 'Institution' } - ) - return done() - }) - }) - }) -}) diff --git a/services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js b/services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js index 6bcfe19fd1..1a3a1d6ed9 100644 --- a/services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js +++ b/services/web/test/unit/src/UserMembership/UserMembershipControllerTests.js @@ -336,6 +336,7 @@ describe('UserMembershipController', function() { describe('create', function() { beforeEach(function() { this.req.params.name = 'institution' + this.req.entityConfig = EntityConfigs['institution'] return (this.req.params.id = 123) }) @@ -352,15 +353,5 @@ describe('UserMembershipController', function() { } }) }) - - it('checks canCreate', function(done) { - this.req.params.name = 'group' - return this.UserMembershipController.create(this.req, null, error => { - expect(error).to.extist - expect(error).to.be.an.instanceof(Errors.NotFoundError) - sinon.assert.notCalled(this.UserMembershipHandler.createEntity) - return done() - }) - }) }) }) diff --git a/services/web/test/unit/src/UserMembership/UserMembershipHandlerTests.js b/services/web/test/unit/src/UserMembership/UserMembershipHandlerTests.js index 71d8693ea3..aaefd83bd7 100644 --- a/services/web/test/unit/src/UserMembership/UserMembershipHandlerTests.js +++ b/services/web/test/unit/src/UserMembership/UserMembershipHandlerTests.js @@ -92,81 +92,6 @@ describe('UserMembershipHandler', function() { })) }) - describe('getEntity', function() { - describe('group subscriptions', function() { - it('get subscription', function(done) { - return this.UserMembershipHandler.getEntity( - this.fakeEntityId, - EntityConfigs.group, - this.user, - null, - (error, subscription) => { - should.not.exist(error) - const expectedQuery = { - groupPlan: true, - _id: this.fakeEntityId, - manager_ids: ObjectId(this.user._id) - } - assertCalledWith(this.Subscription.findOne, expectedQuery) - expect(subscription).to.equal(this.subscription) - expect(subscription.membersLimit).to.equal(10) - return done() - } - ) - }) - - it('get for admin', function(done) { - return this.UserMembershipHandler.getEntity( - this.fakeEntityId, - EntityConfigs.group, - { isAdmin: true }, - null, - (error, subscription) => { - should.not.exist(error) - const expectedQuery = { - groupPlan: true, - _id: this.fakeEntityId - } - assertCalledWith(this.Subscription.findOne, expectedQuery) - return done() - } - ) - }) - - it('get with staffAccess field', function(done) { - return this.UserMembershipHandler.getEntity( - this.fakeEntityId, - EntityConfigs.group, - { staffAccess: { institutionMetrics: true } }, - 'institutionMetrics', - (error, subscription) => { - should.not.exist(error) - const expectedQuery = { - groupPlan: true, - _id: this.fakeEntityId - } - assertCalledWith(this.Subscription.findOne, expectedQuery) - return done() - } - ) - }) - - it('handle error', function(done) { - this.Subscription.findOne.yields(new Error('some error')) - return this.UserMembershipHandler.getEntity( - this.fakeEntityId, - EntityConfigs.group, - this.user._id, - null, - (error, subscription) => { - should.exist(error) - return done() - } - ) - }) - }) - }) - describe('getEntityWithoutAuthorizationCheck', function() { it('get publisher', function(done) { return this.UserMembershipHandler.getEntityWithoutAuthorizationCheck( @@ -181,63 +106,6 @@ describe('UserMembershipHandler', function() { } ) }) - - describe('institutions', function() { - it('get institution', function(done) { - return this.UserMembershipHandler.getEntity( - this.institution.v1Id, - EntityConfigs.institution, - this.user, - null, - (error, institution) => { - should.not.exist(error) - const expectedQuery = { - v1Id: this.institution.v1Id, - managerIds: ObjectId(this.user._id) - } - assertCalledWith(this.Institution.findOne, expectedQuery) - expect(institution).to.equal(this.institution) - return done() - } - ) - }) - - it('handle errors', function(done) { - this.Institution.findOne.yields(new Error('nope')) - return this.UserMembershipHandler.getEntity( - this.fakeEntityId, - EntityConfigs.institution, - this.user._id, - null, - (error, institution) => { - should.exist(error) - expect(error).to.not.be.an.instanceof(Errors.NotFoundError) - return done() - } - ) - }) - }) - - describe('publishers', function() { - it('get publisher', function(done) { - return this.UserMembershipHandler.getEntity( - this.publisher.slug, - EntityConfigs.publisher, - this.user, - null, - (error, institution) => { - should.not.exist(error) - const expectedQuery = { - slug: this.publisher.slug, - managerIds: ObjectId(this.user._id) - } - assertCalledWith(this.Publisher.findOne, expectedQuery) - expect(institution).to.equal(this.publisher) - return done() - } - ) - }) - }) }) describe('getUsers', function() {