mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-26 08:36:12 +01:00
@@ -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",
|
||||
|
||||
|
||||
@@ -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)"
|
||||
}
|
||||
@@ -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",
|
||||
|
||||
@@ -40,13 +40,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
var loggedIn = data.config ? data.config.loggedIn : false;
|
||||
|
||||
if (item.route.match('/users') && data.privateUserInfo && !loggedIn) {
|
||||
if (item.route.match('/users') && data.user && !data.user.privileges['view:users']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.route.match('/tags') && data.privateTagListing && !loggedIn) {
|
||||
if (item.route.match('/tags') && data.user && !data.user.privileges['view:tags']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
@@ -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 () {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
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);
|
||||
});
|
||||
};
|
||||
|
||||
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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
28
src/upgrades/1.12.0/global_view_privileges.js
Normal file
28
src/upgrades/1.12.0/global_view_privileges.js
Normal file
@@ -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');
|
||||
|
||||
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');
|
||||
|
||||
@@ -25,21 +25,6 @@
|
||||
</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="col-sm-2 col-xs-12 settings-header">[[admin/settings/tags:related-topics]]</div>
|
||||
<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>
|
||||
</label>
|
||||
</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">
|
||||
<label class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
|
||||
<input class="mdl-switch__input" type="checkbox" data-field="hideFullname">
|
||||
|
||||
@@ -744,6 +744,9 @@ describe('Categories', function () {
|
||||
signature: false,
|
||||
'local:login': false,
|
||||
'group:create': false,
|
||||
'view:users': false,
|
||||
'view:tags': false,
|
||||
'view:groups': false,
|
||||
});
|
||||
|
||||
done();
|
||||
@@ -784,6 +787,9 @@ describe('Categories', function () {
|
||||
'groups:search:content': true,
|
||||
'groups:search:users': 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:file': false,
|
||||
'groups:signature': true,
|
||||
|
||||
@@ -1336,14 +1336,15 @@ describe('Controllers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return 401 if privateUserInfo is turned on', function (done) {
|
||||
meta.config.privateUserInfo = 1;
|
||||
request(nconf.get('url') + '/api/user/foo', { json: true }, function (err, res, body) {
|
||||
meta.config.privateUserInfo = 0;
|
||||
it('should return 401 if user does not have view:users privilege', function (done) {
|
||||
privileges.global.rescind(['view:users'], 'guests', function (err) {
|
||||
assert.ifError(err);
|
||||
assert.equal(res.statusCode, 401);
|
||||
assert.equal(body, 'not-authorized');
|
||||
done();
|
||||
request(nconf.get('url') + '/api/user/foo', { json: true }, function (err, res, body) {
|
||||
assert.ifError(err);
|
||||
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) {
|
||||
var privileges = require('../../src/privileges');
|
||||
privileges.global.give([
|
||||
'chat', 'upload:post:image', 'signature', 'search:content',
|
||||
'search:users', 'search:tags', 'local:login',
|
||||
], 'registered-users', next);
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
privileges.global.give([
|
||||
'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) {
|
||||
|
||||
@@ -13,24 +13,39 @@ describe('helpers', function () {
|
||||
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({
|
||||
navigation: [{ route: '/users' }],
|
||||
privateUserInfo: true,
|
||||
config: {
|
||||
loggedIn: false,
|
||||
user: {
|
||||
privileges: {
|
||||
'view:users': false,
|
||||
},
|
||||
},
|
||||
}, 0);
|
||||
assert(!flag);
|
||||
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({
|
||||
navigation: [{ route: '/tags' }],
|
||||
privateTagListing: true,
|
||||
config: {
|
||||
loggedIn: false,
|
||||
user: {
|
||||
privileges: {
|
||||
'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);
|
||||
assert(!flag);
|
||||
|
||||
Reference in New Issue
Block a user