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

Generate tokens on old projects if they're not present

Shane Kilkelly 9 лет назад
Родитель
Сommit
6482cd7dd8

+ 17 - 2
services/web/app/coffee/Features/Editor/EditorController.coffee

@@ -9,6 +9,7 @@ DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
 EditorRealTimeController = require("./EditorRealTimeController")
 EditorRealTimeController = require("./EditorRealTimeController")
 async = require('async')
 async = require('async')
 LockManager = require("../../infrastructure/LockManager")
 LockManager = require("../../infrastructure/LockManager")
+PublicAccessLevels = require("../Authorization/PublicAccessLevels")
 _ = require('underscore')
 _ = require('underscore')
 
 
 module.exports = EditorController =
 module.exports = EditorController =
@@ -200,8 +201,22 @@ module.exports = EditorController =
 	setPublicAccessLevel : (project_id, newAccessLevel, callback = (err) ->) ->
 	setPublicAccessLevel : (project_id, newAccessLevel, callback = (err) ->) ->
 		ProjectDetailsHandler.setPublicAccessLevel project_id, newAccessLevel, (err) ->
 		ProjectDetailsHandler.setPublicAccessLevel project_id, newAccessLevel, (err) ->
 			return callback(err) if err?
 			return callback(err) if err?
-			EditorRealTimeController.emitToRoom project_id, 'publicAccessLevelUpdated', newAccessLevel
-			callback()
+			EditorRealTimeController.emitToRoom(
+				project_id,
+				'project:publicAccessLevel:changed',
+				{newAccessLevel}
+			)
+			if newAccessLevel == PublicAccessLevels.TOKEN_BASED
+				ProjectDetailsHandler.ensureTokensArePresent project_id, (err, tokens) ->
+					return callback(err) if err?
+					EditorRealTimeController.emitToRoom(
+						project_id,
+						'project:tokens:changed',
+						{tokens}
+					)
+					callback()
+			else
+				callback()
 
 
 	setRootDoc: (project_id, newRootDocID, callback = (err) ->) ->
 	setRootDoc: (project_id, newRootDocID, callback = (err) ->) ->
 		ProjectEntityHandler.setRootDoc project_id, newRootDocID, (err) ->
 		ProjectEntityHandler.setRootDoc project_id, newRootDocID, (err) ->

+ 21 - 1
services/web/app/coffee/Features/Project/ProjectDetailsHandler.coffee

@@ -6,6 +6,7 @@ tpdsUpdateSender = require '../ThirdPartyDataStore/TpdsUpdateSender'
 _ = require("underscore")
 _ = require("underscore")
 PublicAccessLevels = require("../Authorization/PublicAccessLevels")
 PublicAccessLevels = require("../Authorization/PublicAccessLevels")
 Errors = require("../Errors/Errors")
 Errors = require("../Errors/Errors")
+ProjectTokenGenerator = require('./ProjectTokenGenerator')
 
 
 module.exports = ProjectDetailsHandler =
 module.exports = ProjectDetailsHandler =
 	getDetails: (project_id, callback)->
 	getDetails: (project_id, callback)->
@@ -65,6 +66,25 @@ module.exports = ProjectDetailsHandler =
 	setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
 	setPublicAccessLevel : (project_id, newAccessLevel, callback = ->)->
 		logger.log project_id: project_id, level: newAccessLevel, "set public access level"
 		logger.log project_id: project_id, level: newAccessLevel, "set public access level"
 		# TODO: remove the read-only and read-and-write items from here
 		# TODO: remove the read-only and read-and-write items from here
-		if project_id? && newAccessLevel? and _.include [PublicAccessLevels.READ_ONLY, PublicAccessLevels.READ_AND_WRITE, PublicAccessLevels.PRIVATE, PublicAccessLevels.TOKEN_BASED], newAccessLevel
+		if project_id? && newAccessLevel? and _.include [
+			PublicAccessLevels.READ_ONLY,
+			PublicAccessLevels.READ_AND_WRITE,
+			PublicAccessLevels.PRIVATE,
+			PublicAccessLevels.TOKEN_BASED
+		], newAccessLevel
 			Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
 			Project.update {_id:project_id},{publicAccesLevel:newAccessLevel}, (err)->
 				callback()
 				callback()
+
+	ensureTokensArePresent: (project_id, callback=(err, tokens)->) ->
+		ProjectGetter.getProject project_id, {tokens: 1}, (err, project) ->
+			return callback(err) if err?
+			if project.tokens? and project.tokens.readOnly? and project.tokens.readAndWrite?
+				return callback(null, project.tokens)
+			else
+				tokens =
+					readOnly: ProjectTokenGenerator.readOnlyToken()
+					readAndWrite: ProjectTokenGenerator.readAndWriteToken()
+				Project.update {_id: project_id}, {$set: {tokens: tokens}}, (err) ->
+					return callback(err) if err?
+					callback(null, tokens)
+

