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

[web] Upgrade restricted user access if they are invited members (#9401)

* [web] Upgrade restricted user access if they are invited members

Previously, if a user joined a project via a read-only link and later on
joined the project via an invite, we would still treat them as
restricted users, disabling chat and commenting. This patch changes
that, so that we do *not* consider an invited user restricted.

GitOrigin-RevId: e2acdfd29cc0687cb7276310a9c96d697087b21a
Mathias Jakobsen 4 лет назад
Родитель
Сommit
b5e2604041

+ 20 - 3
services/web/app/src/Features/Authorization/AuthorizationManager.js

@@ -11,12 +11,19 @@ const Errors = require('../Errors/Errors')
 const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper')
 const { hasAdminAccess } = require('../Helpers/AdminAuthorizationHelper')
 const Settings = require('@overleaf/settings')
 const Settings = require('@overleaf/settings')
 
 
-function isRestrictedUser(userId, privilegeLevel, isTokenMember) {
+function isRestrictedUser(
+  userId,
+  privilegeLevel,
+  isTokenMember,
+  isInvitedMember
+) {
   if (privilegeLevel === PrivilegeLevels.NONE) {
   if (privilegeLevel === PrivilegeLevels.NONE) {
     return true
     return true
   }
   }
   return (
   return (
-    privilegeLevel === PrivilegeLevels.READ_ONLY && (isTokenMember || !userId)
+    privilegeLevel === PrivilegeLevels.READ_ONLY &&
+    (isTokenMember || !userId) &&
+    !isInvitedMember
   )
   )
 }
 }
 
 
@@ -30,7 +37,17 @@ async function isRestrictedUserForProject(userId, projectId, token) {
     userId,
     userId,
     projectId
     projectId
   )
   )
