Просмотр исходного кода

Merge pull request #969 from sharelatex/ew-check-doc-token-access

check access for doc on read only token
Ersun Warncke 8 лет назад
Родитель
Сommit
7b90fcb186

+ 20 - 17
services/web/app/coffee/Features/TokenAccess/TokenAccessController.coffee

@@ -94,23 +94,26 @@ module.exports = TokenAccessController =
 					return next(new Errors.NotFoundError())
 				TokenAccessController._tryHigherAccess(token, userId, req, res, next)
 			else
-				if !userId?
-					logger.log {userId, projectId: project._id},
-						"[TokenAccess] adding anonymous user to project with readOnly token"
-					TokenAccessHandler.grantSessionTokenAccess(req, project._id, token)
-					req._anonymousAccessToken = token
-					return TokenAccessController._loadEditor(project._id, req, res, next)
-				else
-					if project.owner_ref.toString() == userId
+				TokenAccessHandler.checkV1Access token, (err, allow_access, redirect_path) ->
+					return next err if err?
+					return res.redirect redirect_path unless allow_access
+					if !userId?
 						logger.log {userId, projectId: project._id},
-							"[TokenAccess] user is already project owner"
-						return TokenAccessController._loadEditor(project._id, req, res, next)
-					logger.log {userId, projectId: project._id},
-						"[TokenAccess] adding user to project with readOnly token"
-					TokenAccessHandler.addReadOnlyUserToProject userId, project._id, (err) ->
-						if err?
-							logger.err {err, token, userId, projectId: project._id},
-								"[TokenAccess] error adding user to project with readAndWrite token"
-							return next(err)
+							"[TokenAccess] adding anonymous user to project with readOnly token"
+						TokenAccessHandler.grantSessionTokenAccess(req, project._id, token)
+						req._anonymousAccessToken = token
 						return TokenAccessController._loadEditor(project._id, req, res, next)
+					else
+						if project.owner_ref.toString() == userId
+							logger.log {userId, projectId: project._id},
+								"[TokenAccess] user is already project owner"
+							return TokenAccessController._loadEditor(project._id, req, res, next)
+						logger.log {userId, projectId: project._id},
+							"[TokenAccess] adding user to project with readOnly token"
+						TokenAccessHandler.addReadOnlyUserToProject userId, project._id, (err) ->
+							if err?
+								logger.err {err, token, userId, projectId: project._id},
+									"[TokenAccess] error adding user to project with readAndWrite token"
+								return next(err)
+							return TokenAccessController._loadEditor(project._id, req, res, next)
 

+ 8 - 0
services/web/app/coffee/Features/TokenAccess/TokenAccessHandler.coffee

@@ -4,6 +4,7 @@ PublicAccessLevels = require '../Authorization/PublicAccessLevels'
 PrivilegeLevels = require '../Authorization/PrivilegeLevels'
 ObjectId = require("mongojs").ObjectId
 Settings = require('settings-sharelatex')
+V1Api = require "../V1/V1Api"
 
 module.exports = TokenAccessHandler =
 
@@ -108,3 +109,10 @@ module.exports = TokenAccessHandler =
 				project.tokens.readAndWrite = ''
 			if privilegeLevel != PrivilegeLevels.READ_ONLY
 				project.tokens.readOnly = ''
+
+	checkV1Access: (token, callback=(err, allow, redirect)->) ->
+		return callback(null, true) unless Settings.apis?.v1?
+		V1Api.request { url: "/api/v1/sharelatex/docs/#{token}/is_published" }, (err, response, body) ->
+			return callback err if err?
+			callback null, false, body.published_path if body.allow == false
+			callback null, true

+ 26 - 0
services/web/app/coffee/Features/V1/V1Api.coffee

@@ -0,0 +1,26 @@
+request = require 'request'
+settings = require 'settings-sharelatex'
+
+# TODO: check what happens when these settings aren't defined
+DEFAULT_V1_PARAMS = {
+	baseUrl: settings?.apis?.v1?.url
+	auth:
+		user: settings?.apis?.v1?.user
+		pass: settings?.apis?.v1?.pass
+	json: true,
+	timeout: 30 * 1000
+}
+
+request = request.defaults(DEFAULT_V1_PARAMS)
+
+module.exports = V1Api =
+	request: (options, callback) ->
+		return request(options) if !callback?
+		request options, (error, response, body) ->
+			return callback(error, response, body) if error?
+			if 200 <= response.statusCode < 300 or response.statusCode in (options.expectedStatusCodes or [])
+				callback null, response, body
+			else
+				error = new Error("overleaf v1 returned non-success code: #{response.statusCode}")
+				error.statusCode = response.statusCode
+				callback error

