mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #2870 from overleaf/sk-restrict-chat
Block restricted users from Chat endpoints GitOrigin-RevId: caec8fe2bc93d567dd57f32dc765bd74ba53e933
This commit is contained in:
parent
74313e4b82
commit
f4950c21bf
5 changed files with 199 additions and 0 deletions
|
@ -43,6 +43,33 @@ module.exports = AuthorizationMiddleware = {
|
|||
})
|
||||
},
|
||||
|
||||
blockRestrictedUserFromProject(req, res, next) {
|
||||
AuthorizationMiddleware._getUserAndProjectId(req, function(
|
||||
error,
|
||||
userId,
|
||||
projectId
|
||||
) {
|
||||
if (error) {
|
||||
return next(error)
|
||||
}
|
||||
const token = TokenAccessHandler.getRequestToken(req, projectId)
|
||||
AuthorizationManager.isRestrictedUserForProject(
|
||||
userId,
|
||||
projectId,
|
||||
token,
|
||||
(err, isRestrictedUser) => {
|
||||
if (err) {
|
||||
return next(err)
|
||||
}
|
||||
if (isRestrictedUser) {
|
||||
return res.sendStatus(403)
|
||||
}
|
||||
next()
|
||||
}
|
||||
)
|
||||
})
|
||||
},
|
||||
|
||||
ensureUserCanReadProject(req, res, next) {
|
||||
AuthorizationMiddleware._getUserAndProjectId(req, function(
|
||||
error,
|
||||
|
|
|
@ -801,11 +801,13 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
|
|||
|
||||
webRouter.get(
|
||||
'/project/:project_id/messages',
|
||||
AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
ChatController.getMessages
|
||||
)
|
||||
webRouter.post(
|
||||
'/project/:project_id/messages',
|
||||
AuthorizationMiddleware.blockRestrictedUserFromProject,
|
||||
AuthorizationMiddleware.ensureUserCanReadProject,
|
||||
RateLimiterMiddleware.rateLimit({
|
||||
endpointName: 'send-chat-message',
|
||||
|
|
|
@ -4,6 +4,7 @@ const User = require('./helpers/User')
|
|||
const request = require('./helpers/request')
|
||||
const settings = require('settings-sharelatex')
|
||||
|
||||
require('./helpers/MockChatApi')
|
||||
require('./helpers/MockDocstoreApi')
|
||||
require('./helpers/MockDocUpdaterApi')
|
||||
|
||||
|
@ -262,6 +263,38 @@ function expectNoAnonymousAdminAccess(user, projectId, callback) {
|
|||
)
|
||||
}
|
||||
|
||||
function expectChatAccess(user, projectId, callback) {
|
||||
user.request.get(
|
||||
{
|
||||
url: `/project/${projectId}/messages`,
|
||||
json: true
|
||||
},
|
||||
(error, response) => {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
expect(response.statusCode).to.equal(200)
|
||||
callback()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
function expectNoChatAccess(user, projectId, callback) {
|
||||
user.request.get(
|
||||
{
|
||||
url: `/project/${projectId}/messages`,
|
||||
json: true
|
||||
},
|
||||
(error, response) => {
|
||||
if (error != null) {
|
||||
return callback(error)
|
||||
}
|
||||
expect(response.statusCode).to.equal(403)
|
||||
callback()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
describe('Authorization', function() {
|
||||
beforeEach(function(done) {
|
||||
this.timeout(90000)
|
||||
|
@ -316,6 +349,10 @@ describe('Authorization', function() {
|
|||
expectAdminAccess(this.owner, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow the owner user chat messages access', function(done) {
|
||||
expectChatAccess(this.owner, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow another user read access to the project', function(done) {
|
||||
expectNoReadAccess(
|
||||
this.other1,
|
||||
|
@ -342,6 +379,10 @@ describe('Authorization', function() {
|
|||
expectNoAdminAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow another user chat messages access', function(done) {
|
||||
expectNoChatAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow anonymous user read access to it', function(done) {
|
||||
expectNoReadAccess(
|
||||
this.anon,
|
||||
|
@ -368,6 +409,10 @@ describe('Authorization', function() {
|
|||
expectNoAnonymousAdminAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow anonymous user chat messages access', function(done) {
|
||||
expectNoChatAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow site admin users read access to it', function(done) {
|
||||
expectReadAccess(this.site_admin, this.projectId, done)
|
||||
})
|
||||
|
@ -422,6 +467,10 @@ describe('Authorization', function() {
|
|||
expectReadAccess(this.ro_user, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow the read-only user chat messages access', function(done) {
|
||||
expectChatAccess(this.ro_user, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow the read-only user write access to its content', function(done) {
|
||||
expectNoContentWriteAccess(this.ro_user, this.projectId, done)
|
||||
})
|
||||
|
@ -454,6 +503,10 @@ describe('Authorization', function() {
|
|||
it('should not allow the read-write user admin access to it', function(done) {
|
||||
expectNoAdminAccess(this.rw_user, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow the read-write user chat messages access', function(done) {
|
||||
expectChatAccess(this.rw_user, this.projectId, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('public read-write project', function() {
|
||||
|
@ -475,6 +528,10 @@ describe('Authorization', function() {
|
|||
expectContentWriteAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow a user chat messages access', function(done) {
|
||||
expectChatAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow a user write access to its settings', function(done) {
|
||||
expectNoSettingsWriteAccess(
|
||||
this.other1,
|
||||
|
@ -496,6 +553,10 @@ describe('Authorization', function() {
|
|||
expectContentWriteAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow an anonymous user chat messages access', function(done) {
|
||||
expectChatAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow an anonymous user write access to its settings', function(done) {
|
||||
expectNoSettingsWriteAccess(
|
||||
this.anon,
|
||||
|
@ -542,6 +603,11 @@ describe('Authorization', function() {
|
|||
expectNoAdminAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
// NOTE: legacy readOnly access does not count as 'restricted' in the new model
|
||||
it('should allow a user chat messages access', function(done) {
|
||||
expectChatAccess(this.other1, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should allow an anonymous user read access to it', function(done) {
|
||||
expectReadAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
@ -562,5 +628,9 @@ describe('Authorization', function() {
|
|||
it('should not allow an anonymous user admin access to it', function(done) {
|
||||
expectNoAnonymousAdminAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
|
||||
it('should not allow an anonymous user chat messages access', function(done) {
|
||||
expectNoChatAccess(this.anon, this.projectId, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
51
services/web/test/acceptance/src/helpers/MockChatApi.js
Normal file
51
services/web/test/acceptance/src/helpers/MockChatApi.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
const express = require('express')
|
||||
const bodyParser = require('body-parser')
|
||||
const app = express()
|
||||
|
||||
const projects = {}
|
||||
|
||||
const MessageHttpController = {
|
||||
getGlobalMessages: (req, res) => {
|
||||
res.send(projects[req.params.project_id] || [])
|
||||
},
|
||||
sendGlobalMessage: (req, res) => {
|
||||
const projectId = req.params.project_id
|
||||
const message = {
|
||||
id: Math.random().toString(),
|
||||
content: req.body.content,
|
||||
timestamp: Date.now(),
|
||||
user_id: req.body.user_id
|
||||
}
|
||||
projects[projectId] = projects[projectId] || []
|
||||
projects[projectId].push(message)
|
||||
res.sendStatus(201).send(Object.assign({ room_id: projectId }, message))
|
||||
}
|
||||
}
|
||||
|
||||
const MockChatApi = {
|
||||
run() {
|
||||
app.use(bodyParser.json())
|
||||
|
||||
app.get(
|
||||
'/project/:project_id/messages',
|
||||
MessageHttpController.getGlobalMessages
|
||||
)
|
||||
app.post(
|
||||
'/project/:project_id/messages',
|
||||
MessageHttpController.sendGlobalMessage
|
||||
)
|
||||
|
||||
app
|
||||
.listen(3010, error => {
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
})
|
||||
.on('error', error => {
|
||||
console.error('error starting MockChatApi:', error.message)
|
||||
return process.exit(1)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
MockChatApi.run()
|
|
@ -440,6 +440,55 @@ describe('AuthorizationMiddleware', function() {
|
|||
})
|
||||
})
|
||||
|
||||
describe('blockRestrictedUserFromProject', function() {
|
||||
beforeEach(function() {
|
||||
this.AuthorizationMiddleware._getUserAndProjectId = sinon
|
||||
.stub()
|
||||
.callsArgWith(1, null, this.userId, this.project_id)
|
||||
})
|
||||
|
||||
it('should issue a 401 response for a restricted user', function(done) {
|
||||
this.AuthorizationManager.isRestrictedUserForProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, true)
|
||||
this.req = {}
|
||||
this.next = sinon.stub()
|
||||
this.res.sendStatus = status => {
|
||||
expect(status).to.equal(403)
|
||||
expect(
|
||||
this.AuthorizationManager.isRestrictedUserForProject.called
|
||||
).to.equal(true)
|
||||
expect(this.next.called).to.equal(false)
|
||||
done()
|
||||
}
|
||||
this.AuthorizationMiddleware.blockRestrictedUserFromProject(
|
||||
this.req,
|
||||
this.res,
|
||||
this.next
|
||||
)
|
||||
})
|
||||
|
||||
it('should pass through for a regular user', function(done) {
|
||||
this.AuthorizationManager.isRestrictedUserForProject = sinon
|
||||
.stub()
|
||||
.callsArgWith(3, null, false)
|
||||
this.req = {}
|
||||
this.res.sendStatus = sinon.stub()
|
||||
this.next = status => {
|
||||
expect(
|
||||
this.AuthorizationManager.isRestrictedUserForProject.called
|
||||
).to.equal(true)
|
||||
expect(this.res.sendStatus.called).to.equal(false)
|
||||
done()
|
||||
}
|
||||
this.AuthorizationMiddleware.blockRestrictedUserFromProject(
|
||||
this.req,
|
||||
this.res,
|
||||
this.next
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('ensureUserCanReadMultipleProjects', function() {
|
||||
beforeEach(function() {
|
||||
this.AuthorizationManager.canUserReadProject = sinon.stub()
|
||||
|
|
Loading…
Reference in a new issue