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

[web] Log messages to the project audit log and do so for enabling/disabling sharing (#10281)

GitOrigin-RevId: d6af26bcbafb4d1789fca6319fd343fe8c2ecd25
Dr. Sasha Göbbels 3 лет назад
Родитель
Сommit
c9cf530e36

+ 2 - 0
services/web/app/src/Features/Project/ProjectAuditLogHandler.js

@@ -1,9 +1,11 @@
 const { ProjectAuditLogEntry } = require('../../models/ProjectAuditLogEntry')
+const { callbackify } = require('../../util/promises')
 
 module.exports = {
   promises: {
     addEntry,
   },
+  addEntry: callbackify(addEntry), // callback version of adEntry
 }
 
 /**

+ 35 - 9
services/web/app/src/Features/Project/ProjectController.js

@@ -45,6 +45,8 @@ const SubscriptionViewModelBuilder = require('../Subscription/SubscriptionViewMo
 const SurveyHandler = require('../Survey/SurveyHandler')
 const { expressify } = require('../../util/promises')
 const ProjectListController = require('./ProjectListController')
+const ProjectAuditLogHandler = require('./ProjectAuditLogHandler')
+const PublicAccessLevels = require('../Authorization/PublicAccessLevels')
 
 /**
  * @typedef {import("./types").GetProjectsRequest} GetProjectsRequest
@@ -131,9 +133,21 @@ const ProjectController = {
 
   updateProjectAdminSettings(req, res, next) {
     const projectId = req.params.Project_id
+    const user = SessionManager.getSessionUser(req.session)
+    const publicAccessLevel = req.body.publicAccessLevel
+    const publicAccessLevels = [
+      PublicAccessLevels.READ_ONLY,
+      PublicAccessLevels.READ_AND_WRITE,
+      PublicAccessLevels.PRIVATE,
+      PublicAccessLevels.TOKEN_BASED,
+    ]
+
+    if (
+      req.body.publicAccessLevel != null &&
+      publicAccessLevels.includes(publicAccessLevel)
+    ) {
+      const jobs = []
 
-    const jobs = []
-    if (req.body.publicAccessLevel != null) {
       jobs.push(callback =>
         EditorController.setPublicAccessLevel(
           projectId,
@@ -141,14 +155,26 @@ const ProjectController = {
           callback
         )
       )
-    }
 
-    async.series(jobs, error => {
-      if (error != null) {
-        return next(error)
-      }
-      res.sendStatus(204)
-    })
+      jobs.push(callback =>
+        ProjectAuditLogHandler.addEntry(
+          projectId,
+          'toggle-access-level',
+          user._id,
+          { publicAccessLevel: req.body.publicAccessLevel, status: 'OK' },
+          callback
+        )
+      )
+
+      async.series(jobs, error => {
+        if (error != null) {
+          return next(error)
+        }
+        res.sendStatus(204)
+      })
+    } else {
+      res.sendStatus(500)
+    }
   },
 
   deleteProject(req, res) {

+ 2 - 0
services/web/app/src/Features/Project/ProjectDetailsHandler.js

@@ -200,6 +200,8 @@ async function setPublicAccessLevel(projectId, newAccessLevel) {
       { _id: projectId },
       { publicAccesLevel: newAccessLevel }
     ).exec()
+  } else {
+    throw new Errors.InvalidError('unexpected access level')
   }
 }
 

+ 8 - 1
services/web/app/src/router.js

@@ -64,6 +64,7 @@ const logger = require('@overleaf/logger')
 const _ = require('underscore')
 const { expressify } = require('./util/promises')
 const { plainTextResponse } = require('./infrastructure/Response')
+const PublicAccessLevels = require('./Features/Authorization/PublicAccessLevels')
 
 module.exports = { initialize }
 
@@ -401,7 +402,13 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) {
   )
   webRouter.post(
     '/project/:Project_id/settings',
-    validate({ body: Joi.object() }),
+    validate({
+      body: Joi.object({
+        publicAccessLevel: Joi.string()
+          .valid(PublicAccessLevels.PRIVATE, PublicAccessLevels.TOKEN_BASED)
+          .optional(),
+      }),
+    }),
     AuthorizationMiddleware.ensureUserCanWriteProjectSettings,
     ProjectController.updateProjectSettings
   )

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

@@ -144,6 +144,9 @@ describe('ProjectController', function () {
     this.SurveyHandler = {
       getSurvey: sinon.stub().yields(null, {}),
     }
+    this.ProjectAuditLogHandler = {
+      addEntry: sinon.stub().yields(),
+    }
 
     this.ProjectController = SandboxedModule.require(MODULE_PATH, {
       requires: {
@@ -190,6 +193,7 @@ describe('ProjectController', function () {
         },
         '../Institutions/InstitutionsFeatures': this.InstitutionsFeatures,
         '../Survey/SurveyHandler': this.SurveyHandler,
+        './ProjectAuditLogHandler': this.ProjectAuditLogHandler,
       },
     })
 
@@ -293,17 +297,34 @@ describe('ProjectController', function () {
     it('should update the public access level', function (done) {
       this.EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
       this.req.body = {
-        publicAccessLevel: (this.publicAccessLevel = 'readonly'),
+        publicAccessLevel: 'readOnly',
       }
       this.res.sendStatus = code => {
         this.EditorController.setPublicAccessLevel
-          .calledWith(this.project_id, this.publicAccessLevel)
+          .calledWith(this.project_id, 'readOnly')
           .should.equal(true)
         code.should.equal(204)
         done()
       }
       this.ProjectController.updateProjectAdminSettings(this.req, this.res)
     })
+
+    it('should record the change in the project audit log', function (done) {
+      this.EditorController.setPublicAccessLevel = sinon.stub().callsArg(2)
+      this.req.body = {
+        publicAccessLevel: 'readOnly',
+      }
+      this.res.sendStatus = code => {
+        this.ProjectAuditLogHandler.addEntry
+          .calledWith(this.project_id, 'toggle-access-level', this.user._id, {
+            publicAccessLevel: 'readOnly',
+            status: 'OK',
+          })
+          .should.equal(true)
+        done()
+      }
+      this.ProjectController.updateProjectAdminSettings(this.req, this.res)
+    })
   })
 
   describe('deleteProject', function () {