mirror of
https://github.com/jambonz/jambonz-api-server.git
synced 2026-10-28 10:24:12 +00:00
Feat/371 view only user implementation using user_permissions (#381)
* https://github.com/jambonz/jambonz-api-server/issues/371 Implemented view_only permission feature * calling prepare-permissions in create-test-db.js * check if there is only 1 permission and if it is VIEW_ONLY then consider user as read-only user * setting is_view_only flag for view user by userid
This commit is contained in:
1 parent
1c55bad04f
commit
4c935c7fda
14 files changed
+529
-12
No files matched your search
@@ -71,9 +71,13 @@ router.post('/', async(req, res) => {
|
||||
obj.service_provider_sid = r[0].service_provider_sid;
|
||||
obj.service_provider_name = service_provider[0].name;
|
||||
}
|
||||
// if there is only one permission and it is VIEW_ONLY, then the user is view only
|
||||
// this is to prevent write operations on the API
|
||||
const is_view_only = permissions.length === 1 && permissions.includes('VIEW_ONLY');
|
||||
const payload = {
|
||||
scope: obj.scope,
|
||||
permissions,
|
||||
is_view_only,
|
||||
...(obj.service_provider_sid && {
|
||||
service_provider_sid: obj.service_provider_sid,
|
||||
service_provider_name: obj.service_provider_name
|
||||
@@ -83,7 +87,8 @@ router.post('/', async(req, res) => {
|
||||
account_name: obj.account_name,
|
||||
service_provider_name: obj.service_provider_name
|
||||
}),
|
||||
user_sid: obj.user_sid
|
||||
user_sid: obj.user_sid,
|
||||
name: username
|
||||
};
|
||||
|
||||
const expiresIn = parseInt(process.env.JWT_EXPIRES_IN || 60) * 60;
|
||||
|
||||
+53
-5
@@ -1,5 +1,7 @@
|
||||
const router = require('express').Router();
|
||||
const User = require('../../models/user');
|
||||
const UserPermissions = require('../../models/user-permissions');
|
||||
const Permissions = require('../../models/permissions');
|
||||
const {DbErrorBadRequest, BadRequestError, DbErrorForbidden} = require('../../utils/errors');
|
||||
const {generateHashedPassword, verifyPassword} = require('../../utils/password-utils');
|
||||
const {promisePool} = require('../../db');
|
||||
@@ -38,7 +40,8 @@ const validateRequest = async(user_sid, req) => {
|
||||
email,
|
||||
email_activation_code,
|
||||
force_change,
|
||||
is_active
|
||||
is_active,
|
||||
is_view_only
|
||||
} = payload;
|
||||
|
||||
const [r] = await promisePool.query(retrieveSql, user_sid);
|
||||
@@ -93,7 +96,8 @@ const validateRequest = async(user_sid, req) => {
|
||||
if (email_activation_code && !email) {
|
||||
throw new DbErrorBadRequest('email and email_activation_code both required');
|
||||
}
|
||||
if (!name && !new_password && !email && !initial_password && !force_change && !is_active)
|
||||
if (!name && !new_password && !email && !initial_password && !force_change && !is_active &&
|
||||
is_view_only === undefined)
|
||||
throw new DbErrorBadRequest('no updates requested');
|
||||
|
||||
return user;
|
||||
@@ -140,7 +144,35 @@ const ensureUserRetrievalIsAllowed = (req, user) => {
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
async function updateViewOnlyUserPermission(is_view_only, user_sid) {
|
||||
try {
|
||||
const [viewOnlyPermission] = await Permissions.retrieveByName('VIEW_ONLY');
|
||||
if (!viewOnlyPermission) {
|
||||
throw new Error('VIEW_ONLY permission not found');
|
||||
}
|
||||
|
||||
const existingPermissions = await UserPermissions.retrieveByUserIdPermissionSid(
|
||||
user_sid,
|
||||
viewOnlyPermission.permission_sid
|
||||
);
|
||||
if (is_view_only && existingPermissions.length === 0) {
|
||||
await UserPermissions.make({
|
||||
user_sid,
|
||||
permission_sid: viewOnlyPermission.permission_sid,
|
||||
});
|
||||
} else if (!is_view_only && existingPermissions.length > 0) {
|
||||
await UserPermissions.remove(existingPermissions[0].user_permissions_sid);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to update user permissions: ${err.message}`);
|
||||
}
|
||||
}
|
||||
async function removeViewOnlyUserPermission(user_id) {
|
||||
const [viewOnlyPermission] = await Permissions.retrieveByName('VIEW_ONLY');
|
||||
if (viewOnlyPermission) {
|
||||
await UserPermissions.remove(user_id, viewOnlyPermission.permission_sid);
|
||||
}
|
||||
}
|
||||
router.get('/', async(req, res) => {
|
||||
const logger = req.app.locals.logger;
|
||||
|
||||
@@ -308,7 +340,14 @@ router.get('/:user_sid', async(req, res) => {
|
||||
}
|
||||
|
||||
ensureUserRetrievalIsAllowed(req, user);
|
||||
|
||||
const [viewOnlyPermission] = await Permissions.retrieveByName('VIEW_ONLY');
|
||||
const existingPermissions = await UserPermissions.retrieveByUserId(
|
||||
user_sid
|
||||
);
|
||||
logger.debug(`existingPermissions of ${user_sid}: ${JSON.stringify(existingPermissions)}`);
|
||||
user.is_view_only = existingPermissions.length === 1 &&
|
||||
existingPermissions[0].permission_sid === viewOnlyPermission.permission_sid;
|
||||
logger.debug(`User ${user_sid} is view-only user: ${user.is_view_only}`);
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { hashed_password, ...rest } = user;
|
||||
return res.status(200).json(rest);
|
||||
@@ -332,9 +371,9 @@ router.put('/:user_sid', async(req, res) => {
|
||||
is_active,
|
||||
force_change,
|
||||
account_sid,
|
||||
service_provider_sid
|
||||
service_provider_sid,
|
||||
is_view_only
|
||||
} = req.body;
|
||||
|
||||
//if (req.user.user_sid && req.user.user_sid !== user_sid) return res.sendStatus(403);
|
||||
|
||||
if (!hasAdminAuth &&
|
||||
@@ -427,6 +466,8 @@ router.put('/:user_sid', async(req, res) => {
|
||||
//TODO: send email with activation code
|
||||
}
|
||||
}
|
||||
// update user permissions
|
||||
await updateViewOnlyUserPermission(is_view_only, user_sid);
|
||||
res.sendStatus(204);
|
||||
} catch (err) {
|
||||
sysError(logger, res, err);
|
||||
@@ -443,6 +484,8 @@ router.post('/', async(req, res) => {
|
||||
};
|
||||
const allUsers = await User.retrieveAll();
|
||||
delete payload.initial_password;
|
||||
const is_view_only = payload.is_view_only;
|
||||
delete payload.is_view_only;
|
||||
|
||||
try {
|
||||
if (req.body.initial_password) {
|
||||
@@ -464,6 +507,7 @@ router.post('/', async(req, res) => {
|
||||
if (req.user.hasAdminAuth) {
|
||||
logger.debug({payload}, 'POST /users');
|
||||
const uuid = await User.make(payload);
|
||||
await updateViewOnlyUserPermission(is_view_only, uuid);
|
||||
res.status(201).json({user_sid: uuid});
|
||||
}
|
||||
else if (req.user.hasAccountAuth) {
|
||||
@@ -472,6 +516,7 @@ router.post('/', async(req, res) => {
|
||||
...payload,
|
||||
account_sid: req.user.account_sid,
|
||||
});
|
||||
await updateViewOnlyUserPermission(is_view_only, uuid);
|
||||
res.status(201).json({user_sid: uuid});
|
||||
}
|
||||
else if (req.user.hasServiceProviderAuth) {
|
||||
@@ -480,6 +525,7 @@ router.post('/', async(req, res) => {
|
||||
...payload,
|
||||
service_provider_sid: req.user.service_provider_sid,
|
||||
});
|
||||
await updateViewOnlyUserPermission(is_view_only, uuid);
|
||||
res.status(201).json({user_sid: uuid});
|
||||
}
|
||||
} catch (err) {
|
||||
@@ -497,6 +543,8 @@ router.delete('/:user_sid', async(req, res) => {
|
||||
const user = allUsers.filter((user) => user.user_sid === user_sid);
|
||||
|
||||
ensureUserDeletionIsAllowed(req, activeAdminUsers, user);
|
||||
logger.debug(`Removing view-only permission for user ${user_sid}`);
|
||||
await removeViewOnlyUserPermission(user_sid);
|
||||
await User.remove(user_sid);
|
||||
|
||||
/* invalidate the jwt of the deleted user */
|
||||
|
||||
Reference in new issue
Block a user