diff --git a/services/web/app/src/Features/SystemMessages/SystemMessageController.js b/services/web/app/src/Features/SystemMessages/SystemMessageController.js index dace617725..dcf4a16c95 100644 --- a/services/web/app/src/Features/SystemMessages/SystemMessageController.js +++ b/services/web/app/src/Features/SystemMessages/SystemMessageController.js @@ -1,8 +1,13 @@ const Settings = require('settings-sharelatex') +const AuthenticationController = require('../Authentication/AuthenticationController') const SystemMessageManager = require('./SystemMessageManager') const ProjectController = { getMessages(req, res, next) { + if (!AuthenticationController.isUserLoggedIn(req)) { + // gracefully handle requests from anonymous users + return res.json([]) + } SystemMessageManager.getMessages((err, messages) => { if (err) { next(err) diff --git a/services/web/app/src/router.js b/services/web/app/src/router.js index 63a3a577f2..f610e6cd6d 100644 --- a/services/web/app/src/router.js +++ b/services/web/app/src/router.js @@ -107,11 +107,8 @@ function initialize(webRouter, privateApiRouter, publicApiRouter) { ) } - webRouter.get( - '/system/messages', - AuthenticationController.requireLogin(), - SystemMessageController.getMessages - ) + // .getMessages will generate an empty response for anonymous users. + webRouter.get('/system/messages', SystemMessageController.getMessages) webRouter.get( '/user/settings', diff --git a/services/web/frontend/js/ide/metadata/services/metadata.js b/services/web/frontend/js/ide/metadata/services/metadata.js index 8e68b7d512..958b624489 100644 --- a/services/web/frontend/js/ide/metadata/services/metadata.js +++ b/services/web/frontend/js/ide/metadata/services/metadata.js @@ -85,6 +85,12 @@ export default App.factory('metadata', function($http, ide) { }) metadata.scheduleLoadDocMetaFromServer = function(docId) { + if (ide.$scope.permissionsLevel === 'readOnly') { + // The POST request is blocked for users without write permission. + // The user will not be able to consume the meta data for edits anyways. + return + } + // De-bounce loading labels with a timeout const existingTimeout = debouncer[docId] diff --git a/services/web/frontend/js/ide/settings/controllers/SettingsController.js b/services/web/frontend/js/ide/settings/controllers/SettingsController.js index cbe2180086..357d770358 100644 --- a/services/web/frontend/js/ide/settings/controllers/SettingsController.js +++ b/services/web/frontend/js/ide/settings/controllers/SettingsController.js @@ -195,6 +195,11 @@ export default App.controller('SettingsController', function( if (typeof oldRootDoc_id === 'undefined') { return } + if ($scope.permissionsLevel === 'readOnly') { + // The user is unauthorized to persist rootDoc changes. + // Use the new value for this very editor session only. + return + } // otherwise only save changes, null values are allowed if (rootDoc_id !== oldRootDoc_id) { settings.saveProjectSettings({ rootDocId: rootDoc_id }).catch(() => { diff --git a/services/web/frontend/js/ide/share/controllers/ShareProjectModalController.js b/services/web/frontend/js/ide/share/controllers/ShareProjectModalController.js index c5ca86e383..1d889aaef8 100644 --- a/services/web/frontend/js/ide/share/controllers/ShareProjectModalController.js +++ b/services/web/frontend/js/ide/share/controllers/ShareProjectModalController.js @@ -57,7 +57,16 @@ App.controller('ShareProjectModalController', function( ) $scope.autocompleteContacts = [] - $http.get('/user/contacts').then(function(response) { + if ($scope.isRestrictedTokenMember) { + // Restricted token members are users who join via a read-only link. + // They will not be able to invite any users, so skip the lookup of + // their contacts. This request would result in a 403 for anonymous + // users, which in turn would redirect them to the /login. + } else { + $http.get('/user/contacts').then(processContactsResponse) + } + + function processContactsResponse(response) { const { data } = response $scope.autocompleteContacts = data.contacts || [] for (let contact of $scope.autocompleteContacts) { @@ -77,7 +86,7 @@ App.controller('ShareProjectModalController', function( contact.display = contact.name } } - }) + } const getCurrentMemberEmails = () => ($scope.project.members || []).map(u => u.email)