diff --git a/api/src/controllers/users.js b/api/src/controllers/users.js index 4c5978bbcac..1576daa67c5 100644 --- a/api/src/controllers/users.js +++ b/api/src/controllers/users.js @@ -7,9 +7,9 @@ const logger = require('../logger'); const serverUtils = require('../server-utils'); const replication = require('../services/replication'); -const hasFullPermission = req => { +const hasPermissions = (req, permissions) => { return auth - .check(req, ['can_edit', 'can_update_users']) + .check(req, permissions) .then(() => true) .catch(err => { if (err.code === 403) { @@ -19,6 +19,8 @@ const hasFullPermission = req => { }); }; +const hasFullPermission = req => hasPermissions(req, ['can_edit', 'can_update_users']); + const isUpdatingSelf = (req, credentials, username) => { return auth.getUserCtx(req).then(userCtx => { return ( @@ -134,6 +136,35 @@ const convertUserListToV1 = (users=[]) => { return users; }; +const getUserByUsername = async (req, res) => { + try { + const username = req.params.username; + const credentials = auth.basicAuthCredentials(req); + const [hasPermission, isGettingSelf] = await Promise.all([ + hasPermissions(req, 'can_view_users'), + isUpdatingSelf(req, credentials, username), + ]).catch(error => { + if (error.statusCode === 401) { + throw { code: 401, message: 'Not logged in', err: error }; + } + + throw error; + }); + + if (!hasPermission && !isGettingSelf) { + throw { + message: 'You do not have permissions to fetch this person', + code: 403, + }; + } + + const body = await users.getUser(username); + res.json(body); + } catch (error) { + serverUtils.error(error, req, res); + } +}; + module.exports = { get: (req, res) => { return getUserList(req) @@ -173,7 +204,7 @@ module.exports = { ]) => { if (basic === false) { // If you're passing basic auth we're going to validate it, even if we - // technicaly don't need to (because you already have a valid cookie and + // technically don't need to (because you already have a valid cookie and // full permission). // This is to maintain consistency in the personal change password UI: // we want to validate the password you pass regardless of your permissions @@ -236,6 +267,10 @@ module.exports = { v2: { get: async (req, res) => { + if (req.params?.username) { + return getUserByUsername(req, res); + } + try { const body = await getUserList(req); res.json(body); diff --git a/api/src/routing.js b/api/src/routing.js index 4583ed87ca5..84c5cfd0b0a 100644 --- a/api/src/routing.js +++ b/api/src/routing.js @@ -416,6 +416,7 @@ app.post('/api/v1/forms/validate', textParser, forms.validate); app.get('/api/v1/users', users.get); app.get('/api/v2/users', users.v2.get); +app.get('/api/v2/users/:username', users.v2.get); app.postJson('/api/v1/users', users.create); app.postJsonOrCsv('/api/v2/users', users.v2.create); app.postJson('/api/v1/users/:username', users.update); diff --git a/shared-libs/user-management/src/users.js b/shared-libs/user-management/src/users.js index 1429af90a71..14c580074f0 100644 --- a/shared-libs/user-management/src/users.js +++ b/shared-libs/user-management/src/users.js @@ -768,7 +768,7 @@ const hydrateUserSettings = (userSettings) => { const getUserDoc = (username, dbName) => { return db[dbName] - .get(`org.couchdb.user:${username}`) + .get(createID(username)) .catch(err => { err.db = dbName; throw err; @@ -801,6 +801,11 @@ const getUserSettings = async({ name }) => { */ module.exports = { deleteUser: username => deleteUser(createID(username)), + getUser: async (username) => { + const [user, setting] = await getUserDocsByName(username); + const facilities = await facility.list([user], [setting]); + return mapUsers([user], [setting], facilities)[0]; + }, getList: async (filters) => { const [users, settings] = await getUsersAndSettings(filters); const facilities = await facility.list(users);