Skip to content

Commit d259d5d

Browse files
authored
Merge pull request #1230 from jembi/Rework-roles-and-permissions
Rework roles and permissions
2 parents f451ea2 + 96467f1 commit d259d5d

40 files changed

+16667
-1948
lines changed

package-lock.json

+14,326-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

performance/mediator/package-lock.json

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api/apps.js

+16-16
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,12 @@
11
'use strict'
22

33
import logger from 'winston'
4-
import * as authorisation from './authorisation'
4+
55
import {AppModelAPI} from '../model/apps'
6+
import { RoleModelAPI } from '../model/role'
67
import {DEFAULT_IMPORT_MAP_PATHS} from '../constants'
8+
import * as utils from '../utils'
79

8-
/*
9-
Checks admin permission for create, update and delete operations.
10-
Throws error if user does not have admin access
11-
*/
12-
const checkUserPermission = (ctx, operation) => {
13-
if (!authorisation.inGroup('admin', ctx.authenticated)) {
14-
ctx.statusCode = 403
15-
throw Error(
16-
`User ${ctx.authenticated.email} is not an admin, API access to ${operation} an app denied.`
17-
)
18-
}
19-
}
2010

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

5444
export async function addApp(ctx) {
5545
try {
56-
checkUserPermission(ctx, 'add')
46+
const authorised = await utils.checkUserPermission(ctx, 'addApp', 'app-manage-all')
47+
48+
if (!authorised) return
5749

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

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

7769
export async function updateApp(ctx, appId) {
7870
try {
79-
checkUserPermission(ctx, 'update')
71+
const authorised = await utils.checkUserPermission(ctx, 'updateApp', 'app-manage-all')
72+
73+
if (!authorised) return
8074

8175
validateId(ctx, appId)
8276

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

116110
export async function getApp(ctx, appId) {
117111
try {
112+
const authorised = await utils.checkUserPermission(ctx, 'getApp', 'app-view-all', 'app-view-specified', appId)
113+
114+
if (!authorised) return
115+
118116
validateId(ctx, appId)
119117

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

131129
export async function deleteApp(ctx, appId) {
132130
try {
133-
checkUserPermission(ctx, 'delete')
131+
const authorised = await utils.checkUserPermission(ctx, 'deleteApp', 'app-manage-all')
132+
133+
if (!authorised) return
134134

135135
validateId(ctx, appId)
136136

src/api/audits.js

+19-48
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import logger from 'winston'
55
import os from 'os'
66

77
import * as auditing from '../auditing'
8-
import * as authorisation from './authorisation'
98
import * as utils from '../utils'
109
import {AuditMetaModel, AuditModel} from '../model/audits'
1110
import {config} from '../config'
@@ -61,20 +60,13 @@ function auditLogUsed(auditId, outcome, user) {
6160
* Adds a Audit
6261
*/
6362
export async function addAudit(ctx) {
64-
// Test if the user is authorised
65-
if (!authorisation.inGroup('admin', ctx.authenticated)) {
66-
utils.logAndSetResponse(
67-
ctx,
68-
403,
69-
`User ${ctx.authenticated.email} is not an admin, API access to addAudit denied.`,
70-
'info'
71-
)
72-
return
73-
}
63+
try {
64+
const authorised = await utils.checkUserPermission(ctx, 'addAudit', 'audit-trail-manage')
7465

75-
const auditData = ctx.request.body
66+
if (!authorised) return
67+
68+
const auditData = ctx.request.body
7669

77-
try {
7870
const audit = new AuditModel(auditData)
7971
await audit.save()
8072
await processAuditMeta(audit)
@@ -99,18 +91,11 @@ function checkPatientID(patientID) {
9991
* Retrieves the list of Audits
10092
*/
10193
export async function getAudits(ctx) {
102-
// Must be admin
103-
if (!authorisation.inGroup('admin', ctx.authenticated)) {
104-
utils.logAndSetResponse(
105-
ctx,
106-
403,
107-
`User ${ctx.authenticated.email} is not an admin, API access to getAudits denied.`,
108-
'info'
109-
)
110-
return
111-
}
112-
11394
try {
95+
const authorised = await utils.checkUserPermission(ctx, 'getAudits', 'audit-trail-view')
96+
97+
if (!authorised) return
98+
11499
let filters
115100
const filtersObject = ctx.request.query
116101

@@ -237,21 +222,14 @@ export async function getAudits(ctx) {
237222
* Retrieves the details for a specific Audit Record
238223
*/
239224
export async function getAuditById(ctx, auditId) {
240-
// Must be admin
241-
if (!authorisation.inGroup('admin', ctx.authenticated)) {
242-
utils.logAndSetResponse(
243-
ctx,
244-
403,
245-
`User ${ctx.authenticated.email} is not an admin, API access to getAuditById denied.`,
246-
'info'
247-
)
248-
return
249-
}
225+
try {
226+
const authorised = await utils.checkUserPermission(ctx, 'getAuditById', 'audit-trail-view')
250227

251-
// Get the values to use
252-
auditId = unescape(auditId)
228+
if (!authorised) return
229+
230+
// Get the values to use
231+
auditId = unescape(auditId)
253232

254-
try {
255233
// get projection object
256234
const projectionFiltersObject = getProjectionObject('full')
257235

@@ -296,18 +274,11 @@ export async function getAuditById(ctx, auditId) {
296274
* construct audit filtering dropdown options
297275
*/
298276
export async function getAuditsFilterOptions(ctx) {
299-
// Must be admin
300-
if (!authorisation.inGroup('admin', ctx.authenticated)) {
301-
utils.logAndSetResponse(
302-
ctx,
303-
403,
304-
`User ${ctx.authenticated.email} is not an admin, API access to getAudits denied.`,
305-
'info'
306-
)
307-
return
308-
}
309-
310277
try {
278+
const authorised = await utils.checkUserPermission(ctx, 'getAuditsFilterOptions', 'audit-trail-view')
279+
280+
if (!authorised) return
281+
311282
ctx.body = await AuditMetaModel.findOne({}).exec()
312283
} catch (e) {
313284
utils.logAndSetResponse(

src/api/authentication.js

+4-10
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import os from 'os'
77
import * as auditing from '../auditing'
88
import * as authorisation from './authorisation'
99
import passport from '../passport'
10-
import {logAndSetResponse} from '../utils'
10+
import {logAndSetResponse, checkUserPermission} from '../utils'
1111
import {config} from '../config'
1212
import {
1313
BASIC_AUTH_TYPE,
@@ -201,15 +201,9 @@ export async function authenticate(ctx, next) {
201201
}
202202

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

214208
if (!config.authentication || !Object.keys(config.authentication).length) {
215209
logAndSetResponse(

src/api/authorisation.js

+16-15
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
'use strict'
22

33
import {ChannelModelAPI} from '../model/channels'
4+
import { RoleModelAPI } from '../model/role'
45

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

10+
const getUserChannelsByPermissions = (user, allPermission, specifiedPermission) =>
11+
RoleModelAPI.find({name: {$in: user.groups}}).then(roles => {
12+
if (roles.find(role => role.permissions[allPermission] || role.permissions[allPermission.replace('view', 'manage')])) {
13+
return ChannelModelAPI.find({}).exec()
14+
}
15+
const specifiedChannels = roles.reduce((prev, curr) =>
16+
prev.concat(curr.permissions[specifiedPermission], curr.permissions[specifiedPermission.replace('view', 'manage')]),
17+
[]
18+
)
19+
return ChannelModelAPI.find({_id: {$in: specifiedChannels}}).exec()
20+
})
21+
922
/**
1023
* A promise returning function that returns the list
1124
* of viewable channels for a user.
1225
*/
13-
export function getUserViewableChannels(user, access = 'txViewAcl') {
14-
// if admin find all channels
15-
if (inGroup('admin', user)) {
16-
return ChannelModelAPI.find({}).exec()
17-
} else {
18-
// otherwise only channels that this user has access to
19-
return ChannelModelAPI.find({[access]: {$in: user.groups}}).exec()
20-
}
26+
export function getUserViewableChannels(user) {
27+
return getUserChannelsByPermissions(user, 'channel-view-all', 'channel-view-specified')
2128
}
2229

2330
/**
2431
* A promise returning function that returns the list
2532
* of rerunnable channels for a user.
2633
*/
2734
export function getUserRerunableChannels(user) {
28-
// if admin allow all channel
29-
if (inGroup('admin', user)) {
30-
return ChannelModelAPI.find({}).exec()
31-
} else {
32-
// otherwise figure out what this user can rerun
33-
return ChannelModelAPI.find({txRerunAcl: {$in: user.groups}}).exec()
34-
}
35+
return getUserChannelsByPermissions(user, 'transaction-rerun-all', 'transaction-rerun-specified')
3536
}

src/api/certificateAuthority.js

+5-11
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import logger from 'winston'
44
import pem from 'pem'
55

6-
import * as authorisation from './authorisation'
76
import * as utils from '../utils'
87
import {KeystoreModelAPI} from '../model/keystore'
98
import {promisify} from 'util'
@@ -12,17 +11,12 @@ const readCertificateInfo = promisify(pem.readCertificateInfo)
1211
const getFingerprint = promisify(pem.getFingerprint)
1312

1413
export async function generateCert(ctx) {
15-
// Must be admin
14+
const authorised = await utils.checkUserPermission(ctx, 'generateCert', 'certificates-manage')
15+
16+
if (!authorised) return
17+
1618
let result
17-
if (authorisation.inGroup('admin', ctx.authenticated) === false) {
18-
utils.logAndSetResponse(
19-
ctx,
20-
403,
21-
`User ${ctx.authenticated.email} is not an admin, API access to getServerKey by id denied.`,
22-
'info'
23-
)
24-
return
25-
}
19+
2620
const {
2721
request: {body: options}
2822
} = ctx

0 commit comments

Comments
 (0)