mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge pull request #3492 from overleaf/jel-calc-notification-period
Flag emails in affiliation notification period GitOrigin-RevId: d2d4868ba3a49e69b85a3bdca16d12276ac5c006
This commit is contained in:
parent
37f083809a
commit
b44aed56dd
6 changed files with 804 additions and 41 deletions
|
@ -2,6 +2,8 @@ const { callbackify } = require('util')
|
|||
const { db } = require('../../infrastructure/mongodb')
|
||||
const metrics = require('@overleaf/metrics')
|
||||
const logger = require('logger-sharelatex')
|
||||
const moment = require('moment')
|
||||
const settings = require('settings-sharelatex')
|
||||
const { promisifyAll } = require('../../util/promises')
|
||||
const {
|
||||
promises: InstitutionsAPIPromises
|
||||
|
@ -11,6 +13,35 @@ const Errors = require('../Errors/Errors')
|
|||
const Features = require('../../infrastructure/Features')
|
||||
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) {
|
||||
const user = await UserGetter.promises.getUser(userId, {
|
||||
email: 1,
|
||||
|
@ -155,8 +186,8 @@ var decorateFullEmails = (
|
|||
emailsData,
|
||||
affiliationsData,
|
||||
samlIdentifiers
|
||||
) =>
|
||||
emailsData.map(function(emailData) {
|
||||
) => {
|
||||
emailsData.forEach(function(emailData) {
|
||||
emailData.default = emailData.email === defaultEmail
|
||||
|
||||
const affiliation = affiliationsData.find(
|
||||
|
@ -164,31 +195,33 @@ var decorateFullEmails = (
|
|||
)
|
||||
if (affiliation) {
|
||||
const { institution, inferred, role, department, licence } = affiliation
|
||||
const inReconfirmNotificationPeriod = _emailInReconfirmNotificationPeriod(
|
||||
emailData,
|
||||
institution
|
||||
)
|
||||
emailData.affiliation = {
|
||||
institution,
|
||||
inferred,
|
||||
inReconfirmNotificationPeriod,
|
||||
role,
|
||||
department,
|
||||
licence
|
||||
}
|
||||
} else {
|
||||
emailsData.affiliation = null
|
||||
}
|
||||
|
||||
if (emailData.samlProviderId) {
|
||||
emailData.samlIdentifier = samlIdentifiers.find(
|
||||
samlIdentifier => samlIdentifier.providerId === emailData.samlProviderId
|
||||
)
|
||||
} else {
|
||||
emailsData.samlIdentifier = null
|
||||
}
|
||||
|
||||
emailData.emailHasInstitutionLicence = InstitutionsHelper.emailHasLicence(
|
||||
emailData
|
||||
)
|
||||
|
||||
return emailData
|
||||
})
|
||||
|
||||
return emailsData
|
||||
}
|
||||
;[
|
||||
'getUser',
|
||||
'getUserEmail',
|
||||
|
|
|
@ -186,3 +186,5 @@ module.exports =
|
|||
# setting to true since many features are enabled/disabled after availability of this
|
||||
# property (check Features.js)
|
||||
overleaf: true
|
||||
|
||||
reconfirmNotificationDays: 14
|
||||
|
|
|
@ -516,6 +516,7 @@ describe('Subscriptions', function() {
|
|||
department: 'Math',
|
||||
role: 'Prof',
|
||||
inferred: false,
|
||||
inReconfirmNotificationPeriod: false,
|
||||
institution: {
|
||||
name: 'Stanford',
|
||||
confirmed: true
|
||||
|
|
|
@ -1,39 +1,14 @@
|
|||
const { expect } = require('chai')
|
||||
const async = require('async')
|
||||
const moment = require('moment')
|
||||
const Features = require('../../../app/src/infrastructure/Features')
|
||||
const User = require('./helpers/User')
|
||||
const UserHelper = require('./helpers/UserHelper')
|
||||
const UserUpdater = require('../../../app/src/Features/User/UserUpdater')
|
||||
const { db, ObjectId } = require('../../../app/src/infrastructure/mongodb')
|
||||
const MockV1Api = require('./helpers/MockV1Api')
|
||||
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() {
|
||||
beforeEach(function(done) {
|
||||
this.timeout(20000)
|
||||
|
@ -283,14 +258,14 @@ describe('UserEmails', function() {
|
|||
password: userHelper.getDefaultPassword()
|
||||
})
|
||||
// original confirmation
|
||||
await confirmEmail(userHelper, email)
|
||||
await userHelper.confirmEmail(userHelper.user._id, email)
|
||||
const user = (await UserHelper.getUser({ email })).user
|
||||
confirmedAtDate = user.emails[0].confirmedAt
|
||||
expect(user.emails[0].confirmedAt).to.exist
|
||||
expect(user.emails[0].reconfirmedAt).to.exist
|
||||
})
|
||||
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
|
||||
expect(user.emails[0].confirmedAt).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)
|
||||
})
|
||||
})
|
||||
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -4,7 +4,9 @@ const Settings = require('settings-sharelatex')
|
|||
const UserCreator = require('../../../../app/src/Features/User/UserCreator')
|
||||
const UserGetter = require('../../../../app/src/Features/User/UserGetter')
|
||||
const UserUpdater = require('../../../../app/src/Features/User/UserUpdater')
|
||||
const moment = require('moment')
|
||||
const request = require('request-promise-native')
|
||||
const { db } = require('../../../../app/src/infrastructure/mongodb')
|
||||
const { ObjectId } = require('mongodb')
|
||||
|
||||
let globalUserNum = 1
|
||||
|
@ -289,6 +291,76 @@ class 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
|
||||
|
|
|
@ -2,6 +2,7 @@ const { ObjectId } = require('mongodb')
|
|||
const should = require('chai').should()
|
||||
const SandboxedModule = require('sandboxed-module')
|
||||
const assert = require('assert')
|
||||
const moment = require('moment')
|
||||
const path = require('path')
|
||||
const sinon = require('sinon')
|
||||
const modulePath = path.join(
|
||||
|
@ -41,7 +42,6 @@ describe('UserGetter', function() {
|
|||
},
|
||||
ObjectId
|
||||
}
|
||||
const settings = { apis: { v1: { url: 'v1.url', user: '', pass: '' } } }
|
||||
this.getUserAffiliations = sinon.stub().resolves([])
|
||||
|
||||
this.UserGetter = SandboxedModule.require(modulePath, {
|
||||
|
@ -57,7 +57,9 @@ describe('UserGetter', function() {
|
|||
'@overleaf/metrics': {
|
||||
timeAsyncMethod: sinon.stub()
|
||||
},
|
||||
'settings-sharelatex': settings,
|
||||
'settings-sharelatex': (this.settings = {
|
||||
reconfirmNotificationDays: 14
|
||||
}),
|
||||
'../Institutions/InstitutionsAPI': {
|
||||
promises: {
|
||||
getUserAffiliations: this.getUserAffiliations
|
||||
|
@ -192,7 +194,8 @@ describe('UserGetter', function() {
|
|||
inferred: affiliationsData[0].inferred,
|
||||
department: affiliationsData[0].department,
|
||||
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() {
|
||||
|
|
Loading…
Reference in a new issue