Skip to content

Commit 7e77fb6

Browse files
committed
HAWNG-902: Fixes ACL rules not being translated back to client
* rbac.ts * Fixes the response format of a bulk request to correct deduplication and structure the json by context mbean name * Adds tracing to help with debugging * rbac.test.ts * Adds tests proving the response of a bulk request * gateway-test-inputs.ts * Fixes expected responses of bulk request tests
1 parent 4d6c29a commit 7e77fb6

File tree

5 files changed

+11794
-41
lines changed

5 files changed

+11794
-41
lines changed

docker/gateway/src/gateway-test-inputs.ts

+45-40
Original file line numberDiff line numberDiff line change
@@ -119,47 +119,52 @@ export const testData = {
119119
],
120120
},
121121
value: {
122-
'gc()': {
123-
CanInvoke: true,
124-
Method: 'gc()',
125-
ObjectName: 'java.lang:type=Memory',
126-
},
127-
'addOrUpdateRoutesFromXml(java.lang.String)': {
128-
CanInvoke: true,
129-
Method: 'addOrUpdateRoutesFromXml(java.lang.String)',
130-
ObjectName:
131-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
132-
},
133-
'addOrUpdateRoutesFromXml(java.lang.String,boolean)': {
134-
CanInvoke: true,
135-
Method: 'addOrUpdateRoutesFromXml(java.lang.String,boolean)',
136-
ObjectName:
137-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
138-
},
139-
'dumpStatsAsXml(boolean)': {
140-
CanInvoke: true,
141-
Method: 'dumpStatsAsXml(boolean)',
142-
ObjectName:
143-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
144-
},
145-
'getCamelId()': {
146-
CanInvoke: true,
147-
Method: 'getCamelId()',
148-
ObjectName:
149-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
150-
},
151-
'getRedeliveries()': {
152-
CanInvoke: true,
153-
Method: 'getRedeliveries()',
154-
ObjectName:
155-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
156-
},
157-
'sendStringBody(java.lang.String,java.lang.String)': {
158-
CanInvoke: true,
159-
Method: 'sendStringBody(java.lang.String,java.lang.String)',
160-
ObjectName:
161-
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
122+
'java.lang:type=Memory': {
123+
'gc()': {
124+
CanInvoke: true,
125+
Method: 'gc()',
126+
ObjectName: 'java.lang:type=Memory',
127+
},
162128
},
129+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context':
130+
{
131+
'addOrUpdateRoutesFromXml(java.lang.String)': {
132+
CanInvoke: true,
133+
Method: 'addOrUpdateRoutesFromXml(java.lang.String)',
134+
ObjectName:
135+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
136+
},
137+
'addOrUpdateRoutesFromXml(java.lang.String,boolean)': {
138+
CanInvoke: true,
139+
Method: 'addOrUpdateRoutesFromXml(java.lang.String,boolean)',
140+
ObjectName:
141+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
142+
},
143+
'dumpStatsAsXml(boolean)': {
144+
CanInvoke: true,
145+
Method: 'dumpStatsAsXml(boolean)',
146+
ObjectName:
147+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
148+
},
149+
'getCamelId()': {
150+
CanInvoke: true,
151+
Method: 'getCamelId()',
152+
ObjectName:
153+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
154+
},
155+
'getRedeliveries()': {
156+
CanInvoke: true,
157+
Method: 'getRedeliveries()',
158+
ObjectName:
159+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
160+
},
161+
'sendStringBody(java.lang.String,java.lang.String)': {
162+
CanInvoke: true,
163+
Method: 'sendStringBody(java.lang.String,java.lang.String)',
164+
ObjectName:
165+
'org.apache.camel:context=io.fabric8.quickstarts.karaf-camel-log-log-example-context,name="log-example-context",type=context',
166+
},
167+
},
163168
},
164169
timestamp: 1718286845551,
165170
},

docker/gateway/src/jolokia-agent/jolokia-agent.ts

+4
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ import * as RBAC from './rbac'
2121

2222
const aclFile = fs.readFileSync(process.env['HAWTIO_ONLINE_RBAC_ACL'] || `${__dirname}/ACL.yaml`, 'utf8')
2323
const aclYaml = yaml.parse(aclFile)
24+
25+
logger.trace('=== imported ACL yaml ===')
26+
logger.trace(aclYaml)
27+
2428
RBAC.initACL(aclYaml)
2529

2630
let isRbacEnabled = typeof process.env['HAWTIO_ONLINE_RBAC_ACL'] !== 'undefined'

docker/gateway/src/jolokia-agent/rbac.test.ts

+43
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ import * as yaml from 'yaml'
22
import * as fs from 'fs'
33
import * as rbac from './rbac'
44
import {
5+
BulkValue,
56
MBeanInfoCache,
67
OptimisedJmxDomains,
78
OptimisedMBeanOperations,
89
hasMBeanOperation,
910
isOptimisedCachedDomains,
1011
} from './globals'
12+
import { ExecRequest } from 'jolokia.js'
1113