+ 16 - 0
services/web/app/coffee/Features/Project/ProjectTokenGenerator.coffee

@@ -0,0 +1,16 @@
+module.exports = ProjectTokenGenerator =
+
+
+	readOnlyToken: () ->
+		length = 12
+		tokenAlpha = 'bcdfghjkmnpqrstvwxyz'
+		result = ''
+		for _n in [1..length]
+			i = Math.floor(Math.floor(Math.random() * tokenAlpha.length))
+			result += tokenAlpha[i]
+		return result
+
+	readAndWriteToken: () ->
+		numerics = Math.random().toString().slice(2, 12)
+		token = ProjectTokenGenerator.readOnlyToken()
+		return "#{numerics}#{token}"

+ 3 - 12
services/web/app/coffee/models/Project.coffee

@@ -6,14 +6,7 @@ logger = require('logger-sharelatex')
 sanitize = require('sanitizer')
 sanitize = require('sanitizer')
 concreteObjectId = require('mongoose').Types.ObjectId
 concreteObjectId = require('mongoose').Types.ObjectId
 Errors  = require "../Features/Errors/Errors"
 Errors  = require "../Features/Errors/Errors"
-
-generateToken = (length) ->
-	tokenAlpha = 'bcdfghjkmnpqrstvwxyz'
-	result = ''
-	for _n in [1..length]
-		i = Math.floor(Math.floor(Math.random() * tokenAlpha.length))
-		result += tokenAlpha[i]
-	return result
+ProjectTokenGenerator = require '../Features/Project/ProjectTokenGenerator'
 
 
 Schema = mongoose.Schema
 Schema = mongoose.Schema
 ObjectId = Schema.ObjectId
 ObjectId = Schema.ObjectId
@@ -43,13 +36,11 @@ ProjectSchema = new Schema
 	tokens            :
 	tokens            :
 		readOnly        : {
 		readOnly        : {
 			type: String,
 			type: String,
-			default: generateToken(14),
-			index: {unique: true}
+			default: ProjectTokenGenerator.readOnlyToken()
 		}
 		}
 		readAndWrite    : {
 		readAndWrite    : {
 			type: String,
 			type: String,
-			default: Math.random().toString().slice(2, 10) + generateToken(12)
-			index: {unique: true}
+			default: ProjectTokenGenerator.readAndWriteToken()
 		}
 		}
 	tokenAccessReadOnly_refs         : [ type:ObjectId, ref:'User' ]
 	tokenAccessReadOnly_refs         : [ type:ObjectId, ref:'User' ]
 	tokenAccessReadAndWrite_refs     : [ type:ObjectId, ref:'User' ]
 	tokenAccessReadAndWrite_refs     : [ type:ObjectId, ref:'User' ]

+ 10 - 0
services/web/public/coffee/ide/share/controllers/ShareController.coffee

@@ -12,6 +12,16 @@ define [
 					scope: $scope
 					scope: $scope
 				)
 				)
 
 
+			ide.socket.on 'project:tokens:changed', (data) =>
+				if data.tokens?
+					ide.$scope.project.tokens = data.tokens
+					$scope.$digest()
+
+			ide.socket.on 'project:publicAccessLevel:changed', (data) =>
+				if data.newAccessLevel?
+					ide.$scope.project.publicAccesLevel = data.newAccessLevel
+					$scope.$digest()
+
 			ide.socket.on 'project:membership:changed', (data) =>
 			ide.socket.on 'project:membership:changed', (data) =>
 				if data.members
 				if data.members
 					projectMembers.getMembers()
 					projectMembers.getMembers()

+ 9 - 3
services/web/public/coffee/ide/share/controllers/ShareProjectModalController.coffee

@@ -1,7 +1,7 @@
 define [
 define [
 	"base"
 	"base"
 ], (App) ->
 ], (App) ->
