mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-06-06 23:59:01 +02:00
Merge pull request #2114 from overleaf/jel-saml-account-linking
Login and link SAML account GitOrigin-RevId: 97daf9b6028ece3b3a19715873439f5fea7ecebb
This commit is contained in:
committed by
sharelatex
parent
837599d89c
commit
4b894c152f
@@ -54,6 +54,16 @@ class NotInV2Error extends BackwardCompatibleError {}
|
||||
|
||||
class SLInV2Error extends BackwardCompatibleError {}
|
||||
|
||||
class SAMLIdentityExistsError extends BackwardCompatibleError {
|
||||
constructor(arg) {
|
||||
super(arg)
|
||||
if (!this.message) {
|
||||
this.message =
|
||||
'provider and external id already linked to another account'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SAMLUserNotFoundError extends BackwardCompatibleError {
|
||||
constructor(arg) {
|
||||
super(arg)
|
||||
@@ -108,6 +118,7 @@ module.exports = {
|
||||
EmailExistsError,
|
||||
InvalidError,
|
||||
NotInV2Error,
|
||||
SAMLIdentityExistsError,
|
||||
SAMLUserNotFoundError,
|
||||
SLInV2Error,
|
||||
ThirdPartyIdentityExistsError,
|
||||
|
||||
@@ -1,5 +1,99 @@
|
||||
const Errors = require('../Errors/Errors')
|
||||
const logger = require('logger-sharelatex')
|
||||
const OError = require('@overleaf/o-error')
|
||||
const { User } = require('../../models/User')
|
||||
const UserGetter = require('../User/UserGetter')
|
||||
const UserUpdater = require('../User/UserUpdater')
|
||||
|
||||
function _addIdentifier(userId, externalUserId, providerId) {
|
||||
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
|
||||
}
|
||||
|
||||
async function _addInstitutionEmail(userId, email) {
|
||||
const user = await UserGetter.promises.getUser(userId)
|
||||
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) {
|
||||
// nothing to do, email is already added and confirmed
|
||||
} else if (emailAlreadyAssociated) {
|
||||
// add and confirm email
|
||||
await _confirmEmail(user._id, email)
|
||||
} else {
|
||||
// add and confirm email
|
||||
await _addEmail(user._id, email)
|
||||
await _confirmEmail(user._id, email)
|
||||
}
|
||||
}
|
||||
|
||||
function _addEmail(userId, institutionEmail) {
|
||||
return new Promise((resolve, reject) => {
|
||||
UserUpdater.addEmailAddress(userId, institutionEmail, function(
|
||||
error,
|
||||
addEmailResult
|
||||
) {
|
||||
if (error) {
|
||||
logger.log(
|
||||
error,
|
||||
userId,
|
||||
'could not add institution email after SAML linking'
|
||||
)
|
||||
reject(error)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function _confirmEmail(userId, institutionEmail) {
|
||||
return new Promise((resolve, reject) => {
|
||||
UserUpdater.confirmEmail(userId, institutionEmail, function(
|
||||
error,
|
||||
confirmedResult
|
||||
) {
|
||||
if (error) {
|
||||
logger.log(
|
||||
error,
|
||||
userId,
|
||||
'could not confirm institution email after SAML linking'
|
||||
)
|
||||
reject(error)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function getUser(providerId, externalUserId) {
|
||||
if (providerId == null || externalUserId == null) {
|
||||
@@ -17,6 +111,16 @@ async function getUser(providerId, externalUserId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function linkAccounts(
|
||||
userId,
|
||||
externalUserId,
|
||||
institutionEmail,
|
||||
providerId
|
||||
) {
|
||||
await _addIdentifier(userId, externalUserId, providerId)
|
||||
await _addInstitutionEmail(userId, institutionEmail)
|
||||
}
|
||||
|
||||
const SAMLIdentityManager = {
|
||||
_getUserQuery(providerId, externalUserId) {
|
||||
externalUserId = externalUserId.toString()
|
||||
@@ -27,7 +131,8 @@ const SAMLIdentityManager = {
|
||||
}
|
||||
return query
|
||||
},
|
||||
getUser
|
||||
getUser,
|
||||
linkAccounts
|
||||
}
|
||||
|
||||
module.exports = SAMLIdentityManager
|
||||
|
||||
@@ -121,6 +121,7 @@ const UserSchema = new Schema({
|
||||
refreshToken: { type: String }
|
||||
},
|
||||
awareOfV2: { type: Boolean, default: false },
|
||||
samlIdentifiers: { type: Array, default: [] },
|
||||
thirdPartyIdentifiers: { type: Array, default: [] },
|
||||
migratedAt: { type: Date }
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user