1214
const aclFile = fs.readFileSync(process.env['HAWTIO_ONLINE_RBAC_ACL'] || `${__dirname}/ACL.yaml`, 'utf8')
1315
const aclYaml = yaml.parse(aclFile)
@@ -288,3 +290,44 @@ describe('parseProperties', function () {
288290
})
289291
})
290292
})
293+
294+
describe('bulk-intercept-responses', () => {
295+
it('should handle bulk intercepts correctly', () => {
296+
const ctx1 = 'org.apache.camel:context=MyCamel,type=context,name="MyCamel"'
297+
const ctx2 = 'org.apache.camel:context=MyCamel,type=consumers,name=TimerConsumer(0x6a04d2a4)'
298+
299+
const arg: Record<string, string[]> = {}
300+
arg[ctx1] = ['stop()', 'getGlobalOptions()', 'reset()', 'reset(boolean)']
301+
arg[ctx2] = ['getState()', 'stop()', 'getInflightExchanges()', 'getServiceType()', 'getRunLoggingLevel()']
302+
303+
const request: ExecRequest = {
304+
type: 'exec',
305+
mbean: 'hawtio:type=security,area=jmx,name=HawtioOnlineRBAC',
306+
operation: 'canInvoke(java.util.Map)',
307+
arguments: [arg],
308+
}
309+
310+
const mbeansFile = fs.readFileSync(`${__dirname}/test.domainMBeans.json`, 'utf8')
311+
const mbeans = JSON.parse(mbeansFile)
312+
313+
const intercepted = rbac.intercept(request, admin, mbeans)
314+
315+
expect(intercepted.response?.value).toBeDefined()
316+
const value = intercepted.response?.value as Record<string, Record<string, BulkValue>>
317+
expect(Object.getOwnPropertyNames(value)).toHaveLength(2)
318+
319+
expect(value[ctx1]).toBeDefined()
320+
expect(Object.getOwnPropertyNames(value[ctx1])).toHaveLength(arg[ctx1].length)
321+
let stopOp = value[ctx1]['stop()']
322+
expect(stopOp).toBeDefined()
323+
expect(stopOp.CanInvoke).toBeTruthy()
324+
expect(stopOp.ObjectName).toBe(ctx1)
325+
326+
expect(value[ctx2]).toBeDefined()
327+
expect(Object.getOwnPropertyNames(value[ctx2])).toHaveLength(arg[ctx2].length)
328+
stopOp = value[ctx2]['stop()']
329+
expect(stopOp).toBeDefined()
330+
expect(stopOp.CanInvoke).toBeTruthy()
331+
expect(stopOp.ObjectName).toBe(ctx2)
332+
})
333+
})

docker/gateway/src/jolokia-agent/rbac.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@ function isExecRBACRegistryList(request: MBeanRequest) {
9090

9191
// ===== intercept =========================================
9292
export function intercept(request: MBeanRequest, role: string, mbeans: JmxDomains): Intercepted {
93+
logger.trace(`Calling intercept on mbean request`)
94+
logger.trace(`Intercept role: ${role}`)
95+
logger.trace(`Intercept request:`)
96+
logger.trace(request)
97+
logger.trace(`Intercept mbeans:`)
98+
logger.trace(mbeans)
99+
93100
const intercepted = (value: unknown) => ({
94101
intercepted: true,
95102
request: request,
@@ -112,6 +119,8 @@ export function intercept(request: MBeanRequest, role: string, mbeans: JmxDomain
112119

113120
// Intercept client-side RBAC canInvoke(java.lang.String) request
114121
if (isCanInvokeRequest(request) && isArgumentExecRequest(request)) {
122+
logger.trace('Intercept: canInvokeRequest')
123+
115124
const args: unknown[] = request.arguments || []
116125
if (args.length > 0) {
117126
const mbean = args[0] as string
@@ -169,6 +178,9 @@ export function intercept(request: MBeanRequest, role: string, mbeans: JmxDomain
169178

170179
// Intercept client-side RBAC canInvoke(java.util.Map) request
171180
if (isBulkCanInvokeRequest(request) && isArgumentExecRequest(request)) {
181+
logger.trace(
182+
`Intercept: processing a bulk request ${request.mbean} ${!request.arguments ? '<No arguments>' : request.arguments[0]}`,
183+
)
172184
const args: unknown[] = request.arguments || []
173185
if (args.length > 0 && isRecord(args[0])) {
174186
const argEntries = Object.entries(args[0])
@@ -178,20 +190,29 @@ export function intercept(request: MBeanRequest, role: string, mbeans: JmxDomain
178190
const mbean = argEntry[0]
179191
const operations = toStringArray(argEntry[1])
180192

193+
const opValues: Record<string, unknown> = {}
181194
operations.forEach(operation => {
195+
logger.trace(`Intercept: testing operation ${operation} => canInvoke: ${canInvoke(mbean, operation, role)}`)
196+
182197
const bulkValue: BulkValue = {
183198
CanInvoke: canInvoke(mbean, operation, role),
184199
Method: operation,
185200
ObjectName: mbean,
186201
}
187-
value[operation] = bulkValue
202+
opValues[operation] = bulkValue
188203
})
204+
205+
value[mbean] = opValues
189206
})
207+
208+
logger.trace('Intercept: bulk result')
209+
logger.trace(intercepted(value))
190210
return intercepted(value)
191211
}
192212
}
193213

194214
if (rbacRegistryEnabled) {
215+
logger.trace(`Intercept: RBAC registry enabled: ${rbacRegistryEnabled}`)
195216
// Intercept client-side RBACRegistry discovery request
196217
if (isListRBACRegistry(request)) {
197218
return intercepted({
@@ -209,6 +230,7 @@ export function intercept(request: MBeanRequest, role: string, mbeans: JmxDomain
209230

210231
// Intercept client-side optimised list MBeans request
211232
if (isExecRBACRegistryList(request)) {
233+
logger.trace(`Intercept: registry list request`)
212234
return intercepted(optimisedMBeans(mbeans, role))
213235
}
214236
}

0 commit comments

Comments
 (0)