-	App.controller "ShareProjectModalController", ($scope, $modalInstance, $timeout, projectMembers, projectInvites, $modal, $http) ->
+	App.controller "ShareProjectModalController", ($scope, $modalInstance, $timeout, projectMembers, projectInvites, $modal, $http, ide) ->
 		$scope.inputs = {
 		$scope.inputs = {
 			privileges: "readAndWrite"
 			privileges: "readAndWrite"
 			contacts: []
 			contacts: []
@@ -200,10 +200,16 @@ define [
 			}
 			}
 
 
 		$scope.getReadAndWriteTokenLink = () ->
 		$scope.getReadAndWriteTokenLink = () ->
-			location.origin + "/" + $scope.project.tokens.readAndWrite
+			if $scope?.project?.tokens?.readAndWrite?
+				location.origin + "/" + $scope.project.tokens.readAndWrite
+			else
+				''
 
 
 		$scope.getReadOnlyTokenLink = () ->
 		$scope.getReadOnlyTokenLink = () ->
-			location.origin + "/read/" + $scope.project.tokens.readOnly
+			if $scope?.project?.tokens?.readOnly?
+				location.origin + "/read/" + $scope.project.tokens.readOnly
+			else
+				''
 
 
 		$scope.done = () ->
 		$scope.done = () ->
 			$modalInstance.close()
 			$modalInstance.close()

+ 94 - 15
services/web/test/UnitTests/coffee/Editor/EditorControllerTests.coffee

@@ -564,20 +564,99 @@ describe "EditorController", ->
 
 
 	describe "setPublicAccessLevel", ->
 	describe "setPublicAccessLevel", ->
 
 
-		beforeEach ->
-			@newAccessLevel = "public"
-			@ProjectDetailsHandler.setPublicAccessLevel = sinon.stub().callsArgWith(2, null)
-			@EditorRealTimeController.emitToRoom = sinon.stub()
-
-		it "should call the EditorController", (done)->
-			@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
-				@ProjectDetailsHandler.setPublicAccessLevel.calledWith(@project_id, @newAccessLevel).should.equal true
-				done()
-
-		it "should emit the update to the room", (done)->
-			@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
-				@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'publicAccessLevelUpdated', @newAccessLevel).should.equal true				
-				done()
+		describe 'when setting to private', ->
+			beforeEach ->
+				@newAccessLevel = 'private'
+				@ProjectDetailsHandler.setPublicAccessLevel = sinon.stub().callsArgWith(2, null)
+				@ProjectDetailsHandler.ensureTokensArePresent = sinon.stub()
+					.callsArgWith(1, null, @tokens)
+				@EditorRealTimeController.emitToRoom = sinon.stub()
+
+			it 'should set the access level', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@ProjectDetailsHandler.setPublicAccessLevel
+						.calledWith(@project_id, @newAccessLevel).should.equal true
+					done()
+
+			it 'should broadcast the access level change', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@EditorRealTimeController.emitToRoom
+						.calledWith(@project_id, 'project:publicAccessLevel:changed').should.equal true
+					done()
+
+			it 'should not ensure tokens are present for project', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@ProjectDetailsHandler.ensureTokensArePresent
+						.calledWith(@project_id).should.equal false
+					done()
+
+			it 'should not broadcast a token change', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@EditorRealTimeController.emitToRoom
+						.calledWith(@project_id, 'project:tokens:changed', {tokens: @tokens})
+							.should.equal false
+					done()
+
+			it 'should not produce an error', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, (err) =>
+					expect(err).to.not.exist
+					done()
+
+		describe 'when setting to tokenBased', ->
+			beforeEach ->
+				@newAccessLevel = 'tokenBased'
+				@tokens = {readOnly: 'aaa', readAndWrite: '42bbb'}
+				@ProjectDetailsHandler.setPublicAccessLevel = sinon.stub()
+					.callsArgWith(2, null)
+				@ProjectDetailsHandler.ensureTokensArePresent = sinon.stub()
+					.callsArgWith(1, null, @tokens)
+				@EditorRealTimeController.emitToRoom = sinon.stub()
+
+			it 'should set the access level', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@ProjectDetailsHandler.setPublicAccessLevel
+						.calledWith(@project_id, @newAccessLevel).should.equal true
+					done()
+
+			it 'should broadcast the access level change', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@EditorRealTimeController.emitToRoom
+						.calledWith(@project_id, 'project:publicAccessLevel:changed')
+							.should.equal true
+					done()
+
+			it 'should ensure tokens are present for project', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@ProjectDetailsHandler.ensureTokensArePresent
+						.calledWith(@project_id).should.equal true
+					done()
+
+			it 'should broadcast the token change too', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+					@EditorRealTimeController.emitToRoom
+						.calledWith(@project_id, 'project:tokens:changed', {tokens: @tokens})
+							.should.equal true
+					done()
+
+			it 'should not produce an error', (done) ->
+				@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, (err) =>
+					expect(err).to.not.exist
+					done()
+
+		# beforeEach ->
+		# 	@newAccessLevel = "public"
+		# 	@ProjectDetailsHandler.setPublicAccessLevel = sinon.stub().callsArgWith(2, null)
+		# 	@EditorRealTimeController.emitToRoom = sinon.stub()
+
+		# it "should call the EditorController", (done)->
+		# 	@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+		# 		@ProjectDetailsHandler.setPublicAccessLevel.calledWith(@project_id, @newAccessLevel).should.equal true
+		# 		done()
+
+		# it "should emit the update to the room", (done)->
+		# 	@EditorController.setPublicAccessLevel @project_id, @newAccessLevel, =>
+		# 		@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'publicAccessLevelUpdated', @newAccessLevel).should.equal true				
+		# 		done()
 
 
 	describe "setRootDoc", ->
 	describe "setRootDoc", ->
 
 
