3 new global privileges
view:users
view:tags
view:groups
v1.18.x
Barış Soner Uşaklı 6 years ago
parent ae779ea4f9
commit c72da5595a

@ -10,6 +10,9 @@
"search-content": "Search Content", "search-content": "Search Content",
"search-users": "Search Users", "search-users": "Search Users",
"search-tags": "Search Tags", "search-tags": "Search Tags",
"view-users": "View Users",
"view-tags": "View Tags",
"view-groups": "View Groups",
"allow-local-login": "Local Login", "allow-local-login": "Local Login",
"allow-group-creation": "Group Create", "allow-group-creation": "Group Create",

@ -5,8 +5,6 @@
"min-length": "Minimum Tag Length", "min-length": "Minimum Tag Length",
"max-length": "Maximum Tag Length", "max-length": "Maximum Tag Length",
"goto-manage": "Click here to visit the tag management page.", "goto-manage": "Click here to visit the tag management page.",
"privacy": "Privacy",
"list-private": "Make the tags list private",
"related-topics": "Related Topics", "related-topics": "Related Topics",
"max-related-topics": "Maximum related topics to display (if supported by theme)" "max-related-topics": "Maximum related topics to display (if supported by theme)"
} }

@ -14,7 +14,6 @@
"disable-email-changes": "Disable email changes", "disable-email-changes": "Disable email changes",
"disable-password-changes": "Disable password changes", "disable-password-changes": "Disable password changes",
"allow-account-deletion": "Allow account deletion", "allow-account-deletion": "Allow account deletion",
"user-info-private": "Hide user list and data from guests",
"hide-fullname": "Hide fullname from users", "hide-fullname": "Hide fullname from users",
"hide-email": "Hide email from users", "hide-email": "Hide email from users",
"themes": "Themes", "themes": "Themes",

