Merge pull request #3492 from overleaf/jel-calc-notification-period

Flag emails in affiliation notification period

GitOrigin-RevId: d2d4868ba3a49e69b85a3bdca16d12276ac5c006
This commit is contained in:
Jessica Lawshe 2021-01-19 09:51:48 -06:00 committed by Copybot
parent 37f083809a
commit b44aed56dd
6 changed files with 804 additions and 41 deletions

View file

@ -2,6 +2,8 @@ const { callbackify } = require('util')
const { db } = require('../../infrastructure/mongodb') const { db } = require('../../infrastructure/mongodb')
const metrics = require('@overleaf/metrics') const metrics = require('@overleaf/metrics')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
const moment = require('moment')
const settings = require('settings-sharelatex')
const { promisifyAll } = require('../../util/promises') const { promisifyAll } = require('../../util/promises')
const { const {
promises: InstitutionsAPIPromises promises: InstitutionsAPIPromises
@ -11,6 +13,35 @@ const Errors = require('../Errors/Errors')
const Features = require('../../infrastructure/Features') const Features = require('../../infrastructure/Features')
const { normalizeQuery, normalizeMultiQuery } = require('../Helpers/Mongo') const { normalizeQuery, normalizeMultiQuery } = require('../Helpers/Mongo')
function _emailInReconfirmNotificationPeriod(emailData, institutionData) {
const globalReconfirmPeriod = settings.reconfirmNotificationDays
if (!globalReconfirmPeriod) return false
// only show notification for institutions with reconfirmation enabled
if (!institutionData || !institutionData.maxConfirmationMonths) return false
if (!emailData.confirmedAt) return false
if (institutionData.ssoEnabled && !emailData.samlProviderId) {
// For SSO, only show notification for linked email
return false
}
// reconfirmedAt will not always be set, use confirmedAt as fallback
const lastConfirmed = emailData.reconfirmedAt || emailData.confirmedAt
const lastDayToReconfirm = moment(lastConfirmed).add(
institutionData.maxConfirmationMonths,
'months'
)
const notificationStarts = moment(lastDayToReconfirm).subtract(
globalReconfirmPeriod,
'days'
)
return moment().isAfter(notificationStarts)
}
async function getUserFullEmails(userId) { async function getUserFullEmails(userId) {
const user = await UserGetter.promises.getUser(userId, { const user = await UserGetter.promises.getUser(userId, {
email: 1, email: 1,
@ -155,8 +186,8 @@ var decorateFullEmails = (
emailsData, emailsData,
affiliationsData, affiliationsData,
samlIdentifiers samlIdentifiers
) => ) => {
emailsData.map(function(emailData) { emailsData.forEach(function(emailData) {
emailData.default = emailData.email === defaultEmail emailData.default = emailData.email === defaultEmail
const affiliation = affiliationsData.find( const affiliation = affiliationsData.find(
@ -164,31 +195,33 @@ var decorateFullEmails = (
) )
if (affiliation) { if (affiliation) {
const { institution, inferred, role, department, licence } = affiliation const { institution, inferred, role, department, licence } = affiliation
const inReconfirmNotificationPeriod = _emailInReconfirmNotificationPeriod(
emailData,
institution
)
emailData.affiliation = { emailData.affiliation = {
institution, institution,
inferred, inferred,
inReconfirmNotificationPeriod,
role, role,
department, department,
licence licence
} }
} else {
emailsData.affiliation = null
} }
if (emailData.samlProviderId) { if (emailData.samlProviderId) {
emailData.samlIdentifier = samlIdentifiers.find( emailData.samlIdentifier = samlIdentifiers.find(
samlIdentifier => samlIdentifier.providerId === emailData.samlProviderId samlIdentifier => samlIdentifier.providerId === emailData.samlProviderId
) )
} else {
emailsData.samlIdentifier = null
} }
emailData.emailHasInstitutionLicence = InstitutionsHelper.emailHasLicence( emailData.emailHasInstitutionLicence = InstitutionsHelper.emailHasLicence(
emailData emailData
) )
return emailData
}) })
return emailsData
}
;[ ;[
'getUser', 'getUser',
'getUserEmail', 'getUserEmail',

View file

@ -186,3 +186,5 @@ module.exports =
# setting to true since many features are enabled/disabled after availability of this # setting to true since many features are enabled/disabled after availability of this
# property (check Features.js) # property (check Features.js)
overleaf: true overleaf: true
reconfirmNotificationDays: 14

View file

@ -516,6 +516,7 @@ describe('Subscriptions', function() {
department: 'Math', department: 'Math',
role: 'Prof', role: 'Prof',
inferred: false, inferred: false,
inReconfirmNotificationPeriod: false,
institution: { institution: {
name: 'Stanford', name: 'Stanford',
confirmed: true confirmed: true

View file

@ -1,39 +1,14 @@
const { expect } = require('chai') const { expect } = require('chai')
const async = require('async') const async = require('async')
const moment = require('moment')
const Features = require('../../../app/src/infrastructure/Features')
const User = require('./helpers/User') const User = require('./helpers/User')
const UserHelper = require('./helpers/UserHelper') const UserHelper = require('./helpers/UserHelper')
const UserUpdater = require('../../../app/src/Features/User/UserUpdater')
const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb') const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
const MockV1Api = require('./helpers/MockV1Api') const MockV1Api = require('./helpers/MockV1Api')
const expectErrorResponse = require('./helpers/expectErrorResponse') const expectErrorResponse = require('./helpers/expectErrorResponse')
async function confirmEmail(userHelper, email) {
let response
// UserHelper.createUser does not create a confirmation token
response = await userHelper.request.post({
form: {
email
},
simple: false,
uri: '/user/emails/resend_confirmation'
})
expect(response.statusCode).to.equal(200)
const tokenData = await db.tokens
.find({
use: 'email_confirmation',
'data.user_id': userHelper.user._id.toString(),
usedAt: { $exists: false }
})
.next()
response = await userHelper.request.post({
form: {
token: tokenData.token
},
simple: false,
uri: '/user/emails/confirm'
})
expect(response.statusCode).to.equal(200)
}
describe('UserEmails', function() { describe('UserEmails', function() {
beforeEach(function(done) { beforeEach(function(done) {
this.timeout(20000) this.timeout(20000)
@ -283,14 +258,14 @@ describe('UserEmails', function() {
password: userHelper.getDefaultPassword() password: userHelper.getDefaultPassword()
}) })
// original confirmation // original confirmation
await confirmEmail(userHelper, email) await userHelper.confirmEmail(userHelper.user._id, email)
const user = (await UserHelper.getUser({ email })).user const user = (await UserHelper.getUser({ email })).user
confirmedAtDate = user.emails[0].confirmedAt confirmedAtDate = user.emails[0].confirmedAt
expect(user.emails[0].confirmedAt).to.exist expect(user.emails[0].confirmedAt).to.exist
expect(user.emails[0].reconfirmedAt).to.exist expect(user.emails[0].reconfirmedAt).to.exist
}) })
it('should set reconfirmedAt and not reset confirmedAt', async function() { it('should set reconfirmedAt and not reset confirmedAt', async function() {
await confirmEmail(userHelper, email) await userHelper.confirmEmail(userHelper.user._id, email)
const user = (await UserHelper.getUser({ email })).user const user = (await UserHelper.getUser({ email })).user
expect(user.emails[0].confirmedAt).to.exist expect(user.emails[0].confirmedAt).to.exist
expect(user.emails[0].reconfirmedAt).to.exist expect(user.emails[0].reconfirmedAt).to.exist
@ -1019,4 +994,159 @@ describe('UserEmails', function() {
expect(user.auditLog[0].ip).to.equal(this.user.request.ip) expect(user.auditLog[0].ip).to.equal(this.user.request.ip)
}) })
}) })
describe('notification period', function() {
let defaultEmail, userHelper, email1, email2, email3
const maxConfirmationMonths = 12
beforeEach(async function() {
if (!Features.hasFeature('affiliations')) {
this.skip()
}
userHelper = new UserHelper()
defaultEmail = userHelper.getDefaultEmail()
userHelper = await UserHelper.createUser({ email: defaultEmail })
userHelper = await UserHelper.loginUser({
email: defaultEmail,
password: userHelper.getDefaultPassword()
})
const institutionId = MockV1Api.createInstitution({
ssoEnabled: false,
maxConfirmationMonths
})
const domain = 'example-affiliation.com'
MockV1Api.addInstitutionDomain(institutionId, domain)
email1 = `leonard@${domain}`
email2 = `mccoy@${domain}`
email3 = `bones@${domain}`
})
describe('non SSO affiliations', function() {
beforeEach(async function() {
// create a user with 3 affiliations at the institution.
// all are within in the notification period
const backdatedDays = maxConfirmationMonths * 2 * 30
const userId = userHelper.user._id
await userHelper.addEmailAndConfirm(userId, email1)
await userHelper.addEmailAndConfirm(userId, email2)
await userHelper.addEmailAndConfirm(userId, email3)
await userHelper.backdateConfirmation(userId, email1, backdatedDays)
await userHelper.backdateConfirmation(userId, email2, backdatedDays)
await userHelper.backdateConfirmation(userId, email3, backdatedDays)
})
it('should flag inReconfirmNotificationPeriod for all affiliations in period', async function() {
const response = await userHelper.request.get('/user/emails')
expect(response.statusCode).to.equal(200)
const fullEmails = JSON.parse(response.body)
expect(fullEmails.length).to.equal(4)
expect(fullEmails[0].affiliation).to.not.exist
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[3].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
})
describe('should flag emails before their confirmation expires, but within the notification period', function() {
beforeEach(async function() {
const dateInPeriodButNotExpired = moment()
.subtract(maxConfirmationMonths, 'months')
.add(14, 'days')
.toDate()
const backdatedDays = moment().diff(dateInPeriodButNotExpired, 'days')
await userHelper.backdateConfirmation(
userHelper.user._id,
email1,
backdatedDays
)
await userHelper.backdateConfirmation(
userHelper.user._id,
email2,
backdatedDays
)
await userHelper.backdateConfirmation(
userHelper.user._id,
email3,
backdatedDays
)
})
it('should flag the emails', async function() {
const response = await userHelper.request.get('/user/emails')
expect(response.statusCode).to.equal(200)
const fullEmails = JSON.parse(response.body)
expect(fullEmails.length).to.equal(4)
expect(fullEmails[0].affiliation).to.not.exist
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[3].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
// ensure dates are not past reconfirmation period
function _getLastDayToReconfirm(date) {
return moment(date).add(maxConfirmationMonths, 'months')
}
expect(
moment(fullEmails[1].reconfirmedAt).isAfter(
_getLastDayToReconfirm(fullEmails[1].reconfirmedAt)
)
).to.equal(false)
expect(
moment(fullEmails[2].reconfirmedAt).isAfter(
_getLastDayToReconfirm(fullEmails[2].reconfirmedAt)
)
).to.equal(false)
expect(
moment(fullEmails[3].reconfirmedAt).isAfter(
_getLastDayToReconfirm(fullEmails[3].reconfirmedAt)
)
).to.equal(false)
})
})
describe('missing reconfirmedAt', function() {
beforeEach(async function() {
const userId = userHelper.user._id
const query = {
_id: userId,
'emails.email': email2
}
const update = {
$unset: { 'emails.$.reconfirmedAt': true }
}
await UserUpdater.promises.updateUser(query, update)
})
it('should fallback to confirmedAt for date check', async function() {
const response = await userHelper.request.get('/user/emails')
expect(response.statusCode).to.equal(200)
const fullEmails = JSON.parse(response.body)
expect(fullEmails.length).to.equal(4)
expect(fullEmails[0].affiliation).to.not.exist
expect(fullEmails[2].reconfirmedAt).to.not.exist
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[3].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
})
})
})
})
}) })

View file

@ -4,7 +4,9 @@ const Settings = require('settings-sharelatex')
const UserCreator = require('../../../../app/src/Features/User/UserCreator') const UserCreator = require('../../../../app/src/Features/User/UserCreator')
const UserGetter = require('../../../../app/src/Features/User/UserGetter') const UserGetter = require('../../../../app/src/Features/User/UserGetter')
const UserUpdater = require('../../../../app/src/Features/User/UserUpdater') const UserUpdater = require('../../../../app/src/Features/User/UserUpdater')
const moment = require('moment')
const request = require('request-promise-native') const request = require('request-promise-native')
const { db } = require('../../../../app/src/infrastructure/mongodb')
const { ObjectId } = require('mongodb') const { ObjectId } = require('mongodb')
let globalUserNum = 1 let globalUserNum = 1
@ -289,6 +291,76 @@ class UserHelper {
return userHelper return userHelper
} }
async addEmailAndConfirm(userId, email) {
let response = await this.request.post({
form: {
email
},
simple: false,
uri: '/user/emails'
})
expect(response.statusCode).to.equal(204)
const token = (
await db.tokens.findOne({
'data.user_id': userId.toString(),
'data.email': email
})
).token
response = await this.request.post({
form: {
token
},
simple: false,
uri: '/user/emails/confirm'
})
expect(response.statusCode).to.equal(200)
}
async backdateConfirmation(userId, email, days) {
const confirmedDate = moment()
.subtract(days, 'days')
.toDate()
const query = {
_id: userId,
'emails.email': email
}
const update = {
$set: {
'emails.$.confirmedAt': confirmedDate,
'emails.$.reconfirmedAt': confirmedDate
}
}
await UserUpdater.promises.updateUser(query, update)
}
async confirmEmail(userId, email) {
let response
// UserHelper.createUser does not create a confirmation token
response = await this.request.post({
form: {
email
},
simple: false,
uri: '/user/emails/resend_confirmation'
})
expect(response.statusCode).to.equal(200)
const tokenData = await db.tokens
.find({
use: 'email_confirmation',
'data.user_id': userId.toString(),
usedAt: { $exists: false }
})
.next()
response = await this.request.post({
form: {
token: tokenData.token
},
simple: false,
uri: '/user/emails/confirm'
})
expect(response.statusCode).to.equal(200)
}
} }
module.exports = UserHelper module.exports = UserHelper

View file

@ -2,6 +2,7 @@ const { ObjectId } = require('mongodb')
const should = require('chai').should() const should = require('chai').should()
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const assert = require('assert') const assert = require('assert')
const moment = require('moment')
const path = require('path') const path = require('path')
const sinon = require('sinon') const sinon = require('sinon')
const modulePath = path.join( const modulePath = path.join(
@ -41,7 +42,6 @@ describe('UserGetter', function() {
}, },
ObjectId ObjectId
} }
const settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
this.getUserAffiliations = sinon.stub().resolves([]) this.getUserAffiliations = sinon.stub().resolves([])
this.UserGetter = SandboxedModule.require(modulePath, { this.UserGetter = SandboxedModule.require(modulePath, {
@ -57,7 +57,9 @@ describe('UserGetter', function() {
'@overleaf/metrics': { '@overleaf/metrics': {
timeAsyncMethod: sinon.stub() timeAsyncMethod: sinon.stub()
}, },
'settings-sharelatex': settings, 'settings-sharelatex': (this.settings = {
reconfirmNotificationDays: 14
}),
'../Institutions/InstitutionsAPI': { '../Institutions/InstitutionsAPI': {
promises: { promises: {
getUserAffiliations: this.getUserAffiliations getUserAffiliations: this.getUserAffiliations
@ -192,7 +194,8 @@ describe('UserGetter', function() {
inferred: affiliationsData[0].inferred, inferred: affiliationsData[0].inferred,
department: affiliationsData[0].department, department: affiliationsData[0].department,
role: affiliationsData[0].role, role: affiliationsData[0].role,
licence: affiliationsData[0].licence licence: affiliationsData[0].licence,
inReconfirmNotificationPeriod: false
} }
}, },
{ {
@ -262,6 +265,528 @@ describe('UserGetter', function() {
} }
) )
}) })
describe('affiliation reconfirmation', function() {
const institutionNonSSO = {
id: 1,
name: 'University Name',
commonsAccount: true,
isUniversity: true,
confirmed: true,
ssoBeta: false,
ssoEnabled: false,
maxConfirmationMonths: 12
}
const institutionSSO = {
id: 2,
name: 'SSO University Name',
isUniversity: true,
confirmed: true,
ssoBeta: false,
ssoEnabled: true,
maxConfirmationMonths: 12
}
describe('non-SSO institutions', function() {
const email1 = 'leonard@example-affiliation.com'
const email2 = 'mccoy@example-affiliation.com'
const affiliationsData = [
{
email: email1,
role: 'Prof',
department: 'Medicine',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
},
{
email: email2,
role: 'Prof',
department: 'Medicine',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
}
]
it('should flag inReconfirmNotificationPeriod for all affiliations in period', function(done) {
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
reversedHostname: 'moc.noitailiffa-elpmaxe',
confirmedAt: moment()
.subtract(
institutionNonSSO.maxConfirmationMonths + 2,
'months'
)
.toDate(),
default: true
},
{
email: email2,
reversedHostname: 'moc.noitailiffa-elpmaxe',
confirmedAt: moment()
.subtract(
institutionNonSSO.maxConfirmationMonths + 1,
'months'
)
.toDate()
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
done()
}
)
})
it('should not flag affiliations outside of notification period', function(done) {
const aboutToBeWithinPeriod = moment()
.subtract(institutionNonSSO.maxConfirmationMonths, 'months')
.add(15, 'days')
.toDate() // expires in 15 days
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
reversedHostname: 'moc.noitailiffa-elpmaxe',
confirmedAt: new Date(),
default: true
},
{
email: email2,
reversedHostname: 'moc.noitailiffa-elpmaxe',
confirmedAt: aboutToBeWithinPeriod
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
done()
}
)
})
})
describe('SSO institutions', function() {
it('should flag only linked email, if in notification period', function(done) {
const email1 = 'email1@sso.bar'
const email2 = 'email2@sso.bar'
const email3 = 'email3@sso.bar'
const affiliationsData = [
{
email: email1,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionSSO
},
{
email: email2,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionSSO
},
{
email: email3,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionSSO
}
]
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
reversedHostname: 'rab.oss',
confirmedAt: new Date('2019-09-24'),
reconfirmedAt: new Date('2019-09-24'),
default: true
},
{
email: email2,
reversedHostname: 'rab.oss',
confirmedAt: new Date('2019-09-24'),
reconfirmedAt: new Date('2019-09-24'),
samlProviderId: institutionSSO.id
},
{
email: email3,
reversedHostname: 'rab.oss',
confirmedAt: new Date('2019-09-24'),
reconfirmedAt: new Date('2019-09-24')
}
],
samlIdentifiers: [
{
providerId: institutionSSO.id,
externalUserId: 'abc123'
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
done()
}
)
})
})
describe('multiple institution affiliations', function() {
it('should flag each institution', function(done) {
const email1 = 'email1@sso.bar'
const email2 = 'email2@sso.bar'
const email3 = 'email3@foo.bar'
const email4 = 'email4@foo.bar'
const affiliationsData = [
{
email: email1,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionSSO
},
{
email: email2,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionSSO
},
{
email: email3,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
},
{
email: email4,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
}
]
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
reversedHostname: 'rab.oss',
confirmedAt: '2019-09-24T20:25:08.503Z',
default: true
},
{
email: email2,
reversedHostname: 'rab.oss',
confirmedAt: new Date('2019-09-24T20:25:08.503Z'),
samlProviderId: institutionSSO.id
},
{
email: email3,
reversedHostname: 'rab.oof',
confirmedAt: new Date('2019-10-24T20:25:08.503Z')
},
{
email: email4,
reversedHostname: 'rab.oof',
confirmedAt: new Date('2019-09-24T20:25:08.503Z')
}
],
samlIdentifiers: [
{
providerId: institutionSSO.id,
externalUserId: 'abc123'
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.to.equal(false)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[3].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
done()
}
)
})
})
describe('reconfirmedAt', function() {
it('only use confirmedAt when no reconfirmedAt', function(done) {
const email1 = 'email1@foo.bar'
const email2 = 'email2@foo.bar'
const email3 = 'email3@foo.bar'
const affiliationsData = [
{
email: email1,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
},
{
email: email2,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
},
{
email: email3,
role: 'Prof',
department: 'Maths',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
}
]
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
reversedHostname: 'rab.oof',
confirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 2,
'months'
),
reconfirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 3,
'months'
),
default: true
},
{
email: email2,
reversedHostname: 'rab.oof',
confirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 3,
'months'
),
reconfirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 2,
'months'
)
},
{
email: email3,
reversedHostname: 'rab.oof',
confirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 4,
'months'
),
reconfirmedAt: moment().subtract(
institutionSSO.maxConfirmationMonths * 4,
'months'
)
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
done()
}
)
})
})
describe('before reconfirmation period expires and within reconfirmation notification period', function() {
const email = 'leonard@example-affiliation.com'
it('should flag the email', function(done) {
const confirmedAt = moment()
.subtract(institutionNonSSO.maxConfirmationMonths, 'months')
.subtract(14, 'days')
.toDate() // expires in 14 days
const affiliationsData = [
{
email,
role: 'Prof',
department: 'Medicine',
inferred: false,
licence: 'pro_plus',
institution: institutionNonSSO
}
]
const user = {
_id: '12390i',
email,
emails: [
{
email,
confirmedAt,
default: true
}
]
}
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.equal(true)
done()
}
)
})
})
describe('when no Settings.reconfirmNotificationDays', function() {
it('should always return inReconfirmNotificationPeriod:false', function(done) {
const email1 = 'email1@sso.bar'
const email2 = 'email2@foo.bar'
const email3 = 'email3@foo.bar'
const confirmedAtAboutToExpire = moment()
.subtract(institutionNonSSO.maxConfirmationMonths, 'months')
.subtract(14, 'days')
.toDate() // expires in 14 days
const affiliationsData = [
{
email: email1,
institution: institutionSSO
},
{
email: email2,
institution: institutionNonSSO
},
{
email: email3,
institution: institutionNonSSO
}
]
const user = {
_id: '12390i',
email: email1,
emails: [
{
email: email1,
confirmedAt: confirmedAtAboutToExpire,
default: true,
samlProviderId: institutionSSO.id
},
{
email: email2,
confirmedAt: new Date('2019-09-24T20:25:08.503Z')
},
{
email: email3,
confirmedAt: new Date('2019-10-24T20:25:08.503Z')
}
],
samlIdentifiers: [
{
providerId: institutionSSO.id,
externalUserId: 'abc123'
}
]
}
this.settings.reconfirmNotificationDays = undefined
this.getUserAffiliations.resolves(affiliationsData)
this.UserGetter.promises.getUser = sinon.stub().resolves(user)
this.UserGetter.getUserFullEmails(
this.fakeUser._id,
(error, fullEmails) => {
expect(error).to.not.exist
expect(
fullEmails[0].affiliation.inReconfirmNotificationPeriod
).to.to.equal(false)
expect(
fullEmails[1].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
expect(
fullEmails[2].affiliation.inReconfirmNotificationPeriod
).to.equal(false)
done()
}
)
})
})
})
}) })
describe('getUserbyMainEmail', function() { describe('getUserbyMainEmail', function() {