Skip to content

Commit

Permalink
Merge pull request #1230 from jembi/Rework-roles-and-permissions
Browse files Browse the repository at this point in the history
Rework roles and permissions
  • Loading branch information
drizzentic authored Oct 7, 2024
2 parents f451ea2 + 96467f1 commit d259d5d
Show file tree
Hide file tree
Showing 40 changed files with 16,667 additions and 1,948 deletions.
14,343 changes: 14,326 additions & 17 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions performance/mediator/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 16 additions & 16 deletions src/api/apps.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
'use strict'

import logger from 'winston'
import * as authorisation from './authorisation'

import {AppModelAPI} from '../model/apps'
import { RoleModelAPI } from '../model/role'
import {DEFAULT_IMPORT_MAP_PATHS} from '../constants'
import * as utils from '../utils'

/*
Checks admin permission for create, update and delete operations.
Throws error if user does not have admin access
*/
const checkUserPermission = (ctx, operation) => {
if (!authorisation.inGroup('admin', ctx.authenticated)) {
ctx.statusCode = 403
throw Error(
`User ${ctx.authenticated.email} is not an admin, API access to ${operation} an app denied.`
)
}
}

/*
Returns app if it exists, if not it throws an error
Expand Down Expand Up @@ -53,7 +43,9 @@ const validateId = (ctx, id) => {

export async function addApp(ctx) {
try {
checkUserPermission(ctx, 'add')
const authorised = await utils.checkUserPermission(ctx, 'addApp', 'app-manage-all')

if (!authorised) return

const app = new AppModelAPI(ctx.request.body)

Expand All @@ -76,7 +68,9 @@ export async function addApp(ctx) {

export async function updateApp(ctx, appId) {
try {
checkUserPermission(ctx, 'update')
const authorised = await utils.checkUserPermission(ctx, 'updateApp', 'app-manage-all')

if (!authorised) return

validateId(ctx, appId)

Expand Down Expand Up @@ -115,6 +109,10 @@ export async function getApps(ctx) {

export async function getApp(ctx, appId) {
try {
const authorised = await utils.checkUserPermission(ctx, 'getApp', 'app-view-all', 'app-view-specified', appId)

if (!authorised) return

validateId(ctx, appId)

const app = await checkAppExists(ctx, appId)
Expand All @@ -130,7 +128,9 @@ export async function getApp(ctx, appId) {

export async function deleteApp(ctx, appId) {
try {
checkUserPermission(ctx, 'delete')
const authorised = await utils.checkUserPermission(ctx, 'deleteApp', 'app-manage-all')

if (!authorised) return

validateId(ctx, appId)

Expand Down
67 changes: 19 additions & 48 deletions src/api/audits.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import logger from 'winston'
import os from 'os'

import * as auditing from '../auditing'
import * as authorisation from './authorisation'
import * as utils from '../utils'
import {AuditMetaModel, AuditModel} from '../model/audits'
import {config} from '../config'
Expand Down Expand Up @@ -61,20 +60,13 @@ function auditLogUsed(auditId, outcome, user) {
* Adds a Audit
*/
export async function addAudit(ctx) {
// Test if the user is authorised
if (!authorisation.inGroup('admin', ctx.authenticated)) {
utils.logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to addAudit denied.`,
'info'
)
return
}
try {
const authorised = await utils.checkUserPermission(ctx, 'addAudit', 'audit-trail-manage')

const auditData = ctx.request.body
if (!authorised) return

const auditData = ctx.request.body

try {
const audit = new AuditModel(auditData)
await audit.save()
await processAuditMeta(audit)
Expand All @@ -99,18 +91,11 @@ function checkPatientID(patientID) {
* Retrieves the list of Audits
*/
export async function getAudits(ctx) {
// Must be admin
if (!authorisation.inGroup('admin', ctx.authenticated)) {
utils.logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to getAudits denied.`,
'info'
)
return
}