@@ -594,4 +673,4 @@ describe "EditorController", ->
 		it "should emit the update to the room", (done)->
 		it "should emit the update to the room", (done)->
 			@EditorController.setRootDoc @project_id, @newRootDocID, =>
 			@EditorController.setRootDoc @project_id, @newRootDocID, =>
 				@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'rootDocUpdated', @newRootDocID).should.equal true				
 				@EditorRealTimeController.emitToRoom.calledWith(@project_id, 'rootDocUpdated', @newRootDocID).should.equal true				
-				done()
+				done()

+ 74 - 0
services/web/test/UnitTests/coffee/Project/ProjectDetailsHandlerTests.coffee

@@ -38,6 +38,7 @@ describe 'ProjectDetailsHandler', ->
 			'logger-sharelatex':
 			'logger-sharelatex':
 				log:->
 				log:->
 				err:->
 				err:->
+			'./ProjectTokenGenerator': @ProjectTokenGenerator = {}
 
 
 	describe "getDetails", ->
 	describe "getDetails", ->
 
 
@@ -149,3 +150,76 @@ describe 'ProjectDetailsHandler', ->
 				@ProjectModel.update.calledWith({_id: @project_id}, {publicAccesLevel: @accessLevel}).should.equal true
 				@ProjectModel.update.calledWith({_id: @project_id}, {publicAccesLevel: @accessLevel}).should.equal true
 				done()
 				done()
 
 
+	describe "ensureTokensArePresent", ->
+		beforeEach ->
+
+		describe 'when the project has tokens', ->
+			beforeEach ->
+				@project =
+					_id: @project_id
+					tokens:
+						readOnly: 'aaa'
+						readAndWrite: '42bbb'
+				@ProjectGetter.getProject = sinon.stub()
+					.callsArgWith(2, null, @project)
+				@ProjectModel.update = sinon.stub()
+
+			it 'should get the project', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(@ProjectGetter.getProject.callCount).to.equal 1
+					expect(@ProjectGetter.getProject.calledWith(@project_id, {tokens: 1}))
+						.to.equal true
+					done()
+
+			it 'should not update the project with new tokens', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(@ProjectModel.update.callCount).to.equal 0
+					done()
+
+			it 'should produce the tokens without error', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(err).to.not.exist
+					expect(tokens).to.deep.equal @project.tokens
+					done()
+
+		describe 'when tokens are missing', ->
+			beforeEach ->
+				@project =
+					_id: @project_id
+				@ProjectGetter.getProject = sinon.stub()
+					.callsArgWith(2, null, @project)
+				@readOnlyToken = 'abc'
+				@readAndWriteToken = '42def'
+				@ProjectTokenGenerator.readOnlyToken = sinon.stub().returns(@readOnlyToken)
+				@ProjectTokenGenerator.readAndWriteToken = sinon.stub().returns(@readAndWriteToken)
+				@ProjectModel.update = sinon.stub()
+					.callsArgWith(2, null)
+
+			it 'should get the project', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(@ProjectGetter.getProject.callCount).to.equal 1
+					expect(@ProjectGetter.getProject.calledWith(@project_id, {tokens: 1}))
+						.to.equal true
+					done()
+
+			it 'should update the project with new tokens', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(@ProjectTokenGenerator.readOnlyToken.callCount)
+						.to.equal 1
+					expect(@ProjectTokenGenerator.readAndWriteToken.callCount)
+						.to.equal 1
+					expect(@ProjectModel.update.callCount).to.equal 1
+					expect(@ProjectModel.update.calledWith(
+						{_id: @project_id},
+						{$set: {tokens: {readOnly: @readOnlyToken, readAndWrite: @readAndWriteToken}}}
+					)).to.equal true
+					done()
+
+			it 'should produce the tokens without error', (done) ->
+				@handler.ensureTokensArePresent @project_id, (err, tokens) =>
+					expect(err).to.not.exist
+					expect(tokens).to.deep.equal {
+						readOnly: @readOnlyToken,
+						readAndWrite: @readAndWriteToken
+					}
+					done()