Просмотр исходного кода

[web] SSO linking for existing group members (#15471)

* [web] SSO linking for existing group users

GitOrigin-RevId: 22a5a5a28a213860f88ae0284c1ef51a31bb268f
Miguel Serrano 2 лет назад
Родитель
Сommit
e22e8ff7a8

+ 100 - 0
services/web/app/src/Features/Subscription/GroupSSOHandler.js

@@ -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,
+  },
+}

+ 10 - 1
services/web/app/src/models/User.js

@@ -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',