mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
[web] SSO linking for existing group members (#15471)
* [web] SSO linking for existing group users GitOrigin-RevId: 22a5a5a28a213860f88ae0284c1ef51a31bb268f
This commit is contained in:
parent
1f56441184
commit
e22e8ff7a8
2 changed files with 110 additions and 1 deletions
100
services/web/app/src/Features/Subscription/GroupSSOHandler.js
Normal file
100
services/web/app/src/Features/Subscription/GroupSSOHandler.js
Normal file
|
@ -0,0 +1,100 @@
|
|||
const { SSOConfig } = require('../../models/SSOConfig')
|
||||
const UserAuditLogHandler = require('../User/UserAuditLogHandler')
|
||||
const UserUpdater = require('../User/UserUpdater')
|
||||
const SAMLIdentityManager = require('../User/SAMLIdentityManager')
|
||||
const { User } = require('../../models/User')
|
||||
const Errors = require('../Errors/Errors')
|
||||
|
||||
async function canEnrollInSubscription(userId, subscription) {
|
||||
const ssoEnabled = await isSSOEnabled(subscription)
|
||||
if (!ssoEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
const userIsMember = subscription.member_ids.some(
|
||||
memberId => memberId.toString() === userId.toString()
|
||||
)
|
||||
if (!userIsMember) {
|
||||
return false
|
||||
}
|
||||
|
||||
const user = await User.findOne(
|
||||
{ _id: userId },
|
||||
{ projection: { enrollment: 1 } }
|
||||
).exec()
|
||||
|
||||
const userIsEnrolled = user.enrollment?.sso?.some(
|
||||
enrollment => enrollment.groupId.toString() === subscription._id.toString()
|
||||
)
|
||||
if (userIsEnrolled) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
async function enrollInSubscription(
|
||||
userId,
|
||||
subscription,
|
||||
externalUserId,
|
||||
userIdAttribute,
|
||||
auditLog
|
||||
) {
|
||||
const canEnroll = await canEnrollInSubscription(userId, subscription)
|
||||
if (!canEnroll) {
|
||||
throw new Errors.SubscriptionNotFoundError(
|
||||
'cannot enroll user in SSO subscription',
|
||||
{
|
||||
info: { userId, subscription },
|
||||
}
|
||||
)
|
||||
}
|
||||
const providerId = `ol-group-subscription-id:${subscription._id.toString()}`
|
||||
|
||||
const userBySamlIdentifier = await SAMLIdentityManager.getUser(
|
||||
providerId,
|
||||
externalUserId,
|
||||
userIdAttribute
|
||||
)
|
||||
|
||||
if (userBySamlIdentifier) {
|
||||
throw new Errors.SAMLIdentityExistsError()
|
||||
}
|
||||
|
||||
const samlIdentifiers = {
|
||||
externalUserId,
|
||||
userIdAttribute,
|
||||
providerId,
|
||||
}
|
||||
|
||||
await UserUpdater.promises.updateUser(userId, {
|
||||
$push: {
|
||||
samlIdentifiers,
|
||||
'enrollment.sso': {
|
||||
groupId: subscription._id,
|
||||
linkedAt: new Date(),
|
||||
primary: true,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await UserAuditLogHandler.promises.addEntry(
|
||||
userId,
|
||||
'group-sso-link',
|
||||
auditLog.initiatorId,
|
||||
auditLog.ipAddress,
|
||||
samlIdentifiers
|
||||
)
|
||||
}
|
||||
|
||||
async function isSSOEnabled(subscription) {
|
||||
const ssoConfig = await SSOConfig.findById(subscription.ssoConfig).exec()
|
||||
return ssoConfig?.enabled
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
promises: {
|
||||
canEnrollInSubscription,
|
||||
enrollInSubscription,
|
||||
isSSOEnabled,
|
||||
},
|
||||
}
|
|
@ -32,7 +32,16 @@ const UserSchema = new Schema(
|
|||
institution: { type: String, default: '' },
|
||||
hashedPassword: String,
|
||||
enrollment: {
|
||||
// sso: { type: Boolean, default: false },
|
||||
sso: [
|
||||
{
|
||||
groupId: {
|
||||
type: ObjectId,
|
||||
ref: 'Subscription',
|
||||
},
|
||||
linkedAt: Date,
|
||||
primary: { type: Boolean, default: false },
|
||||
},
|
||||
],
|
||||
managedBy: {
|
||||
type: ObjectId,
|
||||
ref: 'Subscription',
|
||||
|
|
Loading…
Reference in a new issue