Merge pull request #23131 from overleaf/kh-teardown-link-sharing-split-tests

[web] tear down link sharing split tests

GitOrigin-RevId: 449e9f368405aea1500035269428e7ae0c37d8fb
This commit is contained in:
Kristina
2025-02-04 09:04:25 +00:00
committed by Copybot
parent 8831fb9a18
commit e3fdcdd601
24 changed files with 806 additions and 2264 deletions
@@ -13,8 +13,6 @@ import { expressify } from '@overleaf/promise-utils'
import { hasAdminAccess } from '../Helpers/AdminAuthorizationHelper.js'
import TokenAccessHandler from '../TokenAccess/TokenAccessHandler.js'
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js'
import ProjectGetter from '../Project/ProjectGetter.js'
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
import LimitationsManager from '../Subscription/LimitationsManager.js'
import PrivilegeLevels from '../Authorization/PrivilegeLevels.js'
@@ -83,24 +81,14 @@ async function setCollaboratorInfo(req, res, next) {
const { privilegeLevel } = req.body
if (privilegeLevel !== PrivilegeLevels.READ_ONLY) {
const project = await ProjectGetter.promises.getProject(projectId, {
owner_ref: 1,
})
const linkSharingChanges =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-warning'
)
const allowed =
await LimitationsManager.promises.canAddXEditCollaborators(projectId, 1)
if (linkSharingChanges?.variant === 'active') {
if (!allowed) {
return HttpErrorHandler.forbidden(
req,
res,
'edit collaborator limit reached'
)
}
if (!allowed) {
return HttpErrorHandler.forbidden(
req,
res,
'edit collaborator limit reached'
)
}
}
await CollaboratorsHandler.promises.setCollaboratorPrivilegeLevel(
@@ -15,7 +15,6 @@ import { expressify } from '@overleaf/promise-utils'
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js'
import Errors from '../Errors/Errors.js'
import AuthenticationController from '../Authentication/AuthenticationController.js'
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
import PrivilegeLevels from '../Authorization/PrivilegeLevels.js'
// This rate limiter allows a different number of requests depending on the
@@ -98,28 +97,12 @@ async function inviteToProject(req, res) {
logger.debug({ projectId, email, sendingUserId }, 'inviting to project')
const project = await ProjectGetter.promises.getProject(projectId, {
owner_ref: 1,
})
const linkSharingChanges =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-warning'
)
let allowed = false
if (linkSharingChanges?.variant === 'active') {
// if link-sharing-warning is active, can always invite read-only collaborators
if (privileges === PrivilegeLevels.READ_ONLY) {
allowed = true
} else {
allowed = await LimitationsManager.promises.canAddXEditCollaborators(
projectId,
1
)
}
// can always invite read-only collaborators
if (privileges === PrivilegeLevels.READ_ONLY) {
allowed = true
} else {
allowed = await LimitationsManager.promises.canAddXCollaborators(
allowed = await LimitationsManager.promises.canAddXEditCollaborators(
projectId,
1
)
@@ -9,7 +9,6 @@ import UserGetter from '../User/UserGetter.js'
import ProjectGetter from '../Project/ProjectGetter.js'
import NotificationsBuilder from '../Notifications/NotificationsBuilder.js'
import PrivilegeLevels from '../Authorization/PrivilegeLevels.js'
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
import LimitationsManager from '../Subscription/LimitationsManager.js'
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js'
import _ from 'lodash'
@@ -148,14 +147,8 @@ const CollaboratorsInviteHandler = {
const project = await ProjectGetter.promises.getProject(projectId, {
owner_ref: 1,
})
const linkSharingEnforcement =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-enforcement'
)
const pendingEditor =
invite.privileges === PrivilegeLevels.READ_AND_WRITE &&
linkSharingEnforcement?.variant === 'active' &&
!(await LimitationsManager.promises.canAcceptEditCollaboratorInvite(
project._id
))
@@ -485,52 +485,32 @@ const _ProjectController = {
anonRequestToken
)
const [
linkSharingChanges,
linkSharingEnforcement,
reviewerRoleAssignment,
] = await Promise.all([
SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-warning'
),
SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-enforcement'
),
SplitTestHandler.promises.getAssignmentForUser(
const reviewerRoleAssignment =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'reviewer-role'
),
])
)
if (linkSharingChanges?.variant === 'active') {
if (linkSharingEnforcement?.variant === 'active') {
await Modules.promises.hooks.fire(
'enforceCollaboratorLimit',
await Modules.promises.hooks.fire('enforceCollaboratorLimit', projectId)
if (isTokenMember) {
// Check explicitly that the user is in read write token refs, while this could be inferred
// from the privilege level, the privilege level of token members might later be restricted
const isReadWriteTokenMember =
await CollaboratorsGetter.promises.userIsReadWriteTokenMember(
userId,
projectId
)
}
if (isTokenMember) {
// Check explicitly that the user is in read write token refs, while this could be inferred
// from the privilege level, the privilege level of token members might later be restricted
const isReadWriteTokenMember =
await CollaboratorsGetter.promises.userIsReadWriteTokenMember(
if (isReadWriteTokenMember) {
// Check for an edge case where a user is both in read write token access refs but also
// an invited read write member. Ensure they are not redirected to the sharing updates page
// We could also delete the token access ref if the user is already a member of the project
const isInvitedReadWriteMember =
await CollaboratorsGetter.promises.isUserInvitedReadWriteMemberOfProject(
userId,
projectId
)
if (isReadWriteTokenMember) {
// Check for an edge case where a user is both in read write token access refs but also
// an invited read write member. Ensure they are not redirected to the sharing updates page
// We could also delete the token access ref if the user is already a member of the project
const isInvitedReadWriteMember =
await CollaboratorsGetter.promises.isUserInvitedReadWriteMemberOfProject(
userId,
projectId
)
if (!isInvitedReadWriteMember) {
return res.redirect(`/project/${projectId}/sharing-updates`)
}
if (!isInvitedReadWriteMember) {
return res.redirect(`/project/${projectId}/sharing-updates`)
}
}
}
@@ -589,9 +569,6 @@ const _ProjectController = {
const exceedAtLimit = planLimit > -1 && namedEditors >= planLimit
const projectOpenedSegmentation = {
projectId: project._id,
// temporary link sharing segmentation:
linkSharingWarning: linkSharingChanges?.variant,
linkSharingEnforcement: linkSharingEnforcement?.variant,
namedEditors,
pendingEditors,
tokenEditors: project.tokenAccessReadAndWrite_refs?.length || 0,
@@ -833,8 +810,6 @@ const _ProjectController = {
useOpenTelemetry: Settings.useOpenTelemetryClient,
hasTrackChangesFeature: Features.hasFeature('track-changes'),
projectTags,
linkSharingWarning: linkSharingChanges?.variant === 'active',
linkSharingEnforcement: linkSharingEnforcement?.variant === 'active',
usedLatex:
// only use the usedLatex value if the split test is enabled
splitTestAssignments['default-visual-for-beginners']?.variant ===
@@ -8,7 +8,6 @@ import { expressify } from '@overleaf/promise-utils'
import AuthorizationManager from '../Authorization/AuthorizationManager.js'
import PrivilegeLevels from '../Authorization/PrivilegeLevels.js'
import ProjectAuditLogHandler from '../Project/ProjectAuditLogHandler.js'
import SplitTestHandler from '../SplitTests/SplitTestHandler.js'
import CollaboratorsInviteHandler from '../Collaborators/CollaboratorsInviteHandler.mjs'
import CollaboratorsHandler from '../Collaborators/CollaboratorsHandler.js'
import EditorRealTimeController from '../Editor/EditorRealTimeController.js'
@@ -317,108 +316,60 @@ async function grantTokenAccessReadAndWrite(req, res, next) {
return next(new Errors.NotFoundError())
}
const linkSharingChanges =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-warning'
)
if (linkSharingChanges?.variant === 'active') {
if (!confirmedByUser) {
return res.json({
requireAccept: {
linkSharingChanges: true,
projectName: project.name,
},
})
}
const linkSharingEnforcement =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-enforcement'
)
const pendingEditor =
linkSharingEnforcement?.variant === 'active' &&
!(await LimitationsManager.promises.canAcceptEditCollaboratorInvite(
project._id
))
await ProjectAuditLogHandler.promises.addEntry(
project._id,
'accept-via-link-sharing',
userId,
req.ip,
{
privileges: pendingEditor ? 'readOnly' : 'readAndWrite',
...(pendingEditor && { pendingEditor: true }),
}
)
AnalyticsManager.recordEventForUserInBackground(
userId,
'project-joined',
{
mode: pendingEditor ? 'read-only' : 'read-write',
projectId: project._id.toString(),
...(pendingEditor && { pendingEditor: true }),
}
)
await CollaboratorsHandler.promises.addUserIdToProject(
project._id,
undefined,
userId,
pendingEditor
? PrivilegeLevels.READ_ONLY
: PrivilegeLevels.READ_AND_WRITE,
{ pendingEditor }
)
// remove pending invite and notification
const userEmails =
await UserGetter.promises.getUserConfirmedEmails(userId)
await CollaboratorsInviteHandler.promises.revokeInviteForUser(
project._id,
userEmails
)
// Should be a noop if the user is already a member,
// and would redirect transparently into the project.
EditorRealTimeController.emitToRoom(
project._id,
'project:membership:changed',
{ members: true, invites: true }
)
if (!confirmedByUser) {
return res.json({
redirect: `/project/${project._id}`,
})
} else {
if (!confirmedByUser) {
return res.json({
requireAccept: {
projectName: project.name,
},
})
}
if (!project.tokenAccessReadAndWrite_refs.some(id => id.equals(userId))) {
await ProjectAuditLogHandler.promises.addEntry(
project._id,
'join-via-token',
userId,
req.ip,
{ privileges: 'readAndWrite' }
)
}
await TokenAccessHandler.promises.addReadAndWriteUserToProject(
userId,
project._id
)
return res.json({
redirect: `/project/${project._id}`,
tokenAccessGranted: tokenType,
requireAccept: {
projectName: project.name,
},
})
}
const pendingEditor =
!(await LimitationsManager.promises.canAcceptEditCollaboratorInvite(
project._id
))
await ProjectAuditLogHandler.promises.addEntry(
project._id,
'accept-via-link-sharing',
userId,
req.ip,
{
privileges: pendingEditor ? 'readOnly' : 'readAndWrite',
...(pendingEditor && { pendingEditor: true }),
}
)
AnalyticsManager.recordEventForUserInBackground(userId, 'project-joined', {
mode: pendingEditor ? 'read-only' : 'read-write',
projectId: project._id.toString(),
...(pendingEditor && { pendingEditor: true }),
})
await CollaboratorsHandler.promises.addUserIdToProject(
project._id,
undefined,
userId,
pendingEditor
? PrivilegeLevels.READ_ONLY
: PrivilegeLevels.READ_AND_WRITE,
{ pendingEditor }
)
// remove pending invite and notification
const userEmails = await UserGetter.promises.getUserConfirmedEmails(userId)
await CollaboratorsInviteHandler.promises.revokeInviteForUser(
project._id,
userEmails
)
// Should be a noop if the user is already a member,
// and would redirect transparently into the project.
EditorRealTimeController.emitToRoom(
project._id,
'project:membership:changed',
{ members: true, invites: true }
)
return res.json({
redirect: `/project/${project._id}`,
})
} catch (err) {
return next(
OError.tag(
@@ -516,14 +467,6 @@ async function ensureUserCanUseSharingUpdatesConsentPage(req, res, next) {
if (!project) {
throw new Errors.NotFoundError()
}
const linkSharingChanges =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-warning'
)
if (linkSharingChanges?.variant !== 'active') {
return AsyncFormHelper.redirect(req, res, `/project/${projectId}`)
}
const isReadWriteTokenMember =
await CollaboratorsGetter.promises.userIsReadWriteTokenMember(
userId,
@@ -567,13 +510,7 @@ async function moveReadWriteToCollaborators(req, res, next) {
userId,
projectId
)
const linkSharingEnforcement =
await SplitTestHandler.promises.getAssignmentForUser(
project.owner_ref,
'link-sharing-enforcement'
)
const pendingEditor =
linkSharingEnforcement?.variant === 'active' &&
!(await LimitationsManager.promises.canAcceptEditCollaboratorInvite(
project._id
))