2019-10-03 10:10:22 -04:00
|
|
|
const EmailHandler = require('../Email/EmailHandler')
|
2019-08-27 16:48:00 -04:00
|
|
|
const Errors = require('../Errors/Errors')
|
2019-09-12 10:01:12 -04:00
|
|
|
const logger = require('logger-sharelatex')
|
|
|
|
const OError = require('@overleaf/o-error')
|
2019-08-27 16:48:00 -04:00
|
|
|
const { User } = require('../../models/User')
|
2019-09-12 10:01:12 -04:00
|
|
|
const UserGetter = require('../User/UserGetter')
|
|
|
|
const UserUpdater = require('../User/UserUpdater')
|
|
|
|
|
|
|
|
function _addIdentifier(userId, externalUserId, providerId) {
|
2019-09-30 09:21:31 -04:00
|
|
|
providerId = providerId.toString()
|
2019-09-12 10:01:12 -04:00
|
|
|
const query = {
|
|
|
|
_id: userId,
|
|
|
|
'samlIdentifiers.providerId': {
|
|
|
|
$ne: providerId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const update = {
|
|
|
|
$push: {
|
|
|
|
samlIdentifiers: {
|
|
|
|
externalUserId,
|
|
|
|
providerId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// First update user.samlIdentifiers
|
|
|
|
let updatedUser = User.findOneAndUpdate(query, update, { new: true }).exec()
|
|
|
|
try {
|
|
|
|
updatedUser = User.findOneAndUpdate(query, update, { new: true }).exec()
|
|
|
|
} catch (err) {
|
|
|
|
if (err && err.code === 11000) {
|
|
|
|
throw new Errors.SAMLIdentityExistsError()
|
|
|
|
} else if (err != null) {
|
|
|
|
logger.log(err, userId, 'failed to add institution SAML identifier')
|
|
|
|
throw new OError(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return updatedUser
|
|
|
|
}
|
|
|
|
|
2019-09-30 09:21:31 -04:00
|
|
|
function _getUserQuery(providerId, externalUserId) {
|
|
|
|
externalUserId = externalUserId.toString()
|
|
|
|
providerId = providerId.toString()
|
|
|
|
const query = {
|
|
|
|
'samlIdentifiers.externalUserId': externalUserId,
|
|
|
|
'samlIdentifiers.providerId': providerId
|
|
|
|
}
|
|
|
|
return query
|
|
|
|
}
|
|
|
|
|
|
|
|
async function _addInstitutionEmail(userId, email, providerId) {
|
2019-09-12 10:01:12 -04:00
|
|
|
const user = await UserGetter.promises.getUser(userId)
|
2019-09-30 09:21:31 -04:00
|
|
|
const query = {
|
|
|
|
_id: userId,
|
|
|
|
'emails.email': email
|
|
|
|
}
|
|
|
|
const update = {
|
|
|
|
$set: {
|
|
|
|
'emails.$.samlProviderId': providerId.toString()
|
|
|
|
}
|
|
|
|
}
|
2019-09-12 10:01:12 -04:00
|
|
|
if (user == null) {
|
|
|
|
logger.log(userId, 'could not find user for institution SAML linking')
|
|
|
|
throw new Errors.NotFoundError('user not found')
|
|
|
|
}
|
|
|
|
const emailAlreadyAssociated = user.emails.find(e => e.email === email)
|
|
|
|
if (emailAlreadyAssociated && emailAlreadyAssociated.confirmedAt) {
|
2019-09-30 09:21:31 -04:00
|
|
|
await UserUpdater.promises.updateUser(query, update)
|
2019-09-12 10:01:12 -04:00
|
|
|
} else if (emailAlreadyAssociated) {
|
|
|
|
// add and confirm email
|
2019-09-30 09:21:31 -04:00
|
|
|
await UserUpdater.promises.confirmEmail(user._id, email)
|
|
|
|
await UserUpdater.promises.updateUser(query, update)
|
2019-09-12 10:01:12 -04:00
|
|
|
} else {
|
|
|
|
// add and confirm email
|
2019-09-30 09:21:31 -04:00
|
|
|
await UserUpdater.promises.addEmailAddress(user._id, email)
|
|
|
|
await UserUpdater.promises.confirmEmail(user._id, email)
|
|
|
|
await UserUpdater.promises.updateUser(query, update)
|
2019-09-12 10:01:12 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-03 10:10:22 -04:00
|
|
|
async function _sendLinkedEmail(userId, providerName) {
|
|
|
|
const user = await UserGetter.promises.getUser(userId, { email: 1 })
|
|
|
|
const emailOptions = {
|
|
|
|
to: user.email,
|
|
|
|
provider: providerName
|
|
|
|
}
|
|
|
|
EmailHandler.sendEmail(
|
|
|
|
'emailThirdPartyIdentifierLinked',
|
|
|
|
emailOptions,
|
|
|
|
error => {
|
|
|
|
if (error != null) {
|
|
|
|
logger.warn(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
function _sendUnlinkedEmail(primaryEmail, providerName) {
|
|
|
|
const emailOptions = {
|
|
|
|
to: primaryEmail,
|
|
|
|
provider: providerName
|
|
|
|
}
|
|
|
|
EmailHandler.sendEmail(
|
|
|
|
'emailThirdPartyIdentifierUnlinked',
|
|
|
|
emailOptions,
|
|
|
|
error => {
|
|
|
|
if (error != null) {
|
|
|
|
logger.warn(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-08-27 16:48:00 -04:00
|
|
|
async function getUser(providerId, externalUserId) {
|
|
|
|
if (providerId == null || externalUserId == null) {
|
|
|
|
throw new Error('invalid arguments')
|
|
|
|
}
|
2019-10-03 10:10:22 -04:00
|
|
|
const query = _getUserQuery(providerId, externalUserId)
|
|
|
|
let user = await User.findOne(query).exec()
|
|
|
|
if (!user) {
|
|
|
|
throw new Errors.SAMLUserNotFoundError()
|
2019-08-27 16:48:00 -04:00
|
|
|
}
|
2019-10-03 10:10:22 -04:00
|
|
|
return user
|
2019-08-27 16:48:00 -04:00
|
|
|
}
|
|
|
|
|
2019-09-12 10:01:12 -04:00
|
|
|
async function linkAccounts(
|
|
|
|
userId,
|
|
|
|
externalUserId,
|
|
|
|
institutionEmail,
|
2019-10-03 10:10:22 -04:00
|
|
|
providerId,
|
|
|
|
providerName
|
2019-09-12 10:01:12 -04:00
|
|
|
) {
|
2019-10-03 10:10:22 -04:00
|
|
|
await _addIdentifier(userId, externalUserId, providerId)
|
|
|
|
await _addInstitutionEmail(userId, institutionEmail, providerId)
|
|
|
|
await _sendLinkedEmail(userId, providerName)
|
|
|
|
}
|
|
|
|
|
|
|
|
async function unlinkAccounts(userId, primaryEmail, providerId, providerName) {
|
|
|
|
const query = {
|
|
|
|
_id: userId
|
|
|
|
}
|
|
|
|
const update = {
|
|
|
|
$pull: {
|
|
|
|
samlIdentifiers: {
|
|
|
|
providerId
|
|
|
|
}
|
|
|
|
}
|
2019-09-30 09:21:31 -04:00
|
|
|
}
|
2019-10-03 10:10:22 -04:00
|
|
|
await User.update(query, update).exec()
|
|
|
|
_sendUnlinkedEmail(primaryEmail, providerName)
|
2019-09-12 10:01:12 -04:00
|
|
|
}
|
|
|
|
|
2019-08-27 16:48:00 -04:00
|
|
|
const SAMLIdentityManager = {
|
2019-09-12 10:01:12 -04:00
|
|
|
getUser,
|
2019-10-03 10:10:22 -04:00
|
|
|
linkAccounts,
|
|
|
|
unlinkAccounts
|
2019-08-27 16:48:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = SAMLIdentityManager
|