@ -40,13 +40,15 @@
return false; 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; return false;
} }
if (item.route.match('/tags') && data.privateTagListing && !loggedIn) { if (item.route.match('/groups') && data.user && !data.user.privileges['view:groups']) {
return false; return false;
} }
@ -179,7 +181,7 @@
} }
return states.map(function (priv) { return states.map(function (priv) {
var guestDisabled = ['groups:moderate', 'groups:posts:upvote', 'groups:posts:downvote', 'groups:local:login', 'groups:group:create']; 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 = var disabled =
(member === 'guests' && guestDisabled.includes(priv.name)) || (member === 'guests' && guestDisabled.includes(priv.name)) ||
(member === 'spiders' && !spidersEnabled.includes(priv.name)); (member === 'spiders' && !spidersEnabled.includes(priv.name));

@ -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 exports.handleErrors = function handleErrors(err, req, res, next) { // eslint-disable-line no-unused-vars
var cases = { var cases = {
EBADCSRFTOKEN: function () { EBADCSRFTOKEN: function () {
winston.error(req.path + '\n', err.message); winston.error(req.path + '\n' + err.message);
res.sendStatus(403); res.sendStatus(403);
}, },
'blacklisted-ip': function () { 'blacklisted-ip': function () {

@ -13,6 +13,7 @@ var meta = require('../meta');
var posts = require('../posts'); var posts = require('../posts');
var batch = require('../batch'); var batch = require('../batch');
var events = require('../events'); var events = require('../events');
var privileges = require('../privileges');
var accountHelpers = require('./accounts/helpers'); var accountHelpers = require('./accounts/helpers');
var userController = module.exports; var userController = module.exports;
@ -84,27 +85,33 @@ userController.getUserDataByField = function (callerUid, field, fieldValue, call
}; };
userController.getUserDataByUID = function (callerUid, uid, callback) { 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)) { if (!parseInt(uid, 10)) {
return callback(new Error('[[error:no-user]]')); 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({ results.userData.email = results.settings.showemail && !meta.config.hideEmail ? results.userData.email : undefined;
userData: async.apply(user.getUserData, uid), results.userData.fullname = results.settings.showfullname && !meta.config.hideFullname ? results.userData.fullname : undefined;
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;
callback(null, results.userData); next(null, results.userData);
}); },
], callback);
}; };
userController.exportPosts = function (req, res, next) { userController.exportPosts = function (req, res, next) {

@ -385,7 +385,8 @@ function giveGlobalPrivileges(next) {
var privileges = require('./privileges'); var privileges = require('./privileges');
var defaultPrivileges = [ var defaultPrivileges = [
'chat', 'upload:post:image', 'signature', 'search:content', '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); privileges.global.give(defaultPrivileges, 'registered-users', next);
} }

@ -196,8 +196,6 @@ module.exports = function (middleware) {
templateValues.defaultLang = meta.config.defaultLang || 'en-GB'; templateValues.defaultLang = meta.config.defaultLang || 'en-GB';
templateValues.userLang = res.locals.config.userLang; templateValues.userLang = res.locals.config.userLang;
templateValues.languageDirection = results.languageDirection; templateValues.languageDirection = results.languageDirection;
templateValues.privateUserInfo = meta.config.privateUserInfo;
templateValues.privateTagListing = meta.config.privateTagListing;
templateValues.template = { name: res.locals.template }; templateValues.template = { name: res.locals.template };
templateValues.template[res.locals.template] = true; templateValues.template[res.locals.template] = true;

@ -13,8 +13,8 @@ var plugins = require('../plugins');
var meta = require('../meta'); var meta = require('../meta');
var user = require('../user'); var user = require('../user');
var groups = require('../groups'); var groups = require('../groups');
var analytics = require('../analytics'); var analytics = require('../analytics');
var privileges = require('../privileges');
var controllers = { var controllers = {
api: require('./../controllers/api'), api: require('./../controllers/api'),
@ -112,11 +112,12 @@ middleware.routeTouchIcon = function routeTouchIcon(req, res) {
}; };
middleware.privateTagListing = function privateTagListing(req, res, next) { 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); controllers.helpers.notAllowed(req, res);
} else { });
next();
}
}; };
middleware.exposeGroupName = function exposeGroupName(req, res, next) { middleware.exposeGroupName = function exposeGroupName(req, res, next) {

@ -2,6 +2,7 @@
var async = require('async'); var async = require('async');
var nconf = require('nconf'); var nconf = require('nconf');
var winston = require('winston');
var meta = require('../meta'); var meta = require('../meta');
var user = require('../user'); var user = require('../user');
@ -87,11 +88,26 @@ module.exports = function (middleware) {
} }
middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) { middleware.checkGlobalPrivacySettings = function checkGlobalPrivacySettings(req, res, next) {
if (!req.loggedIn && meta.config.privateUserInfo) { winston.warn('[middleware], checkGlobalPrivacySettings deprecated, use canViewUsers or canViewGroups');
return middleware.authenticate(req, res, next); 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) { middleware.checkAccountPermissions = function checkAccountPermissions(req, res, next) {

@ -21,6 +21,9 @@ module.exports = function (privileges) {
{ name: '[[admin/manage/privileges:search-content]]' }, { name: '[[admin/manage/privileges:search-content]]' },
{ name: '[[admin/manage/privileges:search-users]]' }, { name: '[[admin/manage/privileges:search-users]]' },
{ name: '[[admin/manage/privileges:search-tags]]' }, { 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-local-login]]' },
{ name: '[[admin/manage/privileges:allow-group-creation]]' }, { name: '[[admin/manage/privileges:allow-group-creation]]' },
]; ];
@ -34,6 +37,9 @@ module.exports = function (privileges) {
'search:content', 'search:content',
'search:users', 'search:users',
'search:tags', 'search:tags',
'view:users',
'view:tags',
'view:groups',
'local:login', 'local:login',
'group:create', 'group:create',
]; ];
@ -94,6 +100,9 @@ module.exports = function (privileges) {
'search:content': privData['search:content'] || isAdminOrMod, 'search:content': privData['search:content'] || isAdminOrMod,
'search:users': privData['search:users'] || isAdminOrMod, 'search:users': privData['search:users'] || isAdminOrMod,
'search:tags': privData['search:tags'] || 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); }, next);
}, },
], callback); ], callback);