+ 3 - 0
services/web/test/acceptance/coffee/helpers/MockV1Api.coffee

@@ -81,5 +81,8 @@ module.exports = MockV1Api =
 		.on "error", (error) ->
 			console.error "error starting MockV1Api:", error.message
 			process.exit(1)
+		
+		app.get '/api/v1/sharelatex/docs/:token/is_published', (req, res, next) =>
+			res.json { allow: true }
 
 MockV1Api.run()

+ 18 - 0
services/web/test/unit/coffee/TokenAccess/TokenAccessControllerTests.coffee

@@ -34,6 +34,9 @@ describe "TokenAccessController", ->
 				overleaf:
 					host: 'http://overleaf.test:5000'
 			}
+			'../V1/V1Api': @V1Api = {
+				request: sinon.stub().callsArgWith(1, null, {}, { allow: true })
+			}
 
 		@AuthenticationController.getLoggedInUserId = sinon.stub().returns(@userId.toString())
 
@@ -395,6 +398,21 @@ describe "TokenAccessController", ->
 
 	describe 'readOnlyToken', ->
 		beforeEach ->
+			@TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, true)
+
+		describe 'when access not allowed by v1 api', ->
+			beforeEach ->
+				@req = new MockRequest()
+				@res = new MockResponse()
+				@res.redirect = sinon.stub()
+				@next = sinon.stub()
+				@TokenAccessHandler.findProjectWithReadOnlyToken = sinon.stub()
+						.callsArgWith(1, null, @project)
+				@TokenAccessHandler.checkV1Access = sinon.stub().callsArgWith(1, null, false, 'doc-url')
+				@TokenAccessController.readOnlyToken @req, @res, @next
+
+			it 'should redirect to doc-url', ->
+				expect(@res.redirect.calledWith('doc-url')).to.equal true
 
 		describe 'with a user', ->
 			beforeEach ->

+ 47 - 2
services/web/test/unit/coffee/TokenAccess/TokenAccessHandlerTests.coffee

@@ -19,9 +19,11 @@ describe "TokenAccessHandler", ->
 		@req = {}
 		@TokenAccessHandler = SandboxedModule.require modulePath, requires:
 			'../../models/Project': {Project: @Project = {}}
-			'settings-sharelatex': {}
+			'settings-sharelatex': @settings = {}
 			'../Collaborators/CollaboratorsHandler': @CollaboratorsHandler = {}
-
+			'../V1/V1Api': @V1Api = {
+				request: sinon.stub()
+			}
 
 	describe 'findProjectWithReadOnlyToken', ->
 		beforeEach ->
@@ -489,3 +491,46 @@ describe "TokenAccessHandler", ->
 			@TokenAccessHandler.protectTokens(@project, 'owner')
 			expect(@project.tokens.readAndWrite).to.equal 'rw'
 			expect(@project.tokens.readOnly).to.equal 'ro'
+
+	describe 'checkV1Access', ->
+		beforeEach ->
+			@callback = sinon.stub()
+
+		describe 'when v1 api not set', ->
+			beforeEach ->
+				@TokenAccessHandler.checkV1Access @token, @callback
+
+			it 'should not check access and return true', ->
+				expect(@V1Api.request.called).to.equal false
+				expect(@callback.calledWith null, true).to.equal true
+
+		describe 'when v1 api is set', ->
+			beforeEach ->
+				@settings.apis = { v1: 'v1' }
+
+			describe 'when access allowed', ->
+				beforeEach ->
+					@V1Api.request = sinon.stub().callsArgWith(1, null, {}, { allow: true} )
+					@TokenAccessHandler.checkV1Access @token, @callback
+
+				it 'should check api', ->
+					expect(@V1Api.request.calledWith { url: "/api/v1/sharelatex/docs/#{@token}/is_published" }).to.equal true
+
+				it 'should callback with true', ->
+					expect(@callback.calledWith null, true).to.equal true
+
+			describe 'when access denied', ->
+				beforeEach ->
+					@V1Api.request = sinon.stub().callsArgWith(1, null, {}, { allow: false, published_path: 'doc-url'} )
+					@TokenAccessHandler.checkV1Access @token, @callback
+
+				it 'should callback with false and redirect', ->
+					expect(@callback.calledWith null, false, 'doc-url').to.equal true
+
+			describe 'on error', ->
+				beforeEach ->
+					@V1Api.request = sinon.stub().callsArgWith(1, 'error')
+					@TokenAccessHandler.checkV1Access @token, @callback
+
+				it 'should callback with error', ->
+					expect(@callback.calledWith 'error').to.equal true