mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05:00
Allow open from template button when lacking source
GitOrigin-RevId: 8fd49bff6cc0f66d041bb70f9345b2744978be3a
This commit is contained in:
parent
ee800f7448
commit
ed3147a58f
44 changed files with 148 additions and 538 deletions
|
@ -132,9 +132,6 @@ compile_full:
|
|||
$(MAKE) compile_modules_full
|
||||
$(MAKE) compile # ide.js, main.js, share.js, and anything missed
|
||||
|
||||
compile_css_full:
|
||||
$(MAKE) css_full
|
||||
|
||||
compile_modules: $(MODULE_MAKEFILES)
|
||||
@set -e; \
|
||||
for dir in $(MODULE_DIRS); \
|
||||
|
|
|
@ -203,7 +203,7 @@ module.exports = AuthenticationController =
|
|||
return next()
|
||||
else
|
||||
logger.log url:req.url, "user trying to access endpoint not in global whitelist"
|
||||
AuthenticationController.setRedirectInSession(req)
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
return res.redirect "/login"
|
||||
|
||||
httpAuth: basicAuth (user, pass)->
|
||||
|
@ -212,16 +212,6 @@ module.exports = AuthenticationController =
|
|||
logger.err user:user, pass:pass, "invalid login details"
|
||||
return isValid
|
||||
|
||||
setRedirectInSession: (req, value) ->
|
||||
if !value?
|
||||
value = if Object.keys(req.query).length > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else "#{req.path}"
|
||||
if (
|
||||
req.session? &&
|
||||
!/^\/(socket.io|js|stylesheets|img)\/.*$/.test(value) &&
|
||||
!/^.*\.(png|jpeg|svg)$/.test(value)
|
||||
)
|
||||
req.session.postLoginRedirect = value
|
||||
|
||||
_redirectToLoginOrRegisterPage: (req, res)->
|
||||
if (req.query.zipUrl? or req.query.project_name? or req.path == '/user/subscription/new')
|
||||
return AuthenticationController._redirectToRegisterPage(req, res)
|
||||
|
@ -230,14 +220,14 @@ module.exports = AuthenticationController =
|
|||
|
||||
_redirectToLoginPage: (req, res) ->
|
||||
logger.log url: req.url, "user not logged in so redirecting to login page"
|
||||
AuthenticationController.setRedirectInSession(req)
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
url = "/login?#{querystring.stringify(req.query)}"
|
||||
res.redirect url
|
||||
Metrics.inc "security.login-redirect"
|
||||
|
||||
_redirectToRegisterPage: (req, res) ->
|
||||
logger.log url: req.url, "user not logged in so redirecting to register page"
|
||||
AuthenticationController.setRedirectInSession(req)
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
url = "/register?#{querystring.stringify(req.query)}"
|
||||
res.redirect url
|
||||
Metrics.inc "security.login-redirect"
|
||||
|
@ -255,6 +245,16 @@ module.exports = AuthenticationController =
|
|||
Metrics.inc "user.login.failed"
|
||||
callback()
|
||||
|
||||
_setRedirectInSession: (req, value) ->
|
||||
if !value?
|
||||
value = if Object.keys(req.query).length > 0 then "#{req.path}?#{querystring.stringify(req.query)}" else "#{req.path}"
|
||||
if (
|
||||
req.session? &&
|
||||
!/^\/(socket.io|js|stylesheets|img)\/.*$/.test(value) &&
|
||||
!/^.*\.(png|jpeg|svg)$/.test(value)
|
||||
)
|
||||
req.session.postLoginRedirect = value
|
||||
|
||||
_getRedirectFromSession: (req) ->
|
||||
return req?.session?.postLoginRedirect || null
|
||||
|
||||
|
|
|
@ -117,5 +117,5 @@ module.exports = AuthorizationMiddlewear =
|
|||
logger.log {from: from}, "redirecting to login"
|
||||
redirect_to = "/login"
|
||||
if from?
|
||||
AuthenticationController.setRedirectInSession(req, from)
|
||||
AuthenticationController._setRedirectInSession(req, from)
|
||||
res.redirect redirect_to
|
||||
|
|
|
@ -270,7 +270,7 @@ module.exports = ProjectController =
|
|||
project: (cb)->
|
||||
ProjectGetter.getProject(
|
||||
project_id,
|
||||
{ name: 1, lastUpdated: 1, track_changes: 1, owner_ref: 1, brandVariationId: 1, overleaf: 1 },
|
||||
{ name: 1, lastUpdated: 1, track_changes: 1, owner_ref: 1, brandVariationId: 1, 'overleaf.history.display': 1 },
|
||||
cb
|
||||
)
|
||||
user: (cb)->
|
||||
|
@ -323,9 +323,6 @@ module.exports = ProjectController =
|
|||
if subscription? and subscription.freeTrial? and subscription.freeTrial.expiresAt?
|
||||
allowedFreeTrial = !!subscription.freeTrial.allowed || true
|
||||
|
||||
showGitBridge =
|
||||
user.betaProgram && !project.overleaf?.id? # don't support v1 projects yet
|
||||
|
||||
logger.log project_id:project_id, "rendering editor page"
|
||||
res.render 'project/editor',
|
||||
title: project.name
|
||||
|
@ -373,7 +370,7 @@ module.exports = ProjectController =
|
|||
brandVariation: brandVariation
|
||||
allowedImageNames: Settings.allowedImageNames || []
|
||||
gitBridgePublicBaseUrl: Settings.gitBridgePublicBaseUrl
|
||||
showGitBridge: showGitBridge
|
||||
showGitBridge: req.query?.gitbridge == 'true' || user.isAdmin
|
||||
timer.done()
|
||||
|
||||
_buildProjectList: (allProjects, v1Projects = [])->
|
||||
|
|
|
@ -74,7 +74,13 @@ module.exports = ProjectDetailsHandler =
|
|||
if arguments.length is 3 && typeof suffixes is 'function' # make suffixes an optional argument
|
||||
callback = suffixes
|
||||
suffixes = []
|
||||
ProjectDetailsHandler.ensureProjectNameIsUnique user_id, name, suffixes, callback
|
||||
timestamp = new Date().toISOString().replace(/T(\d+):(\d+):(\d+)\..*/,' $1$2$3') # strip out unwanted characters
|
||||
ProjectDetailsHandler.ensureProjectNameIsUnique user_id, name, suffixes.concat(" (#{timestamp})"), callback
|
||||
|
||||
_addSuffixToProjectName: (name, suffix = '') ->
|
||||
# append the suffix and truncate the project title if needed
|
||||
truncatedLength = ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length
|
||||
return name.substr(0, truncatedLength) + suffix
|
||||
|
||||
# FIXME: we should put a lock around this to make it completely safe, but we would need to do that at
|
||||
# the point of project creation, rather than just checking the name at the start of the import.
|
||||
|
@ -100,12 +106,8 @@ module.exports = ProjectDetailsHandler =
|
|||
candidateName = ProjectDetailsHandler._addSuffixToProjectName(name, suffix)
|
||||
if isUnique(candidateName)
|
||||
return callback(null, candidateName, true)
|
||||
# if there are no (more) suffixes, use a numeric one
|
||||
uniqueName = ProjectDetailsHandler._addNumericSuffixToProjectName(name, allProjectNames)
|
||||
if uniqueName?
|
||||
callback(null, uniqueName, true)
|
||||
else
|
||||
callback(new Error("Failed to generate a unique name for file: #{name}"))
|
||||
# we couldn't make the name unique, something is wrong
|
||||
return callback new Errors.InvalidNameError("Project name could not be made unique")
|
||||
|
||||
fixProjectName: (name) ->
|
||||
if name == "" || !name
|
||||
|
@ -154,30 +156,3 @@ module.exports = ProjectDetailsHandler =
|
|||
Project.update {_id: project_id}, {$set: {tokens: tokens}}, (err) ->
|
||||
return callback(err) if err?
|
||||
callback(null, tokens)
|
||||
|
||||
_addSuffixToProjectName: (name, suffix = '') ->
|
||||
# append the suffix and truncate the project title if needed
|
||||
truncatedLength = ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length
|
||||
return name.substr(0, truncatedLength) + suffix
|
||||
|
||||
_addNumericSuffixToProjectName: (name, allProjectNames) ->
|
||||
NUMERIC_SUFFIX_MATCH = / \((\d+)\)$/
|
||||
suffixedName = (basename, number) ->
|
||||
suffix = " (#{number})"
|
||||
return basename.substr(0, ProjectDetailsHandler.MAX_PROJECT_NAME_LENGTH - suffix.length) + suffix
|
||||
|
||||
match = name.match(NUMERIC_SUFFIX_MATCH)
|
||||
basename = name
|
||||
n = 1
|
||||
last = allProjectNames.size + n
|
||||
|
||||
if match?
|
||||
basename = name.replace(NUMERIC_SUFFIX_MATCH, '')
|
||||
n = parseInt(match[1])
|
||||
|
||||
while n <= last
|
||||
candidate = suffixedName(basename, n)
|
||||
return candidate unless allProjectNames.has(candidate)
|
||||
n += 1
|
||||
|
||||
return null
|
|
@ -12,7 +12,6 @@ UserGetter = require "../User/UserGetter"
|
|||
FeaturesUpdater = require './FeaturesUpdater'
|
||||
planFeatures = require './planFeatures'
|
||||
GroupPlansData = require './GroupPlansData'
|
||||
V1SubscriptionManager = require "./V1SubscriptionManager"
|
||||
|
||||
module.exports = SubscriptionController =
|
||||
|
||||
|
@ -98,8 +97,7 @@ module.exports = SubscriptionController =
|
|||
managedGroupSubscriptions,
|
||||
confirmedMemberInstitutions,
|
||||
managedInstitutions,
|
||||
v1Subscriptions,
|
||||
v1SubscriptionStatus
|
||||
v1Subscriptions
|
||||
} = results
|
||||
logger.log {
|
||||
user,
|
||||
|
@ -108,8 +106,7 @@ module.exports = SubscriptionController =
|
|||
managedGroupSubscriptions,
|
||||
confirmedMemberInstitutions,
|
||||
managedInstitutions,
|
||||
v1Subscriptions,
|
||||
v1SubscriptionStatus
|
||||
v1Subscriptions
|
||||
}, "showing subscription dashboard"
|
||||
plans = SubscriptionViewModelBuilder.buildViewModel()
|
||||
data = {
|
||||
|
@ -121,8 +118,7 @@ module.exports = SubscriptionController =
|
|||
managedGroupSubscriptions,
|
||||
confirmedMemberInstitutions,
|
||||
managedInstitutions,
|
||||
v1Subscriptions,
|
||||
v1SubscriptionStatus
|
||||
v1Subscriptions
|
||||
}
|
||||
res.render "subscriptions/dashboard", data
|
||||
|
||||
|
@ -162,15 +158,6 @@ module.exports = SubscriptionController =
|
|||
return next(err)
|
||||
res.redirect "/user/subscription"
|
||||
|
||||
cancelV1Subscription: (req, res, next) ->
|
||||
user_id = AuthenticationController.getLoggedInUserId(req)
|
||||
logger.log {user_id}, "canceling v1 subscription"
|
||||
V1SubscriptionManager.cancelV1Subscription user_id, (err)->
|
||||
if err?
|
||||
logger.err err:err, user_id:user_id, "something went wrong canceling v1 subscription"
|
||||
return next(err)
|
||||
res.redirect "/user/subscription"
|
||||
|
||||
updateSubscription: (req, res, next)->
|
||||
_origin = req?.query?.origin || null
|
||||
user = AuthenticationController.getSessionUser(req)
|
||||
|
|
|
@ -40,8 +40,6 @@ module.exports =
|
|||
webRouter.post '/user/subscription/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelSubscription
|
||||
webRouter.post '/user/subscription/reactivate', AuthenticationController.requireLogin(), SubscriptionController.reactivateSubscription
|
||||
|
||||
webRouter.post '/user/subscription/v1/cancel', AuthenticationController.requireLogin(), SubscriptionController.cancelV1Subscription
|
||||
|
||||
webRouter.put '/user/subscription/extend', AuthenticationController.requireLogin(), SubscriptionController.extendTrial
|
||||
|
||||
webRouter.get "/user/subscription/upgrade-annual", AuthenticationController.requireLogin(), SubscriptionController.renderUpgradeToAnnualPlanPage
|
||||
|
|
|
@ -29,7 +29,13 @@ module.exports = SubscriptionUpdater =
|
|||
@addUsersToGroup(subscriptionId, [userId], callback)
|
||||
|
||||
addUsersToGroup: (subscriptionId, memberIds, callback)->
|
||||
@addUsersToGroupWithoutFeaturesRefresh subscriptionId, memberIds, (err) ->
|
||||
logger.log subscriptionId: subscriptionId, memberIds: memberIds, "adding members into mongo subscription"
|
||||
searchOps =
|
||||
_id: subscriptionId
|
||||
insertOperation =
|
||||
{ $addToSet: { member_ids: { $each: memberIds } } }
|
||||
|
||||
Subscription.findAndModify searchOps, insertOperation, (err, subscription) ->
|
||||
return callback(err) if err?
|
||||
|
||||
# Only apply features updates to users, not user stubs
|
||||
|
@ -39,14 +45,6 @@ module.exports = SubscriptionUpdater =
|
|||
userIds = users.map (u) -> u._id.toString()
|
||||
async.map userIds, FeaturesUpdater.refreshFeatures, callback
|
||||
|
||||
addUsersToGroupWithoutFeaturesRefresh: (subscriptionId, memberIds, callback)->
|
||||
logger.log subscriptionId: subscriptionId, memberIds: memberIds, "adding members into mongo subscription"
|
||||
searchOps =
|
||||
_id: subscriptionId
|
||||
insertOperation =
|
||||
{ $addToSet: { member_ids: { $each: memberIds } } }
|
||||
|
||||
Subscription.findAndModify searchOps, insertOperation, callback
|
||||
|
||||
removeUserFromGroup: (subscriptionId, user_id, callback)->
|
||||
searchOps =
|
||||
|
|
|
@ -51,10 +51,6 @@ module.exports =
|
|||
return cb(error) if error?
|
||||
# Only return one argument to async.auto, otherwise it returns an array
|
||||
cb(null, subscriptions)
|
||||
v1SubscriptionStatus: (cb) ->
|
||||
V1SubscriptionManager.getSubscriptionStatusFromV1 user._id, (error, status, v1Id) ->
|
||||
return cb(error) if error?
|
||||
cb(null, status)
|
||||
}, (err, results) ->
|
||||
return callback(err) if err?
|
||||
{
|
||||
|
@ -64,7 +60,6 @@ module.exports =
|
|||
confirmedMemberInstitutions,
|
||||
managedInstitutions,
|
||||
v1Subscriptions,
|
||||
v1SubscriptionStatus,
|
||||
recurlySubscription,
|
||||
plan
|
||||
} = results
|
||||
|
@ -73,7 +68,6 @@ module.exports =
|
|||
confirmedMemberInstitutions ?= []
|
||||
managedInstitutions ?= []
|
||||
v1Subscriptions ?= {}
|
||||
v1SubscriptionStatus ?= {}
|
||||
|
||||
|
||||
if personalSubscription?.toObject?
|
||||
|
@ -103,8 +97,7 @@ module.exports =
|
|||
memberGroupSubscriptions,
|
||||
confirmedMemberInstitutions,
|
||||
managedInstitutions,
|
||||
v1Subscriptions,
|
||||
v1SubscriptionStatus
|
||||
v1Subscriptions
|
||||
}
|
||||
|
||||
buildViewModel : ->
|
||||
|
|
|
@ -39,18 +39,6 @@ module.exports = V1SubscriptionManager =
|
|||
url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscriptions"
|
||||
}, callback
|
||||
|
||||
getSubscriptionStatusFromV1: (userId, callback=(err, status) ->) ->
|
||||
V1SubscriptionManager._v1Request userId, {
|
||||
method: 'GET',
|
||||
url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscription_status"
|
||||
}, callback
|
||||
|
||||
cancelV1Subscription: (userId, callback=(err)->) ->
|
||||
V1SubscriptionManager._v1Request userId, {
|
||||
method: 'DELETE',
|
||||
url: (v1Id) -> "/api/v1/sharelatex/users/#{v1Id}/subscription"
|
||||
}, callback
|
||||
|
||||
v1IdForUser: (userId, callback=(err, v1Id) ->) ->
|
||||
UserGetter.getUser userId, {'overleaf.id': 1}, (err, user) ->
|
||||
return callback(err) if err?
|
||||
|
@ -88,7 +76,7 @@ module.exports = V1SubscriptionManager =
|
|||
pass: settings.apis.v1.pass
|
||||
sendImmediately: true
|
||||
json: true,
|
||||
timeout: 15 * 1000
|
||||
timeout: 5 * 1000
|
||||
}, (error, response, body) ->
|
||||
if error?
|
||||
# Specially handle no connection err, so warning can be shown
|
||||
|
|
|
@ -21,5 +21,5 @@ module.exports = SudoModeMiddlewear =
|
|||
return next()
|
||||
else
|
||||
logger.log {userId}, "[SudoMode] sudo mode not active, redirecting"
|
||||
AuthenticationController.setRedirectInSession(req)
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
return res.redirect('/confirm-password')
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = TokenAccessController =
|
|||
else
|
||||
logger.log {token, projectId: project._id},
|
||||
"[TokenAccess] deny anonymous read-and-write token access"
|
||||
AuthenticationController.setRedirectInSession(req)
|
||||
AuthenticationController._setRedirectInSession(req)
|
||||
return res.redirect('/restricted')
|
||||
if project.owner_ref.toString() == userId
|
||||
logger.log {userId, projectId: project._id},
|
||||
|
|
|
@ -51,7 +51,7 @@ module.exports =
|
|||
# such as being sent from the editor to /login, then set the redirect explicitly
|
||||
if req.query.redir? and !AuthenticationController._getRedirectFromSession(req)?
|
||||
logger.log {redir: req.query.redir}, "setting explicit redirect from login page"
|
||||
AuthenticationController.setRedirectInSession(req, req.query.redir)
|
||||
AuthenticationController._setRedirectInSession(req, req.query.redir)
|
||||
res.render 'user/login',
|
||||
title: 'login',
|
||||
email: req.query.email
|
||||
|
|
|
@ -6,31 +6,13 @@ Errors = require('../Errors/Errors')
|
|||
logger = require("logger-sharelatex")
|
||||
|
||||
module.exports =
|
||||
requireTeamAccess: (req, res, next) ->
|
||||
requireAccessToEntity('team', req.params.id, req, res, next)
|
||||
|
||||
requireGroupAccess: (req, res, next) ->
|
||||
requireAccessToEntity('group', req.params.id, req, res, next)
|
||||
|
||||
requireGroupManagersAccess: (req, res, next) ->
|
||||
requireAccessToEntity('groupManagers', req.params.id, req, res, next)
|
||||
|
||||
requireInstitutionAccess: (req, res, next) ->
|
||||
requireAccessToEntity('institution', req.params.id, req, res, next)
|
||||
|
||||
requirePublisherAccess: (req, res, next) ->
|
||||
requireAccessToEntity('publisher', req.params.id, req, res, next)
|
||||
|
||||
requireGraphAccess: (req, res, next) ->
|
||||
requireAccessToEntity(
|
||||
req.query.resource_type, req.query.resource_id, req, res, next
|
||||
)
|
||||
|
||||
requireAccessToEntity = (entityName, entityId, req, res, next) ->
|
||||
requireEntityAccess: (entityName, entityIdOverride = null) ->
|
||||
(req, res, next) ->
|
||||
loggedInUser = AuthenticationController.getSessionUser(req)
|
||||
unless loggedInUser
|
||||
return AuthorizationMiddlewear.redirectToRestricted req, res, next
|
||||
|
||||
entityId = entityIdOverride or req.params.id
|
||||
getEntity entityName, entityId, loggedInUser, (error, entity, entityConfig) ->
|
||||
return next(error) if error?
|
||||
unless entity?
|
||||
|
|
|
@ -62,19 +62,3 @@ module.exports =
|
|||
pathsFor: (id) ->
|
||||
addMember: "/manage/institutions/#{id}/managers"
|
||||
removeMember: "/manage/institutions/#{id}/managers"
|
||||
|
||||
publisher:
|
||||
modelName: 'Publisher'
|
||||
fields:
|
||||
primaryKey: 'slug'
|
||||
read: ['managerIds']
|
||||
write: 'managerIds'
|
||||
access: 'managerIds'
|
||||
name: 'name'
|
||||
translations:
|
||||
title: 'publisher_account'
|
||||
subtitle: 'managers_management'
|
||||
remove: 'remove_manager'
|
||||
pathsFor: (id) ->
|
||||
addMember: "/manage/publishers/#{id}/managers"
|
||||
removeMember: "/manage/publishers/#{id}/managers"
|
||||
|
|
|
@ -4,7 +4,6 @@ Errors = require('../Errors/Errors')
|
|||
EntityModels =
|
||||
Institution: require('../../models/Institution').Institution
|
||||
Subscription: require('../../models/Subscription').Subscription
|
||||
Publisher: require('../../models/Publisher').Publisher
|
||||
UserMembershipViewModel = require('./UserMembershipViewModel')
|
||||
UserGetter = require('../User/UserGetter')
|
||||
logger = require('logger-sharelatex')
|
||||
|
|
|
@ -5,52 +5,36 @@ TeamInvitesController = require '../Subscription/TeamInvitesController'
|
|||
|
||||
module.exports =
|
||||
apply: (webRouter) ->
|
||||
# group members routes
|
||||
webRouter.get '/manage/groups/:id/members',
|
||||
UserMembershipAuthorization.requireGroupAccess,
|
||||
UserMembershipAuthorization.requireEntityAccess('group'),
|
||||
UserMembershipController.index
|
||||
webRouter.post '/manage/groups/:id/invites',
|
||||
UserMembershipAuthorization.requireGroupAccess,
|
||||
UserMembershipAuthorization.requireEntityAccess('group'),
|
||||
TeamInvitesController.createInvite
|
||||
webRouter.delete '/manage/groups/:id/user/:user_id',
|
||||
UserMembershipAuthorization.requireGroupAccess,
|
||||
UserMembershipAuthorization.requireEntityAccess('group'),
|
||||
SubscriptionGroupController.removeUserFromGroup
|
||||
webRouter.delete '/manage/groups/:id/invites/:email',
|
||||
UserMembershipAuthorization.requireGroupAccess,
|
||||
UserMembershipAuthorization.requireEntityAccess('group'),
|
||||
TeamInvitesController.revokeInvite
|
||||
webRouter.get '/manage/groups/:id/members/export',
|
||||
UserMembershipAuthorization.requireGroupAccess,
|
||||
UserMembershipAuthorization.requireEntityAccess('group'),
|
||||
UserMembershipController.exportCsv
|
||||
|
||||
# group managers routes
|
||||
webRouter.get "/manage/groups/:id/managers",
|
||||
UserMembershipAuthorization.requireGroupManagersAccess,
|
||||
UserMembershipController.index
|
||||
webRouter.post "/manage/groups/:id/managers",
|
||||
UserMembershipAuthorization.requireGroupManagersAccess,
|
||||
UserMembershipController.add
|
||||
webRouter.delete "/manage/groups/:id/managers/:userId",
|
||||
UserMembershipAuthorization.requireGroupManagersAccess,
|
||||
UserMembershipController.remove
|
||||
|
||||
# institution members routes
|
||||
webRouter.get "/manage/institutions/:id/managers",
|
||||
UserMembershipAuthorization.requireInstitutionAccess,
|
||||
regularEntitites =
|
||||
groups: 'groupManagers'
|
||||
institutions: 'institution'
|
||||
for pathName, entityName of regularEntitites
|
||||
do (pathName, entityName) ->
|
||||
webRouter.get "/manage/#{pathName}/:id/managers",
|
||||
UserMembershipAuthorization.requireEntityAccess(entityName),
|
||||
UserMembershipController.index
|
||||
webRouter.post "/manage/institutions/:id/managers",
|
||||
UserMembershipAuthorization.requireInstitutionAccess,
|
||||
UserMembershipController.add
|
||||
webRouter.delete "/manage/institutions/:id/managers/:userId",
|
||||
UserMembershipAuthorization.requireInstitutionAccess,
|
||||
UserMembershipController.remove
|
||||
|
||||
# publisher members routes
|
||||
webRouter.get "/manage/publishers/:id/managers",
|
||||
UserMembershipAuthorization.requirePublisherAccess,
|
||||
UserMembershipController.index
|
||||
webRouter.post "/manage/publishers/:id/managers",
|
||||
UserMembershipAuthorization.requirePublisherAccess,
|
||||
webRouter.post "/manage/#{pathName}/:id/managers",
|
||||
UserMembershipAuthorization.requireEntityAccess(entityName),
|
||||
UserMembershipController.add
|
||||
webRouter.delete "/manage/publishers/:id/managers/:userId",
|
||||
UserMembershipAuthorization.requirePublisherAccess,
|
||||
|
||||
webRouter.delete "/manage/#{pathName}/:id/managers/:userId",
|
||||
UserMembershipAuthorization.requireEntityAccess(entityName),
|
||||
UserMembershipController.remove
|
||||
|
|
|
@ -16,7 +16,6 @@ hashedFiles = {}
|
|||
Path = require 'path'
|
||||
Features = require "./Features"
|
||||
Modules = require "./Modules"
|
||||
moment = require 'moment'
|
||||
|
||||
jsPath =
|
||||
if Settings.useMinifiedJs
|
||||
|
@ -127,7 +126,6 @@ module.exports = (app, webRouter, privateApiRouter, publicApiRouter)->
|
|||
res.locals.fullJsPath = Url.resolve(staticFilesBase, jsPath)
|
||||
res.locals.lib = PackageVersions.lib
|
||||
|
||||
res.locals.moment = moment
|
||||
|
||||
res.locals.buildJsPath = (jsFile, opts = {})->
|
||||
path = Path.join(jsPath, jsFile)
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
mongoose = require 'mongoose'
|
||||
Schema = mongoose.Schema
|
||||
ObjectId = Schema.ObjectId
|
||||
settings = require 'settings-sharelatex'
|
||||
request = require 'request'
|
||||
|
||||
PublisherSchema = new Schema
|
||||
slug: { type: String, required: true }
|
||||
managerIds: [ type:ObjectId, ref:'User' ]
|
||||
|
||||
# fetch publisher's (brand on v1) data from v1 API. Errors are ignored
|
||||
PublisherSchema.method 'fetchV1Data', (callback = (error, publisher)->) ->
|
||||
request {
|
||||
baseUrl: settings.apis.v1.url
|
||||
url: "/api/v2/brands/#{this.slug}"
|
||||
method: 'GET'
|
||||
auth:
|
||||
user: settings.apis.v1.user
|
||||
pass: settings.apis.v1.pass
|
||||
sendImmediately: true
|
||||
}, (error, response, body) =>
|
||||
try parsedBody = JSON.parse(body) catch e
|
||||
this.name = parsedBody?.name
|
||||
this.partner = parsedBody?.partner
|
||||
callback(null, this)
|
||||
|
||||
conn = mongoose.createConnection(settings.mongo.url, {
|
||||
server: {poolSize: settings.mongo.poolSize || 10},
|
||||
config: {autoIndex: false}
|
||||
})
|
||||
|
||||
Publisher = conn.model 'Publisher', PublisherSchema
|
||||
exports.Publisher = Publisher
|
||||
exports.PublisherSchema = PublisherSchema
|
|
@ -50,7 +50,6 @@ UserSchema = new Schema
|
|||
references: { type:Boolean, default: Settings.defaultFeatures.references }
|
||||
trackChanges: { type:Boolean, default: Settings.defaultFeatures.trackChanges }
|
||||
mendeley: { type:Boolean, default: Settings.defaultFeatures.mendeley }
|
||||
zotero: { type:Boolean, default: Settings.defaultFeatures.zotero }
|
||||
referencesSearch: { type:Boolean, default: Settings.defaultFeatures.referencesSearch }
|
||||
}
|
||||
referal_id : {type:String, default:() -> uuid.v4().split("-")[0]}
|
||||
|
|
|
@ -162,7 +162,7 @@ script(type='text/ng-template', id='newFileModalTemplate')
|
|||
on-enter="create()",
|
||||
name="url"
|
||||
)
|
||||
.row-spaced-small
|
||||
.row-spaced.small
|
||||
label(for="name") File name in this project
|
||||
input.form-control(
|
||||
type="text",
|
||||
|
|
|
@ -351,7 +351,7 @@ script(type="text/ng-template", id="v1ImportModalTemplate")
|
|||
| <strong>Direct git access to your projects</strong> is not yet available, but you can migrate your project to the Overleaf v2 GitHub integration
|
||||
|
|
||||
a(href='https://www.overleaf.com/help/343-working-offline-in-overleaf-v2', target='_blank') Read More.
|
||||
li There is no <strong>CiteULike</strong> integration
|
||||
li There are <strong>no Zotero and CiteULike</strong> integrations yet
|
||||
li Some <strong>Journals and Services in the Submit menu</strong> don't support direct submissions yet
|
||||
.v1-import-cta
|
||||
p
|
||||
|
|
|
@ -35,9 +35,6 @@ block content
|
|||
-if (settings.overleaf && v1Subscriptions)
|
||||
include ./dashboard/_v1_subscriptions
|
||||
|
||||
-if (v1SubscriptionStatus)
|
||||
include ./dashboard/_v1_subscription_status
|
||||
|
||||
-if (!hasAnySubscription)
|
||||
p You're on the #{settings.appName} Free plan.
|
||||
|
|
||||
|
|
|
@ -5,20 +5,9 @@ each managedGroupSubscription in managedGroupSubscriptions
|
|||
+teamName(managedGroupSubscription)
|
||||
p
|
||||
a.btn.btn-primary(href="/manage/groups/" + managedGroupSubscription._id + "/members")
|
||||
i.fa.fa-fw.fa-users
|
||||
|
|
||||
| Manage members
|
||||
|
|
||||
p
|
||||
a(href="/manage/groups/" + managedGroupSubscription._id + "/managers")
|
||||
i.fa.fa-fw.fa-users
|
||||
|
|
||||
a.btn.btn-primary(href="/manage/groups/" + managedGroupSubscription._id + "/managers")
|
||||
| Manage group managers
|
||||
|
|
||||
p
|
||||
a(href="/metrics/groups/" + managedGroupSubscription._id)
|
||||
i.fa.fa-fw.fa-line-chart
|
||||
|
|
||||
| View metrics
|
||||
|
||||
hr
|
||||
|
|
|
@ -4,18 +4,6 @@ each institution in managedInstitutions
|
|||
|
|
||||
strong= institution.name
|
||||
p
|
||||
a.btn.btn-primary(href="/metrics/institutions/" + institution.v1Id)
|
||||
i.fa.fa-fw.fa-line-chart
|
||||
|
|
||||
| View metrics
|
||||
p
|
||||
a(href="/institutions/" + institution.v1Id + "/hub")
|
||||
i.fa.fa-fw.fa-user-circle
|
||||
|
|
||||
| View hub
|
||||
p
|
||||
a(href="/manage/institutions/" + institution.v1Id + "/managers")
|
||||
i.fa.fa-fw.fa-users
|
||||
|
|
||||
a.btn.btn-primary(href="/manage/institutions/" + institution.v1Id + "/managers")
|
||||
| Manage institution managers
|
||||
hr
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
- if (v1SubscriptionStatus['team'])
|
||||
p
|
||||
| You have a legacy group licence from Overleaf v1.
|
||||
- if (v1SubscriptionStatus['team']['will_end_at'])
|
||||
p
|
||||
| Your current group licence ends on
|
||||
|
|
||||
strong= moment(v1SubscriptionStatus['team']['will_end_at']).format('Do MMM YY')
|
||||
|
|
||||
| and will
|
||||
|
|
||||
- if (v1SubscriptionStatus['team']['will_renew'])
|
||||
| be automatically renewed.
|
||||
- else
|
||||
| not be automatically renewed.
|
||||
- if (v1SubscriptionStatus['can_cancel_team'])
|
||||
p
|
||||
form(method="POST", action="/user/subscription/v1/cancel")
|
||||
input(type="hidden", name="_csrf", value=csrfToken)
|
||||
button().btn.btn-danger Stop automatic renewal
|
||||
- else
|
||||
p
|
||||
| Please
|
||||
|
|
||||
a(href="/contact") contact support
|
||||
|
|
||||
| to make changes to your plan
|
||||
hr
|
||||
|
||||
- if (v1SubscriptionStatus['product'])
|
||||
p
|
||||
| You have a legacy Overleaf v1
|
||||
|
|
||||
strong= v1SubscriptionStatus['product']['display_name']
|
||||
|
|
||||
| plan.
|
||||
p
|
||||
| Your plan ends on
|
||||
|
|
||||
strong= moment(v1SubscriptionStatus['product']['will_end_at']).format('Do MMM YY')
|
||||
|
|
||||
| and will
|
||||
|
|
||||
- if (v1SubscriptionStatus['product']['will_renew'])
|
||||
| be automatically renewed.
|
||||
- else
|
||||
| not be automatically renewed.
|
||||
- if (v1SubscriptionStatus['can_cancel'])
|
||||
p
|
||||
form(method="POST", action="/user/subscription/v1/cancel")
|
||||
input(type="hidden", name="_csrf", value=csrfToken)
|
||||
button().btn.btn-danger Stop automatic renewal
|
||||
- else
|
||||
p
|
||||
| Please
|
||||
|
|
||||
a(href="/contact") contact support
|
||||
|
|
||||
| to make changes to your plan
|
||||
hr
|
|
@ -1,3 +1,11 @@
|
|||
- if (v1Subscriptions.has_subscription)
|
||||
-hasAnySubscription = true
|
||||
p
|
||||
| You are subscribed to Overleaf through Overleaf v1
|
||||
p
|
||||
a.btn.btn-primary(href='/sign_in_to_v1?return_to=/users/edit%23status') Manage v1 Subscription
|
||||
hr
|
||||
|
||||
- if (v1Subscriptions.teams && v1Subscriptions.teams.length > 0)
|
||||
-hasAnySubscription = true
|
||||
for team in v1Subscriptions.teams
|
||||
|
|
|
@ -17,7 +17,7 @@ services:
|
|||
PROJECT_HISTORY_ENABLED: 'true'
|
||||
ENABLED_LINKED_FILE_TYPES: 'url'
|
||||
LINKED_URL_PROXY: 'http://localhost:6543'
|
||||
ENABLED_LINKED_FILE_TYPES: 'url,project_file,project_output_file,mendeley,zotero'
|
||||
ENABLED_LINKED_FILE_TYPES: 'url,project_file,project_output_file,mendeley'
|
||||
SHARELATEX_CONFIG: /app/test/acceptance/config/settings.test.coffee
|
||||
NODE_ENV: production
|
||||
depends_on:
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 6 KiB |
|
@ -79,8 +79,7 @@ define([
|
|||
name: file.name,
|
||||
id: file._id,
|
||||
type: 'file',
|
||||
linkedFileData,
|
||||
created: file.created
|
||||
linkedFileData
|
||||
})
|
||||
return this.recalculateDocList()
|
||||
})
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
color: @link-color-alt;
|
||||
&:focus, &:hover {
|
||||
background-color: transparent!important;
|
||||
border: 0;
|
||||
border: 0!important;
|
||||
color: @link-hover-color-alt;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,11 +42,3 @@
|
|||
@editor-toggler-hover-bg-color: @ieee-blue;
|
||||
|
||||
@toggle-switch-highlight-color: @ieee-blue;
|
||||
|
||||
@footer-link-color : @link-color;
|
||||
@footer-link-hover-color : @link-hover-color;
|
||||
|
||||
@navbar-subdued-hover-color : @ieee-blue;
|
||||
@navbar-default-link-hover-bg : @ieee-blue;
|
||||
@navbar-default-link-hover-color: @ieee-blue;
|
||||
@navbar-default-link-active-bg : @ieee-blue;
|
||||
|
|
|
@ -4,41 +4,3 @@
|
|||
@is-overleaf : true;
|
||||
@is-overleaf-light: false;
|
||||
@show-rich-text : true;
|
||||
|
||||
@ieee-wedge: 30px;
|
||||
|
||||
body > .portal-ieee {
|
||||
padding-top: @header-height;
|
||||
}
|
||||
|
||||
.portal-ieee {
|
||||
.ieee-header {
|
||||
background-color: @ieee-blue;
|
||||
margin-bottom: @margin-xl;
|
||||
padding-bottom: @padding-sm;
|
||||
padding-top: @padding-sm;
|
||||
h1 {
|
||||
margin: 0;
|
||||
}
|
||||
.ieee-logo {
|
||||
width: @navbar-brand-width;
|
||||
}
|
||||
}
|
||||
.ieee-subheader {
|
||||
background-color: @ieee-blue;
|
||||
color: #ffffff;
|
||||
line-height: 1;
|
||||
padding: @padding-md @ieee-wedge;
|
||||
position: relative;
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
border-style: solid;
|
||||
left: -1px;
|
||||
top: -1px;
|
||||
border-color: @content-alt-bg-color transparent;
|
||||
border-width: @ieee-wedge @ieee-wedge 0 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,8 +234,7 @@ describe 'Subscriptions', ->
|
|||
before (done) ->
|
||||
v1Id = MockV1Api.nextV1Id()
|
||||
MockV1Api.setUser v1Id, {
|
||||
subscription: {},
|
||||
subscription_status: {}
|
||||
subscription: {}
|
||||
}
|
||||
MockV1Api.setAffiliations [{
|
||||
email: 'confirmed-affiliation-email@stanford.example.edu'
|
||||
|
@ -282,10 +281,6 @@ describe 'Subscriptions', ->
|
|||
name: 'Test team'
|
||||
}]
|
||||
}
|
||||
subscription_status: @subscription_status = {
|
||||
product: { 'mock': 'product' }
|
||||
team: null
|
||||
}
|
||||
}
|
||||
@user.setV1Id v1Id, (error) =>
|
||||
return done(error) if error?
|
||||
|
@ -301,28 +296,3 @@ describe 'Subscriptions', ->
|
|||
|
||||
it 'should return a v1Subscriptions', ->
|
||||
expect(@data.v1Subscriptions).to.deep.equal @subscription
|
||||
|
||||
it 'should return a v1SubscriptionStatus', ->
|
||||
expect(@data.v1SubscriptionStatus).to.deep.equal @subscription_status
|
||||
|
||||
describe.only 'canceling', ->
|
||||
before (done) ->
|
||||
@user = new User()
|
||||
MockV1Api.setUser v1Id = MockV1Api.nextV1Id(), @v1_user = {}
|
||||
async.series [
|
||||
(cb) => @user.login(cb)
|
||||
(cb) => @user.setV1Id(v1Id, cb)
|
||||
], (error) =>
|
||||
@user.request {
|
||||
method: 'POST',
|
||||
url: '/user/subscription/v1/cancel'
|
||||
}, (error, @response) =>
|
||||
return done(error) if error?
|
||||
done()
|
||||
|
||||
it 'should tell v1 to cancel the subscription', ->
|
||||
expect(@v1_user.canceled).to.equal true
|
||||
|
||||
it 'should redirect to the subscription dashboard', ->
|
||||
expect(@response.statusCode).to.equal 302
|
||||
expect(@response.headers.location).to.equal '/user/subscription'
|
||||
|
|
|
@ -38,9 +38,6 @@ module.exports = MockDocUpdaterApi =
|
|||
app.post "/project/:project_id/doc/:doc_id", (req, res, next) =>
|
||||
res.sendStatus 204
|
||||
|
||||
app.delete "/project/:project_id", (req, res) =>
|
||||
res.sendStatus 204
|
||||
|
||||
app.post "/project/:project_id/doc/:doc_id/flush", (req, res, next) =>
|
||||
res.sendStatus 204
|
||||
|
||||
|
|
|
@ -53,20 +53,6 @@ module.exports = MockV1Api =
|
|||
else
|
||||
res.sendStatus 404
|
||||
|
||||
app.get "/api/v1/sharelatex/users/:v1_user_id/subscription_status", (req, res, next) =>
|
||||
user = @users[req.params.v1_user_id]
|
||||
if user?.subscription_status?
|
||||
res.json user.subscription_status
|
||||
else
|
||||
res.sendStatus 404
|
||||
|
||||
app.delete "/api/v1/sharelatex/users/:v1_user_id/subscription", (req, res, next) =>
|
||||
user = @users[req.params.v1_user_id]
|
||||
if user?
|
||||
user.canceled = true
|
||||
res.sendStatus 200
|
||||
else
|
||||
res.sendStatus 404
|
||||
|
||||
app.post "/api/v1/sharelatex/users/:v1_user_id/sync", (req, res, next) =>
|
||||
@syncUserFeatures(req.params.v1_user_id)
|
||||
|
|
|
@ -491,10 +491,10 @@ describe "AuthenticationController", ->
|
|||
beforeEach ->
|
||||
@req.headers = {}
|
||||
@AuthenticationController.httpAuth = sinon.stub()
|
||||
@setRedirect = sinon.spy(@AuthenticationController, 'setRedirectInSession')
|
||||
@_setRedirect = sinon.spy(@AuthenticationController, '_setRedirectInSession')
|
||||
|
||||
afterEach ->
|
||||
@setRedirect.restore()
|
||||
@_setRedirect.restore()
|
||||
|
||||
describe "with white listed url", ->
|
||||
beforeEach ->
|
||||
|
@ -540,7 +540,7 @@ describe "AuthenticationController", ->
|
|||
@AuthenticationController.requireGlobalLogin @req, @res, @next
|
||||
|
||||
it 'should have called setRedirectInSession', ->
|
||||
@setRedirect.callCount.should.equal 1
|
||||
@_setRedirect.callCount.should.equal 1
|
||||
|
||||
it "should redirect to the /login page", ->
|
||||
@res.redirectedTo.should.equal "/login"
|
||||
|
@ -640,18 +640,18 @@ describe "AuthenticationController", ->
|
|||
@callback.called.should.equal true
|
||||
|
||||
|
||||
describe 'setRedirectInSession', ->
|
||||
describe '_setRedirectInSession', ->
|
||||
beforeEach ->
|
||||
@req = {session: {}}
|
||||
@req.path = "/somewhere"
|
||||
@req.query = {one: "1"}
|
||||
|
||||
it 'should set redirect property on session', ->
|
||||
@AuthenticationController.setRedirectInSession(@req)
|
||||
@AuthenticationController._setRedirectInSession(@req)
|
||||
expect(@req.session.postLoginRedirect).to.equal "/somewhere?one=1"
|
||||
|
||||
it 'should set the supplied value', ->
|
||||
@AuthenticationController.setRedirectInSession(@req, '/somewhere/specific')
|
||||
@AuthenticationController._setRedirectInSession(@req, '/somewhere/specific')
|
||||
expect(@req.session.postLoginRedirect).to.equal "/somewhere/specific"
|
||||
|
||||
describe 'with a png', ->
|
||||
|
@ -659,7 +659,7 @@ describe "AuthenticationController", ->
|
|||
@req = {session: {}}
|
||||
|
||||
it 'should not set the redirect', ->
|
||||
@AuthenticationController.setRedirectInSession(@req, '/something.png')
|
||||
@AuthenticationController._setRedirectInSession(@req, '/something.png')
|
||||
expect(@req.session.postLoginRedirect).to.equal undefined
|
||||
|
||||
describe 'with a js path', ->
|
||||
|
@ -668,7 +668,7 @@ describe "AuthenticationController", ->
|
|||
@req = {session: {}}
|
||||
|
||||
it 'should not set the redirect', ->
|
||||
@AuthenticationController.setRedirectInSession(@req, '/js/something.js')
|
||||
@AuthenticationController._setRedirectInSession(@req, '/js/something.js')
|
||||
expect(@req.session.postLoginRedirect).to.equal undefined
|
||||
|
||||
describe '_getRedirectFromSession', ->
|
||||
|
|
|
@ -163,14 +163,12 @@ describe 'ProjectDetailsHandler', ->
|
|||
describe "ensureProjectNameIsUnique", ->
|
||||
beforeEach ->
|
||||
@result = {
|
||||
owned: [{_id: 1, name:"name"}, {_id: 2, name: "name1"}, {_id: 3, name: "name11"}, {_id: 100, name: "numeric"}]
|
||||
owned: [{_id: 1, name:"name"}, {_id: 2, name: "name1"}, {_id: 3, name: "name11"}]
|
||||
readAndWrite: [{_id: 4, name:"name2"}, {_id: 5, name:"name22"}]
|
||||
readOnly: [{_id:6, name:"name3"}, {_id:7, name: "name33"}]
|
||||
tokenReadAndWrite: [{_id:8, name:"name4"}, {_id:9, name:"name44"}]
|
||||
tokenReadOnly: [{_id:10, name:"name5"}, {_id:11, name:"name55"}, {_id:12, name:"x".repeat(15)}]
|
||||
}
|
||||
for i in [1..20].concat([30..40])
|
||||
@result.owned.push {_id: 100 + i, name: "numeric (#{i})"}
|
||||
@ProjectGetter.findAllUsersProjects = sinon.stub().callsArgWith(2, null, @result)
|
||||
|
||||
it "should leave a unique name unchanged", (done) ->
|
||||
|
@ -198,34 +196,9 @@ describe 'ProjectDetailsHandler', ->
|
|||
expect(changed).to.equal true
|
||||
done()
|
||||
|
||||
it "should use a numeric index if no suffix is supplied", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "name1", [], (error, name, changed) ->
|
||||
expect(name).to.equal "name1 (1)"
|
||||
expect(changed).to.equal true
|
||||
done()
|
||||
|
||||
it "should use a numeric index if all suffixes are exhausted", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "name", ["1", "11"], (error, name, changed) ->
|
||||
expect(name).to.equal "name (1)"
|
||||
expect(changed).to.equal true
|
||||
done()
|
||||
|
||||
it "should find the next lowest available numeric index for the base name", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "numeric", [], (error, name, changed) ->
|
||||
expect(name).to.equal "numeric (21)"
|
||||
expect(changed).to.equal true
|
||||
done()
|
||||
|
||||
it "should find the next available numeric index when a numeric index is already present", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "numeric (5)", [], (error, name, changed) ->
|
||||
expect(name).to.equal "numeric (21)"
|
||||
expect(changed).to.equal true
|
||||
done()
|
||||
|
||||
it "should not find a numeric index lower than the one already present", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "numeric (31)", [], (error, name, changed) ->
|
||||
expect(name).to.equal "numeric (41)"
|
||||
expect(changed).to.equal true
|
||||
it "should return an error if the name cannot be made unique", (done) ->
|
||||
@handler.ensureProjectNameIsUnique @user_id, "name", ["1", "5", "55"], (error, name, changed) ->
|
||||
expect(error).to.eql new Errors.InvalidNameError("Project name could not be made unique")
|
||||
done()
|
||||
|
||||
describe "fixProjectName", ->
|
||||
|
|
|
@ -79,7 +79,6 @@ describe "SubscriptionController", ->
|
|||
"./RecurlyWrapper": @RecurlyWrapper = {}
|
||||
"./FeaturesUpdater": @FeaturesUpdater = {}
|
||||
"./GroupPlansData": @GroupPlansData = {}
|
||||
"./V1SubscriptionManager": @V1SubscriptionManager = {}
|
||||
|
||||
|
||||
@res = new MockResponse()
|
||||
|
|
|
@ -13,7 +13,7 @@ describe 'SudoModeMiddlewear', ->
|
|||
isSudoModeActive: sinon.stub()
|
||||
@AuthenticationController =
|
||||
getLoggedInUserId: sinon.stub().returns(@userId)
|
||||
setRedirectInSession: sinon.stub()
|
||||
_setRedirectInSession: sinon.stub()
|
||||
@SudoModeMiddlewear = SandboxedModule.require modulePath, requires:
|
||||
'./SudoModeHandler': @SudoModeHandler
|
||||
'../Authentication/AuthenticationController': @AuthenticationController
|
||||
|
@ -54,7 +54,7 @@ describe 'SudoModeMiddlewear', ->
|
|||
|
||||
describe 'when sudo mode is not active', ->
|
||||
beforeEach ->
|
||||
@AuthenticationController.setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController._setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId)
|
||||
@SudoModeHandler.isSudoModeActive = sinon.stub().callsArgWith(1, null, false)
|
||||
|
||||
|
@ -71,8 +71,8 @@ describe 'SudoModeMiddlewear', ->
|
|||
|
||||
it 'should set redirect in session', (done) ->
|
||||
@call () =>
|
||||
@AuthenticationController.setRedirectInSession.callCount.should.equal 1
|
||||
@AuthenticationController.setRedirectInSession.calledWith(@req).should.equal true
|
||||
@AuthenticationController._setRedirectInSession.callCount.should.equal 1
|
||||
@AuthenticationController._setRedirectInSession.calledWith(@req).should.equal true
|
||||
done()
|
||||
|
||||
it 'should redirect to the password-prompt page', (done) ->
|
||||
|
|
|
@ -61,7 +61,7 @@ describe "TokenAccessController", ->
|
|||
@TokenAccessHandler.addReadAndWriteUserToProject = sinon.stub()
|
||||
.callsArgWith(2, null)
|
||||
@ProjectController.loadEditor = sinon.stub()
|
||||
@AuthenticationController.setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController._setRedirectInSession = sinon.stub()
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should try to find a project with this token', (done) ->
|
||||
|
@ -173,7 +173,7 @@ describe "TokenAccessController", ->
|
|||
.callsArgWith(2, null)
|
||||
@ProjectController.loadEditor = sinon.stub()
|
||||
@TokenAccessHandler.grantSessionTokenAccess = sinon.stub()
|
||||
@AuthenticationController.setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController._setRedirectInSession = sinon.stub()
|
||||
@TokenAccessController.readAndWriteToken @req, @res, @next
|
||||
|
||||
it 'should not add the user to the project with read-write access', (done) ->
|
||||
|
@ -192,8 +192,8 @@ describe "TokenAccessController", ->
|
|||
done()
|
||||
|
||||
it 'should set redirect in session', (done) ->
|
||||
expect(@AuthenticationController.setRedirectInSession.callCount).to.equal 1
|
||||
expect(@AuthenticationController.setRedirectInSession.calledWith(@req)).to.equal true
|
||||
expect(@AuthenticationController._setRedirectInSession.callCount).to.equal 1
|
||||
expect(@AuthenticationController._setRedirectInSession.calledWith(@req)).to.equal true
|
||||
done()
|
||||
|
||||
it 'should redirect to restricted page', (done) ->
|
||||
|
|
|
@ -28,7 +28,7 @@ describe "UserPagesController", ->
|
|||
getLoggedInUserId: sinon.stub().returns(@user._id)
|
||||
getSessionUser: sinon.stub().returns(@user)
|
||||
_getRedirectFromSession: sinon.stub()
|
||||
setRedirectInSession: sinon.stub()
|
||||
_setRedirectInSession: sinon.stub()
|
||||
@UserPagesController = SandboxedModule.require modulePath, requires:
|
||||
"settings-sharelatex": @settings
|
||||
"logger-sharelatex":
|
||||
|
@ -92,13 +92,13 @@ describe "UserPagesController", ->
|
|||
|
||||
beforeEach ->
|
||||
@AuthenticationController._getRedirectFromSession = sinon.stub().returns(null)
|
||||
@AuthenticationController.setRedirectInSession = sinon.stub()
|
||||
@AuthenticationController._setRedirectInSession = sinon.stub()
|
||||
@req.query.redir = '/somewhere/in/particular'
|
||||
|
||||
it 'should set a redirect', (done) ->
|
||||
@res.render = (page) =>
|
||||
@AuthenticationController.setRedirectInSession.callCount.should.equal 1
|
||||
expect(@AuthenticationController.setRedirectInSession.lastCall.args[1]).to.equal @req.query.redir
|
||||
@AuthenticationController._setRedirectInSession.callCount.should.equal 1
|
||||
expect(@AuthenticationController._setRedirectInSession.lastCall.args[1]).to.equal @req.query.redir
|
||||
done()
|
||||
@UserPagesController.loginPage @req, @res
|
||||
|
||||
|
|
|
@ -30,9 +30,10 @@ describe "UserMembershipAuthorization", ->
|
|||
log: ->
|
||||
err: ->
|
||||
|
||||
describe 'requireAccessToEntity', ->
|
||||
describe 'requireEntityAccess', ->
|
||||
it 'get entity', (done) ->
|
||||
@UserMembershipAuthorization.requireGroupAccess @req, null, (error) =>
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'group'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
|
@ -44,9 +45,19 @@ describe "UserMembershipAuthorization", ->
|
|||
expect(@req.entityConfig).to.exist
|
||||
done()
|
||||
|
||||
it 'handle unknown entity', (done) ->
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'foo'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.extist
|
||||
expect(error).to.be.an.instanceof(Errors.NotFoundError)
|
||||
sinon.assert.notCalled(@UserMembershipHandler.getEntity)
|
||||
expect(@req.entity).to.not.exist
|
||||
done()
|
||||
|
||||
it 'handle entity not found', (done) ->
|
||||
@UserMembershipHandler.getEntity.yields(null, null)
|
||||
@UserMembershipAuthorization.requireGroupAccess @req, null, (error) =>
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'institution'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.extist
|
||||
sinon.assert.called(@AuthorizationMiddlewear.redirectToRestricted)
|
||||
sinon.assert.called(@UserMembershipHandler.getEntity)
|
||||
|
@ -55,63 +66,34 @@ describe "UserMembershipAuthorization", ->
|
|||
|
||||
it 'handle anonymous user', (done) ->
|
||||
@AuthenticationController.getSessionUser.returns(null)
|
||||
@UserMembershipAuthorization.requireGroupAccess @req, null, (error) =>
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'institution'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.extist
|
||||
sinon.assert.called(@AuthorizationMiddlewear.redirectToRestricted)
|
||||
sinon.assert.notCalled(@UserMembershipHandler.getEntity)
|
||||
expect(@req.entity).to.not.exist
|
||||
done()
|
||||
|
||||
describe 'requireEntityAccess', ->
|
||||
it 'handle team access', (done) ->
|
||||
@UserMembershipAuthorization.requireTeamAccess @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
@req.params.id,
|
||||
fields: primaryKey: 'overleaf.id'
|
||||
)
|
||||
done()
|
||||
|
||||
it 'handle group access', (done) ->
|
||||
@UserMembershipAuthorization.requireGroupAccess @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
@req.params.id,
|
||||
translations: title: 'group_account'
|
||||
)
|
||||
done()
|
||||
|
||||
it 'handle group managers access', (done) ->
|
||||
@UserMembershipAuthorization.requireGroupManagersAccess @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
@req.params.id,
|
||||
translations: subtitle: 'managers_management'
|
||||
)
|
||||
done()
|
||||
|
||||
it 'handle institution access', (done) ->
|
||||
@UserMembershipAuthorization.requireInstitutionAccess @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
@req.params.id,
|
||||
modelName: 'Institution',
|
||||
)
|
||||
done()
|
||||
|
||||
it 'handle graph access', (done) ->
|
||||
@req.query.resource_id = 'mock-resource-id'
|
||||
@req.query.resource_type = 'institution'
|
||||
middlewear = @UserMembershipAuthorization.requireGraphAccess
|
||||
it 'can override entity id', (done) ->
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'group', 'entity-id-override'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
sinon.assert.calledWithMatch(
|
||||
@UserMembershipHandler.getEntity,
|
||||
@req.query.resource_id,
|
||||
modelName: 'Institution',
|
||||
'entity-id-override',
|
||||
)
|
||||
done()
|
||||
|
||||
it "doesn't cache entity id between requests", (done) ->
|
||||
middlewear = @UserMembershipAuthorization.requireEntityAccess 'group'
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
lastCallArs = @UserMembershipHandler.getEntity.lastCall.args
|
||||
expect(lastCallArs[0]).to.equal @req.params.id
|
||||
newEntityId = 'another-mock-id'
|
||||
@req.params.id = newEntityId
|
||||
middlewear @req, null, (error) =>
|
||||
expect(error).to.not.extist
|
||||
lastCallArs = @UserMembershipHandler.getEntity.lastCall.args
|
||||
expect(lastCallArs[0]).to.equal newEntityId
|
||||
done()
|
||||
|
|
|
@ -29,11 +29,6 @@ describe 'UserMembershipHandler', ->
|
|||
v1Id: 123
|
||||
managerIds: [ObjectId(), ObjectId(), ObjectId()]
|
||||
update: sinon.stub().yields(null)
|
||||
@publisher =
|
||||
_id: 'mock-publisher-id'
|
||||
slug: 'slug'
|
||||
managerIds: [ObjectId(), ObjectId()]
|
||||
update: sinon.stub().yields(null)
|
||||
|
||||
@UserMembershipViewModel =
|
||||
buildAsync: sinon.stub().yields(null, { _id: 'mock-member-id'})
|
||||
|
@ -44,15 +39,12 @@ describe 'UserMembershipHandler', ->
|
|||
findOne: sinon.stub().yields(null, @institution)
|
||||
@Subscription =
|
||||
findOne: sinon.stub().yields(null, @subscription)
|
||||
@Publisher =
|
||||
findOne: sinon.stub().yields(null, @publisher)
|
||||
@UserMembershipHandler = SandboxedModule.require modulePath, requires:
|
||||
'./UserMembershipViewModel': @UserMembershipViewModel
|
||||
'../User/UserGetter': @UserGetter
|
||||
'../Errors/Errors': Errors
|
||||
'../../models/Institution': Institution: @Institution
|
||||
'../../models/Subscription': Subscription: @Subscription
|
||||
'../../models/Publisher': Publisher: @Publisher
|
||||
'logger-sharelatex':
|
||||
log: ->
|
||||
err: ->
|
||||
|
@ -102,15 +94,6 @@ describe 'UserMembershipHandler', ->
|
|||
expect(error).to.not.be.an.instanceof(Errors.NotFoundError)
|
||||
done()
|
||||
|
||||
describe 'publishers', ->
|
||||
it 'get publisher', (done) ->
|
||||
@UserMembershipHandler.getEntity @publisher.slug, EntityConfigs.publisher, @user, (error, institution) =>
|
||||
should.not.exist(error)
|
||||
expectedQuery = slug: @publisher.slug, managerIds: ObjectId(@user._id)
|
||||
assertCalledWith(@Publisher.findOne, expectedQuery)
|
||||
expect(institution).to.equal @publisher
|
||||
done()
|
||||
|
||||
describe 'getUsers', ->
|
||||
describe 'group', ->
|
||||
it 'build view model for all users', (done) ->
|
||||
|
|
Loading…
Reference in a new issue