diff --git a/public/language/en-GB/admin/manage/privileges.json b/public/language/en-GB/admin/manage/privileges.json index 6cadf3798c..5e6d451b67 100644 --- a/public/language/en-GB/admin/manage/privileges.json +++ b/public/language/en-GB/admin/manage/privileges.json @@ -10,6 +10,9 @@ "search-content": "Search Content", "search-users": "Search Users", "search-tags": "Search Tags", + "view-users": "View Users", + "view-tags": "View Tags", + "view-groups": "View Groups", "allow-local-login": "Local Login", "allow-group-creation": "Group Create", diff --git a/public/language/en-GB/admin/settings/tags.json b/public/language/en-GB/admin/settings/tags.json index 6f31f60ba0..d329cff68b 100644 --- a/public/language/en-GB/admin/settings/tags.json +++ b/public/language/en-GB/admin/settings/tags.json @@ -5,8 +5,6 @@ "min-length": "Minimum Tag Length", "max-length": "Maximum Tag Length", "goto-manage": "Click here to visit the tag management page.", - "privacy": "Privacy", - "list-private": "Make the tags list private", "related-topics": "Related Topics", "max-related-topics": "Maximum related topics to display (if supported by theme)" } \ No newline at end of file diff --git a/public/language/en-GB/admin/settings/user.json b/public/language/en-GB/admin/settings/user.json index a504f5ef8d..2cfb16addc 100644 --- a/public/language/en-GB/admin/settings/user.json +++ b/public/language/en-GB/admin/settings/user.json @@ -14,7 +14,6 @@ "disable-email-changes": "Disable email changes", "disable-password-changes": "Disable password changes", "allow-account-deletion": "Allow account deletion", - "user-info-private": "Hide user list and data from guests", "hide-fullname": "Hide fullname from users", "hide-email": "Hide email from users", "themes": "Themes", diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index d649518949..b6d56c9e7b 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -40,13 +40,15 @@ return false; } - var loggedIn = data.config ? data.config.loggedIn : false; + if (item.route.match('/users') && data.user && !data.user.privileges['view:users']) { + return false; + } - if (item.route.match('/users') && data.privateUserInfo && !loggedIn) { + if (item.route.match('/tags') && data.user && !data.user.privileges['view:tags']) { return false; } - if (item.route.match('/tags') && data.privateTagListing && !loggedIn) { + if (item.route.match('/groups') && data.user && !data.user.privileges['view:groups']) { return false; } @@ -179,7 +181,7 @@ } return states.map(function (priv) { var guestDisabled = ['groups:moderate', 'groups:posts:upvote', 'groups:posts:downvote', 'groups:local:login', 'groups:group:create']; - var spidersEnabled = ['groups:find', 'groups:read', 'groups:topics:read']; + var spidersEnabled = ['groups:find', 'groups:read', 'groups:topics:read', 'groups:view:users', 'groups:view:tags', 'groups:view:groups']; var disabled = (member === 'guests' && guestDisabled.includes(priv.name)) || (member === 'spiders' && !spidersEnabled.includes(priv.name)); diff --git a/src/controllers/errors.js b/src/controllers/errors.js index 849750e391..032924acd6 100644 --- a/src/controllers/errors.js +++ b/src/controllers/errors.js @@ -39,7 +39,7 @@ exports.handleURIErrors = function handleURIErrors(err, req, res, next) { exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars var cases = { EBADCSRFTOKEN: function () { - winston.error(req.path + '\n', err.message); + winston.error(req.path + '\n' + err.message); res.sendStatus(403); }, 'blacklisted-ip': function () { diff --git a/src/controllers/user.js b/src/controllers/user.js index 66eaeafa8e..ea3ba03083 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -13,6 +13,7 @@ var meta = require('../meta'); var posts = require('../posts'); var batch = require('../batch'); var events = require('../events'); +var privileges = require('../privileges'); var accountHelpers = require('./accounts/helpers'); var userController = module.exports; @@ -84,27 +85,33 @@ userController.getUserDataByField = function (callerUid, field, fieldValue, call }; userController.getUserDataByUID = function (callerUid, uid, callback) { - if (parseInt(callerUid, 10) <= 0 && meta.config.privateUserInfo) { - return callback(new Error('[[error:no-privileges]]')); - } - if (!parseInt(uid, 10)) { return callback(new Error('[[error:no-user]]')); } + async.waterfall([ + function (next) { + privileges.global.can('view:users', callerUid, next); + }, + function (canView, next) { + if (!canView) { + return next(new Error('[[error:no-privileges]]')); + } + async.parallel({ + userData: async.apply(user.getUserData, uid), + settings: async.apply(user.getSettings, uid), + }, next); + }, + function (results, next) { + if (!results.userData) { + return next(new Error('[[error:no-user]]')); + } - async.parallel({ - userData: async.apply(user.getUserData, uid), - settings: async.apply(user.getSettings, uid), - }, function (err, results) { - if (err || !results.userData) { - return callback(err || new Error('[[error:no-user]]')); - } - - results.userData.email = results.settings.showemail && !meta.config.hideEmail ? results.userData.email : undefined; - results.userData.fullname = results.settings.showfullname && !meta.config.hideFullname ? results.userData.fullname : undefined; + results.userData.email = results.settings.showemail && !meta.config.hideEmail ? results.userData.email : undefined; + results.userData.fullname = results.settings.showfullname && !meta.config.hideFullname ? results.userData.fullname : undefined; - callback(null, results.userData); - }); + next(null, results.userData); + }, + ], callback); }; userController.exportPosts = function (req, res, next) { diff --git a/src/install.js b/src/install.js index be0de86dc7..1397dac31a 100644 --- a/src/install.js +++ b/src/install.js @@ -385,7 +385,8 @@ function giveGlobalPrivileges(next) { var privileges = require('./privileges'); var defaultPrivileges = [ 'chat', 'upload:post:image', 'signature', 'search:content', - 'search:users', 'search:tags', 'local:login', + 'search:users', 'search:tags', 'view:users', 'view:tags', 'view:groups', + 'local:login', ]; privileges.global.give(defaultPrivileges, 'registered-users', next); } diff --git a/src/middleware/header.js b/src/middleware/header.js index 65ec79d219..6f9fdb7321 100644 --- a/src/middleware/header.js +++ b/src/middleware/header.js @@ -196,8 +196,6 @@ module.exports = function (middleware) { templateValues.defaultLang = meta.config.defaultLang || 'en-GB'; templateValues.userLang = res.locals.config.userLang; templateValues.languageDirection = results.languageDirection; - templateValues.privateUserInfo = meta.config.privateUserInfo; - templateValues.privateTagListing = meta.config.privateTagListing; templateValues.template = { name: res.locals.template }; templateValues.template[res.locals.template] = true; diff --git a/src/middleware/index.js b/src/middleware/index.js index 896ddbf76c..d2dd50b663 100644 --- a/src/middleware/index.js +++ b/src/middleware/index.js @@ -13,8 +13,8 @@ var plugins = require('../plugins'); var meta = require('../meta'); var user = require('../user'); var groups = require('../groups'); - var analytics = require('../analytics'); +var privileges = require('../privileges'); var controllers = { api: require('./../controllers/api'), @@ -112,11 +112,12 @@ middleware.routeTouchIcon = function routeTouchIcon(req, res) { }; middleware.privateTagListing = function privateTagListing(req, res, next) { - if (!req.loggedIn && meta.config.privateTagListing) { + privileges.global.can('view:tags', req.uid, function (err, canView) { + if (err || canView) { + return next(err); + } controllers.helpers.notAllowed(req, res); - } else { - next(); - } + }); }; middleware.exposeGroupName = function exposeGroupName(req, res, next) { diff --git a/src/middleware/user.js b/src/middleware/user.js index 366e4fdc9d..86cb998332 100644 --- a/src/middleware/user.js +++ b/src/middleware/user.js @@ -2,6 +2,7 @@ var async = require('async'); var nconf = require('nconf'); +var winston = require('winston'); var meta = require('../meta'); var user = require('../user'); @@ -87,11 +88,26 @@ module.exports = function (middleware) { } middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) { - if (!req.loggedIn && meta.config.privateUserInfo) { - return middleware.authenticate(req, res, next); - } + winston.warn('[middleware], checkGlobalPrivacySettings deprecated, use canViewUsers or canViewGroups'); + middleware.canViewUsers(req, res, next); + }; + + middleware.canViewUsers = function canViewUsers(req, res, next) { + privileges.global.can('view:users', req.uid, function (err, canView) { + if (err || canView) { + return next(err); + } + controllers.helpers.notAllowed(req, res); + }); + }; - next(); + middleware.canViewGroups = function canViewGroups(req, res, next) { + privileges.global.can('view:groups', req.uid, function (err, canView) { + if (err || canView) { + return next(err); + } + controllers.helpers.notAllowed(req, res); + }); }; middleware.checkAccountPermissions = function checkAccountPermissions(req, res, next) { diff --git a/src/privileges/global.js b/src/privileges/global.js index aee98a836d..275aa73275 100644 --- a/src/privileges/global.js +++ b/src/privileges/global.js @@ -21,6 +21,9 @@ module.exports = function (privileges) { { name: '[[admin/manage/privileges:search-content]]' }, { name: '[[admin/manage/privileges:search-users]]' }, { name: '[[admin/manage/privileges:search-tags]]' }, + { name: '[[admin/manage/privileges:view-users]]' }, + { name: '[[admin/manage/privileges:view-tags]]' }, + { name: '[[admin/manage/privileges:view-groups]]' }, { name: '[[admin/manage/privileges:allow-local-login]]' }, { name: '[[admin/manage/privileges:allow-group-creation]]' }, ]; @@ -34,6 +37,9 @@ module.exports = function (privileges) { 'search:content', 'search:users', 'search:tags', + 'view:users', + 'view:tags', + 'view:groups', 'local:login', 'group:create', ]; @@ -94,6 +100,9 @@ module.exports = function (privileges) { 'search:content': privData['search:content'] || isAdminOrMod, 'search:users': privData['search:users'] || isAdminOrMod, 'search:tags': privData['search:tags'] || isAdminOrMod, + 'view:users': privData['view:users'] || isAdminOrMod, + 'view:tags': privData['view:tags'] || isAdminOrMod, + 'view:groups': privData['view:groups'] || isAdminOrMod, }, next); }, ], callback); diff --git a/src/routes/accounts.js b/src/routes/accounts.js index e5b6198ed1..33c65769ab 100644 --- a/src/routes/accounts.js +++ b/src/routes/accounts.js @@ -4,8 +4,8 @@ var helpers = require('./helpers'); var setupPageRoute = helpers.setupPageRoute; module.exports = function (app, middleware, controllers) { - var middlewares = [middleware.checkGlobalPrivacySettings, middleware.exposeUid]; - var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, middleware.exposeUid]; + var middlewares = [middleware.canViewUsers, middleware.exposeUid]; + var accountMiddlewares = [middleware.canViewUsers, middleware.checkAccountPermissions, middleware.exposeUid]; setupPageRoute(app, '/me/*', middleware, [], middleware.redirectMeToUserslug); setupPageRoute(app, '/uid/:uid*', middleware, [], middleware.redirectUidToUserslug); diff --git a/src/routes/api.js b/src/routes/api.js index 1fc95c6764..9e4aff58cf 100644 --- a/src/routes/api.js +++ b/src/routes/api.js @@ -16,10 +16,10 @@ module.exports = function (app, middleware, controllers) { } }, controllers.api.getConfig); - router.get('/me', middleware.checkGlobalPrivacySettings, controllers.user.getCurrentUser); - router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUID); - router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUsername); - router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.user.getUserByEmail); + router.get('/me', middleware.canViewUsers, controllers.user.getCurrentUser); + router.get('/user/uid/:uid', middleware.canViewUsers, controllers.user.getUserByUID); + router.get('/user/username/:username', middleware.canViewUsers, controllers.user.getUserByUsername); + router.get('/user/email/:email', middleware.canViewUsers, controllers.user.getUserByEmail); router.get('/user/uid/:userslug/export/posts', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportPosts); router.get('/user/uid/:userslug/export/uploads', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportUploads); @@ -40,8 +40,8 @@ module.exports = function (app, middleware, controllers) { var middlewares = [middleware.maintenanceMode, multipartMiddleware, middleware.validateFiles, middleware.applyCSRF]; router.post('/post/upload', middlewares, uploadsController.uploadPost); router.post('/topic/thumb/upload', middlewares, uploadsController.uploadThumb); - router.post('/user/:userslug/uploadpicture', middlewares.concat([middleware.authenticate, middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadPicture); + router.post('/user/:userslug/uploadpicture', middlewares.concat([middleware.authenticate, middleware.canViewUsers, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadPicture); - router.post('/user/:userslug/uploadcover', middlewares.concat([middleware.authenticate, middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadCoverPicture); + router.post('/user/:userslug/uploadcover', middlewares.concat([middleware.authenticate, middleware.canViewUsers, middleware.checkAccountPermissions]), controllers.accounts.edit.uploadCoverPicture); router.post('/groups/uploadpicture', middlewares.concat([middleware.authenticate]), controllers.groups.uploadCover); }; diff --git a/src/routes/index.js b/src/routes/index.js index e62b115059..2a6d787f78 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -77,13 +77,13 @@ function categoryRoutes(app, middleware, controllers) { } function userRoutes(app, middleware, controllers) { - var middlewares = [middleware.checkGlobalPrivacySettings]; + var middlewares = [middleware.canViewUsers]; setupPageRoute(app, '/users', middleware, middlewares, controllers.users.index); } function groupRoutes(app, middleware, controllers) { - var middlewares = [middleware.checkGlobalPrivacySettings]; + var middlewares = [middleware.canViewGroups]; setupPageRoute(app, '/groups', middleware, middlewares, controllers.groups.list); setupPageRoute(app, '/groups/:slug', middleware, middlewares, controllers.groups.details); diff --git a/src/upgrades/1.12.0/global_view_privileges.js b/src/upgrades/1.12.0/global_view_privileges.js new file mode 100644 index 0000000000..d074d4b148 --- /dev/null +++ b/src/upgrades/1.12.0/global_view_privileges.js @@ -0,0 +1,28 @@ +'use strict'; + +var async = require('async'); +var privileges = require('../../privileges'); + +module.exports = { + name: 'Global view privileges', + timestamp: Date.UTC(2019, 0, 5), + method: function (callback) { + var meta = require('../../meta'); + + var tasks = [ + async.apply(privileges.global.give, ['view:users', 'view:tags', 'view:groups'], 'registered-users'), + ]; + + if (parseInt(meta.config.privateUserInfo, 10) !== 1) { + tasks.push(async.apply(privileges.global.give, ['view:users', 'view:groups'], 'guests')); + tasks.push(async.apply(privileges.global.give, ['view:users', 'view:groups'], 'spiders')); + } + + if (parseInt(meta.config.privateTagListing, 10) !== 1) { + tasks.push(async.apply(privileges.global.give, ['view:tags'], 'guests')); + tasks.push(async.apply(privileges.global.give, ['view:tags'], 'spiders')); + } + + async.series(tasks, callback); + }, +}; diff --git a/src/upgrades/1.12.0/group_create_privilege.js b/src/upgrades/1.12.0/group_create_privilege.js index 59f45ced84..af2d7d2f61 100644 --- a/src/upgrades/1.12.0/group_create_privilege.js +++ b/src/upgrades/1.12.0/group_create_privilege.js @@ -3,7 +3,7 @@ var privileges = require('../../privileges'); module.exports = { - name: 'Update category watch data', + name: 'Group create global privilege', timestamp: Date.UTC(2019, 0, 4), method: function (callback) { var meta = require('../../meta'); diff --git a/src/views/admin/settings/tags.tpl b/src/views/admin/settings/tags.tpl index c08a816e49..5d93bda00f 100644 --- a/src/views/admin/settings/tags.tpl +++ b/src/views/admin/settings/tags.tpl @@ -25,21 +25,6 @@ - -
-
[[admin/settings/tags:privacy]]
-
-
-
- -
-
-
-
-
[[admin/settings/tags:related-topics]]
diff --git a/src/views/admin/settings/user.tpl b/src/views/admin/settings/user.tpl index a10867a271..874e55eb60 100644 --- a/src/views/admin/settings/user.tpl +++ b/src/views/admin/settings/user.tpl @@ -65,12 +65,6 @@ [[admin/settings/user:allow-account-deletion]]
-
- -