feat: added PUT/DELETE /api/v1/users/:uid/ban routes

v1.18.x
Julian Lam 5 years ago
parent db5c5b2cfb
commit a5af2dc819

@ -242,19 +242,74 @@ paths:
$ref: '#/components/schemas/Status'
response:
type: object
'/{uid}/ban':
put:
tags:
- users
summary: bans a user
parameters:
- in: path
name: uid
schema:
type: integer
required: true
description: uid of the user to ban
requestBody:
content:
application/json:
schema:
type: object
properties:
until:
type: number
description: UNIX timestamp of the ban expiry
example: 1585775608076
reason:
type: string
example: the reason for the ban
responses:
'200':
description: successfully banned user
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
delete:
tags:
- users
summary: unbans a user
parameters:
- in: path
name: uid
schema:
type: integer
required: true
description: uid of the user to unban
responses:
'200':
description: successfully unbanned user
content:
application/json:
schema:
type: object
properties:
status:
$ref: '#/components/schemas/Status'
response:
type: object
components:
schemas:
Status:
type: object
properties:
code:
allOf:
- title: Success
type: string
example: ok
- title: Error
type: string
example: error
type: string
example: ok
message:
type: string
example: OK

@ -44,6 +44,7 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
$('.users-table [component="user/select/single"]:checked').parents('.user-row').remove();
}
// use onSuccess/onFail instead
function done(successMessage, className, flag) {
return function (err) {
if (err) {
@ -57,6 +58,18 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
};
}
function onSuccess(successMessage, className, flag) {
app.alertSuccess(successMessage);
if (className) {
update(className, flag);
}
unselectAll();
}
function onFail(err) {
app.alertError(err.message);
}
$('[component="user/select/all"]').on('click', function () {
$('.users-table [component="user/select/single"]').prop('checked', $(this).is(':checked'));
});
@ -119,7 +132,20 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
bootbox.confirm((uids.length > 1 ? '[[admin/manage/users:alerts.confirm-ban-multi]]' : '[[admin/manage/users:alerts.confirm-ban]]'), function (confirm) {
if (confirm) {
socket.emit('user.banUsers', { uids: uids, reason: '' }, done('[[admin/manage/users:alerts.ban-success]]', '.ban', true));
var requests = uids.map(function (uid) {
return $.ajax({
url: config.relative_path + '/api/v1/users/' + uid + '/ban',
method: 'put',
});
});
$.when(requests)
.done(function () {
onSuccess('[[admin/manage/users:alerts.ban-success]]', '.ban', true);
})
.fail(function (ev) {
onFail(ev.responseJSON.status);
});
}
});
});
@ -150,7 +176,24 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
return data;
}, {});
var until = formData.length > 0 ? (Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))) : 0;
socket.emit('user.banUsers', { uids: uids, until: until, reason: formData.reason }, done('[[admin/manage/users:alerts.ban-success]]', '.ban', true));
var requests = uids.map(function (uid) {
return $.ajax({
url: config.relative_path + '/api/v1/users/' + uid + '/ban',
method: 'put',
data: {
until: until,
reason: formData.reason,
},
});
});
$.when(requests)
.done(function () {
onSuccess('[[admin/manage/users:alerts.ban-success]]', '.ban', true);
}).fail(function (ev) {
onFail(ev.responseJSON.status);
});
},
},
},
@ -165,7 +208,19 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
return false; // specifically to keep the menu open
}
socket.emit('user.unbanUsers', uids, done('[[admin/manage/users:alerts.unban-success]]', '.ban', false));
var requests = uids.map(function (uid) {
return $.ajax({
url: config.relative_path + '/api/v1/users/' + uid + '/ban',
method: 'delete',
});
});
$.when(requests)
.done(function () {
onSuccess('[[admin/manage/users:alerts.unban-success]]', '.ban', false);
}).fail(function (ev) {
onFail(ev.responseJSON.status);
});
});
$('.reset-lockout').on('click', function () {

@ -154,20 +154,21 @@ define('forum/account/header', [
var until = formData.length > 0 ? (Date.now() + (formData.length * 1000 * 60 * 60 * (parseInt(formData.unit, 10) ? 24 : 1))) : 0;
socket.emit('user.banUsers', {
uids: [theirid],
until: until,
reason: formData.reason || '',
}, function (err) {
if (err) {
return app.alertError(err.message);
}
$.ajax({
url: config.relative_path + '/api/v1/users/' + theirid + '/ban',
method: 'put',
data: {
until: until,
reason: formData.reason || '',
},
}).done(function () {
if (typeof onSuccess === 'function') {
return onSuccess();
}
ajaxify.refresh();
}).fail(function (ev) {
app.alertError(ev.responseJSON.status.message);
});
},
},
@ -177,11 +178,13 @@ define('forum/account/header', [
}
function unbanAccount() {
socket.emit('user.unbanUsers', [ajaxify.data.theirid], function (err) {
if (err) {
return app.alertError(err.message);
}
$.ajax({
url: config.relative_path + '/api/v1/users/' + ajaxify.data.theirid + '/ban',
method: 'delete',
}).done(function () {
ajaxify.refresh();
}).fail(function (ev) {
app.alertError(ev.responseJSON.status.message);
});
}

@ -7,7 +7,11 @@ const privileges = require('../../privileges');
const notifications = require('../../notifications');
const meta = require('../../meta');
const events = require('../../events');
const translator = require('../../translator');
const db = require('../../database');
const helpers = require('../helpers');
const sockets = require('../../socket.io');
const Users = module.exports;
@ -155,3 +159,62 @@ Users.unfollow = async (req, res) => {
});
helpers.formatApiResponse(200, res);
};
Users.ban = async (req, res) => {
if (!await privileges.users.hasBanPrivilege(req.user.uid)) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
} else if (await user.isAdministrator(req.params.uid)) {
return helpers.formatApiResponse(403, res, new Error('[[error:cant-ban-other-admins]]'));
}
const banData = await user.bans.ban(req.params.uid, req.body.until, req.body.reason);
await db.setObjectField('uid:' + req.params.uid + ':ban:' + banData.timestamp, 'fromUid', req.user.uid);
if (!req.body.reason) {
req.body.reason = await translator.translate('[[user:info.banned-no-reason]]');
}
sockets.in('uid_' + req.params.uid).emit('event:banned', {
until: req.body.until,
reason: req.body.reason,
});
await events.log({
type: 'user-ban',
uid: req.user.uid,
targetUid: req.params.uid,
ip: req.ip,
reason: req.body.reason || undefined,
});
plugins.fireHook('action:user.banned', {
callerUid: req.user.uid,
ip: req.ip,
uid: req.params.uid,
until: req.body.until > 0 ? req.body.until : undefined,
reason: req.body.reason || undefined,
});
await user.auth.revokeAllSessions(req.params.uid);
helpers.formatApiResponse(200, res);
};
Users.unban = async (req, res) => {
if (!await privileges.users.hasBanPrivilege(req.user.uid)) {
return helpers.formatApiResponse(403, res, new Error('[[error:no-privileges]]'));
}
await user.bans.unban(req.params.uid);
await events.log({
type: 'user-unban',
uid: req.user.uid,
targetUid: req.params.uid,
ip: req.ip,
});
plugins.fireHook('action:user.unbanned', {
callerUid: req.user.uid,
ip: req.ip,
uid: req.params.uid,
});
helpers.formatApiResponse(200, res);
};

@ -32,59 +32,8 @@ function authenticatedRoutes() {
setupApiRoute(router, '/:uid/follow', middleware, [...middlewares], 'post', controllers.write.users.follow);
setupApiRoute(router, '/:uid/follow', middleware, [...middlewares], 'delete', controllers.write.users.unfollow);
// app.put('/:uid/follow', apiMiddleware.requireUser, function(req, res) {
// Users.follow(req.user.uid, req.params.uid, function(err) {
// return errorHandler.handle(err, res);
// });
// });
// app.delete('/:uid/follow', apiMiddleware.requireUser, function(req, res) {
// Users.unfollow(req.user.uid, req.params.uid, function(err) {
// return errorHandler.handle(err, res);
// });
// });
// app.route('/:uid/chats')
// .post(apiMiddleware.requireUser, function(req, res) {
// if (!utils.checkRequired(['message'], req, res)) {
// return false;
// }
// var timestamp = parseInt(req.body.timestamp, 10) || Date.now();
// function addMessage(roomId) {
// Messaging.addMessage({
// uid: req.user.uid,
// roomId: roomId,
// content: req.body.message,
// timestamp: timestamp,
// }, function(err, message) {
// if (parseInt(req.body.quiet, 10) !== 1) {
// Messaging.notifyUsersInRoom(req.user.uid, roomId, message);
// }
// return errorHandler.handle(err, res, message);
// });
// }
// Messaging.canMessageUser(req.user.uid, req.params.uid, function(err) {
// if (err) {
// return errorHandler.handle(err, res);
// }
// if (req.body.roomId) {
// addMessage(req.body.roomId);
// } else {
// Messaging.newRoom(req.user.uid, [req.params.uid], function(err, roomId) {
// if (err) {
// return errorHandler.handle(err, res);
// }
// addMessage(roomId);
// });
// }
// });
// });
setupApiRoute(router, '/:uid/ban', middleware, [...middlewares, middleware.exposePrivileges], 'put', controllers.write.users.ban);
setupApiRoute(router, '/:uid/ban', middleware, [...middlewares, middleware.exposePrivileges], 'delete', controllers.write.users.unban);
// app.route('/:uid/ban')
// .put(apiMiddleware.requireUser, apiMiddleware.requireAdmin, function(req, res) {
@ -131,6 +80,52 @@ function authenticatedRoutes() {
// errorHandler.handle(err, res);
// });
// });
/**
* Chat routes were not migrated because chats may get refactored... also the logic is derpy
* It also does not take into account multiple chats for a given user.
*/
// app.route('/:uid/chats')
// .post(apiMiddleware.requireUser, function(req, res) {
// if (!utils.checkRequired(['message'], req, res)) {
// return false;
// }
// var timestamp = parseInt(req.body.timestamp, 10) || Date.now();
// function addMessage(roomId) {
// Messaging.addMessage({
// uid: req.user.uid,
// roomId: roomId,
// content: req.body.message,
// timestamp: timestamp,
// }, function(err, message) {
// if (parseInt(req.body.quiet, 10) !== 1) {
// Messaging.notifyUsersInRoom(req.user.uid, roomId, message);
// }
// return errorHandler.handle(err, res, message);
// });
// }
// Messaging.canMessageUser(req.user.uid, req.params.uid, function(err) {
// if (err) {
// return errorHandler.handle(err, res);
// }
// if (req.body.roomId) {
// addMessage(req.body.roomId);
// } else {
// Messaging.newRoom(req.user.uid, [req.params.uid], function(err, roomId) {
// if (err) {
// return errorHandler.handle(err, res);
// }
// addMessage(roomId);
// });
// }
// });
// });
}
module.exports = function () {

@ -1,21 +1,18 @@
'use strict';
const winston = require('winston');
const db = require('../../database');
const user = require('../../user');
const meta = require('../../meta');
const websockets = require('../index');
const events = require('../../events');
const privileges = require('../../privileges');
const plugins = require('../../plugins');
const emailer = require('../../emailer');
const translator = require('../../translator');
const utils = require('../../../public/src/utils');
const flags = require('../../flags');
module.exports = function (SocketUser) {
SocketUser.banUsers = async function (socket, data) {
websockets.warnDeprecated(socket, 'PUT /api/v1/users/:uid/ban');
if (!data || !Array.isArray(data.uids)) {
throw new Error('[[error:invalid-data]]');
}
@ -43,6 +40,8 @@ module.exports = function (SocketUser) {
};
SocketUser.unbanUsers = async function (socket, uids) {
websockets.warnDeprecated(socket, 'DELETE /api/v1/users/:uid/ban');
await toggleBan(socket.uid, uids, async function (uid) {
await user.bans.unban(uid);
await events.log({
@ -76,19 +75,7 @@ module.exports = function (SocketUser) {
if (isAdmin) {
throw new Error('[[error:cant-ban-other-admins]]');
}
const username = await user.getUserField(uid, 'username');
const siteTitle = meta.config.title || 'NodeBB';
const data = {
subject: '[[email:banned.subject, ' + siteTitle + ']]',
username: username,
until: until ? utils.toISOString(until) : false,
reason: reason,
};
try {
await emailer.send('banned', uid, data);
} catch (err) {
winston.error('[emailer.send] ' + err.message);
}
const banData = await user.bans.ban(uid, until, reason);
await db.setObjectField('uid:' + uid + ':ban:' + banData.timestamp, 'fromUid', callerUid);

@ -1,5 +1,10 @@
'use strict';
const winston = require('winston');
const meta = require('../meta');
const utils = require('../utils');
const emailer = require('../emailer');
const db = require('../database');
module.exports = function (User) {
@ -38,6 +43,22 @@ module.exports = function (User) {
} else {
await db.sortedSetRemove('users:banned:expire', uid);
}
// Email notification of ban
const username = await User.getUserField(uid, 'username');
const siteTitle = meta.config.title || 'NodeBB';
const data = {
subject: '[[email:banned.subject, ' + siteTitle + ']]',
username: username,
until: until ? utils.toISOString(until) : false,
reason: reason,
};
try {
await emailer.send('banned', uid, data);
} catch (err) {
winston.error('[emailer.send] ' + err.message);
}
return banData;
};

Loading…
Cancel
Save