Bladeren bron

Merge pull request #1006 from sharelatex/hb-v2-affiliations-callback

Add affiliations on university domain confirmation
Hugh O'Brien 8 jaren geleden
bovenliggende
commit
876663f94b

+ 24 - 0
services/web/app/coffee/Features/Institutions/InstitutionsController.coffee

@@ -0,0 +1,24 @@
+logger = require("logger-sharelatex")
+UserGetter = require("../User/UserGetter")
+{ addAffiliation } = require("../Institutions/InstitutionsAPI")
+async = require('async')
+
+module.exports = InstitutionsController =
+	confirmDomain: (req, res, next) ->
+		hostname = req.body.hostname
+		reversedHostname = hostname.trim().split('').reverse().join('')
+		UserGetter.getUsersByHostname hostname, {_id:1, emails:1}, (error, users) ->
+			if error?
+				logger.err error: error, 'problem fetching users by hostname'
+				return next(error)
+			async.map users, ((user) ->
+				matchingEmails = user.emails.filter (email) -> email.reversedHostname == reversedHostname
+				for email in matchingEmails
+					addAffiliation user._id, email.email, {confirmedAt: email.confirmedAt}, (error) =>
+						if error?
+							logger.err error: error, 'problem adding affiliation while confirming hostname'
+							return next(error)
+				), (error) ->
+					if error?
+						return next(error)
+			res.sendStatus 200

+ 5 - 0
services/web/app/coffee/Features/User/UserGetter.coffee

@@ -58,6 +58,11 @@ module.exports = UserGetter =
 			# well
 			@getUserByMainEmail email, projection, callback
 
+	getUsersByHostname: (hostname, projection, callback = (error, users) ->) ->
+		reversedHostname = hostname.trim().split('').reverse().join('')
+		query = emails: { $exists: true }, 'emails.reversedHostname': reversedHostname
+		db.users.find query, projection, callback
+
 	getUsers: (user_ids, projection, callback = (error, users) ->) ->
 		try
 			user_ids = user_ids.map (u) -> ObjectId(u.toString())

+ 2 - 1
services/web/app/coffee/Features/User/UserUpdater.coffee

@@ -66,7 +66,8 @@ module.exports = UserUpdater =
 					logger.err error: error, 'problem adding affiliation while adding email'
 					return callback(error)
 
-				update = $push: emails: email: newEmail, createdAt: new Date()
+				reversedHostname = newEmail.split('@')[1].split('').reverse().join('')
+				update = $push: emails: email: newEmail, createdAt: new Date(), reversedHostname: reversedHostname
 				@updateUser userId, update, (error) ->
 					if error?
 						logger.err error: error, 'problem updating users emails'

+ 1 - 0
services/web/app/coffee/models/User.coffee

@@ -10,6 +10,7 @@ UserSchema = new Schema
 	email             : {type : String, default : ''}
 	emails: [{
 		email: { type : String, default : '' },
+		reversedHostname: { type : String, default : '' },
 		createdAt: { type : Date, default: () -> new Date() },
 		confirmedAt: { type: Date }
 	}],

+ 2 - 0
services/web/app/coffee/router.coffee

@@ -50,6 +50,7 @@ TokenAccessController = require('./Features/TokenAccess/TokenAccessController')
 Features = require('./infrastructure/Features')
 LinkedFilesRouter = require './Features/LinkedFiles/LinkedFilesRouter'
 TemplatesRouter = require './Features/Templates/TemplatesRouter'
+InstitutionsController = require './Features/Institutions/InstitutionsController'
 UserMembershipRouter = require './Features/UserMembership/UserMembershipRouter'
 
 logger = require("logger-sharelatex")
@@ -332,6 +333,7 @@ module.exports = class Router
 			),
 			AuthenticationController.httpAuth,
 			CompileController.getFileFromClsiWithoutUser
+		publicApiRouter.post '/api/institutions/confirm_university_domain', AuthenticationController.httpAuth, InstitutionsController.confirmDomain
 
 		webRouter.get '/teams', (req, res, next) ->
 			# Match v1 behaviour - if the user is signed in, show their teams list

+ 57 - 0
services/web/test/unit/coffee/Institutions/InstitutionsControllerTests.coffee