-  return isRestrictedUser(userId, privilegeLevel, isTokenMember)
+  const isInvitedMember =
+    await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
+      userId,
+      projectId
+    )
+  return isRestrictedUser(
+    userId,
+    privilegeLevel,
+    isTokenMember,
+    isInvitedMember
+  )
 }
 }
 
 
 async function getPublicAccessLevel(projectId) {
 async function getPublicAccessLevel(projectId) {

+ 3 - 0
services/web/app/src/Features/Collaborators/CollaboratorsGetter.js

@@ -118,6 +118,9 @@ async function getInvitedCollaboratorCount(projectId) {
 }
 }
 
 
 async function isUserInvitedMemberOfProject(userId, projectId) {
 async function isUserInvitedMemberOfProject(userId, projectId) {
+  if (!userId) {
+    return false
+  }
   const members = await getMemberIdsWithPrivilegeLevels(projectId)
   const members = await getMemberIdsWithPrivilegeLevels(projectId)
   for (const member of members) {
   for (const member of members) {
     if (
     if (

+ 7 - 1
services/web/app/src/Features/Editor/EditorHttpController.js

@@ -131,10 +131,16 @@ async function _buildJoinProjectView(req, projectId, userId) {
     userId,
     userId,
     projectId
     projectId
   )
   )
+  const isInvitedMember =
+    await CollaboratorsGetter.promises.isUserInvitedMemberOfProject(
+      userId,
+      projectId
+    )
   const isRestrictedUser = AuthorizationManager.isRestrictedUser(
   const isRestrictedUser = AuthorizationManager.isRestrictedUser(
     userId,
     userId,
     privilegeLevel,
     privilegeLevel,
-    isTokenMember
+    isTokenMember,
+    isInvitedMember
   )
   )
   return {
   return {
     project: ProjectEditorHandler.buildProjectModelView(
     project: ProjectEditorHandler.buildProjectModelView(

+ 10 - 1
services/web/app/src/Features/Project/ProjectController.js

@@ -833,6 +833,13 @@ const ProjectController = {
           }
           }
           CollaboratorsGetter.userIsTokenMember(userId, projectId, cb)
           CollaboratorsGetter.userIsTokenMember(userId, projectId, cb)
         },
         },
+        isInvitedMember(cb) {
+          CollaboratorsGetter.isUserInvitedMemberOfProject(
+            userId,
+            projectId,
+            cb
+          )
+        },
         brandVariation: [
         brandVariation: [
           'project',
           'project',
           (results, cb) => {
           (results, cb) => {
@@ -1060,6 +1067,7 @@ const ProjectController = {
           subscription,
           subscription,
           userIsMemberOfGroupSubscription,
           userIsMemberOfGroupSubscription,
           isTokenMember,
           isTokenMember,
+          isInvitedMember,
           brandVariation,
           brandVariation,
           newSourceEditorAssignment,
           newSourceEditorAssignment,
           pdfjsAssignment,
           pdfjsAssignment,
@@ -1220,7 +1228,8 @@ const ProjectController = {
               isRestrictedTokenMember: AuthorizationManager.isRestrictedUser(
               isRestrictedTokenMember: AuthorizationManager.isRestrictedUser(
                 userId,
                 userId,
                 privilegeLevel,
                 privilegeLevel,
-                isTokenMember
+                isTokenMember,
+                isInvitedMember
               ),
               ),
               languages: Settings.languages,
               languages: Settings.languages,
               learnedWords,
               learnedWords,

+ 11 - 9
services/web/test/unit/src/Authorization/AuthorizationManagerTests.js

@@ -65,17 +65,19 @@ describe('AuthorizationManager', function () {
   describe('isRestrictedUser', function () {
   describe('isRestrictedUser', function () {
     it('should produce the correct values', function () {
     it('should produce the correct values', function () {
       const notRestrictedScenarios = [
       const notRestrictedScenarios = [
-        [null, 'readAndWrite', false],
-        ['id', 'readAndWrite', true],
-        ['id', 'readOnly', false],
+        [null, 'readAndWrite', false, false],
+        ['id', 'readAndWrite', true, false],
+        ['id', 'readAndWrite', true, true],
+        ['id', 'readOnly', false, false],
+        ['id', 'readOnly', false, true],
       ]
       ]
       const restrictedScenarios = [
       const restrictedScenarios = [
-        [null, 'readOnly', false],
-        ['id', 'readOnly', true],
-        [null, false, true],
-        [null, false, false],
-        ['id', false, true],
-        ['id', false, false],
+        [null, 'readOnly', false, false],
+        ['id', 'readOnly', true, false],
+        [null, false, true, false],
+        [null, false, false, false],
+        ['id', false, true, false],
+        ['id', false, false, false],
       ]
       ]
       for (const notRestrictedArgs of notRestrictedScenarios) {
       for (const notRestrictedArgs of notRestrictedScenarios) {
         expect(
         expect(

+ 2 - 1
services/web/test/unit/src/Editor/EditorHttpControllerTests.js

@@ -59,6 +59,7 @@ describe('EditorHttpController', function () {
         getInvitedMembersWithPrivilegeLevels: sinon
         getInvitedMembersWithPrivilegeLevels: sinon
           .stub()
           .stub()
           .resolves(['members', 'mock']),
           .resolves(['members', 'mock']),
+        isUserInvitedMemberOfProject: sinon.stub().resolves(false),
       },
       },
     }
     }
     this.CollaboratorsHandler = {
     this.CollaboratorsHandler = {
@@ -234,7 +235,7 @@ describe('EditorHttpController', function () {
         this.req.query = { user_id: 'anonymous-user' }
         this.req.query = { user_id: 'anonymous-user' }
         this.res.json.callsFake(() => done())
         this.res.json.callsFake(() => done())
         this.AuthorizationManager.isRestrictedUser
         this.AuthorizationManager.isRestrictedUser
-          .withArgs(null, 'readOnly', false)
+          .withArgs(null, 'readOnly', false, false)
           .returns(true)
           .returns(true)
         this.AuthorizationManager.promises.getPrivilegeLevelForProject
         this.AuthorizationManager.promises.getPrivilegeLevelForProject
           .withArgs(null, this.project._id, this.token)
           .withArgs(null, this.project._id, this.token)

+ 2 - 0
services/web/test/unit/src/Project/ProjectControllerTests.js

@@ -96,6 +96,7 @@ describe('ProjectController', function () {
     }
     }
     this.CollaboratorsGetter = {
     this.CollaboratorsGetter = {
       userIsTokenMember: sinon.stub().callsArgWith(2, null, false),
       userIsTokenMember: sinon.stub().callsArgWith(2, null, false),
+      isUserInvitedMemberOfProject: sinon.stub().callsArgWith(2, null, true),
     }
     }
     this.ProjectEntityHandler = {}
     this.ProjectEntityHandler = {}
     this.NotificationBuilder = {
     this.NotificationBuilder = {
@@ -1014,6 +1015,7 @@ describe('ProjectController', function () {
     })
     })
 
 
     it('should add isRestrictedTokenMember', function (done) {
     it('should add isRestrictedTokenMember', function (done) {
+      this.AuthorizationManager.isRestrictedUser.returns(false)
       this.res.render = (pageName, opts) => {
       this.res.render = (pageName, opts) => {
         opts.isRestrictedTokenMember.should.exist
         opts.isRestrictedTokenMember.should.exist
         opts.isRestrictedTokenMember.should.equal(false)
         opts.isRestrictedTokenMember.should.equal(false)