@ -4,8 +4,8 @@ var helpers = require('./helpers');
var setupPageRoute = helpers.setupPageRoute; var setupPageRoute = helpers.setupPageRoute;
module.exports = function (app, middleware, controllers) { module.exports = function (app, middleware, controllers) {
var middlewares = [middleware.checkGlobalPrivacySettings, middleware.exposeUid]; var middlewares = [middleware.canViewUsers, middleware.exposeUid];
var accountMiddlewares = [middleware.checkGlobalPrivacySettings, middleware.checkAccountPermissions, middleware.exposeUid]; var accountMiddlewares = [middleware.canViewUsers, middleware.checkAccountPermissions, middleware.exposeUid];
setupPageRoute(app, '/me/*', middleware, [], middleware.redirectMeToUserslug); setupPageRoute(app, '/me/*', middleware, [], middleware.redirectMeToUserslug);
setupPageRoute(app, '/uid/:uid*', middleware, [], middleware.redirectUidToUserslug); setupPageRoute(app, '/uid/:uid*', middleware, [], middleware.redirectUidToUserslug);

@ -16,10 +16,10 @@ module.exports = function (app, middleware, controllers) {
} }
}, controllers.api.getConfig); }, controllers.api.getConfig);
router.get('/me', middleware.checkGlobalPrivacySettings, controllers.user.getCurrentUser); router.get('/me', middleware.canViewUsers, controllers.user.getCurrentUser);
router.get('/user/uid/:uid', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUID); router.get('/user/uid/:uid', middleware.canViewUsers, controllers.user.getUserByUID);
router.get('/user/username/:username', middleware.checkGlobalPrivacySettings, controllers.user.getUserByUsername); router.get('/user/username/:username', middleware.canViewUsers, controllers.user.getUserByUsername);
router.get('/user/email/:email', middleware.checkGlobalPrivacySettings, controllers.user.getUserByEmail); 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/posts', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportPosts);
router.get('/user/uid/:userslug/export/uploads', middleware.checkAccountPermissions, middleware.exposeUid, controllers.user.exportUploads); 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]; var middlewares = [middleware.maintenanceMode, multipartMiddleware, middleware.validateFiles, middleware.applyCSRF];
router.post('/post/upload', middlewares, uploadsController.uploadPost); router.post('/post/upload', middlewares, uploadsController.uploadPost);
router.post('/topic/thumb/upload', middlewares, uploadsController.uploadThumb); 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); router.post('/groups/uploadpicture', middlewares.concat([middleware.authenticate]), controllers.groups.uploadCover);
}; };

@ -77,13 +77,13 @@ function categoryRoutes(app, middleware, controllers) {
} }
function userRoutes(app, middleware, controllers) { function userRoutes(app, middleware, controllers) {
var middlewares = [middleware.checkGlobalPrivacySettings]; var middlewares = [middleware.canViewUsers];
setupPageRoute(app, '/users', middleware, middlewares, controllers.users.index); setupPageRoute(app, '/users', middleware, middlewares, controllers.users.index);
} }
function groupRoutes(app, middleware, controllers) { function groupRoutes(app, middleware, controllers) {
var middlewares = [middleware.checkGlobalPrivacySettings]; var middlewares = [middleware.canViewGroups];
setupPageRoute(app, '/groups', middleware, middlewares, controllers.groups.list); setupPageRoute(app, '/groups', middleware, middlewares, controllers.groups.list);
setupPageRoute(app, '/groups/:slug', middleware, middlewares, controllers.groups.details); setupPageRoute(app, '/groups/:slug', middleware, middlewares, controllers.groups.details);

@ -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);
},
};

@ -3,7 +3,7 @@
var privileges = require('../../privileges'); var privileges = require('../../privileges');
module.exports = { module.exports = {
name: 'Update category watch data', name: 'Group create global privilege',
timestamp: Date.UTC(2019, 0, 4), timestamp: Date.UTC(2019, 0, 4),
method: function (callback) { method: function (callback) {
var meta = require('../../meta'); var meta = require('../../meta');

@ -25,21 +25,6 @@
</div> </div>
</div> </div>
<div class="row">
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/tags:privacy]]</div>
<div class="col-sm-10 col-xs-12">
<form>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" data-field="privateTagListing">
<span class="mdl-switch__label">[[admin/settings/tags:list-private]]</span>
</label>
</div>
</form>
</div>
</div>
<div class="row"> <div class="row">
<div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/tags:related-topics]]</div> <div class="col-sm-2 col-xs-12 settings-header">[[admin/settings/tags:related-topics]]</div>
<div class="col-sm-10 col-xs-12"> <div class="col-sm-10 col-xs-12">

