Files
Verso/services/web/app/src/Features/Authorization/AuthorizationManager.js
T
Alf EatonandCopybot 1be43911b4 Merge pull request #3942 from overleaf/prettier-trailing-comma
Set Prettier's "trailingComma" setting to "es5"

GitOrigin-RevId: 9f14150511929a855b27467ad17be6ab262fe5d5
2021-04-28 02:10:01 +00:00

296 lines
8.8 KiB
JavaScript

const CollaboratorsGetter = require('../Collaborators/CollaboratorsGetter')
const CollaboratorsHandler = require('../Collaborators/CollaboratorsHandler')
const ProjectGetter = require('../Project/ProjectGetter')
const { User } = require('../../models/User')
const PrivilegeLevels = require('./PrivilegeLevels')
const TokenAccessHandler = require('../TokenAccess/TokenAccessHandler')
const PublicAccessLevels = require('./PublicAccessLevels')
const Errors = require('../Errors/Errors')
const { ObjectId } = require('mongodb')
const { promisifyAll } = require('../../util/promises')
const AuthorizationManager = {
isRestrictedUser(userId, privilegeLevel, isTokenMember) {
if (privilegeLevel === PrivilegeLevels.NONE) {
return true
}
return (
privilegeLevel === PrivilegeLevels.READ_ONLY && (isTokenMember || !userId)
)
},
isRestrictedUserForProject(userId, projectId, token, callback) {
AuthorizationManager.getPrivilegeLevelForProject(
userId,
projectId,
token,
(err, privilegeLevel) => {
if (err) {
return callback(err)
}
CollaboratorsHandler.userIsTokenMember(
userId,
projectId,
(err, isTokenMember) => {
if (err) {
return callback(err)
}
callback(
null,
AuthorizationManager.isRestrictedUser(
userId,
privilegeLevel,
isTokenMember
)
)
}
)
}
)
},
getPublicAccessLevel(projectId, callback) {
if (!ObjectId.isValid(projectId)) {
return callback(new Error('invalid project id'))
}
// Note, the Project property in the DB is `publicAccesLevel`, without the second `s`
ProjectGetter.getProject(
projectId,
{ publicAccesLevel: 1 },
function (error, project) {
if (error) {
return callback(error)
}
if (!project) {
return callback(
new Errors.NotFoundError(`no project found with id ${projectId}`)
)
}
callback(null, project.publicAccesLevel)
}
)
},
// Get the privilege level that the user has for the project
// Returns:
// * privilegeLevel: "owner", "readAndWrite", of "readOnly" if the user has
// access. false if the user does not have access
// * becausePublic: true if the access level is only because the project is public.
// * becauseSiteAdmin: true if access level is only because user is admin
getPrivilegeLevelForProject(userId, projectId, token, callback) {
if (userId) {
AuthorizationManager.getPrivilegeLevelForProjectWithUser(
userId,
projectId,
token,
callback
)
} else {
AuthorizationManager.getPrivilegeLevelForProjectWithoutUser(
projectId,
token,
callback
)
}
},
// User is present, get their privilege level from database
getPrivilegeLevelForProjectWithUser(userId, projectId, token, callback) {
CollaboratorsGetter.getMemberIdPrivilegeLevel(
userId,
projectId,
function (error, privilegeLevel) {
if (error) {
return callback(error)
}
if (privilegeLevel && privilegeLevel !== PrivilegeLevels.NONE) {
// The user has direct access
return callback(null, privilegeLevel, false, false)
}
AuthorizationManager.isUserSiteAdmin(userId, function (error, isAdmin) {
if (error) {
return callback(error)
}
if (isAdmin) {
return callback(null, PrivilegeLevels.OWNER, false, true)
}
// Legacy public-access system
// User is present (not anonymous), but does not have direct access
AuthorizationManager.getPublicAccessLevel(
projectId,
function (err, publicAccessLevel) {
if (err) {
return callback(err)
}
if (publicAccessLevel === PublicAccessLevels.READ_ONLY) {
return callback(null, PrivilegeLevels.READ_ONLY, true, false)
}
if (publicAccessLevel === PublicAccessLevels.READ_AND_WRITE) {
return callback(
null,
PrivilegeLevels.READ_AND_WRITE,
true,
false
)
}
callback(null, PrivilegeLevels.NONE, false, false)
}
)
})
}
)
},
// User is Anonymous, Try Token-based access
getPrivilegeLevelForProjectWithoutUser(projectId, token, callback) {
AuthorizationManager.getPublicAccessLevel(
projectId,
function (err, publicAccessLevel) {
if (err) {
return callback(err)
}
if (publicAccessLevel === PublicAccessLevels.READ_ONLY) {
// Legacy public read-only access for anonymous user
return callback(null, PrivilegeLevels.READ_ONLY, true, false)
}
if (publicAccessLevel === PublicAccessLevels.READ_AND_WRITE) {
// Legacy public read-write access for anonymous user
return callback(null, PrivilegeLevels.READ_AND_WRITE, true, false)
}
if (publicAccessLevel === PublicAccessLevels.TOKEN_BASED) {
return AuthorizationManager.getPrivilegeLevelForProjectWithToken(
projectId,
token,
callback
)
}
// Deny anonymous user access
callback(null, PrivilegeLevels.NONE, false, false)
}
)
},
getPrivilegeLevelForProjectWithToken(projectId, token, callback) {
// Anonymous users can have read-only access to token-based projects,
// while read-write access must be logged in,
// unless the `enableAnonymousReadAndWriteSharing` setting is enabled
TokenAccessHandler.validateTokenForAnonymousAccess(
projectId,
token,
function (err, isValidReadAndWrite, isValidReadOnly) {
if (err) {
return callback(err)
}
if (isValidReadOnly) {
// Grant anonymous user read-only access
return callback(null, PrivilegeLevels.READ_ONLY, false, false)
}
if (isValidReadAndWrite) {
// Grant anonymous user read-and-write access
return callback(null, PrivilegeLevels.READ_AND_WRITE, false, false)
}
// Deny anonymous access
callback(null, PrivilegeLevels.NONE, false, false)
}
)
},
canUserReadProject(userId, projectId, token, callback) {
AuthorizationManager.getPrivilegeLevelForProject(
userId,
projectId,
token,
function (error, privilegeLevel) {
if (error) {
return callback(error)
}
callback(
null,
[
PrivilegeLevels.OWNER,
PrivilegeLevels.READ_AND_WRITE,
PrivilegeLevels.READ_ONLY,
].includes(privilegeLevel)
)
}
)
},
canUserWriteProjectContent(userId, projectId, token, callback) {
AuthorizationManager.getPrivilegeLevelForProject(
userId,
projectId,
token,
function (error, privilegeLevel) {
if (error) {
return callback(error)
}
callback(
null,
[PrivilegeLevels.OWNER, PrivilegeLevels.READ_AND_WRITE].includes(
privilegeLevel
)
)
}
)
},
canUserWriteProjectSettings(userId, projectId, token, callback) {
AuthorizationManager.getPrivilegeLevelForProject(
userId,
projectId,
token,
function (error, privilegeLevel, becausePublic) {
if (error) {
return callback(error)
}
if (privilegeLevel === PrivilegeLevels.OWNER) {
return callback(null, true)
}
if (
privilegeLevel === PrivilegeLevels.READ_AND_WRITE &&
!becausePublic
) {
return callback(null, true)
}
callback(null, false)
}
)
},
canUserAdminProject(userId, projectId, token, callback) {
AuthorizationManager.getPrivilegeLevelForProject(
userId,
projectId,
token,
function (error, privilegeLevel, becausePublic, becauseSiteAdmin) {
if (error) {
return callback(error)
}
callback(
null,
privilegeLevel === PrivilegeLevels.OWNER,
becauseSiteAdmin
)
}
)
},
isUserSiteAdmin(userId, callback) {
if (!userId) {
return callback(null, false)
}
User.findOne({ _id: userId }, { isAdmin: 1 }, function (error, user) {
if (error) {
return callback(error)
}
callback(null, (user && user.isAdmin) === true)
})
},
}
module.exports = AuthorizationManager
module.exports.promises = promisifyAll(AuthorizationManager, {
without: 'isRestrictedUser',
})