mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-27 19:11:56 +02:00
Merge pull request #29881 from overleaf/jel-domain-capture-email-invite-cta
[web] Update CTA for domain capture group invites GitOrigin-RevId: addaa81c443da63124b7841f087e34145fe3bfe6
This commit is contained in:
@@ -269,11 +269,25 @@ async function resendInvite(req, res, next) {
|
||||
return await createInvite(req, res)
|
||||
}
|
||||
|
||||
let acceptInviteUrl
|
||||
if (subscription.domainCaptureEnabled) {
|
||||
const samlInitPath = (
|
||||
await Modules.promises.hooks.fire(
|
||||
'getGroupSSOInitPath',
|
||||
subscription,
|
||||
userEmail
|
||||
)
|
||||
)?.[0]
|
||||
acceptInviteUrl = `${settings.siteUrl}${samlInitPath}`
|
||||
} else {
|
||||
acceptInviteUrl = `${settings.siteUrl}/subscription/invites/${currentInvite.token}/`
|
||||
}
|
||||
|
||||
const opts = {
|
||||
to: userEmail,
|
||||
admin: subscription.admin_id,
|
||||
inviter: currentInvite.inviterName,
|
||||
acceptInviteUrl: `${settings.siteUrl}/subscription/invites/${currentInvite.token}/`,
|
||||
acceptInviteUrl,
|
||||
reminder: true,
|
||||
}
|
||||
|
||||
|
||||
@@ -172,6 +172,7 @@ async function createTeamInvitesForLegacyInvitedEmail(email) {
|
||||
}
|
||||
|
||||
async function _createInvite(subscription, email, inviter, auditLog) {
|
||||
const { domainCaptureEnabled, managedUsersEnabled } = subscription
|
||||
const { possible, reason } = await _checkIfInviteIsPossible(
|
||||
subscription,
|
||||
email
|
||||
@@ -231,27 +232,42 @@ async function _createInvite(subscription, email, inviter, auditLog) {
|
||||
invite = invite.toObject()
|
||||
invite.sentAt = new Date()
|
||||
} else {
|
||||
invite = {
|
||||
email,
|
||||
inviterName,
|
||||
token: crypto.randomBytes(32).toString('hex'),
|
||||
sentAt: new Date(),
|
||||
if (domainCaptureEnabled) {
|
||||
invite = {
|
||||
email,
|
||||
inviterName,
|
||||
sentAt: new Date(),
|
||||
domainCapture: true,
|
||||
}
|
||||
} else {
|
||||
invite = {
|
||||
email,
|
||||
inviterName,
|
||||
token: crypto.randomBytes(32).toString('hex'),
|
||||
sentAt: new Date(),
|
||||
}
|
||||
}
|
||||
|
||||
subscription.teamInvites.push(invite)
|
||||
}
|
||||
|
||||
try {
|
||||
await _sendNotificationToExistingUser(
|
||||
subscription,
|
||||
email,
|
||||
invite,
|
||||
subscription.managedUsersEnabled
|
||||
)
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Failed to send notification to existing user when creating group invitation'
|
||||
)
|
||||
if (!domainCaptureEnabled) {
|
||||
// no need to create notification when domain capture is enabled since
|
||||
// dash will show one on page load for non-managed groups, and for managed groups
|
||||
// dash is not loadable until user joins the group
|
||||
try {
|
||||
await _sendNotificationToExistingUser(
|
||||
subscription,
|
||||
email,
|
||||
invite,
|
||||
subscription.managedUsersEnabled
|
||||
)
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Failed to send notification to existing user when creating group invitation'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
await subscription.save()
|
||||
@@ -273,7 +289,22 @@ async function _createInvite(subscription, email, inviter, auditLog) {
|
||||
'Error adding group audit log entry for group-invite-sent'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
let acceptInviteUrl
|
||||
if (domainCaptureEnabled) {
|
||||
const samlInitPath = (
|
||||
await Modules.promises.hooks.fire(
|
||||
'getGroupSSOInitPath',
|
||||
subscription,
|
||||
email
|
||||
)
|
||||
)?.[0]
|
||||
acceptInviteUrl = `${settings.siteUrl}${samlInitPath}`
|
||||
} else {
|
||||
acceptInviteUrl = `${settings.siteUrl}/subscription/invites/${invite.token}/`
|
||||
}
|
||||
if (managedUsersEnabled) {
|
||||
let admin = {}
|
||||
try {
|
||||
admin = await SubscriptionLocator.promises.getAdminEmailAndName(
|
||||
@@ -289,7 +320,7 @@ async function _createInvite(subscription, email, inviter, auditLog) {
|
||||
to: email,
|
||||
admin,
|
||||
inviter,
|
||||
acceptInviteUrl: `${settings.siteUrl}/subscription/invites/${invite.token}/`,
|
||||
acceptInviteUrl,
|
||||
appName: settings.appName,
|
||||
}
|
||||
|
||||
@@ -308,10 +339,9 @@ async function _createInvite(subscription, email, inviter, auditLog) {
|
||||
const opts = {
|
||||
to: email,
|
||||
inviter,
|
||||
acceptInviteUrl: `${settings.siteUrl}/subscription/invites/${invite.token}/`,
|
||||
acceptInviteUrl,
|
||||
appName: settings.appName,
|
||||
}
|
||||
|
||||
await EmailHandler.promises.sendEmail('verifyEmailToJoinTeam', opts)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ export const TeamInviteSchema = new Schema(
|
||||
{
|
||||
email: { type: String, required: true },
|
||||
token: { type: String },
|
||||
inviterName: { type: String },
|
||||
inviterName: { type: String, optional: true },
|
||||
sentAt: { type: Date },
|
||||
domainCapture: { type: Boolean, default: false, optional: true },
|
||||
},
|
||||
{ minimize: false }
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { expect, vi } from 'vitest'
|
||||
import sinon from 'sinon'
|
||||
import MockResponse from '../helpers/MockResponse.js'
|
||||
|
||||
const modulePath =
|
||||
'../../../../app/src/Features/Subscription/TeamInvitesController'
|
||||
@@ -74,7 +75,9 @@ describe('TeamInvitesController', function () {
|
||||
}
|
||||
|
||||
ctx.RateLimiter = {
|
||||
RateLimiter: class {},
|
||||
RateLimiter: class {
|
||||
consume = sinon.stub().resolves()
|
||||
},
|
||||
}
|
||||
|
||||
vi.doMock(
|
||||
@@ -274,4 +277,58 @@ describe('TeamInvitesController', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('resendInvite', function () {
|
||||
const email = 'user@example.com'
|
||||
const initPath = '/saml/ukamf/init?group_id=12345'
|
||||
beforeEach(function (ctx) {
|
||||
ctx.subscription = { teamInvites: [{ email }], populate: sinon.stub() }
|
||||
ctx.req = {
|
||||
entity: ctx.subscription,
|
||||
body: {
|
||||
email,
|
||||
},
|
||||
}
|
||||
ctx.res = new MockResponse()
|
||||
ctx.next = sinon.stub()
|
||||
})
|
||||
|
||||
it('sends the invite email again', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
const res = new MockResponse()
|
||||
res.callback = () => {
|
||||
res.statusCode.should.equal(200)
|
||||
resolve()
|
||||
}
|
||||
|
||||
ctx.Controller.resendInvite(ctx.req, res, ctx.next)
|
||||
})
|
||||
})
|
||||
|
||||
describe('when domain capture is enabled', function () {
|
||||
beforeEach(function (ctx) {
|
||||
ctx.req.entity.domainCaptureEnabled = true
|
||||
|
||||
ctx.Modules.promises.hooks.fire.resolves([initPath])
|
||||
})
|
||||
|
||||
it('sends the invite again', async function (ctx) {
|
||||
await new Promise(resolve => {
|
||||
const res = new MockResponse()
|
||||
res.callback = () => {
|
||||
sinon.assert.calledWith(
|
||||
ctx.Modules.promises.hooks.fire,
|
||||
'getGroupSSOInitPath',
|
||||
ctx.subscription,
|
||||
email
|
||||
)
|
||||
res.statusCode.should.equal(200)
|
||||
resolve()
|
||||
}
|
||||
|
||||
ctx.Controller.resendInvite(ctx.req, res, ctx.next)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -131,7 +131,7 @@ describe('TeamInvitesHandler', function () {
|
||||
}))
|
||||
|
||||
vi.doMock('@overleaf/settings', () => ({
|
||||
default: { siteUrl: 'http://example.com' },
|
||||
default: { siteUrl: 'http://example.com', appName: 'Overleaf' },
|
||||
}))
|
||||
|
||||
vi.doMock('../../../../app/src/models/TeamInvite', () => ({
|
||||
@@ -414,6 +414,90 @@ describe('TeamInvitesHandler', function () {
|
||||
'addGroupAuditLogEntry'
|
||||
)
|
||||
})
|
||||
|
||||
describe('when domain capture is enabled', function () {
|
||||
it('creates a domain capture invite', async function (ctx) {
|
||||
const initPath = '/saml/ukamf/init?group_id=12345'
|
||||
ctx.Modules.promises.hooks.fire.resolves([initPath])
|
||||
ctx.UserGetter.promises.getUser.resolves(ctx.manager)
|
||||
|
||||
ctx.subscription.domainCaptureEnabled = true
|
||||
const invite = await ctx.TeamInvitesHandler.promises.createInvite(
|
||||
ctx.manager._id,
|
||||
ctx.subscription,
|
||||
'user@example.com',
|
||||
{ domainCapture: true }
|
||||
)
|
||||
expect(invite.token).to.be.undefined
|
||||
expect(invite.domainCapture).to.be.true
|
||||
expect(invite.email).to.eq('user@example.com')
|
||||
|
||||
sinon.assert.calledWith(
|
||||
ctx.Modules.promises.hooks.fire,
|
||||
'getGroupSSOInitPath',
|
||||
ctx.subscription,
|
||||
invite.email
|
||||
)
|
||||
|
||||
ctx.EmailHandler.promises.sendEmail
|
||||
.calledWith(
|
||||
'verifyEmailToJoinTeam',
|
||||
sinon.match({
|
||||
to: 'user@example.com',
|
||||
inviter: ctx.manager,
|
||||
acceptInviteUrl: `http://example.com${initPath}`,
|
||||
appName: 'Overleaf',
|
||||
})
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
|
||||
describe('when managed users is also enabled', function () {
|
||||
it('creates a domain capture invite', async function (ctx) {
|
||||
ctx.SubscriptionLocator.promises.getAdminEmailAndName = sinon
|
||||
.stub()
|
||||
.resolves(ctx.manager)
|
||||
|
||||
ctx.UserGetter.promises.getUserByAnyEmail
|
||||
.withArgs('user@example.com')
|
||||
.resolves(ctx.user)
|
||||
const initPath = '/saml/ukamf/init?group_id=12345'
|
||||
ctx.Modules.promises.hooks.fire.resolves([initPath])
|
||||
ctx.UserGetter.promises.getUser.resolves(ctx.manager)
|
||||
ctx.subscription.managedUsersEnabled = true
|
||||
ctx.subscription.domainCaptureEnabled = true
|
||||
const invite = await ctx.TeamInvitesHandler.promises.createInvite(
|
||||
ctx.manager._id,
|
||||
ctx.subscription,
|
||||
'user@example.com',
|
||||
{ domainCapture: true }
|
||||
)
|
||||
expect(invite.token).to.be.undefined
|
||||
expect(invite.domainCapture).to.be.true
|
||||
expect(invite.email).to.eq('user@example.com')
|
||||
|
||||
sinon.assert.calledWith(
|
||||
ctx.Modules.promises.hooks.fire,
|
||||
'getGroupSSOInitPath',
|
||||
ctx.subscription,
|
||||
invite.email
|
||||
)
|
||||
|
||||
ctx.EmailHandler.promises.sendEmail
|
||||
.calledWith(
|
||||
'inviteNewUserToJoinManagedUsers',
|
||||
sinon.match({
|
||||
to: 'user@example.com',
|
||||
inviter: ctx.manager,
|
||||
acceptInviteUrl: `http://example.com${initPath}`,
|
||||
appName: 'Overleaf',
|
||||
admin: ctx.manager,
|
||||
})
|
||||
)
|
||||
.should.equal(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('importInvite', function () {
|
||||
|
||||
Reference in New Issue
Block a user