try {
const authorised = await utils.checkUserPermission(ctx, 'getAudits', 'audit-trail-view')

if (!authorised) return

let filters
const filtersObject = ctx.request.query

Expand Down Expand Up @@ -237,21 +222,14 @@ export async function getAudits(ctx) {
* Retrieves the details for a specific Audit Record
*/
export async function getAuditById(ctx, auditId) {
// Must be admin
if (!authorisation.inGroup('admin', ctx.authenticated)) {
utils.logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to getAuditById denied.`,
'info'
)
return
}
try {
const authorised = await utils.checkUserPermission(ctx, 'getAuditById', 'audit-trail-view')

// Get the values to use
auditId = unescape(auditId)
if (!authorised) return

// Get the values to use
auditId = unescape(auditId)

try {
// get projection object
const projectionFiltersObject = getProjectionObject('full')

Expand Down Expand Up @@ -296,18 +274,11 @@ export async function getAuditById(ctx, auditId) {
* construct audit filtering dropdown options
*/
export async function getAuditsFilterOptions(ctx) {
// Must be admin
if (!authorisation.inGroup('admin', ctx.authenticated)) {
utils.logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to getAudits denied.`,
'info'
)
return
}

try {
const authorised = await utils.checkUserPermission(ctx, 'getAuditsFilterOptions', 'audit-trail-view')

if (!authorised) return

ctx.body = await AuditMetaModel.findOne({}).exec()
} catch (e) {
utils.logAndSetResponse(
Expand Down
14 changes: 4 additions & 10 deletions src/api/authentication.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import os from 'os'
import * as auditing from '../auditing'
import * as authorisation from './authorisation'
import passport from '../passport'
import {logAndSetResponse} from '../utils'
import {logAndSetResponse, checkUserPermission} from '../utils'
import {config} from '../config'
import {
BASIC_AUTH_TYPE,
Expand Down Expand Up @@ -201,15 +201,9 @@ export async function authenticate(ctx, next) {
}

export async function getEnabledAuthenticationTypes(ctx, next) {
if (!authorisation.inGroup('admin', ctx.authenticated)) {
logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to get enabled authentication types denied.`,
'info'
)
return next()
}
const authorised = await checkUserPermission(ctx, 'getAuthType', 'client-manage-all')

if (!authorised) return

if (!config.authentication || !Object.keys(config.authentication).length) {
logAndSetResponse(
Expand Down
31 changes: 16 additions & 15 deletions src/api/authorisation.js
Original file line number Diff line number Diff line change
@@ -1,35 +1,36 @@
'use strict'

import {ChannelModelAPI} from '../model/channels'
import { RoleModelAPI } from '../model/role'

export function inGroup(group, user) {
return user.groups.indexOf(group) >= 0
}

const getUserChannelsByPermissions = (user, allPermission, specifiedPermission) =>
RoleModelAPI.find({name: {$in: user.groups}}).then(roles => {
if (roles.find(role => role.permissions[allPermission] || role.permissions[allPermission.replace('view', 'manage')])) {
return ChannelModelAPI.find({}).exec()
}
const specifiedChannels = roles.reduce((prev, curr) =>
prev.concat(curr.permissions[specifiedPermission], curr.permissions[specifiedPermission.replace('view', 'manage')]),
[]
)
return ChannelModelAPI.find({_id: {$in: specifiedChannels}}).exec()
})

/**
* A promise returning function that returns the list
* of viewable channels for a user.
*/
export function getUserViewableChannels(user, access = 'txViewAcl') {
// if admin find all channels
if (inGroup('admin', user)) {
return ChannelModelAPI.find({}).exec()
} else {
// otherwise only channels that this user has access to
return ChannelModelAPI.find({[access]: {$in: user.groups}}).exec()
}
export function getUserViewableChannels(user) {
return getUserChannelsByPermissions(user, 'channel-view-all', 'channel-view-specified')
}

/**
* A promise returning function that returns the list
* of rerunnable channels for a user.
*/
export function getUserRerunableChannels(user) {
// if admin allow all channel
if (inGroup('admin', user)) {
return ChannelModelAPI.find({}).exec()
} else {
// otherwise figure out what this user can rerun
return ChannelModelAPI.find({txRerunAcl: {$in: user.groups}}).exec()
}
return getUserChannelsByPermissions(user, 'transaction-rerun-all', 'transaction-rerun-specified')
}
16 changes: 5 additions & 11 deletions src/api/certificateAuthority.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logger from 'winston'
import pem from 'pem'

import * as authorisation from './authorisation'
import * as utils from '../utils'
import {KeystoreModelAPI} from '../model/keystore'
import {promisify} from 'util'
Expand All @@ -12,17 +11,12 @@ const readCertificateInfo = promisify(pem.readCertificateInfo)
const getFingerprint = promisify(pem.getFingerprint)

export async function generateCert(ctx) {
// Must be admin
const authorised = await utils.checkUserPermission(ctx, 'generateCert', 'certificates-manage')

if (!authorised) return

let result
if (authorisation.inGroup('admin', ctx.authenticated) === false) {
utils.logAndSetResponse(
ctx,
403,
`User ${ctx.authenticated.email} is not an admin, API access to getServerKey by id denied.`,
'info'
)
return
}

const {
request: {body: options}
} = ctx
Expand Down
Loading

0 comments on commit d259d5d

Please sign in to comment.