@@ -0,0 +1,57 @@
+should = require('chai').should()
+SandboxedModule = require('sandboxed-module')
+assert = require('assert')
+path = require('path')
+sinon = require('sinon')
+modulePath = path.join __dirname, "../../../../app/js/Features/Institutions/InstitutionsController"
+expect = require("chai").expect
+
+describe "InstitutionsController", ->
+
+	beforeEach ->
+		@logger = err: sinon.stub(), log: ->
+		@host = "mit.edu".split('').reverse().join('')
+		@stubbedUser1 =
+			_id: "3131231"
+			name:"bob"
+			email:"hello@world.com"
+			emails: [
+				{"email":"stubb1@mit.edu","reversedHostname":@host},
+				{"email":"test@test.com","reversedHostname":"test.com"},
+				{"email":"another@mit.edu","reversedHostname":@host}
+			]
+		@stubbedUser2 =
+			_id: "3131232"
+			name:"test"
+			email:"hello2@world.com"
+			emails: [
+				{"email":"subb2@mit.edu","reversedHostname":@host}
+			]
+		
+		@getUsersByHostname = sinon.stub().callsArgWith(2, null, [ @stubbedUser1, @stubbedUser2 ])
+		@addAffiliation = sinon.stub().callsArgWith(3, null)
+		@InstitutionsController = SandboxedModule.require modulePath, requires:
+			'logger-sharelatex': @logger
+			'../User/UserGetter':
+				getUsersByHostname: @getUsersByHostname
+			'../Institutions/InstitutionsAPI':
+				addAffiliation: @addAffiliation
+
+		@req =
+			body:{}
+
+		@res =
+			send: sinon.stub()
+			json: sinon.stub()
+		@next = sinon.stub()
+
+	describe 'confirmDomain', ->
+		it 'should add affiliations for matching users', (done)->
+			@req.body.hostname = "mit.edu"
+			@res.sendStatus = (code) =>
+				@getUsersByHostname.calledOnce.should.equal true
+				@addAffiliation.calledThrice.should.equal true
+				@addAffiliation.calledWith(@stubbedUser1._id, @stubbedUser1.emails[0].email).should.equal true
+				@addAffiliation.calledWith(@stubbedUser1._id, @stubbedUser1.emails[2].email).should.equal true
+				done()
+			@InstitutionsController.confirmDomain @req, @res, @next

+ 23 - 7
services/web/test/unit/coffee/User/UserGetterTests.coffee

@@ -14,12 +14,15 @@ describe "UserGetter", ->
 			_id: '12390i'
 			email: 'email2@foo.bar'
 			emails: [
-				{ email: 'email1@foo.bar' }
-				{ email: 'email2@foo.bar' }
+				{ email: 'email1@foo.bar', reversedHostname: 'rab.oof' }
+				{ email: 'email2@foo.bar', reversedHostname: 'rab.oof' }
 			]
 		@findOne = sinon.stub().callsArgWith(2, null, @fakeUser)
+		@find = sinon.stub().callsArgWith(2, null, [ @fakeUser ])
 		@Mongo =
-			db: users: findOne: @findOne
+			db: users:
+				 findOne: @findOne
+				 find: @find
 			ObjectId: (id) -> return id
 		settings = apis: { v1: { url: 'v1.url', user: '', pass: '' } }
 		@getUserAffiliations = sinon.stub().callsArgWith(1, null, [])
@@ -66,8 +69,8 @@ describe "UserGetter", ->
 			@UserGetter.getUser = sinon.stub().callsArgWith(2, null, @fakeUser)
 			@UserGetter.getUserFullEmails @fakeUser._id, (error, fullEmails) =>
 				assert.deepEqual fullEmails, [
-					{ email: 'email1@foo.bar', default: false }
-					{ email: 'email2@foo.bar', default: true }
+					{ email: 'email1@foo.bar', reversedHostname: 'rab.oof', default: false }
+					{ email: 'email2@foo.bar', reversedHostname: 'rab.oof', default: true }
 				]
 				done()
 
@@ -86,7 +89,8 @@ describe "UserGetter", ->
 			@UserGetter.getUserFullEmails @fakeUser._id, (error, fullEmails) =>
 				assert.deepEqual fullEmails, [
 					{
-						email: 'email1@foo.bar'
+						email: 'email1@foo.bar',
+						reversedHostname: 'rab.oof'
 						default: false
 						affiliation:
 							institution: affiliationsData[0].institution
@@ -94,7 +98,7 @@ describe "UserGetter", ->
 							department: affiliationsData[0].department
 							role: affiliationsData[0].role
 					}
-					{ email: 'email2@foo.bar', default: true }
+					{ email: 'email2@foo.bar', reversedHostname: 'rab.oof', default: true }
 				]
 				done()
 
@@ -161,6 +165,18 @@ describe "UserGetter", ->
 				@findOne.calledWith(email: email, projection).should.equal true
 				done()
 
+	describe "getUsersByHostname", ->
+		it "should find user by hostname", (done)->
+			hostname = "bar.foo"
+			expectedQuery =
+				emails: {$exists: true },
+				'emails.reversedHostname': hostname.split('').reverse().join('')
+			projection = emails: 1
+			@UserGetter.getUsersByHostname hostname, projection, (error, users) =>
+				@find.calledOnce.should.equal true
+				@find.calledWith(expectedQuery, projection).should.equal true
+				done()
+
 	describe 'ensureUniqueEmailAddress', ->
 		beforeEach ->
 			@UserGetter.getUserByAnyEmail = sinon.stub()

+ 2 - 1
services/web/test/unit/coffee/User/UserUpdaterTests.coffee

@@ -90,9 +90,10 @@ describe "UserUpdater", ->
 			@UserUpdater.addEmailAddress @stubbedUser._id, @newEmail, (err)=>
 				@UserGetter.ensureUniqueEmailAddress.called.should.equal true
 				should.not.exist(err)
+				reversedHostname = @newEmail.split('@')[1].split('').reverse().join('')
 				@UserUpdater.updateUser.calledWith(
 					@stubbedUser._id,
-					$push: { emails: { email: @newEmail, createdAt: sinon.match.date } }
+					$push: { emails: { email: @newEmail, createdAt: sinon.match.date, reversedHostname: reversedHostname } }
 				).should.equal true
 				done()