@ -65,12 +65,6 @@
<span class="mdl-switch__label"><strong>[[admin/settings/user:allow-account-deletion]]</strong></span> <span class="mdl-switch__label"><strong>[[admin/settings/user:allow-account-deletion]]</strong></span>
</label> </label>
</div> </div>
<div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" data-field="privateUserInfo">
<span class="mdl-switch__label"><strong>[[admin/settings/user:user-info-private]]</strong></span>
</label>
</div>
<div class="checkbox"> <div class="checkbox">
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect"> <label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input class="mdl-switch__input" type="checkbox" data-field="hideFullname"> <input class="mdl-switch__input" type="checkbox" data-field="hideFullname">

@ -744,6 +744,9 @@ describe('Categories', function () {
signature: false, signature: false,
'local:login': false, 'local:login': false,
'group:create': false, 'group:create': false,
'view:users': false,
'view:tags': false,
'view:groups': false,
}); });
done(); done();
@ -784,6 +787,9 @@ describe('Categories', function () {
'groups:search:content': true, 'groups:search:content': true,
'groups:search:users': true, 'groups:search:users': true,
'groups:search:tags': true, 'groups:search:tags': true,
'groups:view:users': true,
'groups:view:tags': true,
'groups:view:groups': true,
'groups:upload:post:image': true, 'groups:upload:post:image': true,
'groups:upload:post:file': false, 'groups:upload:post:file': false,
'groups:signature': true, 'groups:signature': true,

@ -1336,14 +1336,15 @@ describe('Controllers', function () {
}); });
}); });
it('should return 401 if privateUserInfo is turned on', function (done) { it('should return 401 if user does not have view:users privilege', function (done) {
meta.config.privateUserInfo = 1; privileges.global.rescind(['view:users'], 'guests', function (err) {
request(nconf.get('url') + '/api/user/foo', { json: true }, function (err, res, body) {
meta.config.privateUserInfo = 0;
assert.ifError(err); assert.ifError(err);
assert.equal(res.statusCode, 401); request(nconf.get('url') + '/api/user/foo', { json: true }, function (err, res, body) {
assert.equal(body, 'not-authorized'); assert.ifError(err);
done(); assert.equal(res.statusCode, 401);
assert.equal(body, 'not-authorized');
privileges.global.give(['view:users'], 'guests', done);
});
}); });
}); });

@ -236,10 +236,19 @@ function setupDefaultConfigs(meta, next) {
function giveDefaultGlobalPrivileges(next) { function giveDefaultGlobalPrivileges(next) {
var privileges = require('../../src/privileges'); var privileges = require('../../src/privileges');
privileges.global.give([ async.waterfall([
'chat', 'upload:post:image', 'signature', 'search:content', function (next) {
'search:users', 'search:tags', 'local:login', privileges.global.give([
], 'registered-users', next); 'chat', 'upload:post:image', 'signature', 'search:content',
'search:users', 'search:tags', 'local:login', 'view:users', 'view:tags', 'view:groups',
], 'registered-users', next);
},
function (next) {
privileges.global.give([
'view:users', 'view:tags', 'view:groups',
], 'guests', next);
},
], next);
} }
function enableDefaultPlugins(callback) { function enableDefaultPlugins(callback) {

@ -13,24 +13,39 @@ describe('helpers', function () {
done(); done();
}); });
it('should return false if route is /users and privateUserInfo is on and user is not logged in', function (done) { it('should return false if route is /users and user does not have view:users privilege', function (done) {
var flag = helpers.displayMenuItem({ var flag = helpers.displayMenuItem({
navigation: [{ route: '/users' }], navigation: [{ route: '/users' }],
privateUserInfo: true, user: {
config: { privileges: {
loggedIn: false, 'view:users': false,
},
}, },
}, 0); }, 0);
assert(!flag); assert(!flag);
done(); done();
}); });
it('should return false if route is /tags and privateTagListing is on and user is not logged in', function (done) { it('should return false if route is /tags and user does not have view:tags privilege', function (done) {
var flag = helpers.displayMenuItem({ var flag = helpers.displayMenuItem({
navigation: [{ route: '/tags' }], navigation: [{ route: '/tags' }],
privateTagListing: true, user: {
config: { privileges: {
loggedIn: false, 'view:tags': false,
},
},
}, 0);
assert(!flag);
done();
});
it('should return false if route is /groups and user does not have view:groups privilege', function (done) {
var flag = helpers.displayMenuItem({
navigation: [{ route: '/groups' }],
user: {
privileges: {
'view:groups': false,
},
}, },
}, 0); }, 0);
assert(!flag); assert(!flag);

Loading…
Cancel
Save